blob: 477513dfeb7a3064d0c2f6cb91d166f23bc8038d [file] [log] [blame]
David Herrmanncb992212011-11-17 14:12:01 +01001/*
2 * HID driver for Nintendo Wiimote extension devices
3 * Copyright (c) 2011 David Herrmann
4 */
5
6/*
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 */
12
13#include <linux/atomic.h>
14#include <linux/module.h>
15#include <linux/spinlock.h>
16#include <linux/workqueue.h>
17#include "hid-wiimote.h"
18
19struct wiimote_ext {
20 struct wiimote_data *wdata;
21 struct work_struct worker;
22
23 atomic_t opened;
24 atomic_t mp_opened;
25 bool plugged;
26 bool motionp;
27 __u8 ext_type;
28};
29
30enum wiiext_type {
31 WIIEXT_NONE, /* placeholder */
32 WIIEXT_CLASSIC, /* Nintendo classic controller */
33 WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */
34};
35
David Herrmann82fb1b32011-11-17 14:12:02 +010036/* diable all extensions */
37static void ext_disable(struct wiimote_ext *ext)
38{
39 unsigned long flags;
David Herrmann492ba952011-11-17 14:12:03 +010040 __u8 wmem = 0x55;
41
42 if (!wiimote_cmd_acquire(ext->wdata)) {
43 wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
44 wiimote_cmd_release(ext->wdata);
45 }
David Herrmann82fb1b32011-11-17 14:12:02 +010046
47 spin_lock_irqsave(&ext->wdata->state.lock, flags);
48 ext->motionp = false;
49 ext->ext_type = WIIEXT_NONE;
David Herrmann492ba952011-11-17 14:12:03 +010050 wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
David Herrmann82fb1b32011-11-17 14:12:02 +010051 spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
52}
53
54static bool motionp_read(struct wiimote_ext *ext)
55{
David Herrmann492ba952011-11-17 14:12:03 +010056 __u8 rmem[2], wmem;
57 ssize_t ret;
58 bool avail = false;
59
60 if (wiimote_cmd_acquire(ext->wdata))
61 return false;
62
63 /* initialize motion plus */
64 wmem = 0x55;
65 ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem));
66 if (ret)
67 goto error;
68
69 /* read motion plus ID */
70 ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2);
71 if (ret == 2 || rmem[1] == 0x5)
72 avail = true;
73
74error:
75 wiimote_cmd_release(ext->wdata);
76 return avail;
David Herrmann82fb1b32011-11-17 14:12:02 +010077}
78
79static __u8 ext_read(struct wiimote_ext *ext)
80{
David Herrmann492ba952011-11-17 14:12:03 +010081 ssize_t ret;
82 __u8 rmem[2], wmem;
83 __u8 type = WIIEXT_NONE;
84
85 if (!ext->plugged)
86 return WIIEXT_NONE;
87
88 if (wiimote_cmd_acquire(ext->wdata))
89 return WIIEXT_NONE;
90
91 /* initialize extension */
92 wmem = 0x55;
93 ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
94 if (!ret) {
95 /* disable encryption */
96 wmem = 0x0;
97 wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem));
98 }
99
100 /* read extension ID */
101 ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2);
102 if (ret == 2) {
103 if (rmem[0] == 0 && rmem[1] == 0)
104 type = WIIEXT_NUNCHUCK;
105 else if (rmem[0] == 0x01 && rmem[1] == 0x01)
106 type = WIIEXT_CLASSIC;
107 }
108
109 wiimote_cmd_release(ext->wdata);
110
111 return type;
David Herrmann82fb1b32011-11-17 14:12:02 +0100112}
113
114static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type)
115{
116 unsigned long flags;
David Herrmann492ba952011-11-17 14:12:03 +0100117 __u8 wmem;
118 int ret;
119
120 if (motionp) {
121 if (wiimote_cmd_acquire(ext->wdata))
122 return;
123
124 if (ext_type == WIIEXT_CLASSIC)
125 wmem = 0x07;
126 else if (ext_type == WIIEXT_NUNCHUCK)
127 wmem = 0x05;
128 else
129 wmem = 0x04;
130
131 ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem));
132 wiimote_cmd_release(ext->wdata);
133 if (ret)
134 return;
135 }
David Herrmann82fb1b32011-11-17 14:12:02 +0100136
137 spin_lock_irqsave(&ext->wdata->state.lock, flags);
138 ext->motionp = motionp;
139 ext->ext_type = ext_type;
David Herrmann492ba952011-11-17 14:12:03 +0100140 wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
David Herrmann82fb1b32011-11-17 14:12:02 +0100141 spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
142}
143
David Herrmanncb992212011-11-17 14:12:01 +0100144static void wiiext_worker(struct work_struct *work)
145{
146 struct wiimote_ext *ext = container_of(work, struct wiimote_ext,
147 worker);
David Herrmann82fb1b32011-11-17 14:12:02 +0100148 bool motionp;
149 __u8 ext_type;
150
151 ext_disable(ext);
152 motionp = motionp_read(ext);
153 ext_type = ext_read(ext);
154 ext_enable(ext, motionp, ext_type);
David Herrmanncb992212011-11-17 14:12:01 +0100155}
156
157/* schedule work only once, otherwise mark for reschedule */
158static void wiiext_schedule(struct wiimote_ext *ext)
159{
160 queue_work(system_nrt_wq, &ext->worker);
161}
162
163/*
164 * Reacts on extension port events
165 * Whenever the driver gets an event from the wiimote that an extension has been
166 * plugged or unplugged, this funtion shall be called. It checks what extensions
167 * are connected and initializes and activates them.
168 * This can be called in atomic context. The initialization is done in a
169 * separate worker thread. The state.lock spinlock must be held by the caller.
170 */
171void wiiext_event(struct wiimote_data *wdata, bool plugged)
172{
173 if (!wdata->ext)
174 return;
175
176 if (wdata->ext->plugged == plugged)
177 return;
178
179 wdata->ext->plugged = plugged;
180 /*
181 * We need to call wiiext_schedule(wdata->ext) here, however, the
182 * extension initialization logic is not fully understood and so
183 * automatic initialization is not supported, yet.
184 */
185}
186
187/*
188 * Returns true if the current DRM mode should contain extension data and false
189 * if there is no interest in extension data.
190 * All supported extensions send 6 byte extension data so any DRM that contains
191 * extension bytes is fine.
192 * The caller must hold the state.lock spinlock.
193 */
194bool wiiext_active(struct wiimote_data *wdata)
195{
196 if (!wdata->ext)
197 return false;
198
199 return wdata->ext->motionp || wdata->ext->ext_type;
200}
201
David Herrmannc1e51392011-11-17 14:12:04 +0100202static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
203 char *buf)
204{
205 struct wiimote_data *wdata = dev_to_wii(dev);
206 __u8 type = WIIEXT_NONE;
207 bool motionp = false;
208 unsigned long flags;
209
210 spin_lock_irqsave(&wdata->state.lock, flags);
211 if (wdata->ext) {
212 motionp = wdata->ext->motionp;
213 type = wdata->ext->ext_type;
214 }
215 spin_unlock_irqrestore(&wdata->state.lock, flags);
216
217 if (type == WIIEXT_NUNCHUCK) {
218 if (motionp)
219 return sprintf(buf, "motionp+nunchuck\n");
220 else
221 return sprintf(buf, "nunchuck\n");
222 } else if (type == WIIEXT_CLASSIC) {
223 if (motionp)
224 return sprintf(buf, "motionp+classic\n");
225 else
226 return sprintf(buf, "classic\n");
227 } else {
228 if (motionp)
229 return sprintf(buf, "motionp\n");
230 else
231 return sprintf(buf, "none\n");
232 }
233}
234
235static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL);
236
David Herrmanncb992212011-11-17 14:12:01 +0100237/* Initializes the extension driver of a wiimote */
238int wiiext_init(struct wiimote_data *wdata)
239{
240 struct wiimote_ext *ext;
241 unsigned long flags;
David Herrmannc1e51392011-11-17 14:12:04 +0100242 int ret;
David Herrmanncb992212011-11-17 14:12:01 +0100243
244 ext = kzalloc(sizeof(*ext), GFP_KERNEL);
245 if (!ext)
246 return -ENOMEM;
247
248 ext->wdata = wdata;
249 INIT_WORK(&ext->worker, wiiext_worker);
250
David Herrmannc1e51392011-11-17 14:12:04 +0100251 ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension);
252 if (ret)
253 goto err;
254
David Herrmanncb992212011-11-17 14:12:01 +0100255 spin_lock_irqsave(&wdata->state.lock, flags);
256 wdata->ext = ext;
257 spin_unlock_irqrestore(&wdata->state.lock, flags);
258
259 return 0;
David Herrmannc1e51392011-11-17 14:12:04 +0100260
261err:
262 kfree(ext);
263 return ret;
David Herrmanncb992212011-11-17 14:12:01 +0100264}
265
266/* Deinitializes the extension driver of a wiimote */
267void wiiext_deinit(struct wiimote_data *wdata)
268{
269 struct wiimote_ext *ext = wdata->ext;
270 unsigned long flags;
271
272 if (!ext)
273 return;
274
275 /*
276 * We first unset wdata->ext to avoid further input from the wiimote
277 * core. The worker thread does not access this pointer so it is not
278 * affected by this.
279 * We kill the worker after this so it does not get respawned during
280 * deinitialization.
281 */
282
283 spin_lock_irqsave(&wdata->state.lock, flags);
284 wdata->ext = NULL;
285 spin_unlock_irqrestore(&wdata->state.lock, flags);
286
David Herrmannc1e51392011-11-17 14:12:04 +0100287 device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
288
David Herrmanncb992212011-11-17 14:12:01 +0100289 cancel_work_sync(&ext->worker);
290 kfree(ext);
291}