aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-wiimote-ext.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-wiimote-ext.c')
-rw-r--r--drivers/hid/hid-wiimote-ext.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
new file mode 100644
index 000000000000..fa9c67722aca
--- /dev/null
+++ b/drivers/hid/hid-wiimote-ext.c
@@ -0,0 +1,130 @@
1/*
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
36static void wiiext_worker(struct work_struct *work)
37{
38 struct wiimote_ext *ext = container_of(work, struct wiimote_ext,
39 worker);
40}
41
42/* schedule work only once, otherwise mark for reschedule */
43static void wiiext_schedule(struct wiimote_ext *ext)
44{
45 queue_work(system_nrt_wq, &ext->worker);
46}
47
48/*
49 * Reacts on extension port events
50 * Whenever the driver gets an event from the wiimote that an extension has been
51 * plugged or unplugged, this funtion shall be called. It checks what extensions
52 * are connected and initializes and activates them.
53 * This can be called in atomic context. The initialization is done in a
54 * separate worker thread. The state.lock spinlock must be held by the caller.
55 */
56void wiiext_event(struct wiimote_data *wdata, bool plugged)
57{
58 if (!wdata->ext)
59 return;
60
61 if (wdata->ext->plugged == plugged)
62 return;
63
64 wdata->ext->plugged = plugged;
65 /*
66 * We need to call wiiext_schedule(wdata->ext) here, however, the
67 * extension initialization logic is not fully understood and so
68 * automatic initialization is not supported, yet.
69 */
70}
71
72/*
73 * Returns true if the current DRM mode should contain extension data and false
74 * if there is no interest in extension data.
75 * All supported extensions send 6 byte extension data so any DRM that contains
76 * extension bytes is fine.
77 * The caller must hold the state.lock spinlock.
78 */
79bool wiiext_active(struct wiimote_data *wdata)
80{
81 if (!wdata->ext)
82 return false;
83
84 return wdata->ext->motionp || wdata->ext->ext_type;
85}
86
87/* Initializes the extension driver of a wiimote */
88int wiiext_init(struct wiimote_data *wdata)
89{
90 struct wiimote_ext *ext;
91 unsigned long flags;
92
93 ext = kzalloc(sizeof(*ext), GFP_KERNEL);
94 if (!ext)
95 return -ENOMEM;
96
97 ext->wdata = wdata;
98 INIT_WORK(&ext->worker, wiiext_worker);
99
100 spin_lock_irqsave(&wdata->state.lock, flags);
101 wdata->ext = ext;
102 spin_unlock_irqrestore(&wdata->state.lock, flags);
103
104 return 0;
105}
106
107/* Deinitializes the extension driver of a wiimote */
108void wiiext_deinit(struct wiimote_data *wdata)
109{
110 struct wiimote_ext *ext = wdata->ext;
111 unsigned long flags;
112
113 if (!ext)
114 return;
115
116 /*
117 * We first unset wdata->ext to avoid further input from the wiimote
118 * core. The worker thread does not access this pointer so it is not
119 * affected by this.
120 * We kill the worker after this so it does not get respawned during
121 * deinitialization.
122 */
123
124 spin_lock_irqsave(&wdata->state.lock, flags);
125 wdata->ext = NULL;
126 spin_unlock_irqrestore(&wdata->state.lock, flags);
127
128 cancel_work_sync(&ext->worker);
129 kfree(ext);
130}