blob: 233bdfe3205b6148ed582ddce5a6bd4448cd4bac [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
202/* Initializes the extension driver of a wiimote */
203int wiiext_init(struct wiimote_data *wdata)
204{
205 struct wiimote_ext *ext;
206 unsigned long flags;
207
208 ext = kzalloc(sizeof(*ext), GFP_KERNEL);
209 if (!ext)
210 return -ENOMEM;
211
212 ext->wdata = wdata;
213 INIT_WORK(&ext->worker, wiiext_worker);
214
215 spin_lock_irqsave(&wdata->state.lock, flags);
216 wdata->ext = ext;
217 spin_unlock_irqrestore(&wdata->state.lock, flags);
218
219 return 0;
220}
221
222/* Deinitializes the extension driver of a wiimote */
223void wiiext_deinit(struct wiimote_data *wdata)
224{
225 struct wiimote_ext *ext = wdata->ext;
226 unsigned long flags;
227
228 if (!ext)
229 return;
230
231 /*
232 * We first unset wdata->ext to avoid further input from the wiimote
233 * core. The worker thread does not access this pointer so it is not
234 * affected by this.
235 * We kill the worker after this so it does not get respawned during
236 * deinitialization.
237 */
238
239 spin_lock_irqsave(&wdata->state.lock, flags);
240 wdata->ext = NULL;
241 spin_unlock_irqrestore(&wdata->state.lock, flags);
242
243 cancel_work_sync(&ext->worker);
244 kfree(ext);
245}