【linux kernel 3.18】字符设备驱动创建和注销流程

【linux kernel 3.18】字符设备驱动创建和注销流程

一、前言

前言主要阐述了编写该技术文档的目的,使用的平台环境,以及看懂该文档所需要掌握的基本知识,方便学习者按图索骥,填充相关的知识体系。

linux驱动开发的过程中,我们每天会接触到大量的代码,会遇到不同的总线和设备,有时会忘记某些设备驱动的注册、使用和注销的流程,或者由于没有系统化地掌握、注册注销流程的一些细节,导致代码编译不通过,通过了却无法调试成功,使得工程师本应该专注于核心代码的实现和优化,却花费了大量的时间去检测框架上的错误。因此,本技术文档的第一个目的,就是将驱动设备的注册、注销过程流程化,加快编写效率,也方便工程师查漏。

但是,为了突出流程化的这个特点,该文档应当具有的一个特点就是,即使是一个尚未接触过linux驱动(但有一定编程基础)的新人,看到该文档也应该能够大致明白整个流程,从而迅速上手,实现某些驱动功能,完成某些较为简单的工作任务。因此,该文档也应当具有一定的机制原理介绍。

本文就是在技术性和流程化的两端中取平衡。

内核版本Linux 3.18linux不同版本的驱动框架差异较大,尤其是kernel 3.x版本引入了设备树,与2.x版本有着巨大的差别,因此本文中所讲的代码、机制,不一定适用于所有版本,要根据工作环境来灵活选择是否采用本文中的内核机制)

预备知识C语言(熟练)、 内核模块的加载与卸载(熟悉)、linux总线驱动设备框架(熟悉)

*程度水平

了解:阅读过相关的技术资料,知道其中的技术原理,但并没有经过实践。

熟悉:阅读过相关的工程代码或者开放源代码,知道该项知识是如何应用到实际的工程中的。

熟练:能够熟练地应用该项知识完成一定的工程任务。

精通:能够详细地阐述该项知识的技术实现细节,能够分析该项知识在实际应用中客观存在的优点和缺点,并能够横向对比相似的技术,比较它们的优劣之处。

 

*英文名限定

*英文名限定

为了区分代码中的英文和普通的英文,方便读者阅读理解,在此将正文中出现的英文进行了划分,划分如下:

英文正文:英文名首部不加任何解释

英文函数名:英文名后添加小括号,如device_init()。为了节省行文篇幅,在正文中就不写返回类型和函数参数了

英文变量/结构体:英文名首部添加”.”,.bus_type

所有的函数名和英文变量/结构体都是该函数/变量在内核代码中的实际名称,没有缩写或者简写,可以直接搜索内核工程找到相应的定义

 

二、字符设备创建流程

 

1.创建cdev结构体

例:

struct cdev xxx_cdev;

 

2.创建dev_t结构体,并调用MKDEV完成dev_t的创建(若要动态创建则只需创建结构体即可)

例:

dev_t devno=MKDEV(major,minor);

 

其中major是字符设备的主设备号,范围是0-255;minor是字符设备的次设备号,范围是0-255

 

3.调用register_chrdev_region(dev_t from,unsigned count,char *name)申请字符设备号

例:

register_chrdev_region(devno,1,”xxx_device”);

其中第一个参数为已经MKDEV好的dev_t结构体,第二个参数是要注册的设备数,第三个是设备的名称。

若担心主设备号被占用,也可采用动态分配的方式。

alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);

其中第一个参数为未调用MKDEV的空dev_t结构体,第二个参数是次设备号(若要创建多个设备则该项为次设备号起始编号),第三个是要注册的设备数,第四个是设备名称。

 

4.创建file_operation结构体并填充相关函数

例:

static const struct file_operations xxx_fops={

.owner=THIS_MODULE,

.read=xxx_read,

.write=xxx_write

.ioctl=xxx_ioctl,

.open=xxx_open,

.release=xxx_release,

.unlocked_ioctl=xxx_unlocked_ioctl,

};

接下来分析常用的成员组成。

 ssize_t (*read)(struct file *filp,char __user * buf,size_t count,loff_t *ppos)

filp为文件指针,buf为用户空间的字符串首地址,count为读取的字节长度,ppos为当前文件的读写偏移地址。

ssize_t (*write)(struct file *filp,char __user * buf,size_t count,loff_t *ppos)

filp为文件指针,buf为用户空间的字符串首地址,count为要写入的字节长度,ppos为当前文件的读写偏移地址。

readwrite的实例化函数常用copy_from_usercopy_to_user来完成对用户空间的读写操作。

△copy_to_user(void __user *to, const void *from, unsigned long n);

△第一个参数为要写入的用户空间的首字节地址,第二个参数为要读取内核空间的首字节地址,第三个参数为要写入的字节数。

△copy_from_user(void *to, const void __user *from, unsigned long n);

△第一个参数为要写入的内核空间的首字节地址,第二个参数为要读取的用户空间的首字节地址,第三个参数为要写入的字节数。

ioctl已经被unlocked_ioctl取代。

long (*unlocked_ioctl)(struct file* filp,unsigned int cmd,unsigned long arg);

第一个参数为文件指针,第二个参数为指令类型,第三个参数为传入的自定义参数。

int (*release)(struct inode*,struct file*);

关闭文件时调用。

int (*open)(struct inode*,struct file*);

开启文件时调用。

 

以上函数指针所指向的函数实体均需自己实现,如要在write()中实现设备实体的写操作比如总线通信或者 GPIO通信等。

 

5.初始化cdev,关联cdevfops

例:

cdev_init(cdev* ,file_operations *);

两个参数均是指针

 

6.添加cdev设备

cdev_add(cdev *,dev_t,int);

第一个参数为cdev指针,第二个参数是已经注册了的dev_t,第三个参数为要创建的设备数。

 

7.创建设备节点

 例:

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

device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);

首先调用class_create创建一个新的设备类,然后调用device_create将之前生成的dev_t传入,即可在/dev下找到对应的字符设备。

函数原型如下:

struct class  *class_create(struct module *owner,  const char *name) ;

struct device *device_create(struct class *class,   //指定所要创建的设备所从属的类

struct device *parent, //这个设备的父设备,如果没有就设置为NULL

dev_t devt,             //设备号

void *drvdata, //自定义驱动数据

const char *fmt, ...)//设备名称

 

以上,完成字符设备的创建。该创建流程可单独成型,也可嵌入其它总线驱动创建的过程之中。

 

 

三、字符设备注销流程

1.调用cdev_del(cdev *)注销cdev

2.调用device_destroy(class *,dev_t)和class_destroy(class*) 注销设备节点和设备类

例:

device_destroy(my_class, MKDEV(adc_major, 0));//delete device node under /dev

 class_destroy(my_class);  //delete class created by us

3.调用unregister_chrdev_region(dev_t,int )释放设备号

    其中dev_t为已经注册了的dev_t,int为设备数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值