您的位置:首页 > 运维架构 > Linux

linux字符设备驱动总结之:全自动创建设备及节点 .

2013-03-01 16:46 701 查看
/***************************************************************************************************

linux字符设备驱动总结之:全自动创建设备及节点

看了LDD3,深入浅出LDD,以及各个博文,还是需要总结下的。

张永辉 2012年10月9日

***************************************************************************************************/

概览:

第一步:注册设备号 信息#tail -f /var/log/message

注册函数:

register_chrdev_region() 或 查看#lsmod

alloc_chrdev_region() 或 查看#cat /proc/devices

register_chrdev()

注销函数:

unregist_chrdev_region() 或

unregister_chrdev()

第二步:初始化cdev并添加到系统

初始化cdev

静态初始化 cdev_init() 或

动态初始化 cdev_alloc()

添加到系统函数

cdev_add()

从系统删除函数

cdev_del()

第三步:创建设备节点

创建类

class_create() 将放于/sysfs 查看#ls /sys/class

删除类

class_destroy()

创建节点

device_create() 或 class_device_create() 将存放于/dev 查看#ls /dev

删除节点

device_destroy() 或 class_device_destroy()

第四步:简单示例(待续...)

/***************************************************************************************************

第一步:注册设备号

***************************************************************************************************/

Linux内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里。

该散列表中的每一个元素是一个 char_device_struct 结构,它的定义如下:

static struct char_device_struct

{

struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针

unsigned int major; // 主设备号

unsigned int baseminor; // 起始次设备号

int minorct; // 设备编号的范围大小

char name[64]; // 处理该设备编号范围内的设备驱动的名称

struct file_operations *fops; // 没有使用

struct cdev *cdev; // 指向字符设备驱动程序描述符的指针

}*chrdevs[CHRDEV_MAJOR_HASH_SIZE];

1 每一个主设备有一个会分配一个此结构,可以有多个次设备号。次设备是依次递增的。

2 内核提供了5个函数来来管理字符设备编号。

register_chrdev_region() 指定初始值

alloc_chrdev_region() 动态分配

register_chrdev() 指定设备号

他们都会调用 __register_chrdev_region() 来注册一组设备编号范围(一个char_device_struct结构),我们使用其中一个即可。

unregist_chrdev_region() 释放都用此函数

unregister_chrdev() 都调用了 __unregister_chrdev_region() 来注销设备

注册:

register_chrdev_region(dev_t first,unsigned int count,char *name)

first :要分配的设备编号范围的初始值(次设备号常设为0);

count :连续编号范围.

Name :编号相关联的设备名称. (/proc/devices);

int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);

*dev :存放返回的设备号

firstminor :第一个次设备号的号数,常为0;

int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

major :要注册的设备号, 若为0则自动分配一个

name :设备名

*fops :以后再聊

释放:

void unregister_chrdev(unsigned int major, const char *name);

void unregister_chrdev_region(dev_t from, unsigned count);

3 示例:略

4 参考:感谢原著 (有此6个函数的源码及解说)。

http://blog.csdn.net/iLetLet/article/details/6180314

/***************************************************************************************************

第二步:初始化 cdev 并添加到系统

***************************************************************************************************/

1.内核中每个字符设备都对应一个 cdev 结构的变量,定义如下:

linux-2.6.22/include/linux/cdev.h

struct cdev

{

struct kobject kobj; //每个 cdev 都是一个 kobject

struct module *owner; //指向实现驱动的模块

const struct file_operations *ops; //操纵这个字符设备文件的方法

struct list_head list; //与 cdev 对应的字符设备文件的 inode->i_devices 的链表头

dev_t dev; //起始设备编号

unsigned int count; //设备范围号大小

};

2. 初始化cdev :有两种定义初始化方式:

方式1:静态内存定义初始化:

struct cdev my_cdev;

cdev_init(&my_cdev, &fops);

my_cdev.owner = THIS_MODULE;

方式2:动态内存定义初始化:

struct cdev *my_cdev = cdev_alloc();

my_cdev->ops = &fops;

my_cdev->owner = THIS_MODULE;

下面是2函数的具体代码:

struct cdev *cdev_alloc(void) //它主要完成了空间的申请和简单的初始化操作;

{

struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);

if (p)

{

INIT_LIST_HEAD(&p->list);

kobject_init(&p->kobj, &ktype_cdev_dynamic);

}

return p;

}

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{

memset(cdev, 0, sizeof *cdev); //主要是对空间起到一个清零作用并较之cdev_alloc多了一个ops的赋值操作

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

3. 添加cdev到系统

为此可以调用 cdev_add() 函数。传入cdev结构的指针,起始设备编号,以及设备编号范围。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

{

p->dev = dev;

p->count = count;

return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);

}

释放时使用 cdev_del()函数来释放cdev占用的内存。

void cdev_del(struct cdev *p)

{

cdev_unmap(p->dev, p->count); //释放 cdev_map 散列表中的对象

kobject_put(&p->kobj); //释放 cdev 结构本身。

}

4.关于kobject_init() kobj_map()

内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。

这个结构的变量中包含一个散列表用来快速存取所有的对象。

kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。

当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。

/***************************************************************************************************

第三步:创建设备节点

***************************************************************************************************/

方法一:利用mknod命令手动创建设备节点。

方法二:实际上Linux内核为我们提供了一组函数,可以在模块加载的时候在/dev目录下创建相应设备节点,在卸载时可删除该节点。

原理:

1 内核中定义了struct class结构体,它对应一个类。

2 先调用class_create()函数,可以用它来创建一个类,这个类将存放于sysfs下面.

3 再调用device_create()函数,从而在/dev目录下创建相应的设备节点。

4 卸载模块对应的函数是 device_destroy 和 class_destroy()

注:2.6 以后的版本使用device_create(),之前的版本使用的class_device_create()。

详解:

1:class结构:

include/linux/device.h

struct class

{

const char *name;

struct module *owner;

struct kset subsys;

struct list_head devices;

struct list_head interfaces;

struct kset class_dirs;

struct semaphore sem; /* locks children, devices, interfaces */

struct class_attribute *class_attrs;

struct device_attribute *dev_attrs;

int (*dev_uevent) (struct device *dev,struct kobj_uevent_env *env);

void (*class_release)(struct class *class);

void (*dev_release) (struct device *dev);

int (*suspend) (struct device *dev, pm_message_t state);

int (*resume) (struct device *dev);

};

2:class_create()

class_create()在/drivers/base/class.c中实现:

struct class *class_create(struct module *owner, // 指定类的所有者是哪个模块

const char *name) // 指定类名

{

struct class *cls;

int retval;

cls = kzalloc(sizeof(*cls), GFP_KERNEL);

if (!cls)

{

retval = -ENOMEM;

goto error;

}

cls->name = name;

cls->owner = owner;

cls->class_release = class_create_release;

retval = class_register(cls);

if (retval)

goto error;

return cls;

error:

kfree(cls);

return ERR_PTR(retval);

}

3:device_create()函数在/drivers/base/core.c中实现:

struct device *device_create(struct class *class, //指定所要创建的设备所从属的类

struct devicev *parent, //这个设备的父设备,如果没有就指定为NULL

dev_t devt, //设备号

const char *fmt, //设备名称

...) //从设备号

{

va_list vargs;

struct device *dev;

va_start(vargs, fmt);

dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs);

va_end(vargs);

return dev;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: