19 linux字符设备驱动

来源:互联网 发布:sql case when 多行 编辑:程序博客网 时间:2024/06/11 10:11

记得内核里是用面向对象的思想来实现的。但不是完全面向对象.
在linux内核里使用”struct cdev”类型的一个对象来描述一个字符设备驱动。

#include <linux/cdev.h>struct cdev {    struct kobject kobj;       //内核用于管理字符设备驱动, kobject就是内核里最底层的类. 内核里会自动管理此成员.    struct module *owner;      //通常设为THIS_MODULE, 用于防止驱动在使用中时卸载驱动模块    const struct file_operations *ops;  //怎样操作(vfs), 也就是实现当用户进程进行open/read/write等操作时,驱动里对应的操作.    struct list_head list;     //内核链表节点,内核里自动管理此成员.    dev_t dev;                 //设备号    unsigned int count;        //设备数};
同时内核里也提供对cdev对象操作的函数:void cdev_init(struct cdev *, const struct file_operations *); //初始化cdev对象struct cdev *cdev_alloc(void); //动态分配一个cdev对象int cdev_add(struct cdev *, dev_t, unsigned); //设置cdev对象使用设备号及设备个数, 再把cdev对象增加到内核里,让它工作起来.void cdev_del(struct cdev *); //从内核里移除cdev对象

////////字符设备驱动实现的基本流程//////////

1. 申请设备号 register_chrdev_region(...);2. 声明一个cdev对象    struct cdev mycdev;   声明一个file_operations的文件操作对象    struct file_operations fops = {        .owner = THIS_MODULE,        .read = 读函数地址        ....    };3. 初始化cdev对象,并把fops对象与cdev对象关联起来    cdev_init(&mycdev, &fops); //mycdev.ops = &fops;    mycdev.owner = THIS_MODULE;     4. 把cdev对象加入内核里cdev_map(字符设备驱动的哈希表), 并指定该驱动对象的设备号    cdev_add(&mycdev, 设备号, 次设备号的个数);5. 卸载模块时, 要把设备驱动对象从内核里移除, 并把设备号反注册    unregister_chrdev_region(..);    cdev_del(&mycdev);

////////////////////////////////////////////
测试代码test.c:

#include <linux/init.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/cdev.h>#define MYMA  1234#define MYMI  3344#define COUNT    3dev_t devid; //用于存放设备号struct cdev mycdev; int myopen(struct inode *ind, struct file *fl){    printk("in %s\n", __func__);    return 0;}ssize_t myread(struct file *fl, char *__user buf, size_t len, loff_t *off){    printk("in %s\n", __func__);    return 0;}ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off){    printk("in %s\n", __func__);    return len;}struct file_operations fops = {    .owner = THIS_MODULE,    .open = myopen,    .read = myread,    .write = mywrite,};static int __init test_init(void){    int ret;    devid = MKDEV(MYMA, MYMI); //生成一个设备号    ret = register_chrdev_region(devid, COUNT, "mydev");    if (ret < 0)        goto err0;    //执行到这里,则有三个设备号(1234,3344), (1234, 3345), (1234, 3346)    cdev_init(&mycdev, &fops);    mycdev.owner = THIS_MODULE;    ret = cdev_add(&mycdev, devid, COUNT);    if (ret < 0)        goto err1;      return 0;err1:    unregister_chrdev_region(devid, COUNT);err0:    return ret;}static void __exit test_exit(void){    //使用完后需回收设备号    unregister_chrdev_region(devid, COUNT);    cdev_del(&mycdev);}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");

///////////////////////////
编译加载驱动模块后,需要用”mknod /dev/设备文件名 c 主设备号 次设备号”来创建设备文件.

mknod /dev/mydev0 c 1234 3344mknod /dev/mydev1 c 1234 3345mknod /dev/mydev2 c 1234 3346然后可以写应用程序来操作设备文件,也可以用命令来简单地测试。cat /dev/mydev0  //会触发驱动里的openread函数echo "kkk" > /dev/mydev0   //会触发驱动里的open, write函数.