Linux设备管理文件系统,mdev,热插拔

时间:2022-07-21 11:16:35

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/05/15/6422862.aspx

每次写驱动都要手动创建设备文件过于麻烦,使用设备管理文件系统则方便很多。在2.6的内核以前一直使用的是devfs,但是它存在许多缺陷。它创建了大量的设备文件,其实这些设备更本不存在。而且设备与设备文件的映射具有不确定性,比如U盘即可能对应sda,又可能对应sdb。没有足够的主/辅设备号。2.6之后的内核引入了sysfs文件系统,它挂载在/sys上,配合udev使用,可以很好的完成devfs的功能,并弥补了那些缺点。(这里说一下,当今内核已经使用netlink了,由于我才疏学浅这里暂不介绍)。
udev是用户空间的一个应用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精简版。
首先在busybox中添加支持mdev的选项:

C-sharp代码 
  1. Linux System Utilities --->   
  2.     [*] mdev   
  3.     [*]   Support /etc/mdev.conf   
  4.     [*]     Support subdirs/symlinks   
  5.     [*]       Support regular expressions substitutions when renaming device   
  6.     [*]     Support command execution at device addition/removal  

最后修改/etc/fstab:

C-sharp代码 
  1. # device  mount-point     type      options    dump    fsck order  
  2. #----------------------------------------------------------------  
  3. procfs    /proc           proc      defaults    0      0  
  4. sysfs     /sys            sysfs     defaults    0      0  
  5. tmpfs     /dev/shm        tmpfs     defaults    0      0  
  6. usbfs     /proc/bus/usb   usbfs     defaults    0      0  
  7. ramfs     /dev            ramfs     defaults    0      0  
  8. none      /dev/pts        devpts    mode=0622   0      0  

然后修改/etc/init.d/rcS:

C-sharp代码 
  1. # Mount virtual filesystem  
  2. /bin/mount -t     proc     procfs    /proc  
  3. /bin/mount -n -t  sysfs    sysfs     /sys  
  4. /bin/mount -n -t  usbfs    usbfs     /proc/bus/usb  
  5. /bin/mount -t     ramfs    ramfs     /dev  
  6.  
  7. # Make dir  
  8. /bin/mkdir -p /dev/pts  
  9. /bin/mkdir -p /dev/shm  
  10. /bin/mkdir -p /var/log  
  11. /bin/mount -n -t devpts none     /dev/pts -o mode=0622  
  12. /bin/mount -n -t tmpfs tmpfs     /dev/shm  
  13.  
  14. # Make device node  
  15. echo /sbin/mdev > /proc/sys/kernel/hotplug  
  16. /sbin/mdev -s  

重新打包文件系统,这样/sys目录,/dev目录就有东西了。

Linux设备管理文件系统,mdev,热插拔

如下是我的PWM驱动程序的初始化函数。调用create_class在/sys目录下创建类,调用device_create在/dev目录下创建设备节点。
下面是create_class的原型:

C-sharp代码 
  1. /* This is a #define to keep the compiler from merging different 
  2.  * instances of the __key variable */  
  3. #define class_create(owner, name)       \  
  4. ({                      \  
  5.     static struct lock_class_key __key; \  
  6.     __class_create(owner, name, &__key);    \  
  7. })  
  8. extern struct class * __must_check __class_create(struct module *owner,  
  9.                           const char *name,  
  10.                           struct lock_class_key *key);  

class_destroy的原型如下:

C-sharp代码 
  1. extern void class_destroy(struct class *cls);  

device_create的原型如下:

C-sharp代码 
  1. extern struct device *device_create(struct class *cls, struct device *parent,  
  2.                     dev_t devt, void *drvdata,  
  3.                     const char *fmt, ...)  
  4.                     __attribute__((format(printf, 5, 6)));  

device_destroy的原型如下:

C-sharp代码 
  1. extern void device_destroy(struct class *cls, dev_t devt);  

现在简单说一下mdev的基本原理:
执行mdev -s
以'-s'为参数调用位于/sbin目录下的mdev(其实是个链接,作用是传递参数给/bin目录下的busybox程序并调用它),mdev扫描/sys/class和/sys/block中所有的类设备目录,如果在目录中含有名为“dev”的文件,且文件中包含的是设备号,则mdev就利用这些为这个设备在/dev下创建设备节点文件。一般只在启动时才执行一次“mdev -s”;
热插拔事件:由于启动运行了命令:echo /sbin/mdev > proc/sys/kernel/hotplug,那么当有热插拔事件产生时,内核就会调用位于/sbin目录的mdev。这时mdev通过环境变量中的ACTION和DEVPATH,(这两个变量是系统自带的)来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否有"dev"的属性文件,如果有就利用这些信息为这个设备在/dev下创建设备节点文件。

下边是我写的PWM程序:

C-sharp代码 
  1. #include <linux/init.h>  
  2. #include <linux/module.h>  
  3. #include <mach/hardware.h>  
  4. #include <asm/io.h>  
  5. #include <plat/regs-timer.h>  
  6. #include <linux/clk.h>  
  7. #include <linux/device.h>  
  8. #include <linux/cdev.h>  
  9. #include <linux/err.h>  
  10. #include <linux/fs.h>  
  11. #include <mach/regs-gpio.h>  
  12. int MYPWM_MAJOR = 0;  
  13. int MYPWM_MINOR = 0;  
  14. #define MYPWM_NAME "lwp-bell"  
  15. dev_t dev_num;  
  16. struct class *pwm;  
  17. struct cdev *pwm_cdev;  
  18.   
  19. /*打开设备*/  
  20. int mypwm_open(struct inode*inode, struct file*filp){  
  21.     printk("/dev/lwp-bell is open\n");  
  22.     return 0;  
  23. }  
  24.   
  25. int mypwm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){  
  26.   
  27.     if(cmd == 0){  
  28.         s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);  
  29.         s3c2410_gpio_setpin(S3C2410_GPB0, 0);  
  30.     }  
  31.     else{  
  32.         unsigned long tcon;  
  33.         unsigned long tcnt;  
  34.         unsigned long tcfg0;  
  35.         unsigned long tcfg1;  
  36.         struct clk *clk_p;  
  37.         unsigned long pclk;  
  38.         s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0);  
  39.         tcfg0 = __raw_readl(S3C2410_TCFG0);  
  40.         tcfg1 = __raw_readl(S3C2410_TCFG1);  
  41.         tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;  
  42.         tcfg0 |= (50-1);  
  43.         tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;  
  44.         tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;  
  45.         __raw_writel(tcfg0, S3C2410_TCFG0);  
  46.         __raw_writel(tcfg1, S3C2410_TCFG1);  
  47.         clk_p = clk_get(NULL,"pclk");  
  48.         /*从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义*/  
  49.         pclk = clk_get_rate(clk_p);  
  50.         /*计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)*/  
  51.         tcnt = pclk/50/16/cmd;  
  52.         __raw_writel(tcnt, S3C2410_TCNTB(0));  
  53.         __raw_writel(tcnt/2,S3C2410_TCMPB(0));  
  54.         tcon = __raw_readl(S3C2410_TCON);  
  55.         /*关闭死区、自动重载、关反相器、更新TCNTB0&TCMPB0、启动定时器0*/  
  56.         tcon &= ~0x1f;  
  57.         tcon |= 0xb;  
  58.         __raw_writel(tcon, S3C2410_TCON);  
  59.         /*//清除定时器0的手动更新位*/  
  60.         tcon &= ~2;  
  61.         __raw_writel(tcon, S3C2410_TCON);  
  62.     }  
  63.     return 0;  
  64. }  
  65.   
  66. int mypwm_close(struct inode*inode, struct file*filp){  
  67.     printk("/dev/lwp-bell is closed\n");  
  68.     return 0;  
  69. }  
  70.   
  71. struct file_operations mypwm_ops={  
  72.     .owner = THIS_MODULE,  
  73.     .open = mypwm_open,  
  74.     .release = mypwm_close,  
  75.     .ioctl = mypwm_ioctl,  
  76. };  
  77.   
  78. void mypwm_setup(void){  
  79.     dev_t num;  
  80.     num = MKDEV(MYPWM_MAJOR, MYPWM_MINOR);  
  81.     cdev_init(pwm_cdev, &mypwm_ops);  
  82.     cdev_add(pwm_cdev, num, 1);                                      //注册cdev结构体  
  83. }  
  84.   
  85. static int __init mypwm_init(void){  
  86.     int ret;  
  87.     ret = alloc_chrdev_region(&dev_num, MYPWM_MINOR,1, MYPWM_NAME);  //让系统来分配设备号  
  88.     if(ret < 0)  
  89.         printk("can't get major number\n");  
  90.     MYPWM_MAJOR = MAJOR(dev_num);  
  91.     pwm_cdev = kmalloc(sizeof(struct cdev), GFP_KERNEL);             //动态申请cdev结构体的内存  
  92.     if(!pwm_cdev){  
  93.         return -ENOMEM;  
  94.     }  
  95.     memset(pwm_cdev, 0, sizeof(pwm_cdev));  
  96.     mypwm_setup();                                                    //填充cdev结构体的成员  
  97.     pwm = class_create(THIS_MODULE,MYPWM_NAME);                       //创建/sys下的类  
  98.     if(IS_ERR(pwm)){  
  99.         printk("ERROR: Fail to create lwp-bell class\n");  
  100.     }  
  101.     device_create(pwm,NULL,dev_num,NULL,MYPWM_NAME);                   //创建设备文件  
  102.     printk("pwm driver lwp-bell is registered success\n");  
  103.     return 0;  
  104. }  
  105.   
  106. static void __exit mypwm_exit(void){  
  107.     unregister_chrdev_region(dev_num,1);            /*归还设备号*/  
  108.     device_destroy(pwm,dev_num);                    /*删除设备文件*/  
  109.     class_destroy(pwm);                             /*删除类*/  
  110.     kfree(pwm_cdev);                                /*释放设备结构体内存*/   
  111.     cdev_del(pwm_cdev);                             /*注销cdev*/  
  112.     printk("pwm driver lwp-bell is removed success\n");  
  113. }  
  114.   
  115. module_init(mypwm_init);  
  116. module_exit(mypwm_exit);  
  117.   
  118. MODULE_LICENSE("GPL");  
  119. MODULE_AUTHOR("liwanpeng");  

用户程序:

C-sharp代码 
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <fcntl.h>  
  4. #include <sys/ioctl.h>  
  5.   
  6. int main(){  
  7.     int fd, cmd;  
  8.     cmd = 0;  
  9.     fd = open("/dev/lwp-bell",O_RDWR);  
  10.     if(fd < 0){  
  11.         printf("cannot open /dev/lwp-bell\n");  
  12.         exit(1);  
  13.     }  
  14.     while(1){  
  15.         scanf("%d", &cmd);  
  16.         printf("cmd is %d\n",cmd);  
  17.         ioctl(fd, cmd);  
  18.         if(cmd <= 0)  
  19.             break;  
  20.     }  
  21.     close(fd);  
  22.     return 0;  
  23. }