【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
Linux字符设备驱动传统框架
1. 驱动常见类型解释
// 函数操作集
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 *);
......
};
struct cdev {
struct kobject kobj;
struct module *owner; //模块所有者(THIS_MODULE),用于模块计数
const struct file_operations *ops; //函数操作集
struct list_head list;
dev_t dev; //设备号(主设备号 + 次设备号)
unsigned int count; //设备数量
};
2. 驱动的注册
static int __init chrdev_init(void)
{
...
/* 构造cdev设备对象 */
struct cdev *cdev_alloc(void);
/* 初始化cdev设备对象 */
void cdev_init(struct cdev*, const struct file_opeartions*);
/* 申请设备号,静态or动态*/
/* 为字符设备静态申请第一个设备号 */
int register_chrdev_region(dev_t from, unsigned count, const char* name);
/* 为字符设备动态申请第一个设备号 */
int alloc_chrdev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);
ma = MAJOR(dev) //从dev_t数据中得到主设备号
mi = MINOR(dev) //从dev_t数据中得到次设备号
MKDEV(ma,1) //将主设备号和次设备号组合成设备号,多用于批量创建/删除设备文件
/* 注册字符设备对象cdev到内核 */
int cdev_add(struct cdev* , dev_t, unsigned);
...
}
3. 驱动的注销
static void __exit chrdev_exit(void)
{
...
/* cdev_del()、cdev_put()二选一 */
/* 从内核注销cdev设备对象 */
void cdev_del(struct cdev* );
/* 从内核注销cdev设备对象 */
void cdev_put(stuct cdev *);
/* 回收设备号 */
void unregister_chrdev_region(dev_t from, unsigned count);
...
}
4. 驱动的操作函数
ssize_t led_write(struct file *pfile, const char __user *userbuf, size_t size, loff_t *loff)
{
// long copy_from_user(void *to, const void __user * from, unsigned long n)
return 0;
}
ssize_t led_read(struct file *pfile, char __user *userbuf, size_t size, loff_t *loff)
{
// long copy_to_user(void __user *to, const void *from, unsigned long n)
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write
};
5. 创建驱动的设备文件, 使用udev
前置操作: (有了这些准备,只需要导出相应的设备信息到"/sys"就可以按照我们的要求自动创建设备文件) (二选一)
1.在编译内核时添加如下选项:
Device Drivers —>
Generic Driver Options —>
[ * ]Maintain a devtmpfs filesystem to mount at /dev
[ * ]Automount devtmpfs at /dev,after the kernel mounted the rootfs
OR 2. 在根文件系统的启动脚本写入以下内容(/etc/init.d/SXXYYY)
mount -t sysfs none sysfs /sys
mdev -s//udev也行
class_create(owner,name);
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
void class_destroy(struct class *cls);
void device_destroy(struct class *cls, dev_t devt);
6. 下面给出完整代码, 无具体的功能
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
static dev_t dev_num = 0;
static struct cdev *cdevice = NULL;
static struct class *sys_class = NULL;
static struct device *class_device = NULL;
int led_open(struct inode *pinode, struct file *pfile)
{
return 0;
}
ssize_t led_write(struct file *pfile, const char __user *userbuf, size_t size, loff_t *loff)
{
//long copy_from_user(void *to, const void __user * from, unsigned long n)
return 0;
}
ssize_t led_read(struct file *pfile, char __user *userbuf, size_t size, loff_t *loff)
{
// long copy_to_user(void __user *to, const void *from, unsigned long n)
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write
};
static int __init led_init(void)
{
cdevice = cdev_alloc(); // 构建cdev设备对象
cdev_init(cdevice, &led_fops); // 初始化cdev设备对象, 将它与操作函数集绑定起来
#if 0 // 申请设备号, 静态 or 动态.
register_chrdev_region(dev_t from, unsigned count, const char * name)
#else
alloc_chrdev_region(&dev_num, 0, 1, "yangbkLed");
#endif
cdev_add(cdevice, dev_num, 1);
sys_class = class_create(THIS_MODULE, "yangbkClass"); // 在/sys中新建设备类
class_device = device_create(sys_class, NULL, dev_num, NULL, "yangbkDevice"); // 在sys_class指向的类中创建一组(个)设备文件
return 0;
}
static void __exit led_exit(void)
{
device_destroy(sys_class, dev_num);
class_destroy(sys_class);
#if 0 // 向内核注销cdev设备对象
cdev_put(cdevice);
#else
cdev_del(cdevice);
#endif
unregister_chrdev_region(dev_num, 1); // 回收设备号.
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("yangbkGit");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("traditional driver model.");
MODULE_ALIAS("model");