 
- UID
- 852665
|

(转) ok6410学习笔记(17.中断下部分之工作队列)
本节知识点:基础知识:1.其实工作队列就是在中断处理函数中,起一个线程,然后把要处理的数据都传递给这个线程,让这个线程去执行你写好的特定程序即回调函数。
2.当在linux下运行应用程序的是 可以使用 ./a.out & 来让应用程序在后台运行。这样还可以进行其他操作,但是要想杀死进程就得用ps和kill pid了
重点函数:1.struct work_struct 工作队列结构体
2.回调函数:void (*work_func)(struct work_struct*work)
3.INIT_WORK(_work,_func) 这个宏是将工作队列和回调函数建立联系 第一参数是工作队列结构体的地址 第二个参数是回调函数 初始化工作结构
4.DECLARE_WORK(n,f) 初始化并定义工作结构 就是少了struct work_struct这部
5.int schedule_work(struct work_struct *work) 让操作系统开始起线程 处理回调函数 也就是将指定工作添加到内核维护的全局工作队列中去
6.struct delayed_work 延迟工作队列结构体
7.INIT_DELAYED_WORK(_work,_func) 这个宏是将工作队列和回调函数建立联系 初始化工作结构
8. DECLARE_DELAYED_WORK(n,f) 初始化并定义工作结构 跟4的区别只是一个是延迟的 一个是没有延迟的 结构体不同而已
9.schedule_delayed_work(struct delaye_struct *dwork, unsigned long delay) delay是延时时间 单位是jiffies
驱动结构:1.先定义struct work_struct工作队列结构体
2.编写回调函数void (*work_func)(struct work_struct*work)
3.INIT_WORK(_work,_func)将回调函数和工作队列结构体 建立联系
4.第一步和第三步可以合并为 DECLARE_WORK(n,f) 初始化并定义工作结构
5.在中断处理函数中,当紧急的事情都处理完后 调用schedule_work(struct work_struct *work) 让操作系统并行的去找回调函数
本节代码:本节代码只有plat_bun_drv.c有变化,其余的文件都跟上节代码一样。
plat_bun_drv.c:
[cpp] view plaincopy
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/miscdevice.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/platform_device.h>
- #include <linux/interrupt.h>
- #include <linux/uaccess.h>
- #include <linux/io.h>
- #include <mach/map.h>
- #include <mach/regs-gpio.h>
- #include <linux/poll.h>
- #include <linux/irq.h>
- #include <asm/unistd.h>
- #include <linux/device.h>
- #include <linux/sched.h>
- #include <linux/wait.h>
- #include "plat_bun_drv.h"
- #include <mach/gpio-bank-n.h>
- MODULE_AUTHOR("Hao");
- MODULE_LICENSE("Dual BSD/GPL");
- struct delayed_work dwork; //定义延迟工作队列结构体
- volatile unsigned long GPIOM_VA_BASE;//定义一个全局变量 保存ioremap映射的地址
- #define GPIOM_PA_BASE 0x7f008820
- #define GPIOM_CON_VA GPIOM_VA_BASE
- #define GPIOM_DAT_VA (GPIOM_VA_BASE+0x4)
- #define GPIOM_PUD_VA (GPIOM_VA_BASE+0x8)
- struct resource led_res={
- .name = "led io-mem", //设备资源的名字
- .start = GPIOM_PA_BASE,
- .end = GPIOM_PA_BASE + 0xc,
- .flags = IORESOURCE_MEM,
- };
- int key_num=1; //定义一个全局变量 来存放中断处理函数返回的按键值
- int key_flag=0;//默认为0 在poll中不返回掩码
- static DECLARE_WAIT_QUEUE_HEAD(waitq); //利用这个宏 定义初始化 等待队列 替代了wait_queue_head_t q和init_waitqueue_head(&q)两个函数
- typedef
struct { - int irq;
- int num;
- char *name;
- } button_irq_t;
- button_irq_t button_irqs [] = {
- {0, 0, "KEY1"},
- {0, 1, "KEY2"},
- {0, 2, "KEY3"},
- {0, 3, "KEY4"},
- {0, 4, "KEY5"},
- {0, 5, "KEY6"},
- };
- /*中断下半部 工作队列回调函数 本来应该是把中断中的一些内容写在这里面的 但我的中断实在是太短了 没事写的了 就写了一个打印消息*/
- static
void buttons_work_func(struct work_struct *work) - {
- //这个其实就是在中断处理函数中 让操作系统起了一个线程来处理 后续不紧急的程序 只不过这个线程是一些列的是一个队列
- //这里要注意 的就是 中断处理函数中的一些变量啊 参数啊 往这个回调函数传递的一些问题
- wake_up_interruptible(&waitq);//唤醒poll的阻塞
- /*把唤醒阻塞拿到这里面来 这样这个延迟就能起到消抖动的作用了*/
- printk("20ms later working!!!\n");
- }
- /*中断处理函数 在混杂设备的open函数中request_irq中定义的名字叫buttons_interrupt*/
- /*本来其实在这里判断是那个按键中断 然后让led对应点亮就行了 但是驱动的所有判断都应该是给用户空间传递的 所有判断应该在用户空间进行 点亮led应该在用户空间进行*/
- static irqreturn_t buttons_interrupt(int irq, void *dev_id)
- {
- printk("you are in buttons_interrupt!!!\n");
- button_irq_t *button_irqs = (button_irq_t *)dev_id; /*在触发中断的时候 操作系统会根据中断源信号调用对应的处理函数
- 并同时将中断号irq 和 dev_id传递到这个函数 因为在注册中断的时候dev_id为了保证唯一性 输入的是button_irqs结构体的地址 这里直接使用这地址 是操作系统传递的*/
- /*判断是否是这个设备产生中断 本来是为了给share中断用的 这里使用是因为所有按键的外部中断都是这一个中断处理函数*/
- key_num = button_irqs->num; //数据处理
- key_flag=1;
- schedule_delayed_work(&dwork, HZ/50); //延迟20ms,用作按键消抖
- return IRQ_RETVAL(IRQ_HANDLED); //接收到了准确的中断信号,并且作了相应正确的处理
- /*步骤:1,判断本设备是否发送中断
- 2,数据处理 这个数据一般都是要通过read传递给用户空间的
- 3,清楚中断标志位 外部中断其实没有中断标志位 只有一个EINT0PEND寄存器 记录那个外部中断发生了 内核应该就是通过这个寄存器的值来判断同一中断组中不同外部中断的
- 4,唤醒阻塞的进程 这唤醒就是唤醒poll的阻塞的
- */
- }
- static
int mem_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) - {
- printk("you are in mem_read!!!\n");
- int ret;
- while(!key_flag)
- {
- if (filp->f_flags & O_NONBLOCK) //判断文件是否不允许阻塞
- {
- return -EAGAIN;
- }
- /*其实这个阻塞的可能性很低 因为都是poll阻塞被唤醒后 才进入read的 完全没必要来一个阻塞型设备驱动 就当练手把*/
- wait_event_interruptible(waitq, key_flag); //可以中断的阻塞 第一个参数是阻塞队列 第二个参数是阻塞条件
- }
- key_flag=0;//在下次中断来临前 还是让poll阻塞吧
- if (copy_to_user((void *)buff,(void *)(&key_num), count))//把字符设备模块从内核空间拷贝到用户空间
- {
- ret = - EFAULT;
- }
- else
- {
- ret = count;
- }
- return ret;
- }
- /*这里面真正找到了poll的意义了 如果没有poll进行文件监控 看那个文件可以读(利用的就是按键被按下 中断处理函数被调用 把poll的标志改变了)
- 就要不断的用read进行轮询的读取了然后进行阻塞型设备驱动 被挂起 要是多个文件就疯了 各种被挂起就进行不了了*/
- static unsigned int mem_poll( struct file *file, struct poll_table_struct *wait)
- {
- printk("you are in mem_poll!!!\n");
- /*步骤:1,poll_wait把文件的等待队列加入*wait
- 2,判断标志返回掩码*/
- unsigned int mask = 0;
- poll_wait(file, &waitq, wait); //加入等待队列
- if(key_flag)
- {
- mask |= POLLIN | POLLRDNORM;//返回可读标志
- }
- return mask;
- }
- static
void ok6410_led_setup(struct resource *led_resource) - {
- unsigned long temp;
- led_res=*led_resource;
- request_mem_region(led_resource->start,(led_resource->end-led_resource->start),led_resource->name);//申请i/o内存 设备资源的名字
- //其实我觉得用上面那个资源的结构体意义不打 因为request_mem_region就是在跟系统申请这个资源 等价于了把上面的那个资源结构体拷贝到了内核中的设备资源链表
- GPIOM_VA_BASE = (volatile unsigned long )ioremap(led_resource->start, led_resource->end-led_resource->start);//
- /****************************可以直接对地址进行操作***************************************/
- /*
- (*(volatile unsigned long *)GPIOM_VA_BASE)&=~0xffff;
- (*(volatile unsigned long *)GPIOM_VA_BASE)|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
- temp=0;
- (*(volatile unsigned long*)GPIOM_DAT_VA)=temp; //默认所有灯都亮
- */
- /*******************也可以用函数api进行操作 貌似这个方式更加安全***************************/
- temp&=~0xffff;
- temp|=0x1|(0x1<<4)|(0x1<<8)|(0x1<<12);
- writel(temp, GPIOM_CON_VA);
- temp|=0xf;
- temp=0;
- writel(temp, GPIOM_DAT_VA);
- }
- static
void ok6410_led_release(void) - {
- iounmap((void*)GPIOM_VA_BASE);
- release_mem_region(led_res.start,led_res.end-led_res.start);
- }
- static
long memdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) - {
- printk("you are in memdev_ioctl!!!\n");
- int ret=0;
- int err=0;
- int kernel_num=1991;
- //char kernel_buf[20]="hello kernel!!!";
- /*先判断命令号是否正确*/
- if (_IOC_TYPE(cmd) != CMD_KTYPE) //获得命令的type类型是否正确
- return -EINVAL;
- if (_IOC_NR(cmd) > LED_KCMD) //获得命令的num类型 是否小于命令个数
- return -EINVAL;
- /*获命令的数据传输方向 根据各自的方向判断*/
- if (_IOC_DIR(cmd) & _IOC_READ)
- err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));/*此函数是根据
- 内核空间写的 是用来判断 arg应用程序传来的用户空间 是否有效的 所以对于用户空间来说是写*/
- else
if (_IOC_DIR(cmd) & _IOC_WRITE) - err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));//对于用户空间来说是读 成功返回1 失败返回0
- if (err)
- return -EFAULT;
- /*实现CMD的用法*/
- switch(cmd)
- {
- case LEDR_KCMD:
- ret=__put_user(kernel_num, (int *)arg); //把内核中的int型数据读入用户空间 unsigned long arg就是一个地址值 kernel->arg
- break;
- case LEDW_KCMD:
- ret=__get_user(kernel_num, (int *)arg); //arg->kernel_num 把用户空间的数据传递给kernel_num
- printk(KERN_EMERG "WRITE_KCMD is in kernel!!! kernel_num:%d \n",kernel_num);
- if(0==kernel_num)
- {
- writel(0x1e, GPIOM_DAT_VA);//将4个led全部点亮
- }
- if(1==kernel_num)
- {
- writel(0x1d, GPIOM_DAT_VA);//将4个led全部熄灭
- }
- if(2==kernel_num)
- {
- writel(0x1b, GPIOM_DAT_VA);//将4个led全部点亮
- }
- if(3==kernel_num)
- {
- writel(0x17, GPIOM_DAT_VA);//将4个led全部熄灭
- }
- if(4==kernel_num)
- {
- writel(0x0, GPIOM_DAT_VA);//将4个led全部点亮
- }
- if(5==kernel_num)
- {
- writel(0x1f, GPIOM_DAT_VA);//将4个led全部熄灭
- }
- break;
- default:
- return -EINVAL;
- break;
- }
- return 0;
- }
- /*混杂设备中 当关闭文件的时候 应该先释放中断资源*/
- int mem_release(struct inode *inode, struct file *filp)
- {
- printk("you are in mem_release!!!\n");
- int i;
- for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
- {
- free_irq(button_irqs.irq, (void *)&button_irqs);
- }
- ok6410_led_release();
- return 0;
- }
- /*混杂设备中 当打开文件的时候 应该先申请中断资源*/
- int mem_open(struct inode *inode,struct file *filp)
- {
- printk("you are in mem_open!!!\n");
- int i;
- int err;
- for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
- {
- /*请求中断设备资源 第一个参数是中断号这里面与硬件平台一一对应 第二个参数是中断处理程序 这里面都
- 公用一个处理程序 第三个参数是标志位 标志是共享中断,快速中断,触发方式 第四个参数是中断id 是区
- 分共享中断 不同设备时候用的 这里面用的&button_irqs的地址保证它的唯一性 这里没有安全判断*/
- err=request_irq(button_irqs.irq, buttons_interrupt, IRQ_TYPE_EDGE_FALLING,
- button_irqs.name, (void *)&button_irqs);//在这里设置成下降沿触发 中断处理函数就简单些
- if (err)
- {
- printk("request_irq is error!!!\n");//此时其实应该有disable_irq 或者 free_irq等函数把申请过的irq释放掉 但是我嫌麻烦就没写
- return err;
- }
- }
- ok6410_led_setup(&led_res);
- return 0;
- }
- static
const
struct file_operations mem_fops = //定义此字符设备的file_operations
- { //这里是对结构体整体赋值的方式
- .owner = THIS_MODULE, //函数名都可以自己定义 都是函数指针
- .open = mem_open,
- .release = mem_release,
- .unlocked_ioctl=memdev_ioctl,
- .read=mem_read,
- .poll=mem_poll,
- };
- static
struct miscdevice misc = { - .minor = 0,//设置为0 系统自动分配次设备号
- .name = "misc_bun", //我觉得这个是设备节点的名字 就是/dev路径下的文件的名字
- .fops = &mem_fops, //文件操作
- };
- static
int bun_drv_probe(struct platform_device *dev) //这里面写功能驱动
- {
- int ret;
- int i;
- struct resource *res;
- printk("Driver found device which my driver can handle!\n");
- INIT_DELAYED_WORK(&dwork, buttons_work_func);//初始化 工作队列结构体 把工作队列和回调函数建立连续
- for(i=0; i<6; i++)
- {
- res=platform_get_resource(dev,IORESOURCE_IRQ,i);
- if(res==NULL)
- {
- printk("no memory resource %d\n",i);
- return 0;
- }
- button_irqs.irq = res->start; //把设备中的irq资源都带到驱动程序中 赋值给button_irqs结构体数组中
- }
- misc_register(&misc);
- return ret;
- }
- static
int bun_drv_remove(struct platform_device *dev) - {
- printk("Driver found device unpluged!\n");
- misc_deregister(&misc);
- return 0;
- }
- struct platform_driver bun_drv={
- .probe=bun_drv_probe,
- .remove=bun_drv_remove,
- .driver={
- .owner=THIS_MODULE,
- .name="plat_bun_dev", //platform总线 里面驱动的名字 这个名字要和设备的名字一样
- }
- };
- static
int __init platform_bun_drv_int(void) - {
- return platform_driver_register(&bun_drv);
- }
- static
void __exit platform_bun_drv_exit(void) - {
- platform_driver_unregister(&bun_drv);
- }
- module_init(platform_bun_drv_int);
- module_exit(platform_bun_drv_exit);
http://blog.csdn.net/mbh_1991/article/details/9244475 |
|