zoukankan      html  css  js  c++  java
  • linux之misc及使用misc创建字符设备

    1:linux字符设备及udev

         1.1字符设备

    字符设备就是:一个一个字节来进行访问的,不能对字符设备进行随机读写。简单字符设备创建实例如下:

    1. #include <linux/module.h>  
    2. #include <linux/kernel.h>  
    3. #include <linux/types.h>  
    4. #include <linux/fs.h>  
    5. #include <linux/init.h>  
    6. #include <linux/delay.h>  
    7. #include <asm/uaccess.h>  
    8. #include <asm/irq.h>  
    9. #include <asm/io.h>  
    10. #include <linux/cdev.h>  
    11. #include <linux/device.h>  
    12.   
    13. #define LED_MAJOR 241    /*预设的LED_MAJOR的主设备号*/  
    14. static int led_major = LED_MAJOR;  
    15. volatile unsigned long *gpbcon = NULL ;  
    16. volatile unsigned long *gpbdat = NULL ;    
    17. volatile unsigned long *gpbup = NULL ;  
    18.   
    19. struct cdev cdev; /*cdev结构体*/   
    20. static int led_drv_open(struct inode *inode, struct file *file)  
    21. {  
    22.     printk("first_drv_open ");  
    23.     return 0;  
    24. }  
    25.   
    26. static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)  
    27. {  
    28.     return 0;  
    29. }  
    30.   
    31. static struct file_operations led_drv_fops = {  
    32.     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */  
    33.     .open   =   led_drv_open,       
    34.     .write  =   led_drv_write,       
    35. };  
    36. /*初始化并注册cdev*/  
    37. static void led_setup_cdev(void)  
    38. {  
    39.   int err, devno = MKDEV(led_major, 0);//index 为从设备号  
    40.   cdev_init(&cdev, &led_drv_fops);  
    41.   cdev.owner = THIS_MODULE;  
    42.   cdev.ops = &led_drv_fops;  
    43.   err = cdev_add(&cdev, devno, 1);//devno 为第一个设备号,1为数量  
    44.   if (err)  
    45.     printk(KERN_NOTICE "Error %d adding", err);  
    46. }  
    47.   
    48. static int led_drv_init(void)  
    49. {  
    50.     int result;  
    51.     dev_t devno;  
    52.     devno=MKDEV(led_major,0);  
    53.     if(led_major)//静态申请设备号  
    54.         result=register_chrdev_region(devno,1,"led1_dev");  
    55.     else  
    56.     {  
    57.         result = alloc_chrdev_region(&devno,0,1,"led1_dev");//动态申请设备号  
    58.         led_major = MAJOR(devno);  
    59.     }  
    60.     if(result<0)  
    61.     {  
    62.         printk (KERN_WARNING "hello: can't get major number %d ", led_major);  
    63.         return result;  
    64.     }          
    65.     led_setup_cdev();      
    66.     return 0;     
    67. }  
    68.   
    69. static void led_drv_exit(void)  
    70. {  
    71.     cdev_del(&cdev); // 注销cdev  
    72.     unregister_chrdev_region(MKDEV(led_major,0),1);//释放设备号  
    73. }  
    74.   
    75. module_init(led_drv_init);  
    76. module_exit(led_drv_exit);  
    77. MODULE_LICENSE("GPL");  

    上面是一个字符设备的框架,没有实质性的东西,如:没有实现file_operations结构中的成员函数,同时led_drv_open(),led_drv_write()没有做任何东西。

    现在将创建一个基本的字符设备过程总结如下:

    C:初始化并关联file_operations结构体。  cdev_init(&cdev, &led_drv_fops);其中file_operations结构体中主要定义对字符设备的操作函数。

    D:添加字符设备到内核。int cdev_add(struct cdev *p, dev_t dev, unsigned count),

    E:移除字符设备及设备号。    cdev_del(&cdev); unregister_chrdev_region(MKDEV(led_major,0),1);完成于cdev_add()和register_chrdev_region()相反的工作

    上面涉及到的API可以在函数linux/fs/char_dev.c中找到定义。

    按照上面的方法可以创建一个主设备号为:241,次设备号为:0的一个字符设备。

    可以讲上面的代码编译进内核:.

    其方式如下:

    将上面的代码保存到test.c中

    在[root@centos /opt/FriendlyARM/linux-2.6.32.2/drivers]$ 目下下面建立文件件 mkdir suiyuan

    之后在suiyuan中创建makefile,将上面的代码直接编译进内核,因此可以不需要kconfig文件

    其makefile的内容为:

    obj-$(CONFIG_SUIYUAN_HELLOWORLD)+= suiyuan_hello_module.o
    #obj-$(CONFIG_STATIC_SUIYUAN_HELLOWORLD) += static_suiyuan_hello_module.o
    obj-m+= static_suiyuan_hello_module.o
    obj-y+= class_device_driver.o
    obj-y+= leds.o
    obj-y+= test.o
    #obj-y+= ledsv2.o
    #obj-y+= ledsv3.o

    表示将obj-y表示将test编译进内核,在加载内的时候将该字符设备加载进内核。

    同时在再修改/opt/FriendlyARM/linux-2.6.32.2/drivers目录下面的Makefile文件,在Makefile文件的末尾添加obj-y    += suiyuan/ 表示对suiyuan目录下面的所有文件进行编译。

    有上面可以知道kconfig文件费必须,同时只有在需要将内核编译为模块的时候,才需要编写配置文件kconfig。

    重新编译内核,将编译好的内核转化为uImage 格式。

    对于上面创建的字符设备在使用的时候需要,创建设备文件。创建设备文件使用命令:mknod

    mknod的使用方式如下:

    定义:mknod - make block or character special files

    语法:mknod /dev/ttyUSBn c Major Minor

    1,n要等于次设备号Minor,且要小于主设备号Major.

    2, c:面向字符设备(b:面向块设备,如:磁盘、软盘、磁带;其他设备基本都为字符设备).

    在上面的代码中,我们的主从设备号为:241和0。故创建设备节点:mknod /dev/suiyuan c 241 0

    之后可以[@mini2440 /dev]#cat test
    led_drv_open
    cat: read error: Invalid argument

    如上面的结果,表示应用程序cat在访问设备文件的时候,调用了file_operations 数据结构中的open()函数。应用程序和底层驱动就此联系了起来。

     1.2 udev是什么及其作用

     udev文件系统是针对2.6内核,提供一个基于用户空间的动态设备节点管理和命名的使用程序,udev 以守护进程的形式运行,通过侦听内核发出来的 uevent 来动态的管理/dev目录下的设备文件。:udev是一种工具,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。udev动态的根据根文件系统目录下面的/sys文件主动的创建设备文件,故在编写好设备驱动的时候,不需要再手动通过命令mknod来创建设备文件。udev涉及到/sys目录下面的文件,而/sys涉及到linux设备驱动模式。以后自己在总结这方面的东西。udev是一个用户程序(user-mode daemon)。

    现在就如何通过udev动态的创建/dev/目录下面的文件为例进行说明。

    1. #include <linux/module.h>  
    2. #include <linux/kernel.h>  
    3. #include <linux/types.h>  
    4. #include <linux/fs.h>  
    5. #include <linux/init.h>  
    6. #include <linux/delay.h>  
    7. #include <asm/uaccess.h>  
    8. #include <asm/irq.h>  
    9. #include <asm/io.h>  
    10. #include <linux/cdev.h>  
    11. #include <linux/device.h>  
    12. #include <asm/system.h>       /* cli(), *_flags */  
    13. #include <asm/uaccess.h>  /* copy_*_user */  
    14. #include <asm/io.h>  
    15. #include <linux/ioport.h>  
    16. #include <linux/device.h>      /* for sys mdev */  
    17.   
    18. #define LEDS_DEBUG  
    19. #undef PDEBUG             /* undef it, just in case */  
    20. #ifdef LEDS_DEBUG  
    21. #  ifdef __KERNEL__  
    22.      /* This one if debugging is on, and kernel space */  
    23. #    define PDEBUG(fmt, args...) printk( KERN_EMERG "leds: " fmt, ## args)  
    24. #  else  
    25.      /* This one for user space */  
    26. #    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)  
    27. #  endif  
    28. #else  
    29. #  define PDEBUG(fmt, args...) /* not debugging: nothing */  
    30. #endif  
    31.   
    32. #define LEDS_MAJOR 400  
    33. #define LEDON 0  
    34. #define LEDOFF 1  
    35. #define BUZON 2  
    36. #define BUZOFF 3  
    37. #define GPBCON 0x56000010  
    38.   
    39. struct leds_dev {  
    40.     unsigned char value;    /* When LED lighted, its value bit is 1, otherwise 0 */  
    41.     struct cdev cdev;  
    42. };  
    43.   
    44. static struct class *leds_class;  
    45. static struct leds_dev *leds_devp;  
    46. static int leds_major = LEDS_MAJOR;  
    47. static void *virtaddr;  
    48.   
    49. static int leds_open(struct inode *inode, struct file *filp)  
    50. {  
    51.     printk("file_operations:open ");  
    52.     return 0;  
    1. }  
    2. static int leds_release(struct inode *inode, struct file *filp)  
    3. {  
    4.     printk("file_operations:open ");  
    5.     return 0;  
    6. }  
    7.   
    8. static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  
    9. {  
    10.     printk("file_operations:read ");  
    11.     return 0;  
    12. }  
    13. static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)  
    14. {  
    15.     printk("file_operations:write ");  
    16.     return 0;  
    17. }  
    18. static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  
    19. {  
    20.     printk("file_operations:ioctl ");  
    21.    return 0;  
    22. }  
    23.       
    24. static struct file_operations leds_fops =  
    25. {  
    26.     .owner   = THIS_MODULE,  
    27.     .read    = leds_read,  
    28.     .ioctl   = leds_ioctl,  
    29.     .open    = leds_open,  
    30.     .release = leds_release,  
    31.     .write   = led_write  
    32. };  
    33.   
    34. static void leds_setup_cdev(struct leds_dev *dev, int index)  
    35. {  
    36.     int err, devno = MKDEV(leds_major, index);  
    37.     cdev_init(&dev->cdev, &leds_fops);  
    38.     dev->cdev.owner = THIS_MODULE;  
    39.     dev->cdev.ops = &leds_fops;  
    40.     err = cdev_add(&dev->cdev, devno, 1);  
    41.     if (err)  
    42.     {  
    43.         printk(KERN_NOTICE "Error adding LEDS ");  
    44.     }  
    45.           
    46. }  
    47.   
    48. static int leds_init(void)  
    49. {  
    50.     int result;  
    51.     dev_t devno = MKDEV(leds_major, 0);  
    52.       
    53.     PDEBUG("enter led_init ");  
    54.   
    55.     if (leds_major)  
    56.     {  
    57.         result = register_chrdev_region(devno, 1, "leds");  
    58.     }  
    59.     else   
    60.     {  
    61.         result = alloc_chrdev_region(&devno, 0, 10, "leds");  
    62.         leds_major = MAJOR(devno);  
    63.     }  
    64.     if (result < 0)  
    65.     {  
    66.        return result;  
    67.     }  
    68.     leds_devp = kmalloc(sizeof(struct leds_dev), GFP_KERNEL);  
    69.     memset(leds_devp, 0, sizeof(struct leds_dev));  
    70.     leds_setup_cdev(leds_devp, 0);  
    71.   
    72.     leds_class = class_create(THIS_MODULE, "suiyuan_leds_class");  
    73.     if (IS_ERR(leds_class))  
    74.     {  
    75.       printk(KERN_WARNING "failed in creating class ");  
    76.     }  
    77. //  class_device_create(leds_class, NULL, devno, NULL, "suiyuan_leds");//这个函数在较早的额内核可以使用  
    78.     device_create(leds_class, NULL, devno, NULL, "suiyuan_leds"); //高版本内核可以使用我的是2.6.32.2-FriendlyARM  
    79.    
    80.     printk("leds initialized ");  
    81.   return 0;  
    82. }  
    83.   
    84. void suiyuan_leds_cleanup(void)  
    85. {  
    86.     cdev_del(&leds_devp->cdev);  
    87.     kfree(leds_devp);  
    88.     unregister_chrdev_region(MKDEV(leds_major, 0), 1);  
    89.     //class_device_destroy(leds_class, MKDEV(leds_major, 0));  
    90.         device_destroy(leds_class, MKDEV(leds_major, 0));  
    91.     class_destroy(leds_class);  
    92.     printk("leds driver unloaded ");  
    93. }  
    94.   
    95. module_init(leds_init);  
    96. module_exit(suiyuan_leds_cleanup);  
    97.   
    98. MODULE_AUTHOR("suiyuan");  
    99. MODULE_LICENSE("Dual BSD/GPL");  

    上面的代码建立了一个简单字符的设备,但是在最后使用函数class_create()和device_create()用来分别在/sys和/dev目录下面创建相应的文件夹及文件。

    首先在:在/sys目录中

    [@mini2440 /sys/dev/char]#ls
    10:130  116:16  1:11    400:0  //是自己创建的设备文件

    [@mini2440 /sys/class]#ls
    bdi                 input               net                 sound
    block               leds                rtc                 suiyuan_leds_class 是上面的函数class_create()所创建
    class_suiyuan       mem                 scsi_device         tty
    firmware            misc                scsi_disk           vc
    graphics            mmc_host            scsi_generic        video4linux
    i2c-dev             mtd                 scsi_host           vtconsole

    在./dev目录下面:

    crw-rw----    1 0        0         400,   0 Feb 16 20:11 suiyuan_leds 是device_create()函数所创建的设备文件有对应的主从设备号

    在执行 cat

    [@mini2440 /dev]#cat suiyuan_leds
    file_operations:open
    file_operations:read
    file_operations:open

    表示调用了file_operations中的驱动函数,open()和read().

    现在在说说 udev.conf文件,现在创建/etc/udev.conf文件在其文件中添加

    如下类容:suiyuan_leds 0:5 0666 =MyTestLed #MyTestLed为设备suiyuan_leds的别名。在加载文件系统的时候,将执行udev.conf中的内容。

    之后重现加载rootfs 。此时的/dev/如下:

    [@mini2440 /dev]#ls -al
    total 4
    drwxr-xr-x    3 0        0                0 Feb 17 08:34 .
    drwxr-xr-x   16 0        0             4096 Feb 13 11:08 ..
    crw-rw-rw-    1 0        5         400,   0 Feb 17 08:34 MyTestLed

    同时也可以参考mini2440上面提供的udev.conf 文件中的内容。

    2:misc设备

     下面是一个misc设备的主要组成,结构相对明确。

    1. #include <linux/miscdevice.h>  
    2. #include <linux/delay.h>  
    3. #include <asm/irq.h>  
    4. #include <linux/kernel.h>  
    5. #include <linux/module.h>  
    6. #include <linux/init.h>  
    7. #include <linux/mm.h>  
    8. #include <linux/fs.h>  
    9. #include <linux/types.h>  
    10. #include <linux/delay.h>  
    11. #include <linux/moduleparam.h>  
    12. #include <linux/slab.h>  
    13. #include <linux/errno.h>  
    14. #include <linux/ioctl.h>  
    15. #include <linux/cdev.h>  
    16. #include <linux/string.h>  
    17. #include <linux/list.h>  
    18. #include <linux/pci.h>  
    19. #include <asm/uaccess.h>  
    20. #include <asm/atomic.h>  
    21. #include <asm/unistd.h>  
    22.   
    23. static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)  
    24. {  
    25.     printk ("misc  leds_ioctl ");  
    26.     return 0;  
    27. }  
    28.   
    29. static int leds_open(struct inode *inode, struct file *filp)  
    30. {  
    31.     printk ("misc  in leds_open ");  
    32.     return 0;  
    33. }  
    34. static int leds_release(struct inode *inode, struct file *filp)  
    35. {  
    36.     printk ("misc leds_release ");  
    37.     return 0;  
    38. }  
    39.   
    40. static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  
    41. {  
    42.       printk ("misc leds_read ");  
    43.       return 1;  
    44. }  
    45.   
    46. static struct file_operations leds_fops =  
    47. {  
    48.     .owner   = THIS_MODULE,  
    49.     .read    = leds_read,  
    50.     .ioctl   = leds_ioctl,  
    51.     .open    = leds_open,  
    52.     .release = leds_release  
    53. };  
    54.   
    55. static struct miscdevice misc = {  
    56.     .minor = MISC_DYNAMIC_MINOR,  
    57.     .name = "misc_leds", //此名称将显示在/dev目录下面  
    58.     .fops = &leds_fops,  
    59. };  
    60.   
    61. static int __init dev_init(void)  
    62. {  
    63.     int ret = misc_register(&misc);  
    64.     printk ("misc leds initialized ");  
    65.     return ret;  
    66. }  
    67.   
    68. static void __exit dev_exit(void)  
    69. {  
    70.     misc_deregister(&misc);  
    71.     printk("misc leds unloaded ");  
    72. }  
    73.   
    74. module_init(dev_init);  
    75. module_exit(dev_exit);  
    76. MODULE_LICENSE("GPL");  
    77. MODULE_AUTHOR("suiyuan");  


     

    将上面的代码保存为suiyuanMisc.c,并编译进内核。

    [root@centos /opt/FriendlyARM/linux-2.6.32.2]$make
      CHK     include/linux/version.h
    make[1]: `include/asm-arm/mach-types.h' is up to date.
      CHK     include/linux/utsrelease.h
      SYMLINK include/asm -> include/asm-arm
      CALL    scripts/checksyscalls.sh
      CHK     include/linux/compile.h
      CC      drivers/suiyuan/suiyuanMisc.o
      LD      drivers/suiyuan/built-in.o
      LD      drivers/built-in.o
      LD      vmlinux.o
      MODPOST vmlinux.o
    WARNING: vmlinux: 'ledoff' exported twice. Previous export was in vmlinux
    WARNING: vmlinux: 'ledon' exported twice. Previous export was in vmlinux
      GEN     .version
      CHK     include/linux/compile.h
      UPD     include/linux/compile.h
      CC      init/version.o
      LD      init/built-in.o
      LD      .tmp_vmlinux1
      KSYM    .tmp_kallsyms1.S
      AS      .tmp_kallsyms1.o
      LD      .tmp_vmlinux2
      KSYM    .tmp_kallsyms2.S
      AS      .tmp_kallsyms2.o
      LD      vmlinux
      SYSMAP  System.map
      SYSMAP  .tmp_System.map
      OBJCOPY arch/arm/boot/Image
      Kernel: arch/arm/boot/Image is ready
      GZIP    arch/arm/boot/compressed/piggy.gz
      AS      arch/arm/boot/compressed/piggy.o
      LD      arch/arm/boot/compressed/vmlinux
      OBJCOPY arch/arm/boot/zImage
      Kernel: arch/arm/boot/zImage is ready
      Building modules, stage 2.
      MODPOST 17 modules
    WARNING: vmlinux: 'ledoff' exported twice. Previous export was in vmlinux
    WARNING: vmlinux: 'ledon' exported twice. Previous export was in vmlinux

    将zImage转化为uImage,并通过nfs下载到内容,并启动。可以在启动的时候看到:misc leds initialized 信息。

    之后可以在mini2440的/dev/目录下面看到。

    有如下设备:crw-rw----    1 0        0          10,  56 Feb 18 14:44 misc_leds_suiyuan  主设备号为:10,次设备号为:56

    同时可以看看文件:/proc/misc,其文件中记载了系统中所加载misc设备。

     53 network_throughput
     54 network_latency
     55 cpu_dma_latency
     56 misc_leds_suiyuan
     57 leds_03
    130 watchdog
     58 camera
     59 adc
     60 pwm
     61 buttons
     62 leds
     63 backlight

    misc总结如下:

    misc字符设备,该类设备使用同一个主设备号10,misc字符设备使用的数据结构
    struct miscdevice {
    int minor;
    const char *name;
    struct file_operations *fops;
    struct list_head list;
    struct device *dev;
    struct class_device *class;
    char devfs_name[64];
    };

    杂项设备(misc device)
    在 Linux 内核的includelinuxmiscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主设备号10 ,一起归于misc device,其实misc_register就是用主设备号10调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备。

    misc_device是特殊的字符设备。注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_create()或者device_create()。

    因此使用misc创建字符设备最简单方便。

  • 相关阅读:
    WordPress“无法将上传的文件移动至wp-content/uploads/”的解决办法
    npm安装less和less-loadernpm或者stylus和stylus-loader
    vue-cli脚手架安装
    JavaScript 中的回调函数
    css同时满足两个类名才有效果的写法
    jQuery对象与JS原生对象之间的转换
    css3在动画完成后执行事件
    5秒让你的View变3D,ThreeDLayout使用和实现
    给大家安利一个学习angular2的视频网站
    JAVA中的常量定义在class中还是interface中比较合理?
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7703737.html
Copyright © 2011-2022 走看看