18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ION iCade input driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Bastien Nocera <hadess@hadess.net> 68c2ecf20Sopenharmony_ci * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/hid.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "hid-ids.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * ↑ A C Y L 208c2ecf20Sopenharmony_ci * ← → 218c2ecf20Sopenharmony_ci * ↓ B X Z R 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * UP ON,OFF = w,e 258c2ecf20Sopenharmony_ci * RT ON,OFF = d,c 268c2ecf20Sopenharmony_ci * DN ON,OFF = x,z 278c2ecf20Sopenharmony_ci * LT ON,OFF = a,q 288c2ecf20Sopenharmony_ci * A ON,OFF = y,t 298c2ecf20Sopenharmony_ci * B ON,OFF = h,r 308c2ecf20Sopenharmony_ci * C ON,OFF = u,f 318c2ecf20Sopenharmony_ci * X ON,OFF = j,n 328c2ecf20Sopenharmony_ci * Y ON,OFF = i,m 338c2ecf20Sopenharmony_ci * Z ON,OFF = k,p 348c2ecf20Sopenharmony_ci * L ON,OFF = o,g 358c2ecf20Sopenharmony_ci * R ON,OFF = l,v 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* The translation code uses HID usage instead of input layer 398c2ecf20Sopenharmony_ci * keys. This code generates a lookup table that makes 408c2ecf20Sopenharmony_ci * translation quick. 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * #include <linux/input.h> 438c2ecf20Sopenharmony_ci * #include <stdio.h> 448c2ecf20Sopenharmony_ci * #include <assert.h> 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * #define unk KEY_UNKNOWN 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * < copy of hid_keyboard[] from hid-input.c > 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * struct icade_key_translation { 518c2ecf20Sopenharmony_ci * int from; 528c2ecf20Sopenharmony_ci * const char *to; 538c2ecf20Sopenharmony_ci * int press; 548c2ecf20Sopenharmony_ci * }; 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * static const struct icade_key_translation icade_keys[] = { 578c2ecf20Sopenharmony_ci * { KEY_W, "KEY_UP", 1 }, 588c2ecf20Sopenharmony_ci * { KEY_E, "KEY_UP", 0 }, 598c2ecf20Sopenharmony_ci * { KEY_D, "KEY_RIGHT", 1 }, 608c2ecf20Sopenharmony_ci * { KEY_C, "KEY_RIGHT", 0 }, 618c2ecf20Sopenharmony_ci * { KEY_X, "KEY_DOWN", 1 }, 628c2ecf20Sopenharmony_ci * { KEY_Z, "KEY_DOWN", 0 }, 638c2ecf20Sopenharmony_ci * { KEY_A, "KEY_LEFT", 1 }, 648c2ecf20Sopenharmony_ci * { KEY_Q, "KEY_LEFT", 0 }, 658c2ecf20Sopenharmony_ci * { KEY_Y, "BTN_A", 1 }, 668c2ecf20Sopenharmony_ci * { KEY_T, "BTN_A", 0 }, 678c2ecf20Sopenharmony_ci * { KEY_H, "BTN_B", 1 }, 688c2ecf20Sopenharmony_ci * { KEY_R, "BTN_B", 0 }, 698c2ecf20Sopenharmony_ci * { KEY_U, "BTN_C", 1 }, 708c2ecf20Sopenharmony_ci * { KEY_F, "BTN_C", 0 }, 718c2ecf20Sopenharmony_ci * { KEY_J, "BTN_X", 1 }, 728c2ecf20Sopenharmony_ci * { KEY_N, "BTN_X", 0 }, 738c2ecf20Sopenharmony_ci * { KEY_I, "BTN_Y", 1 }, 748c2ecf20Sopenharmony_ci * { KEY_M, "BTN_Y", 0 }, 758c2ecf20Sopenharmony_ci * { KEY_K, "BTN_Z", 1 }, 768c2ecf20Sopenharmony_ci * { KEY_P, "BTN_Z", 0 }, 778c2ecf20Sopenharmony_ci * { KEY_O, "BTN_THUMBL", 1 }, 788c2ecf20Sopenharmony_ci * { KEY_G, "BTN_THUMBL", 0 }, 798c2ecf20Sopenharmony_ci * { KEY_L, "BTN_THUMBR", 1 }, 808c2ecf20Sopenharmony_ci * { KEY_V, "BTN_THUMBR", 0 }, 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * { } 838c2ecf20Sopenharmony_ci * }; 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * static int 868c2ecf20Sopenharmony_ci * usage_for_key (int key) 878c2ecf20Sopenharmony_ci * { 888c2ecf20Sopenharmony_ci * int i; 898c2ecf20Sopenharmony_ci * for (i = 0; i < 256; i++) { 908c2ecf20Sopenharmony_ci * if (hid_keyboard[i] == key) 918c2ecf20Sopenharmony_ci * return i; 928c2ecf20Sopenharmony_ci * } 938c2ecf20Sopenharmony_ci * assert(0); 948c2ecf20Sopenharmony_ci * } 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * int main (int argc, char **argv) 978c2ecf20Sopenharmony_ci * { 988c2ecf20Sopenharmony_ci * const struct icade_key_translation *trans; 998c2ecf20Sopenharmony_ci * int max_usage = 0; 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * for (trans = icade_keys; trans->from; trans++) { 1028c2ecf20Sopenharmony_ci * int usage = usage_for_key (trans->from); 1038c2ecf20Sopenharmony_ci * max_usage = usage > max_usage ? usage : max_usage; 1048c2ecf20Sopenharmony_ci * } 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * printf ("#define ICADE_MAX_USAGE %d\n\n", max_usage); 1078c2ecf20Sopenharmony_ci * printf ("struct icade_key {\n"); 1088c2ecf20Sopenharmony_ci * printf ("\tu16 to;\n"); 1098c2ecf20Sopenharmony_ci * printf ("\tu8 press:1;\n"); 1108c2ecf20Sopenharmony_ci * printf ("};\n\n"); 1118c2ecf20Sopenharmony_ci * printf ("static const struct icade_key " 1128c2ecf20Sopenharmony_ci * "icade_usage_table[%d] = {\n", max_usage + 1); 1138c2ecf20Sopenharmony_ci * for (trans = icade_keys; trans->from; trans++) { 1148c2ecf20Sopenharmony_ci * printf ("\t[%d] = { %s, %d },\n", 1158c2ecf20Sopenharmony_ci * usage_for_key (trans->from), trans->to, trans->press); 1168c2ecf20Sopenharmony_ci * } 1178c2ecf20Sopenharmony_ci * printf ("};\n"); 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * return 0; 1208c2ecf20Sopenharmony_ci * } 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define ICADE_MAX_USAGE 29 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct icade_key { 1268c2ecf20Sopenharmony_ci u16 to; 1278c2ecf20Sopenharmony_ci u8 press:1; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct icade_key icade_usage_table[30] = { 1318c2ecf20Sopenharmony_ci [26] = { KEY_UP, 1 }, 1328c2ecf20Sopenharmony_ci [8] = { KEY_UP, 0 }, 1338c2ecf20Sopenharmony_ci [7] = { KEY_RIGHT, 1 }, 1348c2ecf20Sopenharmony_ci [6] = { KEY_RIGHT, 0 }, 1358c2ecf20Sopenharmony_ci [27] = { KEY_DOWN, 1 }, 1368c2ecf20Sopenharmony_ci [29] = { KEY_DOWN, 0 }, 1378c2ecf20Sopenharmony_ci [4] = { KEY_LEFT, 1 }, 1388c2ecf20Sopenharmony_ci [20] = { KEY_LEFT, 0 }, 1398c2ecf20Sopenharmony_ci [28] = { BTN_A, 1 }, 1408c2ecf20Sopenharmony_ci [23] = { BTN_A, 0 }, 1418c2ecf20Sopenharmony_ci [11] = { BTN_B, 1 }, 1428c2ecf20Sopenharmony_ci [21] = { BTN_B, 0 }, 1438c2ecf20Sopenharmony_ci [24] = { BTN_C, 1 }, 1448c2ecf20Sopenharmony_ci [9] = { BTN_C, 0 }, 1458c2ecf20Sopenharmony_ci [13] = { BTN_X, 1 }, 1468c2ecf20Sopenharmony_ci [17] = { BTN_X, 0 }, 1478c2ecf20Sopenharmony_ci [12] = { BTN_Y, 1 }, 1488c2ecf20Sopenharmony_ci [16] = { BTN_Y, 0 }, 1498c2ecf20Sopenharmony_ci [14] = { BTN_Z, 1 }, 1508c2ecf20Sopenharmony_ci [19] = { BTN_Z, 0 }, 1518c2ecf20Sopenharmony_ci [18] = { BTN_THUMBL, 1 }, 1528c2ecf20Sopenharmony_ci [10] = { BTN_THUMBL, 0 }, 1538c2ecf20Sopenharmony_ci [15] = { BTN_THUMBR, 1 }, 1548c2ecf20Sopenharmony_ci [25] = { BTN_THUMBR, 0 }, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic const struct icade_key *icade_find_translation(u16 from) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci if (from > ICADE_MAX_USAGE) 1608c2ecf20Sopenharmony_ci return NULL; 1618c2ecf20Sopenharmony_ci return &icade_usage_table[from]; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int icade_event(struct hid_device *hdev, struct hid_field *field, 1658c2ecf20Sopenharmony_ci struct hid_usage *usage, __s32 value) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci const struct icade_key *trans; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || 1708c2ecf20Sopenharmony_ci !usage->type) 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* We ignore the fake key up, and act only on key down */ 1748c2ecf20Sopenharmony_ci if (!value) 1758c2ecf20Sopenharmony_ci return 1; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci trans = icade_find_translation(usage->hid & HID_USAGE); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!trans) 1808c2ecf20Sopenharmony_ci return 1; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci input_event(field->hidinput->input, usage->type, 1838c2ecf20Sopenharmony_ci trans->to, trans->press); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 1; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int icade_input_mapping(struct hid_device *hdev, struct hid_input *hi, 1898c2ecf20Sopenharmony_ci struct hid_field *field, struct hid_usage *usage, 1908c2ecf20Sopenharmony_ci unsigned long **bit, int *max) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci const struct icade_key *trans; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) { 1958c2ecf20Sopenharmony_ci trans = icade_find_translation(usage->hid & HID_USAGE); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!trans) 1988c2ecf20Sopenharmony_ci return -1; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci hid_map_usage(hi, usage, bit, max, EV_KEY, trans->to); 2018c2ecf20Sopenharmony_ci set_bit(trans->to, hi->input->keybit); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 1; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* ignore others */ 2078c2ecf20Sopenharmony_ci return -1; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int icade_input_mapped(struct hid_device *hdev, struct hid_input *hi, 2128c2ecf20Sopenharmony_ci struct hid_field *field, struct hid_usage *usage, 2138c2ecf20Sopenharmony_ci unsigned long **bit, int *max) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci if (usage->type == EV_KEY) 2168c2ecf20Sopenharmony_ci set_bit(usage->type, hi->input->evbit); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return -1; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic const struct hid_device_id icade_devices[] = { 2228c2ecf20Sopenharmony_ci { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci { } 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, icade_devices); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic struct hid_driver icade_driver = { 2298c2ecf20Sopenharmony_ci .name = "icade", 2308c2ecf20Sopenharmony_ci .id_table = icade_devices, 2318c2ecf20Sopenharmony_ci .event = icade_event, 2328c2ecf20Sopenharmony_ci .input_mapped = icade_input_mapped, 2338c2ecf20Sopenharmony_ci .input_mapping = icade_input_mapping, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_cimodule_hid_driver(icade_driver); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); 2398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ION iCade input driver"); 240