开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
前言
linux驱动的初始化过程分为三部分。
1设备编号的申请,在/proc/devices里出现;2设备的注册,其中包括对设备的操作;3设备节点的注册,出现在/dev目录下。
字符设备的注册
涉及到的结构体是 struct cdev,包含于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;
} __randomize_layout;
cdev是2.6内核以来新的字符设备注册方式,以前使用的注册接口是int register_chedev(unsigned int major,const char*name,struct file_operations *fops),对于咱们学习新的驱动来说,只要了解即可。开发时候尽量使用新的注册接口,因为旧的注册机制会在将来的内核实现中消失。
有两种注册方式:静态和动态。
静态方式,实体结构需要调用cdev_init接口初始化,需要赋值owner成员。
static void init_cdev_static(dev_t DevId)
{
int err;
struct cdev sDev;
cdev_init(&sDev,&MyFOps);
sDev.owner = THIS_MODULE;
err = cdev_add(&sDev,DevId,1);
if(err) printk(KERN_ALERT "cdev_add err [%d]] !!\n",err);
return err;
}
动态方式,不需要调用cdev_init接口初始化,需要代码里主动对owner和ops进行赋值。
static int init_cdev_dyn(dev_t DevId)
{
int err = 0;
MyDev = cdev_alloc();
if(MyDev == NULL) {
printk(KERN_ALERT "cdev_alloc fail\n");
return -1;;
}
MyDev->ops = &MyFOps;
MyDev->owner = THIS_MODULE;
err = cdev_add(MyDev,DevId,1);
if(err) printk(KERN_ALERT "cdev_add err [%d]] !!\n",err);
return err;
}
cdev_add函数一旦成功返回,设备就成功在系统中存在,且可被调用操作了。所以需要等待所有的设备操作完全准备好,才能调用cdev_add接口。
字符设备的自创建
需要的接口位于 linux/device.h头文件里。
首先使用class_create(owner, name) 接口创建类,类会在 /sys/class下创建个相应的文件夹。
再次使用 device_create(truct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) 创建逻辑设备,和相应的类关联起来,并自动在/dev下创建逻辑设备节点。
以下是示例代码。
int DevCreate(dev_t devId)
{
TestDri = class_create(THIS_MODULE,"TestDri");
if(IS_ERR(TestDri)){
printk(KERN_ALERT "class_create err\n");
return -1;
}
device_create(TestDri,NULL,devId,NULL,"zsy001");
return 0;
}
查看/sys下效果
/dev下可自动创建出设备节点。
由此,完成对设备节点的自创建工作。