S5PV310的矩阵按键接口
看一下芯片上的专用接口,如下图,全用的话有点多。
关于专用接口的寄存器,这些寄存器我们后面要用得到的,按键的行、列信息会在这里面暂存的。
以S5PV310为例,驱动代码:samsung-keypad.c
软件部分:
总体流程图如下,这个是在触摸屏基础上改过来的,感觉流程都是这个样子的。中断触发,中断处理。
一、矩阵键行、列设定,和上报键值设定
在android-kernel-samsung-dev/arch/arm/mach-exynos/mach-smdkv310.c中
- static uint32_t smdkv310_keymap[] __initdata = {
-
- KEY(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3),
- KEY(0, 6, KEY_4), KEY(0, 7, KEY_5),
- KEY(1, 3, KEY_A), KEY(1, 4, KEY_C), KEY(1, 5, KEY_E),
- KEY(1, 6, KEY_B), KEY(1, 7, KEY_D)
- };
-
- static struct matrix_keymap_data smdkv310_keymap_data __initdata = {
- .keymap = smdkv310_keymap,
- .keymap_size = ARRAY_SIZE(smdkv310_keymap),
- };
- static struct samsung_keypad_platdata smdkv310_keypad_data __initdata = {
- .keymap_data = &smdkv310_keymap_data,
- .rows = 2,
- .cols = 8,
- };
- static void __init smdkv310_machine_init(void)
- {
- samsung_keypad_set_platdata(&smdkv310_keypad_data);
- }
(1)、KEY(row, col,keycode)
KEY这个宏在android-kernel-samsung-dev/include/linux/input/Matrix_keypad.h中实现:
- #define MATRIX_MAX_ROWS 32
- #define MATRIX_MAX_COLS 32
- #define KEY(row, col, val) ((((row) & (MATRIX_MAX_ROWS - 1)) << 24) |\
- (((col) & (MATRIX_MAX_COLS - 1)) << 16) |\
- ((val) & 0xffff))
keycode的值在android-kernel-samsung-dev/include/linux/input.h中有定义,如下:
- #define KEY_RESERVED 0
- #define KEY_ESC 1
- #define KEY_1 2
- #define KEY_2 3
- #define KEY_3 4
- #define KEY_4 5
- #define KEY_5 6
- #define KEY_6 7
- #define KEY_7 8
- #define KEY_8 9
- #define KEY_9 10
- #define KEY_0 11
- #define KEY_MINUS 12
- #define KEY_EQUAL 13
- #define KEY_BACKSPACE 14
- #define KEY_TAB 15
- #define KEY_Q 16
- #define KEY_W 17
- #define KEY_E 18
- #define KEY_R 19
- #define KEY_T 20
- #define KEY_Y 21
- #define KEY_U 22
(2)、行列设定;
(3)、平台设备初始化;
- samsung_keypad_set_platdata(&smdkv310_keypad_data)。
二、上面设定的keycode键值和上层相对应
4.0.3_r1/device/samsung/smdkv310/samsung-keypad.kl中
- key 2 DPAD_UP WAKE_DROPPED
- key 3 DPAD_CENTER WAKE_DROPPED
- key 4 DPAD_DOWN WAKE_DROPPED
- key 5 DPAD_RIGHT WAKE_DROPPED
- key 6 DPAD_LEFT WAKE_DROPPED
- key 18 VOLUME_DOWN WAKE
- key 30 HOME WAKE_DROPPED
- key 32 MENU WAKE_DROPPED
- key 46 VOLUME_UP WAKE
- key 48 BACK WAKE_DROPPED
- key 10 POWER WAKE
总体对应图:
以KEY_A为例,KEY_A 30最终和上层的keypad.kl中的30 HOME相对应
三、矩阵键盘驱动程序分析
android-kernel-samsung-dev/drivers/input/keyboard/samsung-keypad.c
1、probe函数分析:
- static int __devinit samsung_keypad_probe(struct platform_device *pdev)
- {
- const struct samsung_keypad_platdata *pdata;
- const struct matrix_keymap_data *keymap_data;
- struct samsung_keypad *keypad;
- struct resource *res;
- struct input_dev *input_dev;
- unsigned int row_shift;
- unsigned int keymap_size;
- int error;
- ………………
- keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
-
- keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!keypad || !input_dev) {
- error = -ENOMEM;
- goto err_free_mem;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- error = -ENODEV;
- goto err_free_mem;
- }
-
- keypad->base = ioremap(res->start, resource_size(res));
- if (!keypad->base) {
- error = -EBUSY;
- goto err_free_mem;
- }
- …………
-
- keypad->input_dev = input_dev;
- keypad->row_shift = row_shift;
- keypad->rows = pdata->rows;
- keypad->cols = pdata->cols;
- init_waitqueue_head(&keypad->wait);
-
- input_dev->name = pdev->name;
- input_dev->id.bustype = BUS_HOST;
- input_dev->dev.parent = &pdev->dev;
- input_set_drvdata(input_dev, keypad);
-
- input_dev->open = samsung_keypad_open;
- input_dev->close = samsung_keypad_close;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY);
- if (!pdata->no_autorepeat)
- input_dev->evbit[0] |= BIT_MASK(EV_REP);
-
- input_set_capability(input_dev, EV_MSC, MSC_SCAN);
-
- input_dev->keycode = keypad->keycodes;
- input_dev->keycodesize = sizeof(keypad->keycodes[0]);
- input_dev->keycodemax = pdata->rows << row_shift;
-
- matrix_keypad_build_keymap(keymap_data, row_shift,
- input_dev->keycode, input_dev->keybit);
-
- keypad->irq = platform_get_irq(pdev, 0);
- if (keypad->irq < 0) {
- error = keypad->irq;
- goto err_put_clk;
- }
-
- error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq,
- IRQF_ONESHOT, dev_name(&pdev->dev), keypad);
- if (error) {
- dev_err(&pdev->dev, "failed to register keypad interrupt\n");
- goto err_put_clk;
- }
-
- error = input_register_device(keypad->input_dev);
- if (error)
- goto err_free_irq;
-
- device_init_wakeup(&pdev->dev, pdata->wakeup);
- platform_set_drvdata(pdev, keypad);
- return 0;
-
- ………………
- }
(1)、input参数初始化;
(2)、打开、关闭函数;
- input_dev->open = samsung_keypad_open;
- static int samsung_keypad_open(struct input_dev *input_dev)
- {
- struct samsung_keypad *keypad = input_get_drvdata(input_dev);
- samsung_keypad_start(keypad);
- return 0;
- }
- 其实open函数调用samsung_keypad_start()函数,对按键的寄存器一些操作,如下面寄存器列表中的。
- static void samsung_keypad_start(struct samsung_keypad *keypad)
- {
- unsigned int val;
-
- keypad->stopped = false;
- clk_enable(keypad->clk);
-
- val = readl(keypad->base + SAMSUNG_KEYIFCON);
- val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
- writel(val, keypad->base + SAMSUNG_KEYIFCON);
-
- writel(0, keypad->base + SAMSUNG_KEYIFCOL);
- }
(3)、中断函数注册;
- error=request_threaded_irq(keypad->irq,NULL, samsung_keypad_irq,IRQF_ONESHOT, dev_name(&pdev->dev), keypad);
request_threaded_irq这个函数也许我们比较陌生,可是看下下面一个函数也许就不难理解了:
- static inline int __must_check
- request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
- const char *name, void *dev)
- {
- return request_threaded_irq(irq, handler, NULL, flags, name, dev);
- }
这个函数跟中断的作用是一样的,keypad->irq= platform_get_irq(pdev, 0);于中段号,当有按键按下时,会跳到中断函数,samsung_keypad_irq中;
(4)、input驱动注册,input驱动比较重要,触摸屏、按键、gsensor、battery等都是通过input子系统上报的。
2、中断函数: samsung_keypad_irq分析,当有按键按下时,调用这个函数
- static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
- {
- struct samsung_keypad *keypad = dev_id;
-
- unsigned int row_state[SAMSUNG_MAX_COLS];
- unsigned int val;
- bool key_down;
- do {
- val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
-
-
- writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
-
- samsung_keypad_scan(keypad, row_state);
-
- key_down = samsung_keypad_report(keypad, row_state);
-
- if (key_down)
- wait_event_timeout(keypad->wait, keypad->stopped,
- msecs_to_jiffies(50));
- } while (key_down && !keypad->stopped);
- return IRQ_HANDLED;
- }
(1)、清除中断;
(2)、扫描行列值,写入寄存器(后面分析);
(3)、键值上报,这是函数的主要部分了(后面分析);
(4)、延时去抖动,如果有按键按下,有一个段时间的延时,看是否真正有按键,这就是所说的去抖动;
3、当按键按下时,行列值的扫描函数samsung_keypad_scan执行,写入相应行列寄存器
上图我们知道,对于矩阵键盘,主控有专门的接口,也有相应的寄存器,
- static void samsung_keypad_scan(struct samsung_keypad *keypad,
- unsigned int *row_state)
- {
- struct device *dev = keypad->input_dev->dev.parent;
- unsigned int col;
- unsigned int val;
- for (col = 0; col < keypad->cols; col++) {
- if (samsung_keypad_is_s5pv210(dev)) {
- val = S5PV210_KEYIFCOLEN_MASK;
- val &= ~(1 << col) << 8;
- } else {
- val = SAMSUNG_KEYIFCOL_MASK;
- val &= ~(1 << col);
- }
- writel(val, keypad->base + SAMSUNG_KEYIFCOL);
- mdelay(1);
- val = readl(keypad->base + SAMSUNG_KEYIFROW);
- row_state[col] = ~val & ((1 << keypad->rows) - 1);
- }
-
- writel(0, keypad->base + SAMSUNG_KEYIFCOL);
- }
4、通过扫描键值写入相应寄存器,然后通过
- static bool samsung_keypad_report(struct samsung_keypad *keypad,
- unsigned int *row_state)
- {
- struct input_dev *input_dev = keypad->input_dev;
- unsigned int changed;
- unsigned int pressed;
- unsigned int key_down = 0;
- unsigned int val;
- unsigned int col, row;
-
- for (col = 0; col < keypad->cols; col++) {
- changed = row_state[col] ^ keypad->row_state[col];
- key_down |= row_state[col];
- if (!changed)
- continue;
- for (row = 0; row < keypad->rows; row++) {
- if (!(changed & (1 << row)))
- continue;
- pressed = row_state[col] & (1 << row);
- dev_dbg(&keypad->input_dev->dev,
- "key %s, row: %d, col: %d\n",
- pressed ? "pressed" : "released", row, col);
-
- val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
- printk("key %s, row: %d, col: %d\n",pressed ? "pressed" : "released", row, col);
- printk("test by xu_bin for val = %d,key = %d\n",val,keypad->keycodes[val]);
- input_event(input_dev, EV_MSC, MSC_SCAN, val);
-
- input_report_key(input_dev,
- keypad->keycodes[val], pressed);
- }
-
- input_sync(keypad->input_dev);
- }
- memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
- return key_down;
- }
(1)、#defineMATRIX_SCAN_CODE(row, col, row_shift) (((row)<< (row_shift)) + (col))
row_shift = 3
如:row = 1; col = 6; row_shift = 3
val = MATRIX_SCAN_CODE(row, col,keypad->row_shift) = ((1)<<(3)+(6)) = 14;
就相当于:(1,6)这个数组里面的值:48
printk("key %s, row: %d, col:%d\n",pressed ? "pressed" : "released", row, col);
printk("test by xu_bin for val =%d,key = %d\n",val,keypad->keycodes[val]);
(2)、上报键值keypad->keycodes[val],这个值是对于我们这个驱动来说的最终值;
(3)、input上报后同步,这个和input子系统相关。
这样就完成了驱动部分的上报。