一、scull介绍
1.scull(Simple Character Utility for loading Localities),即区域装载的简单字符工具。scull是一个操作内存区域的字符设备驱动程序,这片区域相当于一个设备。
2.优点
scull和硬件无关,而只是操作从内核中分配的一些内存,任何人都可以编译和运行scull,并且可以将scull移植到Linux支持的计算机平台上。
3.工作过程
①Linux启动时运行加载驱动模块的脚本。insmod时调用模块的init函数进行初始化,调用cdev_init( )和cdev_add( )来进行字符设备的初始化,并把这个设备添加进入系统。这个过程会创建/proc/modules、/proc/devices两个文件和/sys/devices目录中的对应项目。
②用户空间的程序通过系统调用open打开设备(eg:fopen("/dev/scull0",“w”)),此时Linux会生成一个file结构,其中包含f_pos(位置指针),f_mode(打开方式是否只读等)等状态信息。然后调用模块中定义的open( )函数,把刚生成的file结构作为参数传给open( )。open( )通常需要根据情况做一些诸如设置互质标记位之类的工具。
③用户空间的程序通过系统调用进行读写操作(eg:使用fprintf( )等函数),会调用驱动中的read( ),write( ),llseek( )等函数。
④此时若有另一个用户程序打开这个设备文件,则会再创建一个file结构。
⑤每一个程序完成操作,关闭设备文件时,会销毁对应的file结构。但是,只有最后一个进程关闭文件时才会调用驱动程序的release( )函数。
⑥关机时调用驱动模块的exit函数,释放资源。
二、设备与驱动工作关联
1.Linux内核中
①使用cdev结构体来描述字符设备
②通过cdev的成员dev_t来定义设备号(主、次设备号)来确定字符设备的唯一性
③通过cdev的成员file_operations来定义字符设备驱动提供的VFS的函数接口
如open( ),read(),write( )等。
2.Linux字符设备驱动中
①模块加载函数通过register_chrdev_region( )或alloc_chrdev_region( )来静态或动态获取设备号。
②通过cdev_init( )建立cdev与file_opreations之间的联系,通过cdev_add( )向系统添加一个cdev以完成注册。
③模块卸载通过cdev_del( )来注销cdev,通过unregister_chrdev_region( )来释放设备号。
3.用户空间访问该设备程序
通过Linux系统调用,如open( ),read( ),write( ),来调用file_opreations来定义字符设备提供给VFS的接口函数。
三、函数源代码介绍
1.cdev结构体(用来描述一个字符设备)
<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; //隶属于同一主设备号的次设备号的个数.
};
2.cdev_init()函数
作用:建立cdev和file_opreations之间的联系。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev); //将整个结构体清零初始化。
INIT_LIST_HEAD(&cdev->list); //初始化list成员使其指向自身
kobject_init(&cdev->kobj, &ktype_cdev_default)