162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for Maltron L90 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 1999 Andreas Gal 662306a36Sopenharmony_ci * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 762306a36Sopenharmony_ci * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 862306a36Sopenharmony_ci * Copyright (c) 2008 Jiri Slaby 962306a36Sopenharmony_ci * Copyright (c) 2012 David Dillow <dave@thedillows.org> 1062306a36Sopenharmony_ci * Copyright (c) 2006-2013 Jiri Kosina 1162306a36Sopenharmony_ci * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> 1262306a36Sopenharmony_ci * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com> 1362306a36Sopenharmony_ci * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com> 1462306a36Sopenharmony_ci * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com> 1562306a36Sopenharmony_ci * Copyright (c) 2018 William Whistler <wtbw@wtbw.co.uk> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/hid.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "hid-ids.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* The original buggy USB descriptor */ 2562306a36Sopenharmony_cistatic u8 maltron_rdesc_o[] = { 2662306a36Sopenharmony_ci 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 2762306a36Sopenharmony_ci 0x09, 0x80, /* Usage (Sys Control) */ 2862306a36Sopenharmony_ci 0xA1, 0x01, /* Collection (Application) */ 2962306a36Sopenharmony_ci 0x85, 0x02, /* Report ID (2) */ 3062306a36Sopenharmony_ci 0x75, 0x01, /* Report Size (1) */ 3162306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 3262306a36Sopenharmony_ci 0x15, 0x00, /* Logical Minimum (0) */ 3362306a36Sopenharmony_ci 0x25, 0x01, /* Logical Maximum (1) */ 3462306a36Sopenharmony_ci 0x09, 0x82, /* Usage (Sys Sleep) */ 3562306a36Sopenharmony_ci 0x81, 0x06, /* Input (Data,Var,Rel) */ 3662306a36Sopenharmony_ci 0x09, 0x82, /* Usage (Sys Sleep) */ 3762306a36Sopenharmony_ci 0x81, 0x06, /* Input (Data,Var,Rel) */ 3862306a36Sopenharmony_ci 0x09, 0x83, /* Usage (Sys Wake Up) */ 3962306a36Sopenharmony_ci 0x81, 0x06, /* Input (Data,Var,Rel) */ 4062306a36Sopenharmony_ci 0x75, 0x05, /* Report Size (5) */ 4162306a36Sopenharmony_ci 0x81, 0x01, /* Input (Const,Array,Abs) */ 4262306a36Sopenharmony_ci 0xC0, /* End Collection */ 4362306a36Sopenharmony_ci 0x05, 0x0C, /* Usage Page (Consumer) */ 4462306a36Sopenharmony_ci 0x09, 0x01, /* Usage (Consumer Control) */ 4562306a36Sopenharmony_ci 0xA1, 0x01, /* Collection (Application) */ 4662306a36Sopenharmony_ci 0x85, 0x03, /* Report ID (3) */ 4762306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 4862306a36Sopenharmony_ci 0x75, 0x10, /* Report Size (16) */ 4962306a36Sopenharmony_ci 0x19, 0x00, /* Usage Minimum (Unassigned) */ 5062306a36Sopenharmony_ci 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ 5162306a36Sopenharmony_ci 0x81, 0x00, /* Input (Data,Array,Abs) */ 5262306a36Sopenharmony_ci 0xC0, /* End Collection */ 5362306a36Sopenharmony_ci 0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */ 5462306a36Sopenharmony_ci 0x09, 0x01, /* Usage (0x01) */ 5562306a36Sopenharmony_ci 0xA1, 0x01, /* Collection (Application) */ 5662306a36Sopenharmony_ci 0x85, 0x04, /* Report ID (4) */ 5762306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 5862306a36Sopenharmony_ci 0x75, 0x10, /* Report Size (16) */ 5962306a36Sopenharmony_ci 0x19, 0x00, /* Usage Minimum (0x00) */ 6062306a36Sopenharmony_ci 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ 6162306a36Sopenharmony_ci 0x81, 0x00, /* Input (Data,Array,Abs) */ 6262306a36Sopenharmony_ci 0x75, 0x02, /* Report Size (2) */ 6362306a36Sopenharmony_ci 0x25, 0x02, /* Logical Maximum (2) */ 6462306a36Sopenharmony_ci 0x09, 0x90, /* Usage (0x90) */ 6562306a36Sopenharmony_ci 0xB1, 0x02, /* Feature (Data,Var,Abs) */ 6662306a36Sopenharmony_ci 0x75, 0x06, /* Report Size (6) */ 6762306a36Sopenharmony_ci 0xB1, 0x01, /* Feature (Const,Array,Abs) */ 6862306a36Sopenharmony_ci 0x75, 0x01, /* Report Size (1) */ 6962306a36Sopenharmony_ci 0x25, 0x01, /* Logical Maximum (1) */ 7062306a36Sopenharmony_ci 0x05, 0x08, /* Usage Page (LEDs) */ 7162306a36Sopenharmony_ci 0x09, 0x2A, /* Usage (On-Line) */ 7262306a36Sopenharmony_ci 0x91, 0x02, /* Output (Data,Var,Abs) */ 7362306a36Sopenharmony_ci 0x09, 0x4B, /* Usage (Generic Indicator) */ 7462306a36Sopenharmony_ci 0x91, 0x02, /* Output (Data,Var,Abs) */ 7562306a36Sopenharmony_ci 0x75, 0x06, /* Report Size (6) */ 7662306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 7762306a36Sopenharmony_ci 0x91, 0x01, /* Output (Const,Array,Abs) */ 7862306a36Sopenharmony_ci 0xC0 /* End Collection */ 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* The patched descriptor, allowing media key events to be accepted as valid */ 8262306a36Sopenharmony_cistatic u8 maltron_rdesc[] = { 8362306a36Sopenharmony_ci 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 8462306a36Sopenharmony_ci 0x09, 0x80, /* Usage (Sys Control) */ 8562306a36Sopenharmony_ci 0xA1, 0x01, /* Collection (Application) */ 8662306a36Sopenharmony_ci 0x85, 0x02, /* Report ID (2) */ 8762306a36Sopenharmony_ci 0x75, 0x01, /* Report Size (1) */ 8862306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 8962306a36Sopenharmony_ci 0x15, 0x00, /* Logical Minimum (0) */ 9062306a36Sopenharmony_ci 0x25, 0x01, /* Logical Maximum (1) */ 9162306a36Sopenharmony_ci 0x09, 0x82, /* Usage (Sys Sleep) */ 9262306a36Sopenharmony_ci 0x81, 0x06, /* Input (Data,Var,Rel) */ 9362306a36Sopenharmony_ci 0x09, 0x82, /* Usage (Sys Sleep) */ 9462306a36Sopenharmony_ci 0x81, 0x06, /* Input (Data,Var,Rel) */ 9562306a36Sopenharmony_ci 0x09, 0x83, /* Usage (Sys Wake Up) */ 9662306a36Sopenharmony_ci 0x81, 0x06, /* Input (Data,Var,Rel) */ 9762306a36Sopenharmony_ci 0x75, 0x05, /* Report Size (5) */ 9862306a36Sopenharmony_ci 0x81, 0x01, /* Input (Const,Array,Abs) */ 9962306a36Sopenharmony_ci 0xC0, /* End Collection */ 10062306a36Sopenharmony_ci 0x05, 0x0C, /* Usage Page (Consumer) */ 10162306a36Sopenharmony_ci 0x09, 0x01, /* Usage (Consumer Control) */ 10262306a36Sopenharmony_ci 0xA1, 0x01, /* Collection (Application) */ 10362306a36Sopenharmony_ci 0x85, 0x03, /* Report ID (3) */ 10462306a36Sopenharmony_ci 0x15, 0x00, /* Logical Minimum (0) - changed */ 10562306a36Sopenharmony_ci 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) - changed */ 10662306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 10762306a36Sopenharmony_ci 0x75, 0x10, /* Report Size (16) */ 10862306a36Sopenharmony_ci 0x19, 0x00, /* Usage Minimum (Unassigned) */ 10962306a36Sopenharmony_ci 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ 11062306a36Sopenharmony_ci 0x81, 0x00, /* Input (Data,Array,Abs) */ 11162306a36Sopenharmony_ci 0xC0, /* End Collection */ 11262306a36Sopenharmony_ci 0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */ 11362306a36Sopenharmony_ci 0x09, 0x01, /* Usage (0x01) */ 11462306a36Sopenharmony_ci 0xA1, 0x01, /* Collection (Application) */ 11562306a36Sopenharmony_ci 0x85, 0x04, /* Report ID (4) */ 11662306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 11762306a36Sopenharmony_ci 0x75, 0x10, /* Report Size (16) */ 11862306a36Sopenharmony_ci 0x19, 0x00, /* Usage Minimum (0x00) */ 11962306a36Sopenharmony_ci 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ 12062306a36Sopenharmony_ci 0x81, 0x00, /* Input (Data,Array,Abs) */ 12162306a36Sopenharmony_ci 0x75, 0x02, /* Report Size (2) */ 12262306a36Sopenharmony_ci 0x25, 0x02, /* Logical Maximum (2) */ 12362306a36Sopenharmony_ci 0x09, 0x90, /* Usage (0x90) */ 12462306a36Sopenharmony_ci 0xB1, 0x02, /* Feature (Data,Var,Abs) */ 12562306a36Sopenharmony_ci 0x75, 0x06, /* Report Size (6) */ 12662306a36Sopenharmony_ci 0xB1, 0x01, /* Feature (Const,Array,Abs) */ 12762306a36Sopenharmony_ci 0x75, 0x01, /* Report Size (1) */ 12862306a36Sopenharmony_ci 0x25, 0x01, /* Logical Maximum (1) */ 12962306a36Sopenharmony_ci 0x05, 0x08, /* Usage Page (LEDs) */ 13062306a36Sopenharmony_ci 0x09, 0x2A, /* Usage (On-Line) */ 13162306a36Sopenharmony_ci 0x91, 0x02, /* Output (Data,Var,Abs) */ 13262306a36Sopenharmony_ci 0x09, 0x4B, /* Usage (Generic Indicator) */ 13362306a36Sopenharmony_ci 0x91, 0x02, /* Output (Data,Var,Abs) */ 13462306a36Sopenharmony_ci 0x75, 0x06, /* Report Size (6) */ 13562306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 13662306a36Sopenharmony_ci 0x91, 0x01, /* Output (Const,Array,Abs) */ 13762306a36Sopenharmony_ci 0xC0 /* End Collection */ 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc, 14162306a36Sopenharmony_ci unsigned int *rsize) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci if (*rsize == sizeof(maltron_rdesc_o) && 14462306a36Sopenharmony_ci !memcmp(maltron_rdesc_o, rdesc, sizeof(maltron_rdesc_o))) { 14562306a36Sopenharmony_ci hid_info(hdev, "Replacing Maltron L90 keyboard report descriptor\n"); 14662306a36Sopenharmony_ci *rsize = sizeof(maltron_rdesc); 14762306a36Sopenharmony_ci return maltron_rdesc; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci return rdesc; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct hid_device_id maltron_devices[] = { 15362306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_MALTRON_KB)}, 15462306a36Sopenharmony_ci { } 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, maltron_devices); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct hid_driver maltron_driver = { 15962306a36Sopenharmony_ci .name = "maltron", 16062306a36Sopenharmony_ci .id_table = maltron_devices, 16162306a36Sopenharmony_ci .report_fixup = maltron_report_fixup 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_cimodule_hid_driver(maltron_driver); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 166