aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-wiimote.c
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@googlemail.com>2011-07-05 07:45:14 -0400
committerJiri Kosina <jkosina@suse.cz>2011-07-11 08:30:23 -0400
commit23c063cb02b69244bbc215cb81c2cad0208fbecf (patch)
tree0fdf0243e91640dc3e072790a16f61f351d7e3ad /drivers/hid/hid-wiimote.c
parent0c218f14487fd67e60059458c48b43cc3d36b96e (diff)
HID: wiimote: Add output queue for wiimote driver
The raw hid output function that is supported by bluetooth low-level hid driver does not provide an output queue and also may sleep. The wiimote driver, though, may need to send data in atomic context so this patch adds a buffered output queue for the wiimote driver. We use the shared workqueue to send our buffer to the hid device. There is always only one active worker which flushes the whole output queue to the device. If our queue is full, every further output is discarded. Special care is needed in the deinitialization routine. When wiimote_hid_remove is called, HID input is already disabled, but HID output may still be used from our worker and is then discarded by the lower HID layers. Therefore, we can safely disable the input layer since it is the only layer that still sends input events. Future sysfs attributes must be freed before unregistering input to avoid the sysfs handlers to send input events to a non-existing input layer. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-wiimote.c')
-rw-r--r--drivers/hid/hid-wiimote.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c
index 811ed8921013..bfc50493ec6b 100644
--- a/drivers/hid/hid-wiimote.c
+++ b/drivers/hid/hid-wiimote.c
@@ -15,15 +15,28 @@
15#include <linux/hid.h> 15#include <linux/hid.h>
16#include <linux/input.h> 16#include <linux/input.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/spinlock.h>
18#include "hid-ids.h" 19#include "hid-ids.h"
19 20
20#define WIIMOTE_VERSION "0.1" 21#define WIIMOTE_VERSION "0.1"
21#define WIIMOTE_NAME "Nintendo Wii Remote" 22#define WIIMOTE_NAME "Nintendo Wii Remote"
23#define WIIMOTE_BUFSIZE 32
24
25struct wiimote_buf {
26 __u8 data[HID_MAX_BUFFER_SIZE];
27 size_t size;
28};
22 29
23struct wiimote_data { 30struct wiimote_data {
24 atomic_t ready; 31 atomic_t ready;
25 struct hid_device *hdev; 32 struct hid_device *hdev;
26 struct input_dev *input; 33 struct input_dev *input;
34
35 spinlock_t qlock;
36 __u8 head;
37 __u8 tail;
38 struct wiimote_buf outq[WIIMOTE_BUFSIZE];
39 struct work_struct worker;
27}; 40};
28 41
29static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, 42static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
@@ -45,6 +58,65 @@ static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
45 return ret; 58 return ret;
46} 59}
47 60
61static void wiimote_worker(struct work_struct *work)
62{
63 struct wiimote_data *wdata = container_of(work, struct wiimote_data,
64 worker);
65 unsigned long flags;
66
67 spin_lock_irqsave(&wdata->qlock, flags);
68
69 while (wdata->head != wdata->tail) {
70 spin_unlock_irqrestore(&wdata->qlock, flags);
71 wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data,
72 wdata->outq[wdata->tail].size);
73 spin_lock_irqsave(&wdata->qlock, flags);
74
75 wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE;
76 }
77
78 spin_unlock_irqrestore(&wdata->qlock, flags);
79}
80
81static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
82 size_t count)
83{
84 unsigned long flags;
85 __u8 newhead;
86
87 if (count > HID_MAX_BUFFER_SIZE) {
88 hid_warn(wdata->hdev, "Sending too large output report\n");
89 return;
90 }
91
92 /*
93 * Copy new request into our output queue and check whether the
94 * queue is full. If it is full, discard this request.
95 * If it is empty we need to start a new worker that will
96 * send out the buffer to the hid device.
97 * If the queue is not empty, then there must be a worker
98 * that is currently sending out our buffer and this worker
99 * will reschedule itself until the queue is empty.
100 */
101
102 spin_lock_irqsave(&wdata->qlock, flags);
103
104 memcpy(wdata->outq[wdata->head].data, buffer, count);
105 wdata->outq[wdata->head].size = count;
106 newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE;
107
108 if (wdata->head == wdata->tail) {
109 wdata->head = newhead;
110 schedule_work(&wdata->worker);
111 } else if (newhead != wdata->tail) {
112 wdata->head = newhead;
113 } else {
114 hid_warn(wdata->hdev, "Output queue is full");
115 }
116
117 spin_unlock_irqrestore(&wdata->qlock, flags);
118}
119
48static int wiimote_input_event(struct input_dev *dev, unsigned int type, 120static int wiimote_input_event(struct input_dev *dev, unsigned int type,
49 unsigned int code, int value) 121 unsigned int code, int value)
50{ 122{
@@ -100,6 +172,9 @@ static struct wiimote_data *wiimote_create(struct hid_device *hdev)
100 wdata->input->id.version = wdata->hdev->version; 172 wdata->input->id.version = wdata->hdev->version;
101 wdata->input->name = WIIMOTE_NAME; 173 wdata->input->name = WIIMOTE_NAME;
102 174
175 spin_lock_init(&wdata->qlock);
176 INIT_WORK(&wdata->worker, wiimote_worker);
177
103 return wdata; 178 return wdata;
104} 179}
105 180
@@ -157,8 +232,11 @@ static void wiimote_hid_remove(struct hid_device *hdev)
157 struct wiimote_data *wdata = hid_get_drvdata(hdev); 232 struct wiimote_data *wdata = hid_get_drvdata(hdev);
158 233
159 hid_info(hdev, "Device removed\n"); 234 hid_info(hdev, "Device removed\n");
235
160 hid_hw_stop(hdev); 236 hid_hw_stop(hdev);
161 input_unregister_device(wdata->input); 237 input_unregister_device(wdata->input);
238
239 cancel_work_sync(&wdata->worker);
162 wiimote_destroy(wdata); 240 wiimote_destroy(wdata);
163} 241}
164 242