aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/hid-wiimote-core.c24
-rw-r--r--drivers/hid/hid-wiimote.h19
2 files changed, 38 insertions, 5 deletions
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
index 00a9b6fa5189..a025d2104d3c 100644
--- a/drivers/hid/hid-wiimote-core.c
+++ b/drivers/hid/hid-wiimote-core.c
@@ -58,11 +58,11 @@ static enum power_supply_property wiimote_battery_props[] = {
58 58
59/* output queue handling */ 59/* output queue handling */
60 60
61static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, 61static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
62 size_t count) 62 size_t count)
63{ 63{
64 __u8 *buf; 64 __u8 *buf;
65 ssize_t ret; 65 int ret;
66 66
67 if (!hdev->hid_output_raw_report) 67 if (!hdev->hid_output_raw_report)
68 return -ENODEV; 68 return -ENODEV;
@@ -84,14 +84,20 @@ static void wiimote_queue_worker(struct work_struct *work)
84 struct wiimote_data *wdata = container_of(queue, struct wiimote_data, 84 struct wiimote_data *wdata = container_of(queue, struct wiimote_data,
85 queue); 85 queue);
86 unsigned long flags; 86 unsigned long flags;
87 int ret;
87 88
88 spin_lock_irqsave(&wdata->queue.lock, flags); 89 spin_lock_irqsave(&wdata->queue.lock, flags);
89 90
90 while (wdata->queue.head != wdata->queue.tail) { 91 while (wdata->queue.head != wdata->queue.tail) {
91 spin_unlock_irqrestore(&wdata->queue.lock, flags); 92 spin_unlock_irqrestore(&wdata->queue.lock, flags);
92 wiimote_hid_send(wdata->hdev, 93 ret = wiimote_hid_send(wdata->hdev,
93 wdata->queue.outq[wdata->queue.tail].data, 94 wdata->queue.outq[wdata->queue.tail].data,
94 wdata->queue.outq[wdata->queue.tail].size); 95 wdata->queue.outq[wdata->queue.tail].size);
96 if (ret < 0) {
97 spin_lock_irqsave(&wdata->state.lock, flags);
98 wiimote_cmd_abort(wdata);
99 spin_unlock_irqrestore(&wdata->state.lock, flags);
100 }
95 spin_lock_irqsave(&wdata->queue.lock, flags); 101 spin_lock_irqsave(&wdata->queue.lock, flags);
96 102
97 wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE; 103 wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE;
@@ -108,7 +114,9 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
108 114
109 if (count > HID_MAX_BUFFER_SIZE) { 115 if (count > HID_MAX_BUFFER_SIZE) {
110 hid_warn(wdata->hdev, "Sending too large output report\n"); 116 hid_warn(wdata->hdev, "Sending too large output report\n");
111 return; 117
118 spin_lock_irqsave(&wdata->queue.lock, flags);
119 goto out_error;
112 } 120 }
113 121
114 /* 122 /*
@@ -134,8 +142,14 @@ static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
134 wdata->queue.head = newhead; 142 wdata->queue.head = newhead;
135 } else { 143 } else {
136 hid_warn(wdata->hdev, "Output queue is full"); 144 hid_warn(wdata->hdev, "Output queue is full");
145 goto out_error;
137 } 146 }
138 147
148 goto out_unlock;
149
150out_error:
151 wiimote_cmd_abort(wdata);
152out_unlock:
139 spin_unlock_irqrestore(&wdata->queue.lock, flags); 153 spin_unlock_irqrestore(&wdata->queue.lock, flags);
140} 154}
141 155
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 301607da7715..34417021606e 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -195,6 +195,16 @@ static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
195 complete(&wdata->state.ready); 195 complete(&wdata->state.ready);
196} 196}
197 197
198/* requires the state.lock spinlock to be held */
199static inline void wiimote_cmd_abort(struct wiimote_data *wdata)
200{
201 /* Abort synchronous request by waking up the sleeping caller. But
202 * reset the state.cmd field to an invalid value so no further event
203 * handlers will work with it. */
204 wdata->state.cmd = WIIPROTO_REQ_MAX;
205 complete(&wdata->state.ready);
206}
207
198static inline int wiimote_cmd_acquire(struct wiimote_data *wdata) 208static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
199{ 209{
200 return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0; 210 return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
@@ -223,11 +233,17 @@ static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
223{ 233{
224 int ret; 234 int ret;
225 235
236 /* The completion acts as implicit memory barrier so we can safely
237 * assume that state.cmd is set on success/failure and isn't accessed
238 * by any other thread, anymore. */
239
226 ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ); 240 ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
227 if (ret < 0) 241 if (ret < 0)
228 return -ERESTARTSYS; 242 return -ERESTARTSYS;
229 else if (ret == 0) 243 else if (ret == 0)
230 return -EIO; 244 return -EIO;
245 else if (wdata->state.cmd != WIIPROTO_REQ_NULL)
246 return -EIO;
231 else 247 else
232 return 0; 248 return 0;
233} 249}
@@ -236,9 +252,12 @@ static inline int wiimote_cmd_wait_noint(struct wiimote_data *wdata)
236{ 252{
237 unsigned long ret; 253 unsigned long ret;
238 254
255 /* no locking needed; see wiimote_cmd_wait() */
239 ret = wait_for_completion_timeout(&wdata->state.ready, HZ); 256 ret = wait_for_completion_timeout(&wdata->state.ready, HZ);
240 if (!ret) 257 if (!ret)
241 return -EIO; 258 return -EIO;
259 else if (wdata->state.cmd != WIIPROTO_REQ_NULL)
260 return -EIO;
242 else 261 else
243 return 0; 262 return 0;
244} 263}