Linux Driver互斥、异步通知、阻塞与非阻塞综合实例

来源:互联网 发布:软件图标下载 编辑:程序博客网 时间:2024/06/08 16:26
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>

#include <linux/irq.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-fns.h>
#include <mach/gpio-nrs.h>

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

#include <linux/poll.h>

#include <linux/semaphore.h>

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;
static DECLARE_WAIT_QUEUE_HEAD(wq);

static DEFINE_SEMAPHORE(button_lock);

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;
    }
    else
    {
        key_val = 0x10|tmp->value;
    }
    condition = 1;
    wake_up_interruptible(&wq);
    return IRQ_HANDLED;
}

static int key_open(struct inode *inode, struct file *file)
{
    if(file->f_flags & O_NONBLOCK)
    {
        if(down_trylock(&button_lock))
            return -EBUSY;
    }
    else
    {
        down(&button_lock);
    }
    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]);
    up(&button_lock);
    return 0;
}

static ssize_t key_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
    if(count != 1)
    {
        return -EINVAL;
    }
    if(file->f_flags & O_NONBLOCK)
    {
        if(!condition)
            return -EAGAIN;
    }
    else
    {
        wait_event_interruptible(wq, condition);
    }
    copy_to_user(buf, &key_val, 1);
    condition = 0;
    return 1;
}

static unsigned key_poll(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &wq, wait);

    if(condition)
        mask |= POLLIN|POLLRDNORM;

    return mask;
}

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

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");

    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);

原创粉丝点击