Linux设备驱动开发之字符设备驱动

Linux 下的驱动程序,不论是字符设备还是paltfrom 虚拟总线设备,看起来很复杂,其实都是固定的一套流程,只要步骤整理出来,实际开发的时候,直接套用模板就可以。

Linux下字符设备的开发遵循一套固定的流程,1. 创建设备 (动态、静态创建)  2. 申请设备号 (主、次设备号)  3. 实现设备的操作方法(file_operations结构体中描述的方法) 4. 向内核中注册设备(将设备号、设备操作方法与设备结构体绑定注册到内核) 5. 在module_exit()函数中实现资源的释放,主要是设备号的释放和注册设备的卸载。6 . 加载驱动模块,创建设备文件 7. 编写应用程序,测试驱动程序。

首先Linux下的驱动程序是按照内核模块的方式进行编写,编译完后要使用Insmod 指令注册到内核中,驱动的入口函数是内核模块的加载函数,也就是再module_init()函数中实现驱动的加载,在module_exit()函数实现驱动的卸载。

下面 我们介绍内核字符设备驱动的编写流程。

1.  创建设备,即定义设备结构体,分为动态和静态两种创建方式。

头文件 #include <linux/cdev.h>

static struct cdev chrdev;          //静态方式创建设备结构体

struct cdev *cdev_alloc(void);   //动态方式创建设备结构体

2. 申请设备号,设备号分为主设备号和次设备号,主设备号用于唯一的确定设备的驱动程序,次设备号用于唯一的确定在同一类驱动程序下的某个设备。

 在Linux下用dev_t 来描述设备号(dev_t 是32位数值类型,其中高12位表示主设备号,低20位表示次设备号)。

头文件 #include <linux/kdev.h>

MAJOR(dev_t dev);   //根据设备号分离出主设备号

MAJOR(dev_t dev);   //根据设备号分离出次设备号

MKDEV(int major,int minor); //根据主设备号和次设备号还原设备号。

静态申请设备号: 使用register_chrdev_region函数分配设备号,优点简单,缺点容易导致设备号冲突,且需要先查阅内核源码的Documentation/devices.txt文件,确定一个未被使用的主设备号,再进行设备号的注册。

动态申请设备号:使用 alloc_chrdev_region 函数分配设备号优点简单,缺点无法在安装驱动前创建设备文件(此时不知道设备号),需要在驱动加载后,从/proc/devices中查询设备号,手动创建设备文件。

头文件: #include <linux/fs.h>

静态申请函数:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

功能: 申请使用从 from 开始的 count 个设备号(主设备号 不变,次设备号增加)

参数:

from:希望申请使用的设备号

count:希望申请使用设备号数目

name:设备名(体现在/proc/devices)

动态申请函数:

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

功能:请求内核动态分配 count 个设备号,且次设备号从 baseminor开始。

参数:

dev:分配到的设备号

baseminor:起始次设备号

count:需要分配的设备号数目

name:设备名(体现在/proc/devices)

3.实现字符设备的操作方法,主要是填充file_operation结构体。

定义fire_operation结构体,并实现里面的操作方法。

static struct file_operations chr_dev_fops = {

.owner = THIS_MODULE,

.open = chr_dev_open,         //一般用于设备的初始化

.release = chr_dev_release,  //一般用于释放设备初始化使用的资源

.write = chr_dev_write,    //向设备写入数据

.read = chr_dev_read,   //从设备读出数据

.lseek = chr_dev_lseek,  //修改文件当前的读写位置

.ioctl = chr_dev_ioctl,    //用于设备控制指令

};

其中编写write 和read 方法是需要注意内存空间的转换,write 是将用户空间的数据写入内核空间,read 是将内核空间的数据读取到用户空间,linux 系统提供了一组内存空间转换函数。

头文件: #include <linux/fs.h>

int copy_from_user(void *to, const void __user *from, int n)

int copy_to_user(void __user *to, const void *from, int n)

参数: to 目标数据地址,from 源数据地址, n 转换字节长度 

4. 向内核中注册设备,使用cdev_init  绑定设备操作方法和设备结构体,使用 cdev_add 绑定设备号和设备结构体并进行注册。

头文件: #include <linux/cdev.h>

设备结构体初始化:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

参数:

cdev: 待初始化的cdev结构

fops: 设备对应的操作函数集

设备结构体注册:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数:

p: 待添加到内核的字符设备结构

dev: 设备号

count: 添加的设备个数

5. 在module_exit函数中实现设备卸载时资源的释放。

释放设备号资源:

头文件:#include <linux/fs.h>

void unregister_chrdev_region(dev_t from, unsigned count)

释放从from开始的count个设备号

卸载注册的设备:

头文件:#include <linux/cdev.h>

int cdev_del(struct cdev *p)

参数: p 要注销的字符设备结构

 6. 加载驱动模块,创建设备文件。

编译驱动内核模块,加载驱动模块到内核:insmod chrdev.ko

进入 /proc/devices 目录下,查找设备对应的主设备号,执行 mknod /dev/chrdev c 248 0 创建设备文件。

7. 编写应用程序进行内核驱动的测试。

在Linux中一切设备皆文件,对文件的读写即是对设备的数据操作,我们可以使用Linux提供的操作接口open、close、read、write,也可以使用C标准库中的接口(推荐使用,可移植性好)fopen、fclose、fwrite、rfread对设备进行操作。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值