当前位置: 首页 > 工具软件 > Fish Sync > 使用案例 >

Android GoldFish新加键盘驱动

茹正祥
2023-12-01

在项目选型决定之前,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输入了。

转载于:https://my.oschina.net/u/617166/blog/412938

 类似资料: