一、结构体:
a、字符设备结构体:
<include/linux/cdev.h>
struct cdev {
struct kobject kobj; //内嵌的内核对象.
struct module *owner; //该字符设备所在的内核模块的对象指针.
const struct file_operations *ops; //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.
struct list_head list; //用来将已经向内核注册的所有字符设备形成链表.
dev_t dev; //字符设备的设备号,由主设备号和次设备号构成.
unsigned int count; //隶属于同一主设备号的次设备号的个数.
};
b、文件操作结构体:
#include<linux/fs.h>
struct file_operations {
struct module *owner;//拥有该结构的模块的指针,一般为THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);//用来修改文件当前的读写位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读取数据
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作
int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替
int (*mmap) (struct file *, struct vm_area_struct *); //用于请求将设备内存映射到进程地址空间
int (*open) (struct inode *, struct file *); //打开
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *); //关闭
int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待处理的数据
int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据
int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
二、函数:
a、初始化函数:
---> 申请设备号:
register_chrdev_region()是为提前知道设备的主次设备号的设备分配设备编号。
<linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
//first
//是你要分配的起始设备编号.
//first 的次编号部分常常是 0, 但是没有要求是那个效果.
//count
//是你请求的连续设备编号的总数. 注意, 如果 count 太大, 你要求的范围可能溢出到下一个主设备号;
//但是只要你要求的编号范围可用, 一切都仍然会正确工作.
//name
//设备的名字; 它会出现在 /proc/devices 和 sysfs 中.
//return value
//如果分配成功进行, register_chrdev_region 的返回值是 0
//出错的情况下, 返回一个负的错误码, 你不能存取请求的区域
alloc_chrdev_region() 是动态分配主次设备号。动态分配设备号可以避免手动指定设备号时带来的缺点,但是它却也有自己的缺点,那就是无法预知在/dev下创建设备节点,因为动态分配设备号不能保证在每次加载驱动module时始终一致(其实若在两次加载同一个驱动module之间并没有加载其他的module,那么自动分配的设备号还是一致的,因为内核分配设备号并不是随机的,但是书上说某些内核开发人员预示不久的将来会用随机方式进行处理),不过,这个缺点可以避免,因为在加载驱动module后,我们可以读取/proc/devices文件以获得Linux内核分配给该设备的主设备号。---from 百度百科
<linux/fs.h>
int alloc_chrdev_region(dev_t *dev,unsigned int -firstminor,unsigned int -count,char *name);
//-firstminor
//第一个次设备号,一般为0
//-count
//分配的设备数量
//*name
//设备的名称
register_chrdev()是老版本的注册方式。
---> 字符设备初始化:
静态内存定义初始化:
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
动态内存定义初始化:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &fops;
my_cdev->owner = THIS_MODULE;
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
---> 添加到系统:
cdev_add()是将字符设备添加到系统。
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
//字符设备结构体
//设备号
//设备数量
b、卸载函数:
unregister_chrdev_region()是将字符设备从系统中释放。
#include<linux/fs.h>
void unregister_chrdev_region(dev_t first, unsigned int count);
//first
//第一个设备号
//count
//申请的设备数量