1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * HID driver for Cougar 500k Gaming Keyboard 4 * 5 * Copyright (c) 2018 Daniel M. Lambea <dmlambea@gmail.com> 6 */ 7 8 #include <linux/hid.h> 9 #include <linux/module.h> 10 11 #include "hid-ids.h" 12 13 MODULE_AUTHOR("Daniel M. Lambea <dmlambea@gmail.com>"); 14 MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard"); 15 MODULE_LICENSE("GPL"); 16 MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18"); 17 18 static int cougar_g6_is_space = 1; 19 module_param_named(g6_is_space, cougar_g6_is_space, int, 0600); 20 MODULE_PARM_DESC(g6_is_space, 21 "If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)"); 22 23 24 #define COUGAR_VENDOR_USAGE 0xff00ff00 25 26 #define COUGAR_FIELD_CODE 1 27 #define COUGAR_FIELD_ACTION 2 28 29 #define COUGAR_KEY_G1 0x83 30 #define COUGAR_KEY_G2 0x84 31 #define COUGAR_KEY_G3 0x85 32 #define COUGAR_KEY_G4 0x86 33 #define COUGAR_KEY_G5 0x87 34 #define COUGAR_KEY_G6 0x78 35 #define COUGAR_KEY_FN 0x0d 36 #define COUGAR_KEY_MR 0x6f 37 #define COUGAR_KEY_M1 0x80 38 #define COUGAR_KEY_M2 0x81 39 #define COUGAR_KEY_M3 0x82 40 #define COUGAR_KEY_LEDS 0x67 41 #define COUGAR_KEY_LOCK 0x6e 42 43 44 /* Default key mappings. The special key COUGAR_KEY_G6 is defined first 45 * because it is more frequent to use the spacebar rather than any other 46 * special keys. Depending on the value of the parameter 'g6_is_space', 47 * the mapping will be updated in the probe function. 48 */ 49 static unsigned char cougar_mapping[][2] = { 50 { COUGAR_KEY_G6, KEY_SPACE }, 51 { COUGAR_KEY_G1, KEY_F13 }, 52 { COUGAR_KEY_G2, KEY_F14 }, 53 { COUGAR_KEY_G3, KEY_F15 }, 54 { COUGAR_KEY_G4, KEY_F16 }, 55 { COUGAR_KEY_G5, KEY_F17 }, 56 { COUGAR_KEY_LOCK, KEY_SCREENLOCK }, 57 /* The following keys are handled by the hardware itself, so no special 58 * treatment is required: 59 { COUGAR_KEY_FN, KEY_RESERVED }, 60 { COUGAR_KEY_MR, KEY_RESERVED }, 61 { COUGAR_KEY_M1, KEY_RESERVED }, 62 { COUGAR_KEY_M2, KEY_RESERVED }, 63 { COUGAR_KEY_M3, KEY_RESERVED }, 64 { COUGAR_KEY_LEDS, KEY_RESERVED }, 65 */ 66 { 0, 0 }, 67 }; 68 69 struct cougar_shared { 70 struct list_head list; 71 struct kref kref; 72 bool enabled; 73 struct hid_device *dev; 74 struct input_dev *input; 75 }; 76 77 struct cougar { 78 bool special_intf; 79 struct cougar_shared *shared; 80 }; 81 82 static LIST_HEAD(cougar_udev_list); 83 static DEFINE_MUTEX(cougar_udev_list_lock); 84 85 static void cougar_fix_g6_mapping(struct hid_device *hdev) 86 { 87 int i; 88 89 for (i = 0; cougar_mapping[i][0]; i++) { 90 if (cougar_mapping[i][0] == COUGAR_KEY_G6) { 91 cougar_mapping[i][1] = 92 cougar_g6_is_space ? KEY_SPACE : KEY_F18; 93 hid_info(hdev, "G6 mapped to %s\n", 94 cougar_g6_is_space ? "space" : "F18"); 95 return; 96 } 97 } 98 hid_warn(hdev, "no mapping defined for G6/spacebar"); 99 } 100 101 /* 102 * Constant-friendly rdesc fixup for mouse interface 103 */ 104 static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc, 105 unsigned int *rsize) 106 { 107 if (rdesc[2] == 0x09 && rdesc[3] == 0x02 && 108 (rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) { 109 hid_info(hdev, 110 "usage count exceeds max: fixing up report descriptor\n"); 111 rdesc[115] = ((HID_MAX_USAGES-1) & 0xff); 112 rdesc[116] = ((HID_MAX_USAGES-1) >> 8); 113 } 114 return rdesc; 115 } 116 117 static struct cougar_shared *cougar_get_shared_data(struct hid_device *hdev) 118 { 119 struct cougar_shared *shared; 120 121 /* Try to find an already-probed interface from the same device */ 122 list_for_each_entry(shared, &cougar_udev_list, list) { 123 if (hid_compare_device_paths(hdev, shared->dev, '/')) { 124 kref_get(&shared->kref); 125 return shared; 126 } 127 } 128 return NULL; 129 } 130 131 static void cougar_release_shared_data(struct kref *kref) 132 { 133 struct cougar_shared *shared = container_of(kref, 134 struct cougar_shared, kref); 135 136 mutex_lock(&cougar_udev_list_lock); 137 list_del(&shared->list); 138 mutex_unlock(&cougar_udev_list_lock); 139 140 kfree(shared); 141 } 142 143 static void cougar_remove_shared_data(void *resource) 144 { 145 struct cougar *cougar = resource; 146 147 if (cougar->shared) { 148 kref_put(&cougar->shared->kref, cougar_release_shared_data); 149 cougar->shared = NULL; 150 } 151 } 152 153 /* 154 * Bind the device group's shared data to this cougar struct. 155 * If no shared data exists for this group, create and initialize it. 156 */ 157 static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar) 158 { 159 struct cougar_shared *shared; 160 int error = 0; 161 162 mutex_lock(&cougar_udev_list_lock); 163 164 shared = cougar_get_shared_data(hdev); 165 if (!shared) { 166 shared = kzalloc(sizeof(*shared), GFP_KERNEL); 167 if (!shared) { 168 error = -ENOMEM; 169 goto out; 170 } 171 172 kref_init(&shared->kref); 173 shared->dev = hdev; 174 list_add_tail(&shared->list, &cougar_udev_list); 175 } 176 177 cougar->shared = shared; 178 179 error = devm_add_action(&hdev->dev, cougar_remove_shared_data, cougar); 180 if (error) { 181 mutex_unlock(&cougar_udev_list_lock); 182 cougar_remove_shared_data(cougar); 183 return error; 184 } 185 186 out: 187 mutex_unlock(&cougar_udev_list_lock); 188 return error; 189 } 190 191 static int cougar_probe(struct hid_device *hdev, 192 const struct hid_device_id *id) 193 { 194 struct cougar *cougar; 195 struct hid_input *next, *hidinput = NULL; 196 unsigned int connect_mask; 197 int error; 198 199 cougar = devm_kzalloc(&hdev->dev, sizeof(*cougar), GFP_KERNEL); 200 if (!cougar) 201 return -ENOMEM; 202 hid_set_drvdata(hdev, cougar); 203 204 error = hid_parse(hdev); 205 if (error) { 206 hid_err(hdev, "parse failed\n"); 207 goto fail; 208 } 209 210 if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { 211 cougar->special_intf = true; 212 connect_mask = HID_CONNECT_HIDRAW; 213 } else 214 connect_mask = HID_CONNECT_DEFAULT; 215 216 error = hid_hw_start(hdev, connect_mask); 217 if (error) { 218 hid_err(hdev, "hw start failed\n"); 219 goto fail; 220 } 221 222 error = cougar_bind_shared_data(hdev, cougar); 223 if (error) 224 goto fail_stop_and_cleanup; 225 226 /* The custom vendor interface will use the hid_input registered 227 * for the keyboard interface, in order to send translated key codes 228 * to it. 229 */ 230 if (hdev->collection->usage == HID_GD_KEYBOARD) { 231 cougar_fix_g6_mapping(hdev); 232 list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) { 233 if (hidinput->registered && hidinput->input != NULL) { 234 cougar->shared->input = hidinput->input; 235 cougar->shared->enabled = true; 236 break; 237 } 238 } 239 } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { 240 error = hid_hw_open(hdev); 241 if (error) 242 goto fail_stop_and_cleanup; 243 } 244 return 0; 245 246 fail_stop_and_cleanup: 247 hid_hw_stop(hdev); 248 fail: 249 hid_set_drvdata(hdev, NULL); 250 return error; 251 } 252 253 /* 254 * Convert events from vendor intf to input key events 255 */ 256 static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report, 257 u8 *data, int size) 258 { 259 struct cougar *cougar; 260 unsigned char code, action; 261 int i; 262 263 cougar = hid_get_drvdata(hdev); 264 if (!cougar->special_intf || !cougar->shared || 265 !cougar->shared->input || !cougar->shared->enabled) 266 return 0; 267 268 code = data[COUGAR_FIELD_CODE]; 269 action = data[COUGAR_FIELD_ACTION]; 270 for (i = 0; cougar_mapping[i][0]; i++) { 271 if (code == cougar_mapping[i][0]) { 272 input_event(cougar->shared->input, EV_KEY, 273 cougar_mapping[i][1], action); 274 input_sync(cougar->shared->input); 275 return 0; 276 } 277 } 278 hid_warn(hdev, "unmapped special key code %x: ignoring\n", code); 279 return 0; 280 } 281 282 static void cougar_remove(struct hid_device *hdev) 283 { 284 struct cougar *cougar = hid_get_drvdata(hdev); 285 286 if (cougar) { 287 /* Stop the vendor intf to process more events */ 288 if (cougar->shared) 289 cougar->shared->enabled = false; 290 if (cougar->special_intf) 291 hid_hw_close(hdev); 292 } 293 hid_hw_stop(hdev); 294 } 295 296 static struct hid_device_id cougar_id_table[] = { 297 { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, 298 USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, 299 {} 300 }; 301 MODULE_DEVICE_TABLE(hid, cougar_id_table); 302 303 static struct hid_driver cougar_driver = { 304 .name = "cougar", 305 .id_table = cougar_id_table, 306 .report_fixup = cougar_report_fixup, 307 .probe = cougar_probe, 308 .remove = cougar_remove, 309 .raw_event = cougar_raw_event, 310 }; 311 312 module_hid_driver(cougar_driver); 313