普通字符设备驱动的两种注册方式

文章转自 https://blog.csdn.net/qq_28992301/article/details/52343585

普通字符设备驱动的两种注册方式(新&旧)

在内核中,对于一个普通的字符设备驱动,不难发现有两种注册方式:

  1. register_chrdev族函数+创建设备类、文件的函数:这种方法是2.4版本流行的旧方法。优点是简单;缺点是无法指定次设备号
  2. register_chrdev_region族+cdev族+创建设备类、文件的函数:这种方法是2.6版本推荐的新方法。优点是可以设置主次设备号;缺点是比较复杂

1.旧版本的注册方式

/*需要的定义*/
int test_major = -1;
static struct class *mydev_pclass;
#define TEST_NAME   "test"
#define MYDEV_CNT     3

/*正式注册*/
test_major = register_chrdev(0, TEST_NAME, &test_fops);
if (test_major < 0) {
        printk(KERN_INFO "register test error\n");
        goto out_err_0;
}

/*用class_create创建一个设备类,这是创建设备文件的前置任务*/
mydev_pclass = class_create(THIS_MODULE, "mydevice");
if (IS_ERR(mydev_pclass)) {     //排错
printk(KERN_ERR "can't register device mydevice class\n");
        goto out_err_1;
}

/*由device_create正式创建设备文件*/
device_create(mydev_pclass, NULL, MKDEV(test_major, 0), NULL, "mydevice");

return 0;

/*倒影式错误处理流程*/
out_err_2:
        class_destroy(mydev_pclass);

out_err_1:
        unregister_chrdev(test_major);

out_err_0:
        return -EINVAL;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • register_chrdev族函数是形成驱动设备的核心。register_chrdev的作用是申请并注册一个设备号、构建一个字符设备体、向内核注册此字符设备
  • register_chrdev函数的major参数可以设置主设备号,若major参数设为0则系统会自动分配一个设备号(这种做法比较可靠、方便)。register_chrdev的返回值就是分配到的主设备号,一般赋给一个全局变量来传递给unregister_chrdev注销函数,注销函数需要主设备号来作为参数
  • 模块卸载函数内必须要包含unregister_chrdev函数,与这里的register_chrdev对应,只不过负责的是注销
  • 最后由内核通知udev(即mdev)来创建一个位于/dev的设备文件。这一步其实是创建API与设备体之间的纽带,这样APP才能通过API操作设备文件(即调用 file_operations内的函数)。主要分为两步class_create先实例化新建一个设备类,然后由device_create正式创建设备文件
  • 值得注意的是,创建设备文件的前提是系统开机时已经启动了udev(即mdev)功能。可以在/etc/init.d/rcS 或/etc/inittab中设置相关命令,详见手动构建rootfs及文件功能分析
  • 关于错误处理,这里使用了内核中常见的goto方法。错误处理就是前面那些操作的逆操作,旨在发生错误时释放那些申请的资源,执行顺序必须符合“倒影式错误处理流程”

2.新版本的注册方式

/*需要用到的定义*/
#define MYDEV_NUM       MKDEV(MYMAJOR, 0)
#define MYNAME      "mydevice"
#define MYDEV_CNT     3

static struct cdev *mydev_pcdev;
static struct class *mydev_pclass;
dev_t mydev_num = 0;
unsigned int mydev_major = 0;

/*正式开始注册*/
/*让内核给我们自动分配设备号(主次设备号)*/
ret = alloc_chrdev_region(&mydev_num, 0, MYDEV_CNT, MYNAME);
mydev_major = MAJOR(mydev_num);
if (ret < 0) {
        printk(KERN_INFO "alloc_chrdev_region fail\n");
        goto out_err_0;
}
printk(KERN_INFO "MAJOR %d\n", mydev_major);

/*cdev_alloc实例化一个字符设备体。为cdev分配内存*/
mydev_pcdev = cdev_alloc();

/*填充cdev设备体。最主要是将file_operations填充进去*/
cdev_init(mydev_pcdev, &mydev_fops);

/*将设备体与设备号绑定并向内核注册一个字符设备*/
ret = cdev_add(mydev_pcdev, MYDEV_NUM, MYDEV_CNT);
if (ret) {
        printk(KERN_INFO "cdev_add error\n");
        goto out_err_1;
}

/*用class_create创建一个设备类,这是创建设备文件的前置任务*/
mydev_pclass = class_create(THIS_MODULE, "mydevice");
if (IS_ERR(mydev_pclass)) {     //排错
printk(KERN_ERR "can't register device mydevice class\n");
        goto out_err_2;
}

/*由device_create正式创建设备文件*/
device_create(mydev_pclass, NULL, MYDEV_NUM, NULL, "mydevice");

return 0;

/*倒影式错误处理流程*/
out_err_3:
        class_destroy(mydev_pclass);

out_err_2:
        cdev_del(mydev_pcdev);

out_err_1:
        unregister_chrdev_region(mydev_num, MYDEV_CNT);

out_err_0:
        return -EINVAL;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 关于分配主次设备号,register_chrdev_region是用户自定义设备号的申请。并且可以通过参数设置设备的个数(即次设备号) ,设备号是通过MKDEV这个宏获得,它可以通过主设备号和次设备号来计算设备号。其实更方便的做法是使用alloc_chrdev_region让内核给我们自动分配一个,这样就不会导致设备号之间重复。
  • 利用cdev_alloc实例化一个字符设备体,为cdev分配内存 。使用cdev_alloc分配内存的好处是cdev_del时内核会为我们自动释放cdev占用的空间,而如果我们自己malloc的话就要自己释放了。
  • 利用cdev_init填充cdev设备体 ,最主要是将file_operations填充进去。有的驱动里面是没用cdev_init()的,而是手动去填充,例如 mydev_pcdev-> owner = THIS_MODULE; mydev_pcdev-> ops = &mydev_fops; 说到底cdev_init()其实就相当于上面两句
  • 利用cdev_add将设备体与设备号绑定,并向内核注册一个字符设备
  • 最后由内核通知udev(即mdev)来创建一个位于/dev的设备文件。这一步其实是创建API与设备体之间的纽带,这样APP才能通过API操作设备文件(即调用 file_operations内的函数)。主要分为两步class_create先实例化新建一个设备类,然后由device_create正式创建设备文件
  • 值得注意的是,创建设备文件的前提是系统开机时已经启动了udev(即mdev)功能。可以在/etc/init.d/rcS 或/etc/inittab中设置相关命令,详见手动构建rootfs及文件功能分析

  • 关于错误处理,这里使用了内核中常见的goto方法。错误处理就是前面那些操作的逆操作,旨在发生错误时释放那些申请的资源,执行顺序必须符合“倒影式错误处理流程”

文章转自 https://blog.csdn.net/qq_28992301/article/details/52343585


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值