linux驱动开发之key

今天做了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);


你可能感兴趣的:(linux,驱动开发)