18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AT and PS/2 keyboard driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 1999-2002 Vojtech Pavlik 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci * This driver can handle standard AT keyboards and PS/2 keyboards in 118c2ecf20Sopenharmony_ci * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb 128c2ecf20Sopenharmony_ci * input-only controllers and AT keyboards connected over a one way RS232 138c2ecf20Sopenharmony_ci * converter. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/input.h> 228c2ecf20Sopenharmony_ci#include <linux/serio.h> 238c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 248c2ecf20Sopenharmony_ci#include <linux/libps2.h> 258c2ecf20Sopenharmony_ci#include <linux/mutex.h> 268c2ecf20Sopenharmony_ci#include <linux/dmi.h> 278c2ecf20Sopenharmony_ci#include <linux/property.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DRIVER_DESC "AT and PS/2 keyboard driver" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); 328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int atkbd_set = 2; 368c2ecf20Sopenharmony_cimodule_param_named(set, atkbd_set, int, 0); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__) 408c2ecf20Sopenharmony_cistatic bool atkbd_reset; 418c2ecf20Sopenharmony_ci#else 428c2ecf20Sopenharmony_cistatic bool atkbd_reset = true; 438c2ecf20Sopenharmony_ci#endif 448c2ecf20Sopenharmony_cimodule_param_named(reset, atkbd_reset, bool, 0); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset, "Reset keyboard during initialization"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic bool atkbd_softrepeat; 488c2ecf20Sopenharmony_cimodule_param_named(softrepeat, atkbd_softrepeat, bool, 0); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(softrepeat, "Use software keyboard repeat"); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic bool atkbd_softraw = true; 528c2ecf20Sopenharmony_cimodule_param_named(softraw, atkbd_softraw, bool, 0); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(softraw, "Use software generated rawmode"); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic bool atkbd_scroll; 568c2ecf20Sopenharmony_cimodule_param_named(scroll, atkbd_scroll, bool, 0); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool atkbd_extra; 608c2ecf20Sopenharmony_cimodule_param_named(extra, atkbd_extra, bool, 0); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards"); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic bool atkbd_terminal; 648c2ecf20Sopenharmony_cimodule_param_named(terminal, atkbd_terminal, bool, 0); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define MAX_FUNCTION_ROW_KEYS 24 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF) 708c2ecf20Sopenharmony_ci#define KEYCODE(keymap) (keymap & 0xFFFF) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Scancode to keycode tables. These are just the default setting, and 748c2ecf20Sopenharmony_ci * are loadable via a userland utility. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define ATKBD_KEYMAP_SIZE 512 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* XXX: need a more general approach */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#include "hpps2atkbd.h" /* include the keyboard scancodes */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#else 888c2ecf20Sopenharmony_ci 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, 898c2ecf20Sopenharmony_ci 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, 908c2ecf20Sopenharmony_ci 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, 918c2ecf20Sopenharmony_ci 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, 928c2ecf20Sopenharmony_ci 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, 938c2ecf20Sopenharmony_ci 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, 948c2ecf20Sopenharmony_ci 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, 958c2ecf20Sopenharmony_ci 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 988c2ecf20Sopenharmony_ci 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, 998c2ecf20Sopenharmony_ci 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, 1008c2ecf20Sopenharmony_ci 159, 0,115, 0,164, 0, 0,116,158, 0,172,166, 0, 0, 0,142, 1018c2ecf20Sopenharmony_ci 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, 1028c2ecf20Sopenharmony_ci 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, 1038c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, 1048c2ecf20Sopenharmony_ci 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 0, 0, 0, 65, 99, 1078c2ecf20Sopenharmony_ci#endif 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 1138c2ecf20Sopenharmony_ci 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, 1148c2ecf20Sopenharmony_ci 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64, 1158c2ecf20Sopenharmony_ci 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66, 1168c2ecf20Sopenharmony_ci 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68, 1178c2ecf20Sopenharmony_ci 113,114, 40, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 88, 70, 1188c2ecf20Sopenharmony_ci 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104, 1198c2ecf20Sopenharmony_ci 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183, 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0, 1228c2ecf20Sopenharmony_ci 0,139,172,163,165,115,152,172,166,140,160,154,113,114,167,168, 1238c2ecf20Sopenharmony_ci 148,149,147,140 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic const unsigned short atkbd_unxlate_table[128] = { 1278c2ecf20Sopenharmony_ci 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, 1288c2ecf20Sopenharmony_ci 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, 1298c2ecf20Sopenharmony_ci 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, 1308c2ecf20Sopenharmony_ci 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, 1318c2ecf20Sopenharmony_ci 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, 1328c2ecf20Sopenharmony_ci 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, 1338c2ecf20Sopenharmony_ci 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, 1348c2ecf20Sopenharmony_ci 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define ATKBD_CMD_SETLEDS 0x10ed 1388c2ecf20Sopenharmony_ci#define ATKBD_CMD_GSCANSET 0x11f0 1398c2ecf20Sopenharmony_ci#define ATKBD_CMD_SSCANSET 0x10f0 1408c2ecf20Sopenharmony_ci#define ATKBD_CMD_GETID 0x02f2 1418c2ecf20Sopenharmony_ci#define ATKBD_CMD_SETREP 0x10f3 1428c2ecf20Sopenharmony_ci#define ATKBD_CMD_ENABLE 0x00f4 1438c2ecf20Sopenharmony_ci#define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */ 1448c2ecf20Sopenharmony_ci#define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */ 1458c2ecf20Sopenharmony_ci#define ATKBD_CMD_SETALL_MB 0x00f8 /* Set all keys to give break codes */ 1468c2ecf20Sopenharmony_ci#define ATKBD_CMD_SETALL_MBR 0x00fa /* ... and repeat */ 1478c2ecf20Sopenharmony_ci#define ATKBD_CMD_RESET_BAT 0x02ff 1488c2ecf20Sopenharmony_ci#define ATKBD_CMD_RESEND 0x00fe 1498c2ecf20Sopenharmony_ci#define ATKBD_CMD_EX_ENABLE 0x10ea 1508c2ecf20Sopenharmony_ci#define ATKBD_CMD_EX_SETLEDS 0x20eb 1518c2ecf20Sopenharmony_ci#define ATKBD_CMD_OK_GETID 0x02e8 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#define ATKBD_RET_ACK 0xfa 1548c2ecf20Sopenharmony_ci#define ATKBD_RET_NAK 0xfe 1558c2ecf20Sopenharmony_ci#define ATKBD_RET_BAT 0xaa 1568c2ecf20Sopenharmony_ci#define ATKBD_RET_EMUL0 0xe0 1578c2ecf20Sopenharmony_ci#define ATKBD_RET_EMUL1 0xe1 1588c2ecf20Sopenharmony_ci#define ATKBD_RET_RELEASE 0xf0 1598c2ecf20Sopenharmony_ci#define ATKBD_RET_HANJA 0xf1 1608c2ecf20Sopenharmony_ci#define ATKBD_RET_HANGEUL 0xf2 1618c2ecf20Sopenharmony_ci#define ATKBD_RET_ERR 0xff 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#define ATKBD_KEY_UNKNOWN 0 1648c2ecf20Sopenharmony_ci#define ATKBD_KEY_NULL 255 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#define ATKBD_SCR_1 0xfffe 1678c2ecf20Sopenharmony_ci#define ATKBD_SCR_2 0xfffd 1688c2ecf20Sopenharmony_ci#define ATKBD_SCR_4 0xfffc 1698c2ecf20Sopenharmony_ci#define ATKBD_SCR_8 0xfffb 1708c2ecf20Sopenharmony_ci#define ATKBD_SCR_CLICK 0xfffa 1718c2ecf20Sopenharmony_ci#define ATKBD_SCR_LEFT 0xfff9 1728c2ecf20Sopenharmony_ci#define ATKBD_SCR_RIGHT 0xfff8 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define ATKBD_SPECIAL ATKBD_SCR_RIGHT 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#define ATKBD_LED_EVENT_BIT 0 1778c2ecf20Sopenharmony_ci#define ATKBD_REP_EVENT_BIT 1 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#define ATKBD_XL_ERR 0x01 1808c2ecf20Sopenharmony_ci#define ATKBD_XL_BAT 0x02 1818c2ecf20Sopenharmony_ci#define ATKBD_XL_ACK 0x04 1828c2ecf20Sopenharmony_ci#define ATKBD_XL_NAK 0x08 1838c2ecf20Sopenharmony_ci#define ATKBD_XL_HANGEUL 0x10 1848c2ecf20Sopenharmony_ci#define ATKBD_XL_HANJA 0x20 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic const struct { 1878c2ecf20Sopenharmony_ci unsigned short keycode; 1888c2ecf20Sopenharmony_ci unsigned char set2; 1898c2ecf20Sopenharmony_ci} atkbd_scroll_keys[] = { 1908c2ecf20Sopenharmony_ci { ATKBD_SCR_1, 0xc5 }, 1918c2ecf20Sopenharmony_ci { ATKBD_SCR_2, 0x9d }, 1928c2ecf20Sopenharmony_ci { ATKBD_SCR_4, 0xa4 }, 1938c2ecf20Sopenharmony_ci { ATKBD_SCR_8, 0x9b }, 1948c2ecf20Sopenharmony_ci { ATKBD_SCR_CLICK, 0xe0 }, 1958c2ecf20Sopenharmony_ci { ATKBD_SCR_LEFT, 0xcb }, 1968c2ecf20Sopenharmony_ci { ATKBD_SCR_RIGHT, 0xd2 }, 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * The atkbd control structure 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistruct atkbd { 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci struct ps2dev ps2dev; 2068c2ecf20Sopenharmony_ci struct input_dev *dev; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* Written only during init */ 2098c2ecf20Sopenharmony_ci char name[64]; 2108c2ecf20Sopenharmony_ci char phys[32]; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci unsigned short id; 2138c2ecf20Sopenharmony_ci unsigned short keycode[ATKBD_KEYMAP_SIZE]; 2148c2ecf20Sopenharmony_ci DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE); 2158c2ecf20Sopenharmony_ci unsigned char set; 2168c2ecf20Sopenharmony_ci bool translated; 2178c2ecf20Sopenharmony_ci bool extra; 2188c2ecf20Sopenharmony_ci bool write; 2198c2ecf20Sopenharmony_ci bool softrepeat; 2208c2ecf20Sopenharmony_ci bool softraw; 2218c2ecf20Sopenharmony_ci bool scroll; 2228c2ecf20Sopenharmony_ci bool enabled; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Accessed only from interrupt */ 2258c2ecf20Sopenharmony_ci unsigned char emul; 2268c2ecf20Sopenharmony_ci bool resend; 2278c2ecf20Sopenharmony_ci bool release; 2288c2ecf20Sopenharmony_ci unsigned long xl_bit; 2298c2ecf20Sopenharmony_ci unsigned int last; 2308c2ecf20Sopenharmony_ci unsigned long time; 2318c2ecf20Sopenharmony_ci unsigned long err_count; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci struct delayed_work event_work; 2348c2ecf20Sopenharmony_ci unsigned long event_jiffies; 2358c2ecf20Sopenharmony_ci unsigned long event_mask; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Serializes reconnect(), attr->set() and event work */ 2388c2ecf20Sopenharmony_ci struct mutex mutex; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci u32 function_row_physmap[MAX_FUNCTION_ROW_KEYS]; 2418c2ecf20Sopenharmony_ci int num_function_row_keys; 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * System-specific keymap fixup routine 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic void (*atkbd_platform_fixup)(struct atkbd *, const void *data); 2488c2ecf20Sopenharmony_cistatic void *atkbd_platform_fixup_data; 2498c2ecf20Sopenharmony_cistatic unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * Certain keyboards to not like ATKBD_CMD_RESET_DIS and stop responding 2538c2ecf20Sopenharmony_ci * to many commands until full reset (ATKBD_CMD_RESET_BAT) is performed. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_cistatic bool atkbd_skip_deactivate; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, 2588c2ecf20Sopenharmony_ci ssize_t (*handler)(struct atkbd *, char *)); 2598c2ecf20Sopenharmony_cistatic ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, 2608c2ecf20Sopenharmony_ci ssize_t (*handler)(struct atkbd *, const char *, size_t)); 2618c2ecf20Sopenharmony_ci#define ATKBD_DEFINE_ATTR(_name) \ 2628c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_##_name(struct atkbd *, char *); \ 2638c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \ 2648c2ecf20Sopenharmony_cistatic ssize_t atkbd_do_show_##_name(struct device *d, \ 2658c2ecf20Sopenharmony_ci struct device_attribute *attr, char *b) \ 2668c2ecf20Sopenharmony_ci{ \ 2678c2ecf20Sopenharmony_ci return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \ 2688c2ecf20Sopenharmony_ci} \ 2698c2ecf20Sopenharmony_cistatic ssize_t atkbd_do_set_##_name(struct device *d, \ 2708c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *b, size_t s) \ 2718c2ecf20Sopenharmony_ci{ \ 2728c2ecf20Sopenharmony_ci return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \ 2738c2ecf20Sopenharmony_ci} \ 2748c2ecf20Sopenharmony_cistatic struct device_attribute atkbd_attr_##_name = \ 2758c2ecf20Sopenharmony_ci __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciATKBD_DEFINE_ATTR(extra); 2788c2ecf20Sopenharmony_ciATKBD_DEFINE_ATTR(force_release); 2798c2ecf20Sopenharmony_ciATKBD_DEFINE_ATTR(scroll); 2808c2ecf20Sopenharmony_ciATKBD_DEFINE_ATTR(set); 2818c2ecf20Sopenharmony_ciATKBD_DEFINE_ATTR(softrepeat); 2828c2ecf20Sopenharmony_ciATKBD_DEFINE_ATTR(softraw); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci#define ATKBD_DEFINE_RO_ATTR(_name) \ 2858c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_##_name(struct atkbd *, char *); \ 2868c2ecf20Sopenharmony_cistatic ssize_t atkbd_do_show_##_name(struct device *d, \ 2878c2ecf20Sopenharmony_ci struct device_attribute *attr, char *b) \ 2888c2ecf20Sopenharmony_ci{ \ 2898c2ecf20Sopenharmony_ci return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \ 2908c2ecf20Sopenharmony_ci} \ 2918c2ecf20Sopenharmony_cistatic struct device_attribute atkbd_attr_##_name = \ 2928c2ecf20Sopenharmony_ci __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciATKBD_DEFINE_RO_ATTR(err_count); 2958c2ecf20Sopenharmony_ciATKBD_DEFINE_RO_ATTR(function_row_physmap); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic struct attribute *atkbd_attributes[] = { 2988c2ecf20Sopenharmony_ci &atkbd_attr_extra.attr, 2998c2ecf20Sopenharmony_ci &atkbd_attr_force_release.attr, 3008c2ecf20Sopenharmony_ci &atkbd_attr_scroll.attr, 3018c2ecf20Sopenharmony_ci &atkbd_attr_set.attr, 3028c2ecf20Sopenharmony_ci &atkbd_attr_softrepeat.attr, 3038c2ecf20Sopenharmony_ci &atkbd_attr_softraw.attr, 3048c2ecf20Sopenharmony_ci &atkbd_attr_err_count.attr, 3058c2ecf20Sopenharmony_ci &atkbd_attr_function_row_physmap.attr, 3068c2ecf20Sopenharmony_ci NULL 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_function_row_physmap(struct atkbd *atkbd, char *buf) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci ssize_t size = 0; 3128c2ecf20Sopenharmony_ci int i; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (!atkbd->num_function_row_keys) 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci for (i = 0; i < atkbd->num_function_row_keys; i++) 3188c2ecf20Sopenharmony_ci size += scnprintf(buf + size, PAGE_SIZE - size, "%02X ", 3198c2ecf20Sopenharmony_ci atkbd->function_row_physmap[i]); 3208c2ecf20Sopenharmony_ci size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); 3218c2ecf20Sopenharmony_ci return size; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic umode_t atkbd_attr_is_visible(struct kobject *kobj, 3258c2ecf20Sopenharmony_ci struct attribute *attr, int i) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 3288c2ecf20Sopenharmony_ci struct serio *serio = to_serio_port(dev); 3298c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (attr == &atkbd_attr_function_row_physmap.attr && 3328c2ecf20Sopenharmony_ci !atkbd->num_function_row_keys) 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return attr->mode; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic struct attribute_group atkbd_attribute_group = { 3398c2ecf20Sopenharmony_ci .attrs = atkbd_attributes, 3408c2ecf20Sopenharmony_ci .is_visible = atkbd_attr_is_visible, 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic const unsigned int xl_table[] = { 3448c2ecf20Sopenharmony_ci ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK, 3458c2ecf20Sopenharmony_ci ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL, 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * Checks if we should mangle the scancode to extract 'release' bit 3508c2ecf20Sopenharmony_ci * in translated mode. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_cistatic bool atkbd_need_xlate(unsigned long xl_bit, unsigned char code) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int i; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1) 3578c2ecf20Sopenharmony_ci return false; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xl_table); i++) 3608c2ecf20Sopenharmony_ci if (code == xl_table[i]) 3618c2ecf20Sopenharmony_ci return test_bit(i, &xl_bit); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return true; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/* 3678c2ecf20Sopenharmony_ci * Calculates new value of xl_bit so the driver can distinguish 3688c2ecf20Sopenharmony_ci * between make/break pair of scancodes for select keys and PS/2 3698c2ecf20Sopenharmony_ci * protocol responses. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_cistatic void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci int i; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xl_table); i++) { 3768c2ecf20Sopenharmony_ci if (!((code ^ xl_table[i]) & 0x7f)) { 3778c2ecf20Sopenharmony_ci if (code & 0x80) 3788c2ecf20Sopenharmony_ci __clear_bit(i, &atkbd->xl_bit); 3798c2ecf20Sopenharmony_ci else 3808c2ecf20Sopenharmony_ci __set_bit(i, &atkbd->xl_bit); 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * Encode the scancode, 0xe0 prefix, and high bit into a single integer, 3888c2ecf20Sopenharmony_ci * keeping kernel 2.4 compatibility for set 2 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_cistatic unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci if (atkbd->set == 3) { 3938c2ecf20Sopenharmony_ci if (atkbd->emul == 1) 3948c2ecf20Sopenharmony_ci code |= 0x100; 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci code = (code & 0x7f) | ((code & 0x80) << 1); 3978c2ecf20Sopenharmony_ci if (atkbd->emul == 1) 3988c2ecf20Sopenharmony_ci code |= 0x80; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return code; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* 4058c2ecf20Sopenharmony_ci * atkbd_interrupt(). Here takes place processing of data received from 4068c2ecf20Sopenharmony_ci * the keyboard into events. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, 4108c2ecf20Sopenharmony_ci unsigned int flags) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 4138c2ecf20Sopenharmony_ci struct input_dev *dev = atkbd->dev; 4148c2ecf20Sopenharmony_ci unsigned int code = data; 4158c2ecf20Sopenharmony_ci int scroll = 0, hscroll = 0, click = -1; 4168c2ecf20Sopenharmony_ci int value; 4178c2ecf20Sopenharmony_ci unsigned short keycode; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci#if !defined(__i386__) && !defined (__x86_64__) 4228c2ecf20Sopenharmony_ci if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) { 4238c2ecf20Sopenharmony_ci dev_warn(&serio->dev, "Frame/parity error: %02x\n", flags); 4248c2ecf20Sopenharmony_ci serio_write(serio, ATKBD_CMD_RESEND); 4258c2ecf20Sopenharmony_ci atkbd->resend = true; 4268c2ecf20Sopenharmony_ci goto out; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (!flags && data == ATKBD_RET_ACK) 4308c2ecf20Sopenharmony_ci atkbd->resend = false; 4318c2ecf20Sopenharmony_ci#endif 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK)) 4348c2ecf20Sopenharmony_ci if (ps2_handle_ack(&atkbd->ps2dev, data)) 4358c2ecf20Sopenharmony_ci goto out; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD)) 4388c2ecf20Sopenharmony_ci if (ps2_handle_response(&atkbd->ps2dev, data)) 4398c2ecf20Sopenharmony_ci goto out; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci pm_wakeup_event(&serio->dev, 0); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (!atkbd->enabled) 4448c2ecf20Sopenharmony_ci goto out; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci input_event(dev, EV_MSC, MSC_RAW, code); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (atkbd_platform_scancode_fixup) 4498c2ecf20Sopenharmony_ci code = atkbd_platform_scancode_fixup(atkbd, code); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (atkbd->translated) { 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) { 4548c2ecf20Sopenharmony_ci atkbd->release = code >> 7; 4558c2ecf20Sopenharmony_ci code &= 0x7f; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!atkbd->emul) 4598c2ecf20Sopenharmony_ci atkbd_calculate_xl_bit(atkbd, data); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci switch (code) { 4638c2ecf20Sopenharmony_ci case ATKBD_RET_BAT: 4648c2ecf20Sopenharmony_ci atkbd->enabled = false; 4658c2ecf20Sopenharmony_ci serio_reconnect(atkbd->ps2dev.serio); 4668c2ecf20Sopenharmony_ci goto out; 4678c2ecf20Sopenharmony_ci case ATKBD_RET_EMUL0: 4688c2ecf20Sopenharmony_ci atkbd->emul = 1; 4698c2ecf20Sopenharmony_ci goto out; 4708c2ecf20Sopenharmony_ci case ATKBD_RET_EMUL1: 4718c2ecf20Sopenharmony_ci atkbd->emul = 2; 4728c2ecf20Sopenharmony_ci goto out; 4738c2ecf20Sopenharmony_ci case ATKBD_RET_RELEASE: 4748c2ecf20Sopenharmony_ci atkbd->release = true; 4758c2ecf20Sopenharmony_ci goto out; 4768c2ecf20Sopenharmony_ci case ATKBD_RET_ACK: 4778c2ecf20Sopenharmony_ci case ATKBD_RET_NAK: 4788c2ecf20Sopenharmony_ci if (printk_ratelimit()) 4798c2ecf20Sopenharmony_ci dev_warn(&serio->dev, 4808c2ecf20Sopenharmony_ci "Spurious %s on %s. " 4818c2ecf20Sopenharmony_ci "Some program might be trying to access hardware directly.\n", 4828c2ecf20Sopenharmony_ci data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys); 4838c2ecf20Sopenharmony_ci goto out; 4848c2ecf20Sopenharmony_ci case ATKBD_RET_ERR: 4858c2ecf20Sopenharmony_ci atkbd->err_count++; 4868c2ecf20Sopenharmony_ci dev_dbg(&serio->dev, "Keyboard on %s reports too many keys pressed.\n", 4878c2ecf20Sopenharmony_ci serio->phys); 4888c2ecf20Sopenharmony_ci goto out; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci code = atkbd_compat_scancode(atkbd, code); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (atkbd->emul && --atkbd->emul) 4948c2ecf20Sopenharmony_ci goto out; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci keycode = atkbd->keycode[code]; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!(atkbd->release && test_bit(code, atkbd->force_release_mask))) 4998c2ecf20Sopenharmony_ci if (keycode != ATKBD_KEY_NULL) 5008c2ecf20Sopenharmony_ci input_event(dev, EV_MSC, MSC_SCAN, code); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci switch (keycode) { 5038c2ecf20Sopenharmony_ci case ATKBD_KEY_NULL: 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case ATKBD_KEY_UNKNOWN: 5068c2ecf20Sopenharmony_ci dev_warn(&serio->dev, 5078c2ecf20Sopenharmony_ci "Unknown key %s (%s set %d, code %#x on %s).\n", 5088c2ecf20Sopenharmony_ci atkbd->release ? "released" : "pressed", 5098c2ecf20Sopenharmony_ci atkbd->translated ? "translated" : "raw", 5108c2ecf20Sopenharmony_ci atkbd->set, code, serio->phys); 5118c2ecf20Sopenharmony_ci dev_warn(&serio->dev, 5128c2ecf20Sopenharmony_ci "Use 'setkeycodes %s%02x <keycode>' to make it known.\n", 5138c2ecf20Sopenharmony_ci code & 0x80 ? "e0" : "", code & 0x7f); 5148c2ecf20Sopenharmony_ci input_sync(dev); 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci case ATKBD_SCR_1: 5178c2ecf20Sopenharmony_ci scroll = 1; 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci case ATKBD_SCR_2: 5208c2ecf20Sopenharmony_ci scroll = 2; 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci case ATKBD_SCR_4: 5238c2ecf20Sopenharmony_ci scroll = 4; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci case ATKBD_SCR_8: 5268c2ecf20Sopenharmony_ci scroll = 8; 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci case ATKBD_SCR_CLICK: 5298c2ecf20Sopenharmony_ci click = !atkbd->release; 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci case ATKBD_SCR_LEFT: 5328c2ecf20Sopenharmony_ci hscroll = -1; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci case ATKBD_SCR_RIGHT: 5358c2ecf20Sopenharmony_ci hscroll = 1; 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci default: 5388c2ecf20Sopenharmony_ci if (atkbd->release) { 5398c2ecf20Sopenharmony_ci value = 0; 5408c2ecf20Sopenharmony_ci atkbd->last = 0; 5418c2ecf20Sopenharmony_ci } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) { 5428c2ecf20Sopenharmony_ci /* Workaround Toshiba laptop multiple keypress */ 5438c2ecf20Sopenharmony_ci value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2; 5448c2ecf20Sopenharmony_ci } else { 5458c2ecf20Sopenharmony_ci value = 1; 5468c2ecf20Sopenharmony_ci atkbd->last = code; 5478c2ecf20Sopenharmony_ci atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci input_event(dev, EV_KEY, keycode, value); 5518c2ecf20Sopenharmony_ci input_sync(dev); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (value && test_bit(code, atkbd->force_release_mask)) { 5548c2ecf20Sopenharmony_ci input_event(dev, EV_MSC, MSC_SCAN, code); 5558c2ecf20Sopenharmony_ci input_report_key(dev, keycode, 0); 5568c2ecf20Sopenharmony_ci input_sync(dev); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (atkbd->scroll) { 5618c2ecf20Sopenharmony_ci if (click != -1) 5628c2ecf20Sopenharmony_ci input_report_key(dev, BTN_MIDDLE, click); 5638c2ecf20Sopenharmony_ci input_report_rel(dev, REL_WHEEL, 5648c2ecf20Sopenharmony_ci atkbd->release ? -scroll : scroll); 5658c2ecf20Sopenharmony_ci input_report_rel(dev, REL_HWHEEL, hscroll); 5668c2ecf20Sopenharmony_ci input_sync(dev); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci atkbd->release = false; 5708c2ecf20Sopenharmony_ciout: 5718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int atkbd_set_repeat_rate(struct atkbd *atkbd) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci const short period[32] = 5778c2ecf20Sopenharmony_ci { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125, 5788c2ecf20Sopenharmony_ci 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 }; 5798c2ecf20Sopenharmony_ci const short delay[4] = 5808c2ecf20Sopenharmony_ci { 250, 500, 750, 1000 }; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci struct input_dev *dev = atkbd->dev; 5838c2ecf20Sopenharmony_ci unsigned char param; 5848c2ecf20Sopenharmony_ci int i = 0, j = 0; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD]) 5878c2ecf20Sopenharmony_ci i++; 5888c2ecf20Sopenharmony_ci dev->rep[REP_PERIOD] = period[i]; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci while (j < ARRAY_SIZE(delay) - 1 && delay[j] < dev->rep[REP_DELAY]) 5918c2ecf20Sopenharmony_ci j++; 5928c2ecf20Sopenharmony_ci dev->rep[REP_DELAY] = delay[j]; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci param = i | (j << 5); 5958c2ecf20Sopenharmony_ci return ps2_command(&atkbd->ps2dev, ¶m, ATKBD_CMD_SETREP); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int atkbd_set_leds(struct atkbd *atkbd) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct input_dev *dev = atkbd->dev; 6018c2ecf20Sopenharmony_ci unsigned char param[2]; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) 6048c2ecf20Sopenharmony_ci | (test_bit(LED_NUML, dev->led) ? 2 : 0) 6058c2ecf20Sopenharmony_ci | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); 6068c2ecf20Sopenharmony_ci if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS)) 6078c2ecf20Sopenharmony_ci return -1; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (atkbd->extra) { 6108c2ecf20Sopenharmony_ci param[0] = 0; 6118c2ecf20Sopenharmony_ci param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0) 6128c2ecf20Sopenharmony_ci | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0) 6138c2ecf20Sopenharmony_ci | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) 6148c2ecf20Sopenharmony_ci | (test_bit(LED_MISC, dev->led) ? 0x10 : 0) 6158c2ecf20Sopenharmony_ci | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); 6168c2ecf20Sopenharmony_ci if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS)) 6178c2ecf20Sopenharmony_ci return -1; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/* 6248c2ecf20Sopenharmony_ci * atkbd_event_work() is used to complete processing of events that 6258c2ecf20Sopenharmony_ci * can not be processed by input_event() which is often called from 6268c2ecf20Sopenharmony_ci * interrupt context. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic void atkbd_event_work(struct work_struct *work) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci mutex_lock(&atkbd->mutex); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!atkbd->enabled) { 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * Serio ports are resumed asynchronously so while driver core 6388c2ecf20Sopenharmony_ci * thinks that device is already fully operational in reality 6398c2ecf20Sopenharmony_ci * it may not be ready yet. In this case we need to keep 6408c2ecf20Sopenharmony_ci * rescheduling till reconnect completes. 6418c2ecf20Sopenharmony_ci */ 6428c2ecf20Sopenharmony_ci schedule_delayed_work(&atkbd->event_work, 6438c2ecf20Sopenharmony_ci msecs_to_jiffies(100)); 6448c2ecf20Sopenharmony_ci } else { 6458c2ecf20Sopenharmony_ci if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) 6468c2ecf20Sopenharmony_ci atkbd_set_leds(atkbd); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) 6498c2ecf20Sopenharmony_ci atkbd_set_repeat_rate(atkbd); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci mutex_unlock(&atkbd->mutex); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* 6568c2ecf20Sopenharmony_ci * Schedule switch for execution. We need to throttle requests, 6578c2ecf20Sopenharmony_ci * otherwise keyboard may become unresponsive. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_cistatic void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci unsigned long delay = msecs_to_jiffies(50); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (time_after(jiffies, atkbd->event_jiffies + delay)) 6648c2ecf20Sopenharmony_ci delay = 0; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci atkbd->event_jiffies = jiffies; 6678c2ecf20Sopenharmony_ci set_bit(event_bit, &atkbd->event_mask); 6688c2ecf20Sopenharmony_ci mb(); 6698c2ecf20Sopenharmony_ci schedule_delayed_work(&atkbd->event_work, delay); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/* 6738c2ecf20Sopenharmony_ci * Event callback from the input module. Events that change the state of 6748c2ecf20Sopenharmony_ci * the hardware are processed here. If action can not be performed in 6758c2ecf20Sopenharmony_ci * interrupt context it is offloaded to atkbd_event_work. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int atkbd_event(struct input_dev *dev, 6798c2ecf20Sopenharmony_ci unsigned int type, unsigned int code, int value) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct atkbd *atkbd = input_get_drvdata(dev); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (!atkbd->write) 6848c2ecf20Sopenharmony_ci return -1; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci switch (type) { 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci case EV_LED: 6898c2ecf20Sopenharmony_ci atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT); 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci case EV_REP: 6938c2ecf20Sopenharmony_ci if (!atkbd->softrepeat) 6948c2ecf20Sopenharmony_ci atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT); 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci default: 6988c2ecf20Sopenharmony_ci return -1; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/* 7038c2ecf20Sopenharmony_ci * atkbd_enable() signals that interrupt handler is allowed to 7048c2ecf20Sopenharmony_ci * generate input events. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic inline void atkbd_enable(struct atkbd *atkbd) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci serio_pause_rx(atkbd->ps2dev.serio); 7108c2ecf20Sopenharmony_ci atkbd->enabled = true; 7118c2ecf20Sopenharmony_ci serio_continue_rx(atkbd->ps2dev.serio); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/* 7158c2ecf20Sopenharmony_ci * atkbd_disable() tells input handler that all incoming data except 7168c2ecf20Sopenharmony_ci * for ACKs and command response should be dropped. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic inline void atkbd_disable(struct atkbd *atkbd) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci serio_pause_rx(atkbd->ps2dev.serio); 7228c2ecf20Sopenharmony_ci atkbd->enabled = false; 7238c2ecf20Sopenharmony_ci serio_continue_rx(atkbd->ps2dev.serio); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int atkbd_activate(struct atkbd *atkbd) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &atkbd->ps2dev; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/* 7318c2ecf20Sopenharmony_ci * Enable the keyboard to receive keystrokes. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { 7358c2ecf20Sopenharmony_ci dev_err(&ps2dev->serio->dev, 7368c2ecf20Sopenharmony_ci "Failed to enable keyboard on %s\n", 7378c2ecf20Sopenharmony_ci ps2dev->serio->phys); 7388c2ecf20Sopenharmony_ci return -1; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci/* 7458c2ecf20Sopenharmony_ci * atkbd_deactivate() resets and disables the keyboard from sending 7468c2ecf20Sopenharmony_ci * keystrokes. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic void atkbd_deactivate(struct atkbd *atkbd) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &atkbd->ps2dev; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS)) 7548c2ecf20Sopenharmony_ci dev_err(&ps2dev->serio->dev, 7558c2ecf20Sopenharmony_ci "Failed to deactivate keyboard on %s\n", 7568c2ecf20Sopenharmony_ci ps2dev->serio->phys); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 7608c2ecf20Sopenharmony_cistatic bool atkbd_is_portable_device(void) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci static const char * const chassis_types[] = { 7638c2ecf20Sopenharmony_ci "8", /* Portable */ 7648c2ecf20Sopenharmony_ci "9", /* Laptop */ 7658c2ecf20Sopenharmony_ci "10", /* Notebook */ 7668c2ecf20Sopenharmony_ci "14", /* Sub-Notebook */ 7678c2ecf20Sopenharmony_ci "31", /* Convertible */ 7688c2ecf20Sopenharmony_ci "32", /* Detachable */ 7698c2ecf20Sopenharmony_ci }; 7708c2ecf20Sopenharmony_ci int i; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chassis_types); i++) 7738c2ecf20Sopenharmony_ci if (dmi_match(DMI_CHASSIS_TYPE, chassis_types[i])) 7748c2ecf20Sopenharmony_ci return true; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci return false; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci/* 7808c2ecf20Sopenharmony_ci * On many modern laptops ATKBD_CMD_GETID may cause problems, on these laptops 7818c2ecf20Sopenharmony_ci * the controller is always in translated mode. In this mode mice/touchpads will 7828c2ecf20Sopenharmony_ci * not work. So in this case simply assume a keyboard is connected to avoid 7838c2ecf20Sopenharmony_ci * confusing some laptop keyboards. 7848c2ecf20Sopenharmony_ci * 7858c2ecf20Sopenharmony_ci * Skipping ATKBD_CMD_GETID ends up using a fake keyboard id. Using the standard 7868c2ecf20Sopenharmony_ci * 0xab83 id is ok in translated mode, only atkbd_select_set() checks atkbd->id 7878c2ecf20Sopenharmony_ci * and in translated mode that is a no-op. 7888c2ecf20Sopenharmony_ci */ 7898c2ecf20Sopenharmony_cistatic bool atkbd_skip_getid(struct atkbd *atkbd) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci return atkbd->translated && atkbd_is_portable_device(); 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci#else 7948c2ecf20Sopenharmony_cistatic inline bool atkbd_skip_getid(struct atkbd *atkbd) { return false; } 7958c2ecf20Sopenharmony_ci#endif 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci/* 7988c2ecf20Sopenharmony_ci * atkbd_probe() probes for an AT keyboard on a serio port. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int atkbd_probe(struct atkbd *atkbd) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &atkbd->ps2dev; 8048c2ecf20Sopenharmony_ci unsigned char param[2]; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci/* 8078c2ecf20Sopenharmony_ci * Some systems, where the bit-twiddling when testing the io-lines of the 8088c2ecf20Sopenharmony_ci * controller may confuse the keyboard need a full reset of the keyboard. On 8098c2ecf20Sopenharmony_ci * these systems the BIOS also usually doesn't do it for us. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (atkbd_reset) 8138c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT)) 8148c2ecf20Sopenharmony_ci dev_warn(&ps2dev->serio->dev, 8158c2ecf20Sopenharmony_ci "keyboard reset failed on %s\n", 8168c2ecf20Sopenharmony_ci ps2dev->serio->phys); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (atkbd_skip_getid(atkbd)) { 8198c2ecf20Sopenharmony_ci atkbd->id = 0xab83; 8208c2ecf20Sopenharmony_ci return 0; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci/* 8248c2ecf20Sopenharmony_ci * Then we check the keyboard ID. We should get 0xab83 under normal conditions. 8258c2ecf20Sopenharmony_ci * Some keyboards report different values, but the first byte is always 0xab or 8268c2ecf20Sopenharmony_ci * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this 8278c2ecf20Sopenharmony_ci * should make sure we don't try to set the LEDs on it. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci param[0] = param[1] = 0xa5; /* initialize with invalid values */ 8318c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) { 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci/* 8348c2ecf20Sopenharmony_ci * If the get ID command failed, we check if we can at least set 8358c2ecf20Sopenharmony_ci * the LEDs on the keyboard. This should work on every keyboard out there. 8368c2ecf20Sopenharmony_ci * It also turns the LEDs off, which we want anyway. 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ci param[0] = 0; 8398c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS)) 8408c2ecf20Sopenharmony_ci return -1; 8418c2ecf20Sopenharmony_ci atkbd->id = 0xabba; 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (!ps2_is_keyboard_id(param[0])) 8468c2ecf20Sopenharmony_ci return -1; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci atkbd->id = (param[0] << 8) | param[1]; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (atkbd->id == 0xaca1 && atkbd->translated) { 8518c2ecf20Sopenharmony_ci dev_err(&ps2dev->serio->dev, 8528c2ecf20Sopenharmony_ci "NCD terminal keyboards are only supported on non-translating controllers. " 8538c2ecf20Sopenharmony_ci "Use i8042.direct=1 to disable translation.\n"); 8548c2ecf20Sopenharmony_ci return -1; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci/* 8588c2ecf20Sopenharmony_ci * Make sure nothing is coming from the keyboard and disturbs our 8598c2ecf20Sopenharmony_ci * internal state. 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_ci if (!atkbd_skip_deactivate) 8628c2ecf20Sopenharmony_ci atkbd_deactivate(atkbd); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return 0; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* 8688c2ecf20Sopenharmony_ci * atkbd_select_set checks if a keyboard has a working Set 3 support, and 8698c2ecf20Sopenharmony_ci * sets it into that. Unfortunately there are keyboards that can be switched 8708c2ecf20Sopenharmony_ci * to Set 3, but don't work well in that (BTC Multimedia ...) 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &atkbd->ps2dev; 8768c2ecf20Sopenharmony_ci unsigned char param[2]; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci atkbd->extra = false; 8798c2ecf20Sopenharmony_ci/* 8808c2ecf20Sopenharmony_ci * For known special keyboards we can go ahead and set the correct set. 8818c2ecf20Sopenharmony_ci * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and 8828c2ecf20Sopenharmony_ci * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards. 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (atkbd->translated) 8868c2ecf20Sopenharmony_ci return 2; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (atkbd->id == 0xaca1) { 8898c2ecf20Sopenharmony_ci param[0] = 3; 8908c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET); 8918c2ecf20Sopenharmony_ci return 3; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (allow_extra) { 8958c2ecf20Sopenharmony_ci param[0] = 0x71; 8968c2ecf20Sopenharmony_ci if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) { 8978c2ecf20Sopenharmony_ci atkbd->extra = true; 8988c2ecf20Sopenharmony_ci return 2; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (atkbd_terminal) { 9038c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB); 9048c2ecf20Sopenharmony_ci return 3; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (target_set != 3) 9088c2ecf20Sopenharmony_ci return 2; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) { 9118c2ecf20Sopenharmony_ci atkbd->id = param[0] << 8 | param[1]; 9128c2ecf20Sopenharmony_ci return 2; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci param[0] = 3; 9168c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET)) 9178c2ecf20Sopenharmony_ci return 2; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci param[0] = 0; 9208c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET)) 9218c2ecf20Sopenharmony_ci return 2; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (param[0] != 3) { 9248c2ecf20Sopenharmony_ci param[0] = 2; 9258c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET)) 9268c2ecf20Sopenharmony_ci return 2; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return 3; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic int atkbd_reset_state(struct atkbd *atkbd) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &atkbd->ps2dev; 9378c2ecf20Sopenharmony_ci unsigned char param[1]; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* 9408c2ecf20Sopenharmony_ci * Set the LEDs to a predefined state (all off). 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci param[0] = 0; 9448c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS)) 9458c2ecf20Sopenharmony_ci return -1; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci/* 9488c2ecf20Sopenharmony_ci * Set autorepeat to fastest possible. 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci param[0] = 0; 9528c2ecf20Sopenharmony_ci if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP)) 9538c2ecf20Sopenharmony_ci return -1; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return 0; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci/* 9598c2ecf20Sopenharmony_ci * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a 9608c2ecf20Sopenharmony_ci * reboot. 9618c2ecf20Sopenharmony_ci */ 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic void atkbd_cleanup(struct serio *serio) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci atkbd_disable(atkbd); 9688c2ecf20Sopenharmony_ci ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_DEF); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci/* 9738c2ecf20Sopenharmony_ci * atkbd_disconnect() closes and frees. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic void atkbd_disconnect(struct serio *serio) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci atkbd_disable(atkbd); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci input_unregister_device(atkbd->dev); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* 9878c2ecf20Sopenharmony_ci * Make sure we don't have a command in flight. 9888c2ecf20Sopenharmony_ci * Note that since atkbd->enabled is false event work will keep 9898c2ecf20Sopenharmony_ci * rescheduling itself until it gets canceled and will not try 9908c2ecf20Sopenharmony_ci * accessing freed input device or serio port. 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&atkbd->event_work); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci serio_close(serio); 9958c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 9968c2ecf20Sopenharmony_ci kfree(atkbd); 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci/* 10008c2ecf20Sopenharmony_ci * generate release events for the keycodes given in data 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_cistatic void atkbd_apply_forced_release_keylist(struct atkbd* atkbd, 10038c2ecf20Sopenharmony_ci const void *data) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci const unsigned int *keys = data; 10068c2ecf20Sopenharmony_ci unsigned int i; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (atkbd->set == 2) 10098c2ecf20Sopenharmony_ci for (i = 0; keys[i] != -1U; i++) 10108c2ecf20Sopenharmony_ci __set_bit(keys[i], atkbd->force_release_mask); 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/* 10148c2ecf20Sopenharmony_ci * Most special keys (Fn+F?) on Dell laptops do not generate release 10158c2ecf20Sopenharmony_ci * events so we have to do it ourselves. 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_cistatic unsigned int atkbd_dell_laptop_forced_release_keys[] = { 10188c2ecf20Sopenharmony_ci 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, -1U 10198c2ecf20Sopenharmony_ci}; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci/* 10228c2ecf20Sopenharmony_ci * Perform fixup for HP system that doesn't generate release 10238c2ecf20Sopenharmony_ci * for its video switch 10248c2ecf20Sopenharmony_ci */ 10258c2ecf20Sopenharmony_cistatic unsigned int atkbd_hp_forced_release_keys[] = { 10268c2ecf20Sopenharmony_ci 0x94, -1U 10278c2ecf20Sopenharmony_ci}; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci/* 10308c2ecf20Sopenharmony_ci * Samsung NC10,NC20 with Fn+F? key release not working 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_cistatic unsigned int atkbd_samsung_forced_release_keys[] = { 10338c2ecf20Sopenharmony_ci 0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, -1U 10348c2ecf20Sopenharmony_ci}; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci/* 10378c2ecf20Sopenharmony_ci * Amilo Pi 3525 key release for Fn+Volume keys not working 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_cistatic unsigned int atkbd_amilo_pi3525_forced_release_keys[] = { 10408c2ecf20Sopenharmony_ci 0x20, 0xa0, 0x2e, 0xae, 0x30, 0xb0, -1U 10418c2ecf20Sopenharmony_ci}; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci/* 10448c2ecf20Sopenharmony_ci * Amilo Xi 3650 key release for light touch bar not working 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_cistatic unsigned int atkbd_amilo_xi3650_forced_release_keys[] = { 10478c2ecf20Sopenharmony_ci 0x67, 0xed, 0x90, 0xa2, 0x99, 0xa4, 0xae, 0xb0, -1U 10488c2ecf20Sopenharmony_ci}; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci/* 10518c2ecf20Sopenharmony_ci * Soltech TA12 system with broken key release on volume keys and mute key 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_cistatic unsigned int atkdb_soltech_ta12_forced_release_keys[] = { 10548c2ecf20Sopenharmony_ci 0xa0, 0xae, 0xb0, -1U 10558c2ecf20Sopenharmony_ci}; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci/* 10588c2ecf20Sopenharmony_ci * Many notebooks don't send key release event for volume up/down 10598c2ecf20Sopenharmony_ci * keys, with key list below common among them 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_cistatic unsigned int atkbd_volume_forced_release_keys[] = { 10628c2ecf20Sopenharmony_ci 0xae, 0xb0, -1U 10638c2ecf20Sopenharmony_ci}; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* 10668c2ecf20Sopenharmony_ci * OQO 01+ multimedia keys (64--66) generate e0 6x upon release whereas 10678c2ecf20Sopenharmony_ci * they should be generating e4-e6 (0x80 | code). 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_cistatic unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd, 10708c2ecf20Sopenharmony_ci unsigned int code) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci if (atkbd->translated && atkbd->emul == 1 && 10738c2ecf20Sopenharmony_ci (code == 0x64 || code == 0x65 || code == 0x66)) { 10748c2ecf20Sopenharmony_ci atkbd->emul = 0; 10758c2ecf20Sopenharmony_ci code |= 0x80; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci return code; 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct device *dev = &atkbd->ps2dev.serio->dev; 10848c2ecf20Sopenharmony_ci int i, n; 10858c2ecf20Sopenharmony_ci u32 *ptr; 10868c2ecf20Sopenharmony_ci u16 scancode, keycode; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Parse "linux,keymap" property */ 10898c2ecf20Sopenharmony_ci n = device_property_count_u32(dev, "linux,keymap"); 10908c2ecf20Sopenharmony_ci if (n <= 0 || n > ATKBD_KEYMAP_SIZE) 10918c2ecf20Sopenharmony_ci return -ENXIO; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci ptr = kcalloc(n, sizeof(u32), GFP_KERNEL); 10948c2ecf20Sopenharmony_ci if (!ptr) 10958c2ecf20Sopenharmony_ci return -ENOMEM; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (device_property_read_u32_array(dev, "linux,keymap", ptr, n)) { 10988c2ecf20Sopenharmony_ci dev_err(dev, "problem parsing FW keymap property\n"); 10998c2ecf20Sopenharmony_ci kfree(ptr); 11008c2ecf20Sopenharmony_ci return -EINVAL; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); 11048c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 11058c2ecf20Sopenharmony_ci scancode = SCANCODE(ptr[i]); 11068c2ecf20Sopenharmony_ci keycode = KEYCODE(ptr[i]); 11078c2ecf20Sopenharmony_ci atkbd->keycode[scancode] = keycode; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci kfree(ptr); 11118c2ecf20Sopenharmony_ci return 0; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/* 11158c2ecf20Sopenharmony_ci * atkbd_set_keycode_table() initializes keyboard's keycode table 11168c2ecf20Sopenharmony_ci * according to the selected scancode set 11178c2ecf20Sopenharmony_ci */ 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic void atkbd_set_keycode_table(struct atkbd *atkbd) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct device *dev = &atkbd->ps2dev.serio->dev; 11228c2ecf20Sopenharmony_ci unsigned int scancode; 11238c2ecf20Sopenharmony_ci int i, j; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); 11268c2ecf20Sopenharmony_ci bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (!atkbd_get_keymap_from_fwnode(atkbd)) { 11298c2ecf20Sopenharmony_ci dev_dbg(dev, "Using FW keymap\n"); 11308c2ecf20Sopenharmony_ci } else if (atkbd->translated) { 11318c2ecf20Sopenharmony_ci for (i = 0; i < 128; i++) { 11328c2ecf20Sopenharmony_ci scancode = atkbd_unxlate_table[i]; 11338c2ecf20Sopenharmony_ci atkbd->keycode[i] = atkbd_set2_keycode[scancode]; 11348c2ecf20Sopenharmony_ci atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80]; 11358c2ecf20Sopenharmony_ci if (atkbd->scroll) 11368c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++) 11378c2ecf20Sopenharmony_ci if ((scancode | 0x80) == atkbd_scroll_keys[j].set2) 11388c2ecf20Sopenharmony_ci atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci } else if (atkbd->set == 3) { 11418c2ecf20Sopenharmony_ci memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode)); 11428c2ecf20Sopenharmony_ci } else { 11438c2ecf20Sopenharmony_ci memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode)); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (atkbd->scroll) 11468c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) { 11478c2ecf20Sopenharmony_ci scancode = atkbd_scroll_keys[i].set2; 11488c2ecf20Sopenharmony_ci atkbd->keycode[scancode] = atkbd_scroll_keys[i].keycode; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* 11538c2ecf20Sopenharmony_ci * HANGEUL and HANJA keys do not send release events so we need to 11548c2ecf20Sopenharmony_ci * generate such events ourselves 11558c2ecf20Sopenharmony_ci */ 11568c2ecf20Sopenharmony_ci scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL); 11578c2ecf20Sopenharmony_ci atkbd->keycode[scancode] = KEY_HANGEUL; 11588c2ecf20Sopenharmony_ci __set_bit(scancode, atkbd->force_release_mask); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA); 11618c2ecf20Sopenharmony_ci atkbd->keycode[scancode] = KEY_HANJA; 11628c2ecf20Sopenharmony_ci __set_bit(scancode, atkbd->force_release_mask); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/* 11658c2ecf20Sopenharmony_ci * Perform additional fixups 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci if (atkbd_platform_fixup) 11688c2ecf20Sopenharmony_ci atkbd_platform_fixup(atkbd, atkbd_platform_fixup_data); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/* 11728c2ecf20Sopenharmony_ci * atkbd_set_device_attrs() sets up keyboard's input device structure 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic void atkbd_set_device_attrs(struct atkbd *atkbd) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci struct input_dev *input_dev = atkbd->dev; 11788c2ecf20Sopenharmony_ci int i; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (atkbd->extra) 11818c2ecf20Sopenharmony_ci snprintf(atkbd->name, sizeof(atkbd->name), 11828c2ecf20Sopenharmony_ci "AT Set 2 Extra keyboard"); 11838c2ecf20Sopenharmony_ci else 11848c2ecf20Sopenharmony_ci snprintf(atkbd->name, sizeof(atkbd->name), 11858c2ecf20Sopenharmony_ci "AT %s Set %d keyboard", 11868c2ecf20Sopenharmony_ci atkbd->translated ? "Translated" : "Raw", atkbd->set); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci snprintf(atkbd->phys, sizeof(atkbd->phys), 11898c2ecf20Sopenharmony_ci "%s/input0", atkbd->ps2dev.serio->phys); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci input_dev->name = atkbd->name; 11928c2ecf20Sopenharmony_ci input_dev->phys = atkbd->phys; 11938c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_I8042; 11948c2ecf20Sopenharmony_ci input_dev->id.vendor = 0x0001; 11958c2ecf20Sopenharmony_ci input_dev->id.product = atkbd->translated ? 1 : atkbd->set; 11968c2ecf20Sopenharmony_ci input_dev->id.version = atkbd->id; 11978c2ecf20Sopenharmony_ci input_dev->event = atkbd_event; 11988c2ecf20Sopenharmony_ci input_dev->dev.parent = &atkbd->ps2dev.serio->dev; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, atkbd); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | 12038c2ecf20Sopenharmony_ci BIT_MASK(EV_MSC); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (atkbd->write) { 12068c2ecf20Sopenharmony_ci input_dev->evbit[0] |= BIT_MASK(EV_LED); 12078c2ecf20Sopenharmony_ci input_dev->ledbit[0] = BIT_MASK(LED_NUML) | 12088c2ecf20Sopenharmony_ci BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL); 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (atkbd->extra) 12128c2ecf20Sopenharmony_ci input_dev->ledbit[0] |= BIT_MASK(LED_COMPOSE) | 12138c2ecf20Sopenharmony_ci BIT_MASK(LED_SUSPEND) | BIT_MASK(LED_SLEEP) | 12148c2ecf20Sopenharmony_ci BIT_MASK(LED_MUTE) | BIT_MASK(LED_MISC); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (!atkbd->softrepeat) { 12178c2ecf20Sopenharmony_ci input_dev->rep[REP_DELAY] = 250; 12188c2ecf20Sopenharmony_ci input_dev->rep[REP_PERIOD] = 33; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci input_dev->mscbit[0] = atkbd->softraw ? BIT_MASK(MSC_SCAN) : 12228c2ecf20Sopenharmony_ci BIT_MASK(MSC_RAW) | BIT_MASK(MSC_SCAN); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (atkbd->scroll) { 12258c2ecf20Sopenharmony_ci input_dev->evbit[0] |= BIT_MASK(EV_REL); 12268c2ecf20Sopenharmony_ci input_dev->relbit[0] = BIT_MASK(REL_WHEEL) | 12278c2ecf20Sopenharmony_ci BIT_MASK(REL_HWHEEL); 12288c2ecf20Sopenharmony_ci __set_bit(BTN_MIDDLE, input_dev->keybit); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci input_dev->keycode = atkbd->keycode; 12328c2ecf20Sopenharmony_ci input_dev->keycodesize = sizeof(unsigned short); 12338c2ecf20Sopenharmony_ci input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) { 12368c2ecf20Sopenharmony_ci if (atkbd->keycode[i] != KEY_RESERVED && 12378c2ecf20Sopenharmony_ci atkbd->keycode[i] != ATKBD_KEY_NULL && 12388c2ecf20Sopenharmony_ci atkbd->keycode[i] < ATKBD_SPECIAL) { 12398c2ecf20Sopenharmony_ci __set_bit(atkbd->keycode[i], input_dev->keybit); 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic void atkbd_parse_fwnode_data(struct serio *serio) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 12478c2ecf20Sopenharmony_ci struct device *dev = &serio->dev; 12488c2ecf20Sopenharmony_ci int n; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* Parse "function-row-physmap" property */ 12518c2ecf20Sopenharmony_ci n = device_property_count_u32(dev, "function-row-physmap"); 12528c2ecf20Sopenharmony_ci if (n > 0 && n <= MAX_FUNCTION_ROW_KEYS && 12538c2ecf20Sopenharmony_ci !device_property_read_u32_array(dev, "function-row-physmap", 12548c2ecf20Sopenharmony_ci atkbd->function_row_physmap, n)) { 12558c2ecf20Sopenharmony_ci atkbd->num_function_row_keys = n; 12568c2ecf20Sopenharmony_ci dev_dbg(dev, "FW reported %d function-row key locations\n", n); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci/* 12618c2ecf20Sopenharmony_ci * atkbd_connect() is called when the serio module finds an interface 12628c2ecf20Sopenharmony_ci * that isn't handled yet by an appropriate device driver. We check if 12638c2ecf20Sopenharmony_ci * there is an AT keyboard out there and if yes, we register ourselves 12648c2ecf20Sopenharmony_ci * to the input module. 12658c2ecf20Sopenharmony_ci */ 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic int atkbd_connect(struct serio *serio, struct serio_driver *drv) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct atkbd *atkbd; 12708c2ecf20Sopenharmony_ci struct input_dev *dev; 12718c2ecf20Sopenharmony_ci int err = -ENOMEM; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL); 12748c2ecf20Sopenharmony_ci dev = input_allocate_device(); 12758c2ecf20Sopenharmony_ci if (!atkbd || !dev) 12768c2ecf20Sopenharmony_ci goto fail1; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci atkbd->dev = dev; 12798c2ecf20Sopenharmony_ci ps2_init(&atkbd->ps2dev, serio); 12808c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); 12818c2ecf20Sopenharmony_ci mutex_init(&atkbd->mutex); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci switch (serio->id.type) { 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci case SERIO_8042_XL: 12868c2ecf20Sopenharmony_ci atkbd->translated = true; 12878c2ecf20Sopenharmony_ci fallthrough; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci case SERIO_8042: 12908c2ecf20Sopenharmony_ci if (serio->write) 12918c2ecf20Sopenharmony_ci atkbd->write = true; 12928c2ecf20Sopenharmony_ci break; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci atkbd->softraw = atkbd_softraw; 12968c2ecf20Sopenharmony_ci atkbd->softrepeat = atkbd_softrepeat; 12978c2ecf20Sopenharmony_ci atkbd->scroll = atkbd_scroll; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (atkbd->softrepeat) 13008c2ecf20Sopenharmony_ci atkbd->softraw = true; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci serio_set_drvdata(serio, atkbd); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci err = serio_open(serio, drv); 13058c2ecf20Sopenharmony_ci if (err) 13068c2ecf20Sopenharmony_ci goto fail2; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (atkbd->write) { 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (atkbd_probe(atkbd)) { 13118c2ecf20Sopenharmony_ci err = -ENODEV; 13128c2ecf20Sopenharmony_ci goto fail3; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); 13168c2ecf20Sopenharmony_ci atkbd_reset_state(atkbd); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci } else { 13198c2ecf20Sopenharmony_ci atkbd->set = 2; 13208c2ecf20Sopenharmony_ci atkbd->id = 0xab00; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci atkbd_parse_fwnode_data(serio); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 13268c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group); 13298c2ecf20Sopenharmony_ci if (err) 13308c2ecf20Sopenharmony_ci goto fail3; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci atkbd_enable(atkbd); 13338c2ecf20Sopenharmony_ci if (serio->write) 13348c2ecf20Sopenharmony_ci atkbd_activate(atkbd); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci err = input_register_device(atkbd->dev); 13378c2ecf20Sopenharmony_ci if (err) 13388c2ecf20Sopenharmony_ci goto fail4; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci return 0; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group); 13438c2ecf20Sopenharmony_ci fail3: serio_close(serio); 13448c2ecf20Sopenharmony_ci fail2: serio_set_drvdata(serio, NULL); 13458c2ecf20Sopenharmony_ci fail1: input_free_device(dev); 13468c2ecf20Sopenharmony_ci kfree(atkbd); 13478c2ecf20Sopenharmony_ci return err; 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci/* 13518c2ecf20Sopenharmony_ci * atkbd_reconnect() tries to restore keyboard into a sane state and is 13528c2ecf20Sopenharmony_ci * most likely called on resume. 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic int atkbd_reconnect(struct serio *serio) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 13588c2ecf20Sopenharmony_ci struct serio_driver *drv = serio->drv; 13598c2ecf20Sopenharmony_ci int retval = -1; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (!atkbd || !drv) { 13628c2ecf20Sopenharmony_ci dev_dbg(&serio->dev, 13638c2ecf20Sopenharmony_ci "reconnect request, but serio is disconnected, ignoring...\n"); 13648c2ecf20Sopenharmony_ci return -1; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci mutex_lock(&atkbd->mutex); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci atkbd_disable(atkbd); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (atkbd->write) { 13728c2ecf20Sopenharmony_ci if (atkbd_probe(atkbd)) 13738c2ecf20Sopenharmony_ci goto out; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) 13768c2ecf20Sopenharmony_ci goto out; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* 13798c2ecf20Sopenharmony_ci * Restore LED state and repeat rate. While input core 13808c2ecf20Sopenharmony_ci * will do this for us at resume time reconnect may happen 13818c2ecf20Sopenharmony_ci * because user requested it via sysfs or simply because 13828c2ecf20Sopenharmony_ci * keyboard was unplugged and plugged in again so we need 13838c2ecf20Sopenharmony_ci * to do it ourselves here. 13848c2ecf20Sopenharmony_ci */ 13858c2ecf20Sopenharmony_ci atkbd_set_leds(atkbd); 13868c2ecf20Sopenharmony_ci if (!atkbd->softrepeat) 13878c2ecf20Sopenharmony_ci atkbd_set_repeat_rate(atkbd); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* 13928c2ecf20Sopenharmony_ci * Reset our state machine in case reconnect happened in the middle 13938c2ecf20Sopenharmony_ci * of multi-byte scancode. 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci atkbd->xl_bit = 0; 13968c2ecf20Sopenharmony_ci atkbd->emul = 0; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci atkbd_enable(atkbd); 13998c2ecf20Sopenharmony_ci if (atkbd->write) 14008c2ecf20Sopenharmony_ci atkbd_activate(atkbd); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci retval = 0; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci out: 14058c2ecf20Sopenharmony_ci mutex_unlock(&atkbd->mutex); 14068c2ecf20Sopenharmony_ci return retval; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic const struct serio_device_id atkbd_serio_ids[] = { 14108c2ecf20Sopenharmony_ci { 14118c2ecf20Sopenharmony_ci .type = SERIO_8042, 14128c2ecf20Sopenharmony_ci .proto = SERIO_ANY, 14138c2ecf20Sopenharmony_ci .id = SERIO_ANY, 14148c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 14158c2ecf20Sopenharmony_ci }, 14168c2ecf20Sopenharmony_ci { 14178c2ecf20Sopenharmony_ci .type = SERIO_8042_XL, 14188c2ecf20Sopenharmony_ci .proto = SERIO_ANY, 14198c2ecf20Sopenharmony_ci .id = SERIO_ANY, 14208c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 14218c2ecf20Sopenharmony_ci }, 14228c2ecf20Sopenharmony_ci { 14238c2ecf20Sopenharmony_ci .type = SERIO_RS232, 14248c2ecf20Sopenharmony_ci .proto = SERIO_PS2SER, 14258c2ecf20Sopenharmony_ci .id = SERIO_ANY, 14268c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 14278c2ecf20Sopenharmony_ci }, 14288c2ecf20Sopenharmony_ci { 0 } 14298c2ecf20Sopenharmony_ci}; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(serio, atkbd_serio_ids); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic struct serio_driver atkbd_drv = { 14348c2ecf20Sopenharmony_ci .driver = { 14358c2ecf20Sopenharmony_ci .name = "atkbd", 14368c2ecf20Sopenharmony_ci }, 14378c2ecf20Sopenharmony_ci .description = DRIVER_DESC, 14388c2ecf20Sopenharmony_ci .id_table = atkbd_serio_ids, 14398c2ecf20Sopenharmony_ci .interrupt = atkbd_interrupt, 14408c2ecf20Sopenharmony_ci .connect = atkbd_connect, 14418c2ecf20Sopenharmony_ci .reconnect = atkbd_reconnect, 14428c2ecf20Sopenharmony_ci .disconnect = atkbd_disconnect, 14438c2ecf20Sopenharmony_ci .cleanup = atkbd_cleanup, 14448c2ecf20Sopenharmony_ci}; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, 14478c2ecf20Sopenharmony_ci ssize_t (*handler)(struct atkbd *, char *)) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct serio *serio = to_serio_port(dev); 14508c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci return handler(atkbd, buf); 14538c2ecf20Sopenharmony_ci} 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistatic ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count, 14568c2ecf20Sopenharmony_ci ssize_t (*handler)(struct atkbd *, const char *, size_t)) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci struct serio *serio = to_serio_port(dev); 14598c2ecf20Sopenharmony_ci struct atkbd *atkbd = serio_get_drvdata(serio); 14608c2ecf20Sopenharmony_ci int retval; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci retval = mutex_lock_interruptible(&atkbd->mutex); 14638c2ecf20Sopenharmony_ci if (retval) 14648c2ecf20Sopenharmony_ci return retval; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci atkbd_disable(atkbd); 14678c2ecf20Sopenharmony_ci retval = handler(atkbd, buf, count); 14688c2ecf20Sopenharmony_ci atkbd_enable(atkbd); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci mutex_unlock(&atkbd->mutex); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci return retval; 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf) 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct input_dev *old_dev, *new_dev; 14838c2ecf20Sopenharmony_ci unsigned int value; 14848c2ecf20Sopenharmony_ci int err; 14858c2ecf20Sopenharmony_ci bool old_extra; 14868c2ecf20Sopenharmony_ci unsigned char old_set; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (!atkbd->write) 14898c2ecf20Sopenharmony_ci return -EIO; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci err = kstrtouint(buf, 10, &value); 14928c2ecf20Sopenharmony_ci if (err) 14938c2ecf20Sopenharmony_ci return err; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (value > 1) 14968c2ecf20Sopenharmony_ci return -EINVAL; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (atkbd->extra != value) { 14998c2ecf20Sopenharmony_ci /* 15008c2ecf20Sopenharmony_ci * Since device's properties will change we need to 15018c2ecf20Sopenharmony_ci * unregister old device. But allocate and register 15028c2ecf20Sopenharmony_ci * new one first to make sure we have it. 15038c2ecf20Sopenharmony_ci */ 15048c2ecf20Sopenharmony_ci old_dev = atkbd->dev; 15058c2ecf20Sopenharmony_ci old_extra = atkbd->extra; 15068c2ecf20Sopenharmony_ci old_set = atkbd->set; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci new_dev = input_allocate_device(); 15098c2ecf20Sopenharmony_ci if (!new_dev) 15108c2ecf20Sopenharmony_ci return -ENOMEM; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci atkbd->dev = new_dev; 15138c2ecf20Sopenharmony_ci atkbd->set = atkbd_select_set(atkbd, atkbd->set, value); 15148c2ecf20Sopenharmony_ci atkbd_reset_state(atkbd); 15158c2ecf20Sopenharmony_ci atkbd_activate(atkbd); 15168c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 15178c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci err = input_register_device(atkbd->dev); 15208c2ecf20Sopenharmony_ci if (err) { 15218c2ecf20Sopenharmony_ci input_free_device(new_dev); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci atkbd->dev = old_dev; 15248c2ecf20Sopenharmony_ci atkbd->set = atkbd_select_set(atkbd, old_set, old_extra); 15258c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 15268c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci return err; 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci input_unregister_device(old_dev); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci return count; 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci size_t len = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", 15398c2ecf20Sopenharmony_ci ATKBD_KEYMAP_SIZE, atkbd->force_release_mask); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci buf[len++] = '\n'; 15428c2ecf20Sopenharmony_ci buf[len] = '\0'; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci return len; 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_force_release(struct atkbd *atkbd, 15488c2ecf20Sopenharmony_ci const char *buf, size_t count) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci /* 64 bytes on stack should be acceptable */ 15518c2ecf20Sopenharmony_ci DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE); 15528c2ecf20Sopenharmony_ci int err; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci err = bitmap_parselist(buf, new_mask, ATKBD_KEYMAP_SIZE); 15558c2ecf20Sopenharmony_ci if (err) 15568c2ecf20Sopenharmony_ci return err; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci memcpy(atkbd->force_release_mask, new_mask, sizeof(atkbd->force_release_mask)); 15598c2ecf20Sopenharmony_ci return count; 15608c2ecf20Sopenharmony_ci} 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) 15648c2ecf20Sopenharmony_ci{ 15658c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0); 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count) 15698c2ecf20Sopenharmony_ci{ 15708c2ecf20Sopenharmony_ci struct input_dev *old_dev, *new_dev; 15718c2ecf20Sopenharmony_ci unsigned int value; 15728c2ecf20Sopenharmony_ci int err; 15738c2ecf20Sopenharmony_ci bool old_scroll; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci err = kstrtouint(buf, 10, &value); 15768c2ecf20Sopenharmony_ci if (err) 15778c2ecf20Sopenharmony_ci return err; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (value > 1) 15808c2ecf20Sopenharmony_ci return -EINVAL; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (atkbd->scroll != value) { 15838c2ecf20Sopenharmony_ci old_dev = atkbd->dev; 15848c2ecf20Sopenharmony_ci old_scroll = atkbd->scroll; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci new_dev = input_allocate_device(); 15878c2ecf20Sopenharmony_ci if (!new_dev) 15888c2ecf20Sopenharmony_ci return -ENOMEM; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci atkbd->dev = new_dev; 15918c2ecf20Sopenharmony_ci atkbd->scroll = value; 15928c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 15938c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci err = input_register_device(atkbd->dev); 15968c2ecf20Sopenharmony_ci if (err) { 15978c2ecf20Sopenharmony_ci input_free_device(new_dev); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci atkbd->scroll = old_scroll; 16008c2ecf20Sopenharmony_ci atkbd->dev = old_dev; 16018c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 16028c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci return err; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci input_unregister_device(old_dev); 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci return count; 16098c2ecf20Sopenharmony_ci} 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", atkbd->set); 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci struct input_dev *old_dev, *new_dev; 16198c2ecf20Sopenharmony_ci unsigned int value; 16208c2ecf20Sopenharmony_ci int err; 16218c2ecf20Sopenharmony_ci unsigned char old_set; 16228c2ecf20Sopenharmony_ci bool old_extra; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci if (!atkbd->write) 16258c2ecf20Sopenharmony_ci return -EIO; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci err = kstrtouint(buf, 10, &value); 16288c2ecf20Sopenharmony_ci if (err) 16298c2ecf20Sopenharmony_ci return err; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (value != 2 && value != 3) 16328c2ecf20Sopenharmony_ci return -EINVAL; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if (atkbd->set != value) { 16358c2ecf20Sopenharmony_ci old_dev = atkbd->dev; 16368c2ecf20Sopenharmony_ci old_extra = atkbd->extra; 16378c2ecf20Sopenharmony_ci old_set = atkbd->set; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci new_dev = input_allocate_device(); 16408c2ecf20Sopenharmony_ci if (!new_dev) 16418c2ecf20Sopenharmony_ci return -ENOMEM; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci atkbd->dev = new_dev; 16448c2ecf20Sopenharmony_ci atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra); 16458c2ecf20Sopenharmony_ci atkbd_reset_state(atkbd); 16468c2ecf20Sopenharmony_ci atkbd_activate(atkbd); 16478c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 16488c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci err = input_register_device(atkbd->dev); 16518c2ecf20Sopenharmony_ci if (err) { 16528c2ecf20Sopenharmony_ci input_free_device(new_dev); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci atkbd->dev = old_dev; 16558c2ecf20Sopenharmony_ci atkbd->set = atkbd_select_set(atkbd, old_set, old_extra); 16568c2ecf20Sopenharmony_ci atkbd_set_keycode_table(atkbd); 16578c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci return err; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci input_unregister_device(old_dev); 16628c2ecf20Sopenharmony_ci } 16638c2ecf20Sopenharmony_ci return count; 16648c2ecf20Sopenharmony_ci} 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf) 16678c2ecf20Sopenharmony_ci{ 16688c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0); 16698c2ecf20Sopenharmony_ci} 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count) 16728c2ecf20Sopenharmony_ci{ 16738c2ecf20Sopenharmony_ci struct input_dev *old_dev, *new_dev; 16748c2ecf20Sopenharmony_ci unsigned int value; 16758c2ecf20Sopenharmony_ci int err; 16768c2ecf20Sopenharmony_ci bool old_softrepeat, old_softraw; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (!atkbd->write) 16798c2ecf20Sopenharmony_ci return -EIO; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci err = kstrtouint(buf, 10, &value); 16828c2ecf20Sopenharmony_ci if (err) 16838c2ecf20Sopenharmony_ci return err; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci if (value > 1) 16868c2ecf20Sopenharmony_ci return -EINVAL; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci if (atkbd->softrepeat != value) { 16898c2ecf20Sopenharmony_ci old_dev = atkbd->dev; 16908c2ecf20Sopenharmony_ci old_softrepeat = atkbd->softrepeat; 16918c2ecf20Sopenharmony_ci old_softraw = atkbd->softraw; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci new_dev = input_allocate_device(); 16948c2ecf20Sopenharmony_ci if (!new_dev) 16958c2ecf20Sopenharmony_ci return -ENOMEM; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci atkbd->dev = new_dev; 16988c2ecf20Sopenharmony_ci atkbd->softrepeat = value; 16998c2ecf20Sopenharmony_ci if (atkbd->softrepeat) 17008c2ecf20Sopenharmony_ci atkbd->softraw = true; 17018c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci err = input_register_device(atkbd->dev); 17048c2ecf20Sopenharmony_ci if (err) { 17058c2ecf20Sopenharmony_ci input_free_device(new_dev); 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci atkbd->dev = old_dev; 17088c2ecf20Sopenharmony_ci atkbd->softrepeat = old_softrepeat; 17098c2ecf20Sopenharmony_ci atkbd->softraw = old_softraw; 17108c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci return err; 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci input_unregister_device(old_dev); 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci return count; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0); 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count) 17268c2ecf20Sopenharmony_ci{ 17278c2ecf20Sopenharmony_ci struct input_dev *old_dev, *new_dev; 17288c2ecf20Sopenharmony_ci unsigned int value; 17298c2ecf20Sopenharmony_ci int err; 17308c2ecf20Sopenharmony_ci bool old_softraw; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci err = kstrtouint(buf, 10, &value); 17338c2ecf20Sopenharmony_ci if (err) 17348c2ecf20Sopenharmony_ci return err; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci if (value > 1) 17378c2ecf20Sopenharmony_ci return -EINVAL; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci if (atkbd->softraw != value) { 17408c2ecf20Sopenharmony_ci old_dev = atkbd->dev; 17418c2ecf20Sopenharmony_ci old_softraw = atkbd->softraw; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci new_dev = input_allocate_device(); 17448c2ecf20Sopenharmony_ci if (!new_dev) 17458c2ecf20Sopenharmony_ci return -ENOMEM; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci atkbd->dev = new_dev; 17488c2ecf20Sopenharmony_ci atkbd->softraw = value; 17498c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci err = input_register_device(atkbd->dev); 17528c2ecf20Sopenharmony_ci if (err) { 17538c2ecf20Sopenharmony_ci input_free_device(new_dev); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci atkbd->dev = old_dev; 17568c2ecf20Sopenharmony_ci atkbd->softraw = old_softraw; 17578c2ecf20Sopenharmony_ci atkbd_set_device_attrs(atkbd); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci return err; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci input_unregister_device(old_dev); 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci return count; 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci return sprintf(buf, "%lu\n", atkbd->err_count); 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_cistatic int __init atkbd_setup_forced_release(const struct dmi_system_id *id) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci atkbd_platform_fixup = atkbd_apply_forced_release_keylist; 17748c2ecf20Sopenharmony_ci atkbd_platform_fixup_data = id->driver_data; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci return 1; 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_cistatic int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) 17808c2ecf20Sopenharmony_ci{ 17818c2ecf20Sopenharmony_ci atkbd_platform_scancode_fixup = id->driver_data; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci return 1; 17848c2ecf20Sopenharmony_ci} 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_cistatic int __init atkbd_deactivate_fixup(const struct dmi_system_id *id) 17878c2ecf20Sopenharmony_ci{ 17888c2ecf20Sopenharmony_ci atkbd_skip_deactivate = true; 17898c2ecf20Sopenharmony_ci return 1; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci/* 17938c2ecf20Sopenharmony_ci * NOTE: do not add any more "force release" quirks to this table. The 17948c2ecf20Sopenharmony_ci * task of adjusting list of keys that should be "released" automatically 17958c2ecf20Sopenharmony_ci * by the driver is now delegated to userspace tools, such as udev, so 17968c2ecf20Sopenharmony_ci * submit such quirks there. 17978c2ecf20Sopenharmony_ci */ 17988c2ecf20Sopenharmony_cistatic const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { 17998c2ecf20Sopenharmony_ci { 18008c2ecf20Sopenharmony_ci .matches = { 18018c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 18028c2ecf20Sopenharmony_ci DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 18038c2ecf20Sopenharmony_ci }, 18048c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18058c2ecf20Sopenharmony_ci .driver_data = atkbd_dell_laptop_forced_release_keys, 18068c2ecf20Sopenharmony_ci }, 18078c2ecf20Sopenharmony_ci { 18088c2ecf20Sopenharmony_ci .matches = { 18098c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 18108c2ecf20Sopenharmony_ci DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 18118c2ecf20Sopenharmony_ci }, 18128c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18138c2ecf20Sopenharmony_ci .driver_data = atkbd_dell_laptop_forced_release_keys, 18148c2ecf20Sopenharmony_ci }, 18158c2ecf20Sopenharmony_ci { 18168c2ecf20Sopenharmony_ci .matches = { 18178c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 18188c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"), 18198c2ecf20Sopenharmony_ci }, 18208c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18218c2ecf20Sopenharmony_ci .driver_data = atkbd_hp_forced_release_keys, 18228c2ecf20Sopenharmony_ci }, 18238c2ecf20Sopenharmony_ci { 18248c2ecf20Sopenharmony_ci .matches = { 18258c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 18268c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"), 18278c2ecf20Sopenharmony_ci }, 18288c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18298c2ecf20Sopenharmony_ci .driver_data = atkbd_volume_forced_release_keys, 18308c2ecf20Sopenharmony_ci }, 18318c2ecf20Sopenharmony_ci { 18328c2ecf20Sopenharmony_ci .matches = { 18338c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 18348c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"), 18358c2ecf20Sopenharmony_ci }, 18368c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18378c2ecf20Sopenharmony_ci .driver_data = atkbd_volume_forced_release_keys, 18388c2ecf20Sopenharmony_ci }, 18398c2ecf20Sopenharmony_ci { 18408c2ecf20Sopenharmony_ci .matches = { 18418c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 18428c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"), 18438c2ecf20Sopenharmony_ci }, 18448c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18458c2ecf20Sopenharmony_ci .driver_data = atkbd_volume_forced_release_keys, 18468c2ecf20Sopenharmony_ci }, 18478c2ecf20Sopenharmony_ci { 18488c2ecf20Sopenharmony_ci .matches = { 18498c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 18508c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"), 18518c2ecf20Sopenharmony_ci }, 18528c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18538c2ecf20Sopenharmony_ci .driver_data = atkbd_volume_forced_release_keys, 18548c2ecf20Sopenharmony_ci }, 18558c2ecf20Sopenharmony_ci { 18568c2ecf20Sopenharmony_ci /* Inventec Symphony */ 18578c2ecf20Sopenharmony_ci .matches = { 18588c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), 18598c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), 18608c2ecf20Sopenharmony_ci }, 18618c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18628c2ecf20Sopenharmony_ci .driver_data = atkbd_volume_forced_release_keys, 18638c2ecf20Sopenharmony_ci }, 18648c2ecf20Sopenharmony_ci { 18658c2ecf20Sopenharmony_ci /* Samsung NC10 */ 18668c2ecf20Sopenharmony_ci .matches = { 18678c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 18688c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), 18698c2ecf20Sopenharmony_ci }, 18708c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18718c2ecf20Sopenharmony_ci .driver_data = atkbd_samsung_forced_release_keys, 18728c2ecf20Sopenharmony_ci }, 18738c2ecf20Sopenharmony_ci { 18748c2ecf20Sopenharmony_ci /* Samsung NC20 */ 18758c2ecf20Sopenharmony_ci .matches = { 18768c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 18778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "NC20"), 18788c2ecf20Sopenharmony_ci }, 18798c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18808c2ecf20Sopenharmony_ci .driver_data = atkbd_samsung_forced_release_keys, 18818c2ecf20Sopenharmony_ci }, 18828c2ecf20Sopenharmony_ci { 18838c2ecf20Sopenharmony_ci /* Samsung SQ45S70S */ 18848c2ecf20Sopenharmony_ci .matches = { 18858c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 18868c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), 18878c2ecf20Sopenharmony_ci }, 18888c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18898c2ecf20Sopenharmony_ci .driver_data = atkbd_samsung_forced_release_keys, 18908c2ecf20Sopenharmony_ci }, 18918c2ecf20Sopenharmony_ci { 18928c2ecf20Sopenharmony_ci /* Fujitsu Amilo PA 1510 */ 18938c2ecf20Sopenharmony_ci .matches = { 18948c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 18958c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"), 18968c2ecf20Sopenharmony_ci }, 18978c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 18988c2ecf20Sopenharmony_ci .driver_data = atkbd_volume_forced_release_keys, 18998c2ecf20Sopenharmony_ci }, 19008c2ecf20Sopenharmony_ci { 19018c2ecf20Sopenharmony_ci /* Fujitsu Amilo Pi 3525 */ 19028c2ecf20Sopenharmony_ci .matches = { 19038c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 19048c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"), 19058c2ecf20Sopenharmony_ci }, 19068c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 19078c2ecf20Sopenharmony_ci .driver_data = atkbd_amilo_pi3525_forced_release_keys, 19088c2ecf20Sopenharmony_ci }, 19098c2ecf20Sopenharmony_ci { 19108c2ecf20Sopenharmony_ci /* Fujitsu Amilo Xi 3650 */ 19118c2ecf20Sopenharmony_ci .matches = { 19128c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 19138c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"), 19148c2ecf20Sopenharmony_ci }, 19158c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 19168c2ecf20Sopenharmony_ci .driver_data = atkbd_amilo_xi3650_forced_release_keys, 19178c2ecf20Sopenharmony_ci }, 19188c2ecf20Sopenharmony_ci { 19198c2ecf20Sopenharmony_ci .matches = { 19208c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"), 19218c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "TA12"), 19228c2ecf20Sopenharmony_ci }, 19238c2ecf20Sopenharmony_ci .callback = atkbd_setup_forced_release, 19248c2ecf20Sopenharmony_ci .driver_data = atkdb_soltech_ta12_forced_release_keys, 19258c2ecf20Sopenharmony_ci }, 19268c2ecf20Sopenharmony_ci { 19278c2ecf20Sopenharmony_ci /* OQO Model 01+ */ 19288c2ecf20Sopenharmony_ci .matches = { 19298c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "OQO"), 19308c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), 19318c2ecf20Sopenharmony_ci }, 19328c2ecf20Sopenharmony_ci .callback = atkbd_setup_scancode_fixup, 19338c2ecf20Sopenharmony_ci .driver_data = atkbd_oqo_01plus_scancode_fixup, 19348c2ecf20Sopenharmony_ci }, 19358c2ecf20Sopenharmony_ci { 19368c2ecf20Sopenharmony_ci .matches = { 19378c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), 19388c2ecf20Sopenharmony_ci }, 19398c2ecf20Sopenharmony_ci .callback = atkbd_deactivate_fixup, 19408c2ecf20Sopenharmony_ci }, 19418c2ecf20Sopenharmony_ci { } 19428c2ecf20Sopenharmony_ci}; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic int __init atkbd_init(void) 19458c2ecf20Sopenharmony_ci{ 19468c2ecf20Sopenharmony_ci dmi_check_system(atkbd_dmi_quirk_table); 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci return serio_register_driver(&atkbd_drv); 19498c2ecf20Sopenharmony_ci} 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cistatic void __exit atkbd_exit(void) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci serio_unregister_driver(&atkbd_drv); 19548c2ecf20Sopenharmony_ci} 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_cimodule_init(atkbd_init); 19578c2ecf20Sopenharmony_cimodule_exit(atkbd_exit); 1958