Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Force feedback support for Zeroplus based devices
0004  *
0005  *  Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
0006  */
0007 
0008 /*
0009  */
0010 
0011 
0012 #include <linux/hid.h>
0013 #include <linux/input.h>
0014 #include <linux/slab.h>
0015 #include <linux/module.h>
0016 
0017 #include "hid-ids.h"
0018 
0019 #ifdef CONFIG_ZEROPLUS_FF
0020 
0021 struct zpff_device {
0022     struct hid_report *report;
0023 };
0024 
0025 static int zpff_play(struct input_dev *dev, void *data,
0026              struct ff_effect *effect)
0027 {
0028     struct hid_device *hid = input_get_drvdata(dev);
0029     struct zpff_device *zpff = data;
0030     int left, right;
0031 
0032     /*
0033      * The following is specified the other way around in the Zeroplus
0034      * datasheet but the order below is correct for the XFX Executioner;
0035      * however it is possible that the XFX Executioner is an exception
0036      */
0037 
0038     left = effect->u.rumble.strong_magnitude;
0039     right = effect->u.rumble.weak_magnitude;
0040     dbg_hid("called with 0x%04x 0x%04x\n", left, right);
0041 
0042     left = left * 0x7f / 0xffff;
0043     right = right * 0x7f / 0xffff;
0044 
0045     zpff->report->field[2]->value[0] = left;
0046     zpff->report->field[3]->value[0] = right;
0047     dbg_hid("running with 0x%02x 0x%02x\n", left, right);
0048     hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT);
0049 
0050     return 0;
0051 }
0052 
0053 static int zpff_init(struct hid_device *hid)
0054 {
0055     struct zpff_device *zpff;
0056     struct hid_report *report;
0057     struct hid_input *hidinput;
0058     struct input_dev *dev;
0059     int i, error;
0060 
0061     if (list_empty(&hid->inputs)) {
0062         hid_err(hid, "no inputs found\n");
0063         return -ENODEV;
0064     }
0065     hidinput = list_entry(hid->inputs.next, struct hid_input, list);
0066     dev = hidinput->input;
0067 
0068     for (i = 0; i < 4; i++) {
0069         report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
0070         if (!report)
0071             return -ENODEV;
0072     }
0073 
0074     zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
0075     if (!zpff)
0076         return -ENOMEM;
0077 
0078     set_bit(FF_RUMBLE, dev->ffbit);
0079 
0080     error = input_ff_create_memless(dev, zpff, zpff_play);
0081     if (error) {
0082         kfree(zpff);
0083         return error;
0084     }
0085 
0086     zpff->report = report;
0087     zpff->report->field[0]->value[0] = 0x00;
0088     zpff->report->field[1]->value[0] = 0x02;
0089     zpff->report->field[2]->value[0] = 0x00;
0090     zpff->report->field[3]->value[0] = 0x00;
0091     hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT);
0092 
0093     hid_info(hid, "force feedback for Zeroplus based devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
0094 
0095     return 0;
0096 }
0097 #else
0098 static inline int zpff_init(struct hid_device *hid)
0099 {
0100     return 0;
0101 }
0102 #endif
0103 
0104 static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id)
0105 {
0106     int ret;
0107 
0108     ret = hid_parse(hdev);
0109     if (ret) {
0110         hid_err(hdev, "parse failed\n");
0111         goto err;
0112     }
0113 
0114     ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
0115     if (ret) {
0116         hid_err(hdev, "hw start failed\n");
0117         goto err;
0118     }
0119 
0120     zpff_init(hdev);
0121 
0122     return 0;
0123 err:
0124     return ret;
0125 }
0126 
0127 static const struct hid_device_id zp_devices[] = {
0128     { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
0129     { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
0130     { }
0131 };
0132 MODULE_DEVICE_TABLE(hid, zp_devices);
0133 
0134 static struct hid_driver zp_driver = {
0135     .name = "zeroplus",
0136     .id_table = zp_devices,
0137     .probe = zp_probe,
0138 };
0139 module_hid_driver(zp_driver);
0140 
0141 MODULE_LICENSE("GPL");