Linux内核编程接口函数
转载请注明出处: http://blog.csdn.net/drivelinux/article/details/8656280
功能: 自动分配一个主设备号及基于此主设备号的若干个连续的指定数量的次设备号。
函数原型如下:
/**
* 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)
函数原型:
/**
* 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版本之前的内核中,在之后的版本中不再使用。
函数原型:
/**
* 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)
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct cdev *cdev_alloc(void)
/**
* 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)
/**
* 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)
/**
* 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;
}
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
/**
* 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, ...)
/**
* 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;
}
在内核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()函数的作用,即申请一块内存并且该内存会在函数调用过程中被清零。
该函数实际是一个宏,它在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() - 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()函数注册和注销该平台设备驱动。
读取CPU ID,定义于arch/arm/include/asm/cputype.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
该宏定义的功能是,通过结构体某一个已知成员的指针(成员变量的地址),反向计算出该结构体的地址。其原理也很简单,就是用已知成员变量的地址,减去该成员变量在结构体中的偏移量。
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变量的地址。