1// SPDX-License-Identifier: GPL-2.0
2/*
3 * HID driver for Maltron L90
4 *
5 * Copyright (c) 1999 Andreas Gal
6 * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
7 * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
8 * Copyright (c) 2008 Jiri Slaby
9 * Copyright (c) 2012 David Dillow <dave@thedillows.org>
10 * Copyright (c) 2006-2013 Jiri Kosina
11 * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
12 * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
13 * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
14 * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
15 * Copyright (c) 2018 William Whistler <wtbw@wtbw.co.uk>
16 */
17
18#include <linux/device.h>
19#include <linux/hid.h>
20#include <linux/module.h>
21
22#include "hid-ids.h"
23
24/* The original buggy USB descriptor */
25static u8 maltron_rdesc_o[] = {
26 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
27 0x09, 0x80, /* Usage (Sys Control) */
28 0xA1, 0x01, /* Collection (Application) */
29 0x85, 0x02, /* Report ID (2) */
30 0x75, 0x01, /* Report Size (1) */
31 0x95, 0x01, /* Report Count (1) */
32 0x15, 0x00, /* Logical Minimum (0) */
33 0x25, 0x01, /* Logical Maximum (1) */
34 0x09, 0x82, /* Usage (Sys Sleep) */
35 0x81, 0x06, /* Input (Data,Var,Rel) */
36 0x09, 0x82, /* Usage (Sys Sleep) */
37 0x81, 0x06, /* Input (Data,Var,Rel) */
38 0x09, 0x83, /* Usage (Sys Wake Up) */
39 0x81, 0x06, /* Input (Data,Var,Rel) */
40 0x75, 0x05, /* Report Size (5) */
41 0x81, 0x01, /* Input (Const,Array,Abs) */
42 0xC0, /* End Collection */
43 0x05, 0x0C, /* Usage Page (Consumer) */
44 0x09, 0x01, /* Usage (Consumer Control) */
45 0xA1, 0x01, /* Collection (Application) */
46 0x85, 0x03, /* Report ID (3) */
47 0x95, 0x01, /* Report Count (1) */
48 0x75, 0x10, /* Report Size (16) */
49 0x19, 0x00, /* Usage Minimum (Unassigned) */
50 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
51 0x81, 0x00, /* Input (Data,Array,Abs) */
52 0xC0, /* End Collection */
53 0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */
54 0x09, 0x01, /* Usage (0x01) */
55 0xA1, 0x01, /* Collection (Application) */
56 0x85, 0x04, /* Report ID (4) */
57 0x95, 0x01, /* Report Count (1) */
58 0x75, 0x10, /* Report Size (16) */
59 0x19, 0x00, /* Usage Minimum (0x00) */
60 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
61 0x81, 0x00, /* Input (Data,Array,Abs) */
62 0x75, 0x02, /* Report Size (2) */
63 0x25, 0x02, /* Logical Maximum (2) */
64 0x09, 0x90, /* Usage (0x90) */
65 0xB1, 0x02, /* Feature (Data,Var,Abs) */
66 0x75, 0x06, /* Report Size (6) */
67 0xB1, 0x01, /* Feature (Const,Array,Abs) */
68 0x75, 0x01, /* Report Size (1) */
69 0x25, 0x01, /* Logical Maximum (1) */
70 0x05, 0x08, /* Usage Page (LEDs) */
71 0x09, 0x2A, /* Usage (On-Line) */
72 0x91, 0x02, /* Output (Data,Var,Abs) */
73 0x09, 0x4B, /* Usage (Generic Indicator) */
74 0x91, 0x02, /* Output (Data,Var,Abs) */
75 0x75, 0x06, /* Report Size (6) */
76 0x95, 0x01, /* Report Count (1) */
77 0x91, 0x01, /* Output (Const,Array,Abs) */
78 0xC0 /* End Collection */
79};
80
81/* The patched descriptor, allowing media key events to be accepted as valid */
82static u8 maltron_rdesc[] = {
83 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
84 0x09, 0x80, /* Usage (Sys Control) */
85 0xA1, 0x01, /* Collection (Application) */
86 0x85, 0x02, /* Report ID (2) */
87 0x75, 0x01, /* Report Size (1) */
88 0x95, 0x01, /* Report Count (1) */
89 0x15, 0x00, /* Logical Minimum (0) */
90 0x25, 0x01, /* Logical Maximum (1) */
91 0x09, 0x82, /* Usage (Sys Sleep) */
92 0x81, 0x06, /* Input (Data,Var,Rel) */
93 0x09, 0x82, /* Usage (Sys Sleep) */
94 0x81, 0x06, /* Input (Data,Var,Rel) */
95 0x09, 0x83, /* Usage (Sys Wake Up) */
96 0x81, 0x06, /* Input (Data,Var,Rel) */
97 0x75, 0x05, /* Report Size (5) */
98 0x81, 0x01, /* Input (Const,Array,Abs) */
99 0xC0, /* End Collection */
100 0x05, 0x0C, /* Usage Page (Consumer) */
101 0x09, 0x01, /* Usage (Consumer Control) */
102 0xA1, 0x01, /* Collection (Application) */
103 0x85, 0x03, /* Report ID (3) */
104 0x15, 0x00, /* Logical Minimum (0) - changed */
105 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) - changed */
106 0x95, 0x01, /* Report Count (1) */
107 0x75, 0x10, /* Report Size (16) */
108 0x19, 0x00, /* Usage Minimum (Unassigned) */
109 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
110 0x81, 0x00, /* Input (Data,Array,Abs) */
111 0xC0, /* End Collection */
112 0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */
113 0x09, 0x01, /* Usage (0x01) */
114 0xA1, 0x01, /* Collection (Application) */
115 0x85, 0x04, /* Report ID (4) */
116 0x95, 0x01, /* Report Count (1) */
117 0x75, 0x10, /* Report Size (16) */
118 0x19, 0x00, /* Usage Minimum (0x00) */
119 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
120 0x81, 0x00, /* Input (Data,Array,Abs) */
121 0x75, 0x02, /* Report Size (2) */
122 0x25, 0x02, /* Logical Maximum (2) */
123 0x09, 0x90, /* Usage (0x90) */
124 0xB1, 0x02, /* Feature (Data,Var,Abs) */
125 0x75, 0x06, /* Report Size (6) */
126 0xB1, 0x01, /* Feature (Const,Array,Abs) */
127 0x75, 0x01, /* Report Size (1) */
128 0x25, 0x01, /* Logical Maximum (1) */
129 0x05, 0x08, /* Usage Page (LEDs) */
130 0x09, 0x2A, /* Usage (On-Line) */
131 0x91, 0x02, /* Output (Data,Var,Abs) */
132 0x09, 0x4B, /* Usage (Generic Indicator) */
133 0x91, 0x02, /* Output (Data,Var,Abs) */
134 0x75, 0x06, /* Report Size (6) */
135 0x95, 0x01, /* Report Count (1) */
136 0x91, 0x01, /* Output (Const,Array,Abs) */
137 0xC0 /* End Collection */
138};
139
140static __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc,
141 unsigned int *rsize)
142{
143 if (*rsize == sizeof(maltron_rdesc_o) &&
144 !memcmp(maltron_rdesc_o, rdesc, sizeof(maltron_rdesc_o))) {
145 hid_info(hdev, "Replacing Maltron L90 keyboard report descriptor\n");
146 *rsize = sizeof(maltron_rdesc);
147 return maltron_rdesc;
148 }
149 return rdesc;
150}
151
152static const struct hid_device_id maltron_devices[] = {
153 { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_MALTRON_KB)},
154 { }
155};
156MODULE_DEVICE_TABLE(hid, maltron_devices);
157
158static struct hid_driver maltron_driver = {
159 .name = "maltron",
160 .id_table = maltron_devices,
161 .report_fixup = maltron_report_fixup
162};
163module_hid_driver(maltron_driver);
164
165MODULE_LICENSE("GPL");
166