1、数据结构说明
我们在分析一个内核驱动程序时,总是从数据结构开始分析,那我们驱动程序也
应该如此:从定义数据结构-->初始化数据结构--> 申请设备号-->通知内核-->创建
文件节点。【最基本的路线】
那我们应该来看看字符驱动的三个重要的数据结构:struct inode;struct
file; struct file_operations。图下图:
图1:三个重要又基本的数据结构关系图[图摘自LINUX设备驱动开发详解]
从左往右分析:
1)一个字符设备最基本应该对应一个 struct cdev;它的作用是什么?
2) struct cdev 含有dev_t 设备号成员、struct file_operations
3) struct file_operations 都是系统调用的函数接口,操作方法==>以实现
与用户空间的通信。
4)通知和加载、卸载都是与 struct cdev 紧密相关的,可见它的作用之大。
5)用户空间的系统调用,最终是在内核中struct file_operations结构中实现。
从中我们也可以看到如何写一个简单的字符设备驱动程序:
字符设备创建
[1]【设备编号注册】
[2]【设备结构分配和初始化】
[3]【文件操作接口】实现
[4]【字符设备添加】
[5]【设备文件节点创建】
字符设备销毁
[1] 【设备文件节点销毁】
[1] 【字符设备删除】
[2] 【设备编号回收】
/**********************函数说明*********************/
【设备编号获取】
[头文件]
#include dev_t定义
#include 操作宏定义
/*
* @brief 获取主编号
* @param[in] dev 设备编号
* @return 主编号
*/
MAJOR(dev_t dev);
/*
* @brief 获取次编号
* @param[in] dev 设备编号
* @return 次编号
*/
MINOR(dev_t dev);
/*
* @brief 合成设备编号
* @param[in] major 主编号
* @param[in] minor 次编号
* @return 设备编号
*/
MKDEV(major, minor);
[头文件]
#include
/*
* @brief 获取次编号
* @param[in] inode 设备文件节点
* @return 次编号
*/
static inline unsigned iminor(const struct inode *inode);
/*
* @brief 获取主编号
* @param[in] inode 设备文件节点
* @return 主编号
*/
static inline unsigned imajor(const struct inode *inode);
【设备编号注册】
[头文件]
#include
/*
* @brief 分配设备编号
* @param[in] first 起始设备编号
* @param[in] count 分配编号数量
* @param[in] name 设备名称(在/proc/devices文件中可见)
* @return =0 分配成功
* <0 错误码
*/
int register_chrdev_region(dev_t first, unsigned int count, char
*name);
/*
* @brief 动态分配设备编号
* @param[out] dev 设备编号
* @param[in] firstminor 分配第一个次编号
* @param[in] count 分配编号数量
* @param[in] name 设备名称(在/proc/devices文件中可见)
* @return == 0 分配成功
* < 0 错误码
*/
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name);
//////////////////////////////////////
为何要采用动态而非静态的呢?
动态申请设备号:内核自动帮我们获取空闲的设备号,无需人工去查阅系统我们预留哪些设备号。
//////////////////////////////////////
【设备编号回收】
[头文件]
#include
/*
* @brief 回收设备编号
* @param[in] first 第一个次编号
* @param[in] count 分配编号数量
*/
void unregister_chrdev_region(dev_t first, unsigned int count);
【设备结构分配和初始化】
[头文件]
#include
/*
* @brief 动态分配struct cdev结构(描述一个字符设备)
* @return struct cdev结构指针
*/
struct cdev *cdev_alloc(void);
/*
* @brief 初始化struct cdev结构
* @param[in] cdev struct cdev结构已经定义存在
* @param[in] fops 驱动操作接口,为文件操作提供响应
* @notes 函数会自动做cdev->ops = fops;但是没有
dev->cdev.owner = THIS_MODULE;
*/
void cdev_init(struct cdev *cdev, struct file_operations *fops);
【字符设备添加】
[头文件]
#include
/*
* @brief 增加一个字符设备到系统
* @param[in] p 描述字符设备的struct cdev结构
* @param[in] dev 关联到这个设备的第一个设备号
* @param[in] count 关联到这个设备的设备号个数
* @return == 0 添加成功
* < 0 错误码
* @notes 函数执行成功后,立即可以使用该字符设备,也就是说调用这个之前设备驱动已经完全就绪
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
【字符设备删除】
[头文件]
#include
/*
* @brief 删除一个字符设备
* @param[in] cdev 描述字符设备的struct cdev结构
*/
void cdev_del(struct cdev *dev);
【设备文件节点创建】
[头文件]
#include
/*
* @brief 建立一个类结构
* @param[in] owner 模块所有者
* @param[in] name 类名
* @return 类结构
* IS_ERR(返回值) 判断是否是错误码
* PTR_ERR(返回值) 取得错误码
*/
struct class *class_create(struct module *owner, const char *name);
/*
* @brief 建立设备并且注册它到sysfs中
* @param[in] class 设备所要注册到的类
* @param[in] parent 父设备
* @param[in] devt 字符设备号
* @param[in] drvdata 驱动私有数据
* @param[in] fmt 格式化的设备名
* @return 返回设备结构
* IS_ERR(返回值) 判断是否是错误码
* PTR_ERR(返回值) 取得错误码
*/
struct device *device_create(struct class *class, struct
device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
【设备文件节点销毁】
[头文件]
#include
/*
* @brief 设备销毁
* @param[in] class 设备所属类
* @param[in] devt 设备号
*/
void device_destroy(struct class * class, dev_t devt);
/*
* @brief 类销毁
* @param[in] class 要销毁的类
*/
void class_destroy (struct class * cls);
阅读(1493) | 评论(2) | 转发(0) |