Android 架构解析及驱动 键盘篇

--  作者:老树
--  发布时间: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输入子设备。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值