ARM-第八次

1.主設備號:表示某類設備
2.次設備號:表示某個設備

文件描述符fd

  • 通過文件描述符的下標得到文件指針,通過文件指針的地址,訪問文件信息得到(文件標志,當前位置偏移量,v節點指針),調用一次則占用一個文件下標即一個文件信息

驅動函數


C庫:  fopen fclose fread fwrite fseek ftell

系統
調用  open  close  read  write  lseek  lseek

驅動
程序  open release read write  llseek  llseek(驅動程序里完成核心操作,輔助操作系統代替完成)

驅動函數的代碼

/*======================================================================
    A globalmem driver as an example of char device drivers  
   
    The initial developer of the original code is Hiro Wang
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#include <linux/slab.h>

#define GLOBALMEM_SIZE  0x1000  /*全局內存最大4K字節*/
#define MEM_CLEAR 0x1  /*清0全局內存*/
#define GLOBALMEM_MAJOR 288    /*預設的globalmem的主設備號*/

static int globalmem_major = GLOBALMEM_MAJOR;
/*globalmem設備結構體*/
struct globalmem_dev                                     
{                                                        
  struct cdev cdev; /*cdev結構體*/                       
  unsigned char mem[GLOBALMEM_SIZE]; /*全局內存*/        
};

struct globalmem_dev *globalmem_devp; /*設備結構體指針*/
/*文件打開函數*/
int globalmem_open(struct inode *inode, struct file *filp)//參數是系統調用open()函數時,內核空間執行的輔助代碼傳給驅動函數open
{
  /*將設備結構體指針賦值給文件私有數據指針*/
  filp->private_data = globalmem_devp;
  return 0;
}
/*文件釋放函數*/
int globalmem_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctl設備控制函數 */
/* old version
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)*/
  //可以去操作狀態寄存器與控制寄存器(read,write只能操作數據寄存器,想要得到狀態,或者修改運行狀態只能用ioctl驅動函數)
static long globalmem_ioctl(struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalmem_dev *dev = filp->private_data;/*獲得設備結構體指針*/

  switch (cmd)//請求做什么
  {
    case MEM_CLEAR://對那個空間做清空操作
      memset(dev->mem, 0, GLOBALMEM_SIZE);      
      filp->f_pos = 0;
      printk(KERN_INFO "globalmem is set to zero\n");
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

/*讀函數*/
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)//第一個參數:文件描述符,第二個參數:buf里面保存的是用戶空間的地址,第三個參數:讀size個字節,第四個參數:當前位置
{
  unsigned long p =  *ppos;//當前位置,在文件里多少字節
  unsigned int count = size;
  int ret = 0;
  struct globalmem_dev *dev = filp->private_data; /*獲得設備結構體指針*/

  /*分析和獲取有效的寫長度*/
  if (p >= GLOBALMEM_SIZE)//表示p指向的全局內存外面去了,越界,即是異常判斷
    return count ?  - ENXIO: 0;
  if (count > GLOBALMEM_SIZE - p)//期望讀的字節數大與還剩下的字節數
    count = GLOBALMEM_SIZE - p;//

  /*內核空間->用戶空間*/
  if (copy_to_user(buf, (void*)(dev->mem + p), count))//從p指向的當前位置開始讀內核空間地址的count字節的內容讀用戶空間
  {
    ret =  - EFAULT;//失敗,返回一個非法值
  }
  else
  {
    *ppos += count;//
    ret = count;//
    
    printk(KERN_INFO "read %d bytes(s) from %d\n", count, (int)p);
  }

  return ret;
}

/*寫函數*/
static ssize_t globalmem_write(struct file *filp, const char __user *buf,
  size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  struct globalmem_dev *dev = filp->private_data; /*獲得設備結構體指針*/
  
  /*分析和獲取有效的寫長度*/
  if (p >= GLOBALMEM_SIZE)
    return count ?  - ENXIO: 0;
  if (count > GLOBALMEM_SIZE - p)
    count = GLOBALMEM_SIZE - p;
    
  /*用戶空間->內核空間*/
  if (copy_from_user(dev->mem + p, buf, count))//把用戶空間里的內容count個字節寫到內核空間里去
    ret =  - EFAULT;
  else
  {
    *ppos += count;//當前位置指示器要變大
    ret = count;//實際寫的字節數
    
    printk(KERN_INFO "written %d bytes(s) from %d\n", count, (int)p);
  }

  return ret;
}

/* seek文件定位函數 */
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{//orig:
  loff_t ret = 0;
  switch (orig)
  {
    //文件頭開始偏移
    case 0:   /*相對文件開始位置偏移*/
      if (offset < 0)
      {
        ret =  - EINVAL;
        break;
      }
      if ((unsigned int)offset > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      //當前位置偏移量等于要偏移的字節數
      filp->f_pos = (unsigned int)offset;
      ret = filp->f_pos;//返回實際偏移的數
      break;
    case 1:   /*相對文件當前位置偏移*/
      if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      if ((filp->f_pos + offset) < 0)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos += offset;
      ret = filp->f_pos;
      break;
    case 2:   /*相對文件尾位置偏移*/
      if (offset > 0)
      {
        ret =  - EINVAL;
        break;
      }
      if ((0-offset) > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos = GLOBALMEM_SIZE + offset;
      ret = filp->f_pos;
      break;
    default:
      ret =  - EINVAL;
      break;
  }
  return ret;
}

/*文件操作結構體*/
static const struct file_operations globalmem_fops =
{//const表示只讀變量
//以下是內核驅動函數
  .owner = THIS_MODULE,//固定填THIS
  .llseek = globalmem_llseek,
  .read = globalmem_read,
  .write = globalmem_write,
  .unlocked_ioctl = globalmem_ioctl,
  .open = globalmem_open,
  .release = globalmem_release,//相當于close
};

/*初始化并注冊cdev*/
//傳進來一個指針和此設備號
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
  int err, devno = MKDEV(globalmem_major, index);//將設備加入內核管理

  //cdev_init第一個傳的結構體第一個成員的地址
  //第二個是一個結構體類型變量的初始化
  //有了結構體變量的首地址,就能查看別的所有的成員內容
  cdev_init(&dev->cdev, &globalmem_fops);//將fops里的對象的指針賦值給dev_cdev,open之后,找到與打開的設備號相同的設備
  dev->cdev.owner = THIS_MODULE;
 // dev->cdev.ops = &globalmem_fops;
  err = cdev_add(&dev->cdev, devno, 1);//添加到字符設備鏈表里面
  if (err)
    printk(KERN_NOTICE "Error %d when adding cdev %d", err, index);
}

/*設備驅動模塊加載函數*/
static int __init globalmem_init(void)
{
  int result = -1;
  dev_t devno = MKDEV(globalmem_major, 0);//globalmem_major:主設備號,0:次設備號,dev_t:整型

  /* 申請設備號*/
  if (globalmem_major)//向內核提問,這個設備號是否已經被占用了,如果占用了就失敗了,沒有占用我就占用成功
  {
    result = register_chrdev_region(devno, 1, "globalmem");//在第一個目錄下有一個設備文件,名字為globalmem
  }
  if(result < 0)  /* 動態申請設備號 */
  {//占用了,就向內核提問,有沒有空的主設備號
    result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
    globalmem_major = MAJOR(devno);//將主設備號放到globalmem_major中
  }  
  if (result < 0)
    return result;
    
  /* 動態申請設備結構體的內存*/
  globalmem_devp = (struct globalmem_dev *)kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);//kmalloc內核不能用C庫的malloc,只能用kmalloc,需要空間只是一小塊是就用GFP_KERNEL
  if (!globalmem_devp)    /*申請失敗*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(globalmem_devp, 0, sizeof(struct globalmem_dev));//空間里是不確定的內容,所以要memset清除
  
  globalmem_setup_cdev(globalmem_devp, 0);
  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return result;
}

/*模塊卸載函數*/
static void __exit globalmem_exit(void)
{
  cdev_del(&globalmem_devp->cdev);   /*注銷cdev*/
  kfree(globalmem_devp);     /*釋放設備結構體內存*/
  unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*釋放設備號*/
}

MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);

測試驅動函數的代碼

/*包含操作系統的頭文件*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

/*包含C庫的頭文件*/
#include <stdio.h>

/*包含第三方庫的頭文件*/


/*包含自定義的頭文件*/

int main(void)
{
    int fd=-1;
    char buf[20]="1234567890123456789";
    char outbuf[20]="";
    fd=open("/dev/globalmem",O_RDWR);
    if(fd<0)
    {   
        printf("Open failed\n");
        return 1;
    }
    write(fd,buf,20);
    lseek(fd,0,SEEK_SET);
    read(fd,outbuf,20);
    printf("Outbuf:%s\n",outbuf);

    ioctl(fd,1,0);

    write(fd,"hello",6);
    lseek(fd,0,SEEK_SET);
    read(fd,outbuf,6);
    printf("Outbuf:%s\n",outbuf);

    close(fd);
    return 0;
}

  • 運行結果,首先將globalmem驅動函數下載到開發板的
Paste_Image.png
  • 運行結果,再者將寫好的測試驅動程序的代碼編譯,如下編譯,-Wall,能很好的把一些看不到的警告顯示出來
Paste_Image.png
  • 運行結果,再將測試代碼下載到開發板里,結果出現順序亂掉是因為驅動程序里的printk函數的優先級比printf的優先級高
Paste_Image.png
Paste_Image.png

1.以上的代碼的問題:讀和寫只能非阻塞,不能阻塞(即程序不能進行睡眠)
2.如果同時打開兩個設備文件的話,則會發生進程問題

升級版

1.驅動函數

/*======================================================================
    A globalfifo driver as an example of char device drivers  
    This example is to introduce poll,blocking and non-blocking access
      
    The initial developer of the original code is Baohua Song
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>

#include <linux/slab.h>
#define GLOBALFIFO_SIZE 0x1000  /*è???fifo×??ó4K×??ú*/
#define FIFO_CLEAR 0x1  /*??0è????ú??μ?3€?è*/
#define GLOBALFIFO_MAJOR 253    /*?€éèμ?globalfifoμ??÷éè±?o?*/

static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoéè±??á11ì?*/
struct globalfifo_dev                                     
{                                                        
  struct cdev cdev; /*cdev?á11ì?*/                       
  unsigned int current_len;    /*????*/
  unsigned char mem[GLOBALFIFO_SIZE]; /*????*/        
  wait_queue_head_t r_wait; /*?????*/     
  wait_queue_head_t w_wait; /*?????*/     
};

struct globalfifo_dev *globalfifo_devp; /*éè±??á11ì?????*/
/*???t?ò?aoˉêy*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
  /*??éè±??á11ì??????3?μ?????t??óDêy?Y????*/
  filp->private_data = globalfifo_devp;
  return 0;
}
/*???têí·?oˉêy*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctléè±?????oˉêy */
static long globalfifo_ioctl(struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalfifo_dev *dev = filp->private_data;/*??μ?éè±??á11ì?????*/

  switch (cmd)
  {
    case FIFO_CLEAR:
      dev->current_len = 0;
      memset(dev->mem,0,GLOBALFIFO_SIZE);
      printk(KERN_INFO "globalfifo is set to zero\n");      
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
  unsigned int mask = 0;
  struct globalfifo_dev *dev = filp->private_data; /*??μ?éè±??á11ì?????*/
  
  poll_wait(filp, &dev->r_wait, wait);
  poll_wait(filp, &dev->w_wait, wait);  
  /*fifo·???*/
  if (dev->current_len != 0)//???????0
  {
    mask |= POLLIN | POLLRDNORM;//????|??
  }
  /*fifo·??ú*/
  if (dev->current_len != GLOBALFIFO_SIZE)//???????????
  {
    mask |= POLLOUT | POLLWRNORM; /*????|??*/
  }
     
  return mask;
}


/*globalfifo?áoˉêy*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
  loff_t *ppos)
{
  int ret;
  struct globalfifo_dev *dev = filp->private_data; //??μ?éè±??á11ì?????
  DECLARE_WAITQUEUE(wait, current); //??????,???THis??,????wait??

  add_wait_queue(&dev->r_wait, &wait); //??????????

  /**/
  //printk("Driver:"KERN_INFO"Have no data in device!\n");
  //?????1,??????,???0,?????
  //?????????,???????0,??????,?read??????
  if((filp->f_flags & O_NONBLOCK) && dev->current_len == 0){
     //printk("Driver:"KERN_INFO"device is set as NONBLOACK!\n");
     ret =  - EAGAIN;
     //???????goto
     goto out;
     //???????,???????,?????
  }else if(!(filp->f_flags & O_NONBLOCK)){
     //printk("Driver:"KERN_INFO"device is set as BLOACK!\n");
     //printk("Driver:"KERN_INFO"Before schedule function!\n");
     //?????????,????,interruptible????,??dev->current_len>0??????????,??????????write??,??dev_current_len????????
     wait_event_interruptible(dev->r_wait,dev->current_len > 0);
     //printk("Driver:"KERN_INFO"After schedule function!\n");
     if (signal_pending(current))//????????????,?
     //è?1?ê?òò?aD?o???D?
     {
        ret =  - ERESTARTSYS;
        goto out;
     }
  }
  //printk("Driver:"KERN_INFO"Start read data!\n");

  /*???????????*/
  if (count > dev->current_len)
    count = dev->current_len;//?????????

  if (copy_to_user(buf, dev->mem, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    //????,?????????????,????????????????,??????????????
    memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifoêy?Y?°ò?
    dev->current_len -= count; //óDD§êy?Y3€?è??éù
    printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
     
    wake_up_interruptible(&dev->w_wait); //???????,???????,????????,???????????,?????
    ret = count;
  }
out:
   remove_wait_queue(&dev->r_wait, &wait); //
   set_current_state(TASK_RUNNING);//?????????????
   return ret;
}


/*globalfifoD?2ù×÷*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
  size_t count, loff_t *ppos)
{
  struct globalfifo_dev *dev = filp->private_data; //??μ?éè±??á11ì?????
  int ret;
  DECLARE_WAITQUEUE(wait, current); //??ò?μè?y?óáD
  add_wait_queue(&dev->w_wait, &wait); //??è?D?μè?y?óáDí·
  if ((filp->f_flags & O_NONBLOCK)&& (dev->current_len >= GLOBALFIFO_SIZE))
  {
     ret =  - EAGAIN;
     goto out;
  }
  else if(!(filp->f_flags & O_NONBLOCK))
  {
    //???????????????,????,?????????
     wait_event_interruptible(dev->w_wait,dev->current_len < GLOBALFIFO_SIZE);
     if (signal_pending(current))//??????????,?????
     {
        ret =  - ERESTARTSYS;
        goto out;
     }
  }

  /**/
  if (count > GLOBALFIFO_SIZE - dev->current_len)
    count = GLOBALFIFO_SIZE - dev->current_len;
  //???????
  if (copy_from_user(dev->mem + dev->current_len, buf, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    dev->current_len += count;
    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
      ->current_len);

    wake_up_interruptible(&dev->r_wait); //??????
    
    ret = count;//?????????
  }

  out: 
  remove_wait_queue(&dev->w_wait, &wait); //
  set_current_state(TASK_RUNNING);//?????????????
  return ret;
}


/*???t2ù×÷?á11ì?*/
static const struct file_operations globalfifo_fops =
{
  .owner = THIS_MODULE,
  .read = globalfifo_read,
  .write = globalfifo_write,
  .unlocked_ioctl = globalfifo_ioctl,
  .poll = globalfifo_poll,//????????
  .open = globalfifo_open,
  .release = globalfifo_release,
};

/*3?ê??ˉ2¢×¢2ácdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
  int err, devno = MKDEV(globalfifo_major, index);

  cdev_init(&dev->cdev, &globalfifo_fops);
  dev->cdev.owner = THIS_MODULE;
//  dev->cdev.ops = &globalfifo_fops;
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

/*éè±??y?ˉ?£?é?ó??oˉêy*/
static __init int globalfifo_init(void)
{
  int ret = 0;
  dev_t devno = MKDEV(globalfifo_major, 0);

  /* éê??éè±?o?*/
  if (globalfifo_major)
    ret = register_chrdev_region(devno, 1, "globalfifo");
  if(ret < 0)  /* ?ˉì?éê??éè±?o? */
  {
    ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
    globalfifo_major = MAJOR(devno);
  }
  if (ret < 0)
    return ret;
  /* ?ˉì?éê??éè±??á11ì?μ??ú??*/
  globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  if (!globalfifo_devp)    /*éê??ê§°ü*/
  {
    ret =  - ENOMEM;
    goto fail_malloc;
  }

  memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));

  globalfifo_setup_cdev(globalfifo_devp, 0);

  init_waitqueue_head(&globalfifo_devp->r_wait); /*???????*/
  init_waitqueue_head(&globalfifo_devp->w_wait); /**/

  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return ret;
}


/*?£?éD???oˉêy*/
static __exit void globalfifo_exit(void)
{
  cdev_del(&globalfifo_devp->cdev);   /*×¢?úcdev*/
  kfree(globalfifo_devp);     /*êí·?éè±??á11ì??ú??*/
  unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*êí·?éè±?o?*/
}

MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalfifo_major, int, S_IRUGO);

module_init(globalfifo_init);
module_exit(globalfifo_exit);

數據結構

  • 對同類型的元素進行管理的學科
    1.元素之間的邏輯關系(1)同屬一個集合 (2)元素之間存在一對一的關系:線性關系,線性表(棧(stack):先進后出(在一端做插入,同一端做刪除),隊列(queue):先進先出(在一端做插入,在另一端做刪除)) (3)一對多的關系:樹形關系 (4)多對多的關系:圖形關系
    2.元素之間的存儲方式(1)順序存儲(數組定義,動態分配)(2)鏈式存儲(節點的空間只能動態分配)

select(多功能復用)

最終版

1.驅動函數

/*======================================================================
    A globalfifo driver as an example of char device drivers  
    This example is to introduce poll,blocking and non-blocking access
      
    The initial developer of the original code is Baohua Song
    <author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>

#include <linux/slab.h>
#define GLOBALFIFO_SIZE 0x1000  /*è???fifo×??ó4K×??ú*/
#define FIFO_CLEAR 0x1  /*??0è????ú??μ?3€?è*/
#define GLOBALFIFO_MAJOR 253    /*?€éèμ?globalfifoμ??÷éè±?o?*/

static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoéè±??á11ì?*/
struct globalfifo_dev                                     
{                                                        
  struct cdev cdev; /*cdev?á11ì?*/                       
  unsigned int current_len;    /*fifoóDD§êy?Y3€?è*/
  unsigned char mem[GLOBALFIFO_SIZE]; /*è????ú??*/        
  struct semaphore sem; /*2¢·¢????ó?μ?D?o?á?*/           
  wait_queue_head_t r_wait; /*×èè??áó?μ?μè?y?óáDí·*/     
  wait_queue_head_t w_wait; /*×èè?D?ó?μ?μè?y?óáDí·*/     
};

struct globalfifo_dev *globalfifo_devp; /*éè±??á11ì?????*/
/*???t?ò?aoˉêy*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
  /*??éè±??á11ì??????3?μ?????t??óDêy?Y????*/
  filp->private_data = globalfifo_devp;
  return 0;
}
/*???têí·?oˉêy*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctléè±?????oˉêy */
static long globalfifo_ioctl(struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalfifo_dev *dev = filp->private_data;/*??μ?éè±??á11ì?????*/

  switch (cmd)
  {
    case FIFO_CLEAR:
        down(&dev->sem); //????p??      
      dev->current_len = 0;
      memset(dev->mem,0,GLOBALFIFO_SIZE);//??????
      up(&dev->sem); //????v??
         
      printk(KERN_INFO "globalfifo is set to zero\n");      
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
  unsigned int mask = 0;
  struct globalfifo_dev *dev = filp->private_data; /*??μ?éè±??á11ì?????*/
  
  down(&dev->sem);
  
  poll_wait(filp, &dev->r_wait, wait);
  poll_wait(filp, &dev->w_wait, wait);  
  /*fifo·???*/
  if (dev->current_len != 0)
  {
    mask |= POLLIN | POLLRDNORM; /*±êê?êy?Y?é??μ?*/
  }
  /*fifo·??ú*/
  if (dev->current_len != GLOBALFIFO_SIZE)
  {
    mask |= POLLOUT | POLLWRNORM; /*±êê?êy?Y?éD?è?*/
  }
     
  up(&dev->sem);
  return mask;
}


/*globalfifo?áoˉêy*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
  loff_t *ppos)
{
  int ret;
  struct globalfifo_dev *dev = filp->private_data; //
  DECLARE_WAITQUEUE(wait, current); //??ò?μè?y?óáD

  down(&dev->sem); //p??,????p???????,????
  add_wait_queue(&dev->r_wait, &wait); //??è??áμè?y?óáDí·

  /* μè?yFIFO·??? */
  if (dev->current_len == 0)//??????
  {
    printk("Driver:"KERN_INFO"Have no data in device!\n");
    if (filp->f_flags &O_NONBLOCK)//??????????
    {
      printk("Driver:"KERN_INFO"device is set as NONBLOACK!\n");
      ret =  - EAGAIN;
      goto out;
    } 
    __set_current_state(TASK_INTERRUPTIBLE); //????????????
    up(&dev->sem);//???????,??????????????

    printk("Driver:"KERN_INFO"device is set as BLOCK!\n");
    printk("Driver:"KERN_INFO"Before schedule function!\n");

    schedule(); //?????????
    printk("Driver:"KERN_INFO"After schedule function!\n");
    if (signal_pending(current))//?????????
    //è?1?ê?òò?aD?o???D?
    {
      ret =  - ERESTARTSYS;
      goto out2;
    }

    down(&dev->sem);//??p??
  }
  printk("Driver:"KERN_INFO"Start read data!\n");

  /* ??±?μ?ó??§???? */
  if (count > dev->current_len)
    count = dev->current_len;

  if (copy_to_user(buf, dev->mem, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifoêy?Y?°ò?
    dev->current_len -= count; //óDD§êy?Y3€?è??éù
    printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
     
    wake_up_interruptible(&dev->w_wait); //??D?D?μè?y?óáD
    
    ret = count;
  }
  out: up(&dev->sem); //êí·?D?o?á?
  out2:remove_wait_queue(&dev->w_wait, &wait); //?ó??ê?μ?μè?y?óáDí·ò?3y
  set_current_state(TASK_RUNNING);
  return ret;
}


/*globalfifoD?2ù×÷*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
  size_t count, loff_t *ppos)
{
  struct globalfifo_dev *dev = filp->private_data; //??μ?éè±??á11ì?????
  int ret;
  DECLARE_WAITQUEUE(wait, current); //??ò?μè?y?óáD

  down(&dev->sem); //??è?D?o?á?
  add_wait_queue(&dev->w_wait, &wait); //??è?D?μè?y?óáDí·

  /* μè?yFIFO·??ú */
  if (dev->current_len == GLOBALFIFO_SIZE)
  {
    if (filp->f_flags &O_NONBLOCK)
    //è?1?ê?·?×èè?·??ê
    {
      ret =  - EAGAIN;
      goto out;
    } 
    __set_current_state(TASK_INTERRUPTIBLE); //??±???3ì×?ì??a?ˉ??
    up(&dev->sem);

    schedule(); //μ÷?è??????3ì??DD
    if (signal_pending(current))
    //è?1?ê?òò?aD?o???D?
    {
      ret =  - ERESTARTSYS;
      goto out2;
    }

    down(&dev->sem); //??μ?D?o?á?
  }

  /*?óó??§??????±?μ??úo?????*/
  if (count > GLOBALFIFO_SIZE - dev->current_len)
    count = GLOBALFIFO_SIZE - dev->current_len;

  if (copy_from_user(dev->mem + dev->current_len, buf, count))
  {
    ret =  - EFAULT;
    goto out;
  }
  else
  {
    dev->current_len += count;
    printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
      ->current_len);

    wake_up_interruptible(&dev->r_wait); //??D??áμè?y?óáD
    
    ret = count;
  }

  out: up(&dev->sem); //êí·?D?o?á?
  out2:remove_wait_queue(&dev->w_wait, &wait); //?ó??ê?μ?μè?y?óáDí·ò?3y
  set_current_state(TASK_RUNNING);
  return ret;
}


/*???t2ù×÷?á11ì?*/
static const struct file_operations globalfifo_fops =
{
  .owner = THIS_MODULE,
  .read = globalfifo_read,
  .write = globalfifo_write,
  .unlocked_ioctl = globalfifo_ioctl,
  .poll = globalfifo_poll,
  .open = globalfifo_open,
  .release = globalfifo_release,
};

/*3?ê??ˉ2¢×¢2ácdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
  int err, devno = MKDEV(globalfifo_major, index);

  cdev_init(&dev->cdev, &globalfifo_fops);
  dev->cdev.owner = THIS_MODULE;
  dev->cdev.ops = &globalfifo_fops;
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
    printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

/*éè±??y?ˉ?£?é?ó??oˉêy*/
static __init int globalfifo_init(void)
{
  int ret = 0;
  dev_t devno = MKDEV(globalfifo_major, 0);

  /* éê??éè±?o?*/
  if (globalfifo_major)
    ret = register_chrdev_region(devno, 1, "globalfifo");
  if(ret < 0)  /* ?ˉì?éê??éè±?o? */
  {
    ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
    globalfifo_major = MAJOR(devno);
  }
  if (ret < 0)
    return ret;
  /* ?ˉì?éê??éè±??á11ì?μ??ú??*/
  globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
  if (!globalfifo_devp)    /*éê??ê§°ü*/
  {
    ret =  - ENOMEM;
    goto fail_malloc;
  }

  memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));

  globalfifo_setup_cdev(globalfifo_devp, 0);

  sema_init(&globalfifo_devp->sem,1);   /*3?ê??ˉD?o?á?*/
  init_waitqueue_head(&globalfifo_devp->r_wait); /*3?ê??ˉ?áμè?y?óáDí·*/
  init_waitqueue_head(&globalfifo_devp->w_wait); /*3?ê??ˉD?μè?y?óáDí·*/

  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return ret;
}


/*?£?éD???oˉêy*/
static __exit void globalfifo_exit(void)
{
  cdev_del(&globalfifo_devp->cdev);   /*×¢?úcdev*/
  kfree(globalfifo_devp);     /*êí·?éè±??á11ì??ú??*/
  unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*êí·?éè±?o?*/
}

MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalfifo_major, int, S_IRUGO);

module_init(globalfifo_init);
module_exit(globalfifo_exit);
int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

1.int nfds的值為:最大的文件描述符的下標+1,即為有幾個文件描述符
2.fd_set:是一個結構體的類型

  • 簡單代替讀寫操作的命令
    (1) cat /dev/設備文件/ (讀操作)
    (2)sudo echo afghajhi > /dev/globalfifo (寫操作)
    (3)select 則要寫測試代碼(調用select函數)
open 設備文件
while(1)
{
    FD_CLR
    FD_SET
    select();//返回值為設備文件有數據可讀的個數
    if(FD_ISSET())
    {
    }
    if(FD_ISSET())
    {
    }
}
判斷返回值(<0錯誤,>0正確,=0)
  • 測試驅動程序的讀和寫,運行結果為如下
Paste_Image.png
Paste_Image.png

LED驅動函數

1.GPIO:IO口,通用輸入/輸出

驅動函數

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>


#define DEVICE_NAME "leds"

static unsigned long led_table [] = {
    S3C2410_GPB(5),
    S3C2410_GPB(6),
    S3C2410_GPB(7),
    S3C2410_GPB(8),
};

static unsigned int led_cfg_table [] = {
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
};

static int sbc2440_leds_ioctl(
    struct inode *inode, 
    struct file *file, 
    unsigned int cmd, 
    unsigned long arg)
{
    switch(cmd) {
    case 0:
    case 1:
        if (arg > 4) {
            return -EINVAL;
        }
        s3c2410_gpio_setpin(led_table[arg], !cmd);
        return 0;
    default:
        return -EINVAL;
    }
}

static struct file_operations dev_fops = {
    .owner  =   THIS_MODULE,
    .ioctl  =   sbc2440_leds_ioctl,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
};

static int __init dev_init(void)
{
    int ret;

    int i;
    
    for (i = 0; i < 4; i++) {
        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
        s3c2410_gpio_setpin(led_table[i], 0);
    }

    ret = misc_register(&misc);

    printk (DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit dev_exit(void)
{
    misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

測試代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    int cmd=-1;
    int ledno=-1;
    int fd=-1;
    if(argc<3)
    {
        printf("Usage is testleds 0 2\n");
        return 1;
    }
    sscanf(argv[1],"%d",&cmd);
    sscanf(argv[2],"%d",&ledno);
    if(cmd<0 ||cmd>1)
    {   
        printf("The second arguments is error\n");
        return 2;
    }
    if(ledno<1||ledno>4)
    {
        printf("The third arguments is error\n");
        return 3;
    }
    ledno--;
    fd=open("/dev/leds",O_RDWR);
    if(fd<0)
    {
        printf("Open /dev/leds failed\n");
        return 4;
    }
    ioctl(fd,cmd,ledno);
    close(fd);
    return 0;
}

  • 運行結果為
Paste_Image.png

volatile

1.是可變的關鍵字

  • 一個整型變量,如果沒有前面的關鍵字,編譯器可以會用寄存器去訪問,提高效率,但當另一個進程要進行修改這個變量時,此時這兩個得到的值就不同,所以不要編譯器幫我們進行優化,此時要加volatile關鍵字
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容