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.   
  • 相关阅读:
    diary and html 文本颜色编辑,行距和其它编辑总汇
    bash coding to changeNames
    virtualbox ubuntu 网络连接 以及 连接 secureCRT
    linux 学习6 软件包安装
    linux 学习8 权限管理
    vim 使用2 转载 为了打开方便
    ubuntu
    linux 学习15 16 启动管理,备份和恢复
    linux 学习 14 日志管理
    linux 学习 13 系统管理
  • 原文地址:https://www.cnblogs.com/liuchengchuxiao/p/4209465.html
Copyright © 2011-2022 走看看