162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for Gembird Joypad, "PC Game Controller" 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Red Hat, Inc 662306a36Sopenharmony_ci * Copyright (c) 2015 Benjamin Tissoires 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/hid.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "hid-ids.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define GEMBIRD_START_FAULTY_RDESC 8 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic const __u8 gembird_jpd_faulty_rdesc[] = { 2162306a36Sopenharmony_ci 0x75, 0x08, /* Report Size (8) */ 2262306a36Sopenharmony_ci 0x95, 0x05, /* Report Count (5) */ 2362306a36Sopenharmony_ci 0x15, 0x00, /* Logical Minimum (0) */ 2462306a36Sopenharmony_ci 0x26, 0xff, 0x00, /* Logical Maximum (255) */ 2562306a36Sopenharmony_ci 0x35, 0x00, /* Physical Minimum (0) */ 2662306a36Sopenharmony_ci 0x46, 0xff, 0x00, /* Physical Maximum (255) */ 2762306a36Sopenharmony_ci 0x09, 0x30, /* Usage (X) */ 2862306a36Sopenharmony_ci 0x09, 0x31, /* Usage (Y) */ 2962306a36Sopenharmony_ci 0x09, 0x32, /* Usage (Z) */ 3062306a36Sopenharmony_ci 0x09, 0x32, /* Usage (Z) */ 3162306a36Sopenharmony_ci 0x09, 0x35, /* Usage (Rz) */ 3262306a36Sopenharmony_ci 0x81, 0x02, /* Input (Data,Var,Abs) */ 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * we fix the report descriptor by: 3762306a36Sopenharmony_ci * - marking the first Z axis as constant (so it is ignored by HID) 3862306a36Sopenharmony_ci * - assign the original second Z to Rx 3962306a36Sopenharmony_ci * - assign the original Rz to Ry 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic const __u8 gembird_jpd_fixed_rdesc[] = { 4262306a36Sopenharmony_ci 0x75, 0x08, /* Report Size (8) */ 4362306a36Sopenharmony_ci 0x95, 0x02, /* Report Count (2) */ 4462306a36Sopenharmony_ci 0x15, 0x00, /* Logical Minimum (0) */ 4562306a36Sopenharmony_ci 0x26, 0xff, 0x00, /* Logical Maximum (255) */ 4662306a36Sopenharmony_ci 0x35, 0x00, /* Physical Minimum (0) */ 4762306a36Sopenharmony_ci 0x46, 0xff, 0x00, /* Physical Maximum (255) */ 4862306a36Sopenharmony_ci 0x09, 0x30, /* Usage (X) */ 4962306a36Sopenharmony_ci 0x09, 0x31, /* Usage (Y) */ 5062306a36Sopenharmony_ci 0x81, 0x02, /* Input (Data,Var,Abs) */ 5162306a36Sopenharmony_ci 0x95, 0x01, /* Report Count (1) */ 5262306a36Sopenharmony_ci 0x09, 0x32, /* Usage (Z) */ 5362306a36Sopenharmony_ci 0x81, 0x01, /* Input (Cnst,Arr,Abs) */ 5462306a36Sopenharmony_ci 0x95, 0x02, /* Report Count (2) */ 5562306a36Sopenharmony_ci 0x09, 0x33, /* Usage (Rx) */ 5662306a36Sopenharmony_ci 0x09, 0x34, /* Usage (Ry) */ 5762306a36Sopenharmony_ci 0x81, 0x02, /* Input (Data,Var,Abs) */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc, 6162306a36Sopenharmony_ci unsigned int *rsize) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci __u8 *new_rdesc; 6462306a36Sopenharmony_ci /* delta_size is > 0 */ 6562306a36Sopenharmony_ci size_t delta_size = sizeof(gembird_jpd_fixed_rdesc) - 6662306a36Sopenharmony_ci sizeof(gembird_jpd_faulty_rdesc); 6762306a36Sopenharmony_ci size_t new_size = *rsize + delta_size; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (*rsize >= 31 && !memcmp(&rdesc[GEMBIRD_START_FAULTY_RDESC], 7062306a36Sopenharmony_ci gembird_jpd_faulty_rdesc, 7162306a36Sopenharmony_ci sizeof(gembird_jpd_faulty_rdesc))) { 7262306a36Sopenharmony_ci new_rdesc = devm_kzalloc(&hdev->dev, new_size, GFP_KERNEL); 7362306a36Sopenharmony_ci if (new_rdesc == NULL) 7462306a36Sopenharmony_ci return rdesc; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci dev_info(&hdev->dev, 7762306a36Sopenharmony_ci "fixing Gembird JPD-DualForce 2 report descriptor.\n"); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* start by copying the end of the rdesc */ 8062306a36Sopenharmony_ci memcpy(new_rdesc + delta_size, rdesc, *rsize); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* add the correct beginning */ 8362306a36Sopenharmony_ci memcpy(new_rdesc, rdesc, GEMBIRD_START_FAULTY_RDESC); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* replace the faulty part with the fixed one */ 8662306a36Sopenharmony_ci memcpy(new_rdesc + GEMBIRD_START_FAULTY_RDESC, 8762306a36Sopenharmony_ci gembird_jpd_fixed_rdesc, 8862306a36Sopenharmony_ci sizeof(gembird_jpd_fixed_rdesc)); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci *rsize = new_size; 9162306a36Sopenharmony_ci rdesc = new_rdesc; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return rdesc; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic const struct hid_device_id gembird_devices[] = { 9862306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD, 9962306a36Sopenharmony_ci USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2) }, 10062306a36Sopenharmony_ci { } 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, gembird_devices); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic struct hid_driver gembird_driver = { 10562306a36Sopenharmony_ci .name = "gembird", 10662306a36Sopenharmony_ci .id_table = gembird_devices, 10762306a36Sopenharmony_ci .report_fixup = gembird_report_fixup, 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_cimodule_hid_driver(gembird_driver); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); 11262306a36Sopenharmony_ciMODULE_DESCRIPTION("HID Gembird joypad driver"); 11362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 114