Linux驱动编程day4--实现硬件控制

当有多个任务共同使用一个设备,为防止内存泄漏,解决办法:1、上锁,只允许一个任务占用;2、建立一个链表,为每个任务分配独自的空间,用链表来管理内存空间。
设备号以及各个描述符、套接字、进程号都可以看成是数组的下标。

在用户进程和设备驱动程序之间会有一个虚拟文件系统,它负责将系统调用与驱动函数相关联。
Linux驱动编程day4--实现硬件控制_第1张图片
虚拟文件系统(VFS)为上层的用户提供了一套标准的文件操作接口,如(open, read, write),对下,对文件系统提供一个标准的接口,以便其他操作系统的文件系统可以方便的移植到Linux上,如 file_operations, inode_operations, dentry_operation,而具体的文件系统必须实现这些接口,才能融入VFS框架中。

实现简单操作接口:
当我们访问一个设备结点时,系统是通过设备号来确定具体使用哪一个驱动程序来访问哪一个设备。所以我们在创建文件节点的时候,需要指定主设备号和次设备号。
Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。
驱动模块需要加载到linux内核中才能使用,因此,我们在模块加载程序中就需要向内核进行注册,得到设备号

dev_t g_devno;
static int __init led_init(void){ //加载驱动
int result;
    printk(DEVICE_NAME":led world in kernel module\n"); //不能用printf

    result = alloc_chrdev_region(&g_devno, 0, DEVICE_COUNT, DEVICE_NAME);//0表示主设备号从几开始,1表示有几个设备
    if(result < 0){
        printk("alloc_chrdev_region fail\n");
        return result;
    }
}

为了避免每次都需要手动创建设备结点,在代码中就实现设备结点的自动创建:

struct cdev g_cdev;
dev_class = class_create(THIS_MODULE, DEVICE_NAME); //创建结构体空间
    if(dev_class == NULL){
        printk("device node create fail\n");
    }
    else{
        int i;
        //dev_t dev = MKDEV(major, 0); 把主设备号和次设备号合成一个32位的数
        for(i= 0; i < DEVICE_COUNT; i++)
            device_create(dev_class, NULL, g_devno+i, NULL, "%s%d","led",i);//自动创建设备结点
    }

注册设备号的函数有很多,常见的有register_chrdev();它一般是用作注册一个已经的设备号,手动指定;alloc_chrdev_region();这个函数是按linux内核分配一个设备号,动态分配,一般应使用动态分配设备号。
int alloc_chrdev_region(dev_t *dev, unsigined int firstminor, unsigned int count, char *name);
第一个参数要传入dev_t 类型的变量的地址,作为输出参数,用来保存分配到的范围内的第一个数,firstminor应当是请求的第一个要用的次编号,通常为0,count为设备的数量,name是驱动程序的名字
dev是一个32位的数,它的前12位表示主设备号,后20位表示次设备号
不管如何分配设备编号,在不再使用时,应该释放它:
void unregister_chrdev_region(dev_t first, unsigned int count);

我们已经拥有一些设备号,但是如何将其与驱动操作连在一起呢?file_operations结构就是这个桥梁,这个结构体定义在fs.h中,它是一群函数的指针集合,每个所打开的文件都存在一个f_op指针指向file_operations结构体,里面的操作大部分主要完成系统调用,如open,read等。我们可以将file看成对象,对它进行的操作看成是方法。
一般来说,一个指向file_operations结构的指针称为fops。这个结构体里面的每个域必须指向驱动中的某些函数以完成一些特定的操作,或者赋予NULL值表示没有支持的操作。当被赋予NULL时,内核的具体行为对每个函数来说都不尽相同。
file_operations结构中的各种操作方法时,我们可以发现有许多的参数含有”__user”,用以表明这是用户层空间的指针,在内核中我们不能直接对它进行引用。
事实上,file_operations结构体中的函数就是实现系统调用的接口。而具体的控制硬件, 就在这些函数中实现。

此外,我们还需要关注另一个结构:inode结构
内核使用inode结构体在内核内部表示一个文件。因此,它与表示一个已经打开的文件描述符的结构体(即file 文件结构)是不同的,我们可以使用多个file 文件结构表示同一个文件的多个文件描述符,但此时,所有的这些file文件结构全部都必须只能指向一个inode结构体。
inode结构体包含了一大堆文件相关的信息,但是就针对驱动代码来说,我们只要关心其中的两个域即可:
(1) dev_t i_rdev;
表示设备文件的结点,这个域实际上包含了设备号。
(2)struct cdev *i_cdev;
struct cdev是内核的一个内部结构,它是用来表示字符设备的,当inode结点指向一个字符设备文件时,此域为一个指向inode结构的指针。
此外,内核也提供了两个宏可以从inode结点中获取主次设备号,宏的原型如下:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);

你可能感兴趣的:(linux内核驱动)