19 linux字符设备驱动

记得内核里是用面向对象的思想来实现的。但不是完全面向对象.
在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    3

dev_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 3344
mknod /dev/mydev1 c 1234 3345
mknod /dev/mydev2 c 1234 3346

然后可以写应用程序来操作设备文件,也可以用命令来简单地测试。
cat /dev/mydev0  //会触发驱动里的openread函数
echo "kkk" > /dev/mydev0   //会触发驱动里的open, write函数.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值