Linux内核中:
a -- 使用cdev结构体来描述字符设备;
b -- 通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性;
c -- 通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等;
Linux字符设备驱动中:
a -- 模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号;
b -- 通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;
c -- 模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号
实现字符设备驱动函数:
1、动态申请设备号 注意,申请的主设备号 是一致的
int alloc_chrdev_region(dev_t * dev,unsigned baseminor,
unsigned count,const char * name);
2、定义并初始化 字符设备对象 [属性 方法]
struct cdev {
const struct file_operations *ops; 方法集合
dev_t dev; 设备号
};
3、在用户层创建一个字符设备文件
1)创建一个内核的类
struct class * class_create(owner,name);
owner: THIS_MODULE
name: 你给类起的名字
2)创建节点, 在用户层/dev/目录下 创建一个 字符设备文件/节点
struct device *device_create(struct class *class,
struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
实现字符设备驱动释放的函数:
1、/释放 文件节点/
void device_destroy(struct class * class, dev_t devt)
2、/释放 字符设备驱动对象/
void cdev_del(struct cdev *)
3、/释放设备号/
void unregister_chrdev_region(dev_t from, unsigned count)
cdev 结构体解析
<include/linux/cdev.h>
struct cdev {
struct kobject kobj; //内嵌的内核对象.
struct module *owner; //该字符设备所在的内核模块的对象指针.
const struct file_operations *ops; //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
struct list_head list; //用来将已经向内核注册的所有字符设备形成链表.
dev_t dev; //字符设备的设备号,由主设备号和次设备号构成.
unsigned int count; //隶属于同一主设备号的次设备号的个数.
};
操作这个结构体的入口函数:
1、struct cdev结构体做初始化,最重要的就是建立cdev 和 file_operations之间的连接:
void cdev_init(struct cdev *, const struct file_operations *);
2、分配一个struct cdev结构,动态申请一个cdev内存
struct cdev *cdev_alloc(void);
3、向内核注册一个struct cdev结构,
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
4、注销一个struct cdev结构,
void cdev_del(struct cdev *p);
设备号相应操作
1 – 主设备号和次设备号(二者一起为设备号):
1) -- 从设备号中提取major和minor
MAJOR(dev_t dev);
MINOR(dev_t dev);
2) -- 通过major和minor构建设备号
MKDEV(int major,int minor);
2 – 分配设备号(动态和静态)
1)--静态申请
int register_chrdev_region(dev_t from, unsigned count, const char *name);
2)--动态申请
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
区别:
register_chrdev_region直接将Majorregister_chrdev_region直接将Major 注册进入。alloc_chrdev_region从Major
alloc_chrdev_region从Major = 0 开始,逐个查找设备号,直到找到一个闲置的设备号,并将其注册进去;
举例:
1)、静态
devno = MKDEV(major,minor);
ret = register_chrdev_region(devno, 1, "hello");
cdev_init(&cdev,&hello_ops);
ret = cdev_add(&cdev,devno,1);
2)、动态
alloc_chrdev_region(&devno, minor, 1, "hello");
major = MAJOR(devno);
cdev_init(&cdev,&hello_ops);
ret = cdev_add(&cdev,devno,1)
3)、注册了设备号,做了cdev 的初始化以及cdev 的注册
register_chrdev函数:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
3、注销设备号:
void unregister_chrdev_region(dev_t from, unsigned count);
4、创建设备文件:
利用cat /proc/devices查看申请到的设备名,设备号。
1)使用mknod手工创建:mknod filename type major minor
2)自动创建设备节点: (利用udev(mdev)来实现设备文件的自动创建)
在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)创建对应的设备。
函数讲解
I)创建一个类 class_create(...)
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
销毁函数:void class_destroy(struct class *cls)
II)创建一个字符设备文件 device_create(类,NULL,设备号,NULL,名字)
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)