Merge remote-tracking branches 'asoc/topic/wm8960', 'asoc/topic/wm8978' and 'asoc...
[sfrench/cifs-2.6.git] / drivers / hid / hid-mf.c
1 /*
2  * Force feedback support for Mayflash game controller adapters.
3  *
4  * These devices are manufactured by Mayflash but identify themselves
5  * using the vendor ID of DragonRise Inc.
6  *
7  * Tested with:
8  * 0079:1801 "DragonRise Inc. Mayflash PS3 Game Controller Adapter"
9  * 0079:1803 "DragonRise Inc. Mayflash Wireless Sensor DolphinBar"
10  * 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter"
11  * 0079:1844 "DragonRise Inc. Mayflash GameCube Game Controller Adapter (v04)"
12  *
13  * The following adapters probably work too, but need to be tested:
14  * 0079:1800 "DragonRise Inc. Mayflash WIIU Game Controller Adapter"
15  *
16  * Copyright (c) 2016-2017 Marcel Hasler <mahasler@gmail.com>
17  */
18
19 /*
20  * This program is free software; you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License as published by
22  * the Free Software Foundation; either version 2 of the License, or
23  * (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  * GNU General Public License for more details.
29  */
30
31 #include <linux/input.h>
32 #include <linux/slab.h>
33 #include <linux/hid.h>
34 #include <linux/module.h>
35
36 #include "hid-ids.h"
37
38 struct mf_device {
39         struct hid_report *report;
40 };
41
42 static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect)
43 {
44         struct hid_device *hid = input_get_drvdata(dev);
45         struct mf_device *mf = data;
46         int strong, weak;
47
48         strong = effect->u.rumble.strong_magnitude;
49         weak = effect->u.rumble.weak_magnitude;
50
51         dbg_hid("Called with 0x%04x 0x%04x.\n", strong, weak);
52
53         strong = strong * 0xff / 0xffff;
54         weak = weak * 0xff / 0xffff;
55
56         dbg_hid("Running with 0x%02x 0x%02x.\n", strong, weak);
57
58         mf->report->field[0]->value[0] = weak;
59         mf->report->field[0]->value[1] = strong;
60         hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT);
61
62         return 0;
63 }
64
65 static int mf_init(struct hid_device *hid)
66 {
67         struct mf_device *mf;
68
69         struct list_head *report_list =
70                         &hid->report_enum[HID_OUTPUT_REPORT].report_list;
71
72         struct list_head *report_ptr;
73         struct hid_report *report;
74
75         struct list_head *input_ptr = &hid->inputs;
76         struct hid_input *input;
77
78         struct input_dev *dev;
79
80         int error;
81
82         /* Setup each of the four inputs */
83         list_for_each(report_ptr, report_list) {
84                 report = list_entry(report_ptr, struct hid_report, list);
85
86                 if (report->maxfield < 1 || report->field[0]->report_count < 2) {
87                         hid_err(hid, "Invalid report, this should never happen!\n");
88                         return -ENODEV;
89                 }
90
91                 if (list_is_last(input_ptr, &hid->inputs)) {
92                         hid_err(hid, "Missing input, this should never happen!\n");
93                         return -ENODEV;
94                 }
95
96                 input_ptr = input_ptr->next;
97                 input = list_entry(input_ptr, struct hid_input, list);
98
99                 mf = kzalloc(sizeof(struct mf_device), GFP_KERNEL);
100                 if (!mf)
101                         return -ENOMEM;
102
103                 dev = input->input;
104                 set_bit(FF_RUMBLE, dev->ffbit);
105
106                 error = input_ff_create_memless(dev, mf, mf_play);
107                 if (error) {
108                         kfree(mf);
109                         return error;
110                 }
111
112                 mf->report = report;
113                 mf->report->field[0]->value[0] = 0x00;
114                 mf->report->field[0]->value[1] = 0x00;
115                 hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT);
116         }
117
118         hid_info(hid, "Force feedback for HJZ Mayflash game controller "
119                       "adapters by Marcel Hasler <mahasler@gmail.com>\n");
120
121         return 0;
122 }
123
124 static int mf_probe(struct hid_device *hid, const struct hid_device_id *id)
125 {
126         int error;
127
128         dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n");
129
130         /* Apply quirks as needed */
131         hid->quirks |= id->driver_data;
132
133         error = hid_parse(hid);
134         if (error) {
135                 hid_err(hid, "HID parse failed.\n");
136                 return error;
137         }
138
139         error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
140         if (error) {
141                 hid_err(hid, "HID hw start failed\n");
142                 return error;
143         }
144
145         error = mf_init(hid);
146         if (error) {
147                 hid_err(hid, "Force feedback init failed.\n");
148                 hid_hw_stop(hid);
149                 return error;
150         }
151
152         return 0;
153 }
154
155 static const struct hid_device_id mf_devices[] = {
156         { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3),
157                 .driver_data = HID_QUIRK_MULTI_INPUT },
158         { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR),
159                 .driver_data = HID_QUIRK_MULTI_INPUT },
160         { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1),
161                 .driver_data = HID_QUIRK_MULTI_INPUT },
162         { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2),
163                 .driver_data = 0 }, /* No quirk required */
164         { }
165 };
166 MODULE_DEVICE_TABLE(hid, mf_devices);
167
168 static struct hid_driver mf_driver = {
169         .name = "hid_mf",
170         .id_table = mf_devices,
171         .probe = mf_probe,
172 };
173 module_hid_driver(mf_driver);
174
175 MODULE_LICENSE("GPL");