/********************************************************************************* * Copyright: (C) 2016 Guo Wenxue * All rights reserved. * * Filename: platdev_key.c * Description: This file is the keypad platform driver * * Version: 1.0.0(07/26/2016) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "07/26/2016 05:01:25 PM" * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include /* 1HZ=100*jiffies 1*jiffies=10ms => 1HZ=100*10ms = 1s */ #define CANCEL_DITHERING_DELAY (HZ/50) /* Remove button push down dithering timer delay 20ms */ /*+------------------------------------------------+ | keyboard platform device information | +------------------------------------------------+*/ /* keyboard hardware informtation structure definition */ typedef struct s3c_kbd_info_s { int code; /* input device key code */ int nIRQ; /* keyboard IRQ number*/ unsigned int setting; /* keyboard IRQ Pin Setting*/ unsigned int gpio; /* keyboard GPIO port */ } s3c_kbd_info_t; /* keyboard platform device private data structure */ typedef struct s3c_kbd_platform_data_s { s3c_kbd_info_t *keys; int nkeys; } s3c_kbd_platform_data_t; static s3c_kbd_info_t s3c_kbd_gpios[] = { [0] = { .code = KEY_1, .nIRQ = IRQ_EINT0, .gpio = S3C2410_GPF(0), .setting = S3C2410_GPF0_EINT0, }, [1] = { .code = KEY_2, .nIRQ = IRQ_EINT2, .gpio = S3C2410_GPF(2), .setting = S3C2410_GPF2_EINT2, }, [2] = { .code = KEY_3, .nIRQ = IRQ_EINT3, .gpio = S3C2410_GPF(3), .setting = S3C2410_GPF3_EINT3, }, [3] = { .code = KEY_4, .nIRQ = IRQ_EINT4, .gpio = S3C2410_GPF(4), .setting = S3C2410_GPF4_EINT4, }, }; /* keyboard platform device private data */ static s3c_kbd_platform_data_t s3c_kbd_data = { .keys = s3c_kbd_gpios, .nkeys = ARRAY_SIZE(s3c_kbd_gpios), }; static void platform_kbd_release(struct device * dev) { return; } static struct platform_device s3c_keyboard_device = { .name = "s3c_kbd", .id = 1, .dev = { .platform_data = &s3c_kbd_data, .release = platform_kbd_release, }, }; /*+------------------------------------------------+ | keyboard platform driver information | +------------------------------------------------+*/ typedef struct s3c_kbd_s { struct timer_list *timers; /* every key get a cancel dithering timer */ struct input_dev *input_dev; s3c_kbd_platform_data_t *pdata; } s3c_kbd_t; /*--- end of struct s3c_kbd_s ---*/ s3c_kbd_t *s3c_kbd = NULL; static irqreturn_t s3c_kbd_intterupt(int irq, void *dev_id) { int i; int found = 0; struct platform_device *pdev = dev_id; s3c_kbd_t *s3c_kbd = NULL; s3c_kbd = platform_get_drvdata(pdev); for(i=0; ipdata->nkeys; i++) { if(irq == s3c_kbd->pdata->keys[i].nIRQ) { found = 1; break; } } if(!found) /* An ERROR interrupt */ return IRQ_NONE; mod_timer(&s3c_kbd->timers[i], jiffies+CANCEL_DITHERING_DELAY); return IRQ_HANDLED; } static void cancel_dithering_timer_handler(unsigned long data) { int which =(int)data; unsigned int pinval; pinval = s3c2410_gpio_getpin(s3c_kbd->pdata->keys[which].gpio); if( pinval ) { //printk("s3c_kbd key[%d] code[%d] released\n", which, s3c_kbd->pdata->keys[which].code); input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 0); } else { //printk("s3c_kbd key[%d] code[%d] pressed\n", which, s3c_kbd->pdata->keys[which].code); input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 1); } input_sync(s3c_kbd->input_dev); } static int s3c_kbd_probe(struct platform_device *pdev) { int i = 0; int rv = -ENOMEM; struct input_dev *input_dev = NULL; s3c_kbd_platform_data_t *pdata = pdev->dev.platform_data; /* malloc s3c_kbd struct */ s3c_kbd = kmalloc(sizeof(s3c_kbd_t), GFP_KERNEL); if( !s3c_kbd ) { printk("error: s3c_kbd_probe kmalloc() for s3c_kbd failure\n"); goto fail; } memset(s3c_kbd, 0, sizeof(s3c_kbd_t)); /* malloc cancel dithering timer for every key */ s3c_kbd->timers = (struct timer_list *) kmalloc(pdata->nkeys*sizeof(struct timer_list), GFP_KERNEL); if( !s3c_kbd->timers ) { printk("error: s3c_kbd_probe kmalloc() for s3c_kbd timers failure\n"); goto fail; } memset(s3c_kbd->timers, 0, pdata->nkeys*sizeof(struct timer_list)); /* malloc input_dev for keyboard */ input_dev=input_allocate_device(); if( !input_dev ) { printk("error: s3c_kbd_probe input_allocate_device() failure\n"); goto fail; } /* setup input_dev */ input_dev->name = pdev->name; input_dev->dev.parent = &pdev->dev; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; set_bit(EV_KEY,input_dev->evbit); set_bit(EV_REP,input_dev->evbit); /* Initialize all the keys and interrupt */ for(i=0; inkeys; i++) { set_bit(pdata->keys[i].code, input_dev->keybit); s3c2410_gpio_cfgpin(pdata->keys[i].gpio, pdata->keys[i].setting); irq_set_irq_type(pdata->keys[i].nIRQ, IRQ_TYPE_EDGE_BOTH); rv = request_irq(pdata->keys[i].nIRQ, s3c_kbd_intterupt, IRQF_DISABLED, pdev->name, pdev); if( rv ) { printk("error: request IRQ[%d] for key<%d> failure\n", pdata->keys[i].nIRQ, i); rv = -EBUSY; goto fail; } //printk("s3c_kbd request IRQ[%d] for key<%d> ok\n", pdata->keys[i].nIRQ, i); /* Initialize all the keys cancel dithering timer */ setup_timer(&s3c_kbd->timers[i], cancel_dithering_timer_handler, i); } /* register input device */ rv = input_register_device(input_dev); if( rv ) { printk("error: s3c_kbd_probe input_register_device error!\n"); goto fail; } /* set s3c_kbd as private data in pdev */ s3c_kbd->input_dev = input_dev; s3c_kbd->pdata = pdata; platform_set_drvdata(pdev, s3c_kbd); printk("s3c_kbd_probe ok\n"); return 0; fail: while(i--) { disable_irq(pdata->keys[i].nIRQ); free_irq(pdata->keys[i].nIRQ, pdev); del_timer( &s3c_kbd->timers[i] ); } if(input_dev) { input_free_device(input_dev); } if(s3c_kbd && s3c_kbd->timers) { kfree(s3c_kbd->timers); } if(s3c_kbd) { kfree(s3c_kbd); } printk("s3c_kbd_probe failed\n"); return -ENODEV; } static int s3c_kbd_remove(struct platform_device *pdev) { int i = 0; s3c_kbd_t *s3c_kbd = platform_get_drvdata(pdev); for(i=0; ipdata->nkeys; i++) { del_timer( &s3c_kbd->timers[i] ); disable_irq(s3c_kbd->pdata->keys[i].nIRQ); free_irq(s3c_kbd->pdata->keys[i].nIRQ, pdev); } input_unregister_device(s3c_kbd->input_dev); kfree(s3c_kbd->timers); kfree(s3c_kbd); printk("s3c_kbd_remove ok\n"); return 0; } static struct platform_driver s3c_keyboard_driver = { .probe = s3c_kbd_probe, .remove = s3c_kbd_remove, .driver = { .name = "s3c_kbd", .owner = THIS_MODULE, }, }; static int __init s3c_keyboard_drv_init(void) { int rv; rv = platform_device_register(&s3c_keyboard_device); if(rv) { printk("S3C keyboard platform device register failure\n"); return rv; } rv = platform_driver_register(&s3c_keyboard_driver); if(rv) { printk("s3c keyboard platform driver register failure\n"); platform_device_unregister(&s3c_keyboard_device); return rv; } printk("s3c keyboard platform driver register ok\n"); return 0; } static void __exit s3c_keyboard_drv_exit(void) { printk("s3c keyboard driver exit\n"); platform_device_unregister(&s3c_keyboard_device); platform_driver_unregister(&s3c_keyboard_driver); return ; } module_init(s3c_keyboard_drv_init); module_exit(s3c_keyboard_drv_exit); MODULE_DESCRIPTION("FL2440 board keyboard input driver platform_driver"); MODULE_AUTHOR("Guo Wenxue"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:FL2440 keyboard driver");