Linux字符设备驱动基础(一)
1 Linux驱动分类
-
字符设备:按照字节的顺序读写的设备,其不能随机的读取设备内存中的某一数据。字符设备是面向流的设备,常见的字符设备有串口、控制台、LED等。
-
块设备:通常是指可从设备的任意位置读取到一定长度数据的设备。常见的块设备有:SD卡、U盘、磁盘等。
2 字符设备、字符设备驱动与用户空间三者之间的关系
1)在Linux内核中:
- a – 使用cdev结构体来描述字符设备;
- b – 通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性;
- c – 通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等;
2) 在Linux字符设备驱动中:
- a – 模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号;
- b – 通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;
- c – 模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号;
3) 用户空间访问该设备的程序:
- a – 通过Linux系统调用,如open( )、read( )、write( ),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数;
3 字符设备驱动模型
4 Cdev结构体解析
struct cdev {
struct kobject kobj; //内嵌的内核对象
struct module *owner; //该字符设备锁在的内核模块的对象指针
const struct file_operations *ops; //该字符设备的实现方法
struct list_head list; //链接已在内核注册的所有字符设备
dev_t dev; //该字符设备的设备号,由主设备号与次设备号构成
unsigned int count; //同一主设备号下的次设备号的个数
} __randomize_layout;
Cdev的相关操作接口:
1)cdev_init
/**
* 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)
{
memset(cdev, 0, sizeof *cdev); //将cdev清零
INIT_LIST_HEAD(&cdev->list); //初始化list链表,指向其本身
kobject_init(&cdev->kobj, &ktype_cdev_default); //初始化kobj成员
cdev->ops = fops; //初始化ops成员
}
2)cdev_alloc
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); //动态申请cdev结构体
if (p) {
INIT_LIST_HEAD(&p->list); //初始化list链表
kobject_init(&p->kobj, &ktype_cdev_dynamic); //初始化kobj成员
}
return p;
}
3)cdev_add
该函数向内核注册一个cdev结构体。
/**
* 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)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}
4)cdev_del
该函数向内核注销一个cdev结构体。
/**
* 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.
*
* NOTE: This guarantees that cdev device will no longer be able to be
* opened, however any cdevs already open will remain and their fops will
* still be callable even after cdev_del returns.
*/
void cdev_del(struct cdev *p)
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}