zoukankan      html  css  js  c++  java
  • 字符设备驱动

    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 ", 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 ", 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 ",Buf);  
    12.       
    13.     /*打开设备文件*/  
    14.     fp0 = fopen("/dev/memdev0","r+");  
    15.     if (fp0 == NULL)  
    16.     {  
    17.         printf("Open Memdev0 Error! ");  
    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 ",Buf);  
    30.       
    31.       
    32.     /*读出设备*/  
    33.     fread(Buf, sizeof(Buf), 1, fp0);  
    34.       
    35.     /*检测结果*/  
    36.     printf("BUF: %s ",Buf);  
    37.       
    38.     return 0;      
    39.   
    40.   
  • 相关阅读:
    HDOJ1251解题报告【字典树】
    HDOJ1305解题报告【字典树】
    HDOJ1087解题报告【动态规划】
    HDOJ1075解题报告【STL】
    HDOJ1172解题报告【暴力】
    ibatis 中调用存储过程
    后端开挂:3行代码写出8个接口!
    Go模拟浏览器登录操作代码
    Java架构师必须知道的 6 大设计原则
    easyUI时间戳转换(3种解决方法)
  • 原文地址:https://www.cnblogs.com/liuchengchuxiao/p/4209465.html
Copyright © 2011-2022 走看看