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

 老板:“小明,你的键盘驱动搞的怎么样了?”

 “老板,磨刀不误砍柴工,原理弄清楚了,这就是小菜一碟”

老板:“你先把柴给我砍了,再去网上吹牛”

“好的,老板”

posted on 2017-02-08 14:59  old_dog  阅读(280)  评论(0)    收藏  举报

导航