192c27c51SBernie Thompson /* 292c27c51SBernie Thompson * Manage Keyboard Matrices 392c27c51SBernie Thompson * 492c27c51SBernie Thompson * Copyright (c) 2012 The Chromium OS Authors. 592c27c51SBernie Thompson * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de 692c27c51SBernie Thompson * 792c27c51SBernie Thompson * See file CREDITS for list of people who contributed to this 892c27c51SBernie Thompson * project. 992c27c51SBernie Thompson * 1092c27c51SBernie Thompson * This program is free software; you can redistribute it and/or 1192c27c51SBernie Thompson * modify it under the terms of the GNU General Public License as 1292c27c51SBernie Thompson * published by the Free Software Foundation; either version 2 of 1392c27c51SBernie Thompson * the License, or (at your option) any later version. 1492c27c51SBernie Thompson * 1592c27c51SBernie Thompson * This program is distributed in the hope that it will be useful, 1692c27c51SBernie Thompson * but WITHOUT ANY WARRANTY; without even the implied warranty of 1792c27c51SBernie Thompson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1892c27c51SBernie Thompson * GNU General Public License for more details. 1992c27c51SBernie Thompson * 2092c27c51SBernie Thompson * You should have received a copy of the GNU General Public License 2192c27c51SBernie Thompson * along with this program; if not, write to the Free Software 2292c27c51SBernie Thompson * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2392c27c51SBernie Thompson * MA 02111-1307 USA 2492c27c51SBernie Thompson */ 2592c27c51SBernie Thompson 26*c20dbf64SStephan Linz #include <common.h> 2792c27c51SBernie Thompson #include <fdtdec.h> 2892c27c51SBernie Thompson #include <key_matrix.h> 2992c27c51SBernie Thompson #include <malloc.h> 3092c27c51SBernie Thompson #include <linux/input.h> 3192c27c51SBernie Thompson 3292c27c51SBernie Thompson /** 3392c27c51SBernie Thompson * Determine if the current keypress configuration can cause key ghosting 3492c27c51SBernie Thompson * 3592c27c51SBernie Thompson * We figure this out by seeing if we have two or more keys in the same 3692c27c51SBernie Thompson * column, as well as two or more keys in the same row. 3792c27c51SBernie Thompson * 3892c27c51SBernie Thompson * @param config Keyboard matrix config 3992c27c51SBernie Thompson * @param keys List of keys to check 4092c27c51SBernie Thompson * @param valid Number of valid keypresses to check 4192c27c51SBernie Thompson * @return 0 if no ghosting is possible, 1 if it is 4292c27c51SBernie Thompson */ 4392c27c51SBernie Thompson static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys, 4492c27c51SBernie Thompson int valid) 4592c27c51SBernie Thompson { 4692c27c51SBernie Thompson int key_in_same_col = 0, key_in_same_row = 0; 4792c27c51SBernie Thompson int i, j; 4892c27c51SBernie Thompson 4992c27c51SBernie Thompson for (i = 0; i < valid; i++) { 5092c27c51SBernie Thompson /* 5192c27c51SBernie Thompson * Find 2 keys such that one key is in the same row 5292c27c51SBernie Thompson * and the other is in the same column as the i-th key. 5392c27c51SBernie Thompson */ 5492c27c51SBernie Thompson for (j = i + 1; j < valid; j++) { 5592c27c51SBernie Thompson if (keys[j].col == keys[i].col) 5692c27c51SBernie Thompson key_in_same_col = 1; 5792c27c51SBernie Thompson if (keys[j].row == keys[i].row) 5892c27c51SBernie Thompson key_in_same_row = 1; 5992c27c51SBernie Thompson } 6092c27c51SBernie Thompson } 6192c27c51SBernie Thompson 6292c27c51SBernie Thompson if (key_in_same_col && key_in_same_row) 6392c27c51SBernie Thompson return 1; 6492c27c51SBernie Thompson else 6592c27c51SBernie Thompson return 0; 6692c27c51SBernie Thompson } 6792c27c51SBernie Thompson 6892c27c51SBernie Thompson int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[], 6992c27c51SBernie Thompson int num_keys, int keycode[], int max_keycodes) 7092c27c51SBernie Thompson { 7192c27c51SBernie Thompson const u8 *keymap; 7292c27c51SBernie Thompson int valid, upto; 7392c27c51SBernie Thompson int pos; 7492c27c51SBernie Thompson 7592c27c51SBernie Thompson debug("%s: num_keys = %d\n", __func__, num_keys); 7692c27c51SBernie Thompson keymap = config->plain_keycode; 7792c27c51SBernie Thompson for (valid = upto = 0; upto < num_keys; upto++) { 7892c27c51SBernie Thompson struct key_matrix_key *key = &keys[upto]; 7992c27c51SBernie Thompson 8092c27c51SBernie Thompson debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row, 8192c27c51SBernie Thompson key->col); 8292c27c51SBernie Thompson if (!key->valid) 8392c27c51SBernie Thompson continue; 8492c27c51SBernie Thompson pos = key->row * config->num_cols + key->col; 8592c27c51SBernie Thompson if (config->fn_keycode && pos == config->fn_pos) 8692c27c51SBernie Thompson keymap = config->fn_keycode; 8792c27c51SBernie Thompson 8892c27c51SBernie Thompson /* Convert the (row, col) values into a keycode */ 8992c27c51SBernie Thompson if (valid < max_keycodes) 9092c27c51SBernie Thompson keycode[valid++] = keymap[pos]; 9192c27c51SBernie Thompson debug(" keycode=%d\n", keymap[pos]); 9292c27c51SBernie Thompson } 9392c27c51SBernie Thompson 9492c27c51SBernie Thompson /* For a ghost key config, ignore the keypresses for this iteration. */ 9592c27c51SBernie Thompson if (valid >= 3 && has_ghosting(config, keys, valid)) { 9692c27c51SBernie Thompson valid = 0; 9792c27c51SBernie Thompson debug(" ghosting detected!\n"); 9892c27c51SBernie Thompson } 9992c27c51SBernie Thompson debug(" %d valid keycodes found\n", valid); 10092c27c51SBernie Thompson 10192c27c51SBernie Thompson return valid; 10292c27c51SBernie Thompson } 10392c27c51SBernie Thompson 10492c27c51SBernie Thompson /** 10592c27c51SBernie Thompson * Create a new keycode map from some provided data 10692c27c51SBernie Thompson * 10792c27c51SBernie Thompson * This decodes a keycode map in the format used by the fdt, which is one 10892c27c51SBernie Thompson * word per entry, with the row, col and keycode encoded in that word. 10992c27c51SBernie Thompson * 11092c27c51SBernie Thompson * We create a (row x col) size byte array with each entry containing the 11192c27c51SBernie Thompson * keycode for that (row, col). We also search for map_keycode and return 11292c27c51SBernie Thompson * its position if found (this is used for finding the Fn key). 11392c27c51SBernie Thompson * 11492c27c51SBernie Thompson * @param config Key matrix dimensions structure 11592c27c51SBernie Thompson * @param data Keycode data 11692c27c51SBernie Thompson * @param len Number of entries in keycode table 11792c27c51SBernie Thompson * @param map_keycode Key code to find in the map 11892c27c51SBernie Thompson * @param pos Returns position of map_keycode, if found, else -1 11992c27c51SBernie Thompson * @return map Pointer to allocated map 12092c27c51SBernie Thompson */ 12192c27c51SBernie Thompson static uchar *create_keymap(struct key_matrix *config, u32 *data, int len, 12292c27c51SBernie Thompson int map_keycode, int *pos) 12392c27c51SBernie Thompson { 12492c27c51SBernie Thompson uchar *map; 12592c27c51SBernie Thompson 12692c27c51SBernie Thompson if (pos) 12792c27c51SBernie Thompson *pos = -1; 12892c27c51SBernie Thompson map = (uchar *)calloc(1, config->key_count); 12992c27c51SBernie Thompson if (!map) { 13092c27c51SBernie Thompson debug("%s: failed to malloc %d bytes\n", __func__, 13192c27c51SBernie Thompson config->key_count); 13292c27c51SBernie Thompson return NULL; 13392c27c51SBernie Thompson } 13492c27c51SBernie Thompson 13592c27c51SBernie Thompson for (; len >= sizeof(u32); data++, len -= 4) { 13692c27c51SBernie Thompson u32 tmp = fdt32_to_cpu(*data); 13792c27c51SBernie Thompson int key_code, row, col; 13892c27c51SBernie Thompson int entry; 13992c27c51SBernie Thompson 14092c27c51SBernie Thompson row = (tmp >> 24) & 0xff; 14192c27c51SBernie Thompson col = (tmp >> 16) & 0xff; 14292c27c51SBernie Thompson key_code = tmp & 0xffff; 14392c27c51SBernie Thompson entry = row * config->num_cols + col; 14492c27c51SBernie Thompson map[entry] = key_code; 14592c27c51SBernie Thompson if (pos && map_keycode == key_code) 14692c27c51SBernie Thompson *pos = entry; 14792c27c51SBernie Thompson } 14892c27c51SBernie Thompson 14992c27c51SBernie Thompson return map; 15092c27c51SBernie Thompson } 15192c27c51SBernie Thompson 15292c27c51SBernie Thompson int key_matrix_decode_fdt(struct key_matrix *config, const void *blob, 15392c27c51SBernie Thompson int node) 15492c27c51SBernie Thompson { 15592c27c51SBernie Thompson const struct fdt_property *prop; 15692c27c51SBernie Thompson int offset; 15792c27c51SBernie Thompson 15892c27c51SBernie Thompson /* Check each property name for ones that we understand */ 15992c27c51SBernie Thompson for (offset = fdt_first_property_offset(blob, node); 16092c27c51SBernie Thompson offset > 0; 16192c27c51SBernie Thompson offset = fdt_next_property_offset(blob, offset)) { 16292c27c51SBernie Thompson const char *name; 16392c27c51SBernie Thompson int len; 16492c27c51SBernie Thompson 16592c27c51SBernie Thompson prop = fdt_get_property_by_offset(blob, offset, NULL); 16692c27c51SBernie Thompson name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); 16792c27c51SBernie Thompson len = strlen(name); 16892c27c51SBernie Thompson 16992c27c51SBernie Thompson /* Name needs to match "1,<type>keymap" */ 17092c27c51SBernie Thompson debug("%s: property '%s'\n", __func__, name); 17192c27c51SBernie Thompson if (strncmp(name, "1,", 2) || len < 8 || 17292c27c51SBernie Thompson strcmp(name + len - 6, "keymap")) 17392c27c51SBernie Thompson continue; 17492c27c51SBernie Thompson 17592c27c51SBernie Thompson len -= 8; 17692c27c51SBernie Thompson if (len == 0) { 17792c27c51SBernie Thompson config->plain_keycode = create_keymap(config, 17892c27c51SBernie Thompson (u32 *)prop->data, fdt32_to_cpu(prop->len), 17992c27c51SBernie Thompson KEY_FN, &config->fn_pos); 18092c27c51SBernie Thompson } else if (0 == strncmp(name + 2, "fn-", len)) { 18192c27c51SBernie Thompson config->fn_keycode = create_keymap(config, 18292c27c51SBernie Thompson (u32 *)prop->data, fdt32_to_cpu(prop->len), 18392c27c51SBernie Thompson -1, NULL); 18492c27c51SBernie Thompson } else { 18592c27c51SBernie Thompson debug("%s: unrecognised property '%s'\n", __func__, 18692c27c51SBernie Thompson name); 18792c27c51SBernie Thompson } 18892c27c51SBernie Thompson } 18992c27c51SBernie Thompson debug("%s: Decoded key maps %p, %p from fdt\n", __func__, 19092c27c51SBernie Thompson config->plain_keycode, config->fn_keycode); 19192c27c51SBernie Thompson 19292c27c51SBernie Thompson if (!config->plain_keycode) { 19392c27c51SBernie Thompson debug("%s: cannot find keycode-plain map\n", __func__); 19492c27c51SBernie Thompson return -1; 19592c27c51SBernie Thompson } 19692c27c51SBernie Thompson 19792c27c51SBernie Thompson return 0; 19892c27c51SBernie Thompson } 19992c27c51SBernie Thompson 20092c27c51SBernie Thompson int key_matrix_init(struct key_matrix *config, int rows, int cols) 20192c27c51SBernie Thompson { 20292c27c51SBernie Thompson memset(config, '\0', sizeof(*config)); 20392c27c51SBernie Thompson config->num_rows = rows; 20492c27c51SBernie Thompson config->num_cols = cols; 20592c27c51SBernie Thompson config->key_count = rows * cols; 20692c27c51SBernie Thompson assert(config->key_count > 0); 20792c27c51SBernie Thompson 20892c27c51SBernie Thompson return 0; 20992c27c51SBernie Thompson } 210