一:字符设备开发的基本步骤
1.确定主设备号和次设备号
- 主设备和号是内核识别一类设备的标识。整数(占12位)一般1-255
- 次设备号由内核使用,用于正确确定设备文件所指的设备。整数(20位)一般0-255
- 设备编号的内部表示
- dev_t类型(32位)
用来保存设备编号(包括主设备号(12位)和次设备号(20位))
- 从dev_t获得主设备号和次设备号:
- MAJOR(dev_t);
- MINOR(dev_t);
- 将主设备号和次设备号转换成dev_t类型:
- MKDEV(int major,int minor);
- 分配主设备号
- 手工分配主设备号:找一个内核没有使用的主设备号来使用
- #include<linux/fs.h>
- int register_chrdev_region(dev_t first,unsigned int count,char*name);
- 自动分配
- #include<linux/fs.h>
- int alloc_chrdev_region(dev_t*dev,unsigned int firstminor,unsigned int count,char*name);
- 手工分配主设备号:找一个内核没有使用的主设备号来使用
注意:dev是一个输出指针,firstminor是要使用的被请求的第一个次设备号
- 释放设备号
- void unregister_chrdev_region(dev_t dev,unsigned int count);通常在模块的清除函数中调用。
2.实现字符驱动程序
- 实现file_operations结构体
- 实现初始化函数,注册字符设备
- 实现销毁函数,释放字符设备
- 实现字符设备其他基本成员函数
- 实现字符驱动程序
- cdev结构体
struct cdev
{struct kobject kobj; //内嵌的kobject对象
struct module *owner; //所属模块,通常是自己
struct file_operations *ops;//文件操作结构体
struct list_head list;
dev_t dev; //设备号
unsigned int count;
}
-
- 操作cdev的函数
void cdev_init(struct cdev*cdev,struct file_operations *fops); //cdv与file_operations建立联系
struct cdev *cdev_alloc(void); // 动态建立cdev
int cdev_add(struct cdev*dev,dev_t num,unsigned count);//把字符设备添加进内核
void cdev_del(struct cdev*cdev); //删除设备
-
- file_operations结构体
- 字符驱动和内核的接口在include/linux/fs.h中定义
- 字符驱动只要实现一个file_operations结构体
- 当注册到内核中内核就有了操作此设备的能力。
- file_operations的主要成员:
- file_operations结构体
struct module *owner: 指向模块自身:THIS_MODULE
open:打开设备
release:关闭设备
read:从设备上读数据
write:向设备上写数据
llseek:定位当前读写位置指针
mmap:映射设备空间到进程的地址空间
-
- file结构体
- file结构:
- file_operation结构体相关的一个结构体
- 描述一个正在打开的设备文件
- 成员:
- loff_t f_ops:
- 当前读写位置
- unsigned int f_flags
- 标识文件打开时,是否可读或可写
- 0_RDONLY
- O_NONBLOCK
- O_SYNC
- struct file_operation *f_op
- 文件相关的操作,指向所实现的struct file_operations
- void *private_data:
- 私有数据指针。驱动程序可以将这个字段拥有任何目的或者忽略这个字段。
- file结构:
- inode结构体
- 内核用inode结构体在内部表示文件
- inode与file区别
- file表示打开的文件描述符
- 多个表示打开的文件描述符的file结构,可以指向单个inode结构
- inode结构中的两个主要字段:
- dev_t i_rdev;
- 对表示设备文件的inode结构,该字段包含了真正的设备编号
- struct cdev *i_cdev;
- struct cdev是表示字符设备的内核的内部结构
- 当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针。
- 从一个inode中获得主设备号和次设备号
- unsigned int iminor(struct inode *inode);
- unsigned int imajor(struct inode *inode);
- file结构体
- 注册设备,在模块或驱动初始化时调用
- linux-2.4及以前
- int register_chrdev(unsigned int major,const char *name ,struct file_operations *fops);
- linux-2.6
- void cdev_init(struct cdev *dev,struct file_operations *fops);
- int cdev_add(struct cdev *dev,dev_t num,unsigned count);
- linux-2.4及以前
- 注销设备:在模块卸载时调用
- linux-2.4及以前
- int unregister_chrdev(unsigned int major,const char *name);
- linux-2.6
- void cdev_del(struct cdev *dev);
- linux-2.4及以前
- 设备驱动模块加载模板
static int __init xxx_init(void)
{
----
cdev_init(&xxx.dev.cdv,&xxx_fops);
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);注销设备
}
3.创建设备文件节点