162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for gaming keys on Razer Blackwidow gaming keyboards 462306a36Sopenharmony_ci * Macro Key Keycodes: M1 = 191, M2 = 192, M3 = 193, M4 = 194, M5 = 195 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2021 Jelle van der Waa <jvanderwaa@redhat.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/hid.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/random.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/usb.h> 1562306a36Sopenharmony_ci#include <linux/wait.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "hid-ids.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE 91 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic bool macro_key_remapping = 1; 2462306a36Sopenharmony_cimodule_param(macro_key_remapping, bool, 0644); 2562306a36Sopenharmony_ciMODULE_PARM_DESC(macro_key_remapping, " on (Y) off (N)"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic unsigned char blackwidow_init[RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE] = { 2962306a36Sopenharmony_ci 0x00, 3062306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 3162306a36Sopenharmony_ci 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3262306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3362306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3462306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3562306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3662306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3762306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3862306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3962306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 4062306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 4162306a36Sopenharmony_ci 0x04, 0x00 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int razer_input_mapping(struct hid_device *hdev, 4562306a36Sopenharmony_ci struct hid_input *hi, struct hid_field *field, 4662306a36Sopenharmony_ci struct hid_usage *usage, unsigned long **bit, int *max) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!macro_key_remapping) 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if ((usage->hid & HID_UP_KEYBOARD) != HID_UP_KEYBOARD) 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci switch (usage->hid & ~HID_UP_KEYBOARD) { 5662306a36Sopenharmony_ci case 0x68: 5762306a36Sopenharmony_ci map_key_clear(KEY_MACRO1); 5862306a36Sopenharmony_ci return 1; 5962306a36Sopenharmony_ci case 0x69: 6062306a36Sopenharmony_ci map_key_clear(KEY_MACRO2); 6162306a36Sopenharmony_ci return 1; 6262306a36Sopenharmony_ci case 0x6a: 6362306a36Sopenharmony_ci map_key_clear(KEY_MACRO3); 6462306a36Sopenharmony_ci return 1; 6562306a36Sopenharmony_ci case 0x6b: 6662306a36Sopenharmony_ci map_key_clear(KEY_MACRO4); 6762306a36Sopenharmony_ci return 1; 6862306a36Sopenharmony_ci case 0x6c: 6962306a36Sopenharmony_ci map_key_clear(KEY_MACRO5); 7062306a36Sopenharmony_ci return 1; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int razer_probe(struct hid_device *hdev, const struct hid_device_id *id) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci char *buf; 7962306a36Sopenharmony_ci int ret = 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ret = hid_parse(hdev); 8262306a36Sopenharmony_ci if (ret) 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Only send the enable macro keys command for the third device 8762306a36Sopenharmony_ci * identified as mouse input. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (hdev->type == HID_TYPE_USBMOUSE) { 9062306a36Sopenharmony_ci buf = kmemdup(blackwidow_init, RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE, GFP_KERNEL); 9162306a36Sopenharmony_ci if (buf == NULL) 9262306a36Sopenharmony_ci return -ENOMEM; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ret = hid_hw_raw_request(hdev, 0, buf, RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE, 9562306a36Sopenharmony_ci HID_FEATURE_REPORT, HID_REQ_SET_REPORT); 9662306a36Sopenharmony_ci if (ret != RAZER_BLACKWIDOW_TRANSFER_BUF_SIZE) 9762306a36Sopenharmony_ci hid_err(hdev, "failed to enable macro keys: %d\n", ret); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci kfree(buf); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return hid_hw_start(hdev, HID_CONNECT_DEFAULT); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct hid_device_id razer_devices[] = { 10662306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, 10762306a36Sopenharmony_ci USB_DEVICE_ID_RAZER_BLACKWIDOW) }, 10862306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, 10962306a36Sopenharmony_ci USB_DEVICE_ID_RAZER_BLACKWIDOW_CLASSIC) }, 11062306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, 11162306a36Sopenharmony_ci USB_DEVICE_ID_RAZER_BLACKWIDOW_ULTIMATE) }, 11262306a36Sopenharmony_ci { } 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, razer_devices); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct hid_driver razer_driver = { 11762306a36Sopenharmony_ci .name = "razer", 11862306a36Sopenharmony_ci .id_table = razer_devices, 11962306a36Sopenharmony_ci .input_mapping = razer_input_mapping, 12062306a36Sopenharmony_ci .probe = razer_probe, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_cimodule_hid_driver(razer_driver); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciMODULE_AUTHOR("Jelle van der Waa <jvanderwaa@redhat.com>"); 12562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 126