Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Force feedback support for Holtek On Line Grip based gamepads
0004  *
0005  *  These include at least a Brazilian "Clone Joypad Super Power Fire"
0006  *  which uses vendor ID 0x1241 and identifies as "HOLTEK On Line Grip".
0007  *
0008  *  Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
0009  */
0010 
0011 /*
0012  */
0013 
0014 #include <linux/hid.h>
0015 #include <linux/input.h>
0016 #include <linux/module.h>
0017 #include <linux/slab.h>
0018 
0019 #include "hid-ids.h"
0020 
0021 #ifdef CONFIG_HOLTEK_FF
0022 
0023 /*
0024  * These commands and parameters are currently known:
0025  *
0026  * byte 0: command id:
0027  *  01  set effect parameters
0028  *  02  play specified effect
0029  *  03  stop specified effect
0030  *  04  stop all effects
0031  *  06  stop all effects
0032  *  (the difference between 04 and 06 isn't known; win driver
0033  *   sends 06,04 on application init, and 06 otherwise)
0034  * 
0035  * Commands 01 and 02 need to be sent as pairs, i.e. you need to send 01
0036  * before each 02.
0037  *
0038  * The rest of the bytes are parameters. Command 01 takes all of them, and
0039  * commands 02,03 take only the effect id.
0040  *
0041  * byte 1:
0042  *  bits 0-3: effect id:
0043  *      1: very strong rumble
0044  *      2: periodic rumble, short intervals
0045  *      3: very strong rumble
0046  *      4: periodic rumble, long intervals
0047  *      5: weak periodic rumble, long intervals
0048  *      6: weak periodic rumble, short intervals
0049  *      7: periodic rumble, short intervals
0050  *      8: strong periodic rumble, short intervals
0051  *      9: very strong rumble
0052  *      a: causes an error
0053  *      b: very strong periodic rumble, very short intervals
0054  *      c-f: nothing
0055  *  bit 6: right (weak) motor enabled
0056  *  bit 7: left (strong) motor enabled
0057  *
0058  * bytes 2-3:  time in milliseconds, big-endian
0059  * bytes 5-6:  unknown (win driver seems to use at least 10e0 with effect 1
0060  *             and 0014 with effect 6)
0061  * byte 7:
0062  *  bits 0-3: effect magnitude
0063  */
0064 
0065 #define HOLTEKFF_MSG_LENGTH     7
0066 
0067 static const u8 start_effect_1[] = { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
0068 static const u8 stop_all4[] =      { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
0069 static const u8 stop_all6[] =      { 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
0070 
0071 struct holtekff_device {
0072     struct hid_field *field;
0073 };
0074 
0075 static void holtekff_send(struct holtekff_device *holtekff,
0076               struct hid_device *hid,
0077               const u8 data[HOLTEKFF_MSG_LENGTH])
0078 {
0079     int i;
0080 
0081     for (i = 0; i < HOLTEKFF_MSG_LENGTH; i++) {
0082         holtekff->field->value[i] = data[i];
0083     }
0084 
0085     dbg_hid("sending %7ph\n", data);
0086 
0087     hid_hw_request(hid, holtekff->field->report, HID_REQ_SET_REPORT);
0088 }
0089 
0090 static int holtekff_play(struct input_dev *dev, void *data,
0091              struct ff_effect *effect)
0092 {
0093     struct hid_device *hid = input_get_drvdata(dev);
0094     struct holtekff_device *holtekff = data;
0095     int left, right;
0096     /* effect type 1, length 65535 msec */
0097     u8 buf[HOLTEKFF_MSG_LENGTH] =
0098         { 0x01, 0x01, 0xff, 0xff, 0x10, 0xe0, 0x00 };
0099 
0100     left = effect->u.rumble.strong_magnitude;
0101     right = effect->u.rumble.weak_magnitude;
0102     dbg_hid("called with 0x%04x 0x%04x\n", left, right);
0103 
0104     if (!left && !right) {
0105         holtekff_send(holtekff, hid, stop_all6);
0106         return 0;
0107     }
0108 
0109     if (left)
0110         buf[1] |= 0x80;
0111     if (right)
0112         buf[1] |= 0x40;
0113 
0114     /* The device takes a single magnitude, so we just sum them up. */
0115     buf[6] = min(0xf, (left >> 12) + (right >> 12));
0116 
0117     holtekff_send(holtekff, hid, buf);
0118     holtekff_send(holtekff, hid, start_effect_1);
0119 
0120     return 0;
0121 }
0122 
0123 static int holtekff_init(struct hid_device *hid)
0124 {
0125     struct holtekff_device *holtekff;
0126     struct hid_report *report;
0127     struct hid_input *hidinput;
0128     struct list_head *report_list =
0129             &hid->report_enum[HID_OUTPUT_REPORT].report_list;
0130     struct input_dev *dev;
0131     int error;
0132 
0133     if (list_empty(&hid->inputs)) {
0134         hid_err(hid, "no inputs found\n");
0135         return -ENODEV;
0136     }
0137     hidinput = list_entry(hid->inputs.next, struct hid_input, list);
0138     dev = hidinput->input;
0139 
0140     if (list_empty(report_list)) {
0141         hid_err(hid, "no output report found\n");
0142         return -ENODEV;
0143     }
0144 
0145     report = list_entry(report_list->next, struct hid_report, list);
0146 
0147     if (report->maxfield < 1 || report->field[0]->report_count != 7) {
0148         hid_err(hid, "unexpected output report layout\n");
0149         return -ENODEV;
0150     }
0151 
0152     holtekff = kzalloc(sizeof(*holtekff), GFP_KERNEL);
0153     if (!holtekff)
0154         return -ENOMEM;
0155 
0156     set_bit(FF_RUMBLE, dev->ffbit);
0157 
0158     holtekff->field = report->field[0];
0159 
0160     /* initialize the same way as win driver does */
0161     holtekff_send(holtekff, hid, stop_all4);
0162     holtekff_send(holtekff, hid, stop_all6);
0163 
0164     error = input_ff_create_memless(dev, holtekff, holtekff_play);
0165     if (error) {
0166         kfree(holtekff);
0167         return error;
0168     }
0169 
0170     hid_info(hid, "Force feedback for Holtek On Line Grip based devices by Anssi Hannula <anssi.hannula@iki.fi>\n");
0171 
0172     return 0;
0173 }
0174 #else
0175 static inline int holtekff_init(struct hid_device *hid)
0176 {
0177     return 0;
0178 }
0179 #endif
0180 
0181 static int holtek_probe(struct hid_device *hdev, const struct hid_device_id *id)
0182 {
0183     int ret;
0184 
0185     ret = hid_parse(hdev);
0186     if (ret) {
0187         hid_err(hdev, "parse failed\n");
0188         goto err;
0189     }
0190 
0191     ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
0192     if (ret) {
0193         hid_err(hdev, "hw start failed\n");
0194         goto err;
0195     }
0196 
0197     holtekff_init(hdev);
0198 
0199     return 0;
0200 err:
0201     return ret;
0202 }
0203 
0204 static const struct hid_device_id holtek_devices[] = {
0205     { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
0206     { }
0207 };
0208 MODULE_DEVICE_TABLE(hid, holtek_devices);
0209 
0210 static struct hid_driver holtek_driver = {
0211     .name = "holtek",
0212     .id_table = holtek_devices,
0213     .probe = holtek_probe,
0214 };
0215 module_hid_driver(holtek_driver);
0216 
0217 MODULE_LICENSE("GPL");
0218 MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
0219 MODULE_DESCRIPTION("Force feedback support for Holtek On Line Grip based devices");