linux/drivers/hid/hid-zpff.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Force feedback support for Zeroplus based devices
   4 *
   5 *  Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
   6 */
   7
   8/*
   9 */
  10
  11
  12#include <linux/hid.h>
  13#include <linux/input.h>
  14#include <linux/slab.h>
  15#include <linux/module.h>
  16
  17#include "hid-ids.h"
  18
  19#ifdef CONFIG_ZEROPLUS_FF
  20
  21struct zpff_device {
  22        struct hid_report *report;
  23};
  24
  25static int zpff_play(struct input_dev *dev, void *data,
  26                         struct ff_effect *effect)
  27{
  28        struct hid_device *hid = input_get_drvdata(dev);
  29        struct zpff_device *zpff = data;
  30        int left, right;
  31
  32        /*
  33         * The following is specified the other way around in the Zeroplus
  34         * datasheet but the order below is correct for the XFX Executioner;
  35         * however it is possible that the XFX Executioner is an exception
  36         */
  37
  38        left = effect->u.rumble.strong_magnitude;
  39        right = effect->u.rumble.weak_magnitude;
  40        dbg_hid("called with 0x%04x 0x%04x\n", left, right);
  41
  42        left = left * 0x7f / 0xffff;
  43        right = right * 0x7f / 0xffff;
  44
  45        zpff->report->field[2]->value[0] = left;
  46        zpff->report->field[3]->value[0] = right;
  47        dbg_hid("running with 0x%02x 0x%02x\n", left, right);
  48        hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT);
  49
  50        return 0;
  51}
  52
  53static int zpff_init(struct hid_device *hid)
  54{
  55        struct zpff_device *zpff;
  56        struct hid_report *report;
  57        struct hid_input *hidinput = list_entry(hid->inputs.next,
  58                                                struct hid_input, list);
  59        struct input_dev *dev = hidinput->input;
  60        int i, error;
  61
  62        for (i = 0; i < 4; i++) {
  63                report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
  64                if (!report)
  65                        return -ENODEV;
  66        }
  67
  68        zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
  69        if (!zpff)
  70                return -ENOMEM;
  71
  72        set_bit(FF_RUMBLE, dev->ffbit);
  73
  74        error = input_ff_create_memless(dev, zpff, zpff_play);
  75        if (error) {
  76                kfree(zpff);
  77                return error;
  78        }
  79
  80        zpff->report = report;
  81        zpff->report->field[0]->value[0] = 0x00;
  82        zpff->report->field[1]->value[0] = 0x02;
  83        zpff->report->field[2]->value[0] = 0x00;
  84        zpff->report->field[3]->value[0] = 0x00;
  85        hid_hw_request(hid, zpff->report, HID_REQ_SET_REPORT);
  86
  87        hid_info(hid, "force feedback for Zeroplus based devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
  88
  89        return 0;
  90}
  91#else
  92static inline int zpff_init(struct hid_device *hid)
  93{
  94        return 0;
  95}
  96#endif
  97
  98static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id)
  99{
 100        int ret;
 101
 102        ret = hid_parse(hdev);
 103        if (ret) {
 104                hid_err(hdev, "parse failed\n");
 105                goto err;
 106        }
 107
 108        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
 109        if (ret) {
 110                hid_err(hdev, "hw start failed\n");
 111                goto err;
 112        }
 113
 114        zpff_init(hdev);
 115
 116        return 0;
 117err:
 118        return ret;
 119}
 120
 121static const struct hid_device_id zp_devices[] = {
 122        { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
 123        { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
 124        { }
 125};
 126MODULE_DEVICE_TABLE(hid, zp_devices);
 127
 128static struct hid_driver zp_driver = {
 129        .name = "zeroplus",
 130        .id_table = zp_devices,
 131        .probe = zp_probe,
 132};
 133module_hid_driver(zp_driver);
 134
 135MODULE_LICENSE("GPL");
 136