使用class 自动创建设备节点
#include <linux/init.h>// __init __exit #include <linux/module.h> // module_init module_exit #include <linux/fs.h> //file_operations #include <asm/uaccess.h> //copy_from_user copy_to_user #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <asm/string.h> #include <linux/ioport.h> //request_mem_region #include <asm/io.h> //ioremap #include <linux/cdev.h> #include <linux/device.h> #define MYNAME "led_dev" #define DEVNUM 1 #define S5PV210_PA_GPIOJ0CON 0xe0200240 volatile unsigned int *rGPJ0CON = NULL; volatile unsigned int *rGPJ0DAT = NULL; static dev_t led_dev_no = 0; /************ 静态使用cdev**********/ //static struct cdev led_cdev; /************ 动态使用cdev**********/ static struct cdev *pled_cdev = NULL; static struct class *pled_dev_class = NULL; static int led_open(struct inode *inode, struct file *file); ssize_t led_read(struct file *file, char __user *user, size_t count, loff_t *loff); ssize_t led_write(struct file *file, const char __user *user, size_t count, loff_t *loff); static int led_release(struct inode *inode, struct file *file); static char kbuf[100] = {0}; static const struct file_operations led_fops = { .owner= THIS_MODULE, .open= led_open, .read = led_read, .write = led_write, .release= led_release, }; int led_open(struct inode *inode, struct file *file) { printk(KERN_INFO "led_open successful\n"); return 0; } ssize_t led_read(struct file *file, char __user *user, size_t ucount, loff_t *loff) { printk(KERN_INFO "led_read successful\n"); if (copy_to_user(user,kbuf , ucount)) { printk(KERN_INFO "copy_to_user fail\n"); return -EINVAL; } printk(KERN_INFO "copy_to_user successful\n"); return strlen(kbuf); } ssize_t led_write(struct file *file, const char __user *user, size_t ucount, loff_t *loff) { printk(KERN_INFO "led_write successful\n"); memset(kbuf,0,sizeof(kbuf)); if (copy_from_user(kbuf, user, ucount)) { printk(KERN_INFO "copy_from_user fail\n"); return -EINVAL; } if(!strcmp(kbuf,"on")) { *rGPJ0CON &=0xff000fff; *rGPJ0CON |=0x00111000; *rGPJ0DAT &=~((0x01<<3)|(0x01<<4)|(0x01<<5)); } else if(!strcmp(kbuf,"off")) { *rGPJ0CON &=0xff000fff; *rGPJ0CON |=0x00111000; *rGPJ0DAT |=((0x01<<3)|(0x01<<4)|(0x01<<5)); } return ucount; printk(KERN_INFO "copy_from_user successful\n"); } int led_release(struct inode *inode, struct file *file) { printk(KERN_INFO "led_release successful\n"); return 0; } // 模块安装函数 static int __init chrdev_init(void) { int ret = -1; int retval = -1; printk(KERN_INFO "chrdev_init successful\n"); /******指定主设备号注册*****/ //led_dev_no = MKDEV(250,0); //retval = register_chrdev_region(led_dev_no, DEVNUM,MYNAME); /******内核自动分配主设备号注册*****/ retval = alloc_chrdev_region(&led_dev_no,0,DEVNUM,MYNAME); if (retval) { printk(KERN_WARNING "alloc_chrdev_region fail\n."); ret = -EINVAL; goto err_reg; } printk(KERN_INFO "alloc_chrdev_region successful major = %d,minor = %d \n",MAJOR(led_dev_no),MINOR(led_dev_no)); /****动态申请cdev实体******/ pled_cdev = cdev_alloc(); printk(KERN_INFO "cdev_alloc successful\n"); cdev_init(pled_cdev, &led_fops); retval = cdev_add(pled_cdev, led_dev_no, DEVNUM); if (retval) { printk(KERN_WARNING "cdev_add fail\n."); ret = -EINVAL; goto err_add; } printk(KERN_INFO "cdev_add successful\n"); // 注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息 // 给udev,让udev自动创建和删除设备文件 pled_dev_class = class_create(THIS_MODULE, "led_musk"); if (IS_ERR(pled_dev_class)) { printk(KERN_WARNING "class_create fail\n."); ret = -EINVAL; goto err_class_create; } device_create(pled_dev_class, NULL, led_dev_no, NULL, "led_dev"); //“led_dev”对应/dev/下的名字 if (request_mem_region(S5PV210_PA_GPIOJ0CON, 8, "GPIOJ0CON") == NULL) { printk(KERN_WARNING "failed to get memory region\n"); ret = -ENOENT; goto err_req; } rGPJ0CON = ioremap(S5PV210_PA_GPIOJ0CON,8); if (rGPJ0CON == NULL) { printk(KERN_WARNING "fail to ioremap() region\n"); ret = -ENOENT; goto err_map; } rGPJ0DAT = rGPJ0CON+1; return 0; err_map: release_mem_region(S5PV210_PA_GPIOJ0CON,8); err_req: class_destroy(pled_dev_class); err_class_create: /***for static cdev******/ //cdev_del(&led_cdev); /***for dynamic cdev******/ cdev_del(pled_cdev); err_add: printk(KERN_INFO "err_add err\n"); unregister_chrdev_region(led_dev_no, DEVNUM); err_reg: return ret; } // 模块卸载函数 static void __exit chrdev_exit(void) { iounmap(rGPJ0CON); release_mem_region(S5PV210_PA_GPIOJ0CON,8); device_destroy(pled_dev_class,led_dev_no); class_destroy(pled_dev_class); /***for static cdev******/ //cdev_del(&led_cdev); /***for dynamic cdev******/ cdev_del(pled_cdev); unregister_chrdev_region(led_dev_no, DEVNUM); printk(KERN_INFO "chrdev_exit successful\n"); } module_init(chrdev_init); module_exit(chrdev_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE("GPL");// 描述模块的许可证 MODULE_AUTHOR("musk");// 描述模块的作者 MODULE_DESCRIPTION("module test");// 描述模块的介绍信息 MODULE_ALIAS("alias xxx");// 描述模块的别名信息
一. 什么是class
1.1. class 指的是 设备类(device classes),是对于设备的高级抽象。但实际上 class 也是一个结构体,只不过 class 结构体在声明时是按照类的思想来组织其成员的。运用 class,可以让用户空间的程序根据自己要处理的事情来调用设备,而不是根据设备被接入到系统的方式或设备的工作原理来调用
1.2. 一个 struct class 结构体类型变量对应一个类,内核提供了class_create() 函数,可以用它来创建一个类,这个类存放于 sysfs 下面。 一旦创建了类,再调用 device_create() 函数在 /dev 目录下创建相应的设备节点
1.2.1. sysfs虚拟文件系统
a. sysfs是 Linux 内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与 proc 有些类似,但除了与 proc相同的具有查看和设定内核参数功能之外,还有为 Linux 统一设备模型作为管理之用。相比于 proc 文件系统,使用 sysfs导出内核数据的方式更为统一,并且组织的方式更好,它的设计从 proc 中吸取了很多教训
b. 当使用class_create创建时,在/sys/class/中会创建相应文件
二. linux中的mdev机制
2.1.什么是mdev
2.1.1. mdev是busybox自带的一个简化版的udev。适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所
需的节点文件。在以busybox 为基础构建嵌入式linux 的根文件系统时,使用它是最优的选择
2.1.2. mdev 的使用在busybox 中的mdev.txt 文档已经将得很详细了
2.2. mdev 用法
2.2.1.执行mdev前要挂载 /sys
a. mount -t tmpfs mdev /dev
b.mount -t sysfs sysfs /sys
2.2.2. 命令内核在增删设备时执行/sbin/mdev,使设备节点会被创建和删除
a. echo /sbin/mdev > /proc/sys/kernel/hotplug
2.2.3. 设置mdev,让它在系统启动时创建所有的设备节点
a. mdev -s
2.2.4. 相关操作bsp 下/etc/init.d/rcS启动开发板时会设置。
PATH=/sbin:/bin:/usr/sbin:/usr/bin runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel mount -a echo /sbin/mdev > /proc/sys/kernel/hotplug mdev -s /bin/hostname -F /etc/sysconfig/HOSTNAME ifconfig eth0 192.168.1.20 ~ - /etc/init.d/rcS 1/19 5%
三. 相关函数分析
3.1. class_create函数分析。
3.1.1. 函数调用层级
class_create
__class_create
__class_register
kset_register
kobject_uevent
/* 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); \ }) /** * class_create - create a struct class structure * @owner: pointer to the module that is to "own" this struct class * @name: pointer to a string for the name of this class. * @key: the lock_class_key for this class; used by mutex lock debugging * * This is used to create a struct class pointer that can then be used * in calls to device_create(). * * Returns &struct class pointer on success, or ERR_PTR() on error. * * Note, the pointer created here is to be destroyed when finished by * making a call to class_destroy(). */ struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key) { struct class *cls; int retval; cls = kzalloc(sizeof(*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } cls->name = name; cls->owner = owner; cls->class_release = class_create_release; retval = __class_register(cls, key); if (retval) goto error; return cls; error: kfree(cls); return ERR_PTR(retval); } int __class_register(struct class *cls, struct lock_class_key *key) { struct class_private *cp; int error; pr_debug("device class '%s': registering\n", cls->name); cp = kzalloc(sizeof(*cp), GFP_KERNEL); if (!cp) return -ENOMEM; klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put); INIT_LIST_HEAD(&cp->class_interfaces); kset_init(&cp->class_dirs); __mutex_init(&cp->class_mutex, "struct class mutex", key); error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name); if (error) { kfree(cp); return error; } /* set the default /sys/dev directory for devices of this class */ if (!cls->dev_kobj) cls->dev_kobj = sysfs_dev_char_kobj; #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (cls != &block_class) cp->class_subsys.kobj.kset = class_kset; #else cp->class_subsys.kobj.kset = class_kset; #endif cp->class_subsys.kobj.ktype = &class_ktype; cp->class = cls; cls->p = cp; error = kset_register(&cp->class_subsys); if (error) { kfree(cp); return error; } error = add_class_attrs(class_get(cls)); class_put(cls); return error; } /** * kset_register - initialize and add a kset. * @k: kset. */ int kset_register(struct kset *k) { int err; if (!k) return -EINVAL; kset_init(k); err = kobject_add_internal(&k->kobj); if (err) return err; kobject_uevent(&k->kobj, KOBJ_ADD); return 0; } /** * kobject_uevent - notify userspace by ending an uevent * * @action: action that is happening * @kobj: struct kobject that the action is happening to * * Returns 0 if kobject_uevent() is completed with success or the * corresponding error when it fails. */ int kobject_uevent(struct kobject *kobj, enum kobject_action action) { return kobject_uevent_env(kobj, action, NULL); }
3.2. device_create函数分析。
3.2.1. 函数调用层级
device_create
device_create_vargs
kobject_set_name_vargs
device_register
device_add
kobject_add
device_create_file
device_create_sys_dev_entry
devtmpfs_create_node
device_add_class_symlinks
device_add_attrs
device_pm_add
kobject_uevent
/** * device_create - creates a device and registers it with sysfs * @class: pointer to the struct class that this device should be registered to * @parent: pointer to the parent struct device of this new device, if any * @devt: the dev_t for the char device to be added * @drvdata: the data to be added to the device for callbacks * @fmt: string for the device's name * * This function can be used by char device classes. A struct device * will be created in sysfs, registered to the specified class. * * A "dev" file will be created, showing the dev_t for the device, if * the dev_t is not 0,0. * If a pointer to a parent struct device is passed in, the newly created * struct device will be a child of that device in sysfs. * The pointer to the struct device will be returned from the call. * Any further sysfs files that might be required can be created using this * pointer. * * Returns &struct device pointer on success, or ERR_PTR() on error. * * Note: the struct class passed to this function must have previously * been created with a call to class_create(). */ struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) { va_list vargs; struct device *dev; va_start(vargs, fmt); dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs); va_end(vargs); return dev; }
3.2.2. 调用此函数最早会创建/sys/class/xxx 下dev power subsystem uevent等文件
3.3. device_destroy函数
3.3.1. 函数定义:
/** * device_destroy - removes a device that was created with device_create() * @class: pointer to the struct class that this device was registered with * @devt: the dev_t of the device that was previously registered * * This call unregisters and cleans up a device that was created with a * call to device_create(). */ void device_destroy(struct class *class, dev_t devt) { struct device *dev; dev = class_find_device(class, NULL, &devt, __match_devt); if (dev) { put_device(dev); device_unregister(dev); } }
3.4. class_destroy函数
3.4.1.函数定义
/** * class_destroy - destroys a struct class structure * @cls: pointer to the struct class that is to be destroyed * * Note, the pointer to be destroyed must have been created with a call * to class_create(). */ void class_destroy(struct class *cls) { if ((cls == NULL) || (IS_ERR(cls))) return; class_unregister(cls); }
四. 应用层调用内核
4.1. 应用层代码如下:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #define DEVFILE "/dev/led_dev" int main(void) { char buf[100] = {0}; int fd = -1; if((fd =open(DEVFILE, O_RDWR))<0) { perror("open"); return -1; } printf("open successful fd = %d\n",fd); if(write(fd, "on", strlen("on"))<0) { perror("write"); return -1; } sleep(3); memset(buf,0,sizeof(buf)); if(read(fd, buf, 3)<0) { perror("read"); return -1; } printf("read data = %s\n",buf); if(write(fd, "off", strlen("off"))<0) { perror("write"); return -1; } sleep(3); memset(buf,0,sizeof(buf)); if(read(fd, buf, 4)<0) { perror("read"); return -1; } printf("read data = %s\n",buf); close(fd); return 0; }