原文链接 http://blog.youkuaiyun.com/drivelinux/article/details/8656280
字符设备相关函数
1.alloc_chrdev_region()
功能: 自动分配一个主设备号及基于此主设备号的若干个连续的指定数量的次设备号。
函数原型如下:
- /**
- * alloc_chrdev_region() - register a range of char device numbers
- * @dev: output parameter for first assigned number
- * @baseminor: first of the requested range of minor numbers
- * @count: the number of minor numbers required
- * @name: the name of the associated device or driver
- *
- * Allocates a range of char device numbers. The major number will be
- * chosen dynamically, and returned (along with the first minor number)
- * in @dev. Returns zero or a negative error code.
- */
- int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
2.register_chrdev_region()
功能:注册连续的若干个设备号,用这个函数要自己指定主设备号。
函数原型:
- /**
- * register_chrdev_region() - register a range of device numbers
- * @from: the first in the desired range of device numbers; must include
- * the major number.
- * @count: the number of consecutive device numbers required
- * @name: the name of the device or driver.
- *
- * Return value is zero on success, a negative error code on failure.
- */
- int register_chrdev_region(dev_t from, unsigned count, const char *name)
注: register_chrdev() 函数也用于分配设备号,但是用于2.6版本之前的内核中,在之后的版本中不再使用。
3.unregister_chrdev_region()
功能:注销由alloc_chrdev_region()或者register_chrdev_region()注册的设备号,一般在驱动模块的退出函数中要执行此操作,将注册的设备号还给系统。
函数原型:
- /**
- * unregister_chrdev_region() - return a range of device numbers
- * @from: the first in the range of numbers to unregister
- * @count: the number of device numbers to unregister
- *
- * This function will unregister a range of @count device numbers,
- * starting with @from. The caller should normally be the one who
- * allocated those numbers in the first place...
- */
- void unregister_chrdev_region(dev_t from, unsigned count)
4.cdev_alloc()
功能:分配一个表示字符设备的cdev结构体。
函数原型:
- /**
- * cdev_alloc() - allocate a cdev structure
- *
- * Allocates and returns a cdev structure, or NULL on failure.
- */
- struct cdev *cdev_alloc(void)
5.cdev_init()
功能:初始化由cdev_alloc()分配的表示字符设备的cdev结构体,并通过向该函数传入的struct file_operations *fops参数来指定该字符设备的文件操作函数。
函数原型:
- /**
- * cdev_init() - initialize a cdev structure
- * @cdev: the structure to initialize
- * @fops: the file_operations for this device
- *
- * Initializes @cdev, remembering @fops, making it ready to add to the
- * system with cdev_add().
- */
- void cdev_init(struct cdev *cdev, const struct file_operations *fops)
6.cdev_add()
功能:添加一个(由cdev_alloc()分配并通过cdev_init()初始化的)字符设备到系统中。
函数原型:
- /**
- * cdev_add() - add a char device to the system
- * @p: the cdev structure for the device
- * @dev: the first device number for which this device is responsible
- * @count: the number of consecutive minor numbers corresponding to this
- * device
- *
- * cdev_add() adds the device represented by @p to the system, making it
- * live immediately. A negative error code is returned on failure.
- */
- int cdev_add(struct cdev *p, dev_t dev, unsigned count)
7.cdev_del()
功能:执行和cdev_add()相反的操作,将一个指定的字符设备从系统中删除。
函数原型:
- /**
- * cdev_del() - remove a cdev from the system
- * @p: the cdev structure to be removed
- *
- * cdev_del() removes @p from the system, possibly freeing the structure
- * itself.
- */
- void cdev_del(struct cdev *p)
- struct cdev *myDev_dev;
- /*主设备和从设备号变量*/
- static int myDev_major = 0;
- static int myDev_minor = 0;
- static int __init myDev_init(void)
- {
- int err = -1;
- dev_t dev = 0;
- /*1.动态分配主设备号和从设备号,第三个参数表示分配的此设备号的数量*/
- err = alloc_chrdev_region(&dev, 0, TOTAL_DEVICE_COUNT, "myDev");
- if(err < 0) {
- printk(KERN_ALERT"Failed to alloc char dev region.\n");
- return err;
- }
- myDev_major = MAJOR(dev);
- myDev_minor = MINOR(dev);
- /*2.分配cdev字符设备结构体变量*/
- myDev_dev = cdev_alloc();
- if(!myDev_dev) {
- err = -ENOMEM;
- printk(KERN_ALERT"Failed to alloc myDev_dev.\n");
- return err;
- }
- /*3.初始化cdev字符设备结构体*/
- cdev_init(myDev_dev, &myDev_fops);
- /*4.添加字符设备到系统中*/
- err = cdev_add(myDev_dev,dev, TOTAL_DEVICE_COUNT);
- if(err) {
- return err;
- }
- return err;
- }
8.register_chrdev()
这个函数也是实现注册字符设备的功能,但是相对于上面提到的alloc_chrdev_region()和register_chrdev_region()而言,是比较老的形式。并且register_chrdev()一次能够注册统一主设备号下的256个不同的次设备。并且,在2.6之后的内核版本中不再使用这一函数。
自动创建设备节点相关函数
1.class_create()
功能:创建一个struct class结构体,作为device_create()函数的参数。
函数原型:
- #define class_create(owner, name) \
- ({ \
- static struct lock_class_key __key; \
- __class_create(owner, name, &__key); \
- })
2.device_create()
功能:该函数创建一个设备并将其注册到sysfs中,同时在系统的sys/class和sys/device目录下会生成相应的类和设备入口。并且,该函数还会出发用户空间udev的动作,udev会根据sysfs下的class在/dev目录下创建设备节点,这也为自动创建设备节点提供了一种途径。通过device_create()函数,我们就可以不用通过mknod命令手动的创建设备节点了。
函数原型:
- /**
- * 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, ...)
3.device_create_file()
功能:该函数用于为指定设备在sysfs下建立属性文件,如:
device_create_file(dev, &dev_attr_val);
device_create_file(dev, &dev_attr_val);
其中的dev_attr_val可以通过DEVICE_ATTR宏进行定义,也可以直接通过struct device_attribute xxx=__ATTR(_name,_mode,_show,_store)进行定义,区别就是用DEVICE_ATTR宏进行定义时得到的变量名都是以dev_attr_为前缀的,而通过struct device_attribute xxx=__ATTR(_name,_mode,_show,_store)进行定义时,变量名可以任意指定。
函数原型:
- /**
- * device_create_file - create sysfs attribute file for device.
- * @dev: device.
- * @attr: device attribute descriptor.
- */
- int device_create_file(struct device *dev,const struct device_attribute *attr)
- #define DEVICE_ATTR(_name, _mode, _show, _store) \
- struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
- static struct class* myDev_class = NULL;
- struct device* devc= NULL;
- /*5.创建一个struct class结构体*/
- myDev_class = class_create(THIS_MODULE,"myDevClass");
- if(IS_ERR(myDev_class)) {
- err = PTR_ERR(myDev_class);
- printk(KERN_ALERT"Failed to create class.\n");
- return err;
- }
- /*6.在sysf中生成设备文件并在/dev下自动生成设备节点*/
- devc = device_create(myDev_class,NULL,dev,NULL,"myDev");
- if(IS_ERR(devc)) {
- err = PTR_ERR(devc);
- printk(KERN_ALERT"Failed to create device.");
- return err;
- }
以下贴出以上分两部分介绍的字符设备的初始化代码:
- struct cdev *myDev_dev;
- /*主设备号和从设备号变量*/
- static int myDev_major = 0;
- static int myDev_minor = 0;
- static int __init myDev_init(void)
- {
- int err = -1;
- dev_t dev = 0;
- struct device *devc = NULL;
- /*1.动态分配主设备号和从设备号,第三个参数表示分配的此设备号的数量*/
- err = alloc_chrdev_region(&dev, 0, TOTAL_DEVICE_COUNT, "myDev");
- if(err < 0) {
- printk(KERN_ALERT"Failed to alloc char dev region.\n");
- return err;
- }
- myDev_major = MAJOR(dev);
- myDev_minor = MINOR(dev);
- /*2.分配cdev字符设备结构体变量*/
- myDev_dev = cdev_alloc();
- if(!myDev_dev) {
- err = -ENOMEM;
- printk(KERN_ALERT"Failed to alloc myDev_dev.\n");
- return err;
- }
- /*3.初始化cdev字符设备结构体*/
- cdev_init(myDev_dev, &myDev_fops);
- /*4.添加字符设备到系统中*/
- err = cdev_add(myDev_dev,dev, TOTAL_DEVICE_COUNT);
- if(err) {
- return err;
- }
- /*5.创建一个struct class结构体*/
- myDev_class = class_create(THIS_MODULE,"myDevClass");
- if(IS_ERR(myDev_class)) {
- err = PTR_ERR(myDev_class);
- printk(KERN_ALERT"Failed to create class.\n");
- return err;
- }
- /*6.在sysf中生成设备文件并在/dev下自动生成设备节点*/
- devc = device_create(myDev_class,NULL,dev,NULL,"myDev");
- if(IS_ERR(devc)) {
- err = PTR_ERR(devc);
- printk(KERN_ALERT"Failed to create device.");
- return err;
- }
- return err;
- }
1.kmalloc()和kzalloc()的异同
在内核include/linux/slab.h文件中有如下定义:
- /**
- * kzalloc - allocate memory. The memory is set to zero.
- * @size: how many bytes of memory are required.
- * @flags: the type of memory to allocate (see kmalloc).
- */
- static inline void *kzalloc(size_t size, gfp_t flags)
- {
- return kmalloc(size, flags | __GFP_ZERO);
- }
从注释以及函数原型均能看出kzalloc()函数的作用,即申请一块内存并且该内存会在函数调用过程中被清零。
2.module_platform_driver()
该函数实际是一个宏,它在include/linux/platform_device.h中定义如下:
- /* module_platform_driver() - Helper macro for drivers that don't do
- * anything special in module init/exit. This eliminates a lot of
- * boilerplate. Each module may only use this macro once, and
- * calling it replaces module_init() and module_exit()
- */
- #define module_platform_driver(__platform_driver) \
- module_driver(__platform_driver, platform_driver_register, \
- platform_driver_unregister)
其中的module_driver在/include/linux/device.h中定义,如下:
- /**
- * module_driver() - Helper macro for drivers that don't do anything
- * special in module init/exit. This eliminates a lot of boilerplate.
- * Each module may only use this macro once, and calling it replaces
- * module_init() and module_exit().
- *
- * @__driver: driver name
- * @__register: register function for this driver type
- * @__unregister: unregister function for this driver type
- * @...: Additional arguments to be passed to __register and __unregister.
- *
- * Use this macro to construct bus specific macros for registering
- * drivers, and do not use it on its own.
- */
- #define module_driver(__driver, __register, __unregister, ...) \
- static int __init __driver##_init(void) \
- { \
- return __register(&(__driver) , ##__VA_ARGS__); \
- } \
- module_init(__driver##_init); \
- static void __exit __driver##_exit(void) \
- { \
- __unregister(&(__driver) , ##__VA_ARGS__); \
- } \
- module_exit(__driver##_exit);
由上述定义可知,module_platform_driver()宏的作用就是定义指定名称的平台设备驱动注册函数和平台设备驱动注销函数,并且在函数体内分别通过platform_driver_register()函数和platform_driver_unregister()函数注册和注销该平台设备驱动。
3.read_cpuid_id()
读取CPU ID,定义于arch/arm/include/asm/cputype.h文件中。