diff -uprN -X hid-vanilla/Documentation/dontdiff
hid-vanilla/drivers/hid/hid-cougar.c hid/drivers/hid/hid-cougar.c
--- hid-vanilla/drivers/hid/hid-cougar.c    1970-01-01 00:00:00.000000000 +0000
+++ hid/drivers/hid/hid-cougar.c    2018-07-09 18:42:42.187292906 +0100
@@ -0,0 +1,313 @@
+/*
+ *  HID driver for Cougar 500k Gaming Keyboard
+ *
+ *  Copyright (c) 2018 Daniel M. Lambea <dmlambea@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Daniel M. Lambea <dmlambea@gmail.com>");
+MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard");
+MODULE_LICENSE("GPL");
+MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18");
+
+static int cougar_g6_is_space = 1;
+module_param_named(g6_is_space, cougar_g6_is_space, int, 0600);
+MODULE_PARM_DESC(g6_is_space,
+    "If set, G6 programmable key sends SPACE instead of F18 (0=off,
1=on) (default=1)");
+
+
+#define COUGAR_KEYB_INTFNO        0
+#define COUGAR_MOUSE_INTFNO        1
+#define COUGAR_RESERVED_INTFNO        2
+
+#define COUGAR_RESERVED_FLD_CODE    1
+#define COUGAR_RESERVED_FLD_ACTION    2
+
+#define COUGAR_KEY_G1            0x83
+#define COUGAR_KEY_G2            0x84
+#define COUGAR_KEY_G3            0x85
+#define COUGAR_KEY_G4            0x86
+#define COUGAR_KEY_G5            0x87
+#define COUGAR_KEY_G6            0x78
+#define COUGAR_KEY_FN            0x0d
+#define COUGAR_KEY_MR            0x6f
+#define COUGAR_KEY_M1            0x80
+#define COUGAR_KEY_M2            0x81
+#define COUGAR_KEY_M3            0x82
+#define COUGAR_KEY_LEDS            0x67
+#define COUGAR_KEY_LOCK            0x6e
+
+
+/* Default key mappings */
+static unsigned char cougar_mapping[][2] = {
+    { COUGAR_KEY_G1,   KEY_F13 },
+    { COUGAR_KEY_G2,   KEY_F14 },
+    { COUGAR_KEY_G3,   KEY_F15 },
+    { COUGAR_KEY_G4,   KEY_F16 },
+    { COUGAR_KEY_G5,   KEY_F17 },
+    { COUGAR_KEY_G6,   KEY_F18 },    // This is handled individually
+    { COUGAR_KEY_LOCK, KEY_SCREENLOCK },
+    { 0, 0 },
+};
+
+struct cougar_kbd_data {
+    struct hid_device *owner;
+    struct input_dev  *input;
+};
+
+/*
+ * Constant-friendly rdesc fixup for mouse interface
+ */
+static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+                 unsigned int *rsize)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+
+    if (intf->cur_altsetting->desc.bInterfaceNumber == COUGAR_MOUSE_INTFNO &&
+        (rdesc[0x73] | rdesc[0x74] << 8) >= HID_MAX_USAGES) {
+        hid_info(hdev, "usage count exceeds max: fixing up report descriptor");
+        rdesc[0x73] = ((HID_MAX_USAGES-1) & 0xff);
+        rdesc[0x74] = ((HID_MAX_USAGES-1) >> 8);
+    }
+    return rdesc;
+}
+
+/*
+ * Returns a sibling hid_device with the given intf number
+ */
+static struct hid_device *cougar_get_sibling_hid_device(struct
hid_device *hdev,
+                            const __u8 intfno)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct usb_device *device;
+    struct usb_host_config *hostcfg;
+    struct hid_device *siblingHdev;
+    int i;
+
+    if (intf->cur_altsetting->desc.bInterfaceNumber == intfno)
+        hid_err(hdev, "returning hid device's data from myself?");
+
+    device = interface_to_usbdev(intf);
+    hostcfg = device->config;
+    siblingHdev = NULL;
+    for (i = 0; i < hostcfg->desc.bNumInterfaces; i++) {
+        if (i == intfno)
+            return usb_get_intfdata(hostcfg->interface[i]);
+    }
+    return NULL;
+}
+
+static int cougar_set_drvdata_from_keyb_interface(struct hid_device *hdev)
+{
+    struct hid_device *sibling;
+    struct cougar_kbd_data *kbd;
+
+    /* Search for the keyboard */
+    sibling = cougar_get_sibling_hid_device(hdev, COUGAR_KEYB_INTFNO);
+    if (sibling == NULL) {
+        hid_err(hdev,
+            "no sibling hid device found for intf %d", COUGAR_KEYB_INTFNO);
+        return -ENODEV;
+    }
+
+    kbd = hid_get_drvdata(sibling);
+    if (kbd == NULL || kbd->input == NULL) {
+        hid_err(hdev,
+            "keyboard descriptor not found in intf %d", COUGAR_KEYB_INTFNO);
+        return -ENODATA;
+    }
+
+    /* And save its data on reserved, custom vendor intf. device */
+    hid_set_drvdata(hdev, kbd);
+    return 0;
+}
+
+/*
+ * The probe will save the keyb's input device, so that the
+ * vendor intf will be able to send keys as if it were the
+ * keyboard itself.
+ */
+static int cougar_probe(struct hid_device *hdev,
+            const struct hid_device_id *id)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct cougar_kbd_data *kbd = NULL;
+    unsigned int mask;
+    int ret;
+    __u8 intfno;
+
+    intfno = intf->cur_altsetting->desc.bInterfaceNumber;
+    hid_dbg(hdev, "about to probe intf %d", intfno);
+
+    if (intfno == COUGAR_KEYB_INTFNO) {
+        kbd = devm_kzalloc(&hdev->dev, sizeof(*kbd), GFP_KERNEL);
+        if (kbd == NULL)
+            return -ENOMEM;
+
+        hid_dbg(hdev, "attaching kbd data to intf %d", intfno);
+        hid_set_drvdata(hdev, kbd);
+    }
+
+    /* Parse and start hw */
+    ret = hid_parse(hdev);
+    if (ret)
+        goto err_cleanup;
+
+    /* Reserved, custom vendor interface connects hidraw only */
+    mask = intfno == COUGAR_RESERVED_INTFNO ?
+                HID_CONNECT_HIDRAW : HID_CONNECT_DEFAULT;
+    ret = hid_hw_start(hdev, mask);
+    if (ret)
+        goto err_cleanup;
+
+    if (intfno == COUGAR_RESERVED_INTFNO) {
+        ret = cougar_set_drvdata_from_keyb_interface(hdev);
+        if (ret)
+            goto err_stop_and_cleanup;
+
+        hid_dbg(hdev, "keyboard descriptor attached to intf %d", intfno);
+
+        ret = hid_hw_open(hdev);
+        if (ret)
+            goto err_stop_and_cleanup;
+    }
+    hid_dbg(hdev, "intf %d probed successfully", intfno);
+
+    return 0;
+
+err_stop_and_cleanup:
+    hid_hw_stop(hdev);
+err_cleanup:
+    hid_set_drvdata(hdev, NULL);
+    if (kbd != NULL && kbd->owner == hdev)
+        devm_kfree(&hdev->dev, kbd);
+    return ret;
+}
+
+/*
+ * Keeps track of the keyboard's hid_input
+ */
+static int cougar_input_configured(struct hid_device *hdev, struct
hid_input *hidinput)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    __u8 intfno = intf->cur_altsetting->desc.bInterfaceNumber;
+    struct cougar_kbd_data *kbd;
+
+    if (intfno != COUGAR_KEYB_INTFNO) {
+        /* Skip interfaces other than COUGAR_KEYB_INTFNO,
+         * which is the one containing the configured hidinput
+         */
+        hid_dbg(hdev, "input_configured: skipping intf %d", intfno);
+        return 0;
+    }
+    kbd = hid_get_drvdata(hdev);
+    kbd->owner = hdev;
+    kbd->input = hidinput->input;
+    hid_dbg(hdev, "input_configured: intf %d configured", intfno);
+    return 0;
+}
+
+/*
+ * Converts events from vendor intf to input key events
+ */
+static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report,
+                u8 *data, int size)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct cougar_kbd_data *kbd;
+    unsigned char action, code, transcode;
+    int i;
+
+    if (intf->cur_altsetting->desc.bInterfaceNumber != COUGAR_RESERVED_INTFNO)
+        return 0;
+
+    // Enable this to see a dump of the data received from reserved interface
+    //print_hex_dump(KERN_ERR, DRIVER_NAME " data : ",
DUMP_PREFIX_OFFSET, 8, 1, data, size, 0);
+
+    code = data[COUGAR_RESERVED_FLD_CODE];
+    switch (code) {
+    case COUGAR_KEY_FN:
+        hid_dbg(hdev, "FN (special function) key is handled by the
hardware itself");
+        break;
+    case COUGAR_KEY_MR:
+        hid_dbg(hdev, "MR (macro recording) key is handled by the
hardware itself");
+        break;
+    case COUGAR_KEY_M1:
+        hid_dbg(hdev, "M1 (macro set 1) key is handled by the
hardware itself");
+        break;
+    case COUGAR_KEY_M2:
+        hid_dbg(hdev, "M2 (macro set 2) key is handled by the
hardware itself");
+        break;
+    case COUGAR_KEY_M3:
+        hid_dbg(hdev, "M3 (macro set 3) key is handled by the
hardware itself");
+        break;
+    case COUGAR_KEY_LEDS:
+        hid_dbg(hdev, "LED (led set) key is handled by the hardware itself");
+        break;
+    default:
+        /* Try normal key mappings */
+        for (i = 0; cougar_mapping[i][0]; i++) {
+            if (cougar_mapping[i][0] == code) {
+                action = data[COUGAR_RESERVED_FLD_ACTION];
+                hid_dbg(hdev, "found mapping for code %x", code);
+                if (code == COUGAR_KEY_G6 && cougar_g6_is_space)
+                    transcode = KEY_SPACE;
+                else
+                    transcode = cougar_mapping[i][1];
+
+                kbd = hid_get_drvdata(hdev);
+                input_event(kbd->input, EV_KEY, transcode, action);
+                input_sync(kbd->input);
+                hid_dbg(hdev, "translated to %x", transcode);
+                return 0;
+            }
+        }
+        hid_warn(hdev, "unmapped key code %d: ignoring", code);
+    }
+    return 0;
+}
+
+static void cougar_remove(struct hid_device *hdev)
+{
+    struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+    struct cougar_kbd_data *kbd = hid_get_drvdata(hdev);
+
+    hid_dbg(hdev, "removing %d", intf->cur_altsetting->desc.bInterfaceNumber);
+    if (intf->cur_altsetting->desc.bInterfaceNumber == COUGAR_RESERVED_INTFNO)
+        hid_hw_close(hdev);
+
+    hid_hw_stop(hdev);
+    hid_set_drvdata(hdev, NULL);
+    if (kbd != NULL && kbd->owner == hdev)
+        devm_kfree(&hdev->dev, kbd);
+}
+
+static struct hid_device_id cougar_id_table[] = {
+    { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
+    {}
+};
+MODULE_DEVICE_TABLE(hid, cougar_id_table);
+
+static struct hid_driver cougar_driver = {
+    .name            = "cougar",
+    .id_table        = cougar_id_table,
+    .report_fixup        = cougar_report_fixup,
+    .probe            = cougar_probe,
+    .input_configured    = cougar_input_configured,
+    .remove            = cougar_remove,
+    .raw_event        = cougar_raw_event,
+};
+
+module_hid_driver(cougar_driver);
diff -uprN -X hid-vanilla/Documentation/dontdiff
hid-vanilla/drivers/hid/hid-ids.h hid/drivers/hid/hid-ids.h
--- hid-vanilla/drivers/hid/hid-ids.h    2018-07-09 17:48:30.189316299 +0100
+++ hid/drivers/hid/hid-ids.h    2018-07-09 17:54:29.332832182 +0100
@@ -1001,6 +1001,9 @@
 #define USB_VENDOR_ID_SINO_LITE            0x1345
 #define USB_DEVICE_ID_SINO_LITE_CONTROLLER    0x3008

+#define USB_VENDOR_ID_SOLID_YEAR            0x060b
+#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD    0x500a
+
 #define USB_VENDOR_ID_SOUNDGRAPH    0x15c2
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST    0x0034
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST    0x0046
diff -uprN -X hid-vanilla/Documentation/dontdiff
hid-vanilla/drivers/hid/hid-quirks.c hid/drivers/hid/hid-quirks.c
--- hid-vanilla/drivers/hid/hid-quirks.c    2018-07-09 17:48:30.193316294 +0100
+++ hid/drivers/hid/hid-quirks.c    2018-07-09 17:54:42.708814231 +0100
@@ -610,6 +610,9 @@ static const struct hid_device_id hid_ha
     { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD,
USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
     { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD,
USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
 #endif
+#if IS_ENABLED(CONFIG_HID_COUGAR)
+    { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
+#endif
 #if IS_ENABLED(CONFIG_HID_SONY)
     { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
     { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK,
USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
diff -uprN -X hid-vanilla/Documentation/dontdiff
hid-vanilla/drivers/hid/Kconfig hid/drivers/hid/Kconfig
--- hid-vanilla/drivers/hid/Kconfig    2018-07-09 17:48:30.185316305 +0100
+++ hid/drivers/hid/Kconfig    2018-07-09 18:46:10.075657150 +0100
@@ -207,6 +207,16 @@ config HID_CORSAIR
     - Vengeance K90
     - Scimitar PRO RGB

+config HID_COUGAR
+    tristate "Cougar devices"
+    depends on HID
+    help
+    Support for Cougar devices that are not fully compliant with the
+    HID standard.
+
+    Supported devices:
+    - Cougar 500k Gaming Keyboard
+
 config HID_PRODIKEYS
     tristate "Prodikeys PC-MIDI Keyboard support"
     depends on HID && SND
diff -uprN -X hid-vanilla/Documentation/dontdiff
hid-vanilla/drivers/hid/Makefile hid/drivers/hid/Makefile
--- hid-vanilla/drivers/hid/Makefile    2018-07-09 17:48:30.185316305 +0100
+++ hid/drivers/hid/Makefile    2018-07-09 17:54:15.140851231 +0100
@@ -35,6 +35,7 @@ obj-$(CONFIG_HID_CHERRY)    += hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)    += hid-chicony.o
 obj-$(CONFIG_HID_CMEDIA)    += hid-cmedia.o
 obj-$(CONFIG_HID_CORSAIR)    += hid-corsair.o
+obj-$(CONFIG_HID_COUGAR)    += hid-cougar.o
 obj-$(CONFIG_HID_CP2112)    += hid-cp2112.o
 obj-$(CONFIG_HID_CYPRESS)    += hid-cypress.o
 obj-$(CONFIG_HID_DRAGONRISE)    += hid-dr.o
