1/* 2 * HID driver for Gembird Joypad, "PC Game Controller" 3 * 4 * Copyright (c) 2015 Red Hat, Inc 5 * Copyright (c) 2015 Benjamin Tissoires 6 */ 7 8/* 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the Free 11 * Software Foundation; either version 2 of the License, or (at your option) 12 * any later version. 13 */ 14 15#include <linux/device.h> 16#include <linux/hid.h> 17#include <linux/module.h> 18 19#include "hid-ids.h" 20 21#define GEMBIRD_START_FAULTY_RDESC 8 22 23static const __u8 gembird_jpd_faulty_rdesc[] = { 24 0x75, 0x08, /* Report Size (8) */ 25 0x95, 0x05, /* Report Count (5) */ 26 0x15, 0x00, /* Logical Minimum (0) */ 27 0x26, 0xff, 0x00, /* Logical Maximum (255) */ 28 0x35, 0x00, /* Physical Minimum (0) */ 29 0x46, 0xff, 0x00, /* Physical Maximum (255) */ 30 0x09, 0x30, /* Usage (X) */ 31 0x09, 0x31, /* Usage (Y) */ 32 0x09, 0x32, /* Usage (Z) */ 33 0x09, 0x32, /* Usage (Z) */ 34 0x09, 0x35, /* Usage (Rz) */ 35 0x81, 0x02, /* Input (Data,Var,Abs) */ 36}; 37 38/* 39 * we fix the report descriptor by: 40 * - marking the first Z axis as constant (so it is ignored by HID) 41 * - assign the original second Z to Rx 42 * - assign the original Rz to Ry 43 */ 44static const __u8 gembird_jpd_fixed_rdesc[] = { 45 0x75, 0x08, /* Report Size (8) */ 46 0x95, 0x02, /* Report Count (2) */ 47 0x15, 0x00, /* Logical Minimum (0) */ 48 0x26, 0xff, 0x00, /* Logical Maximum (255) */ 49 0x35, 0x00, /* Physical Minimum (0) */ 50 0x46, 0xff, 0x00, /* Physical Maximum (255) */ 51 0x09, 0x30, /* Usage (X) */ 52 0x09, 0x31, /* Usage (Y) */ 53 0x81, 0x02, /* Input (Data,Var,Abs) */ 54 0x95, 0x01, /* Report Count (1) */ 55 0x09, 0x32, /* Usage (Z) */ 56 0x81, 0x01, /* Input (Cnst,Arr,Abs) */ 57 0x95, 0x02, /* Report Count (2) */ 58 0x09, 0x33, /* Usage (Rx) */ 59 0x09, 0x34, /* Usage (Ry) */ 60 0x81, 0x02, /* Input (Data,Var,Abs) */ 61}; 62 63static __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc, 64 unsigned int *rsize) 65{ 66 __u8 *new_rdesc; 67 /* delta_size is > 0 */ 68 size_t delta_size = sizeof(gembird_jpd_fixed_rdesc) - 69 sizeof(gembird_jpd_faulty_rdesc); 70 size_t new_size = *rsize + delta_size; 71 72 if (*rsize >= 31 && !memcmp(&rdesc[GEMBIRD_START_FAULTY_RDESC], 73 gembird_jpd_faulty_rdesc, 74 sizeof(gembird_jpd_faulty_rdesc))) { 75 new_rdesc = devm_kzalloc(&hdev->dev, new_size, GFP_KERNEL); 76 if (new_rdesc == NULL) 77 return rdesc; 78 79 dev_info(&hdev->dev, 80 "fixing Gembird JPD-DualForce 2 report descriptor.\n"); 81 82 /* start by copying the end of the rdesc */ 83 memcpy(new_rdesc + delta_size, rdesc, *rsize); 84 85 /* add the correct beginning */ 86 memcpy(new_rdesc, rdesc, GEMBIRD_START_FAULTY_RDESC); 87 88 /* replace the faulty part with the fixed one */ 89 memcpy(new_rdesc + GEMBIRD_START_FAULTY_RDESC, 90 gembird_jpd_fixed_rdesc, 91 sizeof(gembird_jpd_fixed_rdesc)); 92 93 *rsize = new_size; 94 rdesc = new_rdesc; 95 } 96 97 return rdesc; 98} 99 100static const struct hid_device_id gembird_devices[] = { 101 { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD, 102 USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2) }, 103 { } 104}; 105MODULE_DEVICE_TABLE(hid, gembird_devices); 106 107static struct hid_driver gembird_driver = { 108 .name = "gembird", 109 .id_table = gembird_devices, 110 .report_fixup = gembird_report_fixup, 111}; 112module_hid_driver(gembird_driver); 113 114MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); 115MODULE_DESCRIPTION("HID Gembird joypad driver"); 116MODULE_LICENSE("GPL"); 117