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