Linux Driver 简单 按键驱动 - 1

来源:互联网 发布:复杂网络建模的研究生 编辑:程序博客网 时间:2024/05/21 04:39
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>


#include <linux/cdev.h>  //cdev_init、cdev_alloc、cdev_add、cdev_del

#include <linux/slab.h>   //存储管理相关API
#include <linux/fs.h>     //file_operations结构
#include <linux/device.h> // 驱动模型相关device API和结构体

#include <linux/irq.h>    //定义中断出发方式:电平发、边沿触发
#include <asm/uaccess.h>  //copy_to_user 和 copy_from_user
#include <linux/interrupt.h> //request_irq和free_irq
#include <plat/gpio-cfg.h>   //s3c_gpio_cfgpin和s3c_gpio_getcfg
#include <plat/gpio-fns.h>   //s3c2410_gpio_setpin、s3c2410_gpio_getpin
#include <mach/gpio-nrs.h>   //定义gpio编号S3C2410_GPA(n)~S3C2410_GPJ(n)

#include <linux/wait.h>     //休眠相关wait_event_interruptible(wq, condition)

DECLARE_WAIT_QUEUE_HEAD(name)、wake_up_interruptible(&wq)   

#include <linux/sched.h>    //signal_pending

static dev_t dev;            //设备编号
static struct cdev *my_cdev = NULL;  
static struct class *cdev_class = NULL;
static struct device *cdev_class_device = NULL;

static unsigned char key_val; //按键值

static volatile int condition = 0; //休眠条件,1是不休眠,0是休眠
static DECLARE_WAIT_QUEUE_HEAD(wq);//初始化休眠队列

struct key_desc //按键自描述结构体
{
    int pin;    //按键引脚值
    char value; //按键返回值
};

static struct key_desc my_desc[4] = {
    { S3C2410_GPF(1), 0x01 },
    { S3C2410_GPF(4), 0x02 },
    { S3C2410_GPF(2), 0x03 },
    { S3C2410_GPF(0), 0x04 },
};

MODULE_LICENSE("GPL");

static irqreturn_t key_irq(int irq, void *dev_id) //按键中断处理函数
{
    struct key_desc *tmp = (struct key_desc *)dev_id;
    if(!s3c2410_gpio_getpin(tmp->pin))
    {
        key_val = tmp->value;//按下读取到低电平,返回0x01~0x04
    }
    else
    {
        key_val = 0xFF;      //按键松开,读取到高电平,返回255
    }
    condition = 1;               /*  有按键按下就唤醒app读取键值  */
    wake_up_interruptible(&wq);
    return IRQ_HANDLED;
}

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

{

    /*  申请中断,开启引脚的中断功能   */

    request_irq(IRQ_EINT1 , key_irq, IRQ_TYPE_EDGE_BOTH, "K1", &my_desc[0]);
    request_irq(IRQ_EINT4 , key_irq, IRQ_TYPE_EDGE_BOTH, "K2", &my_desc[1]);
    request_irq(IRQ_EINT2 , key_irq, IRQ_TYPE_EDGE_BOTH, "K3", &my_desc[2]);
    request_irq(IRQ_EINT0 , key_irq, IRQ_TYPE_EDGE_BOTH, "K4", &my_desc[3]);
    return 0;
}

static ssize_t key_close(struct inode *inode, struct file *file)

{

    /*  释放中断资源,关中断    */

    free_irq(IRQ_EINT1, &my_desc[0]);
    free_irq(IRQ_EINT4, &my_desc[1]);
    free_irq(IRQ_EINT2, &my_desc[2]);
    free_irq(IRQ_EINT0, &my_desc[3]);

    return 0;
}

static ssize_t key_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
    if(count != 1)
    {
        return -EINVAL;
    }
    wait_event_interruptible(wq, condition); //等待按键按下
    copy_to_user(buf, &key_val, 1);  //获取按键的值
    condition = 0; //重新等待
    return 1;
}

static struct file_operations fops = {
    .owner  =   THIS_MODULE,
    .open   =   key_open,
    .release=   key_close,
    .read   =   key_read,
};

static int key_drv_init(void)
{
    int ret = 0;
    
    ret = alloc_chrdev_region(&dev, 0, 4, "mykey");//注册设备编号

    if(ret < 0)
    {
        printk("alloc_chrdev_region\n");
    }

    my_cdev = cdev_alloc();//分配字符设备结构体
    cdev_init(my_cdev, &fops);
    my_cdev->owner = THIS_MODULE;
    cdev_add(my_cdev, dev, 4); //告知内核
    cdev_class = class_create(THIS_MODULE, "mykey"); //创建设备类
    cdev_class_device = device_create(cdev_class, NULL, dev, NULL, "keybutton");//创建设备对象,对应于/dev/keybutton

    return 0;
}

static void key_drv_exit(void)
{
    device_destroy(cdev_class, dev);
    class_destroy(cdev_class);
    cdev_del(my_cdev);
    kzfree(my_cdev);
    unregister_chrdev_region(dev, 4);
    return ;
}

module_init(key_drv_init);
module_exit(key_drv_exit);

原创粉丝点击