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. #define LED_MAJOR 241    /*预设的LED_MAJOR的主设备号*/
  13. static int led_major = LED_MAJOR;
  14. volatile unsigned long *gpbcon = NULL ;
  15. volatile unsigned long *gpbdat = NULL ;
  16. volatile unsigned long *gpbup = NULL ;
  17. struct cdev cdev; /*cdev结构体*/
  18. static int led_drv_open(struct inode *inode, struct file *file)
  19. {
  20. printk("first_drv_open\n");
  21. return 0;
  22. }
  23. static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  24. {
  25. return 0;
  26. }
  27. static struct file_operations led_drv_fops = {
  28. .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  29. .open   =   led_drv_open,
  30. .write  =   led_drv_write,
  31. };
  32. /*初始化并注册cdev*/
  33. static void led_setup_cdev(void)
  34. {
  35. int err, devno = MKDEV(led_major, 0);//index 为从设备号
  36. cdev_init(&cdev, &led_drv_fops);
  37. cdev.owner = THIS_MODULE;
  38. cdev.ops = &led_drv_fops;
  39. err = cdev_add(&cdev, devno, 1);//devno 为第一个设备号,1为数量
  40. if (err)
  41. printk(KERN_NOTICE "Error %d adding", err);
  42. }
  43. static int led_drv_init(void)
  44. {
  45. int result;
  46. dev_t devno;
  47. devno=MKDEV(led_major,0);
  48. if(led_major)//静态申请设备号
  49. result=register_chrdev_region(devno,1,"led1_dev");
  50. else
  51. {
  52. result = alloc_chrdev_region(&devno,0,1,"led1_dev");//动态申请设备号
  53. led_major = MAJOR(devno);
  54. }
  55. if(result<0)
  56. {
  57. printk (KERN_WARNING "hello: can't get major number %d\n", led_major);
  58. return result;
  59. }
  60. led_setup_cdev();
  61. return 0;
  62. }
  63. static void led_drv_exit(void)
  64. {
  65. cdev_del(&cdev); // 注销cdev
  66. unregister_chrdev_region(MKDEV(led_major,0),1);//释放设备号
  67. }
  68. module_init(led_drv_init);
  69. module_exit(led_drv_exit);
  70. 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. #define LEDS_DEBUG
  18. #undef PDEBUG             /* undef it, just in case */
  19. #ifdef LEDS_DEBUG
  20. #  ifdef __KERNEL__
  21. /* This one if debugging is on, and kernel space */
  22. #    define PDEBUG(fmt, args...) printk( KERN_EMERG "leds: " fmt, ## args)
  23. #  else
  24. /* This one for user space */
  25. #    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
  26. #  endif
  27. #else
  28. #  define PDEBUG(fmt, args...) /* not debugging: nothing */
  29. #endif
  30. #define LEDS_MAJOR 400
  31. #define LEDON 0
  32. #define LEDOFF 1
  33. #define BUZON 2
  34. #define BUZOFF 3
  35. #define GPBCON 0x56000010
  36. struct leds_dev {
  37. unsigned char value;    /* When LED lighted, its value bit is 1, otherwise 0 */
  38. struct cdev cdev;
  39. };
  40. static struct class *leds_class;
  41. static struct leds_dev *leds_devp;
  42. static int leds_major = LEDS_MAJOR;
  43. static void *virtaddr;
  44. static int leds_open(struct inode *inode, struct file *filp)
  45. {
  46. printk("file_operations:open\n");
  47. return 0;
  1. }
  2. static int leds_release(struct inode *inode, struct file *filp)
  3. {
  4. printk("file_operations:open\n");
  5. return 0;
  6. }
  7. static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
  8. {
  9. printk("file_operations:read\n");
  10. return 0;
  11. }
  12. static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  13. {
  14. printk("file_operations:write\n");
  15. return 0;
  16. }
  17. static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  18. {
  19. printk("file_operations:ioctl\n");
  20. return 0;
  21. }
  22. static struct file_operations leds_fops =
  23. {
  24. .owner   = THIS_MODULE,
  25. .read    = leds_read,
  26. .ioctl   = leds_ioctl,
  27. .open    = leds_open,
  28. .release = leds_release,
  29. .write   = led_write
  30. };
  31. static void leds_setup_cdev(struct leds_dev *dev, int index)
  32. {
  33. int err, devno = MKDEV(leds_major, index);
  34. cdev_init(&dev->cdev, &leds_fops);
  35. dev->cdev.owner = THIS_MODULE;
  36. dev->cdev.ops = &leds_fops;
  37. err = cdev_add(&dev->cdev, devno, 1);
  38. if (err)
  39. {
  40. printk(KERN_NOTICE "Error adding LEDS\n");
  41. }
  42. }
  43. static int leds_init(void)
  44. {
  45. int result;
  46. dev_t devno = MKDEV(leds_major, 0);
  47. PDEBUG("enter led_init\n");
  48. if (leds_major)
  49. {
  50. result = register_chrdev_region(devno, 1, "leds");
  51. }
  52. else
  53. {
  54. result = alloc_chrdev_region(&devno, 0, 10, "leds");
  55. leds_major = MAJOR(devno);
  56. }
  57. if (result < 0)
  58. {
  59. return result;
  60. }
  61. leds_devp = kmalloc(sizeof(struct leds_dev), GFP_KERNEL);
  62. memset(leds_devp, 0, sizeof(struct leds_dev));
  63. leds_setup_cdev(leds_devp, 0);
  64. leds_class = class_create(THIS_MODULE, "suiyuan_leds_class");
  65. if (IS_ERR(leds_class))
  66. {
  67. printk(KERN_WARNING "failed in creating class\n");
  68. }
  69. //  class_device_create(leds_class, NULL, devno, NULL, "suiyuan_leds");//这个函数在较早的额内核可以使用
  70. device_create(leds_class, NULL, devno, NULL, "suiyuan_leds"); //高版本内核可以使用我的是2.6.32.2-FriendlyARM
  71. printk("leds initialized\n");
  72. return 0;
  73. }
  74. void suiyuan_leds_cleanup(void)
  75. {
  76. cdev_del(&leds_devp->cdev);
  77. kfree(leds_devp);
  78. unregister_chrdev_region(MKDEV(leds_major, 0), 1);
  79. //class_device_destroy(leds_class, MKDEV(leds_major, 0));
  80. device_destroy(leds_class, MKDEV(leds_major, 0));
  81. class_destroy(leds_class);
  82. printk("leds driver unloaded\n");
  83. }
  84. module_init(leds_init);
  85. module_exit(suiyuan_leds_cleanup);
  86. MODULE_AUTHOR("suiyuan");
  87. 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. static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  23. {
  24. printk ("misc  leds_ioctl\n");
  25. return 0;
  26. }
  27. static int leds_open(struct inode *inode, struct file *filp)
  28. {
  29. printk ("misc  in leds_open\n");
  30. return 0;
  31. }
  32. static int leds_release(struct inode *inode, struct file *filp)
  33. {
  34. printk ("misc leds_release\n");
  35. return 0;
  36. }
  37. static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
  38. {
  39. printk ("misc leds_read\n");
  40. return 1;
  41. }
  42. static struct file_operations leds_fops =
  43. {
  44. .owner   = THIS_MODULE,
  45. .read    = leds_read,
  46. .ioctl   = leds_ioctl,
  47. .open    = leds_open,
  48. .release = leds_release
  49. };
  50. static struct miscdevice misc = {
  51. .minor = MISC_DYNAMIC_MINOR,
  52. .name = "misc_leds", //此名称将显示在/dev目录下面
  53. .fops = &leds_fops,
  54. };
  55. static int __init dev_init(void)
  56. {
  57. int ret = misc_register(&misc);
  58. printk ("misc leds initialized\n");
  59. return ret;
  60. }
  61. static void __exit dev_exit(void)
  62. {
  63. misc_deregister(&misc);
  64. printk("misc leds unloaded\n");
  65. }
  66. module_init(dev_init);
  67. module_exit(dev_exit);
  68. MODULE_LICENSE("GPL");
  69. 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 内核的include\linux\miscdevice.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创建字符设备最简单方便。

linux之misc及使用misc创建字符设备的更多相关文章

  1. 自动创建字符设备,不需mknod

    自动创建设备文件 1.自动创建设备文件的流程 字符设备驱动模块 -->创建一个设备驱动class--->创建属于class的device--->调用mdev工具(自动完成)--> ...

  2. Linux驱动实践:你知道【字符设备驱动程序】的两种写法吗?

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...

  3. 嵌入式Linux驱动学习之路(二十七)字符设备驱动的另一种写法

    之前讲的字符设备驱动程序,只要有一个主设备号,那么次设备号无论是什么都会和同一个 struct file_operations 结构体对应. 而本节课讲的是如何在设备号相同的情况下,让不同的次设备号对 ...

  4. 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入

    字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申 ...

  5. 跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架

    misc子系统在Linux中是一个非常简单的子系统,但是其清晰的框架结构非常适合用来研究设备识别模型.本文从misc子系统的使用出发,通过了解其机制来总结一套的设备识别的驱动框架,即使用使用同一个驱动 ...

  6. Linux 设备驱动之字符设备

    参考转载博客:http://blog.chinaunix.net/uid-26833883-id-4369060.html https://www.cnblogs.com/xiaojiang1025/ ...

  7. Linux驱动开发2——字符设备驱动

    1.申请设备号 #include <linux/fs.h> int register_chrdev_region(dev_t first, unsigned int count, char ...

  8. Linux 字符设备驱动模型

    一.使用字符设备驱动程序 1. 编译/安装驱动 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块 2. 创建设备文件 通 ...

  9. Linux 简单字符设备驱动程序 (自顶向下)

    第零章:扯扯淡 特此总结一下写的一个简单字符设备驱动程序的过程,我要强调一下“自顶向下”这个介绍方法,因为我觉得这样更容易让没有接触过设备驱动程序的童鞋更容易理解,“自顶向下”最初从<计算机网络 ...

随机推荐

  1. Vue.js—组件快速入门及Vue路由实例应用

    上次我们学习了Vue.js的基础,并且通过综合的小实例进一步的熟悉了Vue.js的基础应用.今天我们就继续讲讲Vue.js的组件,更加深入的了解Vue,js的使用.首先我们先了解一下什么是Vue.js ...

  2. 深入理解String.intern()方法

    首先进入intern()的源码中, 首先说一点:1.7后的JVM为String在方法区中开辟了一个字符串常量池,如果一个String()不是new()出来的,都将在常量池中拿字符. 注释翻译过来就是, ...

  3. 安装 Apache 源代码包

    把自己在网易博客的文章迁移过来 cd /lamp/httpd-2.2.9 ./configure --prefix=/usr/local/apache2/ --sysconfdir=/usr/loca ...

  4. Thinkphp命名规范

    1.类文件都是以.class.php为后缀(这里是指的ThinkPHP内部使用的类库文件,不代表外部加载的类库文件),使用驼峰法命名,并且首字母大写,例如 DbMysql.class.php: 2.类 ...

  5. 洛谷P2278 [HNOI2003] 操作系统

    题目传送门 分析:题目中提到了优先级,很显然这题要用优先队列+模拟.题目中很多细节需要注意,还是在代码中解释吧,这里我用的是手打的堆. Code: #include<bits/stdc++.h& ...

  6. 并发系列5-大白话聊聊Java并发面试问题之微服务注册中心的读写锁优化【石杉的架构笔记】

  7. Sqli-labs less 4

    Less-4 我们使用?id=1" 注入代码后,我们得到像这样的一个错误: You have an error in your SQL syntax; check the manual th ...

  8. If you want to allow applications containing errors to be published on the server

    If you want to allow applications containing errors to be published on the server, enable the Allow ...

  9. 密码统计分析工具pipal

    密码统计分析工具pipal   pipal是Kali Linux提供的一款密码统计分析工具.该工具可以对一个密码字典的所有密码进行统计分析.它会统计最常用的密码.最常用的基础词语.密码长度占比.构成字 ...

  10. mysql 删除表记录 delete和truncate table区别

    MySQL中删除表记录delete from和truncate table的用法区别: mysql中有两种删除表中记录的方法: (1)delete from语句, (2)truncate table语 ...