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

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来操作设备了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值