--作者:老树
--发布时间:2010-6-2 15:01:19
--Android 架构解析及驱动 键盘篇

一、用户空间

ADROID 2.1 架构解析 7 键盘

以上链接,详细介绍了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输入子设备。

更多相关文章

  1. Android(安卓)按键流程分析笔记
  2. 通过ijetty获取android设备的截图
  3. Android(安卓)Camera2 之 CameraDevice 详解
  4. mac电脑开发android找不到设备
  5. android系统中运行jar文件
  6. Android串口通信:基本知识梳理
  7. android kernel和标准linux kernel的区别
  8. Android(安卓)内核与标准Linux内核的区别
  9. android linux kernel和standard linux kernel的区别

随机推荐

  1. 海康威视Android SDK,即萤石Android SDK
  2. 开发Android下纯C程序时,打开时提示not f
  3. 简述修改logo以及文字
  4. Android系统自带主题样式(android:theme),An
  5. Android界面——LinearLayout和RelativeL
  6. RelativeLayout 常用XML 属性
  7. android Wifi自动连接
  8. Android使用ViewFlipper做页面切换,与手势
  9. Android发展史
  10. 写一个没有Activity的 HelloWorld for an