Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1307925
  • 博文数量: 548
  • 博客积分: 7597
  • 博客等级: 少将
  • 技术积分: 4224
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-15 13:21
个人简介

嵌入式软件工程师&&太极拳

文章分类

全部博文(548)

文章存档

2014年(10)

2013年(76)

2012年(175)

2011年(287)

分类:

2011-10-27 20:54:24

原文地址:linux驱动开发之key 作者:creatorwu

linux驱动开发之key
creator
sz111@126.com

今天做了key的驱动,程序测试ok。
key的驱动牵涉的内核知识很多,有中断,内核定时器,阻塞。
后续有时间我会再写一个详细的分析。

测试如下:
先通过cat /proc/devices 插看utukey的主号为252.
mknod /dev/utukey c 252 0

测试程序如下:
/*
 *      Buttons Example for utulinux 2440
 */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(void)
{
    int buttons_fd;
    int key_value;

    buttons_fd = open("/dev/utukey", 0);
    if (buttons_fd < 0) {
        perror("cann't open device /dev/buttons");
        exit(1);
    }

    for (;;) {
        int ret = read(buttons_fd, &key_value, sizeof key_value);
        printf("You pressed buttons %d\n", key_value);
    }

    close(buttons_fd);
    return 0;
}

驱动程序如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
按键共6个。
EINT0 EINT1 EINT2 EINT3 EINT11 EINT19
GPF0  GPF1  GPF2  GPF3  GPG3   GPG11
K1    K2    K3    K4    K5     K6
*/
#define DEVICE_NAME "utukey"
#define MAX_KEY_BUF 16 //KEY BUFFER SIZE
#define KEY_NUM  6  //key num
#define BUF_HEAD (utukey_dev.buf[utukey_dev.head])
#define BUF_TAIL (utukey_dev.buf[utukey_dev.tail])
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define KEY_TIME_DELAY (HZ/5)
#define KEY_TIME_DELAY1 (HZ/100)
#define UTU_KEY_UP   0x1000
#define UTU_KEY_HOLD 0x2000
#define UTUKEY_MAJOR 252
static dev_t utukey_major = UTUKEY_MAJOR;

#define KEYSTATUS_DOWNX 0
#define KEYSTATUS_DOWN 1
#define KEYSTATUS_UP 2

typedef unsigned int KEY_RET;

struct utukey_dev_t
{
    struct cdev cdev;
    unsigned int keyStatus[KEY_NUM];
    KEY_RET buf[MAX_KEY_BUF];
    unsigned int head,tail;
    wait_queue_head_t wq;
    unsigned int key_status;
};
struct utukey_dev_t utukey_dev;
struct timer_list key_timer[KEY_NUM];

static struct key_info
{
    int irq;//中断号
    unsigned int pin;//gpio port
    unsigned int pin_setting;
    int key_code;//key value
    char *name;
}key_info_tab[] =
{
    {
        IRQ_EINT0,S3C2410_GPF0,S3C2410_GPF0_EINT0,KEY_UP,"Key Up"
    },
    {
        IRQ_EINT1,S3C2410_GPF1,S3C2410_GPF1_EINT1,KEY_DOWN,"Key Down"
    },
    {
        IRQ_EINT2,S3C2410_GPF2,S3C2410_GPF2_EINT2,KEY_LEFT,"Key Left"
    },
    {
        IRQ_EINT3,S3C2410_GPF3,S3C2410_GPF3_EINT3,KEY_RIGHT,"Key Right"
    },
    {
        IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,KEY_ENTER,"Key Enter"
    },
    {
        IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,KEY_EXIT,"Key Exit"
    },
};
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].pin) == 0)
static void utu2440button_timer_callback(unsigned long data);
static irqreturn_t utu2440_isr_kbd(int irq, void *dev_id, struct pt_regs *reg);
static int utukey_open(struct inode *inode, struct file *filp);
static int utukey_release(struct inode *inode, struct file *filp);
static ssize_t utukey_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
static ssize_t utukey_read(struct file *file,char __user *buffer, size_t count, loff_t *ppos);

static const struct file_operations utukey_fops =
{
    .owner = THIS_MODULE,
    .read  = utukey_read,
    .write = utukey_write,
    .open  = utukey_open,
    .release = utukey_release,
};


static void utukey_setup_cdev(void)
{
    int i;
    int err,devno = MKDEV(utukey_major,0);
    cdev_init(&utukey_dev.cdev,&utukey_fops);
    utukey_dev.cdev.owner = THIS_MODULE;
    utukey_dev.cdev.ops = &utukey_fops;
    utukey_dev.key_status = 0;
    err = cdev_add(&utukey_dev.cdev, devno, 1);
    if (err)
    {
        printk(KERN_NOTICE "Error %d adding utukey",err);       
    }
    request_irqs();
    utukey_dev.head = utukey_dev.tail = 0;
    for(i=0; i    {
        utukey_dev.keyStatus[i] = KEYSTATUS_UP;
    }
    init_waitqueue_head(&(utukey_dev.wq));
    for(i=0; i    {       
        key_timer[i].function = utu2440button_timer_callback;
        key_timer[i].data = i;
        init_timer(&key_timer[i]);
    }
}

static int utukey_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &utukey_dev;
    printk(KERN_NOTICE "utukey opened\n");
    return 0;
}

static int utukey_release(struct inode *inode, struct file *filp)
{
    printk(KERN_NOTICE "utukey released\n");
    return 0;
}

static ssize_t utukey_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
    return 0;
}


static ssize_t utukey_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
    KEY_RET key_ret;
    struct utukey_dev_t *dev;
    dev = (struct utukey_dev_t*)filp->private_data;
retry:
    if (utukey_dev.head != utukey_dev.tail)
    {
        key_ret = BUF_TAIL;
        dev->tail = INCBUF(dev->tail,MAX_KEY_BUF);
        copy_to_user(buffer,(char*)&key_ret,sizeof(KEY_RET));
        return sizeof(KEY_RET);
    }else
    {
        if (filp->f_flags & O_NONBLOCK)
        {
            return -EAGAIN;
        }
        interruptible_sleep_on(&(dev->wq));
        if (signal_pending(current))
        {
            return -ERESTARTSYS;
        }
        goto retry;
    }
   
    return sizeof(KEY_RET);
}


static void keyEvent(KEY_RET key)
{
    if (utukey_dev.keyStatus[key] == KEYSTATUS_DOWNX)//刚按下
    {
        BUF_HEAD = key_info_tab[key].key_code;
    }else if (utukey_dev.keyStatus[key] == KEYSTATUS_DOWN)//一直按下
    {
        BUF_HEAD = key_info_tab[key].key_code | UTU_KEY_HOLD;
    }else if (utukey_dev.keyStatus[key] == KEYSTATUS_UP)//抬起
    {
        BUF_HEAD = key_info_tab[key].key_code | UTU_KEY_UP;
    }
    utukey_dev.head = INCBUF(utukey_dev.head,MAX_KEY_BUF);
    wake_up_interruptible(&(utukey_dev.wq));
}

static irqreturn_t utu2440_isr_kbd(int irq, void *dev_id, struct pt_regs *reg)
{
    int key = dev_id;
    int i;
    int found = 0;
   
    for (i=0; i    {
        if (key_info_tab[i].irq == irq) {
            found = 1;
            break;
        }
    }
    if (!found)
    {
        printk(KERN_NOTICE"bad irq %d in button\n", irq);
      return IRQ_NONE;
    }
    //printk(KERN_NOTICE "utus2440_isr_kbd:key:%d\n",key);
    disable_irq(key_info_tab[key].irq);
    utukey_dev.keyStatus[key] = KEYSTATUS_DOWNX;
    key_timer[key].expires = jiffies + KEY_TIME_DELAY1;
    add_timer(&key_timer[key]);   
    return IRQ_HANDLED;
}
static void utu2440button_timer_callback(unsigned long data)
{
    int key = data;
    if (ISKEY_DOWN(key))
    {
        //printk(KERN_NOTICE"utukey_dev.keyStatus[key]:%d\n",key);
        if (utukey_dev.keyStatus[key] == KEYSTATUS_DOWNX)//从中断进入
        {
            keyEvent(key);
            utukey_dev.keyStatus[key] = KEYSTATUS_DOWN;
            key_timer[key].expires = jiffies + KEY_TIME_DELAY;           
            add_timer(&key_timer[key]);
        }else
        {
            keyEvent(key);
            key_timer[key].expires = jiffies + KEY_TIME_DELAY;//HOLD key,每隔200MS发送一次           
            add_timer(&key_timer[key]);
        }
    }else
    {
        utukey_dev.keyStatus[key] = KEYSTATUS_UP;
        //del_timer(&key_info_tab[key]);
        keyEvent(key);
        enable_irq(key_info_tab[key].irq);
    }
}

//申请irq中断
static int request_irqs(void)
{
    int i;
    for (i=0; i    {
        s3c2410_gpio_cfgpin(key_info_tab[i].pin, key_info_tab[i].pin_setting);
        set_irq_type(key_info_tab[i].irq, IRQT_FALLING);//下降沿触发
        if (request_irq(key_info_tab[i].irq, utu2440_isr_kbd, SA_INTERRUPT, DEVICE_NAME, i))
        {
            return -1;
        }
    }
    return 0;
}
//释放irq中断
static void free_irqs(void)
{
    int i;
    for (i=0; i    {
        free_irq(key_info_tab[i].irq, i);
    }
}
static int __init utukey_init(void)
{
    int result;
    dev_t devno = MKDEV(utukey_major,0);
    if (utukey_major)
    {
        result = register_chrdev_region(devno, 1, DEVICE_NAME);
    }else
    {
        result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
        utukey_major = MAJOR(devno);
    }
    if (result < 0)
    {
        return result;
    }
    utukey_setup_cdev();
    //key init
    //key_init();
    return 0;
}
static void __exit utukey_exit(void)
{
    int i;
    cdev_del(&utukey_dev.cdev);
    unregister_chrdev_region(MKDEV(utukey_major,0),1);
    for(i=0; i    {
        del_timer(&key_timer[i]);
    }
    free_irqs();
}

MODULE_AUTHOR("Creator");
MODULE_LICENSE("Dual BSD/GPL");

module_init(utukey_init);
module_exit(utukey_exit);
阅读(716) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~