| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * AT and PS/2 keyboard driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 1999-2002 Vojtech Pavlik |
|---|
| 5 | 6 | */ |
|---|
| 6 | 7 | |
|---|
| 7 | | -/* |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 9 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 10 | | - * the Free Software Foundation. |
|---|
| 11 | | - */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | /* |
|---|
| 14 | 10 | * This driver can handle standard AT keyboards and PS/2 keyboards in |
|---|
| .. | .. |
|---|
| 28 | 24 | #include <linux/libps2.h> |
|---|
| 29 | 25 | #include <linux/mutex.h> |
|---|
| 30 | 26 | #include <linux/dmi.h> |
|---|
| 27 | +#include <linux/property.h> |
|---|
| 31 | 28 | |
|---|
| 32 | 29 | #define DRIVER_DESC "AT and PS/2 keyboard driver" |
|---|
| 33 | 30 | |
|---|
| .. | .. |
|---|
| 66 | 63 | static bool atkbd_terminal; |
|---|
| 67 | 64 | module_param_named(terminal, atkbd_terminal, bool, 0); |
|---|
| 68 | 65 | MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); |
|---|
| 66 | + |
|---|
| 67 | +#define MAX_FUNCTION_ROW_KEYS 24 |
|---|
| 68 | + |
|---|
| 69 | +#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF) |
|---|
| 70 | +#define KEYCODE(keymap) (keymap & 0xFFFF) |
|---|
| 69 | 71 | |
|---|
| 70 | 72 | /* |
|---|
| 71 | 73 | * Scancode to keycode tables. These are just the default setting, and |
|---|
| .. | .. |
|---|
| 234 | 236 | |
|---|
| 235 | 237 | /* Serializes reconnect(), attr->set() and event work */ |
|---|
| 236 | 238 | struct mutex mutex; |
|---|
| 239 | + |
|---|
| 240 | + u32 function_row_physmap[MAX_FUNCTION_ROW_KEYS]; |
|---|
| 241 | + int num_function_row_keys; |
|---|
| 237 | 242 | }; |
|---|
| 238 | 243 | |
|---|
| 239 | 244 | /* |
|---|
| .. | .. |
|---|
| 287 | 292 | __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL); |
|---|
| 288 | 293 | |
|---|
| 289 | 294 | ATKBD_DEFINE_RO_ATTR(err_count); |
|---|
| 295 | +ATKBD_DEFINE_RO_ATTR(function_row_physmap); |
|---|
| 290 | 296 | |
|---|
| 291 | 297 | static struct attribute *atkbd_attributes[] = { |
|---|
| 292 | 298 | &atkbd_attr_extra.attr, |
|---|
| .. | .. |
|---|
| 296 | 302 | &atkbd_attr_softrepeat.attr, |
|---|
| 297 | 303 | &atkbd_attr_softraw.attr, |
|---|
| 298 | 304 | &atkbd_attr_err_count.attr, |
|---|
| 305 | + &atkbd_attr_function_row_physmap.attr, |
|---|
| 299 | 306 | NULL |
|---|
| 300 | 307 | }; |
|---|
| 301 | 308 | |
|---|
| 309 | +static ssize_t atkbd_show_function_row_physmap(struct atkbd *atkbd, char *buf) |
|---|
| 310 | +{ |
|---|
| 311 | + ssize_t size = 0; |
|---|
| 312 | + int i; |
|---|
| 313 | + |
|---|
| 314 | + if (!atkbd->num_function_row_keys) |
|---|
| 315 | + return 0; |
|---|
| 316 | + |
|---|
| 317 | + for (i = 0; i < atkbd->num_function_row_keys; i++) |
|---|
| 318 | + size += scnprintf(buf + size, PAGE_SIZE - size, "%02X ", |
|---|
| 319 | + atkbd->function_row_physmap[i]); |
|---|
| 320 | + size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); |
|---|
| 321 | + return size; |
|---|
| 322 | +} |
|---|
| 323 | + |
|---|
| 324 | +static umode_t atkbd_attr_is_visible(struct kobject *kobj, |
|---|
| 325 | + struct attribute *attr, int i) |
|---|
| 326 | +{ |
|---|
| 327 | + struct device *dev = container_of(kobj, struct device, kobj); |
|---|
| 328 | + struct serio *serio = to_serio_port(dev); |
|---|
| 329 | + struct atkbd *atkbd = serio_get_drvdata(serio); |
|---|
| 330 | + |
|---|
| 331 | + if (attr == &atkbd_attr_function_row_physmap.attr && |
|---|
| 332 | + !atkbd->num_function_row_keys) |
|---|
| 333 | + return 0; |
|---|
| 334 | + |
|---|
| 335 | + return attr->mode; |
|---|
| 336 | +} |
|---|
| 337 | + |
|---|
| 302 | 338 | static struct attribute_group atkbd_attribute_group = { |
|---|
| 303 | 339 | .attrs = atkbd_attributes, |
|---|
| 340 | + .is_visible = atkbd_attr_is_visible, |
|---|
| 304 | 341 | }; |
|---|
| 305 | 342 | |
|---|
| 306 | 343 | static const unsigned int xl_table[] = { |
|---|
| .. | .. |
|---|
| 400 | 437 | if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD)) |
|---|
| 401 | 438 | if (ps2_handle_response(&atkbd->ps2dev, data)) |
|---|
| 402 | 439 | goto out; |
|---|
| 440 | + |
|---|
| 441 | + pm_wakeup_event(&serio->dev, 0); |
|---|
| 403 | 442 | |
|---|
| 404 | 443 | if (!atkbd->enabled) |
|---|
| 405 | 444 | goto out; |
|---|
| .. | .. |
|---|
| 841 | 880 | if (param[0] != 3) { |
|---|
| 842 | 881 | param[0] = 2; |
|---|
| 843 | 882 | if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET)) |
|---|
| 844 | | - return 2; |
|---|
| 883 | + return 2; |
|---|
| 845 | 884 | } |
|---|
| 846 | 885 | |
|---|
| 847 | 886 | ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR); |
|---|
| .. | .. |
|---|
| 996 | 1035 | return code; |
|---|
| 997 | 1036 | } |
|---|
| 998 | 1037 | |
|---|
| 1038 | +static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd) |
|---|
| 1039 | +{ |
|---|
| 1040 | + struct device *dev = &atkbd->ps2dev.serio->dev; |
|---|
| 1041 | + int i, n; |
|---|
| 1042 | + u32 *ptr; |
|---|
| 1043 | + u16 scancode, keycode; |
|---|
| 1044 | + |
|---|
| 1045 | + /* Parse "linux,keymap" property */ |
|---|
| 1046 | + n = device_property_count_u32(dev, "linux,keymap"); |
|---|
| 1047 | + if (n <= 0 || n > ATKBD_KEYMAP_SIZE) |
|---|
| 1048 | + return -ENXIO; |
|---|
| 1049 | + |
|---|
| 1050 | + ptr = kcalloc(n, sizeof(u32), GFP_KERNEL); |
|---|
| 1051 | + if (!ptr) |
|---|
| 1052 | + return -ENOMEM; |
|---|
| 1053 | + |
|---|
| 1054 | + if (device_property_read_u32_array(dev, "linux,keymap", ptr, n)) { |
|---|
| 1055 | + dev_err(dev, "problem parsing FW keymap property\n"); |
|---|
| 1056 | + kfree(ptr); |
|---|
| 1057 | + return -EINVAL; |
|---|
| 1058 | + } |
|---|
| 1059 | + |
|---|
| 1060 | + memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); |
|---|
| 1061 | + for (i = 0; i < n; i++) { |
|---|
| 1062 | + scancode = SCANCODE(ptr[i]); |
|---|
| 1063 | + keycode = KEYCODE(ptr[i]); |
|---|
| 1064 | + atkbd->keycode[scancode] = keycode; |
|---|
| 1065 | + } |
|---|
| 1066 | + |
|---|
| 1067 | + kfree(ptr); |
|---|
| 1068 | + return 0; |
|---|
| 1069 | +} |
|---|
| 1070 | + |
|---|
| 999 | 1071 | /* |
|---|
| 1000 | 1072 | * atkbd_set_keycode_table() initializes keyboard's keycode table |
|---|
| 1001 | 1073 | * according to the selected scancode set |
|---|
| .. | .. |
|---|
| 1003 | 1075 | |
|---|
| 1004 | 1076 | static void atkbd_set_keycode_table(struct atkbd *atkbd) |
|---|
| 1005 | 1077 | { |
|---|
| 1078 | + struct device *dev = &atkbd->ps2dev.serio->dev; |
|---|
| 1006 | 1079 | unsigned int scancode; |
|---|
| 1007 | 1080 | int i, j; |
|---|
| 1008 | 1081 | |
|---|
| 1009 | 1082 | memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); |
|---|
| 1010 | 1083 | bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); |
|---|
| 1011 | 1084 | |
|---|
| 1012 | | - if (atkbd->translated) { |
|---|
| 1085 | + if (!atkbd_get_keymap_from_fwnode(atkbd)) { |
|---|
| 1086 | + dev_dbg(dev, "Using FW keymap\n"); |
|---|
| 1087 | + } else if (atkbd->translated) { |
|---|
| 1013 | 1088 | for (i = 0; i < 128; i++) { |
|---|
| 1014 | 1089 | scancode = atkbd_unxlate_table[i]; |
|---|
| 1015 | 1090 | atkbd->keycode[i] = atkbd_set2_keycode[scancode]; |
|---|
| .. | .. |
|---|
| 1123 | 1198 | } |
|---|
| 1124 | 1199 | } |
|---|
| 1125 | 1200 | |
|---|
| 1201 | +static void atkbd_parse_fwnode_data(struct serio *serio) |
|---|
| 1202 | +{ |
|---|
| 1203 | + struct atkbd *atkbd = serio_get_drvdata(serio); |
|---|
| 1204 | + struct device *dev = &serio->dev; |
|---|
| 1205 | + int n; |
|---|
| 1206 | + |
|---|
| 1207 | + /* Parse "function-row-physmap" property */ |
|---|
| 1208 | + n = device_property_count_u32(dev, "function-row-physmap"); |
|---|
| 1209 | + if (n > 0 && n <= MAX_FUNCTION_ROW_KEYS && |
|---|
| 1210 | + !device_property_read_u32_array(dev, "function-row-physmap", |
|---|
| 1211 | + atkbd->function_row_physmap, n)) { |
|---|
| 1212 | + atkbd->num_function_row_keys = n; |
|---|
| 1213 | + dev_dbg(dev, "FW reported %d function-row key locations\n", n); |
|---|
| 1214 | + } |
|---|
| 1215 | +} |
|---|
| 1216 | + |
|---|
| 1126 | 1217 | /* |
|---|
| 1127 | 1218 | * atkbd_connect() is called when the serio module finds an interface |
|---|
| 1128 | 1219 | * that isn't handled yet by an appropriate device driver. We check if |
|---|
| .. | .. |
|---|
| 1150 | 1241 | |
|---|
| 1151 | 1242 | case SERIO_8042_XL: |
|---|
| 1152 | 1243 | atkbd->translated = true; |
|---|
| 1153 | | - /* Fall through */ |
|---|
| 1244 | + fallthrough; |
|---|
| 1154 | 1245 | |
|---|
| 1155 | 1246 | case SERIO_8042: |
|---|
| 1156 | 1247 | if (serio->write) |
|---|
| .. | .. |
|---|
| 1186 | 1277 | atkbd->id = 0xab00; |
|---|
| 1187 | 1278 | } |
|---|
| 1188 | 1279 | |
|---|
| 1280 | + atkbd_parse_fwnode_data(serio); |
|---|
| 1281 | + |
|---|
| 1189 | 1282 | atkbd_set_keycode_table(atkbd); |
|---|
| 1190 | 1283 | atkbd_set_device_attrs(atkbd); |
|---|
| 1191 | 1284 | |
|---|