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

努力成为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的选项:

Linux System Utilities ---> [*] mdev [*] Support /etc/mdev.conf [*] Support subdirs/symlinks [*] Support regular expressions substitutions when renaming device [*] Support command execution at device addition/removal
最后修改/etc/fstab:

# device mount-point type options dump fsck order #---------------------------------------------------------------- procfs /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /dev/shm tmpfs defaults 0 0 usbfs /proc/bus/usb usbfs defaults 0 0 ramfs /dev ramfs defaults 0 0 none /dev/pts devpts mode=0622 0 0

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

# Mount virtual filesystem /bin/mount -t proc procfs /proc /bin/mount -n -t sysfs sysfs /sys /bin/mount -n -t usbfs usbfs /proc/bus/usb /bin/mount -t ramfs ramfs /dev # Make dir /bin/mkdir -p /dev/pts /bin/mkdir -p /dev/shm /bin/mkdir -p /var/log /bin/mount -n -t devpts none /dev/pts -o mode=0622 /bin/mount -n -t tmpfs tmpfs /dev/shm # Make device node echo /sbin/mdev > /proc/sys/kernel/hotplug /sbin/mdev -s

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

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

/* This is a #define to keep the compiler from merging different * instances of the __key variable */ #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ }) extern struct class * __must_check __class_create(struct module *owner, const char *name, struct lock_class_key *key);

class_destroy的原型如下:

extern void class_destroy(struct class *cls);

device_create的原型如下:

extern struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) __attribute__((format(printf, 5, 6)));

device_destroy的原型如下:

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程序:

#include <linux/init.h> #include <linux/module.h> #include <mach/hardware.h> #include <asm/io.h> #include <plat/regs-timer.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/err.h> #include <linux/fs.h> #include <mach/regs-gpio.h> int MYPWM_MAJOR = 0; int MYPWM_MINOR = 0; #define MYPWM_NAME "lwp-bell" dev_t dev_num; struct class *pwm; struct cdev *pwm_cdev; /*打开设备*/ int mypwm_open(struct inode*inode, struct file*filp){ printk("/dev/lwp-bell is open\n"); return 0; } int mypwm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ if(cmd == 0){ s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP); s3c2410_gpio_setpin(S3C2410_GPB0, 0); } else{ unsigned long tcon; unsigned long tcnt; unsigned long tcfg0; unsigned long tcfg1; struct clk *clk_p; unsigned long pclk; s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0); tcfg0 = __raw_readl(S3C2410_TCFG0); tcfg1 = __raw_readl(S3C2410_TCFG1); tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; tcfg0 |= (50-1); tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; __raw_writel(tcfg0, S3C2410_TCFG0); __raw_writel(tcfg1, S3C2410_TCFG1); clk_p = clk_get(NULL,"pclk"); /*从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义*/ pclk = clk_get_rate(clk_p); /*计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)*/ tcnt = pclk/50/16/cmd; __raw_writel(tcnt, S3C2410_TCNTB(0)); __raw_writel(tcnt/2,S3C2410_TCMPB(0)); tcon = __raw_readl(S3C2410_TCON); /*关闭死区、自动重载、关反相器、更新TCNTB0&TCMPB0、启动定时器0*/ tcon &= ~0x1f; tcon |= 0xb; __raw_writel(tcon, S3C2410_TCON); /*//清除定时器0的手动更新位*/ tcon &= ~2; __raw_writel(tcon, S3C2410_TCON); } return 0; } int mypwm_close(struct inode*inode, struct file*filp){ printk("/dev/lwp-bell is closed\n"); return 0; } struct file_operations mypwm_ops={ .owner = THIS_MODULE, .open = mypwm_open, .release = mypwm_close, .ioctl = mypwm_ioctl, }; void mypwm_setup(void){ dev_t num; num = MKDEV(MYPWM_MAJOR, MYPWM_MINOR); cdev_init(pwm_cdev, &mypwm_ops); cdev_add(pwm_cdev, num, 1); //注册cdev结构体 } static int __init mypwm_init(void){ int ret; ret = alloc_chrdev_region(&dev_num, MYPWM_MINOR,1, MYPWM_NAME); //让系统来分配设备号 if(ret < 0) printk("can't get major number\n"); MYPWM_MAJOR = MAJOR(dev_num); pwm_cdev = kmalloc(sizeof(struct cdev), GFP_KERNEL); //动态申请cdev结构体的内存 if(!pwm_cdev){ return -ENOMEM; } memset(pwm_cdev, 0, sizeof(pwm_cdev)); mypwm_setup(); //填充cdev结构体的成员 pwm = class_create(THIS_MODULE,MYPWM_NAME); //创建/sys下的类 if(IS_ERR(pwm)){ printk("ERROR: Fail to create lwp-bell class\n"); } device_create(pwm,NULL,dev_num,NULL,MYPWM_NAME); //创建设备文件 printk("pwm driver lwp-bell is registered success\n"); return 0; } static void __exit mypwm_exit(void){ unregister_chrdev_region(dev_num,1); /*归还设备号*/ device_destroy(pwm,dev_num); /*删除设备文件*/ class_destroy(pwm); /*删除类*/ kfree(pwm_cdev); /*释放设备结构体内存*/ cdev_del(pwm_cdev); /*注销cdev*/ printk("pwm driver lwp-bell is removed success\n"); } module_init(mypwm_init); module_exit(mypwm_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("liwanpeng");

用户程序:

#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/ioctl.h> int main(){ int fd, cmd; cmd = 0; fd = open("/dev/lwp-bell",O_RDWR); if(fd < 0){ printf("cannot open /dev/lwp-bell\n"); exit(1); } while(1){ scanf("%d", &cmd); printf("cmd is %d\n",cmd); ioctl(fd, cmd); if(cmd <= 0) break; } close(fd); return 0; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值