Linux内核编程接口函数
转载请注明出处: http://blog.csdn.net/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文件中。
4.container_of()
该函数其实是一个宏,定义于include/linux/kernel.h,如下所示:
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
该宏定义的功能是,通过结构体某一个已知成员的指针(成员变量的地址),反向计算出该结构体的地址。其原理也很简单,就是用已知成员变量的地址,减去该成员变量在结构体中的偏移量。
这个功能在kernel编程中十分有用。举一个简单的例子,如下:
struct child_struct
{
int m;
int n;
};
struct parent_struct
{
int a;
int b;
struct child_struct c;
};
struct parent_struct test;
int *address = &test.c.n;
struct parent_struct *test_addr = container_of(address,struct parent_struct,c.n);
通过上述语句,获取到的就是test变量的地址。
注意:需要注意的是,在使用container_of()宏来获取父结构体的地址时,一定不要用指针类型的成员变量进行计算。
因为指针类型的成员变量本身就是用来保存一个地址,并且该地址是不确定的,它可以被赋值成任意值,因此不能用来计算父结构体的地址。
因为指针类型的成员变量本身就是用来保存一个地址,并且该地址是不确定的,它可以被赋值成任意值,因此不能用来计算父结构体的地址。