-- 作者:老树
-- 发布时间:2010-6-2 15:01:19
-- Android 架构解析及驱动 键盘篇
一、用户空间
以上链接,详细介绍了Android用户空间按键的操作,最终通过不断轮询所有设备,直到读取有POLLIN事件产生的设备状态:if(mFDs[i].revents & POLLIN) {res = read(mFDs[i].fd, &iev, sizeof(iev));......
二、底层驱动
1.设备注册
static struct resource s3c_keypad_resource[] = {
[0] = {
.start = S3C64XX_PA_KEYPAD,
.end = S3C64XX_PA_KEYPAD+S3C64XX_SZ_KEYPAD-1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_KEYPAD,
.end = IRQ_KEYPAD,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_keypad = {
.name = "s3c-keypad",
.id = -1,
.num_resource =ARRAY_SIZE(s3c_keypad_resource),
.resource = s3c_keypad_resource,
};
EXPORT_SYMBOL(s3c_device_keypad);
static struct platform_device *smdk6410_devices[] __initdata = {
...
&s3c_device_keypad,
}
static void __init smdk6410_machine_init(void)
{
...
platform_add_devices(smdk6410_devices,ARRAY_SIZE(smdk6410_devices));
...
}
int platform_add_devices(struct platform_device **devs, int num)
{
int i,ret = 0;
for(i=0;i<num;i++){
ret = platform_device_register(devs[i]);
if(ret){
while(--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
2.驱动注册与注销
module_init(s3c_keypad_init);
module_exit(s3c_keypad_exit);
static int __init s3c_keypad_init(void)
{
int ret;
ret = platform_driver_register(&s3c_keypad_driver);
if(!ret)
printk(KERN_INFO"S3C Keypad Driver//n");
return ret;
}
static void __exit s3c_keypad_exit(void)
{
platform_driver_unregister(&s3c_keypad_driver);
}
platform_driver结构体:
static struct platform_driver s3c_keypad_driver = {
.probe =s3c_keypad_probe,
.remove =s3c_keypad_remove,
.suspend =s3c_keypad_suspend,
.resume =s3c_keypad_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-keypad",
},
};
3.设备探测
static int __init s3c_keypad_probe(struct platform_device *pdev)
{
struct resource *res, *keypad_mem, *keypad_irq = NULL;
struct input_dev *input_dev;
int ret, size;
int key, code;
struct s3c_keypad_extra *extra = NULL;
struct s3c_keypad_gpio_key *gpio_key;
int i;
char * input_dev_name;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取设备内存信息
if (res == NULL) {
dev_err(&pdev->dev,"no memory resource specified//n");
return -ENOENT;
}
size = (res->end - res->start) + 1;
keypad_mem = request_mem_region(res->start, size, pdev->name);
if (keypad_mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region//n");
ret = -ENOENT;
goto err_req;
}
key_base = ioremap(res->start, size); //IO空间映射
if (key_base == NULL) {
printk(KERN_ERR "Failed to remap register block//n");
ret = -ENOMEM;
goto err_map;
}
keypad_clock = clk_get(&pdev->dev, "keypad"); //获取keypad对应的时钟
if (IS_ERR(keypad_clock)) {
dev_err(&pdev->dev, "failed to find keypad clock source//n");
ret = PTR_ERR(keypad_clock);
goto err_clk;
}
clk_enable(keypad_clock); //使能keypad模块的时钟
s3c_keypad_data = kzalloc(sizeof(struct s3c_keypad), GFP_KERNEL);
input_dev = input_allocate_device(); //申请一个input_dev设备
input_dev_name = (char *)kmalloc(sizeof("s3c-keypad-revxxxx"), GFP_KERNEL); //这里就是用户空间提到的键值配置表
if (!s3c_keypad_data || !input_dev) {
ret = -ENOMEM;
goto err_alloc;
}
platform_set_drvdata(pdev, s3c_keypad_data);
for (i=0; i<sizeof(s3c_keypad_extra)/sizeof(struct s3c_keypad_extra); i++)
{
if (s3c_keypad_extra[i].board_num == system_rev) {
extra = &s3c_keypad_extra[i];
sprintf(input_dev_name, "%s%s%04x", DEVICE_NAME, "-rev", system_rev);
DPRINTK(": board rev 0x%04x is detected!//n", s3c_keypad_extra[i].board_num);
break;
}
}
if(!extra) {
extra = &s3c_keypad_extra[0];
sprintf(input_dev_name, "%s%s", DEVICE_NAME, "-rev0000"); //default revison
DPRINTK(": failed to detect board rev. set default rev00//n");
}
DPRINTK(": input device name: %s.//n", input_dev_name);
s3c_keypad_data->dev = input_dev;
s3c_keypad_data->extra = extra;
gpio_key = extra->gpio_key;
/* create and register the input driver */
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
s3c_keypad_data->nr_rows = KEYPAD_ROWS;
s3c_keypad_data->no_cols = KEYPAD_COLUMNS;
for(key = 0; key < 32; key++){
code = s3c_keypad_data->keycodes[key] = keypad_keycode[key];
if(code<=0)
continue;
set_bit(code & KEY_MAX, input_dev->keybit);
}
for (i=0; i<extra->gpio_key_num; i++ ){
input_set_capability(input_dev, EV_KEY, (gpio_key+i)->keycode);
}
input_dev->name = DEVICE_NAME;
input_dev->phys = "s3c-keypad/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0001;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0001;
input_dev->keycode = keypad_keycode;
/* init_timer(&keypad_timer); */
keypad_timer.function = keypad_timer_handler;
keypad_timer.data = (unsigned long)s3c_keypad_data;
for (i=0; i<extra->gpio_key_num; i++, gpio_key+=1)
{
s3c_gpio_cfgpin(gpio_key->gpio, S3C_GPIO_SFN(gpio_key->gpio_af));
s3c_gpio_setpull(gpio_key->gpio, S3C_GPIO_PULL_NONE);
set_irq_type(gpio_key->eint, IRQ_TYPE_EDGE_BOTH);
ret = request_irq(gpio_key->eint, gpio_int_handler, IRQF_DISABLED,
"s3c_keypad gpio key", (void *)s3c_keypad_data); //中断注册
if (ret) {
printk(KERN_ERR "request_irq(%d) failed (IRQ for GPIO KEY) !!!//n", gpio_key->eint);
ret = -EIO;
goto err_irq;
}
}
ret = input_register_device(input_dev); //向输入子系统注册此设备
if (ret) {
printk("Unable to register s3c-keypad input device!!!//n");
goto out;
}
return 0;
out:
input_free_device(input_dev);
kfree(s3c_keypad_data);
err_irq:
free_irq(keypad_irq->start, input_dev);
free_irq(keypad_irq->end, input_dev);
gpio_key = extra->gpio_key;
for (i=0; i<extra->gpio_key_num; i++, gpio_key+=1)
free_irq(gpio_key->eint, s3c_keypad_data);
err_alloc:
clk_disable(keypad_clock);
clk_put(keypad_clock);
err_clk:
iounmap(key_base);
err_map:
release_resource(keypad_mem);
kfree(keypad_mem);
err_req:
return ret;
}
4.按键出发中断,中断处理与数据上传
static irqreturn_t gpio_int_handler(int irq, void *dev_id)
{
struct input_dev *dev = s3c_keypad_data->dev;
struct s3c_keypad_extra *extra = s3c_keypad_data->extra;
struct s3c_keypad_gpio_key *gpio_key = extra->gpio_key;
int i,state;
for (i=0; i<extra->gpio_key_num; i++)
{
if (gpio_key[i].eint == irq)
{
gpio_key = &gpio_key[i];
break;
}
}
if (gpio_key != NULL)
{
state = gpio_get_value(gpio_key->gpio);
state ^= gpio_key->state_upset;
if(state) {
input_report_key(dev, gpio_key->keycode, 1);
}
else {
input_report_key(dev, gpio_key->keycode, 0);
}
}
return IRQ_HANDLED;
}
数据上传input_report_key-》input_event-》input_handle_event-》input_pass_event-》handle->handler->event(对应函数:evdev_event)-》数据暂存event(input_event结构体),以供用户空间读写操作。
5.input事件在传输过程中的一些函数
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);//因为按键的存在随机性,所以按键是给系统提供墒随机数的好来源.
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
...
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {//这次来的是否为新的键值
if (value != 2) {
__change_bit(code, dev->key);//通过异或^操作,反转code对应的bitmap,如果value等于2,那么将忽略该按键
if (value)
input_start_autorepeat(dev, code);//键盘按下,那么开启定时检测,这样可以出现连续输入的效果
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
...
}
static void input_start_autorepeat(struct input_dev *dev, int code)
{
if (test_bit(EV_REP, dev->evbit) &&
dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
dev->timer.data) {
dev->repeat_key = code;
mod_timer(&dev->timer,//重新启动定时器input_repeat_key,时间间隔msecs_to_jiffies(dev->rep[REP_DELAY])
jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
}
static void input_repeat_key(unsigned long data)
{
struct input_dev *dev = (void *) data;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
if (test_bit(dev->repeat_key, dev->key) &&
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
input_pass_event(dev, EV_KEY, dev->repeat_key, 2);//交给处理按键函数
if (dev->sync) {
/*
* Only send SYN_REPORT if we are not in a middle
* of driver parsing a new hardware packet.
* Otherwise assume that the driver will send
* SYN_REPORT once it/'s done.
*/
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
}
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies +
msecs_to_jiffies(dev->rep[REP_PERIOD]));
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
以上驱动是input方式完成的,不清楚的可查阅Linux input输入子设备。