zoukankan      html  css  js  c++  java
  • [国嵌攻略][124][阻塞型驱动程序设计]

    阻塞的必要性

    当一个设备无法立刻满足用户的读写请求时应当如何处理?例如,调用read时,设备没有数据提供,但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡眠状态),直到请求可以得到满足。

    内核等待队列

    在实现阻塞驱动的过程中,需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是内核等待队列。

    1.定义等待队列

    wait_queue_head_t my_queue

    2.初始化等待队列

    init_waitqueue_head(&my_queue)

    3.定义和初始化等待队列

    DECLARE_WAIT_QUEUE_HEAD(my_queue)

    4.进入等待队列,睡眠

    4.1.wait_event(queue, condition)

    当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂载queue参数所指定的等待队列上。

    4.2.wait_event_interruptible(queue, condition)

    当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。

    4.3.int wait_event_killable(queue, condition)

    当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。

    5.从等待队列中唤醒进程

    5.1.wake_up(wait_queue_t *q)

    从等待队列中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。

    5.2.wake_up_interruptible(wait_queue_t *q)

    从等待队列中唤醒状态为TASK_INTERRUPTIBLE的进程。

    驱动程序默认都是阻塞型驱动

    头文件

    #include <linux/sched.h>

    error: 'TASK_NORMAL' undeclared

    error: 'TASK_UNINTERRUPTIBLE' undeclared

    注意:调度文件中可能有别的函数与模块名重名,修改模块名即可。

    keydev.c

    /********************************************************************
    *头文件
    *********************************************************************/
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/miscdevice.h>
    #include <linux/interrupt.h>
    #include <linux/io.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>
    #include <linux/sched.h>
    
    
    
    
    /********************************************************************
    *宏定义
    *********************************************************************/
    #define GPGCON 0x56000060   //按键控制寄存器地址
    #define GPGDAT 0x56000064   //按键数据寄存器地址
    
    /********************************************************************
    *全局变量
    *********************************************************************/
    unsigned int *keycon;       //按键控制指针
    unsigned int *keydat;       //按键数据指针
    
    struct work_struct *work;   //按键工作
    struct timer_list timer;    //按键定时
    wait_queue_head_t queue;    //等待队列
    int isData;                 //等待条件
    int keyNum;                 //按键编号
    
    /********************************************************************
    *定时处理
    *********************************************************************/
    //设备定时
    void key_timer(unsigned long data){
        //读取按键状态
        unsigned short datTmp;
        int key1, key2;
        
        datTmp = readw(keydat);   //获取GPGDAT值
        key1 = datTmp & (1<<0);   //获取key1电平
        key2 = datTmp & (1<<3);   //获取key2电平
        
        //判断按键状态
        if(key1 == 0){
            keyNum = 1;
            printk("key1 down!
    ");
        }
        
        if(key2 == 0){
            keyNum = 2;
            printk("key2 down!
    ");
        }
        
        //设置数据状态
        isData = 1;
        wake_up(&queue);
    }
    
    /********************************************************************
    *中断处理
    *********************************************************************/
    //设备中断下部
    void key_work(struct work_struct *work){
        //启动定时设备
        mod_timer(&timer, jiffies + HZ/100);   //定时10ms,jiffies表示系统当前嘀嗒数,1HZ = 1s = 1000jiffies
    }
    
    //设备中断上部
    irqreturn_t key_irq(int irq, void *dev_id){
        //处理硬件相关
        
        //提交硬件无关
        schedule_work(work);
        
        return 0;
    }
    
    /********************************************************************
    *设备方法
    *********************************************************************/
    //设备读取
    ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
        //判断数据状态
        wait_event(queue, isData);
        
        //读取按键编号
        copy_to_user(buf, &keyNum, sizeof(keyNum));
        
        //清零数据状态
        isData = 0;
        
        return sizeof(keyNum);
    }
    
    //设备打开
    int key_open(struct inode *node, struct file *filp){
        return 0;
    }
    
    //设备关闭
    int key_close(struct inode *node, struct file *filp){
        return 0;
    }
    
    //设备方法
    struct file_operations key_fops = {
        .read    = key_read,
        .open    = key_open,
        .release = key_close
    };
    
    /********************************************************************
    *模块安装
    *********************************************************************/
    //混杂设备
    struct miscdevice misdev = {
        .minor = 200,        //次设备号
        .name  = "keydev",   //设备名称
        .fops  = &key_fops   //设备方法
    };
    
    //注册硬件
    void handware_init(void){
        //初始化按键控制寄存器
        unsigned int conTmp;
        
        keycon = ioremap(GPGCON, 4);   //虚拟地址映射
        
        conTmp = readl(keycon);             //获取GPGCON值
        conTmp &= ~((0x3<<6) | (0x3<<0));   //GPB3[7:6]:00,GPG0[1:0]:00
        conTmp |=  ((0x2<<6) | (0x2<<0));   //GPB3[7:6]:EINT[11],GPG0[1:0]:EINT[8]
        writel(conTmp, keycon);             //设置GPGCON值
        
        //初始化按键状态寄存器
        keydat = ioremap(GPGDAT, 2);   //虚拟地址映射
    }
    
    //安装模块
    static int ikey_init(void){
        //注册混杂设备
        misc_register(&misdev);
        
        //注册硬件设备
        handware_init();
            
        //注册中断处理
        request_irq(IRQ_EINT8, key_irq, IRQF_TRIGGER_FALLING, "keyirq", 0);    //下降沿触发,IRQ_EINT8定义在irqs.h文件中
        request_irq(IRQ_EINT11, key_irq, IRQF_TRIGGER_FALLING, "keyirq", 0);
        
        //注册工作队列
        work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
        INIT_WORK(work, key_work);
        
        //注册定时设备
        init_timer(&timer);           //初始化定时器
        timer.function = key_timer;   //添加定时函数
        add_timer(&timer);            //添加定时设备
        
        //注册等待队列
        isData = 0;
        init_waitqueue_head(&queue);
        
        return 0;
    }
    
    //卸载模块
    static void ikey_exit(void){
        //注销混杂设备
        misc_deregister(&misdev);
        
        //注销中断处理
        free_irq(IRQ_EINT8, 0);
        free_irq(IRQ_EINT11, 0);
    }
    
    /********************************************************************
    *模块声明
    *********************************************************************/
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("D");
    MODULE_DESCRIPTION("");
    MODULE_VERSION("v1.0");
    
    module_init(ikey_init);
    module_exit(ikey_exit);

    keyapp.c

    mknod /dev/keydev0 c 10 200

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main(int argc, char **argv){
        //打开设备
        int fd;
        
        fd = open("/dev/keydev0", O_RDWR);
        
        //读取设备
        int keynum;
        
        read(fd, &keynum, sizeof(keynum));
        printf("key number is %d
    ", keynum);
        
        //关闭设备
        close(fd);
    }
  • 相关阅读:
    First Missing Positive
    Find Minimum in Rotated Sorted Array II
    switch两种写法对比
    常用的前端JavaScript方法封装
    如何保证缓存和数据库的一致性?
    14个前端小知识
    dataTable转换特定的类
    C# MD5 32大写位加密 UTF-8编码
    另一个 SqlParameterCollection 中已包含 SqlParameter
    C#实现数据回滚,A事件和B事件同时执行,其中任何一个事件执行失败,都会返回失败
  • 原文地址:https://www.cnblogs.com/d442130165/p/5258845.html
Copyright © 2011-2022 走看看