Linux系统字符设备驱动框架笔记
来源:才华咖 本文已影响1.22W人
来源:才华咖 本文已影响1.22W人
不积跬步,何以至千里。掌握知识都是从很小的点开始的。下面是小编整理的Linux系统字符设备驱动框架笔记,欢迎阅读!
字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标、键盘、显示器、串口等等,当我们执行 ls -l /dev 的时候,就能看到大量的设备文件, c 就是字符设备, b 就是块设备,网络设备没有对应的设备文件。编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码。
驱动模型
Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在 struct file_operations ,当我们写一个驱动的时候,一定要实现相应的接口,这样才能使这个驱动可用,Linux的内核中大量使用"注册+回调"机制进行驱动程序的编写,所谓注册回调,简单的理解,就是当我们open一个设备文件的时候,其实是通过VFS找到相应的inode,并执行此前创建这个设备文件时注册在inode中的'open函数,其他函数也是如此,所以,为了让我们写的驱动能够正常的被应用程序操作,首先要做的就是实现相应的方法,然后再创建相应的设备文件。
#include //for struct cdev
#include //for struct file
#include //for copy_to_user
#include //for error number
/* 准备操作方法集 */
/*
struct file_operations {
struct module *owner; //THIS_MODULE
//读设备
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//写设备
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//映射内核空间到用户空间
int (*mmap) (struct file *, struct vm_area_struct *);
//读写设备参数、读设备状态、控制设备
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//打开设备
int (*open) (struct inode *, struct file *);
//关闭设备
int (*release) (struct inode *, struct file *);
//刷新设备
int (*flush) (struct file *, fl_owner_t id);
//文件定位
loff_t (*llseek) (struct file *, loff_t, int);
//异步通知
int (*fasync) (int, struct file *, int);
//POLL机制
unsigned int (*poll) (struct file *, struct poll_table_struct *);
。。。
};
*/
ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
return 0;
}
struct file fops = {
r = THIS_MODULE,
= myread,
...
};
/* 字符设备对象类型 */
struct cdev {
//public
struct module *owner; //模块所有者(THIS_MODULE),用于模块计数
const struct file_operations *ops; //操作方法集(分工:打开、关闭、读/写、...)
dev_t dev; //设备号(第一个)
unsigned int count; //设备数量
//private
...
};
static int __init chrdev_init(void)
{
...
/* 构造cdev设备对象 */
struct cdev *cdev_alloc(void);
/* 初始化cdev设备对象 */
void cdev_init(struct cdev*, const struct file_opeartions*);
/* 为字符设备静态申请设备号 */
int register_chedev_region(dev_t from, unsigned count, const char* name);
/* 为字符设备动态申请主设备号 */
int alloc_chedev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);
MKDEV(ma,mi) //将主设备号和次设备号组合成设备号
MAJOR(dev) //从dev_t数据中得到主设备号
MINOR(dev) //从dev_t数据中得到次设备号
/* 注册字符设备对象cdev到内核 */
int cdev_add(struct cdev* , dev_t, unsigned);
...
}
static void __exit chrdev_exit(void)
{
...
/* 从内核注销cdev设备对象 */
void cdev_del(struct cdev* );
/* 从内核注销cdev设备对象 */
void cdev_put(stuct cdev *);
/* 回收设备号 */
void unregister_chrdev_region(dev_t from, unsigned count);
...
}
实现read,write
Linux下各个进程都有自己独立的进程空间,即使是将内核的数据映射到用户进程,该数据的PID也会自动转变为该用户进程的PID,由于这种机制的存在,我们不能直接将数据从内核空间和用户空间进行拷贝,而需要专门的拷贝数据函数/宏:
long copy_from_user(void *to, const void __user * from, unsigned long n)
long copy_to_user(void __user *to, const void *from, unsigned long n)
这两个函数可以将内核空间的数据拷贝到回调该函数的用户进程的用户进程空间,有了这两个函数,内核中的read,write就可以实现内核空间和用户空间的数据拷贝。
ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
long ret = 0;
size = size > MAX_KBUF?MAX_KBUF:size;
if(copy_to_user(user_buf, kbuf,size)
return -EAGAIN;
}
return 0;
}
Linux系统时间设置
Linux系统管理:linux修改键盘按键
Linux系统下本机数据的自动备份
Ubuntu Linux系统下设置Static IP的方法
linux系统集群的架构与实现
Linux 系统下光驱软开关与限速介绍
Linux认证系统管理:linux下搭建ftp
Linux认证基础知识:linux操作系统目录结构
如何查看Linux系统架构类型
Linux系统中怎么挂载外界设备
PHP脚本修改Linux或Unix系统口令方法
Linux关闭selinux安全子系统的技巧
Linux系统调用设备的ioctl函数
Linux操作系统学习笔记权限管理
实用的Linux/Unix系统磁带管理命令
Win8电脑不能装Linux系统怎么办
红旗Linux系统配置LILO文件的方法
学习Linux操作系统必备基础知识
PetaLinux操作系统在MicroBlaze系统中的移植解析
设备采购框架协议书
Linux系统下的uname命令
Linux系统中smbclient命令的使用方法
管理信息系统课设指导及写作框架范本
在linux系统中安装virtualbox增强功能(增强包)的详细步骤是什么
Linux系统备份的应用与技巧
Win10中如何安装SUSE Linux子系统
Linux系统中hexdump的命令汇总
Linux操作系统文件系统基础知识
Linux系统启动的大致过程
linux系统常用的系统信息查看命令
linux操作系统要查看系统进程的办法
Linux操作系统上的系统管理命令
Linux系统shell工具打印输出
Linux系统下动态库的生成介绍
Linux操作系统下串口设置及编程
Linux/AIX系统实用监控命令
Linux系统守护进程的启动方法
linux系统如何安装网卡驱动
系统架构设计师
Linux系统xlsatom命令如何使用