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>
This commit is contained in:
Philip Chen 2021-02-22 21:05:04 -08:00 коммит произвёл Dmitry Torokhov
Родитель 311a27da62
Коммит 820c872795
2 изменённых файлов: 85 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,6 @@
What: /sys/class/input/input(x)/device/function_row_physmap
Date: January 2021
Contact: Philip Chen <philipchen@chromium.org>
Description: A space separated list of scancodes for the top row keys,
ordered by the physical positions of the keys, from left
to right.

Просмотреть файл

@ -27,6 +27,8 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#define MAX_NUM_TOP_ROW_KEYS 15
/** /**
* struct cros_ec_keyb - Structure representing EC keyboard device * struct cros_ec_keyb - Structure representing EC keyboard device
* *
@ -42,6 +44,9 @@
* @idev: The input device for the matrix keys. * @idev: The input device for the matrix keys.
* @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @bs_idev: The input device for non-matrix buttons and switches (or NULL).
* @notifier: interrupt event notifier for transport devices * @notifier: interrupt event notifier for transport devices
* @function_row_physmap: An array of the encoded rows/columns for the top
* row function keys, in an order from left to right
* @num_function_row_keys: The number of top row keys in a custom keyboard
*/ */
struct cros_ec_keyb { struct cros_ec_keyb {
unsigned int rows; unsigned int rows;
@ -58,6 +63,9 @@ struct cros_ec_keyb {
struct input_dev *idev; struct input_dev *idev;
struct input_dev *bs_idev; struct input_dev *bs_idev;
struct notifier_block notifier; struct notifier_block notifier;
u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS];
size_t num_function_row_keys;
}; };
/** /**
@ -527,6 +535,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
struct input_dev *idev; struct input_dev *idev;
const char *phys; const char *phys;
int err; int err;
struct property *prop;
const __be32 *p;
u16 *physmap;
u32 key_pos;
int row, col;
err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols);
if (err) if (err)
@ -578,6 +591,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
ckdev->idev = idev; ckdev->idev = idev;
cros_ec_keyb_compute_valid_keys(ckdev); cros_ec_keyb_compute_valid_keys(ckdev);
physmap = ckdev->function_row_physmap;
of_property_for_each_u32(dev->of_node, "function-row-physmap",
prop, p, key_pos) {
if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) {
dev_warn(dev, "Only support up to %d top row keys\n",
MAX_NUM_TOP_ROW_KEYS);
break;
}
row = KEY_ROW(key_pos);
col = KEY_COL(key_pos);
*physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
physmap++;
ckdev->num_function_row_keys++;
}
err = input_register_device(ckdev->idev); err = input_register_device(ckdev->idev);
if (err) { if (err) {
dev_err(dev, "cannot register input device\n"); dev_err(dev, "cannot register input device\n");
@ -587,6 +615,51 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
return 0; return 0;
} }
static ssize_t function_row_physmap_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t size = 0;
int i;
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
u16 *physmap = ckdev->function_row_physmap;
for (i = 0; i < ckdev->num_function_row_keys; i++)
size += scnprintf(buf + size, PAGE_SIZE - size,
"%s%02X", size ? " " : "", physmap[i]);
if (size)
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
return size;
}
static DEVICE_ATTR_RO(function_row_physmap);
static struct attribute *cros_ec_keyb_attrs[] = {
&dev_attr_function_row_physmap.attr,
NULL,
};
static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj,
struct attribute *attr,
int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
if (attr == &dev_attr_function_row_physmap.attr &&
!ckdev->num_function_row_keys)
return 0;
return attr->mode;
}
static const struct attribute_group cros_ec_keyb_attr_group = {
.is_visible = cros_ec_keyb_attr_is_visible,
.attrs = cros_ec_keyb_attrs,
};
static int cros_ec_keyb_probe(struct platform_device *pdev) static int cros_ec_keyb_probe(struct platform_device *pdev)
{ {
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
@ -617,6 +690,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
return err; return err;
} }
err = devm_device_add_group(dev, &cros_ec_keyb_attr_group);
if (err) {
dev_err(dev, "failed to create attributes. err=%d\n", err);
return err;
}
ckdev->notifier.notifier_call = cros_ec_keyb_work; ckdev->notifier.notifier_call = cros_ec_keyb_work;
err = blocking_notifier_chain_register(&ckdev->ec->event_notifier, err = blocking_notifier_chain_register(&ckdev->ec->event_notifier,
&ckdev->notifier); &ckdev->notifier);