字符设备驱动

1、字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。
2、块设备:是指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。
  每一个字符设备或块设备都在/dev目录下对应一个设备文件。linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备。

 

二、字符设备驱动程序基础: 
1、主设备号和次设备号(二者一起为设备号)

主设备号:反应设备类型。 次设备号:区分同类型设备。

typedef u_long dev_t;  在32位机中是4个字节,高12位表示主设备号,低12位表示次设备号

从dev_t中获得主次设备号:        通过主次设备号生成dev_t:

MAJOR(dev_t dev);           MKDEV(int major,int minor);                           
MINOR(dev_t dev);
2、分配设备号(两种方法):
(1)静态申请:
int register_chrdev_region(dev_t from, unsigned count, const char *name);
(2)动态分配:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
注销设备号:
void unregister_chrdev_region(dev_t from, unsigned count);
创建设备文件:
利用cat /proc/devices查看申请到的设备名,设备号。
(1)使用mknod手工创建:mknod filename type major minor
(2)自动创建;
  利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置。在驱动初始化代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备。
3、字符设备驱动程序重要的数据结构:
(1)struct file:代表一个打开的文件描述符,系统中每一个打开的文件在内核中都有一个关联的struct file。它由内核在open时创建,并传递给在文件上操作的任何函数,直到最后关闭。当文件的所有实例都关闭之后,内核释放这个数据结构。
(2)struct inode:用来记录文件的物理信息。它和代表打开的file结构是不同的。一个文件可以对应多个file结构,但只有一个inode结构。inode一般作为file_operations结构中函数的参数传递过来。
  inode译成中文就是索引节点。每个存储设备或存储设备的分区(存储设备是硬盘、软盘、U盘 ... ... )被格式化为文件系统后,应该有两部份,一部份是inode,另一部份是Block,Block是用来存储数据用的。而inode呢,就是用来存储这些数据的信息,这些信息包括文件大小、属主、归属的用户组、读写权限等。inode为每个文件进行信息索引,所以就有了inode的数值。操作系统根据指令,能通过inode值最快的找到相对应的文件
(3)struct file_operations
三、字符设备驱动程序设计:
1.设备注册:
在linux2.6内核中,字符设备使用struct cdev来描述
  1. struct cdev  
  2. {  
  3.   struct kobject kobj;//内嵌的kobject对象  
  4.   struct module *owner;//所属模块  
  5.   struct file_operations *ops;//文件操作结构体  
  6.   struct list_head list;  
  7.   dev_t dev;//设备号,长度为32位,其中高12为主设备号,低20位为此设备号  
  8.   unsigned int count;  
  9. };
字符设备的注册分为三个步骤:
(1)分配cdev: struct cdev *cdev_alloc(void);
(2)初始化cdev: void cdev_init(struct cdev *cdev, const struct file_operations *fops);
(3)添加cdev: int cdev_add(struct cdev *p, dev_t dev, unsigned count)
2.设备操作的实现:file_operations函数集的实现(要明确某个函数什么时候被调用?调用来做什么操作?)
特别注意:驱动程序与应用程序的数据交换:
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n); 
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n); 
put_user(local,user); 
get_user(local,user);

3.设备注销:void cdev_del(struct cdev *p); 

五:字符设备驱动程序分析:

(1)memdev.h 

  1. #ifndef _MEMDEV_H_  
  2. #define _MEMDEV_H_  
  3.   
  4. #ifndef MEMDEV_MAJOR  
  5. #define MEMDEV_MAJOR 251   /*预设的mem的主设备号*/  
  6. #endif  
  7.   
  8. #ifndef MEMDEV_NR_DEVS  
  9. #define MEMDEV_NR_DEVS 2    /*设备数*/  
  10. #endif  
  11.   
  12. #ifndef MEMDEV_SIZE  
  13. #define MEMDEV_SIZE 4096  
  14. #endif  
  15.   
  16. /*mem设备描述结构体*/  
  17. struct mem_dev                                       
  18. {                                                          
  19.   char *data;                        
  20.   unsigned long size;         
  21. };  
  22.   
  23. #endif /* _MEMDEV_H_ *
(2)memdev.c
  1. static mem_major = MEMDEV_MAJOR;  
  2. module_param(mem_major, int, S_IRUGO);  
  3. struct mem_dev *mem_devp; /*设备结构体指针*/  
  4. struct cdev cdev;   
  5.    
  6. /*文件打开函数*/  
  7. int mem_open(struct inode *inode, struct file *filp)  
  8. {  
  9.     struct mem_dev *dev;  
  10.       
  11.     /*获取次设备号*/  
  12.     int num = MINOR(inode->i_rdev);  
  13.    
  14.     if (num >= MEMDEV_NR_DEVS)   
  15.             return -ENODEV;  
  16.     dev = &mem_devp[num];  
  17.       
  18.     /*将设备描述结构指针赋值给文件私有数据指针*/  
  19.     filp->private_data = dev;  
  20.       
  21.     return 0;   
  22. }   
  23. /*文件释放函数*/  
  24. int mem_release(struct inode *inode, struct file *filp)  
  25. {  
  26.   return 0;  
  27. }    
  28. /*读函数*/  
  29. static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)  
  30. {  
  31.   unsigned long p =  *ppos;        /*记录文件指针偏移位置*/    
  32.   unsigned int count = size;    /*记录需要读取的字节数*/   
  33.   int ret = 0;    /*返回值*/    
  34.   struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/  
  35.    
  36.   /*判断读位置是否有效*/  
  37.   if (p >= MEMDEV_SIZE)    /*要读取的偏移大于设备的内存空间*/    
  38.     return 0;  
  39.   if (count > MEMDEV_SIZE - p)     /*要读取的字节大于设备的内存空间*/   
  40.     count = MEMDEV_SIZE - p;  
  41.     
  42.   /*读数据到用户空间:内核空间->用户空间交换数据*/    
  43.   if (copy_to_user(buf, (void*)(dev->data + p), count))  
  44.   {  
  45.     ret =  - EFAULT;  
  46.   }  
  47.   else  
  48.   {  
  49.     *ppos += count;  
  50.     ret = count;        
  51.     printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);  
  52.   }    
  53.   return ret;  
  54. }    
  55. /*写函数*/  
  56. static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)  
  57. {  
  58.   unsigned long p =  *ppos;  
  59.   unsigned int count = size;  
  60.   int ret = 0;  
  61.   struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/  
  62.     
  63.   /*分析和获取有效的写长度*/  
  64.   if (p >= MEMDEV_SIZE)  
  65.     return 0;  
  66.   if (count > MEMDEV_SIZE - p)    /*要写入的字节大于设备的内存空间*/  
  67.     count = MEMDEV_SIZE - p;  
  68.       
  69.   /*从用户空间写入数据*/  
  70.   if (copy_from_user(dev->data + p, buf, count))  
  71.     ret =  - EFAULT;  
  72.   else  
  73.   {  
  74.     *ppos += count;      /*增加偏移位置*/    
  75.     ret = count;      /*返回实际的写入字节数*/   
  76.       
  77.     printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);  
  78.   }    
  79.   return ret;  
  80. }    
  81. /* seek文件定位函数 */  
  82. static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)  
  83. {   
  84.     loff_t newpos;        
  85.     
  86.     switch(whence) {  
  87.       case 0: /* SEEK_SET */       /*相对文件开始位置偏移*/   
  88.         newpos = offset;           /*更新文件指针位置*/  
  89.         break;      
  90.       case 1: /* SEEK_CUR */  
  91.         newpos = filp->f_pos + offset;      
  92.         break;    
  93.       case 2: /* SEEK_END */  
  94.         newpos = MEMDEV_SIZE -1 + offset;  
  95.         break;      
  96.       default/* can't happen */  
  97.         return -EINVAL;  
  98.     }  
  99.     if ((newpos<0) || (newpos>MEMDEV_SIZE))  
  100.         return -EINVAL;  
  101.           
  102.     filp->f_pos = newpos;  
  103.     return newpos;   
  104. }  
  105.    
  106. /*文件操作结构体*/  
  107. static const struct file_operations mem_fops =  
  108. {  
  109.   .owner = THIS_MODULE,  
  110.   .llseek = mem_llseek,  
  111.   .read = mem_read,  
  112.   .write = mem_write,  
  113.   .open = mem_open,  
  114.   .release = mem_release,  
  115. };  
  116.   
  117.   
  118. /*设备驱动模块加载函数*/  
  119. static int memdev_init(void)  
  120. {  
  121.   int result;  
  122.   int i;  
  123.   
  124.   
  125.   dev_t devno = MKDEV(mem_major, 0);  
  126.   
  127.   
  128.    /* 申请设备号,当xxx_major不为0时,表示静态指定;当为0时,表示动态申请*/   
  129.   /* 静态申请设备号*/  
  130.   if (mem_major)  
  131.     result = register_chrdev_region(devno, 2, "memdev");  
  132.   else  /* 动态分配设备号 */  
  133.   {  
  134.     result = alloc_chrdev_region(&devno, 0, 2, "memdev");  
  135.     mem_major = MAJOR(devno);    /*获得申请的主设备号*/  
  136.   }    
  137.     
  138.   if (result < 0)  
  139.     return result;  
  140.    
  141.  /*初始化cdev结构,并传递file_operations结构指针*/   
  142.   cdev_init(&cdev, &mem_fops);      
  143.   cdev.owner = THIS_MODULE;    /*指定所属模块*/  
  144.   cdev.ops = &mem_fops;  
  145.     
  146.   /* 注册字符设备 */  
  147.   cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);  
  148.      
  149.   /* 为设备描述结构分配内存*/  
  150.   mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);  
  151.   if (!mem_devp)    /*申请失败*/  
  152.   {  
  153.     result =  - ENOMEM;  
  154.     goto fail_malloc;  
  155.   }  
  156.   memset(mem_devp, 0, sizeof(struct mem_dev));  
  157.     
  158.   /*为设备分配内存*/  
  159.   for (i=0; i < MEMDEV_NR_DEVS; i++)   
  160.   {  
  161.         mem_devp[i].size = MEMDEV_SIZE;  
  162.         mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);  
  163.         memset(mem_devp[i].data, 0, MEMDEV_SIZE);  
  164.   }  
  165.       
  166.   return 0;  
  167.  
  168.   fail_malloc:   
  169.   unregister_chrdev_region(devno, 1);  
  170.     
  171.   return result;  
  172. }    
  173.   
  174. /*模块卸载函数*/  
  175. static void memdev_exit(void)  
  176. {  
  177.   cdev_del(&cdev);   /*注销设备*/  
  178.   kfree(mem_devp);     /*释放设备结构体内存*/  
  179.   unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/  
  180. }   
  181.   
  182. MODULE_AUTHOR("David Xie");  
  183. MODULE_LICENSE("GPL");  
  184.     
  185. module_init(memdev_init);  
  186. module_exit(memdev_exit); 

 (3)应用程序(测试文件):app-mem.c

  1. #include <stdio.h>  
  2.   
  3.   
  4. int main()  
  5. {  
  6.     FILE *fp0 = NULL;  
  7.     char Buf[4096];  
  8.       
  9.     /*初始化Buf*/  
  10.     strcpy(Buf,"Mem is char dev!");  
  11.     printf("BUF: %s\n",Buf);  
  12.       
  13.     /*打开设备文件*/  
  14.     fp0 = fopen("/dev/memdev0","r+");  
  15.     if (fp0 == NULL)  
  16.     {  
  17.         printf("Open Memdev0 Error!\n");  
  18.         return -1;  
  19.     }  
  20.       
  21.     /*写入设备*/  
  22.     fwrite(Buf, sizeof(Buf), 1, fp0);  
  23.       
  24.     /*重新定位文件位置(思考没有该指令,会有何后果)*/  
  25.     fseek(fp0,0,SEEK_SET);  
  26.       
  27.     /*清除Buf*/  
  28.     strcpy(Buf,"Buf is NULL!");  
  29.     printf("BUF: %s\n",Buf);  
  30.       
  31.       
  32.     /*读出设备*/  
  33.     fread(Buf, sizeof(Buf), 1, fp0);  
  34.       
  35.     /*检测结果*/  
  36.     printf("BUF: %s\n",Buf);  
  37.       
  38.     return 0;      
  39.   
  40.   

 

 

 

posted @ 2015-01-07 21:33  来杯绿茶  阅读(277)  评论(0编辑  收藏  举报