Skip to content

Commit 8f7b057

Browse files
Rajat Jaindtor
authored andcommitted
Input: atkbd - expose function row physical map to userspace
Certain keyboards have their top-row keys intended for actions such as "Browser back", "Browser Refresh", "Fullscreen" etc as their primary mode, thus they will send scan codes for those actions. Further, they don't have a dedicated "Fn" key so don't have the capability to generate function key codes (e.g. F1, F2 etc..). However in this case, if userspace still wants to "synthesize" those function keys using the top row action keys, it needs to know the physical position of the top row keys. (Essentially a mapping between usage codes and a physical location in the top row). This patch enhances the atkbd driver to receive such a mapping from the firmware / device tree, and expose it to userspace in the form of a function-row-physmap attribute. The attribute would be a space separated ordered list of physical codes, for the keys in the function row, in left-to-right order. The attribute will only be present if the kernel knows about such mapping, otherwise the attribute shall not be visible. Signed-off-by: Rajat Jain <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Dmitry Torokhov <[email protected]>
1 parent 6052abf commit 8f7b057

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

drivers/input/keyboard/atkbd.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/libps2.h>
2525
#include <linux/mutex.h>
2626
#include <linux/dmi.h>
27+
#include <linux/property.h>
2728

2829
#define DRIVER_DESC "AT and PS/2 keyboard driver"
2930

@@ -63,6 +64,8 @@ static bool atkbd_terminal;
6364
module_param_named(terminal, atkbd_terminal, bool, 0);
6465
MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2");
6566

67+
#define MAX_FUNCTION_ROW_KEYS 24
68+
6669
/*
6770
* Scancode to keycode tables. These are just the default setting, and
6871
* are loadable via a userland utility.
@@ -230,6 +233,9 @@ struct atkbd {
230233

231234
/* Serializes reconnect(), attr->set() and event work */
232235
struct mutex mutex;
236+
237+
u32 function_row_physmap[MAX_FUNCTION_ROW_KEYS];
238+
int num_function_row_keys;
233239
};
234240

235241
/*
@@ -283,6 +289,7 @@ static struct device_attribute atkbd_attr_##_name = \
283289
__ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL);
284290

285291
ATKBD_DEFINE_RO_ATTR(err_count);
292+
ATKBD_DEFINE_RO_ATTR(function_row_physmap);
286293

287294
static struct attribute *atkbd_attributes[] = {
288295
&atkbd_attr_extra.attr,
@@ -292,11 +299,42 @@ static struct attribute *atkbd_attributes[] = {
292299
&atkbd_attr_softrepeat.attr,
293300
&atkbd_attr_softraw.attr,
294301
&atkbd_attr_err_count.attr,
302+
&atkbd_attr_function_row_physmap.attr,
295303
NULL
296304
};
297305

306+
static ssize_t atkbd_show_function_row_physmap(struct atkbd *atkbd, char *buf)
307+
{
308+
ssize_t size = 0;
309+
int i;
310+
311+
if (!atkbd->num_function_row_keys)
312+
return 0;
313+
314+
for (i = 0; i < atkbd->num_function_row_keys; i++)
315+
size += scnprintf(buf + size, PAGE_SIZE - size, "%02X ",
316+
atkbd->function_row_physmap[i]);
317+
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
318+
return size;
319+
}
320+
321+
static umode_t atkbd_attr_is_visible(struct kobject *kobj,
322+
struct attribute *attr, int i)
323+
{
324+
struct device *dev = container_of(kobj, struct device, kobj);
325+
struct serio *serio = to_serio_port(dev);
326+
struct atkbd *atkbd = serio_get_drvdata(serio);
327+
328+
if (attr == &atkbd_attr_function_row_physmap.attr &&
329+
!atkbd->num_function_row_keys)
330+
return 0;
331+
332+
return attr->mode;
333+
}
334+
298335
static struct attribute_group atkbd_attribute_group = {
299336
.attrs = atkbd_attributes,
337+
.is_visible = atkbd_attr_is_visible,
300338
};
301339

302340
static const unsigned int xl_table[] = {
@@ -1121,6 +1159,22 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
11211159
}
11221160
}
11231161

1162+
static void atkbd_parse_fwnode_data(struct serio *serio)
1163+
{
1164+
struct atkbd *atkbd = serio_get_drvdata(serio);
1165+
struct device *dev = &serio->dev;
1166+
int n;
1167+
1168+
/* Parse "function-row-physmap" property */
1169+
n = device_property_count_u32(dev, "function-row-physmap");
1170+
if (n > 0 && n <= MAX_FUNCTION_ROW_KEYS &&
1171+
!device_property_read_u32_array(dev, "function-row-physmap",
1172+
atkbd->function_row_physmap, n)) {
1173+
atkbd->num_function_row_keys = n;
1174+
dev_dbg(dev, "FW reported %d function-row key locations\n", n);
1175+
}
1176+
}
1177+
11241178
/*
11251179
* atkbd_connect() is called when the serio module finds an interface
11261180
* that isn't handled yet by an appropriate device driver. We check if
@@ -1184,6 +1238,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
11841238
atkbd->id = 0xab00;
11851239
}
11861240

1241+
atkbd_parse_fwnode_data(serio);
1242+
11871243
atkbd_set_keycode_table(atkbd);
11881244
atkbd_set_device_attrs(atkbd);
11891245

0 commit comments

Comments
 (0)