1*047b6188SJelle van der Waa // SPDX-License-Identifier: GPL-2.0+
2*047b6188SJelle van der Waa /*
3*047b6188SJelle van der Waa * HID driver for gaming keys on Razer Blackwidow gaming keyboards
4*047b6188SJelle van der Waa * Macro Key Keycodes: M1 = 191, M2 = 192, M3 = 193, M4 = 194, M5 = 195
5*047b6188SJelle van der Waa *
6*047b6188SJelle van der Waa * Copyright (c) 2021 Jelle van der Waa <jvanderwaa@redhat.com>
7*047b6188SJelle van der Waa */
8*047b6188SJelle van der Waa
9*047b6188SJelle van der Waa #include <linux/device.h>
10*047b6188SJelle van der Waa #include <linux/hid.h>
11*047b6188SJelle van der Waa #include <linux/module.h>
12*047b6188SJelle van der Waa #include <linux/random.h>
13*047b6188SJelle van der Waa #include <linux/sched.h>
14*047b6188SJelle van der Waa #include <linux/usb.h>
15*047b6188SJelle van der Waa #include <linux/wait.h>
16*047b6188SJelle van der Waa
17*047b6188SJelle van der Waa #include "hid-ids.h"
18*047b6188SJelle van der Waa
19*047b6188SJelle van der Waa #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
20*047b6188SJelle van der Waa
21*047b6188SJelle van der Waa #define RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE 91
22*047b6188SJelle van der Waa
23*047b6188SJelle van der Waa static bool macro_key_remapping = 1;
24*047b6188SJelle van der Waa module_param(macro_key_remapping, bool, 0644);
25*047b6188SJelle van der Waa MODULE_PARM_DESC(macro_key_remapping, " on (Y) off (N)");
26*047b6188SJelle van der Waa
27*047b6188SJelle van der Waa
28*047b6188SJelle van der Waa static unsigned char blackwidow_init[RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE] = {
29*047b6188SJelle van der Waa 0x00,
30*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04,
31*047b6188SJelle van der Waa 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40*047b6188SJelle van der Waa 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41*047b6188SJelle van der Waa 0x04, 0x00
42*047b6188SJelle van der Waa };
43*047b6188SJelle van der Waa
razer_input_mapping(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)44*047b6188SJelle van der Waa static int razer_input_mapping(struct hid_device *hdev,
45*047b6188SJelle van der Waa struct hid_input *hi, struct hid_field *field,
46*047b6188SJelle van der Waa struct hid_usage *usage, unsigned long **bit, int *max)
47*047b6188SJelle van der Waa {
48*047b6188SJelle van der Waa
49*047b6188SJelle van der Waa if (!macro_key_remapping)
50*047b6188SJelle van der Waa return 0;
51*047b6188SJelle van der Waa
52*047b6188SJelle van der Waa if ((usage->hid & HID_UP_KEYBOARD) != HID_UP_KEYBOARD)
53*047b6188SJelle van der Waa return 0;
54*047b6188SJelle van der Waa
55*047b6188SJelle van der Waa switch (usage->hid & ~HID_UP_KEYBOARD) {
56*047b6188SJelle van der Waa case 0x68:
57*047b6188SJelle van der Waa map_key_clear(KEY_MACRO1);
58*047b6188SJelle van der Waa return 1;
59*047b6188SJelle van der Waa case 0x69:
60*047b6188SJelle van der Waa map_key_clear(KEY_MACRO2);
61*047b6188SJelle van der Waa return 1;
62*047b6188SJelle van der Waa case 0x6a:
63*047b6188SJelle van der Waa map_key_clear(KEY_MACRO3);
64*047b6188SJelle van der Waa return 1;
65*047b6188SJelle van der Waa case 0x6b:
66*047b6188SJelle van der Waa map_key_clear(KEY_MACRO4);
67*047b6188SJelle van der Waa return 1;
68*047b6188SJelle van der Waa case 0x6c:
69*047b6188SJelle van der Waa map_key_clear(KEY_MACRO5);
70*047b6188SJelle van der Waa return 1;
71*047b6188SJelle van der Waa }
72*047b6188SJelle van der Waa
73*047b6188SJelle van der Waa return 0;
74*047b6188SJelle van der Waa }
75*047b6188SJelle van der Waa
razer_probe(struct hid_device * hdev,const struct hid_device_id * id)76*047b6188SJelle van der Waa static int razer_probe(struct hid_device *hdev, const struct hid_device_id *id)
77*047b6188SJelle van der Waa {
78*047b6188SJelle van der Waa char *buf;
79*047b6188SJelle van der Waa int ret = 0;
80*047b6188SJelle van der Waa
81*047b6188SJelle van der Waa ret = hid_parse(hdev);
82*047b6188SJelle van der Waa if (ret)
83*047b6188SJelle van der Waa return ret;
84*047b6188SJelle van der Waa
85*047b6188SJelle van der Waa /*
86*047b6188SJelle van der Waa * Only send the enable macro keys command for the third device
87*047b6188SJelle van der Waa * identified as mouse input.
88*047b6188SJelle van der Waa */
89*047b6188SJelle van der Waa if (hdev->type == HID_TYPE_USBMOUSE) {
90*047b6188SJelle van der Waa buf = kmemdup(blackwidow_init, RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE, GFP_KERNEL);
91*047b6188SJelle van der Waa if (buf == NULL)
92*047b6188SJelle van der Waa return -ENOMEM;
93*047b6188SJelle van der Waa
94*047b6188SJelle van der Waa ret = hid_hw_raw_request(hdev, 0, buf, RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE,
95*047b6188SJelle van der Waa HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
96*047b6188SJelle van der Waa if (ret != RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE)
97*047b6188SJelle van der Waa hid_err(hdev, "failed to enable macro keys: %d\n", ret);
98*047b6188SJelle van der Waa
99*047b6188SJelle van der Waa kfree(buf);
100*047b6188SJelle van der Waa }
101*047b6188SJelle van der Waa
102*047b6188SJelle van der Waa return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
103*047b6188SJelle van der Waa }
104*047b6188SJelle van der Waa
105*047b6188SJelle van der Waa static const struct hid_device_id razer_devices[] = {
106*047b6188SJelle van der Waa { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,
107*047b6188SJelle van der Waa USB_DEVICE_ID_RAZER_BLACKWIDOW) },
108*047b6188SJelle van der Waa { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,
109*047b6188SJelle van der Waa USB_DEVICE_ID_RAZER_BLACKWIDOW_CLASSIC) },
110*047b6188SJelle van der Waa { HID_USB_DEVICE(USB_VENDOR_ID_RAZER,
111*047b6188SJelle van der Waa USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE) },
112*047b6188SJelle van der Waa { }
113*047b6188SJelle van der Waa };
114*047b6188SJelle van der Waa MODULE_DEVICE_TABLE(hid, razer_devices);
115*047b6188SJelle van der Waa
116*047b6188SJelle van der Waa static struct hid_driver razer_driver = {
117*047b6188SJelle van der Waa .name = "razer",
118*047b6188SJelle van der Waa .id_table = razer_devices,
119*047b6188SJelle van der Waa .input_mapping = razer_input_mapping,
120*047b6188SJelle van der Waa .probe = razer_probe,
121*047b6188SJelle van der Waa };
122*047b6188SJelle van der Waa module_hid_driver(razer_driver);
123*047b6188SJelle van der Waa
124*047b6188SJelle van der Waa MODULE_AUTHOR("Jelle van der Waa <jvanderwaa@redhat.com>");
125*047b6188SJelle van der Waa MODULE_LICENSE("GPL");
126