设备号---主设备号,次设备号
字符设备通过字符设备文件来存取。字符设备文件由使用ls -l的输出的第一列的c标识。其中的两个数字,分别为主设备号和次设备号。
设备号作用-----
主设备号----字符设备文件通过主设备号和字符设备驱动对应。
次设备号--被驱动程序用来辨别操作的是哪个设备。
总结---主设备号用来反映设备类型,次设备号用来区分同类型的设备。
内核中描述设备号---dev_t
其实质为unsigned int 32位整数,其中高12位为主设备号,低20位为次设备号。
主设备号---MAJOR(dev_t dev)
次设备号--MINOR(dev_t dev)
分配主设备号---
---静态申请
方法:
1--根据Documentation/devices.txt确定一个没有使用的主设备号
2--使用register_chrdev_region函数注册设备号
缺点--随机选定的主设备号可能会导致设备号冲突,而使驱动程序无法注册。
int register_chrdev_region(
dev_t from,申请使用的设备号
unsigned count,申请使用的设备号数目
const char *name设备名(体现在/proc/devices)
)
申请使用从from开始的count个设备号(主设备号不变,次设备号增加)
---动态分配
方法:
使用alloc_chrdev_region分配设备号
缺点--无法在安装驱动前创建设备文件(因为安装前还没有分配到主设备号)
解决办法---安装驱动后,从/proc/devices中查询设备号。
int alloc_chrdev_region(
dev_t *dev,分配到的设备号,获取值
unsigned baseminor,起始次设备号
unsigned count,需要分配的设备号数目
const char *name设备名(体现在/proc/devices)
)
功能--请求内核动态分配count个设备号,且此设备号从baseminor开始。
注销设备号----不再使用它们时释放这些设备号
void unregister_chrdev_region(dev_t from,unsigned conunt)
功能:释放从from开始的count个设备号。
创建设备文件---
1--使用mknod
mknod filename type major minor
filename---设备文件名
type---设备文件类型
major---主设备号
minor---次设备号
mknod serial0 c 100 0
2--自动创建
重要结构----字符设备驱动程序设计
struct file---代表一个打开的文件。系统中每个打开的文件在内核空间都有一个关联的struct file。 它由内核在打开文件时创建,在文件关闭后释放。
重要成员----
loff_t f_pos---文件读写位置
struct file_operations *f_op--
一个函数指针的集合,定义在设备上进行的操作。结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。
struct file_operations mem_fops=
{
.owner=THIS_MODULE,
.llseek=mem_seek,
.read=mem_read,
.write=mem_write,
.ioctl=mem_ioctl,
.open=mem_open,
.release=mem_release,
};
struct inode----用来记录文件的物理上的信息。因此,它和代表打开文件的file结构是不同的。一个文件可以对应多个file结构,但只有一个inode结构。
重要成员---
dev_t i_rdev---设备号
设备注册----
在linux2.6内核中,字符设备使用struct cdev来描述。
字符设备的注册可分为如下3步----
1---分配cdev
struct cdev *cdev_alloc(void)
返回一个cdev结构
2---初始化cdev
void cdev_init(
struct cdev *cdev,待初始化的cdev结构
const struct file_operation *fops设备对应的操作函数集
)
3--添加cdev
int cdev_add(
struct cdev *p,待添加到内核的字符设备结构。
dev_t dev,设备号
unsigned conunt添加的设备个数
)
设备操作实现---设备支持的操作
int (*open)(struct inode *,struct file *)
在设备文件上的第一个操作,并要求驱动程序一定要实现这个方法。如果该项为NULL,设备的打开操作永远成功。
是驱动程序用来为以后的操作完成初始化准备工作的。一般完成以下工作,
初始化设备,标明次设备号。
void (*release) (struct inode *,struct file *)
当设备文件被关闭时调用这个操作。与open相仿,release也可以没有。
关闭设备
ssize_t (*read) (struct fiel *,char __user *,size_t,loff_t *)
从设备中读取数据,读取数据到用户空间
ssize_t (*write) (struct fiel *,const char __user *,size_t,loff_t *)
向设备发送数据,将数据传递给驱动程序。
对于以上两个方法,filp是文件指针,count是请求传输的数据量。buff参数指向数据缓存。最后,offp指出当前的访问位置。
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)
unsigned int (*poll) (struct file *,struct poll_table_struct *)
对应select系统调用
int (*ioctl) (struct inode *,struct file *,unsigned int ,unsigned long)
控制设备
int (*mmap) (struct file *,struct vm_area_struct *)
将设备映射到进程虚拟地址空间中
off_t (*llseek) (struct file *,loff_t,int)
修改文件的当前读写位置,并将新位置作为返回值
设备注销----
int cdev_del(struct cdev *p)
p---要注销的字符设备结构。