嵌入式linux之驱动6(无图,详细了解需要下载文章末尾里pdf查看)

                                 字符设备驱动

1.1理解什么是驱动

我们根据这张图来分析一个软件系统的层次关系。

1首先app1等应用层的应用程序使用库提供的open函数等,这些函数可以代表打开led设备文件。

2库根据open函数传入的参数执行swi指令,这条指令会引起CPU异常,进入内核。

3内核的一场处理函数会根据这些参数找到相应的驱动程序,返回一个文件句柄给库,进而返回给应用程序。

4应用程序的得到句柄之后,使用库函数提供的writeioclt函数发出控制命令。

5库根据writeioclt函数传入的参数执行swi指令,这条指令会引起CPU异常进入内核。

6内核的异常处理函数会根据这些参数调用驱动程序的相关函数,点亮LED。实际上内核和驱动程序没有界限,驱动程序最终还是要编译进内核的,只不过有些可能是动态加载。

   在这强调一下驱动程序只是一个工具,他的功能已经被设计好之后,不能通过应用层来修改,只能进行变化,调节,要修改的话需要重新编译驱动程序。

 

 

 

 

 

 

 

1.2驱动的分类和开发步骤

这张图中从应用层到底层硬件之间的映射就是驱动的框架,通过这个框架来完成对底层的控制。

一.驱动程序的分类:可以分为三种,字符设备character device 块设备 block device 网络接口 network interface

二.

三.驱动程序开发的步骤

一般来说编译设备驱动程序的大致流程如下。

1查看原理图,数据手册,了解设备的操作方法。

2在内核中找到相近的驱动程序,参照它进行开发。

3实现驱动程序的初始化,比如向内核注册这个驱动程序,这样应用程序传入文件名时候,内核才能找到相应的驱动程序。

4设计所要实现的功能,比如open,close,read,write等函数。

5需要实现中断服务的设备驱动需要实现中断服务。

6编译该驱动程序到内核,或者用insmod命令加载驱动程序。

7最后测试程序。

四.驱动程序的加载和卸载

1.3字符设备驱动的开发(基础)

下面我们通过控制led灯为切入点进行驱动程序的编写。

我们通过这个结构体告诉内核我们要注册给内核的函数。

然后通过这个init函数进行初始化,我们通过register_chrdev把上面的结构体完整的注册进去,上面两步骤就完成了函数的注册,当然这个init函数也需要用module_int申明一下。

此外还要记上GPL协议。

卸载的话与此相反,这里我们就不说了。

最后我们将已经注册进去的函数进行编译,把想要实现的功能放进去,从而实现我们的闪灯效果。

下面看一下我们的应用程序。

这个应用程序就特别短了。在第14行打开设备文件的时候就会执行驱动程序里的open程序,在应用程序里进行判断开关后将val的地址传送到write函数,之后再驱动函数write中获取传送进来的值进行对寄存器的操作,从而实现开关灯。这样就体现的应用程序无法操作底层,只有通过调用驱动来进行底层访问,这也就是驱动的作用。Rmmod卸载驱动,insmod 是加载驱动。

1.3字符设备驱动的开发(中断基理)

一.中断的处理流程

我们用中断方式来控制按键的程序来进行分析。

二.源码分析

首先我们看一下模块的初始化函数和卸载函数

在内容上没什么特别要说的。驱动程序的核心还是在于file_operations结构体。

static struct file_operations sencod_drv_fops = {

    .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

    .open    =  third_drv_open,     

.read  = third_drv_read,    

.release =  third_drv_close,    

};

这里告诉了内核要注册哪些函数。经过注册之后我们就可以使用它了。

static int third_drv_open(struct inode *inode, struct file *file)

{

/* 配置GPF0,2为输入引脚 */

/* 配置GPG3,11为输入引脚 */

request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);

request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);

request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);

request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);

return 0;}

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

struct pin_desc * pindesc = (struct pin_desc *)dev_id;

unsigned int pinval;

pinval = s3c2410_gpio_getpin(pindesc->pin);

if (pinval)

{

/* 松开 */

key_val = 0x80 | pindesc->key_val;

}

else

{

/* 按下 */

key_val = pindesc->key_val;

}

    ev_press = 1;                  /* 表示中断发生了 */

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

return IRQ_RETVAL(IRQ_HANDLED);

}

当应用程序打开设备文件的时候进入这个函数,从而进行中断处理。Close函数与此相反,他的作用是释放已经注册的中断。最终我们通过read函数将参数传入到应用程序中去,实现中断方式的按键驱动。

应用程序如图所示。

三.简略介绍驱动中的其他重要函数作用。

Poll机制

我们在这加一个poll的函数,他的作用就是避免read等函数一直执行读的状态,限制在一定时间内如果完成不到任务就进行下一个动作如睡眠等。

异步通知

这里fasync函数完成了异步通知的作用:他可以将一个程序的参数或结果传入到另外一个函数中去。如下图

通过上图的这一个机制,我们就可以实现应用程序被动的接受数据,这样应用程序就可以节省很多空间来做其他的,耗费的资源也少。

 

 

 

 

四.同步互斥阻塞

这句话的意思就是同一个时刻只能有一个应用程序使用驱动,其他的需要用到驱动程序的应用程序需要等待。这个功能属于标志位的判断,不是单独一个函数实现的,这个就麻烦了,这里我们先知道有这一个东西吧,明白她的原理和作用。

 

1.3块设备驱动和网络设备驱动的大致了解

现场学习

                                           2018  2 月  8

                                                   刘朋

 链接: https://pan.baidu.com/s/17uZ0ZyZ28ttSn-NVlN18Pw 密码: e6m5

猜你喜欢

转载自blog.csdn.net/liupeng19970119/article/details/80154130