--作者:老树
--发布时间: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.设备注册
staticstruct resources3c_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,
}
};

structplatform_devices3c_device_keypad= {
.name = "s3c-keypad",
.id = -1,
.num_resource =ARRAY_SIZE(s3c_keypad_resource),
.resource = s3c_keypad_resource,
};
EXPORT_SYMBOL(s3c_device_keypad);

staticstruct platform_device*smdk6410_devices[]__initdata = {
...
&s3c_device_keypad,
}

static void __initsmdk6410_machine_init(void)
{
...
platform_add_devices(smdk6410_devices,ARRAY_SIZE(smdk6410_devices));
...
}

intplatform_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 __inits3c_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 __exits3c_keypad_exit(void)
{
platform_driver_unregister(&s3c_keypad_driver);
}

platform_driver结构体:
staticstruct platform_drivers3c_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 __inits3c_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_tgpio_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事件在传输过程中的一些函数
voidinput_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 voidinput_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 voidinput_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 voidinput_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. adb安装及命令总结
  2. Android碎笔录2——按键的点击变色和圆角实现
  3. Android中捕捉menu按键点击事件的方法
  4. Android(安卓)API包名及包的功能的中文介绍
  5. Android(安卓)WiFi Direct文件传输
  6. android 设备唯一码的获取,Cpu号,Mac地址
  7. android拔掉耳机后音乐自动暂停
  8. Android——adb工具的使用
  9. 这款开源 Android(安卓)实时投屏软件是 Qt 做的

随机推荐

  1. Android实现图表绘制和展示
  2. 10个快速开发Android/Iphone web App应用
  3. Android(安卓)webview
  4. Android系统上部署usb打印机
  5. android HAL 详解
  6. ANDROID音频系统散记之二:resample-1
  7. Android中MVP模式
  8. Android(安卓)学习笔记(4)—— ToggleButto
  9. AndroidX介绍及项目迁移
  10. Android(安卓)P 中的网络安全配置指南 ne