字符设备驱动(1)
基于imx6ull以及其使用手册
linux驱动
外设主要分为
- 字符设备
感觉就是简单的i/o?- 以字节驱动为单位,顺序访问,字符设备面向流设备,
- 蜂鸣器,led,鼠标,键盘
- 块设备
- 访问块设备时候,是以扇区过着块为基础,属于无序访问
- 网络设备
- 网络设备就是网络适配器等用来上网的设备
- 网卡分为有线和无线两种
字符设备驱动
- 字符设备驱动在linux系统中有其规定的框架
常规字符设备驱动
为设备定义一个相关结构体
初始化函数 xxx_init
- 向系统申请设备号
- 申请设备内存
- 调用cdev_init()初始化
- 调用cdev_add()向系统注册设备
卸载函数 xxx_exit的定义
- 释放设备号
- 调用cdev_add()
定义 file_operations
- 实现write()
- 实现read()
- 根据需要实现其他函数
//机构体模块
struct A{
struct cdev cdev;
};
struct A *dev;//创建一个设备结构体指针
dev_t devno;//设备号结构体
//读设备
ssize_t xxx_read(struct file *p,char __user *buf,size_t count,loff_t* f_pos)
{
copy_to_user(buf, , , );
}
//写设备
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t* f_pos)
{
copy_from_user(..., buf, ...);
}
//文件操作函数file_operations
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.read = xxx_read,
.write = xxx_write,
};
//设备驱动模块加载函数
static int __init xxx_init(void)
{
...
devno = MKDEV(xxx_major, 0);
//(1)申请设备号
if(xxx_major)
{
register_chrdev_region(devno, 1, "xxx_dev");
}
else
{
alloc_chrdev_region(&devno, 0, 1, "xxx_dev");
}
//(2)为设备结构体申请内存(推荐使用devm_kzalloc)
dev = kzalloc(sizeof(struct xxx_dev_t), GFP_KERNEL);
//(3)初始化cdev
cdev_init(&dev.cdev, &xxx_fops);
dev.cdev.owner = THIS_MODULE;
//(4)向系统注册设备
cdev_add(dev.cdev, dev_no, 1);
}
module_init(xxx_init);
//设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
//释放设备号
unregister_chrdev_region(dev_no, 1);
//注销设备
cdev_del(&dev.cdev);
...
}
module_exit(xxx_exit);
MODULE_LICENSE("GPL v2");
代码解释
-
首先定义了机构体,直接定义了结构体cdev
struct cdev { struct kobject kobj; //内嵌的kobject 对象 struct module *owner; //所属模块 const struct file_operations *ops; //文件操作结构体 struct list_head list; dev_t dev; //设备号 unsigned int count; };
-
然后定义了设备号机构体,并在第37行申请了设备号、
-
实现了文件操作函数总的部分功能。
file_operations
中的成员函数似乎字符设备驱动程序的主要内容 -
设备驱动模块加载函数
-
为此函数注册为设备驱动的入口函数
-
初始化cdev,向系统注册设备
简化版字符设备驱动框架
可以使用这个两个函数对上面的函数进行简化
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
//注册字符设备,返回申请的主设备号
static inline void unregister_chrdev(unsigned int major, const char *name)
//注销字符设备
//通过这两个函数就不用一步一步的去注册设备了
两个文件就实现注册设备了,具体文件于fs/char_dev.c
设备号
· 各种设备都以文件的形式放在/dev 目录下,称为设备文件,应用程序可以打开,关闭,读写这些设备文件,完成对应的设备的操作。
组成
-
一个字符设备或者块设备都有一个主次设备号。
-
主设备号都用来表示一个特定的驱动程序。
-
次设备号永安里表示该驱动程序的各个设备
-
-
dev_t的数据类型表示设备号
typedef __u32 __kernel_dev_t; typedef __kernel_dev_t dev_t;
-
dev_t是一个32位变量
linux设备号范围是0 - 4095
-
分配
-
写好驱动之后,运行驱动
-
驱动编译到linux内核中,linux内核运行时候会自动运行驱动程序
-
驱动程序编译后缀为.ko的模块文件
-
安装和卸载.ko的命令
insmod xxx.ko
安装驱动rmmod xxx.ko
卸载驱动实际上是调用module_init函数,这个函数会注册一个加载函数模块,使用上述命令的时候就调用了函数
-
安装和卸载驱动
modprobe xxx.ko
//安装模块modprobe -r xxx.ko
//卸载模块这个命令在加载模块时候,会同时加载该模块所依赖的其他模块
-
-
实验课题
(2)