/* output queue handling */
 
-static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
-                                                               size_t count)
+static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
+                           size_t count)
 {
        __u8 *buf;
-       ssize_t ret;
+       int ret;
 
        if (!hdev->hid_output_raw_report)
                return -ENODEV;
        struct wiimote_data *wdata = container_of(queue, struct wiimote_data,
                                                  queue);
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&wdata->queue.lock, flags);
 
        while (wdata->queue.head != wdata->queue.tail) {
                spin_unlock_irqrestore(&wdata->queue.lock, flags);
-               wiimote_hid_send(wdata->hdev,
+               ret = wiimote_hid_send(wdata->hdev,
                                 wdata->queue.outq[wdata->queue.tail].data,
                                 wdata->queue.outq[wdata->queue.tail].size);
+               if (ret < 0) {
+                       spin_lock_irqsave(&wdata->state.lock, flags);
+                       wiimote_cmd_abort(wdata);
+                       spin_unlock_irqrestore(&wdata->state.lock, flags);
+               }
                spin_lock_irqsave(&wdata->queue.lock, flags);
 
                wdata->queue.tail = (wdata->queue.tail + 1) % WIIMOTE_BUFSIZE;
 
        if (count > HID_MAX_BUFFER_SIZE) {
                hid_warn(wdata->hdev, "Sending too large output report\n");
-               return;
+
+               spin_lock_irqsave(&wdata->queue.lock, flags);
+               goto out_error;
        }
 
        /*
                wdata->queue.head = newhead;
        } else {
                hid_warn(wdata->hdev, "Output queue is full");
+               goto out_error;
        }
 
+       goto out_unlock;
+
+out_error:
+       wiimote_cmd_abort(wdata);
+out_unlock:
        spin_unlock_irqrestore(&wdata->queue.lock, flags);
 }
 
 
        complete(&wdata->state.ready);
 }
 
+/* requires the state.lock spinlock to be held */
+static inline void wiimote_cmd_abort(struct wiimote_data *wdata)
+{
+       /* Abort synchronous request by waking up the sleeping caller. But
+        * reset the state.cmd field to an invalid value so no further event
+        * handlers will work with it. */
+       wdata->state.cmd = WIIPROTO_REQ_MAX;
+       complete(&wdata->state.ready);
+}
+
 static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
 {
        return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
 {
        int ret;
 
+       /* The completion acts as implicit memory barrier so we can safely
+        * assume that state.cmd is set on success/failure and isn't accessed
+        * by any other thread, anymore. */
+
        ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
        if (ret < 0)
                return -ERESTARTSYS;
        else if (ret == 0)
                return -EIO;
+       else if (wdata->state.cmd != WIIPROTO_REQ_NULL)
+               return -EIO;
        else
                return 0;
 }
 {
        unsigned long ret;
 
+       /* no locking needed; see wiimote_cmd_wait() */
        ret = wait_for_completion_timeout(&wdata->state.ready, HZ);
        if (!ret)
                return -EIO;
+       else if (wdata->state.cmd != WIIPROTO_REQ_NULL)
+               return -EIO;
        else
                return 0;
 }