Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: cros-ec-keyb - expose function row physical map to userspace

The top-row keys in a keyboard usually have dual functionalities.
E.g. A function key "F1" is also an action key "Browser back".

Therefore, when an application receives an action key code from
a top-row key press, the application needs to know how to correlate
the action key code with the function key code and do the conversion
whenever necessary.

Since the userpace already knows the key scanlines (row/column)
associated with a received key code. Essentially, the userspace only
needs a mapping between the key row/column and the matching physical
location in the top row.

So, enhance the cros-ec-keyb driver to create such a mapping
and expose it to userspace in the form of a function_row_physmap
attribute. The attribute would be a space separated ordered list of
row/column codes for the keys in the function row, in a left-to-right
order.

The attribute will only be present when the device has a custom design
for the top-row keys.

Signed-off-by: Philip Chen <philipchen@chromium.org>
Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Link: https://lore.kernel.org/r/20210115122412.v7.2.I6542d7d9d0b246e7079bb16b41e697b2ac4b4e39@changeid
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Philip Chen and committed by
Dmitry Torokhov
820c8727 311a27da

+85
+6
Documentation/ABI/testing/sysfs-driver-input-cros-ec-keyb
··· 1 + What: /sys/class/input/input(x)/device/function_row_physmap 2 + Date: January 2021 3 + Contact: Philip Chen <philipchen@chromium.org> 4 + Description: A space separated list of scancodes for the top row keys, 5 + ordered by the physical positions of the keys, from left 6 + to right.
+79
drivers/input/keyboard/cros_ec_keyb.c
··· 27 27 28 28 #include <asm/unaligned.h> 29 29 30 + #define MAX_NUM_TOP_ROW_KEYS 15 31 + 30 32 /** 31 33 * struct cros_ec_keyb - Structure representing EC keyboard device 32 34 * ··· 44 42 * @idev: The input device for the matrix keys. 45 43 * @bs_idev: The input device for non-matrix buttons and switches (or NULL). 46 44 * @notifier: interrupt event notifier for transport devices 45 + * @function_row_physmap: An array of the encoded rows/columns for the top 46 + * row function keys, in an order from left to right 47 + * @num_function_row_keys: The number of top row keys in a custom keyboard 47 48 */ 48 49 struct cros_ec_keyb { 49 50 unsigned int rows; ··· 63 58 struct input_dev *idev; 64 59 struct input_dev *bs_idev; 65 60 struct notifier_block notifier; 61 + 62 + u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS]; 63 + size_t num_function_row_keys; 66 64 }; 67 65 68 66 /** ··· 535 527 struct input_dev *idev; 536 528 const char *phys; 537 529 int err; 530 + struct property *prop; 531 + const __be32 *p; 532 + u16 *physmap; 533 + u32 key_pos; 534 + int row, col; 538 535 539 536 err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); 540 537 if (err) ··· 591 578 ckdev->idev = idev; 592 579 cros_ec_keyb_compute_valid_keys(ckdev); 593 580 581 + physmap = ckdev->function_row_physmap; 582 + of_property_for_each_u32(dev->of_node, "function-row-physmap", 583 + prop, p, key_pos) { 584 + if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) { 585 + dev_warn(dev, "Only support up to %d top row keys\n", 586 + MAX_NUM_TOP_ROW_KEYS); 587 + break; 588 + } 589 + row = KEY_ROW(key_pos); 590 + col = KEY_COL(key_pos); 591 + *physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); 592 + physmap++; 593 + ckdev->num_function_row_keys++; 594 + } 595 + 594 596 err = input_register_device(ckdev->idev); 595 597 if (err) { 596 598 dev_err(dev, "cannot register input device\n"); ··· 614 586 615 587 return 0; 616 588 } 589 + 590 + static ssize_t function_row_physmap_show(struct device *dev, 591 + struct device_attribute *attr, 592 + char *buf) 593 + { 594 + ssize_t size = 0; 595 + int i; 596 + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); 597 + u16 *physmap = ckdev->function_row_physmap; 598 + 599 + for (i = 0; i < ckdev->num_function_row_keys; i++) 600 + size += scnprintf(buf + size, PAGE_SIZE - size, 601 + "%s%02X", size ? " " : "", physmap[i]); 602 + if (size) 603 + size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); 604 + 605 + return size; 606 + } 607 + 608 + static DEVICE_ATTR_RO(function_row_physmap); 609 + 610 + static struct attribute *cros_ec_keyb_attrs[] = { 611 + &dev_attr_function_row_physmap.attr, 612 + NULL, 613 + }; 614 + 615 + static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj, 616 + struct attribute *attr, 617 + int n) 618 + { 619 + struct device *dev = container_of(kobj, struct device, kobj); 620 + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); 621 + 622 + if (attr == &dev_attr_function_row_physmap.attr && 623 + !ckdev->num_function_row_keys) 624 + return 0; 625 + 626 + return attr->mode; 627 + } 628 + 629 + static const struct attribute_group cros_ec_keyb_attr_group = { 630 + .is_visible = cros_ec_keyb_attr_is_visible, 631 + .attrs = cros_ec_keyb_attrs, 632 + }; 633 + 617 634 618 635 static int cros_ec_keyb_probe(struct platform_device *pdev) 619 636 { ··· 687 614 err = cros_ec_keyb_register_bs(ckdev); 688 615 if (err) { 689 616 dev_err(dev, "cannot register non-matrix inputs: %d\n", err); 617 + return err; 618 + } 619 + 620 + err = devm_device_add_group(dev, &cros_ec_keyb_attr_group); 621 + if (err) { 622 + dev_err(dev, "failed to create attributes. err=%d\n", err); 690 623 return err; 691 624 } 692 625