| .. | .. |
|---|
| 7 | 7 | |
|---|
| 8 | 8 | #include <linux/hid.h> |
|---|
| 9 | 9 | #include <linux/module.h> |
|---|
| 10 | +#include <linux/printk.h> |
|---|
| 10 | 11 | |
|---|
| 11 | 12 | #include "hid-ids.h" |
|---|
| 12 | 13 | |
|---|
| .. | .. |
|---|
| 15 | 16 | MODULE_LICENSE("GPL"); |
|---|
| 16 | 17 | MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18"); |
|---|
| 17 | 18 | |
|---|
| 18 | | -static int cougar_g6_is_space = 1; |
|---|
| 19 | | -module_param_named(g6_is_space, cougar_g6_is_space, int, 0600); |
|---|
| 19 | +static bool g6_is_space = true; |
|---|
| 20 | 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 | | - |
|---|
| 21 | + "If true, G6 programmable key sends SPACE instead of F18 (default=true)"); |
|---|
| 23 | 22 | |
|---|
| 24 | 23 | #define COUGAR_VENDOR_USAGE 0xff00ff00 |
|---|
| 25 | 24 | |
|---|
| .. | .. |
|---|
| 82 | 81 | static LIST_HEAD(cougar_udev_list); |
|---|
| 83 | 82 | static DEFINE_MUTEX(cougar_udev_list_lock); |
|---|
| 84 | 83 | |
|---|
| 85 | | -static void cougar_fix_g6_mapping(struct hid_device *hdev) |
|---|
| 84 | +/** |
|---|
| 85 | + * cougar_fix_g6_mapping - configure the mapping for key G6/Spacebar |
|---|
| 86 | + */ |
|---|
| 87 | +static void cougar_fix_g6_mapping(void) |
|---|
| 86 | 88 | { |
|---|
| 87 | 89 | int i; |
|---|
| 88 | 90 | |
|---|
| 89 | 91 | for (i = 0; cougar_mapping[i][0]; i++) { |
|---|
| 90 | 92 | if (cougar_mapping[i][0] == COUGAR_KEY_G6) { |
|---|
| 91 | 93 | 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"); |
|---|
| 94 | + g6_is_space ? KEY_SPACE : KEY_F18; |
|---|
| 95 | + pr_info("cougar: G6 mapped to %s\n", |
|---|
| 96 | + g6_is_space ? "space" : "F18"); |
|---|
| 95 | 97 | return; |
|---|
| 96 | 98 | } |
|---|
| 97 | 99 | } |
|---|
| 98 | | - hid_warn(hdev, "no mapping defined for G6/spacebar"); |
|---|
| 100 | + pr_warn("cougar: no mappings defined for G6/spacebar"); |
|---|
| 99 | 101 | } |
|---|
| 100 | 102 | |
|---|
| 101 | 103 | /* |
|---|
| .. | .. |
|---|
| 154 | 156 | * Bind the device group's shared data to this cougar struct. |
|---|
| 155 | 157 | * If no shared data exists for this group, create and initialize it. |
|---|
| 156 | 158 | */ |
|---|
| 157 | | -static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar) |
|---|
| 159 | +static int cougar_bind_shared_data(struct hid_device *hdev, |
|---|
| 160 | + struct cougar *cougar) |
|---|
| 158 | 161 | { |
|---|
| 159 | 162 | struct cougar_shared *shared; |
|---|
| 160 | 163 | int error = 0; |
|---|
| .. | .. |
|---|
| 204 | 207 | error = hid_parse(hdev); |
|---|
| 205 | 208 | if (error) { |
|---|
| 206 | 209 | hid_err(hdev, "parse failed\n"); |
|---|
| 207 | | - goto fail; |
|---|
| 210 | + return error; |
|---|
| 208 | 211 | } |
|---|
| 209 | 212 | |
|---|
| 210 | 213 | if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { |
|---|
| .. | .. |
|---|
| 216 | 219 | error = hid_hw_start(hdev, connect_mask); |
|---|
| 217 | 220 | if (error) { |
|---|
| 218 | 221 | hid_err(hdev, "hw start failed\n"); |
|---|
| 219 | | - goto fail; |
|---|
| 222 | + return error; |
|---|
| 220 | 223 | } |
|---|
| 221 | 224 | |
|---|
| 222 | 225 | error = cougar_bind_shared_data(hdev, cougar); |
|---|
| .. | .. |
|---|
| 228 | 231 | * to it. |
|---|
| 229 | 232 | */ |
|---|
| 230 | 233 | if (hdev->collection->usage == HID_GD_KEYBOARD) { |
|---|
| 231 | | - cougar_fix_g6_mapping(hdev); |
|---|
| 232 | 234 | list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) { |
|---|
| 233 | 235 | if (hidinput->registered && hidinput->input != NULL) { |
|---|
| 234 | 236 | cougar->shared->input = hidinput->input; |
|---|
| .. | .. |
|---|
| 237 | 239 | } |
|---|
| 238 | 240 | } |
|---|
| 239 | 241 | } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { |
|---|
| 242 | + /* Preinit the mapping table */ |
|---|
| 243 | + cougar_fix_g6_mapping(); |
|---|
| 240 | 244 | error = hid_hw_open(hdev); |
|---|
| 241 | 245 | if (error) |
|---|
| 242 | 246 | goto fail_stop_and_cleanup; |
|---|
| .. | .. |
|---|
| 245 | 249 | |
|---|
| 246 | 250 | fail_stop_and_cleanup: |
|---|
| 247 | 251 | hid_hw_stop(hdev); |
|---|
| 248 | | -fail: |
|---|
| 249 | | - hid_set_drvdata(hdev, NULL); |
|---|
| 250 | 252 | return error; |
|---|
| 251 | 253 | } |
|---|
| 252 | 254 | |
|---|
| .. | .. |
|---|
| 257 | 259 | u8 *data, int size) |
|---|
| 258 | 260 | { |
|---|
| 259 | 261 | struct cougar *cougar; |
|---|
| 262 | + struct cougar_shared *shared; |
|---|
| 260 | 263 | unsigned char code, action; |
|---|
| 261 | 264 | int i; |
|---|
| 262 | 265 | |
|---|
| 263 | 266 | cougar = hid_get_drvdata(hdev); |
|---|
| 264 | | - if (!cougar->special_intf || !cougar->shared || |
|---|
| 265 | | - !cougar->shared->input || !cougar->shared->enabled) |
|---|
| 267 | + shared = cougar->shared; |
|---|
| 268 | + if (!cougar->special_intf || !shared) |
|---|
| 266 | 269 | return 0; |
|---|
| 270 | + |
|---|
| 271 | + if (!shared->enabled || !shared->input) |
|---|
| 272 | + return -EPERM; |
|---|
| 267 | 273 | |
|---|
| 268 | 274 | code = data[COUGAR_FIELD_CODE]; |
|---|
| 269 | 275 | action = data[COUGAR_FIELD_ACTION]; |
|---|
| 270 | 276 | for (i = 0; cougar_mapping[i][0]; i++) { |
|---|
| 271 | 277 | if (code == cougar_mapping[i][0]) { |
|---|
| 272 | | - input_event(cougar->shared->input, EV_KEY, |
|---|
| 278 | + input_event(shared->input, EV_KEY, |
|---|
| 273 | 279 | cougar_mapping[i][1], action); |
|---|
| 274 | | - input_sync(cougar->shared->input); |
|---|
| 275 | | - return 0; |
|---|
| 280 | + input_sync(shared->input); |
|---|
| 281 | + return -EPERM; |
|---|
| 276 | 282 | } |
|---|
| 277 | 283 | } |
|---|
| 278 | | - hid_warn(hdev, "unmapped special key code %x: ignoring\n", code); |
|---|
| 279 | | - return 0; |
|---|
| 284 | + /* Avoid warnings on the same unmapped key twice */ |
|---|
| 285 | + if (action != 0) |
|---|
| 286 | + hid_warn(hdev, "unmapped special key code %0x: ignoring\n", code); |
|---|
| 287 | + return -EPERM; |
|---|
| 280 | 288 | } |
|---|
| 281 | 289 | |
|---|
| 282 | 290 | static void cougar_remove(struct hid_device *hdev) |
|---|
| .. | .. |
|---|
| 293 | 301 | hid_hw_stop(hdev); |
|---|
| 294 | 302 | } |
|---|
| 295 | 303 | |
|---|
| 296 | | -static struct hid_device_id cougar_id_table[] = { |
|---|
| 304 | +static int cougar_param_set_g6_is_space(const char *val, |
|---|
| 305 | + const struct kernel_param *kp) |
|---|
| 306 | +{ |
|---|
| 307 | + int ret; |
|---|
| 308 | + |
|---|
| 309 | + ret = param_set_bool(val, kp); |
|---|
| 310 | + if (ret) |
|---|
| 311 | + return ret; |
|---|
| 312 | + |
|---|
| 313 | + cougar_fix_g6_mapping(); |
|---|
| 314 | + |
|---|
| 315 | + return 0; |
|---|
| 316 | +} |
|---|
| 317 | + |
|---|
| 318 | +static const struct kernel_param_ops cougar_g6_is_space_ops = { |
|---|
| 319 | + .set = cougar_param_set_g6_is_space, |
|---|
| 320 | + .get = param_get_bool, |
|---|
| 321 | +}; |
|---|
| 322 | +module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644); |
|---|
| 323 | + |
|---|
| 324 | +static const struct hid_device_id cougar_id_table[] = { |
|---|
| 297 | 325 | { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, |
|---|
| 298 | 326 | USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, |
|---|
| 327 | + { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, |
|---|
| 328 | + USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD) }, |
|---|
| 299 | 329 | {} |
|---|
| 300 | 330 | }; |
|---|
| 301 | 331 | MODULE_DEVICE_TABLE(hid, cougar_id_table); |
|---|