在项目选型决定之前,sw工作有些可以通过模拟器完成,比如一些驱动的框架。
sample参考drivers\input\kerboard\gpio_keys.c,采取的是platform driver的模型。
platform device和driver,他们的name段必须相同。否则不能对应devices信息和driver
device是虚拟的设备,主要是注册和声明资源。driver是资源使用者。那样相同的driver可以挂载在不同的资源段上。
以下是改好的key驱动
/*
* Driver for test.
*
* Copyright 2015 Clement Zhao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sysctl.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
struct test_drvdata {
struct gpio_keys_button *button;
struct input_dev *input;
};
static ssize_t test_read(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct test_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
//report key down
input_report_key(input, KEY_1, 1);
//report key up
input_report_key(input, KEY_1, 0);
input_sync(input);
printk("file read\n");
return 0;
}
static ssize_t test_write(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct test_drvdata *ddata = dev_get_drvdata(dev);
struct input_dev *input = ddata->input;
//report key down
input_report_key(input, KEY_1, 1);
//report key up
input_report_key(input, KEY_1, 0);
input_sync(input);
printk("file write\n");
return count;
}
//create dev_attr_xxx attr
static DEVICE_ATTR(test, S_IRUGO | S_IWUGO, test_read, test_write);
static int __devinit test_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct test_drvdata *ddata;
struct input_dev *input;
int wakeup = 0;
int error = 0;
ddata = kzalloc(sizeof(struct test_drvdata),
GFP_KERNEL);
input = input_allocate_device();
if (!ddata || !input) {
return -ENOMEM;
}
platform_set_drvdata(pdev, ddata);
input->name = pdev->name;
input->phys = "test/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
//for idc/dl file index
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0000;
// Enable auto repeat feature of Linux input subsystem
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
ddata->input = input;
struct gpio_keys_button *button = pdata->buttons;
ddata->button = button;
unsigned int type = button->type;
if (button->wakeup){
wakeup = 1;
input_set_capability(input, type, button->code);
//printk("%d %d %d %d\n", EV_KEY, KEY_1, type, button->code);
}
error = input_register_device(input);
if (error) {
pr_err("gpio-keys: Unable to register input device, "
"error: %d\n", error);
goto fail1;
}
device_init_wakeup(&pdev->dev, wakeup);
//create sysfs node
error = device_create_file(&pdev->dev, &dev_attr_test);
if (error < 0){
input_unregister_device(input);
goto fail1;
}
return 0;
fail1:
input_free_device(input);
kfree(ddata);
return error;
}
static int __devexit test_remove(struct platform_device *pdev)
{
struct test_drvdata *ddata = dev_get_drvdata(&pdev->dev);
struct input_dev *input = ddata->input;
input_unregister_device(input);
input_free_device(input);
kfree(ddata);
//remove sysfs node
device_remove_file(&pdev->dev, &dev_attr_test);
return 0;
}
static int test_suspend(struct platform_device *pdev, pm_message_t state)
{
return 0;
}
static int test_resume(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver test_device_driver = {
.probe = test_probe,
.remove = __devexit_p(test_remove),
.suspend = test_suspend,
.resume = test_resume,
.driver = {
.name = "test",
.owner = THIS_MODULE,
}
};
static int __init platdrv_test_init(void)
{
return platform_driver_register(&test_device_driver);
}
static void __exit platdrv_test_exit(void)
{
platform_driver_unregister(&test_device_driver);
}
module_init(platdrv_test_init);
module_exit(platdrv_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Clement Zhao <clement@zhimou.io");
MODULE_DESCRIPTION("test driver for CPU GPIOs");
MODULE_ALIAS("platform:test");
因为是模拟的,所以需要建立触发输入键值的机制。proc和sysfs都是可以使用的,但貌似sysfs比较先进一点,那就直接上sysfs吧。从头文件中还可以找到 show/store 函数的原型,注意到它和虚拟字符设备或 proc 项的 read/write 的作用很类似,但有一点不同是show/store 函数上的 buf/count 参数是在 sysfs 层已作了用户区/内核区的内存复制,虚拟字符设备上常见的 __user属性在这里并不需要,因而也不需要多一次 copy_from_user/copy_to_user, 在 show/store 函数参数上的buf/count 参数已经是内核区的地址,可以直接操作。
device_create_file和sysfs_create_group是2组不同的sysfs节点创建方式。sysfs_create_group要建立koject对象。device_create_file相对简单,直接可以在platform drvier上用。无需额外char或者misc设备。
代码通过DEVICE_ATTR注册了dev_attr_xxx名字的attr,也就是可以通过读写文件节点,进行手动输入。
在kernel启动后,adb shell进入\sys\devices\platform\test,里面建立的test节点可以通过cat/echo进行交互。
接下来就要注册资源,修改arch\mips\mach-goldfish\goldfish-platform.c,加入以下代码
static struct gpio_keys_button gpio_buttons[] = {
{
.code = KEY_MENU,
.gpio = 7,
.active_low = 1,
.desc = "user",
.type = EV_KEY,
.wakeup = 1,
},
};
static struct gpio_keys_platform_data gpio_key_info = {
.buttons = &gpio_buttons,
.nbuttons = ARRAY_SIZE(gpio_buttons),
};
static struct platform_device test = {
.name = "test",
.id = -1,
.dev = {
.platform_data = &gpio_key_info,
},
};
struct platform_device goldfish_pdev_bus_device = {
.name = "goldfish_pdev_bus",
.id = -1,
.num_resources = ARRAY_SIZE(goldfish_pdev_bus_resources),
.resource = goldfish_pdev_bus_resources
};
static void __init goldfish_init(void)
{
platform_device_register(&goldfish_pdev_bus_device);
if (platform_device_register(&test) < 0)
printk("Goldfish Unable to register test device\n");
else
printk("Goldfish Register test device success\n");
}
修改mach-goldfish下的makefile,把编译对象加进去,kerne启动后就自动加载此驱动。
按键驱动扫描到,先input_report_key上报一个键值
http://wenku.baidu.com/link?url=6vHnUBq9k9TzirWiG3j6la6OkanlZGmgGM2CRSGlPyz7Jy8LfKFoSfrR6BYExdMUNCCDdFHTpTF4mBPPWLWVNGWnBud2gQ_ZR7yUTfrv8xG
android系统中,获取到键盘的键值后,会搜索/system/usr/keylayout/*.kl这个文件
Android标准的键值映射表文件为qwerty.kl
WAKE: 当设备睡眠时按下此键,设备将被唤醒,按键事件将会被发送到应用程序.
WAKE_DROPPED: 当设备睡眠时按下此键,设备将被唤醒,而按键事件不会被发送到应用程序.
kecode对照表http://blog.csdn.net/feizhixuan46789/article/details/16801429
那么在Android下外加自己的键盘驱动,还需要以下工作:
1.先要注册platform驱动,然后driver的keycode要和platform里面的keycode要对应。没有注册的keycode无法上传
2.设备根据id的vendor、product code来识别idc文件(另一种是加上version)。idc里面定义kl和kcm文件,这个可以复用qwerty.idc里面的
文件都在framework/base/data/keyboard,新增文件加到common.mk里面即可
3.成功注册后,打印提示:
ventHub: New device: id=2, fd=97, path='/dev/input/event0', name='test', classes=0x1, configuration='/system/usr/idc/Vendor_0001_Product_0001.idc', keyLayout='/system/usr/keylayout/qwerty.kl', keyCharacterMap='/system/usr/keychars/qwerty.kcm', builtinKeyboard=false
InputReader: Device added: id=2, name='test', sources=0x00000101
4.keycode有分scan code和event code,分别在include/linux/input.h定义scan code
kl文件作为scan code和event code的映射关系
根据代码,我们创建一个Vendor_0001_Product_0001.idc,里面内容完全复制qwerty.idc即可,除非是想连kl和kcm文件都要改。
接着在framework/base/data/keyboards/common.mk下加上此文件,重新编译android。配合上我们新建的kernel,模拟器就可以得到用户触发的input输入了。