#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
//设备结构体
struct xxx_dev_t{
struct cdev cdev;
...
}xxx_dev;
/*打开*/
int xxx_open(struct inode *inode, struct file *filp)
{
/******************************************************************************************
struct inode被内核用来代表一个文件,注意和struct file的 区别,struct inode代表文件,
struct file代表打开的文件;同一个文件可以被打开好多次,所以可以对应很多struct file,
但是只对应一个struct inode。
******************************************************************************************/
...
}
int xxx_release(struct inode *inode, struct file *filp)
{
//release释放相当于close
...
}
/* 读设备*/
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t*f_pos)
{
...
copy_to_user(buf, ..., ...);
/*******************************************************************************************************
1、设备驱动的读写函数中,filp是文件结构体指针,buf是用户空间内存的地址,该地址在内核空间不能直接读写,
count是要读写的字节数,f_pos是读写的位置相对于文件 开头的偏移。
2、由于内核空间与用户空间的内存不能直接互访,因此借助函数 copy_from_user()完成用户空间到内核空间的复制,函数 copy_to_user()完成内核空间到用户空间的复制。
copy_from_user()和copy_to_user()的原型如下所示:
unsigned long copy_from_user(void *to, const void _ _user *from, unsigned long count);
unsigned long copy_to_user(void _ _user *to, const void *from, unsigned long count);
**********************************************************************************************************/
...
}
/* 写设备*/
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
/**************************************************************************************************
//filp:与字符设备文件关联的file结构指针, 由内核创建。
//buff : 从设备读取到的数据,需要保存到的位置。由read系统调用提供该参数。
//count: 请求传输的数据量,由read系统调用提供该参数。
//offp: 文件的读写位置,由内核从file结构中取出后,传递进来。
/*buff参数是来源于用户空间的指针,这类指针都不能被内核代码直接引用,
必须使用专门的函数:
int copy_from_user(void *to, const void __user *from, int n)
int copy_to_user(void __user *to, const void *from, int n)*/
************************************************************************************/
...
copy_from_user(..., buf, ...);
...
}
/* ioctl函数 */
int xxx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd)
{
case XXX_CMD1:
...
break;
case XXX_CMD2:
...
break;
default: /* 不能支持的命令 */
return - ENOTTY;
}
return 0;
}
struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_release,
.read = xxx_read,
.write = xxx_write,
.ioctl = xxx_ioctl,
...
};
//设备驱动模块加载函数
static int __init xxx_init(void)
{
/**********************************************************************
分配cdev:
cdev变量的定义可以采用静态和动态两种办法
静态分配:struct cdev mdev;
动态分配: struct cdev *pdev = cdev_alloc();
初始化cdev:
struct cdev的初始化使用cdev_init函数来完成。
cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数:
cdev: 待初始化的cdev结构
fops: 设备对应的操作函数集
注册cdev:
字符设备的注册使用cdev_add函数来完成。
cdev_add(struct cdev *p, dev_t dev, unsigned count)
参数:
p: 待添加到内核的字符设备结构
dev: 设备号
count: 该类设备的设备个数
***********************************************************************/
...
cdev_init(&xxx_dev.cdev, &xxx_fops); //初始化cdev
xxx_dev.cdev.owner = THIS_MODULE;
if (xxx_major) //获取字符设备号
{
register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
}
else
{
alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
}
ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); //注册设备 ...
}
/*设备驱动模块卸载函数*/
static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no, 1); //释放占用的设备号
cdev_del(&xxx_dev.cdev); //注销设备
...
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_AUTHOR("XXXX");
MODULE_LICENSE("GPL");//许可证
驱动模型
最新推荐文章于 2022-08-28 20:43:44 发布