您的位置:首页 > 移动开发 > Android开发

android 字符设备驱动程序的一点小总结

2013-03-13 11:57 411 查看
1. 分配设备号:

extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);

extern int register_chrdev_region(dev_t, unsigned, const char *);

第一种方式是静态分配,适用于指定主设备号的情况

第二种是动态分配,主设备号是动态分配的。

2. 注册设备:

extern int register_chrdev(unsigned int, const char *,

struct file_operations *);

如果前面两步不成功就要注销设备和注销设备号

注销设备:

extern int unregister_chrdev(unsigned int, const char *);

注销设备号:

extern void unregister_chrdev_region(dev_t, unsigned);

说明:

extern int register_chrdev(unsigned int, const char *,

struct file_operations *);

作用:注册一个字符串设备

unsigned int 是主设备号;

const char*是设备名;

struct file_operations*是文件结构体。

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

要想注册一个设备则需要一个主设备号,那么就需要给设备分配一个主设备号。

这个函数的作用:就是给一个设备分配设备编号的。

first是要分配设备编号范围的起始值。

count是所请求连续设备编号的个数。不能太大。

name是设备名,(要与该设备编号范围关联)

如果分配成功,则返回0,

如果分配失败,则返回负的错误码。这时候所请求的编号区域不能用。

注:申请是只是一个设备编号区域。

extern int alloc_chrdev_region(dev_ *dev, unsigned int firstminor, unsinged int count, char *name)

说明:

1.如果我们提前知道设备的编号,那么就用register_chrdev_region(),这个可以工作的很好。

2.但是如果我们不知道呢,linux一直想解决这个问题,这就有了上面的函数。

这个函数是动态申请设备编号,这样就不用考虑设备编号的问题了,这个函数分自动分配一个给我们。

*dev是一个输出参数,当设备成功完成调用后,它将保存已分配范围的第一个编号。

firstminor是要请求的第一个次设备号。一般为0。

count同样是所请求连续设备编号的次数。

*name同样是相应和设备名。

一旦给一个设备分配好了设备号后,我们就可以通过cat /proc/devices读取到我们的设备。

3, cdev初始化

cdev_init

cdev_alloc

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

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; // 设备范围号大小

};

一个 cdev 一般它有两种定义初始化方式:静态的和动态的。

静态内存定义初始化:

struct cdev my_cdev;

cdev_init(&my_cdev, &fops);

my_cdev.owner = THIS_MODULE;

动态内存定义初始化:

struct cdev *my_cdev = cdev_alloc();

my_cdev->ops = &fops;

my_cdev->owner = THIS_MODULE;

两种使用方式的功能是一样的,只是使用的内存区不一样,一般视实际的数据结构需求而定。

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);

INIT_LIST_HEAD(&cdev->list);

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

cdev->ops = fops;

}

由此可见,两个函数完成都功能基本一致,只是 cdev_init() 还多赋了一个 cdev->ops 的值。

4, cdev_add()

初始化 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);

kobject_put(&p->kobj);

}

5, udev自动创建设备结点

可以用mknod来命令手动创建设备节点,也可以使用 class_create(),device_create自动创建设备文件结点

驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用 device_create 创建对应的设备。大致用法如下:

struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);

device_create (myclass , NULL, dev, "%s", “my_device”);

这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件。

设备节点创建以后就可以通过open read write等方式,或者通过ioctl来操作设备了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: