12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2192a1acfSDavid Herrmann /*
392eda7e4SDavid Herrmann * HID driver for Nintendo Wii / Wii U peripherals
492eda7e4SDavid Herrmann * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com>
5192a1acfSDavid Herrmann */
6192a1acfSDavid Herrmann
7192a1acfSDavid Herrmann /*
8192a1acfSDavid Herrmann */
9192a1acfSDavid Herrmann
10192a1acfSDavid Herrmann #include <linux/completion.h>
11192a1acfSDavid Herrmann #include <linux/device.h>
12192a1acfSDavid Herrmann #include <linux/hid.h>
13192a1acfSDavid Herrmann #include <linux/input.h>
14192a1acfSDavid Herrmann #include <linux/module.h>
15192a1acfSDavid Herrmann #include <linux/mutex.h>
16192a1acfSDavid Herrmann #include <linux/spinlock.h>
17192a1acfSDavid Herrmann #include "hid-ids.h"
187e274400SDavid Herrmann #include "hid-wiimote.h"
19192a1acfSDavid Herrmann
2013938538SDavid Herrmann /* output queue handling */
2113938538SDavid Herrmann
wiimote_hid_send(struct hid_device * hdev,__u8 * buffer,size_t count)22d758b1f0SDavid Herrmann static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
23192a1acfSDavid Herrmann size_t count)
24192a1acfSDavid Herrmann {
25192a1acfSDavid Herrmann __u8 *buf;
26d758b1f0SDavid Herrmann int ret;
27192a1acfSDavid Herrmann
287e0bc880SBenjamin Tissoires if (!hdev->ll_driver->output_report)
29192a1acfSDavid Herrmann return -ENODEV;
30192a1acfSDavid Herrmann
31192a1acfSDavid Herrmann buf = kmemdup(buffer, count, GFP_KERNEL);
32192a1acfSDavid Herrmann if (!buf)
33192a1acfSDavid Herrmann return -ENOMEM;
34192a1acfSDavid Herrmann
357e0bc880SBenjamin Tissoires ret = hid_hw_output_report(hdev, buf, count);
36192a1acfSDavid Herrmann
37192a1acfSDavid Herrmann kfree(buf);
38192a1acfSDavid Herrmann return ret;
39192a1acfSDavid Herrmann }
40192a1acfSDavid Herrmann
wiimote_queue_worker(struct work_struct * work)4113938538SDavid Herrmann static void wiimote_queue_worker(struct work_struct *work)
42192a1acfSDavid Herrmann {
4313938538SDavid Herrmann struct wiimote_queue *queue = container_of(work, struct wiimote_queue,
44192a1acfSDavid Herrmann worker);
4513938538SDavid Herrmann struct wiimote_data *wdata = container_of(queue, struct wiimote_data,
4613938538SDavid Herrmann queue);
47192a1acfSDavid Herrmann unsigned long flags;
48d758b1f0SDavid Herrmann int ret;
49192a1acfSDavid Herrmann
5013938538SDavid Herrmann spin_lock_irqsave(&wdata->queue.lock, flags);
51192a1acfSDavid Herrmann
5213938538SDavid Herrmann while (wdata->queue.head != wdata->queue.tail) {
5313938538SDavid Herrmann spin_unlock_irqrestore(&wdata->queue.lock, flags);
54d758b1f0SDavid Herrmann ret = wiimote_hid_send(wdata->hdev,
5513938538SDavid Herrmann wdata->queue.outq[wdata->queue.tail].data,
5613938538SDavid Herrmann wdata->queue.outq[wdata->queue.tail].size);
57d758b1f0SDavid Herrmann if (ret < 0) {
58d758b1f0SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
59d758b1f0SDavid Herrmann wiimote_cmd_abort(wdata);
60d758b1f0SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
61d758b1f0SDavid Herrmann }
6213938538SDavid Herrmann spin_lock_irqsave(&wdata->queue.lock, flags);
63192a1acfSDavid Herrmann
6413938538SDavid Herrmann wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE;
65192a1acfSDavid Herrmann }
66192a1acfSDavid Herrmann
6713938538SDavid Herrmann spin_unlock_irqrestore(&wdata->queue.lock, flags);
68192a1acfSDavid Herrmann }
69192a1acfSDavid Herrmann
wiimote_queue(struct wiimote_data * wdata,const __u8 * buffer,size_t count)70192a1acfSDavid Herrmann static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
71192a1acfSDavid Herrmann size_t count)
72192a1acfSDavid Herrmann {
73192a1acfSDavid Herrmann unsigned long flags;
74192a1acfSDavid Herrmann __u8 newhead;
75192a1acfSDavid Herrmann
76192a1acfSDavid Herrmann if (count > HID_MAX_BUFFER_SIZE) {
77192a1acfSDavid Herrmann hid_warn(wdata->hdev, "Sending too large output report\n");
78d758b1f0SDavid Herrmann
79d758b1f0SDavid Herrmann spin_lock_irqsave(&wdata->queue.lock, flags);
80d758b1f0SDavid Herrmann goto out_error;
81192a1acfSDavid Herrmann }
82192a1acfSDavid Herrmann
83192a1acfSDavid Herrmann /*
84192a1acfSDavid Herrmann * Copy new request into our output queue and check whether the
85192a1acfSDavid Herrmann * queue is full. If it is full, discard this request.
86192a1acfSDavid Herrmann * If it is empty we need to start a new worker that will
87192a1acfSDavid Herrmann * send out the buffer to the hid device.
88192a1acfSDavid Herrmann * If the queue is not empty, then there must be a worker
89192a1acfSDavid Herrmann * that is currently sending out our buffer and this worker
90192a1acfSDavid Herrmann * will reschedule itself until the queue is empty.
91192a1acfSDavid Herrmann */
92192a1acfSDavid Herrmann
9313938538SDavid Herrmann spin_lock_irqsave(&wdata->queue.lock, flags);
94192a1acfSDavid Herrmann
9513938538SDavid Herrmann memcpy(wdata->queue.outq[wdata->queue.head].data, buffer, count);
9613938538SDavid Herrmann wdata->queue.outq[wdata->queue.head].size = count;
9713938538SDavid Herrmann newhead = (wdata->queue.head + 1) % WIIMOTE_BUFSIZE;
98192a1acfSDavid Herrmann
9913938538SDavid Herrmann if (wdata->queue.head == wdata->queue.tail) {
10013938538SDavid Herrmann wdata->queue.head = newhead;
10113938538SDavid Herrmann schedule_work(&wdata->queue.worker);
10213938538SDavid Herrmann } else if (newhead != wdata->queue.tail) {
10313938538SDavid Herrmann wdata->queue.head = newhead;
104192a1acfSDavid Herrmann } else {
105192a1acfSDavid Herrmann hid_warn(wdata->hdev, "Output queue is full");
106d758b1f0SDavid Herrmann goto out_error;
107192a1acfSDavid Herrmann }
108192a1acfSDavid Herrmann
109d758b1f0SDavid Herrmann goto out_unlock;
110d758b1f0SDavid Herrmann
111d758b1f0SDavid Herrmann out_error:
112d758b1f0SDavid Herrmann wiimote_cmd_abort(wdata);
113d758b1f0SDavid Herrmann out_unlock:
11413938538SDavid Herrmann spin_unlock_irqrestore(&wdata->queue.lock, flags);
115192a1acfSDavid Herrmann }
116192a1acfSDavid Herrmann
117192a1acfSDavid Herrmann /*
118192a1acfSDavid Herrmann * This sets the rumble bit on the given output report if rumble is
119192a1acfSDavid Herrmann * currently enabled.
120192a1acfSDavid Herrmann * \cmd1 must point to the second byte in the output report => &cmd[1]
121192a1acfSDavid Herrmann * This must be called on nearly every output report before passing it
122192a1acfSDavid Herrmann * into the output queue!
123192a1acfSDavid Herrmann */
wiiproto_keep_rumble(struct wiimote_data * wdata,__u8 * cmd1)124192a1acfSDavid Herrmann static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
125192a1acfSDavid Herrmann {
126192a1acfSDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE)
127192a1acfSDavid Herrmann *cmd1 |= 0x01;
128192a1acfSDavid Herrmann }
129192a1acfSDavid Herrmann
wiiproto_req_rumble(struct wiimote_data * wdata,__u8 rumble)13020cef813SDavid Herrmann void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
131192a1acfSDavid Herrmann {
132192a1acfSDavid Herrmann __u8 cmd[2];
133192a1acfSDavid Herrmann
134192a1acfSDavid Herrmann rumble = !!rumble;
135192a1acfSDavid Herrmann if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE))
136192a1acfSDavid Herrmann return;
137192a1acfSDavid Herrmann
138192a1acfSDavid Herrmann if (rumble)
139192a1acfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_RUMBLE;
140192a1acfSDavid Herrmann else
141192a1acfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE;
142192a1acfSDavid Herrmann
143192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_RUMBLE;
144192a1acfSDavid Herrmann cmd[1] = 0;
145192a1acfSDavid Herrmann
146192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
147192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
148192a1acfSDavid Herrmann }
149192a1acfSDavid Herrmann
wiiproto_req_leds(struct wiimote_data * wdata,int leds)1506c5ae018SDavid Herrmann void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
151192a1acfSDavid Herrmann {
152192a1acfSDavid Herrmann __u8 cmd[2];
153192a1acfSDavid Herrmann
154192a1acfSDavid Herrmann leds &= WIIPROTO_FLAGS_LEDS;
155192a1acfSDavid Herrmann if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds)
156192a1acfSDavid Herrmann return;
157192a1acfSDavid Herrmann wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds;
158192a1acfSDavid Herrmann
159192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_LED;
160192a1acfSDavid Herrmann cmd[1] = 0;
161192a1acfSDavid Herrmann
162192a1acfSDavid Herrmann if (leds & WIIPROTO_FLAG_LED1)
163192a1acfSDavid Herrmann cmd[1] |= 0x10;
164192a1acfSDavid Herrmann if (leds & WIIPROTO_FLAG_LED2)
165192a1acfSDavid Herrmann cmd[1] |= 0x20;
166192a1acfSDavid Herrmann if (leds & WIIPROTO_FLAG_LED3)
167192a1acfSDavid Herrmann cmd[1] |= 0x40;
168192a1acfSDavid Herrmann if (leds & WIIPROTO_FLAG_LED4)
169192a1acfSDavid Herrmann cmd[1] |= 0x80;
170192a1acfSDavid Herrmann
171192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
172192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
173192a1acfSDavid Herrmann }
174192a1acfSDavid Herrmann
175192a1acfSDavid Herrmann /*
176192a1acfSDavid Herrmann * Check what peripherals of the wiimote are currently
177192a1acfSDavid Herrmann * active and select a proper DRM that supports all of
178192a1acfSDavid Herrmann * the requested data inputs.
1794148b6bfSDavid Herrmann *
1804148b6bfSDavid Herrmann * Not all combinations are actually supported. The following
1814148b6bfSDavid Herrmann * combinations work only with limitations:
1824148b6bfSDavid Herrmann * - IR cam in extended or full mode disables any data transmission
1834148b6bfSDavid Herrmann * of extension controllers. There is no DRM mode that supports
1844148b6bfSDavid Herrmann * extension bytes plus extended/full IR.
1854148b6bfSDavid Herrmann * - IR cam with accelerometer and extension *_EXT8 is not supported.
1864148b6bfSDavid Herrmann * However, all extensions that need *_EXT8 are devices that don't
1874148b6bfSDavid Herrmann * support IR cameras. Hence, this shouldn't happen under normal
1884148b6bfSDavid Herrmann * operation.
1894148b6bfSDavid Herrmann * - *_EXT16 is only supported in combination with buttons and
1904148b6bfSDavid Herrmann * accelerometer. No IR or similar can be active simultaneously. As
1914148b6bfSDavid Herrmann * above, all modules that require it are mutually exclusive with
1924148b6bfSDavid Herrmann * IR/etc. so this doesn't matter.
193192a1acfSDavid Herrmann */
select_drm(struct wiimote_data * wdata)194192a1acfSDavid Herrmann static __u8 select_drm(struct wiimote_data *wdata)
195192a1acfSDavid Herrmann {
196192a1acfSDavid Herrmann __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
1974148b6bfSDavid Herrmann bool ext;
1984148b6bfSDavid Herrmann
1994148b6bfSDavid Herrmann ext = (wdata->state.flags & WIIPROTO_FLAG_EXT_USED) ||
2004148b6bfSDavid Herrmann (wdata->state.flags & WIIPROTO_FLAG_MP_USED);
201192a1acfSDavid Herrmann
202f1d4bed4SDavid Herrmann /* some 3rd-party balance-boards are hard-coded to KEE, *sigh* */
203f1d4bed4SDavid Herrmann if (wdata->state.devtype == WIIMOTE_DEV_BALANCE_BOARD) {
204f1d4bed4SDavid Herrmann if (ext)
205f1d4bed4SDavid Herrmann return WIIPROTO_REQ_DRM_KEE;
206f1d4bed4SDavid Herrmann else
207f1d4bed4SDavid Herrmann return WIIPROTO_REQ_DRM_K;
208f1d4bed4SDavid Herrmann }
209f1d4bed4SDavid Herrmann
210192a1acfSDavid Herrmann if (ir == WIIPROTO_FLAG_IR_BASIC) {
2114148b6bfSDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
212a6be8569SDavid Herrmann /* GEN10 and ealier devices bind IR formats to DRMs.
213a6be8569SDavid Herrmann * Hence, we cannot use DRM_KAI here as it might be
214a6be8569SDavid Herrmann * bound to IR_EXT. Use DRM_KAIE unconditionally so we
215a6be8569SDavid Herrmann * work with all devices and our parsers can use the
216a6be8569SDavid Herrmann * fixed formats, too. */
217192a1acfSDavid Herrmann return WIIPROTO_REQ_DRM_KAIE;
2184148b6bfSDavid Herrmann } else {
219192a1acfSDavid Herrmann return WIIPROTO_REQ_DRM_KIE;
2204148b6bfSDavid Herrmann }
221192a1acfSDavid Herrmann } else if (ir == WIIPROTO_FLAG_IR_EXT) {
222192a1acfSDavid Herrmann return WIIPROTO_REQ_DRM_KAI;
223192a1acfSDavid Herrmann } else if (ir == WIIPROTO_FLAG_IR_FULL) {
224192a1acfSDavid Herrmann return WIIPROTO_REQ_DRM_SKAI1;
225192a1acfSDavid Herrmann } else {
226cb99221bSDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
227cb99221bSDavid Herrmann if (ext)
228cb99221bSDavid Herrmann return WIIPROTO_REQ_DRM_KAE;
229cb99221bSDavid Herrmann else
230192a1acfSDavid Herrmann return WIIPROTO_REQ_DRM_KA;
231cb99221bSDavid Herrmann } else {
232cb99221bSDavid Herrmann if (ext)
2334148b6bfSDavid Herrmann return WIIPROTO_REQ_DRM_KEE;
234192a1acfSDavid Herrmann else
235192a1acfSDavid Herrmann return WIIPROTO_REQ_DRM_K;
236192a1acfSDavid Herrmann }
237192a1acfSDavid Herrmann }
238cb99221bSDavid Herrmann }
239192a1acfSDavid Herrmann
wiiproto_req_drm(struct wiimote_data * wdata,__u8 drm)2407e274400SDavid Herrmann void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
241192a1acfSDavid Herrmann {
242192a1acfSDavid Herrmann __u8 cmd[3];
243192a1acfSDavid Herrmann
244d76f89e1SDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_DRM_LOCKED)
245d76f89e1SDavid Herrmann drm = wdata->state.drm;
246d76f89e1SDavid Herrmann else if (drm == WIIPROTO_REQ_NULL)
247192a1acfSDavid Herrmann drm = select_drm(wdata);
248192a1acfSDavid Herrmann
249192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_DRM;
250192a1acfSDavid Herrmann cmd[1] = 0;
251192a1acfSDavid Herrmann cmd[2] = drm;
252192a1acfSDavid Herrmann
25343d782aeSDavid Herrmann wdata->state.drm = drm;
254192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
255192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
256192a1acfSDavid Herrmann }
257192a1acfSDavid Herrmann
wiiproto_req_status(struct wiimote_data * wdata)258dcf39231SDavid Herrmann void wiiproto_req_status(struct wiimote_data *wdata)
259192a1acfSDavid Herrmann {
260192a1acfSDavid Herrmann __u8 cmd[2];
261192a1acfSDavid Herrmann
262192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_SREQ;
263192a1acfSDavid Herrmann cmd[1] = 0;
264192a1acfSDavid Herrmann
265192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
266192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
267192a1acfSDavid Herrmann }
268192a1acfSDavid Herrmann
wiiproto_req_accel(struct wiimote_data * wdata,__u8 accel)2690ea16757SDavid Herrmann void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
270192a1acfSDavid Herrmann {
271192a1acfSDavid Herrmann accel = !!accel;
272192a1acfSDavid Herrmann if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
273192a1acfSDavid Herrmann return;
274192a1acfSDavid Herrmann
275192a1acfSDavid Herrmann if (accel)
276192a1acfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_ACCEL;
277192a1acfSDavid Herrmann else
278192a1acfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL;
279192a1acfSDavid Herrmann
280192a1acfSDavid Herrmann wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
281192a1acfSDavid Herrmann }
282192a1acfSDavid Herrmann
wiiproto_req_ir1(struct wiimote_data * wdata,__u8 flags)2833b5f03c4SDavid Herrmann void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
284192a1acfSDavid Herrmann {
285192a1acfSDavid Herrmann __u8 cmd[2];
286192a1acfSDavid Herrmann
287192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_IR1;
288192a1acfSDavid Herrmann cmd[1] = flags;
289192a1acfSDavid Herrmann
290192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
291192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
292192a1acfSDavid Herrmann }
293192a1acfSDavid Herrmann
wiiproto_req_ir2(struct wiimote_data * wdata,__u8 flags)2943b5f03c4SDavid Herrmann void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
295192a1acfSDavid Herrmann {
296192a1acfSDavid Herrmann __u8 cmd[2];
297192a1acfSDavid Herrmann
298192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_IR2;
299192a1acfSDavid Herrmann cmd[1] = flags;
300192a1acfSDavid Herrmann
301192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
302192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
303192a1acfSDavid Herrmann }
304192a1acfSDavid Herrmann
305192a1acfSDavid Herrmann #define wiiproto_req_wreg(wdata, os, buf, sz) \
306192a1acfSDavid Herrmann wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
307192a1acfSDavid Herrmann
308192a1acfSDavid Herrmann #define wiiproto_req_weeprom(wdata, os, buf, sz) \
309192a1acfSDavid Herrmann wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
310192a1acfSDavid Herrmann
wiiproto_req_wmem(struct wiimote_data * wdata,bool eeprom,__u32 offset,const __u8 * buf,__u8 size)311192a1acfSDavid Herrmann static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
312192a1acfSDavid Herrmann __u32 offset, const __u8 *buf, __u8 size)
313192a1acfSDavid Herrmann {
314192a1acfSDavid Herrmann __u8 cmd[22];
315192a1acfSDavid Herrmann
316192a1acfSDavid Herrmann if (size > 16 || size == 0) {
317192a1acfSDavid Herrmann hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size);
318192a1acfSDavid Herrmann return;
319192a1acfSDavid Herrmann }
320192a1acfSDavid Herrmann
321192a1acfSDavid Herrmann memset(cmd, 0, sizeof(cmd));
322192a1acfSDavid Herrmann cmd[0] = WIIPROTO_REQ_WMEM;
323192a1acfSDavid Herrmann cmd[2] = (offset >> 16) & 0xff;
324192a1acfSDavid Herrmann cmd[3] = (offset >> 8) & 0xff;
325192a1acfSDavid Herrmann cmd[4] = offset & 0xff;
326192a1acfSDavid Herrmann cmd[5] = size;
327192a1acfSDavid Herrmann memcpy(&cmd[6], buf, size);
328192a1acfSDavid Herrmann
329192a1acfSDavid Herrmann if (!eeprom)
330192a1acfSDavid Herrmann cmd[1] |= 0x04;
331192a1acfSDavid Herrmann
332192a1acfSDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
333192a1acfSDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
334192a1acfSDavid Herrmann }
335192a1acfSDavid Herrmann
wiiproto_req_rmem(struct wiimote_data * wdata,bool eeprom,__u32 offset,__u16 size)3361d3452c6SDavid Herrmann void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset,
3371d3452c6SDavid Herrmann __u16 size)
338fad8c0e3SDavid Herrmann {
339fad8c0e3SDavid Herrmann __u8 cmd[7];
340fad8c0e3SDavid Herrmann
341fad8c0e3SDavid Herrmann if (size == 0) {
342fad8c0e3SDavid Herrmann hid_warn(wdata->hdev, "Invalid length %d rmem request\n", size);
343fad8c0e3SDavid Herrmann return;
344fad8c0e3SDavid Herrmann }
345fad8c0e3SDavid Herrmann
346fad8c0e3SDavid Herrmann cmd[0] = WIIPROTO_REQ_RMEM;
347fad8c0e3SDavid Herrmann cmd[1] = 0;
348fad8c0e3SDavid Herrmann cmd[2] = (offset >> 16) & 0xff;
349fad8c0e3SDavid Herrmann cmd[3] = (offset >> 8) & 0xff;
350fad8c0e3SDavid Herrmann cmd[4] = offset & 0xff;
351fad8c0e3SDavid Herrmann cmd[5] = (size >> 8) & 0xff;
352fad8c0e3SDavid Herrmann cmd[6] = size & 0xff;
353fad8c0e3SDavid Herrmann
354fad8c0e3SDavid Herrmann if (!eeprom)
355fad8c0e3SDavid Herrmann cmd[1] |= 0x04;
356fad8c0e3SDavid Herrmann
357fad8c0e3SDavid Herrmann wiiproto_keep_rumble(wdata, &cmd[1]);
358fad8c0e3SDavid Herrmann wiimote_queue(wdata, cmd, sizeof(cmd));
359fad8c0e3SDavid Herrmann }
360fad8c0e3SDavid Herrmann
361192a1acfSDavid Herrmann /* requries the cmd-mutex to be held */
wiimote_cmd_write(struct wiimote_data * wdata,__u32 offset,const __u8 * wmem,__u8 size)3627e274400SDavid Herrmann int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
363192a1acfSDavid Herrmann const __u8 *wmem, __u8 size)
364192a1acfSDavid Herrmann {
365192a1acfSDavid Herrmann unsigned long flags;
366192a1acfSDavid Herrmann int ret;
367192a1acfSDavid Herrmann
368192a1acfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
369192a1acfSDavid Herrmann wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0);
370192a1acfSDavid Herrmann wiiproto_req_wreg(wdata, offset, wmem, size);
371192a1acfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
372192a1acfSDavid Herrmann
373192a1acfSDavid Herrmann ret = wiimote_cmd_wait(wdata);
374192a1acfSDavid Herrmann if (!ret && wdata->state.cmd_err)
375192a1acfSDavid Herrmann ret = -EIO;
376192a1acfSDavid Herrmann
377192a1acfSDavid Herrmann return ret;
378192a1acfSDavid Herrmann }
379192a1acfSDavid Herrmann
380fad8c0e3SDavid Herrmann /* requries the cmd-mutex to be held */
wiimote_cmd_read(struct wiimote_data * wdata,__u32 offset,__u8 * rmem,__u8 size)381fad8c0e3SDavid Herrmann ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem,
382fad8c0e3SDavid Herrmann __u8 size)
383fad8c0e3SDavid Herrmann {
384fad8c0e3SDavid Herrmann unsigned long flags;
385fad8c0e3SDavid Herrmann ssize_t ret;
386fad8c0e3SDavid Herrmann
387fad8c0e3SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
388fad8c0e3SDavid Herrmann wdata->state.cmd_read_size = size;
389fad8c0e3SDavid Herrmann wdata->state.cmd_read_buf = rmem;
390fad8c0e3SDavid Herrmann wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, offset & 0xffff);
391fad8c0e3SDavid Herrmann wiiproto_req_rreg(wdata, offset, size);
392fad8c0e3SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
393fad8c0e3SDavid Herrmann
394fad8c0e3SDavid Herrmann ret = wiimote_cmd_wait(wdata);
395fad8c0e3SDavid Herrmann
396fad8c0e3SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
397fad8c0e3SDavid Herrmann wdata->state.cmd_read_buf = NULL;
398fad8c0e3SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
399fad8c0e3SDavid Herrmann
400fad8c0e3SDavid Herrmann if (!ret) {
401fad8c0e3SDavid Herrmann if (wdata->state.cmd_read_size == 0)
402fad8c0e3SDavid Herrmann ret = -EIO;
403fad8c0e3SDavid Herrmann else
404fad8c0e3SDavid Herrmann ret = wdata->state.cmd_read_size;
405fad8c0e3SDavid Herrmann }
406fad8c0e3SDavid Herrmann
407fad8c0e3SDavid Herrmann return ret;
408fad8c0e3SDavid Herrmann }
409fad8c0e3SDavid Herrmann
410c57ff761SDavid Herrmann /* requires the cmd-mutex to be held */
wiimote_cmd_init_ext(struct wiimote_data * wdata)411c57ff761SDavid Herrmann static int wiimote_cmd_init_ext(struct wiimote_data *wdata)
412c57ff761SDavid Herrmann {
413c57ff761SDavid Herrmann __u8 wmem;
414c57ff761SDavid Herrmann int ret;
415c57ff761SDavid Herrmann
416c57ff761SDavid Herrmann /* initialize extension */
417c57ff761SDavid Herrmann wmem = 0x55;
418c57ff761SDavid Herrmann ret = wiimote_cmd_write(wdata, 0xa400f0, &wmem, sizeof(wmem));
419c57ff761SDavid Herrmann if (ret)
420c57ff761SDavid Herrmann return ret;
421c57ff761SDavid Herrmann
422c57ff761SDavid Herrmann /* disable default encryption */
423c57ff761SDavid Herrmann wmem = 0x0;
424c57ff761SDavid Herrmann ret = wiimote_cmd_write(wdata, 0xa400fb, &wmem, sizeof(wmem));
425c57ff761SDavid Herrmann if (ret)
426c57ff761SDavid Herrmann return ret;
427c57ff761SDavid Herrmann
428c57ff761SDavid Herrmann return 0;
429c57ff761SDavid Herrmann }
430c57ff761SDavid Herrmann
431c57ff761SDavid Herrmann /* requires the cmd-mutex to be held */
wiimote_cmd_read_ext(struct wiimote_data * wdata,__u8 * rmem)4324148b6bfSDavid Herrmann static __u8 wiimote_cmd_read_ext(struct wiimote_data *wdata, __u8 *rmem)
433c57ff761SDavid Herrmann {
434c57ff761SDavid Herrmann int ret;
435c57ff761SDavid Herrmann
436c57ff761SDavid Herrmann /* read extension ID */
437c57ff761SDavid Herrmann ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
438c57ff761SDavid Herrmann if (ret != 6)
439c57ff761SDavid Herrmann return WIIMOTE_EXT_NONE;
440c57ff761SDavid Herrmann
44195f71266SAndy Shevchenko hid_dbg(wdata->hdev, "extension ID: %6phC\n", rmem);
4424148b6bfSDavid Herrmann
443c57ff761SDavid Herrmann if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
444c57ff761SDavid Herrmann rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
445c57ff761SDavid Herrmann return WIIMOTE_EXT_NONE;
446c57ff761SDavid Herrmann
447b6ee67b3SDavid Herrmann if (rmem[4] == 0x00 && rmem[5] == 0x00)
448b6ee67b3SDavid Herrmann return WIIMOTE_EXT_NUNCHUK;
4499d6f9ecbSDavid Herrmann if (rmem[4] == 0x01 && rmem[5] == 0x01)
4509d6f9ecbSDavid Herrmann return WIIMOTE_EXT_CLASSIC_CONTROLLER;
451f1d4bed4SDavid Herrmann if (rmem[4] == 0x04 && rmem[5] == 0x02)
452f1d4bed4SDavid Herrmann return WIIMOTE_EXT_BALANCE_BOARD;
453b8e0fe31SDavid Herrmann if (rmem[4] == 0x01 && rmem[5] == 0x20)
454b8e0fe31SDavid Herrmann return WIIMOTE_EXT_PRO_CONTROLLER;
455d4bdf2d2SNicolas Adenis-Lamarre if (rmem[0] == 0x01 && rmem[1] == 0x00 &&
456d4bdf2d2SNicolas Adenis-Lamarre rmem[4] == 0x01 && rmem[5] == 0x03)
457d4bdf2d2SNicolas Adenis-Lamarre return WIIMOTE_EXT_DRUMS;
458d4bdf2d2SNicolas Adenis-Lamarre if (rmem[0] == 0x00 && rmem[1] == 0x00 &&
459d4bdf2d2SNicolas Adenis-Lamarre rmem[4] == 0x01 && rmem[5] == 0x03)
460d4bdf2d2SNicolas Adenis-Lamarre return WIIMOTE_EXT_GUITAR;
46105086f3dSJoshua Jun if (rmem[0] == 0x03 && rmem[1] == 0x00 &&
46205086f3dSJoshua Jun rmem[4] == 0x01 && rmem[5] == 0x03)
46305086f3dSJoshua Jun return WIIMOTE_EXT_TURNTABLE;
464f1d4bed4SDavid Herrmann
465c57ff761SDavid Herrmann return WIIMOTE_EXT_UNKNOWN;
466c57ff761SDavid Herrmann }
467c57ff761SDavid Herrmann
4684148b6bfSDavid Herrmann /* requires the cmd-mutex to be held */
wiimote_cmd_init_mp(struct wiimote_data * wdata)4694148b6bfSDavid Herrmann static int wiimote_cmd_init_mp(struct wiimote_data *wdata)
4704148b6bfSDavid Herrmann {
4714148b6bfSDavid Herrmann __u8 wmem;
4724148b6bfSDavid Herrmann int ret;
4734148b6bfSDavid Herrmann
4744148b6bfSDavid Herrmann /* initialize MP */
4754148b6bfSDavid Herrmann wmem = 0x55;
4764148b6bfSDavid Herrmann ret = wiimote_cmd_write(wdata, 0xa600f0, &wmem, sizeof(wmem));
4774148b6bfSDavid Herrmann if (ret)
4784148b6bfSDavid Herrmann return ret;
4794148b6bfSDavid Herrmann
4804148b6bfSDavid Herrmann /* disable default encryption */
4814148b6bfSDavid Herrmann wmem = 0x0;
4824148b6bfSDavid Herrmann ret = wiimote_cmd_write(wdata, 0xa600fb, &wmem, sizeof(wmem));
4834148b6bfSDavid Herrmann if (ret)
4844148b6bfSDavid Herrmann return ret;
4854148b6bfSDavid Herrmann
4864148b6bfSDavid Herrmann return 0;
4874148b6bfSDavid Herrmann }
4884148b6bfSDavid Herrmann
4894148b6bfSDavid Herrmann /* requires the cmd-mutex to be held */
wiimote_cmd_map_mp(struct wiimote_data * wdata,__u8 exttype)4904148b6bfSDavid Herrmann static bool wiimote_cmd_map_mp(struct wiimote_data *wdata, __u8 exttype)
4914148b6bfSDavid Herrmann {
4924148b6bfSDavid Herrmann __u8 wmem;
4934148b6bfSDavid Herrmann
4944148b6bfSDavid Herrmann /* map MP with correct pass-through mode */
4954148b6bfSDavid Herrmann switch (exttype) {
4969d6f9ecbSDavid Herrmann case WIIMOTE_EXT_CLASSIC_CONTROLLER:
497d4bdf2d2SNicolas Adenis-Lamarre case WIIMOTE_EXT_DRUMS:
498d4bdf2d2SNicolas Adenis-Lamarre case WIIMOTE_EXT_GUITAR:
4999d6f9ecbSDavid Herrmann wmem = 0x07;
5009d6f9ecbSDavid Herrmann break;
50105086f3dSJoshua Jun case WIIMOTE_EXT_TURNTABLE:
502b6ee67b3SDavid Herrmann case WIIMOTE_EXT_NUNCHUK:
503b6ee67b3SDavid Herrmann wmem = 0x05;
504b6ee67b3SDavid Herrmann break;
5054148b6bfSDavid Herrmann default:
5064148b6bfSDavid Herrmann wmem = 0x04;
5074148b6bfSDavid Herrmann break;
5084148b6bfSDavid Herrmann }
5094148b6bfSDavid Herrmann
5104148b6bfSDavid Herrmann return wiimote_cmd_write(wdata, 0xa600fe, &wmem, sizeof(wmem));
5114148b6bfSDavid Herrmann }
5124148b6bfSDavid Herrmann
5134148b6bfSDavid Herrmann /* requires the cmd-mutex to be held */
wiimote_cmd_read_mp(struct wiimote_data * wdata,__u8 * rmem)5144148b6bfSDavid Herrmann static bool wiimote_cmd_read_mp(struct wiimote_data *wdata, __u8 *rmem)
5154148b6bfSDavid Herrmann {
5164148b6bfSDavid Herrmann int ret;
5174148b6bfSDavid Herrmann
5184148b6bfSDavid Herrmann /* read motion plus ID */
5194148b6bfSDavid Herrmann ret = wiimote_cmd_read(wdata, 0xa600fa, rmem, 6);
5204148b6bfSDavid Herrmann if (ret != 6)
5214148b6bfSDavid Herrmann return false;
5224148b6bfSDavid Herrmann
52395f71266SAndy Shevchenko hid_dbg(wdata->hdev, "motion plus ID: %6phC\n", rmem);
5244148b6bfSDavid Herrmann
5254148b6bfSDavid Herrmann if (rmem[5] == 0x05)
5264148b6bfSDavid Herrmann return true;
5274148b6bfSDavid Herrmann
52895f71266SAndy Shevchenko hid_info(wdata->hdev, "unknown motion plus ID: %6phC\n", rmem);
5294148b6bfSDavid Herrmann
5304148b6bfSDavid Herrmann return false;
5314148b6bfSDavid Herrmann }
5324148b6bfSDavid Herrmann
5334148b6bfSDavid Herrmann /* requires the cmd-mutex to be held */
wiimote_cmd_read_mp_mapped(struct wiimote_data * wdata)5344148b6bfSDavid Herrmann static __u8 wiimote_cmd_read_mp_mapped(struct wiimote_data *wdata)
5354148b6bfSDavid Herrmann {
5364148b6bfSDavid Herrmann int ret;
5374148b6bfSDavid Herrmann __u8 rmem[6];
5384148b6bfSDavid Herrmann
5394148b6bfSDavid Herrmann /* read motion plus ID */
5404148b6bfSDavid Herrmann ret = wiimote_cmd_read(wdata, 0xa400fa, rmem, 6);
5414148b6bfSDavid Herrmann if (ret != 6)
5424148b6bfSDavid Herrmann return WIIMOTE_MP_NONE;
5434148b6bfSDavid Herrmann
54495f71266SAndy Shevchenko hid_dbg(wdata->hdev, "mapped motion plus ID: %6phC\n", rmem);
5454148b6bfSDavid Herrmann
5464148b6bfSDavid Herrmann if (rmem[0] == 0xff && rmem[1] == 0xff && rmem[2] == 0xff &&
5474148b6bfSDavid Herrmann rmem[3] == 0xff && rmem[4] == 0xff && rmem[5] == 0xff)
5484148b6bfSDavid Herrmann return WIIMOTE_MP_NONE;
5494148b6bfSDavid Herrmann
5504148b6bfSDavid Herrmann if (rmem[4] == 0x04 && rmem[5] == 0x05)
5514148b6bfSDavid Herrmann return WIIMOTE_MP_SINGLE;
5524148b6bfSDavid Herrmann else if (rmem[4] == 0x05 && rmem[5] == 0x05)
5534148b6bfSDavid Herrmann return WIIMOTE_MP_PASSTHROUGH_NUNCHUK;
5544148b6bfSDavid Herrmann else if (rmem[4] == 0x07 && rmem[5] == 0x05)
5554148b6bfSDavid Herrmann return WIIMOTE_MP_PASSTHROUGH_CLASSIC;
5564148b6bfSDavid Herrmann
5574148b6bfSDavid Herrmann return WIIMOTE_MP_UNKNOWN;
5584148b6bfSDavid Herrmann }
5594148b6bfSDavid Herrmann
56027f06942SDavid Herrmann /* device module handling */
56127f06942SDavid Herrmann
56227f06942SDavid Herrmann static const __u8 * const wiimote_devtype_mods[WIIMOTE_DEV_NUM] = {
56327f06942SDavid Herrmann [WIIMOTE_DEV_PENDING] = (const __u8[]){
56427f06942SDavid Herrmann WIIMOD_NULL,
56527f06942SDavid Herrmann },
56627f06942SDavid Herrmann [WIIMOTE_DEV_UNKNOWN] = (const __u8[]){
5679f329741SDavid Herrmann WIIMOD_NO_MP,
56827f06942SDavid Herrmann WIIMOD_NULL,
56927f06942SDavid Herrmann },
57027f06942SDavid Herrmann [WIIMOTE_DEV_GENERIC] = (const __u8[]){
57120cef813SDavid Herrmann WIIMOD_KEYS,
57220cef813SDavid Herrmann WIIMOD_RUMBLE,
573dcf39231SDavid Herrmann WIIMOD_BATTERY,
5746c5ae018SDavid Herrmann WIIMOD_LED1,
5756c5ae018SDavid Herrmann WIIMOD_LED2,
5766c5ae018SDavid Herrmann WIIMOD_LED3,
5776c5ae018SDavid Herrmann WIIMOD_LED4,
5780ea16757SDavid Herrmann WIIMOD_ACCEL,
5793b5f03c4SDavid Herrmann WIIMOD_IR,
58027f06942SDavid Herrmann WIIMOD_NULL,
58127f06942SDavid Herrmann },
58227f06942SDavid Herrmann [WIIMOTE_DEV_GEN10] = (const __u8[]){
58320cef813SDavid Herrmann WIIMOD_KEYS,
58420cef813SDavid Herrmann WIIMOD_RUMBLE,
585dcf39231SDavid Herrmann WIIMOD_BATTERY,
5866c5ae018SDavid Herrmann WIIMOD_LED1,
5876c5ae018SDavid Herrmann WIIMOD_LED2,
5886c5ae018SDavid Herrmann WIIMOD_LED3,
5896c5ae018SDavid Herrmann WIIMOD_LED4,
5900ea16757SDavid Herrmann WIIMOD_ACCEL,
5913b5f03c4SDavid Herrmann WIIMOD_IR,
59227f06942SDavid Herrmann WIIMOD_NULL,
59327f06942SDavid Herrmann },
59427f06942SDavid Herrmann [WIIMOTE_DEV_GEN20] = (const __u8[]){
59520cef813SDavid Herrmann WIIMOD_KEYS,
59620cef813SDavid Herrmann WIIMOD_RUMBLE,
597dcf39231SDavid Herrmann WIIMOD_BATTERY,
5986c5ae018SDavid Herrmann WIIMOD_LED1,
5996c5ae018SDavid Herrmann WIIMOD_LED2,
6006c5ae018SDavid Herrmann WIIMOD_LED3,
6016c5ae018SDavid Herrmann WIIMOD_LED4,
6020ea16757SDavid Herrmann WIIMOD_ACCEL,
6033b5f03c4SDavid Herrmann WIIMOD_IR,
6049f329741SDavid Herrmann WIIMOD_BUILTIN_MP,
60527f06942SDavid Herrmann WIIMOD_NULL,
60627f06942SDavid Herrmann },
607f1d4bed4SDavid Herrmann [WIIMOTE_DEV_BALANCE_BOARD] = (const __u8[]) {
608f1d4bed4SDavid Herrmann WIIMOD_BATTERY,
609f1d4bed4SDavid Herrmann WIIMOD_LED1,
6109f329741SDavid Herrmann WIIMOD_NO_MP,
611f1d4bed4SDavid Herrmann WIIMOD_NULL,
612f1d4bed4SDavid Herrmann },
613b8e0fe31SDavid Herrmann [WIIMOTE_DEV_PRO_CONTROLLER] = (const __u8[]) {
614b8e0fe31SDavid Herrmann WIIMOD_BATTERY,
615b8e0fe31SDavid Herrmann WIIMOD_LED1,
616b8e0fe31SDavid Herrmann WIIMOD_LED2,
617b8e0fe31SDavid Herrmann WIIMOD_LED3,
618b8e0fe31SDavid Herrmann WIIMOD_LED4,
619b8e0fe31SDavid Herrmann WIIMOD_NO_MP,
620b8e0fe31SDavid Herrmann WIIMOD_NULL,
621b8e0fe31SDavid Herrmann },
62227f06942SDavid Herrmann };
62327f06942SDavid Herrmann
wiimote_modules_load(struct wiimote_data * wdata,unsigned int devtype)62427f06942SDavid Herrmann static void wiimote_modules_load(struct wiimote_data *wdata,
62527f06942SDavid Herrmann unsigned int devtype)
62627f06942SDavid Herrmann {
62727f06942SDavid Herrmann bool need_input = false;
62827f06942SDavid Herrmann const __u8 *mods, *iter;
62927f06942SDavid Herrmann const struct wiimod_ops *ops;
63027f06942SDavid Herrmann int ret;
63127f06942SDavid Herrmann
63227f06942SDavid Herrmann mods = wiimote_devtype_mods[devtype];
63327f06942SDavid Herrmann
63427f06942SDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
63527f06942SDavid Herrmann if (wiimod_table[*iter]->flags & WIIMOD_FLAG_INPUT) {
63627f06942SDavid Herrmann need_input = true;
63727f06942SDavid Herrmann break;
63827f06942SDavid Herrmann }
63927f06942SDavid Herrmann }
64027f06942SDavid Herrmann
64127f06942SDavid Herrmann if (need_input) {
64227f06942SDavid Herrmann wdata->input = input_allocate_device();
64327f06942SDavid Herrmann if (!wdata->input)
64427f06942SDavid Herrmann return;
64527f06942SDavid Herrmann
64627f06942SDavid Herrmann input_set_drvdata(wdata->input, wdata);
64727f06942SDavid Herrmann wdata->input->dev.parent = &wdata->hdev->dev;
64827f06942SDavid Herrmann wdata->input->id.bustype = wdata->hdev->bus;
64927f06942SDavid Herrmann wdata->input->id.vendor = wdata->hdev->vendor;
65027f06942SDavid Herrmann wdata->input->id.product = wdata->hdev->product;
65127f06942SDavid Herrmann wdata->input->id.version = wdata->hdev->version;
65227f06942SDavid Herrmann wdata->input->name = WIIMOTE_NAME;
65327f06942SDavid Herrmann }
65427f06942SDavid Herrmann
65527f06942SDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
65627f06942SDavid Herrmann ops = wiimod_table[*iter];
65727f06942SDavid Herrmann if (!ops->probe)
65827f06942SDavid Herrmann continue;
65927f06942SDavid Herrmann
66027f06942SDavid Herrmann ret = ops->probe(ops, wdata);
66127f06942SDavid Herrmann if (ret)
66227f06942SDavid Herrmann goto error;
66327f06942SDavid Herrmann }
66427f06942SDavid Herrmann
66527f06942SDavid Herrmann if (wdata->input) {
66627f06942SDavid Herrmann ret = input_register_device(wdata->input);
66727f06942SDavid Herrmann if (ret)
66827f06942SDavid Herrmann goto error;
66927f06942SDavid Herrmann }
67027f06942SDavid Herrmann
67127f06942SDavid Herrmann spin_lock_irq(&wdata->state.lock);
67227f06942SDavid Herrmann wdata->state.devtype = devtype;
67327f06942SDavid Herrmann spin_unlock_irq(&wdata->state.lock);
67427f06942SDavid Herrmann return;
67527f06942SDavid Herrmann
67627f06942SDavid Herrmann error:
67727f06942SDavid Herrmann for ( ; iter-- != mods; ) {
67827f06942SDavid Herrmann ops = wiimod_table[*iter];
67927f06942SDavid Herrmann if (ops->remove)
68027f06942SDavid Herrmann ops->remove(ops, wdata);
68127f06942SDavid Herrmann }
68227f06942SDavid Herrmann
68327f06942SDavid Herrmann if (wdata->input) {
68427f06942SDavid Herrmann input_free_device(wdata->input);
68527f06942SDavid Herrmann wdata->input = NULL;
68627f06942SDavid Herrmann }
68727f06942SDavid Herrmann }
68827f06942SDavid Herrmann
wiimote_modules_unload(struct wiimote_data * wdata)68927f06942SDavid Herrmann static void wiimote_modules_unload(struct wiimote_data *wdata)
69027f06942SDavid Herrmann {
69127f06942SDavid Herrmann const __u8 *mods, *iter;
69227f06942SDavid Herrmann const struct wiimod_ops *ops;
69327f06942SDavid Herrmann unsigned long flags;
69427f06942SDavid Herrmann
69527f06942SDavid Herrmann mods = wiimote_devtype_mods[wdata->state.devtype];
69627f06942SDavid Herrmann
69727f06942SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
69827f06942SDavid Herrmann wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
69927f06942SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
70027f06942SDavid Herrmann
70127f06942SDavid Herrmann /* find end of list */
70227f06942SDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter)
70327f06942SDavid Herrmann /* empty */ ;
70427f06942SDavid Herrmann
70527f06942SDavid Herrmann if (wdata->input) {
70627f06942SDavid Herrmann input_get_device(wdata->input);
70727f06942SDavid Herrmann input_unregister_device(wdata->input);
70827f06942SDavid Herrmann }
70927f06942SDavid Herrmann
71027f06942SDavid Herrmann for ( ; iter-- != mods; ) {
71127f06942SDavid Herrmann ops = wiimod_table[*iter];
71227f06942SDavid Herrmann if (ops->remove)
71327f06942SDavid Herrmann ops->remove(ops, wdata);
71427f06942SDavid Herrmann }
71527f06942SDavid Herrmann
71627f06942SDavid Herrmann if (wdata->input) {
71727f06942SDavid Herrmann input_put_device(wdata->input);
71827f06942SDavid Herrmann wdata->input = NULL;
71927f06942SDavid Herrmann }
72027f06942SDavid Herrmann }
72127f06942SDavid Herrmann
7224148b6bfSDavid Herrmann /* device extension handling */
7234148b6bfSDavid Herrmann
wiimote_ext_load(struct wiimote_data * wdata,unsigned int ext)7244148b6bfSDavid Herrmann static void wiimote_ext_load(struct wiimote_data *wdata, unsigned int ext)
7254148b6bfSDavid Herrmann {
7264148b6bfSDavid Herrmann unsigned long flags;
7274148b6bfSDavid Herrmann const struct wiimod_ops *ops;
7284148b6bfSDavid Herrmann int ret;
7294148b6bfSDavid Herrmann
7304148b6bfSDavid Herrmann ops = wiimod_ext_table[ext];
7314148b6bfSDavid Herrmann
7324148b6bfSDavid Herrmann if (ops->probe) {
7334148b6bfSDavid Herrmann ret = ops->probe(ops, wdata);
7344148b6bfSDavid Herrmann if (ret)
7354148b6bfSDavid Herrmann ext = WIIMOTE_EXT_UNKNOWN;
7364148b6bfSDavid Herrmann }
7374148b6bfSDavid Herrmann
7384148b6bfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
7394148b6bfSDavid Herrmann wdata->state.exttype = ext;
7404148b6bfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
7414148b6bfSDavid Herrmann }
7424148b6bfSDavid Herrmann
wiimote_ext_unload(struct wiimote_data * wdata)7434148b6bfSDavid Herrmann static void wiimote_ext_unload(struct wiimote_data *wdata)
7444148b6bfSDavid Herrmann {
7454148b6bfSDavid Herrmann unsigned long flags;
7464148b6bfSDavid Herrmann const struct wiimod_ops *ops;
7474148b6bfSDavid Herrmann
7484148b6bfSDavid Herrmann ops = wiimod_ext_table[wdata->state.exttype];
7494148b6bfSDavid Herrmann
7504148b6bfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
7514148b6bfSDavid Herrmann wdata->state.exttype = WIIMOTE_EXT_UNKNOWN;
7524148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_USED;
7534148b6bfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
7544148b6bfSDavid Herrmann
7554148b6bfSDavid Herrmann if (ops->remove)
7564148b6bfSDavid Herrmann ops->remove(ops, wdata);
7574148b6bfSDavid Herrmann }
7584148b6bfSDavid Herrmann
wiimote_mp_load(struct wiimote_data * wdata)7594148b6bfSDavid Herrmann static void wiimote_mp_load(struct wiimote_data *wdata)
7604148b6bfSDavid Herrmann {
7614148b6bfSDavid Herrmann unsigned long flags;
7624148b6bfSDavid Herrmann const struct wiimod_ops *ops;
7634148b6bfSDavid Herrmann int ret;
7644148b6bfSDavid Herrmann __u8 mode = 2;
7654148b6bfSDavid Herrmann
7664148b6bfSDavid Herrmann ops = &wiimod_mp;
7674148b6bfSDavid Herrmann if (ops->probe) {
7684148b6bfSDavid Herrmann ret = ops->probe(ops, wdata);
7694148b6bfSDavid Herrmann if (ret)
7704148b6bfSDavid Herrmann mode = 1;
7714148b6bfSDavid Herrmann }
7724148b6bfSDavid Herrmann
7734148b6bfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
7744148b6bfSDavid Herrmann wdata->state.mp = mode;
7754148b6bfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
7764148b6bfSDavid Herrmann }
7774148b6bfSDavid Herrmann
wiimote_mp_unload(struct wiimote_data * wdata)7784148b6bfSDavid Herrmann static void wiimote_mp_unload(struct wiimote_data *wdata)
7794148b6bfSDavid Herrmann {
7804148b6bfSDavid Herrmann unsigned long flags;
7814148b6bfSDavid Herrmann const struct wiimod_ops *ops;
7824148b6bfSDavid Herrmann
7834148b6bfSDavid Herrmann if (wdata->state.mp < 2)
7844148b6bfSDavid Herrmann return;
7854148b6bfSDavid Herrmann
7864148b6bfSDavid Herrmann ops = &wiimod_mp;
7874148b6bfSDavid Herrmann
7884148b6bfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
7894148b6bfSDavid Herrmann wdata->state.mp = 0;
7904148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_MP_USED;
7914148b6bfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
7924148b6bfSDavid Herrmann
7934148b6bfSDavid Herrmann if (ops->remove)
7944148b6bfSDavid Herrmann ops->remove(ops, wdata);
7954148b6bfSDavid Herrmann }
7964148b6bfSDavid Herrmann
797c57ff761SDavid Herrmann /* device (re-)initialization and detection */
798c57ff761SDavid Herrmann
799c57ff761SDavid Herrmann static const char *wiimote_devtype_names[WIIMOTE_DEV_NUM] = {
800c57ff761SDavid Herrmann [WIIMOTE_DEV_PENDING] = "Pending",
801c57ff761SDavid Herrmann [WIIMOTE_DEV_UNKNOWN] = "Unknown",
802c57ff761SDavid Herrmann [WIIMOTE_DEV_GENERIC] = "Generic",
803c57ff761SDavid Herrmann [WIIMOTE_DEV_GEN10] = "Nintendo Wii Remote (Gen 1)",
804c57ff761SDavid Herrmann [WIIMOTE_DEV_GEN20] = "Nintendo Wii Remote Plus (Gen 2)",
805f1d4bed4SDavid Herrmann [WIIMOTE_DEV_BALANCE_BOARD] = "Nintendo Wii Balance Board",
806b8e0fe31SDavid Herrmann [WIIMOTE_DEV_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
807c57ff761SDavid Herrmann };
808c57ff761SDavid Herrmann
809c57ff761SDavid Herrmann /* Try to guess the device type based on all collected information. We
810c57ff761SDavid Herrmann * first try to detect by static extension types, then VID/PID and the
811c57ff761SDavid Herrmann * device name. If we cannot detect the device, we use
812c57ff761SDavid Herrmann * WIIMOTE_DEV_GENERIC so all modules will get probed on the device. */
wiimote_init_set_type(struct wiimote_data * wdata,__u8 exttype)813c57ff761SDavid Herrmann static void wiimote_init_set_type(struct wiimote_data *wdata,
814c57ff761SDavid Herrmann __u8 exttype)
815c57ff761SDavid Herrmann {
816c57ff761SDavid Herrmann __u8 devtype = WIIMOTE_DEV_GENERIC;
817c57ff761SDavid Herrmann __u16 vendor, product;
818c57ff761SDavid Herrmann const char *name;
819c57ff761SDavid Herrmann
820c57ff761SDavid Herrmann vendor = wdata->hdev->vendor;
821c57ff761SDavid Herrmann product = wdata->hdev->product;
822c57ff761SDavid Herrmann name = wdata->hdev->name;
823c57ff761SDavid Herrmann
824f1d4bed4SDavid Herrmann if (exttype == WIIMOTE_EXT_BALANCE_BOARD) {
825f1d4bed4SDavid Herrmann devtype = WIIMOTE_DEV_BALANCE_BOARD;
826f1d4bed4SDavid Herrmann goto done;
827b8e0fe31SDavid Herrmann } else if (exttype == WIIMOTE_EXT_PRO_CONTROLLER) {
828b8e0fe31SDavid Herrmann devtype = WIIMOTE_DEV_PRO_CONTROLLER;
829b8e0fe31SDavid Herrmann goto done;
830f1d4bed4SDavid Herrmann }
831f1d4bed4SDavid Herrmann
832c57ff761SDavid Herrmann if (!strcmp(name, "Nintendo RVL-CNT-01")) {
833c57ff761SDavid Herrmann devtype = WIIMOTE_DEV_GEN10;
834c57ff761SDavid Herrmann goto done;
835c57ff761SDavid Herrmann } else if (!strcmp(name, "Nintendo RVL-CNT-01-TR")) {
836c57ff761SDavid Herrmann devtype = WIIMOTE_DEV_GEN20;
837c57ff761SDavid Herrmann goto done;
838f1d4bed4SDavid Herrmann } else if (!strcmp(name, "Nintendo RVL-WBC-01")) {
839f1d4bed4SDavid Herrmann devtype = WIIMOTE_DEV_BALANCE_BOARD;
840f1d4bed4SDavid Herrmann goto done;
841b8e0fe31SDavid Herrmann } else if (!strcmp(name, "Nintendo RVL-CNT-01-UC")) {
842b8e0fe31SDavid Herrmann devtype = WIIMOTE_DEV_PRO_CONTROLLER;
843b8e0fe31SDavid Herrmann goto done;
844c57ff761SDavid Herrmann }
845c57ff761SDavid Herrmann
8469316e580SJiri Kosina if (vendor == USB_VENDOR_ID_NINTENDO) {
847c57ff761SDavid Herrmann if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE) {
848c57ff761SDavid Herrmann devtype = WIIMOTE_DEV_GEN10;
849c57ff761SDavid Herrmann goto done;
850c57ff761SDavid Herrmann } else if (product == USB_DEVICE_ID_NINTENDO_WIIMOTE2) {
851c57ff761SDavid Herrmann devtype = WIIMOTE_DEV_GEN20;
852c57ff761SDavid Herrmann goto done;
853c57ff761SDavid Herrmann }
854c57ff761SDavid Herrmann }
855c57ff761SDavid Herrmann
856c57ff761SDavid Herrmann done:
857c57ff761SDavid Herrmann if (devtype == WIIMOTE_DEV_GENERIC)
858c57ff761SDavid Herrmann hid_info(wdata->hdev, "cannot detect device; NAME: %s VID: %04x PID: %04x EXT: %04x\n",
859c57ff761SDavid Herrmann name, vendor, product, exttype);
860c57ff761SDavid Herrmann else
861c57ff761SDavid Herrmann hid_info(wdata->hdev, "detected device: %s\n",
862c57ff761SDavid Herrmann wiimote_devtype_names[devtype]);
863c57ff761SDavid Herrmann
86427f06942SDavid Herrmann wiimote_modules_load(wdata, devtype);
865c57ff761SDavid Herrmann }
866c57ff761SDavid Herrmann
wiimote_init_detect(struct wiimote_data * wdata)867c57ff761SDavid Herrmann static void wiimote_init_detect(struct wiimote_data *wdata)
868c57ff761SDavid Herrmann {
8694148b6bfSDavid Herrmann __u8 exttype = WIIMOTE_EXT_NONE, extdata[6];
870c57ff761SDavid Herrmann bool ext;
871c57ff761SDavid Herrmann int ret;
872c57ff761SDavid Herrmann
873c57ff761SDavid Herrmann wiimote_cmd_acquire_noint(wdata);
874c57ff761SDavid Herrmann
875c57ff761SDavid Herrmann spin_lock_irq(&wdata->state.lock);
87627f06942SDavid Herrmann wdata->state.devtype = WIIMOTE_DEV_UNKNOWN;
877c57ff761SDavid Herrmann wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
878c57ff761SDavid Herrmann wiiproto_req_status(wdata);
879c57ff761SDavid Herrmann spin_unlock_irq(&wdata->state.lock);
880c57ff761SDavid Herrmann
881c57ff761SDavid Herrmann ret = wiimote_cmd_wait_noint(wdata);
882c57ff761SDavid Herrmann if (ret)
883c57ff761SDavid Herrmann goto out_release;
884c57ff761SDavid Herrmann
885c57ff761SDavid Herrmann spin_lock_irq(&wdata->state.lock);
886c57ff761SDavid Herrmann ext = wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED;
887c57ff761SDavid Herrmann spin_unlock_irq(&wdata->state.lock);
888c57ff761SDavid Herrmann
889c57ff761SDavid Herrmann if (!ext)
890c57ff761SDavid Herrmann goto out_release;
891c57ff761SDavid Herrmann
892c57ff761SDavid Herrmann wiimote_cmd_init_ext(wdata);
8934148b6bfSDavid Herrmann exttype = wiimote_cmd_read_ext(wdata, extdata);
894c57ff761SDavid Herrmann
895c57ff761SDavid Herrmann out_release:
896c57ff761SDavid Herrmann wiimote_cmd_release(wdata);
897c57ff761SDavid Herrmann wiimote_init_set_type(wdata, exttype);
8989f329741SDavid Herrmann
8994148b6bfSDavid Herrmann /* schedule MP timer */
9009f329741SDavid Herrmann spin_lock_irq(&wdata->state.lock);
9019f329741SDavid Herrmann if (!(wdata->state.flags & WIIPROTO_FLAG_BUILTIN_MP) &&
9029f329741SDavid Herrmann !(wdata->state.flags & WIIPROTO_FLAG_NO_MP))
9034148b6bfSDavid Herrmann mod_timer(&wdata->timer, jiffies + HZ * 4);
9049f329741SDavid Herrmann spin_unlock_irq(&wdata->state.lock);
9054148b6bfSDavid Herrmann }
9064148b6bfSDavid Herrmann
9074148b6bfSDavid Herrmann /*
9084148b6bfSDavid Herrmann * MP hotplug events are not generated by the wiimote. Therefore, we need
9094148b6bfSDavid Herrmann * polling to detect it. We use a 4s interval for polling MP registers. This
9104148b6bfSDavid Herrmann * seems reasonable considering applications can trigger it manually via
9114148b6bfSDavid Herrmann * sysfs requests.
9124148b6bfSDavid Herrmann */
wiimote_init_poll_mp(struct wiimote_data * wdata)9134148b6bfSDavid Herrmann static void wiimote_init_poll_mp(struct wiimote_data *wdata)
9144148b6bfSDavid Herrmann {
9154148b6bfSDavid Herrmann bool mp;
9164148b6bfSDavid Herrmann __u8 mpdata[6];
9174148b6bfSDavid Herrmann
9184148b6bfSDavid Herrmann wiimote_cmd_acquire_noint(wdata);
9194148b6bfSDavid Herrmann wiimote_cmd_init_mp(wdata);
9204148b6bfSDavid Herrmann mp = wiimote_cmd_read_mp(wdata, mpdata);
9214148b6bfSDavid Herrmann wiimote_cmd_release(wdata);
9224148b6bfSDavid Herrmann
9234148b6bfSDavid Herrmann /* load/unload MP module if it changed */
9244148b6bfSDavid Herrmann if (mp) {
9254148b6bfSDavid Herrmann if (!wdata->state.mp) {
9264148b6bfSDavid Herrmann hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n");
9274148b6bfSDavid Herrmann wiimote_mp_load(wdata);
9284148b6bfSDavid Herrmann }
9294148b6bfSDavid Herrmann } else if (wdata->state.mp) {
9304148b6bfSDavid Herrmann wiimote_mp_unload(wdata);
9314148b6bfSDavid Herrmann }
9324148b6bfSDavid Herrmann
9334148b6bfSDavid Herrmann mod_timer(&wdata->timer, jiffies + HZ * 4);
9344148b6bfSDavid Herrmann }
9354148b6bfSDavid Herrmann
9364148b6bfSDavid Herrmann /*
9374148b6bfSDavid Herrmann * Check whether the wiimote is in the expected state. The extension registers
9384148b6bfSDavid Herrmann * may change during hotplug and initialization so we might get hotplug events
9394148b6bfSDavid Herrmann * that we caused by remapping some memory.
9404148b6bfSDavid Herrmann * We use some heuristics here to check known states. If the wiimote is in the
9414148b6bfSDavid Herrmann * expected state, we can ignore the hotplug event.
9424148b6bfSDavid Herrmann *
9434148b6bfSDavid Herrmann * Returns "true" if the device is in expected state, "false" if we should
9444148b6bfSDavid Herrmann * redo hotplug handling and extension initialization.
9454148b6bfSDavid Herrmann */
wiimote_init_check(struct wiimote_data * wdata)9464148b6bfSDavid Herrmann static bool wiimote_init_check(struct wiimote_data *wdata)
9474148b6bfSDavid Herrmann {
9484148b6bfSDavid Herrmann __u32 flags;
9494148b6bfSDavid Herrmann __u8 type, data[6];
9504148b6bfSDavid Herrmann bool ret, poll_mp;
9514148b6bfSDavid Herrmann
9524148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
9534148b6bfSDavid Herrmann flags = wdata->state.flags;
9544148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
9554148b6bfSDavid Herrmann
9564148b6bfSDavid Herrmann wiimote_cmd_acquire_noint(wdata);
9574148b6bfSDavid Herrmann
9584148b6bfSDavid Herrmann /* If MP is used and active, but the extension is not, we expect:
9594148b6bfSDavid Herrmann * read_mp_mapped() == WIIMOTE_MP_SINGLE
9604148b6bfSDavid Herrmann * state.flags == !EXT_ACTIVE && !MP_PLUGGED && MP_ACTIVE
9614148b6bfSDavid Herrmann * We do not check EXT_PLUGGED because it might change during
9624148b6bfSDavid Herrmann * initialization of MP without extensions.
9634148b6bfSDavid Herrmann * - If MP is unplugged/replugged, read_mp_mapped() fails
9644148b6bfSDavid Herrmann * - If EXT is plugged, MP_PLUGGED will get set */
9654148b6bfSDavid Herrmann if (wdata->state.exttype == WIIMOTE_EXT_NONE &&
9664148b6bfSDavid Herrmann wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) {
9674148b6bfSDavid Herrmann type = wiimote_cmd_read_mp_mapped(wdata);
9684148b6bfSDavid Herrmann ret = type == WIIMOTE_MP_SINGLE;
9694148b6bfSDavid Herrmann
9704148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
9714148b6bfSDavid Herrmann ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
9724148b6bfSDavid Herrmann ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED);
9734148b6bfSDavid Herrmann ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
9744148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
9754148b6bfSDavid Herrmann
9764148b6bfSDavid Herrmann if (!ret)
9774148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "state left: !EXT && MP\n");
9784148b6bfSDavid Herrmann
9794148b6bfSDavid Herrmann /* while MP is mapped, we get EXT_PLUGGED events */
9804148b6bfSDavid Herrmann poll_mp = false;
9814148b6bfSDavid Herrmann
9824148b6bfSDavid Herrmann goto out_release;
9834148b6bfSDavid Herrmann }
9844148b6bfSDavid Herrmann
9854148b6bfSDavid Herrmann /* If MP is unused, but the extension port is used, we expect:
9864148b6bfSDavid Herrmann * read_ext == state.exttype
9874148b6bfSDavid Herrmann * state.flags == !MP_ACTIVE && EXT_ACTIVE
9884148b6bfSDavid Herrmann * - If MP is plugged/unplugged, our timer detects it
9894148b6bfSDavid Herrmann * - If EXT is unplugged/replugged, EXT_ACTIVE will become unset */
9904148b6bfSDavid Herrmann if (!(flags & WIIPROTO_FLAG_MP_USED) &&
9914148b6bfSDavid Herrmann wdata->state.exttype != WIIMOTE_EXT_NONE) {
9924148b6bfSDavid Herrmann type = wiimote_cmd_read_ext(wdata, data);
9934148b6bfSDavid Herrmann ret = type == wdata->state.exttype;
9944148b6bfSDavid Herrmann
9954148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
9964148b6bfSDavid Herrmann ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
9974148b6bfSDavid Herrmann ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
9984148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
9994148b6bfSDavid Herrmann
10004148b6bfSDavid Herrmann if (!ret)
10014148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "state left: EXT && !MP\n");
10024148b6bfSDavid Herrmann
10034148b6bfSDavid Herrmann /* poll MP for hotplug events */
10044148b6bfSDavid Herrmann poll_mp = true;
10054148b6bfSDavid Herrmann
10064148b6bfSDavid Herrmann goto out_release;
10074148b6bfSDavid Herrmann }
10084148b6bfSDavid Herrmann
10094148b6bfSDavid Herrmann /* If neither MP nor an extension are used, we expect:
10104148b6bfSDavid Herrmann * read_ext() == WIIMOTE_EXT_NONE
10114148b6bfSDavid Herrmann * state.flags == !MP_ACTIVE && !EXT_ACTIVE && !EXT_PLUGGED
10124148b6bfSDavid Herrmann * No need to perform any action in this case as everything is
10134148b6bfSDavid Herrmann * disabled already.
10144148b6bfSDavid Herrmann * - If MP is plugged/unplugged, our timer detects it
10154148b6bfSDavid Herrmann * - If EXT is plugged, EXT_PLUGGED will be set */
10164148b6bfSDavid Herrmann if (!(flags & WIIPROTO_FLAG_MP_USED) &&
10174148b6bfSDavid Herrmann wdata->state.exttype == WIIMOTE_EXT_NONE) {
10184148b6bfSDavid Herrmann type = wiimote_cmd_read_ext(wdata, data);
10194148b6bfSDavid Herrmann ret = type == wdata->state.exttype;
10204148b6bfSDavid Herrmann
10214148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
10224148b6bfSDavid Herrmann ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
10234148b6bfSDavid Herrmann ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
10244148b6bfSDavid Herrmann ret = ret && !(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED);
10254148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
10264148b6bfSDavid Herrmann
10274148b6bfSDavid Herrmann if (!ret)
10284148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "state left: !EXT && !MP\n");
10294148b6bfSDavid Herrmann
10304148b6bfSDavid Herrmann /* poll MP for hotplug events */
10314148b6bfSDavid Herrmann poll_mp = true;
10324148b6bfSDavid Herrmann
10334148b6bfSDavid Herrmann goto out_release;
10344148b6bfSDavid Herrmann }
10354148b6bfSDavid Herrmann
10364148b6bfSDavid Herrmann /* The trickiest part is if both EXT and MP are active. We cannot read
10374148b6bfSDavid Herrmann * the EXT ID, anymore, because MP is mapped over it. However, we use
10384148b6bfSDavid Herrmann * a handy trick here:
10394148b6bfSDavid Herrmann * - EXT_ACTIVE is unset whenever !MP_PLUGGED is sent
10404148b6bfSDavid Herrmann * MP_PLUGGED might be re-sent again before we are scheduled, but
10414148b6bfSDavid Herrmann * EXT_ACTIVE will stay unset.
10424148b6bfSDavid Herrmann * So it is enough to check for mp_mapped() and MP_ACTIVE and
10434148b6bfSDavid Herrmann * EXT_ACTIVE. EXT_PLUGGED is a sanity check. */
10444148b6bfSDavid Herrmann if (wdata->state.exttype != WIIMOTE_EXT_NONE &&
10454148b6bfSDavid Herrmann wdata->state.mp > 0 && (flags & WIIPROTO_FLAG_MP_USED)) {
10464148b6bfSDavid Herrmann type = wiimote_cmd_read_mp_mapped(wdata);
10474148b6bfSDavid Herrmann ret = type != WIIMOTE_MP_NONE;
10484148b6bfSDavid Herrmann ret = ret && type != WIIMOTE_MP_UNKNOWN;
10494148b6bfSDavid Herrmann ret = ret && type != WIIMOTE_MP_SINGLE;
10504148b6bfSDavid Herrmann
10514148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
10524148b6bfSDavid Herrmann ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED);
10534148b6bfSDavid Herrmann ret = ret && (wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE);
10544148b6bfSDavid Herrmann ret = ret && (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE);
10554148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
10564148b6bfSDavid Herrmann
10574148b6bfSDavid Herrmann if (!ret)
10584148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "state left: EXT && MP\n");
10594148b6bfSDavid Herrmann
10604148b6bfSDavid Herrmann /* while MP is mapped, we get EXT_PLUGGED events */
10614148b6bfSDavid Herrmann poll_mp = false;
10624148b6bfSDavid Herrmann
10634148b6bfSDavid Herrmann goto out_release;
10644148b6bfSDavid Herrmann }
10654148b6bfSDavid Herrmann
10664148b6bfSDavid Herrmann /* unknown state */
10674148b6bfSDavid Herrmann ret = false;
10684148b6bfSDavid Herrmann
10694148b6bfSDavid Herrmann out_release:
10704148b6bfSDavid Herrmann wiimote_cmd_release(wdata);
10714148b6bfSDavid Herrmann
10724148b6bfSDavid Herrmann /* only poll for MP if requested and if state didn't change */
10739f329741SDavid Herrmann if (ret && poll_mp && !(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
10749f329741SDavid Herrmann !(flags & WIIPROTO_FLAG_NO_MP))
10754148b6bfSDavid Herrmann wiimote_init_poll_mp(wdata);
10764148b6bfSDavid Herrmann
10774148b6bfSDavid Herrmann return ret;
10784148b6bfSDavid Herrmann }
10794148b6bfSDavid Herrmann
10804148b6bfSDavid Herrmann static const char *wiimote_exttype_names[WIIMOTE_EXT_NUM] = {
10814148b6bfSDavid Herrmann [WIIMOTE_EXT_NONE] = "None",
10824148b6bfSDavid Herrmann [WIIMOTE_EXT_UNKNOWN] = "Unknown",
1083b6ee67b3SDavid Herrmann [WIIMOTE_EXT_NUNCHUK] = "Nintendo Wii Nunchuk",
10849d6f9ecbSDavid Herrmann [WIIMOTE_EXT_CLASSIC_CONTROLLER] = "Nintendo Wii Classic Controller",
1085f1d4bed4SDavid Herrmann [WIIMOTE_EXT_BALANCE_BOARD] = "Nintendo Wii Balance Board",
1086b8e0fe31SDavid Herrmann [WIIMOTE_EXT_PRO_CONTROLLER] = "Nintendo Wii U Pro Controller",
1087d4bdf2d2SNicolas Adenis-Lamarre [WIIMOTE_EXT_DRUMS] = "Nintendo Wii Drums",
1088d4bdf2d2SNicolas Adenis-Lamarre [WIIMOTE_EXT_GUITAR] = "Nintendo Wii Guitar",
108905086f3dSJoshua Jun [WIIMOTE_EXT_TURNTABLE] = "Nintendo Wii Turntable"
10904148b6bfSDavid Herrmann };
10914148b6bfSDavid Herrmann
10924148b6bfSDavid Herrmann /*
10934148b6bfSDavid Herrmann * Handle hotplug events
10944148b6bfSDavid Herrmann * If we receive an hotplug event and the device-check failed, we deinitialize
10954148b6bfSDavid Herrmann * the extension ports, re-read all extension IDs and set the device into
10964148b6bfSDavid Herrmann * the desired state. This involves mapping MP into the main extension
10974148b6bfSDavid Herrmann * registers, setting up extension passthrough modes and initializing the
10984148b6bfSDavid Herrmann * requested extensions.
10994148b6bfSDavid Herrmann */
wiimote_init_hotplug(struct wiimote_data * wdata)11004148b6bfSDavid Herrmann static void wiimote_init_hotplug(struct wiimote_data *wdata)
11014148b6bfSDavid Herrmann {
11024148b6bfSDavid Herrmann __u8 exttype, extdata[6], mpdata[6];
11034148b6bfSDavid Herrmann __u32 flags;
11044148b6bfSDavid Herrmann bool mp;
11054148b6bfSDavid Herrmann
11064148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "detect extensions..\n");
11074148b6bfSDavid Herrmann
11084148b6bfSDavid Herrmann wiimote_cmd_acquire_noint(wdata);
11094148b6bfSDavid Herrmann
11104148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
11114148b6bfSDavid Herrmann
11124148b6bfSDavid Herrmann /* get state snapshot that we will then work on */
11134148b6bfSDavid Herrmann flags = wdata->state.flags;
11144148b6bfSDavid Herrmann
11154148b6bfSDavid Herrmann /* disable event forwarding temporarily */
11164148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
11174148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
11184148b6bfSDavid Herrmann
11194148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
11204148b6bfSDavid Herrmann
11214148b6bfSDavid Herrmann /* init extension and MP (deactivates current extension or MP) */
11224148b6bfSDavid Herrmann wiimote_cmd_init_ext(wdata);
11239f329741SDavid Herrmann if (flags & WIIPROTO_FLAG_NO_MP) {
11249f329741SDavid Herrmann mp = false;
11259f329741SDavid Herrmann } else {
11264148b6bfSDavid Herrmann wiimote_cmd_init_mp(wdata);
11274148b6bfSDavid Herrmann mp = wiimote_cmd_read_mp(wdata, mpdata);
11289f329741SDavid Herrmann }
11294148b6bfSDavid Herrmann exttype = wiimote_cmd_read_ext(wdata, extdata);
11304148b6bfSDavid Herrmann
11314148b6bfSDavid Herrmann wiimote_cmd_release(wdata);
11324148b6bfSDavid Herrmann
11334148b6bfSDavid Herrmann /* load/unload extension module if it changed */
11344148b6bfSDavid Herrmann if (exttype != wdata->state.exttype) {
11354148b6bfSDavid Herrmann /* unload previous extension */
11364148b6bfSDavid Herrmann wiimote_ext_unload(wdata);
11374148b6bfSDavid Herrmann
11384148b6bfSDavid Herrmann if (exttype == WIIMOTE_EXT_UNKNOWN) {
113995f71266SAndy Shevchenko hid_info(wdata->hdev, "cannot detect extension; %6phC\n",
114095f71266SAndy Shevchenko extdata);
11414148b6bfSDavid Herrmann } else if (exttype == WIIMOTE_EXT_NONE) {
11424148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
11434148b6bfSDavid Herrmann wdata->state.exttype = WIIMOTE_EXT_NONE;
11444148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
11454148b6bfSDavid Herrmann } else {
11464148b6bfSDavid Herrmann hid_info(wdata->hdev, "detected extension: %s\n",
11474148b6bfSDavid Herrmann wiimote_exttype_names[exttype]);
11484148b6bfSDavid Herrmann /* try loading new extension */
11494148b6bfSDavid Herrmann wiimote_ext_load(wdata, exttype);
11504148b6bfSDavid Herrmann }
11514148b6bfSDavid Herrmann }
11524148b6bfSDavid Herrmann
11534148b6bfSDavid Herrmann /* load/unload MP module if it changed */
11544148b6bfSDavid Herrmann if (mp) {
11554148b6bfSDavid Herrmann if (!wdata->state.mp) {
11564148b6bfSDavid Herrmann hid_info(wdata->hdev, "detected extension: Nintendo Wii Motion Plus\n");
11574148b6bfSDavid Herrmann wiimote_mp_load(wdata);
11584148b6bfSDavid Herrmann }
11594148b6bfSDavid Herrmann } else if (wdata->state.mp) {
11604148b6bfSDavid Herrmann wiimote_mp_unload(wdata);
11614148b6bfSDavid Herrmann }
11624148b6bfSDavid Herrmann
11634148b6bfSDavid Herrmann /* if MP is not used, do not map or activate it */
11644148b6bfSDavid Herrmann if (!(flags & WIIPROTO_FLAG_MP_USED))
11654148b6bfSDavid Herrmann mp = false;
11664148b6bfSDavid Herrmann
11674148b6bfSDavid Herrmann /* map MP into main extension registers if used */
11684148b6bfSDavid Herrmann if (mp) {
11694148b6bfSDavid Herrmann wiimote_cmd_acquire_noint(wdata);
11704148b6bfSDavid Herrmann wiimote_cmd_map_mp(wdata, exttype);
11714148b6bfSDavid Herrmann wiimote_cmd_release(wdata);
11724148b6bfSDavid Herrmann
11734148b6bfSDavid Herrmann /* delete MP hotplug timer */
11744148b6bfSDavid Herrmann del_timer_sync(&wdata->timer);
11754148b6bfSDavid Herrmann } else {
11764148b6bfSDavid Herrmann /* reschedule MP hotplug timer */
11779f329741SDavid Herrmann if (!(flags & WIIPROTO_FLAG_BUILTIN_MP) &&
11789f329741SDavid Herrmann !(flags & WIIPROTO_FLAG_NO_MP))
11794148b6bfSDavid Herrmann mod_timer(&wdata->timer, jiffies + HZ * 4);
11804148b6bfSDavid Herrmann }
11814148b6bfSDavid Herrmann
11824148b6bfSDavid Herrmann spin_lock_irq(&wdata->state.lock);
11834148b6bfSDavid Herrmann
11844148b6bfSDavid Herrmann /* enable data forwarding again and set expected hotplug state */
11854148b6bfSDavid Herrmann if (mp) {
11864148b6bfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_MP_ACTIVE;
11874148b6bfSDavid Herrmann if (wdata->state.exttype == WIIMOTE_EXT_NONE) {
11884148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
11894148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
11904148b6bfSDavid Herrmann } else {
11914148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
11924148b6bfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
11934148b6bfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
11944148b6bfSDavid Herrmann }
11954148b6bfSDavid Herrmann } else if (wdata->state.exttype != WIIMOTE_EXT_NONE) {
11964148b6bfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_EXT_ACTIVE;
11974148b6bfSDavid Herrmann }
11984148b6bfSDavid Herrmann
11994148b6bfSDavid Herrmann /* request status report for hotplug state updates */
12004148b6bfSDavid Herrmann wiiproto_req_status(wdata);
12014148b6bfSDavid Herrmann
12024148b6bfSDavid Herrmann spin_unlock_irq(&wdata->state.lock);
12034148b6bfSDavid Herrmann
12044148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "detected extensions: MP: %d EXT: %d\n",
12054148b6bfSDavid Herrmann wdata->state.mp, wdata->state.exttype);
1206c57ff761SDavid Herrmann }
1207c57ff761SDavid Herrmann
wiimote_init_worker(struct work_struct * work)1208c57ff761SDavid Herrmann static void wiimote_init_worker(struct work_struct *work)
1209c57ff761SDavid Herrmann {
1210c57ff761SDavid Herrmann struct wiimote_data *wdata = container_of(work, struct wiimote_data,
1211c57ff761SDavid Herrmann init_worker);
1212c7da0867SDavid Herrmann bool changed = false;
1213c57ff761SDavid Herrmann
1214c7da0867SDavid Herrmann if (wdata->state.devtype == WIIMOTE_DEV_PENDING) {
1215c57ff761SDavid Herrmann wiimote_init_detect(wdata);
1216c7da0867SDavid Herrmann changed = true;
1217c7da0867SDavid Herrmann }
1218c7da0867SDavid Herrmann
121977a74809SDavid Herrmann if (changed || !wiimote_init_check(wdata))
12204148b6bfSDavid Herrmann wiimote_init_hotplug(wdata);
1221c7da0867SDavid Herrmann
1222c7da0867SDavid Herrmann if (changed)
1223c7da0867SDavid Herrmann kobject_uevent(&wdata->hdev->dev.kobj, KOBJ_CHANGE);
12244148b6bfSDavid Herrmann }
12254148b6bfSDavid Herrmann
__wiimote_schedule(struct wiimote_data * wdata)12264148b6bfSDavid Herrmann void __wiimote_schedule(struct wiimote_data *wdata)
12274148b6bfSDavid Herrmann {
12284148b6bfSDavid Herrmann if (!(wdata->state.flags & WIIPROTO_FLAG_EXITING))
12294148b6bfSDavid Herrmann schedule_work(&wdata->init_worker);
12304148b6bfSDavid Herrmann }
12314148b6bfSDavid Herrmann
wiimote_schedule(struct wiimote_data * wdata)12324148b6bfSDavid Herrmann static void wiimote_schedule(struct wiimote_data *wdata)
12334148b6bfSDavid Herrmann {
12344148b6bfSDavid Herrmann unsigned long flags;
12354148b6bfSDavid Herrmann
12364148b6bfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
12374148b6bfSDavid Herrmann __wiimote_schedule(wdata);
12384148b6bfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
12394148b6bfSDavid Herrmann }
12404148b6bfSDavid Herrmann
wiimote_init_timeout(struct timer_list * t)1241e99e88a9SKees Cook static void wiimote_init_timeout(struct timer_list *t)
12424148b6bfSDavid Herrmann {
1243e99e88a9SKees Cook struct wiimote_data *wdata = from_timer(wdata, t, timer);
12444148b6bfSDavid Herrmann
12454148b6bfSDavid Herrmann wiimote_schedule(wdata);
1246c57ff761SDavid Herrmann }
1247c57ff761SDavid Herrmann
1248c57ff761SDavid Herrmann /* protocol handlers */
1249c57ff761SDavid Herrmann
handler_keys(struct wiimote_data * wdata,const __u8 * payload)1250192a1acfSDavid Herrmann static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
1251192a1acfSDavid Herrmann {
125220cef813SDavid Herrmann const __u8 *iter, *mods;
125320cef813SDavid Herrmann const struct wiimod_ops *ops;
125420cef813SDavid Herrmann
12554148b6bfSDavid Herrmann ops = wiimod_ext_table[wdata->state.exttype];
12564148b6bfSDavid Herrmann if (ops->in_keys) {
12574148b6bfSDavid Herrmann ops->in_keys(wdata, payload);
12584148b6bfSDavid Herrmann return;
12594148b6bfSDavid Herrmann }
12604148b6bfSDavid Herrmann
126120cef813SDavid Herrmann mods = wiimote_devtype_mods[wdata->state.devtype];
126220cef813SDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
126320cef813SDavid Herrmann ops = wiimod_table[*iter];
126420cef813SDavid Herrmann if (ops->in_keys) {
126520cef813SDavid Herrmann ops->in_keys(wdata, payload);
126620cef813SDavid Herrmann break;
126720cef813SDavid Herrmann }
126820cef813SDavid Herrmann }
1269192a1acfSDavid Herrmann }
1270192a1acfSDavid Herrmann
handler_accel(struct wiimote_data * wdata,const __u8 * payload)1271192a1acfSDavid Herrmann static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
1272192a1acfSDavid Herrmann {
12730ea16757SDavid Herrmann const __u8 *iter, *mods;
12740ea16757SDavid Herrmann const struct wiimod_ops *ops;
1275192a1acfSDavid Herrmann
12764148b6bfSDavid Herrmann ops = wiimod_ext_table[wdata->state.exttype];
12774148b6bfSDavid Herrmann if (ops->in_accel) {
12784148b6bfSDavid Herrmann ops->in_accel(wdata, payload);
12794148b6bfSDavid Herrmann return;
12804148b6bfSDavid Herrmann }
12814148b6bfSDavid Herrmann
12820ea16757SDavid Herrmann mods = wiimote_devtype_mods[wdata->state.devtype];
12830ea16757SDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
12840ea16757SDavid Herrmann ops = wiimod_table[*iter];
12850ea16757SDavid Herrmann if (ops->in_accel) {
12860ea16757SDavid Herrmann ops->in_accel(wdata, payload);
12870ea16757SDavid Herrmann break;
12880ea16757SDavid Herrmann }
12890ea16757SDavid Herrmann }
1290192a1acfSDavid Herrmann }
1291192a1acfSDavid Herrmann
valid_ext_handler(const struct wiimod_ops * ops,size_t len)12924148b6bfSDavid Herrmann static bool valid_ext_handler(const struct wiimod_ops *ops, size_t len)
12934148b6bfSDavid Herrmann {
12944148b6bfSDavid Herrmann if (!ops->in_ext)
12954148b6bfSDavid Herrmann return false;
12964148b6bfSDavid Herrmann if ((ops->flags & WIIMOD_FLAG_EXT8) && len < 8)
12974148b6bfSDavid Herrmann return false;
12984148b6bfSDavid Herrmann if ((ops->flags & WIIMOD_FLAG_EXT16) && len < 16)
12994148b6bfSDavid Herrmann return false;
13004148b6bfSDavid Herrmann
13014148b6bfSDavid Herrmann return true;
13024148b6bfSDavid Herrmann }
13034148b6bfSDavid Herrmann
handler_ext(struct wiimote_data * wdata,const __u8 * payload,size_t len)13044148b6bfSDavid Herrmann static void handler_ext(struct wiimote_data *wdata, const __u8 *payload,
13054148b6bfSDavid Herrmann size_t len)
13064148b6bfSDavid Herrmann {
1307876727eaSDavid Herrmann static const __u8 invalid[21] = { 0xff, 0xff, 0xff, 0xff,
1308876727eaSDavid Herrmann 0xff, 0xff, 0xff, 0xff,
1309876727eaSDavid Herrmann 0xff, 0xff, 0xff, 0xff,
1310876727eaSDavid Herrmann 0xff, 0xff, 0xff, 0xff,
1311876727eaSDavid Herrmann 0xff, 0xff, 0xff, 0xff,
1312876727eaSDavid Herrmann 0xff };
13134148b6bfSDavid Herrmann const __u8 *iter, *mods;
13144148b6bfSDavid Herrmann const struct wiimod_ops *ops;
13154148b6bfSDavid Herrmann bool is_mp;
13164148b6bfSDavid Herrmann
1317876727eaSDavid Herrmann if (len > 21)
1318876727eaSDavid Herrmann len = 21;
1319876727eaSDavid Herrmann if (len < 6 || !memcmp(payload, invalid, len))
13204148b6bfSDavid Herrmann return;
13214148b6bfSDavid Herrmann
13224148b6bfSDavid Herrmann /* if MP is active, track MP slot hotplugging */
13234148b6bfSDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
13244148b6bfSDavid Herrmann /* this bit is set for invalid events (eg. during hotplug) */
13254148b6bfSDavid Herrmann if (payload[5] & 0x01)
13264148b6bfSDavid Herrmann return;
13274148b6bfSDavid Herrmann
13284148b6bfSDavid Herrmann if (payload[4] & 0x01) {
13294148b6bfSDavid Herrmann if (!(wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED)) {
13304148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "MP hotplug: 1\n");
13314148b6bfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_MP_PLUGGED;
13324148b6bfSDavid Herrmann __wiimote_schedule(wdata);
13334148b6bfSDavid Herrmann }
13344148b6bfSDavid Herrmann } else {
13354148b6bfSDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_MP_PLUGGED) {
13364148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "MP hotplug: 0\n");
13374148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
13384148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
13394148b6bfSDavid Herrmann __wiimote_schedule(wdata);
13404148b6bfSDavid Herrmann }
13414148b6bfSDavid Herrmann }
13424148b6bfSDavid Herrmann
13434148b6bfSDavid Herrmann /* detect MP data that is sent interleaved with EXT data */
13444148b6bfSDavid Herrmann is_mp = payload[5] & 0x02;
13454148b6bfSDavid Herrmann } else {
13464148b6bfSDavid Herrmann is_mp = false;
13474148b6bfSDavid Herrmann }
13484148b6bfSDavid Herrmann
13494148b6bfSDavid Herrmann /* ignore EXT events if no extension is active */
13504148b6bfSDavid Herrmann if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_ACTIVE) && !is_mp)
13514148b6bfSDavid Herrmann return;
13524148b6bfSDavid Herrmann
13534148b6bfSDavid Herrmann /* try forwarding to extension handler, first */
13544148b6bfSDavid Herrmann ops = wiimod_ext_table[wdata->state.exttype];
13554148b6bfSDavid Herrmann if (is_mp && ops->in_mp) {
13564148b6bfSDavid Herrmann ops->in_mp(wdata, payload);
13574148b6bfSDavid Herrmann return;
13584148b6bfSDavid Herrmann } else if (!is_mp && valid_ext_handler(ops, len)) {
13594148b6bfSDavid Herrmann ops->in_ext(wdata, payload);
13604148b6bfSDavid Herrmann return;
13614148b6bfSDavid Herrmann }
13624148b6bfSDavid Herrmann
13634148b6bfSDavid Herrmann /* try forwarding to MP handler */
13644148b6bfSDavid Herrmann ops = &wiimod_mp;
13654148b6bfSDavid Herrmann if (is_mp && ops->in_mp) {
13664148b6bfSDavid Herrmann ops->in_mp(wdata, payload);
13674148b6bfSDavid Herrmann return;
13684148b6bfSDavid Herrmann } else if (!is_mp && valid_ext_handler(ops, len)) {
13694148b6bfSDavid Herrmann ops->in_ext(wdata, payload);
13704148b6bfSDavid Herrmann return;
13714148b6bfSDavid Herrmann }
13724148b6bfSDavid Herrmann
13734148b6bfSDavid Herrmann /* try forwarding to loaded modules */
13744148b6bfSDavid Herrmann mods = wiimote_devtype_mods[wdata->state.devtype];
13754148b6bfSDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
13764148b6bfSDavid Herrmann ops = wiimod_table[*iter];
13774148b6bfSDavid Herrmann if (is_mp && ops->in_mp) {
13784148b6bfSDavid Herrmann ops->in_mp(wdata, payload);
13794148b6bfSDavid Herrmann return;
13804148b6bfSDavid Herrmann } else if (!is_mp && valid_ext_handler(ops, len)) {
13814148b6bfSDavid Herrmann ops->in_ext(wdata, payload);
13824148b6bfSDavid Herrmann return;
13834148b6bfSDavid Herrmann }
13844148b6bfSDavid Herrmann }
13854148b6bfSDavid Herrmann }
13864148b6bfSDavid Herrmann
13873b5f03c4SDavid Herrmann #define ir_to_input0(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 0)
13883b5f03c4SDavid Herrmann #define ir_to_input1(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 1)
13893b5f03c4SDavid Herrmann #define ir_to_input2(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 2)
13903b5f03c4SDavid Herrmann #define ir_to_input3(wdata, ir, packed) handler_ir((wdata), (ir), (packed), 3)
1391192a1acfSDavid Herrmann
handler_ir(struct wiimote_data * wdata,const __u8 * payload,bool packed,unsigned int id)13923b5f03c4SDavid Herrmann static void handler_ir(struct wiimote_data *wdata, const __u8 *payload,
13933b5f03c4SDavid Herrmann bool packed, unsigned int id)
1394192a1acfSDavid Herrmann {
13953b5f03c4SDavid Herrmann const __u8 *iter, *mods;
13963b5f03c4SDavid Herrmann const struct wiimod_ops *ops;
1397192a1acfSDavid Herrmann
13984148b6bfSDavid Herrmann ops = wiimod_ext_table[wdata->state.exttype];
13994148b6bfSDavid Herrmann if (ops->in_ir) {
14004148b6bfSDavid Herrmann ops->in_ir(wdata, payload, packed, id);
14014148b6bfSDavid Herrmann return;
14024148b6bfSDavid Herrmann }
14034148b6bfSDavid Herrmann
14043b5f03c4SDavid Herrmann mods = wiimote_devtype_mods[wdata->state.devtype];
14053b5f03c4SDavid Herrmann for (iter = mods; *iter != WIIMOD_NULL; ++iter) {
14063b5f03c4SDavid Herrmann ops = wiimod_table[*iter];
14073b5f03c4SDavid Herrmann if (ops->in_ir) {
14083b5f03c4SDavid Herrmann ops->in_ir(wdata, payload, packed, id);
14093b5f03c4SDavid Herrmann break;
1410192a1acfSDavid Herrmann }
14113b5f03c4SDavid Herrmann }
1412192a1acfSDavid Herrmann }
1413192a1acfSDavid Herrmann
14142d44e3d2SDavid Herrmann /* reduced status report with "BB BB" key data only */
handler_status_K(struct wiimote_data * wdata,const __u8 * payload)14152d44e3d2SDavid Herrmann static void handler_status_K(struct wiimote_data *wdata,
14162d44e3d2SDavid Herrmann const __u8 *payload)
1417192a1acfSDavid Herrmann {
1418192a1acfSDavid Herrmann handler_keys(wdata, payload);
1419192a1acfSDavid Herrmann
1420192a1acfSDavid Herrmann /* on status reports the drm is reset so we need to resend the drm */
1421192a1acfSDavid Herrmann wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
14222d44e3d2SDavid Herrmann }
14232d44e3d2SDavid Herrmann
14242d44e3d2SDavid Herrmann /* extended status report with "BB BB LF 00 00 VV" data */
handler_status(struct wiimote_data * wdata,const __u8 * payload)14252d44e3d2SDavid Herrmann static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
14262d44e3d2SDavid Herrmann {
14272d44e3d2SDavid Herrmann handler_status_K(wdata, payload);
1428192a1acfSDavid Herrmann
1429c57ff761SDavid Herrmann /* update extension status */
1430c57ff761SDavid Herrmann if (payload[2] & 0x02) {
14314148b6bfSDavid Herrmann if (!(wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED)) {
14324148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "EXT hotplug: 1\n");
1433c57ff761SDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_EXT_PLUGGED;
14344148b6bfSDavid Herrmann __wiimote_schedule(wdata);
14354148b6bfSDavid Herrmann }
1436c57ff761SDavid Herrmann } else {
14374148b6bfSDavid Herrmann if (wdata->state.flags & WIIPROTO_FLAG_EXT_PLUGGED) {
14384148b6bfSDavid Herrmann hid_dbg(wdata->hdev, "EXT hotplug: 0\n");
1439c57ff761SDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_PLUGGED;
14404148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_MP_PLUGGED;
14414148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_EXT_ACTIVE;
14424148b6bfSDavid Herrmann wdata->state.flags &= ~WIIPROTO_FLAG_MP_ACTIVE;
14434148b6bfSDavid Herrmann __wiimote_schedule(wdata);
14444148b6bfSDavid Herrmann }
1445c57ff761SDavid Herrmann }
1446cb99221bSDavid Herrmann
1447192a1acfSDavid Herrmann wdata->state.cmd_battery = payload[5];
14486b80bb94SDavid Herrmann if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0))
1449192a1acfSDavid Herrmann wiimote_cmd_complete(wdata);
1450192a1acfSDavid Herrmann }
1451192a1acfSDavid Herrmann
14522d44e3d2SDavid Herrmann /* reduced generic report with "BB BB" key data only */
handler_generic_K(struct wiimote_data * wdata,const __u8 * payload)14532d44e3d2SDavid Herrmann static void handler_generic_K(struct wiimote_data *wdata, const __u8 *payload)
14542d44e3d2SDavid Herrmann {
14552d44e3d2SDavid Herrmann handler_keys(wdata, payload);
14562d44e3d2SDavid Herrmann }
14572d44e3d2SDavid Herrmann
handler_data(struct wiimote_data * wdata,const __u8 * payload)1458192a1acfSDavid Herrmann static void handler_data(struct wiimote_data *wdata, const __u8 *payload)
1459192a1acfSDavid Herrmann {
1460fad8c0e3SDavid Herrmann __u16 offset = payload[3] << 8 | payload[4];
1461fad8c0e3SDavid Herrmann __u8 size = (payload[2] >> 4) + 1;
1462fad8c0e3SDavid Herrmann __u8 err = payload[2] & 0x0f;
1463fad8c0e3SDavid Herrmann
1464192a1acfSDavid Herrmann handler_keys(wdata, payload);
1465fad8c0e3SDavid Herrmann
1466fad8c0e3SDavid Herrmann if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_RMEM, offset)) {
1467fad8c0e3SDavid Herrmann if (err)
1468fad8c0e3SDavid Herrmann size = 0;
1469fad8c0e3SDavid Herrmann else if (size > wdata->state.cmd_read_size)
1470fad8c0e3SDavid Herrmann size = wdata->state.cmd_read_size;
1471fad8c0e3SDavid Herrmann
1472fad8c0e3SDavid Herrmann wdata->state.cmd_read_size = size;
1473fad8c0e3SDavid Herrmann if (wdata->state.cmd_read_buf)
1474fad8c0e3SDavid Herrmann memcpy(wdata->state.cmd_read_buf, &payload[5], size);
1475fad8c0e3SDavid Herrmann wiimote_cmd_complete(wdata);
1476fad8c0e3SDavid Herrmann }
1477192a1acfSDavid Herrmann }
1478192a1acfSDavid Herrmann
handler_return(struct wiimote_data * wdata,const __u8 * payload)1479192a1acfSDavid Herrmann static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
1480192a1acfSDavid Herrmann {
1481192a1acfSDavid Herrmann __u8 err = payload[3];
1482192a1acfSDavid Herrmann __u8 cmd = payload[2];
1483192a1acfSDavid Herrmann
1484192a1acfSDavid Herrmann handler_keys(wdata, payload);
1485192a1acfSDavid Herrmann
1486192a1acfSDavid Herrmann if (wiimote_cmd_pending(wdata, cmd, 0)) {
1487192a1acfSDavid Herrmann wdata->state.cmd_err = err;
1488192a1acfSDavid Herrmann wiimote_cmd_complete(wdata);
1489192a1acfSDavid Herrmann } else if (err) {
14904d2b7163STom Rix hid_warn(wdata->hdev, "Remote error %u on req %u\n", err,
1491192a1acfSDavid Herrmann cmd);
1492192a1acfSDavid Herrmann }
1493192a1acfSDavid Herrmann }
1494192a1acfSDavid Herrmann
handler_drm_KA(struct wiimote_data * wdata,const __u8 * payload)1495192a1acfSDavid Herrmann static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
1496192a1acfSDavid Herrmann {
1497192a1acfSDavid Herrmann handler_keys(wdata, payload);
1498192a1acfSDavid Herrmann handler_accel(wdata, payload);
1499192a1acfSDavid Herrmann }
1500192a1acfSDavid Herrmann
handler_drm_KE(struct wiimote_data * wdata,const __u8 * payload)1501192a1acfSDavid Herrmann static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
1502192a1acfSDavid Herrmann {
1503192a1acfSDavid Herrmann handler_keys(wdata, payload);
15044148b6bfSDavid Herrmann handler_ext(wdata, &payload[2], 8);
1505192a1acfSDavid Herrmann }
1506192a1acfSDavid Herrmann
handler_drm_KAI(struct wiimote_data * wdata,const __u8 * payload)1507192a1acfSDavid Herrmann static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
1508192a1acfSDavid Herrmann {
1509192a1acfSDavid Herrmann handler_keys(wdata, payload);
1510192a1acfSDavid Herrmann handler_accel(wdata, payload);
1511192a1acfSDavid Herrmann ir_to_input0(wdata, &payload[5], false);
1512192a1acfSDavid Herrmann ir_to_input1(wdata, &payload[8], false);
1513192a1acfSDavid Herrmann ir_to_input2(wdata, &payload[11], false);
1514192a1acfSDavid Herrmann ir_to_input3(wdata, &payload[14], false);
1515192a1acfSDavid Herrmann }
1516192a1acfSDavid Herrmann
handler_drm_KEE(struct wiimote_data * wdata,const __u8 * payload)1517192a1acfSDavid Herrmann static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
1518192a1acfSDavid Herrmann {
1519192a1acfSDavid Herrmann handler_keys(wdata, payload);
15204148b6bfSDavid Herrmann handler_ext(wdata, &payload[2], 19);
1521192a1acfSDavid Herrmann }
1522192a1acfSDavid Herrmann
handler_drm_KIE(struct wiimote_data * wdata,const __u8 * payload)1523192a1acfSDavid Herrmann static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
1524192a1acfSDavid Herrmann {
1525192a1acfSDavid Herrmann handler_keys(wdata, payload);
1526192a1acfSDavid Herrmann ir_to_input0(wdata, &payload[2], false);
1527192a1acfSDavid Herrmann ir_to_input1(wdata, &payload[4], true);
1528192a1acfSDavid Herrmann ir_to_input2(wdata, &payload[7], false);
1529192a1acfSDavid Herrmann ir_to_input3(wdata, &payload[9], true);
15304148b6bfSDavid Herrmann handler_ext(wdata, &payload[12], 9);
1531192a1acfSDavid Herrmann }
1532192a1acfSDavid Herrmann
handler_drm_KAE(struct wiimote_data * wdata,const __u8 * payload)1533192a1acfSDavid Herrmann static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
1534192a1acfSDavid Herrmann {
1535192a1acfSDavid Herrmann handler_keys(wdata, payload);
1536192a1acfSDavid Herrmann handler_accel(wdata, payload);
15374148b6bfSDavid Herrmann handler_ext(wdata, &payload[5], 16);
1538192a1acfSDavid Herrmann }
1539192a1acfSDavid Herrmann
handler_drm_KAIE(struct wiimote_data * wdata,const __u8 * payload)1540192a1acfSDavid Herrmann static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
1541192a1acfSDavid Herrmann {
1542192a1acfSDavid Herrmann handler_keys(wdata, payload);
1543192a1acfSDavid Herrmann handler_accel(wdata, payload);
1544192a1acfSDavid Herrmann ir_to_input0(wdata, &payload[5], false);
1545192a1acfSDavid Herrmann ir_to_input1(wdata, &payload[7], true);
1546192a1acfSDavid Herrmann ir_to_input2(wdata, &payload[10], false);
1547192a1acfSDavid Herrmann ir_to_input3(wdata, &payload[12], true);
15484148b6bfSDavid Herrmann handler_ext(wdata, &payload[15], 6);
1549192a1acfSDavid Herrmann }
1550192a1acfSDavid Herrmann
handler_drm_E(struct wiimote_data * wdata,const __u8 * payload)1551192a1acfSDavid Herrmann static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
1552192a1acfSDavid Herrmann {
15534148b6bfSDavid Herrmann handler_ext(wdata, payload, 21);
1554192a1acfSDavid Herrmann }
1555192a1acfSDavid Herrmann
handler_drm_SKAI1(struct wiimote_data * wdata,const __u8 * payload)1556192a1acfSDavid Herrmann static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
1557192a1acfSDavid Herrmann {
1558192a1acfSDavid Herrmann handler_keys(wdata, payload);
1559192a1acfSDavid Herrmann
1560192a1acfSDavid Herrmann wdata->state.accel_split[0] = payload[2];
1561192a1acfSDavid Herrmann wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
1562192a1acfSDavid Herrmann wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
1563192a1acfSDavid Herrmann
1564192a1acfSDavid Herrmann ir_to_input0(wdata, &payload[3], false);
1565192a1acfSDavid Herrmann ir_to_input1(wdata, &payload[12], false);
1566192a1acfSDavid Herrmann }
1567192a1acfSDavid Herrmann
handler_drm_SKAI2(struct wiimote_data * wdata,const __u8 * payload)1568192a1acfSDavid Herrmann static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
1569192a1acfSDavid Herrmann {
1570192a1acfSDavid Herrmann __u8 buf[5];
1571192a1acfSDavid Herrmann
1572192a1acfSDavid Herrmann handler_keys(wdata, payload);
1573192a1acfSDavid Herrmann
1574192a1acfSDavid Herrmann wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02);
1575192a1acfSDavid Herrmann wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08);
1576192a1acfSDavid Herrmann
1577192a1acfSDavid Herrmann buf[0] = 0;
1578192a1acfSDavid Herrmann buf[1] = 0;
1579192a1acfSDavid Herrmann buf[2] = wdata->state.accel_split[0];
1580192a1acfSDavid Herrmann buf[3] = payload[2];
1581192a1acfSDavid Herrmann buf[4] = wdata->state.accel_split[1];
1582192a1acfSDavid Herrmann handler_accel(wdata, buf);
1583192a1acfSDavid Herrmann
1584192a1acfSDavid Herrmann ir_to_input2(wdata, &payload[3], false);
1585192a1acfSDavid Herrmann ir_to_input3(wdata, &payload[12], false);
1586192a1acfSDavid Herrmann }
1587192a1acfSDavid Herrmann
1588192a1acfSDavid Herrmann struct wiiproto_handler {
1589192a1acfSDavid Herrmann __u8 id;
1590192a1acfSDavid Herrmann size_t size;
1591192a1acfSDavid Herrmann void (*func)(struct wiimote_data *wdata, const __u8 *payload);
1592192a1acfSDavid Herrmann };
1593192a1acfSDavid Herrmann
1594aeeba45aSIan Abbott static const struct wiiproto_handler handlers[] = {
1595192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status },
15962d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_STATUS, .size = 2, .func = handler_status_K },
1597192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data },
15982d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DATA, .size = 2, .func = handler_generic_K },
1599192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return },
16002d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_RETURN, .size = 2, .func = handler_generic_K },
1601192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
1602192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
16032d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KA, .size = 2, .func = handler_generic_K },
1604192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE },
16052d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KE, .size = 2, .func = handler_generic_K },
1606192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
16072d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KAI, .size = 2, .func = handler_generic_K },
1608192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE },
16092d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KEE, .size = 2, .func = handler_generic_K },
1610192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
16112d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KAE, .size = 2, .func = handler_generic_K },
1612192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
16132d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KIE, .size = 2, .func = handler_generic_K },
1614192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
16152d44e3d2SDavid Herrmann { .id = WIIPROTO_REQ_DRM_KAIE, .size = 2, .func = handler_generic_K },
1616192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E },
1617192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
1618192a1acfSDavid Herrmann { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
1619192a1acfSDavid Herrmann { .id = 0 }
1620192a1acfSDavid Herrmann };
1621192a1acfSDavid Herrmann
wiimote_hid_event(struct hid_device * hdev,struct hid_report * report,u8 * raw_data,int size)1622192a1acfSDavid Herrmann static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
1623192a1acfSDavid Herrmann u8 *raw_data, int size)
1624192a1acfSDavid Herrmann {
1625192a1acfSDavid Herrmann struct wiimote_data *wdata = hid_get_drvdata(hdev);
1626aeeba45aSIan Abbott const struct wiiproto_handler *h;
1627192a1acfSDavid Herrmann int i;
1628192a1acfSDavid Herrmann unsigned long flags;
1629192a1acfSDavid Herrmann
1630192a1acfSDavid Herrmann if (size < 1)
1631192a1acfSDavid Herrmann return -EINVAL;
1632192a1acfSDavid Herrmann
1633192a1acfSDavid Herrmann for (i = 0; handlers[i].id; ++i) {
1634192a1acfSDavid Herrmann h = &handlers[i];
1635192a1acfSDavid Herrmann if (h->id == raw_data[0] && h->size < size) {
16365eae59ccSIan Abbott spin_lock_irqsave(&wdata->state.lock, flags);
1637192a1acfSDavid Herrmann h->func(wdata, &raw_data[1]);
16385eae59ccSIan Abbott spin_unlock_irqrestore(&wdata->state.lock, flags);
16392d44e3d2SDavid Herrmann break;
1640192a1acfSDavid Herrmann }
1641192a1acfSDavid Herrmann }
1642192a1acfSDavid Herrmann
16432d44e3d2SDavid Herrmann if (!handlers[i].id)
1644192a1acfSDavid Herrmann hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0],
1645192a1acfSDavid Herrmann size);
1646192a1acfSDavid Herrmann
1647192a1acfSDavid Herrmann return 0;
1648192a1acfSDavid Herrmann }
1649192a1acfSDavid Herrmann
wiimote_ext_show(struct device * dev,struct device_attribute * attr,char * buf)1650c7da0867SDavid Herrmann static ssize_t wiimote_ext_show(struct device *dev,
1651c7da0867SDavid Herrmann struct device_attribute *attr,
1652c7da0867SDavid Herrmann char *buf)
1653c7da0867SDavid Herrmann {
1654c7da0867SDavid Herrmann struct wiimote_data *wdata = dev_to_wii(dev);
1655c7da0867SDavid Herrmann __u8 type;
1656c7da0867SDavid Herrmann unsigned long flags;
1657c7da0867SDavid Herrmann
1658c7da0867SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
1659c7da0867SDavid Herrmann type = wdata->state.exttype;
1660c7da0867SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
1661c7da0867SDavid Herrmann
1662c7da0867SDavid Herrmann switch (type) {
1663c7da0867SDavid Herrmann case WIIMOTE_EXT_NONE:
1664c7da0867SDavid Herrmann return sprintf(buf, "none\n");
1665c7da0867SDavid Herrmann case WIIMOTE_EXT_NUNCHUK:
1666c7da0867SDavid Herrmann return sprintf(buf, "nunchuk\n");
1667c7da0867SDavid Herrmann case WIIMOTE_EXT_CLASSIC_CONTROLLER:
1668c7da0867SDavid Herrmann return sprintf(buf, "classic\n");
1669c7da0867SDavid Herrmann case WIIMOTE_EXT_BALANCE_BOARD:
1670c7da0867SDavid Herrmann return sprintf(buf, "balanceboard\n");
1671b8e0fe31SDavid Herrmann case WIIMOTE_EXT_PRO_CONTROLLER:
1672b8e0fe31SDavid Herrmann return sprintf(buf, "procontroller\n");
1673d4bdf2d2SNicolas Adenis-Lamarre case WIIMOTE_EXT_DRUMS:
1674d4bdf2d2SNicolas Adenis-Lamarre return sprintf(buf, "drums\n");
1675d4bdf2d2SNicolas Adenis-Lamarre case WIIMOTE_EXT_GUITAR:
1676d4bdf2d2SNicolas Adenis-Lamarre return sprintf(buf, "guitar\n");
167705086f3dSJoshua Jun case WIIMOTE_EXT_TURNTABLE:
167805086f3dSJoshua Jun return sprintf(buf, "turntable\n");
1679c7da0867SDavid Herrmann case WIIMOTE_EXT_UNKNOWN:
1680c7da0867SDavid Herrmann default:
1681c7da0867SDavid Herrmann return sprintf(buf, "unknown\n");
1682c7da0867SDavid Herrmann }
1683c7da0867SDavid Herrmann }
1684c7da0867SDavid Herrmann
wiimote_ext_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1685c7da0867SDavid Herrmann static ssize_t wiimote_ext_store(struct device *dev,
1686c7da0867SDavid Herrmann struct device_attribute *attr,
1687c7da0867SDavid Herrmann const char *buf, size_t count)
1688c7da0867SDavid Herrmann {
1689c7da0867SDavid Herrmann struct wiimote_data *wdata = dev_to_wii(dev);
1690c7da0867SDavid Herrmann
1691c7da0867SDavid Herrmann if (!strcmp(buf, "scan")) {
1692c7da0867SDavid Herrmann wiimote_schedule(wdata);
1693c7da0867SDavid Herrmann } else {
1694c7da0867SDavid Herrmann return -EINVAL;
1695c7da0867SDavid Herrmann }
1696c7da0867SDavid Herrmann
1697c7da0867SDavid Herrmann return strnlen(buf, PAGE_SIZE);
1698c7da0867SDavid Herrmann }
1699c7da0867SDavid Herrmann
1700c7da0867SDavid Herrmann static DEVICE_ATTR(extension, S_IRUGO | S_IWUSR | S_IWGRP, wiimote_ext_show,
1701c7da0867SDavid Herrmann wiimote_ext_store);
1702c7da0867SDavid Herrmann
wiimote_dev_show(struct device * dev,struct device_attribute * attr,char * buf)1703c7da0867SDavid Herrmann static ssize_t wiimote_dev_show(struct device *dev,
1704c7da0867SDavid Herrmann struct device_attribute *attr,
1705c7da0867SDavid Herrmann char *buf)
1706c7da0867SDavid Herrmann {
1707c7da0867SDavid Herrmann struct wiimote_data *wdata = dev_to_wii(dev);
1708c7da0867SDavid Herrmann __u8 type;
1709c7da0867SDavid Herrmann unsigned long flags;
1710c7da0867SDavid Herrmann
1711c7da0867SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
1712c7da0867SDavid Herrmann type = wdata->state.devtype;
1713c7da0867SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
1714c7da0867SDavid Herrmann
1715c7da0867SDavid Herrmann switch (type) {
1716c7da0867SDavid Herrmann case WIIMOTE_DEV_GENERIC:
1717c7da0867SDavid Herrmann return sprintf(buf, "generic\n");
1718c7da0867SDavid Herrmann case WIIMOTE_DEV_GEN10:
1719c7da0867SDavid Herrmann return sprintf(buf, "gen10\n");
1720c7da0867SDavid Herrmann case WIIMOTE_DEV_GEN20:
1721c7da0867SDavid Herrmann return sprintf(buf, "gen20\n");
1722c7da0867SDavid Herrmann case WIIMOTE_DEV_BALANCE_BOARD:
1723c7da0867SDavid Herrmann return sprintf(buf, "balanceboard\n");
1724b8e0fe31SDavid Herrmann case WIIMOTE_DEV_PRO_CONTROLLER:
1725b8e0fe31SDavid Herrmann return sprintf(buf, "procontroller\n");
1726c7da0867SDavid Herrmann case WIIMOTE_DEV_PENDING:
1727c7da0867SDavid Herrmann return sprintf(buf, "pending\n");
1728c7da0867SDavid Herrmann case WIIMOTE_DEV_UNKNOWN:
1729c7da0867SDavid Herrmann default:
1730c7da0867SDavid Herrmann return sprintf(buf, "unknown\n");
1731c7da0867SDavid Herrmann }
1732c7da0867SDavid Herrmann }
1733c7da0867SDavid Herrmann
1734c7da0867SDavid Herrmann static DEVICE_ATTR(devtype, S_IRUGO, wiimote_dev_show, NULL);
1735c7da0867SDavid Herrmann
wiimote_create(struct hid_device * hdev)1736192a1acfSDavid Herrmann static struct wiimote_data *wiimote_create(struct hid_device *hdev)
1737192a1acfSDavid Herrmann {
1738192a1acfSDavid Herrmann struct wiimote_data *wdata;
1739192a1acfSDavid Herrmann
1740192a1acfSDavid Herrmann wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
1741192a1acfSDavid Herrmann if (!wdata)
1742192a1acfSDavid Herrmann return NULL;
1743192a1acfSDavid Herrmann
1744192a1acfSDavid Herrmann wdata->hdev = hdev;
1745192a1acfSDavid Herrmann hid_set_drvdata(hdev, wdata);
1746192a1acfSDavid Herrmann
174713938538SDavid Herrmann spin_lock_init(&wdata->queue.lock);
174813938538SDavid Herrmann INIT_WORK(&wdata->queue.worker, wiimote_queue_worker);
1749192a1acfSDavid Herrmann
1750192a1acfSDavid Herrmann spin_lock_init(&wdata->state.lock);
1751192a1acfSDavid Herrmann init_completion(&wdata->state.ready);
1752192a1acfSDavid Herrmann mutex_init(&wdata->state.sync);
175343d782aeSDavid Herrmann wdata->state.drm = WIIPROTO_REQ_DRM_K;
17546b80bb94SDavid Herrmann wdata->state.cmd_battery = 0xff;
1755192a1acfSDavid Herrmann
1756c57ff761SDavid Herrmann INIT_WORK(&wdata->init_worker, wiimote_init_worker);
1757e99e88a9SKees Cook timer_setup(&wdata->timer, wiimote_init_timeout, 0);
1758c57ff761SDavid Herrmann
1759192a1acfSDavid Herrmann return wdata;
1760192a1acfSDavid Herrmann }
1761192a1acfSDavid Herrmann
wiimote_destroy(struct wiimote_data * wdata)1762192a1acfSDavid Herrmann static void wiimote_destroy(struct wiimote_data *wdata)
1763192a1acfSDavid Herrmann {
17644148b6bfSDavid Herrmann unsigned long flags;
17654148b6bfSDavid Herrmann
176643e5e7c6SDavid Herrmann wiidebug_deinit(wdata);
17674148b6bfSDavid Herrmann
17684148b6bfSDavid Herrmann /* prevent init_worker from being scheduled again */
17694148b6bfSDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
17704148b6bfSDavid Herrmann wdata->state.flags |= WIIPROTO_FLAG_EXITING;
17714148b6bfSDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
1772192a1acfSDavid Herrmann
177320cef813SDavid Herrmann cancel_work_sync(&wdata->init_worker);
1774*292a089dSSteven Rostedt (Google) timer_shutdown_sync(&wdata->timer);
17754148b6bfSDavid Herrmann
1776c7da0867SDavid Herrmann device_remove_file(&wdata->hdev->dev, &dev_attr_devtype);
1777c7da0867SDavid Herrmann device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
1778c7da0867SDavid Herrmann
17794148b6bfSDavid Herrmann wiimote_mp_unload(wdata);
17804148b6bfSDavid Herrmann wiimote_ext_unload(wdata);
178127f06942SDavid Herrmann wiimote_modules_unload(wdata);
178213938538SDavid Herrmann cancel_work_sync(&wdata->queue.worker);
17835682b1a8SDavid Herrmann hid_hw_close(wdata->hdev);
1784192a1acfSDavid Herrmann hid_hw_stop(wdata->hdev);
1785192a1acfSDavid Herrmann
1786192a1acfSDavid Herrmann kfree(wdata);
1787192a1acfSDavid Herrmann }
1788192a1acfSDavid Herrmann
wiimote_hid_probe(struct hid_device * hdev,const struct hid_device_id * id)1789192a1acfSDavid Herrmann static int wiimote_hid_probe(struct hid_device *hdev,
1790192a1acfSDavid Herrmann const struct hid_device_id *id)
1791192a1acfSDavid Herrmann {
1792192a1acfSDavid Herrmann struct wiimote_data *wdata;
1793192a1acfSDavid Herrmann int ret;
1794192a1acfSDavid Herrmann
179590120d66SDavid Herrmann hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
179690120d66SDavid Herrmann
1797192a1acfSDavid Herrmann wdata = wiimote_create(hdev);
1798192a1acfSDavid Herrmann if (!wdata) {
1799192a1acfSDavid Herrmann hid_err(hdev, "Can't alloc device\n");
1800192a1acfSDavid Herrmann return -ENOMEM;
1801192a1acfSDavid Herrmann }
1802192a1acfSDavid Herrmann
1803192a1acfSDavid Herrmann ret = hid_parse(hdev);
1804192a1acfSDavid Herrmann if (ret) {
1805192a1acfSDavid Herrmann hid_err(hdev, "HID parse failed\n");
1806192a1acfSDavid Herrmann goto err;
1807192a1acfSDavid Herrmann }
1808192a1acfSDavid Herrmann
1809192a1acfSDavid Herrmann ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
1810192a1acfSDavid Herrmann if (ret) {
1811192a1acfSDavid Herrmann hid_err(hdev, "HW start failed\n");
1812192a1acfSDavid Herrmann goto err;
1813192a1acfSDavid Herrmann }
1814192a1acfSDavid Herrmann
18155682b1a8SDavid Herrmann ret = hid_hw_open(hdev);
18165682b1a8SDavid Herrmann if (ret) {
18175682b1a8SDavid Herrmann hid_err(hdev, "cannot start hardware I/O\n");
18185682b1a8SDavid Herrmann goto err_stop;
18195682b1a8SDavid Herrmann }
18205682b1a8SDavid Herrmann
1821c7da0867SDavid Herrmann ret = device_create_file(&hdev->dev, &dev_attr_extension);
1822c7da0867SDavid Herrmann if (ret) {
1823c7da0867SDavid Herrmann hid_err(hdev, "cannot create sysfs attribute\n");
1824c7da0867SDavid Herrmann goto err_close;
1825c7da0867SDavid Herrmann }
1826c7da0867SDavid Herrmann
1827c7da0867SDavid Herrmann ret = device_create_file(&hdev->dev, &dev_attr_devtype);
1828c7da0867SDavid Herrmann if (ret) {
1829c7da0867SDavid Herrmann hid_err(hdev, "cannot create sysfs attribute\n");
1830c7da0867SDavid Herrmann goto err_ext;
1831c7da0867SDavid Herrmann }
1832c7da0867SDavid Herrmann
183343e5e7c6SDavid Herrmann ret = wiidebug_init(wdata);
183443e5e7c6SDavid Herrmann if (ret)
183543e5e7c6SDavid Herrmann goto err_free;
183643e5e7c6SDavid Herrmann
1837192a1acfSDavid Herrmann hid_info(hdev, "New device registered\n");
1838192a1acfSDavid Herrmann
1839c57ff761SDavid Herrmann /* schedule device detection */
18404148b6bfSDavid Herrmann wiimote_schedule(wdata);
1841c57ff761SDavid Herrmann
1842192a1acfSDavid Herrmann return 0;
1843192a1acfSDavid Herrmann
1844192a1acfSDavid Herrmann err_free:
1845192a1acfSDavid Herrmann wiimote_destroy(wdata);
1846192a1acfSDavid Herrmann return ret;
1847192a1acfSDavid Herrmann
1848c7da0867SDavid Herrmann err_ext:
1849c7da0867SDavid Herrmann device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
1850c7da0867SDavid Herrmann err_close:
1851c7da0867SDavid Herrmann hid_hw_close(hdev);
1852192a1acfSDavid Herrmann err_stop:
1853192a1acfSDavid Herrmann hid_hw_stop(hdev);
1854192a1acfSDavid Herrmann err:
1855192a1acfSDavid Herrmann input_free_device(wdata->ir);
1856192a1acfSDavid Herrmann input_free_device(wdata->accel);
1857192a1acfSDavid Herrmann kfree(wdata);
1858192a1acfSDavid Herrmann return ret;
1859192a1acfSDavid Herrmann }
1860192a1acfSDavid Herrmann
wiimote_hid_remove(struct hid_device * hdev)1861192a1acfSDavid Herrmann static void wiimote_hid_remove(struct hid_device *hdev)
1862192a1acfSDavid Herrmann {
1863192a1acfSDavid Herrmann struct wiimote_data *wdata = hid_get_drvdata(hdev);
1864192a1acfSDavid Herrmann
1865192a1acfSDavid Herrmann hid_info(hdev, "Device removed\n");
1866192a1acfSDavid Herrmann wiimote_destroy(wdata);
1867192a1acfSDavid Herrmann }
1868192a1acfSDavid Herrmann
1869192a1acfSDavid Herrmann static const struct hid_device_id wiimote_hid_devices[] = {
1870192a1acfSDavid Herrmann { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
1871192a1acfSDavid Herrmann USB_DEVICE_ID_NINTENDO_WIIMOTE) },
1872a33042faSDavid Herrmann { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
1873a33042faSDavid Herrmann USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
1874192a1acfSDavid Herrmann { }
1875192a1acfSDavid Herrmann };
1876a8cbf80eSDaniel G. Morse
1877a8cbf80eSDaniel G. Morse bool wiimote_dpad_as_analog = false;
1878a8cbf80eSDaniel G. Morse module_param_named(dpad_as_analog, wiimote_dpad_as_analog, bool, 0644);
1879a8cbf80eSDaniel G. Morse MODULE_PARM_DESC(dpad_as_analog, "Use D-Pad as main analog input");
1880a8cbf80eSDaniel G. Morse
1881192a1acfSDavid Herrmann MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
1882192a1acfSDavid Herrmann
1883192a1acfSDavid Herrmann static struct hid_driver wiimote_hid_driver = {
1884192a1acfSDavid Herrmann .name = "wiimote",
1885192a1acfSDavid Herrmann .id_table = wiimote_hid_devices,
1886192a1acfSDavid Herrmann .probe = wiimote_hid_probe,
1887192a1acfSDavid Herrmann .remove = wiimote_hid_remove,
1888192a1acfSDavid Herrmann .raw_event = wiimote_hid_event,
1889192a1acfSDavid Herrmann };
1890f425458eSH Hartley Sweeten module_hid_driver(wiimote_hid_driver);
1891192a1acfSDavid Herrmann
1892192a1acfSDavid Herrmann MODULE_LICENSE("GPL");
1893192a1acfSDavid Herrmann MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
189492eda7e4SDavid Herrmann MODULE_DESCRIPTION("Driver for Nintendo Wii / Wii U peripherals");
1895