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