root/drivers/hid/hid-saitek.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. saitek_probe
  2. saitek_report_fixup
  3. saitek_raw_event
  4. saitek_event

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  HID driver for Saitek devices.
   4  *
   5  *  PS1000 (USB gamepad):
   6  *  Fixes the HID report descriptor by removing a non-existent axis and
   7  *  clearing the constant bit on the input reports for buttons and d-pad.
   8  *  (This module is based on "hid-ortek".)
   9  *  Copyright (c) 2012 Andreas Hübner
  10  *
  11  *  R.A.T.7, R.A.T.9, M.M.O.7 (USB gaming mice):
  12  *  Fixes the mode button which cycles through three constantly pressed
  13  *  buttons. All three press events are mapped to one button and the
  14  *  missing release event is generated immediately.
  15  */
  16 
  17 /*
  18  */
  19 
  20 #include <linux/device.h>
  21 #include <linux/hid.h>
  22 #include <linux/module.h>
  23 #include <linux/kernel.h>
  24 
  25 #include "hid-ids.h"
  26 
  27 #define SAITEK_FIX_PS1000       0x0001
  28 #define SAITEK_RELEASE_MODE_RAT7        0x0002
  29 #define SAITEK_RELEASE_MODE_MMO7        0x0004
  30 
  31 struct saitek_sc {
  32         unsigned long quirks;
  33         int mode;
  34 };
  35 
  36 static int saitek_probe(struct hid_device *hdev,
  37                 const struct hid_device_id *id)
  38 {
  39         unsigned long quirks = id->driver_data;
  40         struct saitek_sc *ssc;
  41         int ret;
  42 
  43         ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL);
  44         if (ssc == NULL) {
  45                 hid_err(hdev, "can't alloc saitek descriptor\n");
  46                 return -ENOMEM;
  47         }
  48 
  49         ssc->quirks = quirks;
  50         ssc->mode = -1;
  51 
  52         hid_set_drvdata(hdev, ssc);
  53 
  54         ret = hid_parse(hdev);
  55         if (ret) {
  56                 hid_err(hdev, "parse failed\n");
  57                 return ret;
  58         }
  59 
  60         ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  61         if (ret) {
  62                 hid_err(hdev, "hw start failed\n");
  63                 return ret;
  64         }
  65 
  66         return 0;
  67 }
  68 
  69 static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  70                 unsigned int *rsize)
  71 {
  72         struct saitek_sc *ssc = hid_get_drvdata(hdev);
  73 
  74         if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 &&
  75                         rdesc[20] == 0x09 && rdesc[21] == 0x33 &&
  76                         rdesc[94] == 0x81 && rdesc[95] == 0x03 &&
  77                         rdesc[110] == 0x81 && rdesc[111] == 0x03) {
  78 
  79                 hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n");
  80 
  81                 /* convert spurious axis to a "noop" Logical Minimum (0) */
  82                 rdesc[20] = 0x15;
  83                 rdesc[21] = 0x00;
  84 
  85                 /* clear constant bit on buttons and d-pad */
  86                 rdesc[95] = 0x02;
  87                 rdesc[111] = 0x02;
  88 
  89         }
  90         return rdesc;
  91 }
  92 
  93 static int saitek_raw_event(struct hid_device *hdev,
  94                 struct hid_report *report, u8 *raw_data, int size)
  95 {
  96         struct saitek_sc *ssc = hid_get_drvdata(hdev);
  97 
  98         if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) {
  99                 /* R.A.T.7 uses bits 13, 14, 15 for the mode */
 100                 int mode = -1;
 101                 if (raw_data[1] & 0x01)
 102                         mode = 0;
 103                 else if (raw_data[1] & 0x02)
 104                         mode = 1;
 105                 else if (raw_data[1] & 0x04)
 106                         mode = 2;
 107 
 108                 /* clear mode bits */
 109                 raw_data[1] &= ~0x07;
 110 
 111                 if (mode != ssc->mode) {
 112                         hid_dbg(hdev, "entered mode %d\n", mode);
 113                         if (ssc->mode != -1) {
 114                                 /* use bit 13 as the mode button */
 115                                 raw_data[1] |= 0x04;
 116                         }
 117                         ssc->mode = mode;
 118                 }
 119         } else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) {
 120 
 121                 /* M.M.O.7 uses bits 8, 22, 23 for the mode */
 122                 int mode = -1;
 123                 if (raw_data[1] & 0x80)
 124                         mode = 0;
 125                 else if (raw_data[2] & 0x01)
 126                         mode = 1;
 127                 else if (raw_data[2] & 0x02)
 128                         mode = 2;
 129 
 130                 /* clear mode bits */
 131                 raw_data[1] &= ~0x80;
 132                 raw_data[2] &= ~0x03;
 133 
 134                 if (mode != ssc->mode) {
 135                         hid_dbg(hdev, "entered mode %d\n", mode);
 136                         if (ssc->mode != -1) {
 137                                 /* use bit 8 as the mode button, bits 22
 138                                  * and 23 do not represent buttons
 139                                  * according to the HID report descriptor
 140                                  */
 141                                 raw_data[1] |= 0x80;
 142                         }
 143                         ssc->mode = mode;
 144                 }
 145         }
 146 
 147         return 0;
 148 }
 149 
 150 static int saitek_event(struct hid_device *hdev, struct hid_field *field,
 151                 struct hid_usage *usage, __s32 value)
 152 {
 153         struct saitek_sc *ssc = hid_get_drvdata(hdev);
 154         struct input_dev *input = field->hidinput->input;
 155 
 156         if (usage->type == EV_KEY && value &&
 157                         (((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) &&
 158                           usage->code - BTN_MOUSE == 10) ||
 159                         ((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) &&
 160                          usage->code - BTN_MOUSE == 15))) {
 161 
 162                 input_report_key(input, usage->code, 1);
 163 
 164                 /* report missing release event */
 165                 input_report_key(input, usage->code, 0);
 166 
 167                 return 1;
 168         }
 169 
 170         return 0;
 171 }
 172 
 173 static const struct hid_device_id saitek_devices[] = {
 174         { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
 175                 .driver_data = SAITEK_FIX_PS1000 },
 176         { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5),
 177                 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
 178         { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD),
 179                 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
 180         { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
 181                 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
 182         { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_CONTAGION),
 183                 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
 184         { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9),
 185                 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
 186         { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9),
 187                 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
 188         { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
 189                 .driver_data = SAITEK_RELEASE_MODE_MMO7 },
 190         { }
 191 };
 192 
 193 MODULE_DEVICE_TABLE(hid, saitek_devices);
 194 
 195 static struct hid_driver saitek_driver = {
 196         .name = "saitek",
 197         .id_table = saitek_devices,
 198         .probe = saitek_probe,
 199         .report_fixup = saitek_report_fixup,
 200         .raw_event = saitek_raw_event,
 201         .event = saitek_event,
 202 };
 203 module_hid_driver(saitek_driver);
 204 
 205 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */