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 */ 25 static 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 */ 82 static 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 140 static __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 152 static const struct hid_device_id maltron_devices[] = { 153 { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_MALTRON_KB)}, 154 { } 155 }; 156 MODULE_DEVICE_TABLE(hid, maltron_devices); 157 158 static struct hid_driver maltron_driver = { 159 .name = "maltron", 160 .id_table = maltron_devices, 161 .report_fixup = maltron_report_fixup 162 }; 163 module_hid_driver(maltron_driver); 164 165 MODULE_LICENSE("GPL");