1. kernel_imx/arch/arm/mach-mx5/mx51_babbage.c文件mxc_board_init()函数中
mxc_register_device(&mxc_keypad_device, &keypad_plat_data); //注册按键设备
mxc_register_device定义为:
int __init mxc_register_device(struct platform_device *pdev, void *data)
{
int ret;
pdev->dev.platform_data = data;
ret = platform_device_register(pdev);
if (ret)
pr_debug("Unable to register platform device '%s': %d\n",
pdev->name, ret);
return ret;
}
keypad_plat_data的定义是
static struct keypad_data keypad_plat_data = {
.rowmax = 6,
.colmax = 8,
.learning = 0,
.delay = 2,
.matrix = keymapping, // 48个数据的矩阵键盘表
};
48个数据的矩阵键盘表定义为:
static u16 keymapping[48] = {
KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H,
KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P,
KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X,
KEY_Y, KEY_Z, KEY_ENTER, KEY_F24, KEY_F24, KEY_F24, KEY_F24, KEY_F24,
KEY_TAB, KEY_BACKSPACE, KEY_CAPSLOCK, KEY_COMMA, KEY_F1, KEY_SPACE, KEY_DOT, KEY_F20,
KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_F21, KEY_F10, KEY_F11, KEY_F9,
};
2. kernel_imx/drivers/input/keyboard/mxc_keyb.c文件中
初始探测函数
static int mxc_kpp_probe(struct platform_device *pdev)
{
int i, irq;
int retval;
unsigned int reg_val;
struct resource *res;
keypad = (struct keypad_data *)pdev->dev.platform_data; // 实际为keypad_plat_data数据结构
kpp_dev.kpp_cols = keypad->colmax; // 引入矩阵大小
kpp_dev.kpp_rows = keypad->rowmax;
key_pad_enabled = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
kpp_dev.base = ioremap(res->start, res->end - res->start + 1);
if (!kpp_dev.base)
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
keypad->irq = irq;
/* Enable keypad clock */
kpp_clk = clk_get(&pdev->dev, "kpp_clk");
clk_enable(kpp_clk);
/* IOMUX configuration for keypad */
gpio_keypad_active();
/* Configure keypad */
/* Enable number of rows in keypad (KPCR[7:0])
* Configure keypad columns as open-drain (KPCR[15:8])
*
* Configure the rows/cols in KPP
* LSB nibble in KPP is for 8 rows
* MSB nibble in KPP is for 8 cols
*/
reg_val = __raw_readw(kpp_dev.base + KPCR);
reg_val |= (1 << keypad->rowmax) - 1; /* LSB */
reg_val |= ((1 << keypad->colmax) - 1) << 8; /* MSB */
__raw_writew(reg_val, kpp_dev.base + KPCR);
/* Write 0's to KPDR[15:8] */
reg_val = __raw_readw(kpp_dev.base + KPDR);
reg_val &= 0x00ff;
__raw_writew(reg_val, kpp_dev.base + KPDR);
/* Configure columns as output, rows as input (KDDR[15:0]) */
reg_val = __raw_readw(kpp_dev.base + KDDR);
reg_val |= 0xff00;
reg_val &= 0xff00;
__raw_writew(reg_val, kpp_dev.base + KDDR);
reg_val = __raw_readw(kpp_dev.base + KPSR);
reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD);
reg_val |= KBD_STAT_KPKD;
reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
__raw_writew(reg_val, kpp_dev.base + KPSR);
reg_val |= KBD_STAT_KDIE;
reg_val &= ~KBD_STAT_KRIE;
__raw_writew(reg_val, kpp_dev.base + KPSR);
has_leaning_key = keypad->learning;
mxckpd_keycodes = keypad->matrix; // 引入矩阵数据
mxckpd_keycodes_size = keypad->rowmax * keypad->colmax;
if ((keypad->matrix == (void *)0)
|| (mxckpd_keycodes_size == 0)) {
retval = -ENODEV;
goto err1;
}
mxckbd_dev = input_allocate_device();
if (!mxckbd_dev) {
printk(KERN_ERR
"mxckbd_dev: not enough memory for input device\n");
retval = -ENOMEM;
goto err1;
}
mxckbd_dev->keycode = (void *)mxckpd_keycodes;
mxckbd_dev->keycodesize = sizeof(mxckpd_keycodes[0]);
mxckbd_dev->keycodemax = mxckpd_keycodes_size;
mxckbd_dev->name = "mxckpd";
mxckbd_dev->id.bustype = BUS_HOST;
mxckbd_dev->open = mxc_kpp_open;
mxckbd_dev->close = mxc_kpp_close;
retval = input_register_device(mxckbd_dev);
if (retval < 0) {
printk(KERN_ERR
"mxckbd_dev: failed to register input device\n");
goto err2;
}
/* allocate required memory */
press_scancode = kmalloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]),
GFP_KERNEL);
release_scancode =
kmalloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]), GFP_KERNEL);
if (!press_scancode || !release_scancode) {
retval = -ENOMEM;
goto err3;
}
for (i = 0; i < kpp_dev.kpp_rows; i++) {
press_scancode[i] = kmalloc(kpp_dev.kpp_cols
* sizeof(press_scancode[0][0]),
GFP_KERNEL);
release_scancode[i] =
kmalloc(kpp_dev.kpp_cols * sizeof(release_scancode[0][0]),
GFP_KERNEL);
if (!press_scancode[i] || !release_scancode[i]) {
retval = -ENOMEM;
goto err3;
}
}
cur_rcmap =
kmalloc(kpp_dev.kpp_rows * sizeof(cur_rcmap[0]), GFP_KERNEL);
prev_rcmap =
kmalloc(kpp_dev.kpp_rows * sizeof(prev_rcmap[0]), GFP_KERNEL);
if (!cur_rcmap || !prev_rcmap) {
retval = -ENOMEM;
goto err3;
}
__set_bit(EV_KEY, mxckbd_dev->evbit);
for (i = 0; i < mxckpd_keycodes_size; i++)
__set_bit(mxckpd_keycodes[i], mxckbd_dev->keybit);
for (i = 0; i < kpp_dev.kpp_rows; i++) {
memset(press_scancode[i], -1,
sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
memset(release_scancode[i], -1,
sizeof(release_scancode[0][0]) * kpp_dev.kpp_cols);
}
memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
memset(prev_rcmap, 0, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
key_pad_enabled = 1;
/* Initialize the polling timer */
init_timer(&kpp_dev.poll_timer);
/*
* Request for IRQ number for keypad port. The Interrupt handler
* function (mxc_kpp_interrupt) is called when ever interrupt occurs on
* keypad port.
*/
retval = request_irq(irq, mxc_kpp_interrupt, 0, MOD_NAME, MOD_NAME);
if (retval) {
pr_debug("KPP: request_irq(%d) returned error %d\n",
irq, retval);
goto err3;
}
/* By default, devices should wakeup if they can */
/* So keypad is set as "should wakeup" as it can */
device_init_wakeup(&pdev->dev, 1);
key_dev = &pdev->dev;
if (cpu_is_mx50())
register_early_suspend(&mxc_keypad_earlysuspend);
return 0;
err3:
mxc_kpp_free_allocated();
err2:
input_free_device(mxckbd_dev);
err1:
free_irq(irq, MOD_NAME);
clk_disable(kpp_clk);
clk_put(kpp_clk);
return retval;
}
定时扫描函数
static void mxc_kpp_handle_timer(unsigned long data)
{
unsigned short reg_val;
int i;
if (key_pad_enabled == 0) {
return;
}
if (mxc_kpp_scan_matrix() == 0) {
/*
* Stop scanning and wait for interrupt.
* Enable press interrupt and disable release interrupt.
*/
__raw_writew(0x00FF, kpp_dev.base + KPDR);
reg_val = __raw_readw(kpp_dev.base + KPSR);
reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD);
reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
__raw_writew(reg_val, kpp_dev.base + KPSR);
reg_val |= KBD_STAT_KDIE;
reg_val &= ~KBD_STAT_KRIE;
__raw_writew(reg_val, kpp_dev.base + KPSR);
/*
* No more keys pressed... make sure unwanted key codes are
* not given upstairs
*/
for (i = 0; i < kpp_dev.kpp_rows; i++) {
memset(press_scancode[i], -1,
sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
memset(release_scancode[i], -1,
sizeof(release_scancode[0][0]) *
kpp_dev.kpp_cols);
}
return;
}
/*
* There are still some keys pressed, continue to scan.
* We shall scan again in 10 ms. This has to be tuned according
* to the requirement.
*/
kpp_dev.poll_timer.expires = jiffies + KScanRate;
kpp_dev.poll_timer.function = mxc_kpp_handle_timer;
add_timer(&kpp_dev.poll_timer);
}
扫描函数
static int mxc_kpp_scan_matrix(void)
{
unsigned short reg_val;
int col, row;
short scancode = 0;
int keycnt = 0; /* How many keys are still pressed */
/*
* wmb() linux kernel function which guarantees orderings in write
* operations
*/
wmb();
/* save cur keypad matrix to prev */
memcpy(prev_rcmap, cur_rcmap, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
for (col = 0; col < kpp_dev.kpp_cols; col++) { /* Col */
/* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
reg_val = __raw_readw(kpp_dev.base + KPDR);
reg_val |= 0xff00;
__raw_writew(reg_val, kpp_dev.base + KPDR);
/*
* 3. Configure columns as totem pole outputs(for quick
* discharging of keypad capacitance)
*/
reg_val = __raw_readw(kpp_dev.base + KPCR);
reg_val &= 0x00ff;
__raw_writew(reg_val, kpp_dev.base + KPCR);
udelay(2);
/*
* 4. Configure columns as open-drain
*/
reg_val = __raw_readw(kpp_dev.base + KPCR);
reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
__raw_writew(reg_val, kpp_dev.base + KPCR);
/*
* 5. Write a single column to 0, others to 1.
* 6. Sample row inputs and save data. Multiple key presses
* can be detected on a single column.
* 7. Repeat steps 2 - 6 for remaining columns.
*/
/* Col bit starts at 8th bit in KPDR */
reg_val = __raw_readw(kpp_dev.base + KPDR);
reg_val &= ~(1 << (8 + col));
__raw_writew(reg_val, kpp_dev.base + KPDR);
/* Delay added to avoid propagating the 0 from column to row
* when scanning. */
udelay(5);
/* Read row input */
reg_val = __raw_readw(kpp_dev.base + KPDR);
for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */
if (TEST_BIT(reg_val, row) == 0) {
cur_rcmap[row] = BITSET(cur_rcmap[row], col);
keycnt++;
}
}
}
/*
* 8. Return all columns to 0 in preparation for standby mode.
* 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
* set the KPKR synchronizer chain by writing "1" to KRSS register,
* clear the KPKD synchronizer chain by writing "1" to KDSC register
*/
reg_val = 0x00;
__raw_writew(reg_val, kpp_dev.base + KPDR);
reg_val = __raw_readw(kpp_dev.base + KPDR);
reg_val = __raw_readw(kpp_dev.base + KPSR);
reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS |
KBD_STAT_KDSC;
__raw_writew(reg_val, kpp_dev.base + KPSR);
/* Check key press status change */
/*
* prev_rcmap array will contain the previous status of the keypad
* matrix. cur_rcmap array will contains the present status of the
* keypad matrix. If a bit is set in the array, that (row, col) bit is
* pressed, else it is not pressed.
*
* XORing these two variables will give us the change in bit for
* particular row and column. If a bit is set in XOR output, then that
* (row, col) has a change of status from the previous state. From
* the diff variable the key press and key release of row and column
* are found out.
*
* If the key press is determined then scancode for key pressed
* can be generated using the following statement:
* scancode = ((row * 8) + col);
*
* If the key release is determined then scancode for key release
* can be generated using the following statement:
* scancode = ((row * 8) + col) + MXC_KEYRELEASE;
*/
for (row = 0; row < kpp_dev.kpp_rows; row++) {
unsigned char diff;
/*
* Calculate the change in the keypad row status
*/
diff = prev_rcmap[row] ^ cur_rcmap[row];
for (col = 0; col < kpp_dev.kpp_cols; col++) {
if ((diff >> col) & 0x1) {
/* There is a status change on col */
if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
/*
* Previous state is 0, so now
* a key is pressed
*/
if (has_leaning_key) {
scancode =
mxc_scan_matrix_leaning_key
(row, col, 1);
} else {
scancode =
((row * kpp_dev.kpp_cols) +
col);
KPress = 1;
kpp_dev.iKeyState = KStateUp;
}
pr_debug("Press (%d, %d) scan=%d "
"Kpress=%d\n",
row, col, scancode, KPress);
press_scancode[row][col] =
(short)scancode;
} else {
/*
* Previous state is not 0, so
* now a key is released
*/
if (has_leaning_key) {
scancode =
mxc_scan_matrix_leaning_key
(row, col, 0);
} else {
scancode =
(row * kpp_dev.kpp_cols) +
col + MXC_KEYRELEASE;
KPress = 0;
kpp_dev.iKeyState = KStateDown;
}
pr_debug
("Release (%d, %d) scan=%d Kpress=%d\n",
row, col, scancode, KPress);
release_scancode[row][col] =
(short)scancode;
keycnt++;
}
}
}
}
/*
* This switch case statement is the
* implementation of state machine of debounce
* logic for key press/release.
* The explaination of state machine is as
* follows:
*
* KStateUp State:
* This is in intial state of the state machine
* this state it checks for any key presses.
* The key press can be checked using the
* variable KPress. If KPress is set, then key
* press is identified and switches the to
* KStateFirstDown state for key press to
* debounce.
*
* KStateFirstDown:
* After debounce delay(10ms), if the KPress is
* still set then pass scancode generated to
* input device and change the state to
* KStateDown, else key press debounce is not
* satisfied so change the state to KStateUp.
*
* KStateDown:
* In this state it checks for any key release.
* If KPress variable is cleared, then key
* release is indicated and so, switch the
* state to KStateFirstUp else to state
* KStateDown.
*
* KStateFirstUp:
* After debounce delay(10ms), if the KPress is
* still reset then pass the key release
* scancode to input device and change
* the state to KStateUp else key release is
* not satisfied so change the state to
* KStateDown.
*/
switch (kpp_dev.iKeyState) {
case KStateUp:
if (KPress) {
/* First Down (must debounce). */
kpp_dev.iKeyState = KStateFirstDown;
} else {
/* Still UP.(NO Changes) */
kpp_dev.iKeyState = KStateUp;
}
break;
case KStateFirstDown:
if (KPress) {
for (row = 0; row < kpp_dev.kpp_rows; row++) {
for (col = 0; col < kpp_dev.kpp_cols; col++) {
if ((press_scancode[row][col] != -1)) {
/* Still Down, so add scancode */
scancode =
press_scancode[row][col];
input_event(mxckbd_dev, EV_KEY,
mxckpd_keycodes
[scancode], 1);
if (mxckpd_keycodes[scancode] ==
KEY_LEFTSHIFT) {
input_event(mxckbd_dev,
EV_KEY,
KEY_3, 1);
}
kpp_dev.iKeyState = KStateDown;
press_scancode[row][col] = -1;
}
}
}
} else {
/* Just a bounce */
kpp_dev.iKeyState = KStateUp;
}
break;
case KStateDown:
if (KPress) {
/* Still down (no change) */
kpp_dev.iKeyState = KStateDown;
} else {
/* First Up. Must debounce */
kpp_dev.iKeyState = KStateFirstUp;
}
break;
case KStateFirstUp:
if (KPress) {
/* Just a bounce */
kpp_dev.iKeyState = KStateDown;
} else {
for (row = 0; row < kpp_dev.kpp_rows; row++) {
for (col = 0; col < kpp_dev.kpp_cols; col++) {
if ((release_scancode[row][col] != -1)) {
scancode =
release_scancode[row][col];
scancode =
scancode - MXC_KEYRELEASE;
input_event(mxckbd_dev, EV_KEY,
mxckpd_keycodes
[scancode], 0);
if (mxckpd_keycodes[scancode] ==
KEY_LEFTSHIFT) {
input_event(mxckbd_dev,
EV_KEY,
KEY_3, 0);
}
kpp_dev.iKeyState = KStateUp;
release_scancode[row][col] = -1;
}
}
}
}
break;
default:
return -EBADRQC;
break;
}
return keycnt;
}
键值处理函数
static signed short mxc_scan_matrix_leaning_key(int row, int col, int press)
{
static unsigned first_row;
static unsigned first_set, flag;
signed short scancode = -1;
if (press) {
if ((3 == col) && ((3 == row) ||
(4 == row) || (5 == row) || (6 == row))) {
if (first_set == 0) {
first_set = 1;
first_row = row;
} else {
first_set = 0;
if (((first_row == 6) || (first_row == 3))
&& ((row == 6) || (row == 3)))
scancode = press_down_code;
else if (((first_row == 3) || (first_row == 5))
&& ((row == 3) || (row == 5)))
scancode = press_left_code;
else if (((first_row == 6) || (first_row == 4))
&& ((row == 6) || (row == 4)))
scancode = press_right_code;
else if (((first_row == 4) || (first_row == 5))
&& ((row == 4) || (row == 5)))
scancode = press_up_code;
KPress = 1;
kpp_dev.iKeyState = KStateUp;
pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
row, col, scancode, KPress);
}
} else {
/*
* check for other keys only
* if the cursor key presses
* are not detected may be
* this needs better logic
*/
if ((0 == (cur_rcmap[3] & BITSET(0, 3))) &&
(0 == (cur_rcmap[4] & BITSET(0, 3))) &&
(0 == (cur_rcmap[5] & BITSET(0, 3))) &&
(0 == (cur_rcmap[6] & BITSET(0, 3)))) {
scancode = ((col * kpp_dev.kpp_rows) + row);
KPress = 1;
kpp_dev.iKeyState = KStateUp;
flag = 1;
pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
row, col, scancode, KPress);
}
}
} else {
if ((flag == 0) && (3 == col)
&& ((3 == row) || (4 == row) || (5 == row)
|| (6 == row))) {
if (first_set == 0) {
first_set = 1;
first_row = row;
} else {
first_set = 0;
if (((first_row == 6) || (first_row == 3))
&& ((row == 6) || (row == 3)))
scancode = rel_down_code;
else if (((first_row == 3) || (first_row == 5))
&& ((row == 3) || (row == 5)))
scancode = rel_left_code;
else if (((first_row == 6) || (first_row == 4))
&& ((row == 6) || (row == 4)))
scancode = rel_right_code;
else if (((first_row == 4) || (first_row == 5))
&& ((row == 4) || (row == 5)))
scancode = rel_up_code;
KPress = 0;
kpp_dev.iKeyState = KStateDown;
pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
row, col, scancode, KPress);
}
} else {
/*
* check for other keys only
* if the cursor key presses
* are not detected may be
* this needs better logic
*/
if ((0 == (prev_rcmap[3] & BITSET(0, 3))) &&
(0 == (prev_rcmap[4] & BITSET(0, 3))) &&
(0 == (cur_rcmap[5] & BITSET(0, 3))) &&
(0 == (cur_rcmap[6] & BITSET(0, 3)))) {
scancode = ((col * kpp_dev.kpp_rows) + row) +
MXC_KEYRELEASE;
KPress = 0;
flag = 0;
kpp_dev.iKeyState = KStateDown;
pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
row, col, scancode, KPress);
}
}
}
return scancode;
}