驱动框架图
struct file_operations(include/linux/fs.h)
1.系统调用和驱动程序关联起来的数据结构。(I/O设备的存取操作通过入口进行,入口点是设备驱动程序提供的,设备驱动程序接口是由结构file_operations结构体向系统说明的)。
2.结构每个成员对应一个系统调用。
3.读取其中的函数指针,把控制权转交给函数,完成驱动程序。
驱动操作内核数据结构:file_operations、file、inode。
struct file_operations字符设备驱动的操作和设备号联系在一起。
struct inode
{
dev_t i_rdev; 设备文件的设备号
struct cdev *i_cdev; 代表字符设备的数据结构
}
struct file打开的文件,在执行file_operation中的open操作时被创建。
区别:
file与用户inode指针:inode在内核,file指针在用户空间,由c库来定义。
struct inode代表文件,struct file代表打开的文件。
文件可以打开多次,对应很多struct file,struct inode唯一。
struct module *owner;
不是操作, 指向拥有该结构的模块的指针,避免操作时被卸载,一般为初始化为THIS_MODULES (<linux/module.h>中定义的宏).
loff_t (*llseek) (struct file *, loff_t, int);
函数指针修改文件当前的读写位置,返回新位置.
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
函数指针用来读取数据。成功返回读取的字节数.
ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);
异步读可能在函数返回前不结束的读操作.
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
函数指针发送数据给设备.
ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);
函数指针异步的写入操作
int (*readdir) (struct file *, void *, filldir_t);
函数指针读取目录,对于设备文件,该字段为 NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *);
函数指针返回掩码,用来指出非阻塞的读取或写入是否可能。
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
函数指针执行设备特殊命令的方法
int (*mmap) (struct file *, struct vm_area_struct *);
请求将设备内存映射到进程的地址空间.
int (*open) (struct inode *, struct file *);
指针函数用于打开设备.
int (*flush) (struct file *);
指针函数用于在进程关闭设备文件描述符副本时。
int (*release) (struct inode *, struct file *);
函数指针在file结构释放时,将被调用
int (*fsync) (struct file *, struct dentry *, int);
函数指针用于刷新待处理的数据.
int (*aio_fsync)(struct kiocb *, int);函数对应异步fsync.
int (*fasync) (int, struct file *, int);
函数指针用于通知设备FASYNC标志发生变化.
int (*lock) (struct file *, int, struct file_lock *);
文件加锁;
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
实现发散/汇聚读和写操作. 应用程序偶尔需要做一个包含多个内存区的单个读或写操作.
ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);
系统调用的读,拷贝文件通过描述符.
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
内核调用来发送数据, 一次一页, 到对应的文件.
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
进程的地址空间映射在底层设备的内存段.
int (*check_flags)(int);模块检查传递给 fnctl(F_SETFL…);
调用的标志.
int (*dir_notify)(struct file *, unsigned long);
程序使用fcntl来请求目录改变通知调用.
file_operations结构初始化:
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
GPIO驱动框架(字符设备)
驱动框架代码
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
static struct class *pin_class;
static struct device *pin_class_dev;
static dev_t devno; //设备号
static int major =127; //主设备号
static int minor =0; //次设备号
static char *module_name="pin"; //模块名
static int pin_open(struct inode *inode,struct file *file)
{
printk("pin open sucess\n");
return 0;
}
static ssize_t pin_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
printk("pin write success\n");
return 0;
}
static struct file_operations pin_fops = {
.owner = THIS_MODULE,
.open = pin_open,
.write = pin_write,
};
int __init pin_drv_init(void)
{
int ret;
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&pin_fops); //注册驱动,驱动加入驱动链表
pin_class = class_create(THIS_MODULE,"pinMoudle"); // /dev生成设备
pin_class_dev =device_create(pin_class,NULL,devno,NULL,module_name); //创建设备文件
return 0;
}
void __exit pin_drv_exit(void)
{
device_destroy(pin_class,devno); //清除设备文件
class_destroy(pin_class); //清除设备
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(pin_drv_init); //模块入口
module_exit(pin_drv_exit); //模块退出
MODULE_LICENSE("GPL v2"); //宏声明许可证
驱动框架测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int fd;
fd = open("/dev/pin",O_RDWR);
if(fd < 0)
{
printf("open fail\n");
perror("why");
exit(-1);
}
write(fd,"hello word",10);
return 0;
}
测试流程
ubuntu:
cd /root/linux-rpi-4.14.y/drivers/char
(驱动框架代码)
vim pin_test.c
(修改Makefile,添加驱动模块)
vim Makefile
obj-m += pin_test.o
cd /root/linux-rpi-4.14.y
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 modules
(测试代码)
vim pinTest.c
arm-linux-gnueabihf-gcc pinTest.c -o pinPest
生成(/root/linux-rpi-4.14.y/drivers/char):pin_test.ko,pinPest上传文件到Pi
Pi:
insmod pin_test.ko
lsmod|grep pin
ls -lrt /dev/| tail -f
chmod 666 /dev/pin
./pinTest
dmesg|grep pin|grep success
rmmod pin_test
lsmod|grep pin|grep suc
测试图例
驱动安装成功
驱动文件生成,修改权限
测试代码执行,查看内核成功信息
卸载驱动