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创建字符设备最简单方便。

  • 相关阅读:
    容器跨主机网络通信学习笔记(以Flannel为例)
    Kubernetes控制器Job和CronJob
    记一次使用Flannel插件排错历程
    Kubernetes控制器Deployment
    Kubernetes如何通过StatefulSet支持有状态应用?
    react18 来了,我 get 到...
    gojs 实用高级用法
    vuecli3 vue2 保留 webpack 支持 vite 成功实践
    calibre 报错 This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem. 解决
    unable to recognize "*.yaml": no matches for kind "RoleBinding" in version "rbac.authorization.k8s.io/v1beta1"
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/7703737.html
Copyright © 2011-2022 走看看