Linux字符设备
要做的就是初始化 添加 删除: struct cdev
Linux字符设备就是一个结构体:
struct cdev {
struct kobject kobj;
struct module *owner;//填充时,值要为 THIS_MODULE,表示模块
const struct file_operations *ops;//这个file_operations结构体,注册驱动的关键,要填充成这个结构体变量
struct list_head list;//用来将已经向内核注册的所有字符设备形成链表
dev_t dev;//设备号,主设备号+次设备号
unsigned int count;//次设备号个数
};
这个结构体的作用就是一种按字符/字节进行数据读写的设备,比如键盘、鼠标、串行端口等。
其中每个设备都需要一个自己的设备号,可以通过以下方法向内核申请:
设备号dev_t 高12位是主设备号,低20位是次设备号(可静态分配和动态分配)//32位机占4个字节的情况下
申请: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);//动态申请
void unregister_chrdev_region(dev_t from, unsigned count);//释放or注销设备
(通过判断给定的主设备号是否为0来进行区别,为0的时候为动态注册,否则静态注册)
以下是cdev的添加步骤:
1.申请cdev内存 :struct cdev *cdev_alloc(void);
还有人这样写: struct cdev *my_cdev = cdev_alloc();//利用内核的kmalloc函数为这个结构体分配堆空间
2.初始化cdev成员 void cdev_init(struct cdev *p, const struct file_operations *p);//file_operations就是一套调用函数的集合
有人这样写: void cdev_init(struct cdev *cdev, struct file_operations *fops);//实则是将struct cdev类型的结构体变量和file_operations结构体进行绑定
3.申请设备号
4.向内核里添加一个驱动 int cdev_add(struct cdev *p, dev_t dev, unsigned count);//struct cdev *p - 被注册的cdev对象
//dev_t dev - 设备的第一个设备号
//unsigned - 这个设备连续的次设备号数量
有人这样写: cdev_add(struct cdev *dev, dev_t num, unsigned int count);
//要等到驱动完全准备好处理设备时再添加
5.移除cdev对象 void cdev_del(struct cdev *p);
6.释放cdev内存 void cdev_put(struct cdev *p);
用到的头文件
//<linux/init.h>:进程头文件,内核启动的用户级进程;内核引导和初始化
//<linux/module.h>:动态的加载模块到内核中去
//<linux/kernel.h>:内核头文件,含有一些内核常用的函数原形定义,像调度程序这样的核心子程序
//<linux/cdev.h>:字符设备头文件
//<linux/fs.h>:文件系统头文件,定义文件表结构(flie,buffer_head,m_inode)
//<linux/errno.h>:错误代码头文件
//<asm/current.h>:定义current指针,供内核引用当前进程
//<Linux/sched.h>:中定义current指向当前在运行的进程,在例如open()和read()时指向的是发出调用的进程
//<linux/slab.h>:内存分配机制,不按页分配内存,直接按字节分配
//通过MODULE_LICENSE("GPL")宏声明此模块的许可证,还有"GPLv2","GPL and additional rights","Dual BSD/GPL"等
然后开始定义文件操作,就是file_operations中的操作,举例:demo_open:
/*
static int demo_open(struct inode *inode, struct file *filp)
{
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
//get major and minor from inode
printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n", imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
return 0;
}
*/current指针中:comm当前进程执行的程序文件名,pid当前进程的pid号
(printk:相比于printf多了一个日志级别的设置,用到的KERN_INFO是6级。日志级别小于控制台级别才打印在控制台)
再例如demo_read:
/*
static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
struct inode *inode = filp->f_path.dentry->d_inode;
//get command and pid
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n", current->comm, current->pid, __FILE__, __func__, __LINE__);
//get major and minor from inode
printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n", imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);
return 0;
}
*/其中只多了一行指定inode指针的代码
把这些定义的操作统一存在操作方法集
/*
static struct file_operations fops = {
.owner = THIS_MODULE, .open = demo_open,
.release= demo_release,
.read = demo_read,
.write = demo_write,
};
*/
下一步,进入正题:申请内存,初始化cdev成员,注册/添加(add)cdev对象到内核中,用完之后移除cdev,释放内存。
需要注意,上述demo中file_operations中的打开、关闭、读、写操作还未自定义,只是一个框架,具体代码后续再添加
static __init int mydev_init(void)
{
/* 申请设备号 */
alloc_chrdev_region(&dev_id, 1, 1, "mydev");
/* 分配字符设备 */
mydev = cdev_alloc();//动态分配的
/* 设置字符设备,与fops绑定 */
cdev_init(mydev, &fops);
/* 注册字符设备 */
cdev_add(mydev, dev_t, 1);
/* 打印申请到的主次设备号 */
printk("major:%d; minor:%d\n", MAJOR(dev_id), MINOR(dev_id));
return 0;
}
static __exit void mydev_exit(void)
{
cdev_del(mydev);
kfree(mydev);
unregister_chrdev_region(dev_id, 1);
}
module_init(mydev_init);
module_exit(mydev_exit);
补充:
1.应用层缓冲区buf的内容要复制到内核中,不能直接用memcpy,因为二者不在一个地址空间中。要用memset、copy_from_user
2.在Linux文件系统中,每个文件都用一个struct inode结构体来描述,这个结构体里面记录了这个文件的所有信息,
例如:文件类型,访问权限等。
3.每个驱动程序在应用层的/dev目录下都会有一个设备文件和它对应。
4.每个驱动程序都要分配一个主设备号,字符设备的设备号保存在struct cdev结构体中。
5.在Linux操作系统中,每打开一次文件,Linux操作系统在VFS层都会分配一个struct file结构体来描述打开的这个文件。
该结构体用于维护文件打开权限、文件指针偏移值、私有内存地址等信息。
6.struct inode描述的是文件的静态信息,即这些信息很少会改变。
而struct file描述的是动态信息,即在对文件的操作的时候,struct file里面的信息经常会发生变化。