Android 输入系统 --Linux input子系统-设备驱动层2
一个残酷的现实摆在面前,现在我必须把这个新按键给驱动起来。
首先要判断一个情况,我们的平台是什么?不是安卓么,是的,但是,现在的芯片厂家将谷歌代码适配了自己的CPU,然后针对原始代码做了很多修改,包括输入系统。这里有很多厂家,比如:高通 联科发 展讯等。
我手上的机器和代码是MTK的,所以现在以此为例。
首先要找到按键驱动到底是哪个?MTK平台下的按键驱动放在哪里呢?
kernel3.10/drivers/misc/mediatek/,我们发现大部分需要用的驱动都在这个目录,当然misc目录是放杂项驱动的。
这个目录下面有个keypad目录,下面有个kpd.c文件,这个就是我们的键盘驱动文件,
这些厂家并不将自己的驱动文件放在input目录下,讨厌吧?
当然还需要有一个头文件叫做
kernel3.10/include/linux/Kpd.h
#ifndef __KPD_H__
#define __KPD_H__
#include <linux/ioctl.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/earlysuspend.h>
#include <linux/aee.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <mach/hal_pub_kpd.h>
#define KPD_DEBUG KPD_YES
#define SET_KPD_KCOL _IO('k', 29)
#define KPD_SAY "kpd: "
#if KPD_DEBUG
#define kpd_print(fmt, arg...) printk(KPD_SAY fmt, ##arg)
#else
#define kpd_print(fmt, arg...) do {} while (0)
#endif
#endif /* __KPD_H__ */
没有什么内容,单人最后的宏定义KPD_DEBUG,有两个知识点,1、为何要使用##? 2、下面为何用do while这个两个知识点自己回忆一下,或者上网搜索。
我们先看看linux系统给的示例代码,简单易懂一点。
#include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <asm/irq.h> #include <asm/io.h> static struct input_dev *button_dev;
//中断处理程序 static irqreturn_t button_interrupt(int irq, void *dummy) { input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);//报告一个按键 input_sync(button_dev);//我报告完毕了 return IRQ_HANDLED; } static int __init button_init(void) { int error;
//向系统申请一个中断,并且绑定到指定的函数上 if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) { printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq); return -EBUSY; }
//要求系统分配一个空间给我
button_dev = input_allocate_device();
//系统能够分配空间不够了 if (!button_dev) { printk(KERN_ERR "button.c: Not enough memory\n"); error = -ENOMEM; goto err_free_irq; }
//准备好要上报的按键值和动作 button_dev->evbit[0] = BIT_MASK(EV_KEY); button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
//通过input子系统的接口注册输入设备 error = input_register_device(button_dev); if (error) { printk(KERN_ERR "button.c: Failed to register device\n"); goto err_free_dev; } return 0; err_free_dev: input_free_device(button_dev); err_free_irq: free_irq(BUTTON_IRQ, button_interrupt); return error; } static void __exit button_exit(void) { input_unregister_device(button_dev); free_irq(BUTTON_IRQ, button_interrupt); } module_init(button_init); module_exit(button_exit);
反复的看几遍,看熟后,然后我们看MTK的代码的简化版本
#include <linux/kpd.h>
static void kpd_memory_setting(void); static int kpd_pdrv_probe(struct platform_device *pdev); static int kpd_pdrv_remove(struct platform_device *pdev); static struct platform_driver kpd_pdrv = { .probe = kpd_pdrv_probe, .remove = kpd_pdrv_remove, .driver = { .name = KPD_NAME, .owner = THIS_MODULE, }, };
static irqreturn_t kpd_irq_handler(int irq, void *dev_id) { /* use _nosync to avoid deadlock */ disable_irq_nosync(MT_KP_IRQ_ID); tasklet_schedule(&kpd_keymap_tasklet); return IRQ_HANDLED; }
int kpd_dev_open(struct inode *inode, struct file *file) { return 0; } static struct miscdevice kpd_dev = { .minor = MISC_DYNAMIC_MINOR, .name = KPD_NAME, .fops = &kpd_dev_fops, }; static int kpd_open(struct input_dev *dev) { kpd_slide_qwerty_init(); /* API 1 for kpd slide qwerty init settings */ return 0; } static int kpd_pdrv_probe(struct platform_device *pdev) { int i, r; int err = 0; kpd_ldvt_test_init(); /* API 2 for kpd LFVT test enviroment settings */ /* initialize and register input device (/dev/input/eventX) */ kpd_input_dev = input_allocate_device(); if (!kpd_input_dev) return -ENOMEM; kpd_input_dev->name = KPD_NAME; kpd_input_dev->id.bustype = BUS_HOST; kpd_input_dev->id.vendor = 0x2454; kpd_input_dev->id.product = 0x6500; kpd_input_dev->id.version = 0x0010; kpd_input_dev->open = kpd_open; __set_bit(EV_KEY, kpd_input_dev->evbit); for (i = 0; i < KPD_NUM_KEYS; i++) { if (kpd_keymap[i] != 0) __set_bit(kpd_keymap[i], kpd_input_dev->keybit); } kpd_input_dev->dev.parent = &pdev->dev; r = input_register_device(kpd_input_dev); if (r) { printk(KPD_SAY "register input device failed (%d)\n", r); input_free_device(kpd_input_dev); return r; } /* register device (/dev/mt6575-kpd) */ kpd_dev.parent = &pdev->dev; r = misc_register(&kpd_dev); if (r) { printk(KPD_SAY "register device failed (%d)\n", r); input_unregister_device(kpd_input_dev); return r; } /* register IRQ and EINT */ kpd_set_debounce(KPD_KEY_DEBOUNCE); r = request_irq(MT_KP_IRQ_ID, kpd_irq_handler, IRQF_TRIGGER_FALLING, KPD_NAME, NULL); if (r) { printk(KPD_SAY "register IRQ failed (%d)\n", r); misc_deregister(&kpd_dev); input_unregister_device(kpd_input_dev); return r; } mt_eint_register(); if ((err = kpd_create_attr(&kpd_pdrv.driver))) { kpd_print("create attr file fail\n"); kpd_delete_attr(&kpd_pdrv.driver); return err; } pr_warn(KPD_SAY "%s Done\n", __FUNCTION__); return 0; } /* should never be called */ static int kpd_pdrv_remove(struct platform_device *pdev) { return 0; } static int kpd_pdrv_suspend(struct platform_device *pdev, pm_message_t state) { kpd_suspend = true; kpd_disable_backlight(); kpd_print("suspend!! (%d)\n", kpd_suspend); return 0; } static int kpd_pdrv_resume(struct platform_device *pdev) { kpd_suspend = false; kpd_print("resume!! (%d)\n", kpd_suspend); return 0; } static int __init kpd_mod_init(void) { int r; r = platform_driver_register(&kpd_pdrv); if (r) { printk(KPD_SAY "register driver failed (%d)\n", r); return r; } return 0; } static void __exit kpd_mod_exit(void) { } module_init(kpd_mod_init); module_exit(kpd_mod_exit); module_param(kpd_show_hw_keycode, int, 0644); module_param(kpd_show_register, int, 0644); MODULE_AUTHOR("*** <***@mediatek.com>"); MODULE_DESCRIPTION("MTK Keypad (KPD) Driver v0.*"); MODULE_LICENSE("GPL");
这个代码要从下往上看,先看module_init,这个是设备初始化时第一个会调用的函数。
这个函数其实只有一个函数调用 platform_driver_register(&kpd_pdrv);就是向系统注册按键驱动.
static struct platform_driver kpd_pdrv = { .probe = kpd_pdrv_probe, .remove = kpd_pdrv_remove, .driver = { .name = KPD_NAME, .owner = THIS_MODULE, }, };
注册之后,系统如果成功将设备挂载在虚拟的平台总线上后,就会反过来调用.probe指向的函数,就是kpd_pdrv_probe
static int kpd_pdrv_probe(struct platform_device *pdev) { int i, r; int err = 0; /* initialize and register input device (/dev/input/eventX) */ kpd_input_dev = input_allocate_device(); if (!kpd_input_dev) return -ENOMEM; kpd_input_dev->name = KPD_NAME; kpd_input_dev->id.bustype = BUS_HOST; kpd_input_dev->id.vendor = 0x2454; kpd_input_dev->id.product = 0x6500; kpd_input_dev->id.version = 0x0010; kpd_input_dev->open = kpd_open; __set_bit(EV_KEY, kpd_input_dev->evbit); for (i = 0; i < KPD_NUM_KEYS; i++) { if (kpd_keymap[i] != 0) __set_bit(kpd_keymap[i], kpd_input_dev->keybit); } kpd_input_dev->dev.parent = &pdev->dev; r = input_register_device(kpd_input_dev); if (r) { printk(KPD_SAY "register input device failed (%d)\n", r); input_free_device(kpd_input_dev); return r; } /* register device (/dev/mt6575-kpd) */ kpd_dev.parent = &pdev->dev; r = misc_register(&kpd_dev); if (r) { printk(KPD_SAY "register device failed (%d)\n", r); input_unregister_device(kpd_input_dev); return r; } /* register IRQ and EINT */ kpd_set_debounce(KPD_KEY_DEBOUNCE); r = request_irq(MT_KP_IRQ_ID, kpd_irq_handler, IRQF_TRIGGER_FALLING, KPD_NAME, NULL); if (r) { printk(KPD_SAY "register IRQ failed (%d)\n", r); misc_deregister(&kpd_dev); input_unregister_device(kpd_input_dev); return r; } mt_eint_register(); if ((err = kpd_create_attr(&kpd_pdrv.driver))) { kpd_print("create attr file fail\n"); kpd_delete_attr(&kpd_pdrv.driver); return err; } pr_warn(KPD_SAY "%s Done\n", __FUNCTION__); return 0; }
老板:“小明,你的键盘驱动搞的怎么样了?”
“老板,磨刀不误砍柴工,原理弄清楚了,这就是小菜一碟”
老板:“你先把柴给我砍了,再去网上吹牛”
“好的,老板”
浙公网安备 33010602011771号