字符驱动学习

原创 2016年08月31日 12:10:22
    写完第一个hello world 的驱动以后,想编写一个比较完整的字符设备驱动,就把《linux 设备驱动开发详解》上的globalmem驱动写了一下,写完以后,发现了几个问题,没有自动生成设备节点,在多设备的支持中,两个设备的关系不明确。搜集了一下资料,编写了一个能够自动生成设备节点的字符驱动。
   在字符驱动中,内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。这三个函数都会调用一个共用的 __register_chrdev_region() 函数来注册一组设备编号范围(即一个char_device_struct 结构)。

register_chrdev_region(dev_t first,unsigned int count,char *name) 
First :要分配的设备编号范围的初始值(次设备号常设为0); 
Count:连续编号范围. 
Name:编号相关联的设备名称. (/proc/devices); 

动态分配: 
Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name); 
Firstminor : 通常为0; 
*dev:存放返回的设备号; 

char_device_struct 结构体:

  static struct char_device_struct {
       struct char_device_struct *next;    // 指向散列冲突链表中的下一个元素的指针
       unsigned int major;                         // 主设备号
       unsigned int baseminor;                // 起始次设备号
       int minorct;                                      // 设备编号的范围大小
       char name[64];                               // 处理该设备编号范围内的设备驱动的名称
       struct file_operations *fops;          // 没有使用
       struct cdev *cdev;                          // 指向字符设备驱动程序描述符的指针
   } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

使用udev自动生成设备节点的函数:

class_create(…)在/drivers/base/class.c中实现:

  /**

  * class_create - create a struct class structure

  * @owner: pointer to the module that is to "own" this struct class

  * @nAME: pointer to a string for the name of this class.

  *

  * This is used to create a struct class pointer that can then be used

  * in calls to device_create().

  *

  * Note, the pointer created here is to be destroyed when finished by

  * making a call to class_destroy().

  */

struct class *class_create(struct module *owner, cONst char *name)

第一个参数指定类的所有者是哪个模块,第二个参数指定类名。

device_create(…)函数在/drivers/base/core.c中实现:
  /**
  * device_create - creates a device and registers it with sysfs
  * @class: pointer to the struct class that this device should be registered to
  * @parent: pointer to the parent struct device of this new device, if any
  * @devt: the dev_t for the char device to be added
  * @fmt: string for the device's name
  *
  * This funcTIon can be used by char device classes. A struct device
  * will be created in sysfs, registered to the specified class.
  *
  * A "dev" file will be created, showing the dev_t for the device, if
  * the dev_t is not 0,0.
  * If a pointer to a parent struct device is passed in, the newly created
  * struct device will be a child of that device in sysfs.
  * The pointer to the struct device will be returned from the call.
  * Any further sysfs files that might be required can be created using this
  * pointer.
  *
  * Note: the struct class passed to this function must have previously
  * been created with a call to class_create().
  */
struct device *device_create(struct class *class, struct device *parent,  dev_t devt, const char *fmt, ...)
第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是设备名称,第五个参数是从设备号。

下面的代码就是支持两个设备节点,但是两个设备节点支持同一个设备。
  • static struct class *cdev_class;
  • static int __init gm_cdev_init(void)
  • {
  •     int result;
  •     dev_t devno = MKDEV(MAJOR_NR, 0);

  •     if(!= MAJOR_NR){
  •         result = register_chrdev_region(devno, 2, DEV_NAME);  //注册设备 支持两个设备,此设备号从0开始
  •     } else {
  •         result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
  •         MAJOR_NR = MAJOR(devno);
  •     }

  •     printk(KERN_CRIT"hello\n");
  •     if(result < 0)
  •         return result;

  •     gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
  •     if(NULL == gm_cdevp){
  •         unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
  •         result = -ENOMEM;
  •     }

  •     memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
  •     gm_setup_cdev(&gm_cdevp[0], 0);
  •     gm_setup_cdev(&gm_cdevp[0], 1);
  •     //gm_setup_cdev(&gm_cdevp[1], 1);                                        //使用这一行的话支持两个设备

  •     cdev_class = class_create(THIS_MODULE, DEV_NAME);
  •     if(IS_ERR(cdev_class))
  •         return PTR_ERR(cdev_class);

  •     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0); //生成 /dev/gmem0 节点
  •     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1)//生成 /dev/gmem1 节点
  •     return 0;
  • }

  • void gm_cdev_exit(void)
  • {
  •     cdev_del(&gm_cdevp[0].cdev);                    
  •     cdev_del(&gm_cdevp[0].cdev);

  •     device_destroy(cdev_class, MKDEV(MAJOR_NR, 0));              //delete device node under /dev
  •     device_destroy(cdev_class, MKDEV(MAJOR_NR, 1));              //delete device node under /dev
  •     class_destroy(cdev_class);                                   //delete class created by us

  •     kfree(gm_cdevp);
  •     unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
  • }

  • root@wang:/work/wanghuan/drives# ls
    cdev.c  cdev.ko  hello.c  Makefile  modules.order
    root@wang:/work/wanghuan/drives# insmod cdev.ko 
    root@wang:/work/wanghuan/drives# lsmod | grep cdev
    cdev                    1853  0 
    root@wang:/work/wanghuan/drives# ls /dev/gmem* -l
    crw------- 1 root root 250, 0 2011-06-12 14:00 /dev/gmem0
    crw------- 1 root root 250, 1 2011-06-12 14:00 /dev/gmem1
    root@wang:/work/wanghuan/drives# ls > /dev/gmem0 
    root@wang:/work/wanghuan/drives# cat /dev/gmem1
    cdev.c
    cdev.ko
    hello.c
    Makefile
    modules.order


    下面的代码就是支持两个设备节点,但是两个设备节点支持不同的设备。
  • static struct class *cdev_class;  
  • static int __init gm_cdev_init(void)
  • {
  •     int result;
  •     dev_t devno = MKDEV(MAJOR_NR, 0);

  •     if(!= MAJOR_NR){
  •         result = register_chrdev_region(devno, 2, DEV_NAME);  //注册设备 支持两个设备,此设备号从0开始
  •     } else {
  •         result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
  •         MAJOR_NR = MAJOR(devno);
  •     }

  •     printk(KERN_CRIT"hello\n");
  •     if(result < 0)
  •         return result;

  •     gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
  •     if(NULL == gm_cdevp){
  •         unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
  •         result = -ENOMEM;
  •     }

  •     memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
  •     gm_setup_cdev(&gm_cdevp[0], 0);
  •     //gm_setup_cdev(&gm_cdevp[0], 1);
  •     gm_setup_cdev(&gm_cdevp[1], 1);                                        //使用这一行的话支持两个设备

  •     cdev_class = class_create(THIS_MODULE, DEV_NAME);
  •     if(IS_ERR(cdev_class))
  •         return PTR_ERR(cdev_class);

  •     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0); //生成 /dev/gmem0 节点
  •     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1)//生成 /dev/gmem1 节点
  •     return 0;
  • }

  • void gm_cdev_exit(void)
  • {
  •     cdev_del(&gm_cdevp[0].cdev);                    
  •     cdev_del(&gm_cdevp[1].cdev);

  •     device_destroy(cdev_class, MKDEV(MAJOR_NR, 0));              //delete device node under /dev
  •     device_destroy(cdev_class, MKDEV(MAJOR_NR, 1));              //delete device node under /dev
  •     class_destroy(cdev_class);                                   //delete class created by us

  •     kfree(gm_cdevp);
  •     unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
  • }
  • root@wang:/work/wanghuan/drives# ls
  • cdev.c  cdev.ko  hello.c  Makefile  modules.order
  • root@wang:/work/wanghuan/drives# insmod cdev.ko 
  • root@wang:/work/wanghuan/drives# lsmod | grep cdev
  • cdev                    1853  0 
  • root@wang:/work/wanghuan/drives# ls /dev/gmem* -l
  • crw------- 1 root root 250, 0 2011-06-12 14:08 /dev/gmem0
  • crw------- 1 root root 250, 1 2011-06-12 14:08 /dev/gmem1
  • root@wang:/work/wanghuan/drives# ls > /dev/gmem0
  • root@wang:/work/wanghuan/drives# cat /dev/gmem1
  • root@wang:/work/wanghuan/drives# cat /dev/gmem0
  • cdev.c
  • cdev.ko
  • hello.c
  • Makefile
  • modules.order

    1. /**
    2.  * =====================================================================================
    3.  * Filename: cdev.c
    4.  * Description: 字符设备驱动模型
    5.  * Version: 1.0
    6.  * Created: 2011年06月02日 19时27分50秒
    7.  * Revision: none
    8.  * Compiler: gcc
    9.  *
    10.  * Author: wanghuan,
    11.  * Company:
    12.  * =====================================================================================
    13.  */

    14. #include <linux/module.h>
    15. #include <linux/types.h>
    16. #include <linux/fs.h>         /* file_operation */
    17. #include <linux/errno.h>     /* Error number */
    18. #include <linux/mm.h>    
    19. #include <linux/sched.h>    
    20. #include <linux/slab.h>    
    21. #include <linux/init.h>     /* __init __exit */
    22. #include <linux/device.h>
    23. #include <linux/cdev.h>    
    24. #include <asm/io.h>    
    25. #include <asm/system.h>    
    26. #include <asm/uaccess.h>     /* copy_to_user, copy_from_user */
    27. #include <linux/kernel.h>     /* printk() */

    28. #define GLOBALMEM_SIZE 0x1000
    29. #define MEM_CLEAN 0x1
    30. #define DEV_NAME "gmem"
    31. #define MEM_CLEAR _IO(GLOBALMEM_SIZE, 0)

    32. static int MAJOR_NR = 250;        /* Driver Major Number */
    33. //static int MINOR_NR = 0;        /* Driver Major Number */

    34. struct gm_cdev_t{
    35.     struct cdev cdev;
    36.     unsigned char mem[GLOBALMEM_SIZE];
    37. };
    38. static struct gm_cdev_t *gm_cdevp;

    39. static int gm_open(struct inode *inode, struct file *filp)
    40. {
    41.     struct gm_cdev_t *dev;
    42.     dev = container_of(inode->i_cdev, struct gm_cdev_t, cdev);
    43.     filp->private_data = dev;
    44.     return 0;
    45. }
    46. static int gm_release(struct inode *inode, struct file *filp)
    47. {
    48.     filp->private_data = NULL;
    49.     return 0;
    50. }
    51. static ssize_t gm_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
    52. {
    53.     unsigned long p = *fpos;
    54.     int ret = 0;
    55.     
    56.     struct gm_cdev_t *dev = filp->private_data;
    57.     if(p >= GLOBALMEM_SIZE)
    58.         return 0;

    59.     if(count > GLOBALMEM_SIZE - p)
    60.         count = GLOBALMEM_SIZE -p;

    61.     if(copy_to_user(buf, (void*)(dev->mem) + p, count))
    62.         ret = -EFAULT;
    63.     else{
    64.         *fpos +=count;
    65.         ret =count;

    66.         printk(KERN_INFO "read %d butes from %ld \n", count, p);
    67.     }

    68.     return ret;
    69. }

    70. static ssize_t gm_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
    71. {
    72.     unsigned long p = *fpos;
    73.     int ret = 0;

    74.     struct gm_cdev_t *dev = filp->private_data;
    75.     if(p >= GLOBALMEM_SIZE)
    76.      return 0;
    77.     
    78.     if(p >= GLOBALMEM_SIZE - p)
    79.         count = GLOBALMEM_SIZE - p;

    80.     if(copy_from_user(dev->mem + p, buf, count))
    81.         ret = - EFAULT;
    82.     else{
    83.         *fpos +=count;
    84.         ret = count;

    85.         printk(KERN_INFO "write %d butes from %ld \n", count, p);
    86.     }

    87.     return ret;
    88. }

    89. static loff_t gm_llseek(struct file *filp, loff_t offset, int orig)
    90. {
    91.     loff_t ret;
    92.     switch(orig){
    93.         case 0:
    94.             if(offset < 0){
    95.                 ret = -EFAULT;
    96.                 break;
    97.             }
    98.             if((unsigned int)offset > GLOBALMEM_SIZE){
    99.                 ret = -EFAULT;
    100.                 break;
    101.             }

    102.             filp->f_pos = (unsigned int)offset;
    103.             ret = filp->f_pos;
    104.             break;
    105.         case 1:
    106.             if(filp->f_pos + offset > GLOBALMEM_SIZE){
    107.                 ret = -EFAULT;
    108.                 break;
    109.             }

    110.             if(filp->f_pos + offset < 0){
    111.                 ret = -EFAULT;
    112.                 break;
    113.             }

    114.             filp->f_pos += offset;
    115.             ret = filp->f_pos;
    116.             break;
    117.         default:
    118.             ret = -EFAULT;
    119.     }
    120.     return ret;
    121. }

    122. static int gm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
    123. {
    124.     struct gm_cdev_t *dev = filp->private_data;
    125.     switch(cmd){
    126.         case MEM_CLEAR:
    127.             memset(dev->mem, 0, GLOBALMEM_SIZE);
    128.             printk(KERN_INFO "globalmem is set to zero \n");
    129.             break;
    130.         default:
    131.             return -EINVAL;
    132.     }

    133.     return 0;
    134. }

    135. static const struct file_operations gm_fops = {
    136.     .owner = THIS_MODULE,
    137.     .open = gm_open,
    138.     .release = gm_release,
    139.     .read = gm_read,
    140.     .write = gm_write,
    141.     .ioctl = gm_ioctl,
    142.     .llseek = gm_llseek,
    143. };

    144. static void gm_setup_cdev(struct gm_cdev_t *gm_cdev, int minor)
    145. {
    146.     int err;
    147.     dev_t devno = MKDEV(MAJOR_NR, minor);
    148.     
    149.     cdev_init(&gm_cdev->cdev, &gm_fops);
    150.     gm_cdevp->cdev.owner = THIS_MODULE;
    151.     err = cdev_add(&gm_cdev->cdev, devno, 1);
    152.     if(err != 0)
    153.         printk(KERN_NOTICE "Error %d adding gmen", err);
    154. }

    155. static struct class *cdev_class;
    156. static int __init gm_cdev_init(void)
    157. {
    158.     int result;
    159.     dev_t devno = MKDEV(MAJOR_NR, 0);

    160.     if(0 != MAJOR_NR){
    161.         result = register_chrdev_region(devno, 2, DEV_NAME); //注册设备 支持两个设备,此设备号从0开始
    162.     } else {
    163.         result = alloc_chrdev_region(&devno, 0, 2, DEV_NAME);
    164.         MAJOR_NR = MAJOR(devno);
    165.     }

    166.     printk(KERN_CRIT"hello\n");
    167.     if(result < 0)
    168.         return result;

    169.     gm_cdevp = kmalloc(2*sizeof(struct gm_cdev_t), GFP_KERNEL);
    170.     if(NULL == gm_cdevp){
    171.         unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2);
    172.         result = -ENOMEM;
    173.     }

    174.     memset(gm_cdevp, 0, 2*sizeof(struct gm_cdev_t));
    175.     gm_setup_cdev(&gm_cdevp[0], 0);
    176.     //gm_setup_cdev(&gm_cdevp[0], 1);
    177.     gm_setup_cdev(&gm_cdevp[1], 1);

    178.     cdev_class = class_create(THIS_MODULE, DEV_NAME);
    179.     if(IS_ERR(cdev_class))
    180.         return PTR_ERR(cdev_class);

    181.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 0),    NULL, DEV_NAME"%d", 0);
    182.     device_create(cdev_class, NULL, MKDEV(MAJOR_NR, 1),    NULL, DEV_NAME"%d", 1);
    183.     return 0;
    184. }

    185. void gm_cdev_exit(void)
    186. {
    187.     cdev_del(&gm_cdevp[0].cdev);
    188.     cdev_del(&gm_cdevp[1].cdev);

    189.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 0)); //delete device node under /dev
    190.     device_destroy(cdev_class, MKDEV(MAJOR_NR, 1)); //delete device node under /dev
    191.     class_destroy(cdev_class); //delete class created by us

    192.     kfree(gm_cdevp);
    193.     unregister_chrdev_region(MKDEV(MAJOR_NR, 0), 2); //由于注册了两个设备,最后一个参数为 2
    194. }

    195. module_init(gm_cdev_init);
    196. module_exit(gm_cdev_exit);
    197. MODULE_LICENSE("GPL");
    198. MODULE_AUTHOR("wanghuan");
  • <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
    阅读(379) | 评论(0) | 转发(1) |
    0

    上一篇:ubuntu 内核树

    下一篇:RT3070 STA 驱动移植

    相关热门文章
    给主人留下些什么吧!~~
    评论热议
    版权声明:本文为博主原创文章,转载请注明出处。

    字符设备驱动学习笔记(2.6.23)

    • 2010年04月02日 23:02
    • 16KB
    • 下载

    跟着韦东山老师学习嵌入式----字符设备驱动程序之poll机制

    int poll(struct pollfd *fds,nfds_t nfds, int timeout); 总的来说,Poll机制会判断fds中的文件是否可读,如果可读则会立即返回,返回的值就...

    关于虚拟字符设备驱动的学习笔记globalmem

    1. linux设备驱动程序之简单字符设备驱动开发 2. 一个简单的Linux驱动程序和Makefile 3. “: asm/uaccess.h: 没有那个文件或目录”原因和解决办法 4. “错误: ...

    linux字符设备驱动-重新学习-笔记-2

    linux驱动学习笔记

    ok6410学习笔记(5.阻塞字符驱动)

    本节难点: 1.重点在理解阻塞型设备驱动和内核信号量的区别,详细在内核信号量那节有说明。 2.理解本节的几个重要函数,驱动的结构。 本节知识点:  细节知识点: 1.在判断是否阻塞进程的时候...

    ldd3学习之十二(2):高级字符驱动程序操作--等待队列,阻塞I/O,休眠

    在应用程序调用read,write时,若驱动程序无法立即满足要求,该如何响应?驱动程序应该(默认)阻塞该进程,将其置入休眠状态直到请求可继续。1.休眠进程被置为休眠,意味着它被标识为处于一个特殊的状态...

    学习Ldd3--字符设备驱动(第三章)

    作者:张伟AreS /*********************************************************************/ 代码:D:\学习\个人学习笔记及...

    Linux字符驱动开发学习总结

    linux驱动编写(虚拟字符设备编写)     昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,今天我们就可以了解一下相关方面的内容,并且用一个实例来说明在linux上面...

    ldd3学习之三: 字符驱动

    通过介绍字符设备scull(Simple Character Utility for Loading Localities,区域装载的简单字符工具)的驱动程序编写,来学习Linux设备驱动的基本知识。...

    linux学习--字符设备驱动

    linux驱动有基本的接口进行注册和删除,这里不再做详细说明,本文主要关注linux字符设备驱动框架实现的细节。 1.字符设备驱动抽象结构 字符设备驱动管理的核心对象是字符设备,从字符设备驱动程序...
    内容举报
    返回顶部
    收藏助手
    不良信息举报
    您举报文章:字符驱动学习
    举报原因:
    原因补充:

    (最多只允许输入30个字)