Apparently, the HP rx5670 fails to reboot if a USB keyboard if attached
without this patch (and the OHCI fix we accepted for 2.4.29).

This bug is better known for its effect on Altix, but SGI ships a magic
kernel anyhow, so I don't want to use that as justification.

The original patch comes from Jes Sorensen, but then I corrupted his
idea with my simplifications and a partial backport from 2.6, so if this
fails do not e-mail him.

diff -urpN -X dontdiff linux-2.4.30-pre1/drivers/usb/hid-core.c 
linux-2.4.30-pre1-usb/drivers/usb/hid-core.c
--- linux-2.4.30-pre1/drivers/usb/hid-core.c    2004-11-22 23:04:18.000000000 
-0800
+++ linux-2.4.30-pre1-usb/drivers/usb/hid-core.c        2005-02-10 
11:18:08.000000000 -0800
@@ -1064,18 +1064,31 @@ static int hid_submit_out(struct hid_dev
 static void hid_ctrl(struct urb *urb)
 {
        struct hid_device *hid = urb->context;
+       unsigned long flags;
 
        if (urb->status)
                warn("ctrl urb status %d received", urb->status);
 
+       spin_lock_irqsave(&hid->outlock, flags);
+
        hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
-       if (hid->outhead != hid->outtail)
-               hid_submit_out(hid);
+       if (hid->outhead != hid->outtail) {
+               if (hid_submit_out(hid)) {
+                       clear_bit(HID_OUT_RUNNING, &hid->iofl);
+               }
+               spin_unlock_irqrestore(&hid->outlock, flags);
+               return;
+       }
+
+       clear_bit(HID_OUT_RUNNING, &hid->iofl);
+       spin_unlock_irqrestore(&hid->outlock, flags);
 }
 
 void hid_write_report(struct hid_device *hid, struct hid_report *report)
 {
+       unsigned long flags;
+
        if (hid->report_enum[report->type].numbered) {
                hid->out[hid->outhead].buffer[0] = report->id;
                hid_output_report(report, hid->out[hid->outhead].buffer + 1);
@@ -1087,13 +1100,18 @@ void hid_write_report(struct hid_device 
 
        hid->out[hid->outhead].dr.wValue = cpu_to_le16(((report->type + 1) << 
8) | report->id);
 
+       spin_lock_irqsave(&hid->outlock, flags);
+
        hid->outhead = (hid->outhead + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
        if (hid->outhead == hid->outtail)
                hid->outtail = (hid->outtail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
-       if (hid->urbout.status != -EINPROGRESS)
-               hid_submit_out(hid);
+       if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl))
+               if (hid_submit_out(hid))
+                       clear_bit(HID_OUT_RUNNING, &hid->iofl);
+
+       spin_unlock_irqrestore(&hid->outlock, flags);
 }
 
 int hid_open(struct hid_device *hid)
@@ -1333,6 +1351,8 @@ static struct hid_device *usb_hid_config
                return NULL;
        }
 
+       spin_lock_init(&hid->outlock);
+
        hid->version = hdesc->bcdHID;
        hid->country = hdesc->bCountryCode;
        hid->dev = dev;
diff -urpN -X dontdiff linux-2.4.30-pre1/drivers/usb/hid.h 
linux-2.4.30-pre1-usb/drivers/usb/hid.h
--- linux-2.4.30-pre1/drivers/usb/hid.h 2005-01-26 13:04:23.000000000 -0800
+++ linux-2.4.30-pre1-usb/drivers/usb/hid.h     2005-02-10 11:26:43.000000000 
-0800
@@ -302,6 +302,8 @@ struct hid_control_fifo {
 #define HID_CLAIMED_INPUT      1
 #define HID_CLAIMED_HIDDEV     2
 
+#define HID_OUT_RUNNING                2
+
 struct hid_input {
        struct list_head list;
        struct hid_report *report;
@@ -322,12 +324,15 @@ struct hid_device {                                       
                /* device repo
        struct usb_device *dev;                                         /* USB 
device */
        int ifnum;                                                      /* USB 
interface number */
 
+       unsigned long iofl;                                             /* I/O 
flags (CTRL_RUNNING, OUT_RUNNING) */
+
        struct urb urb;                                                 /* USB 
URB structure */
        char buffer[HID_BUFFER_SIZE];                                   /* Rx 
buffer */
 
        struct urb urbout;                                              /* 
Output URB */
        struct hid_control_fifo out[HID_CONTROL_FIFO_SIZE];             /* 
Transmit buffer */
        unsigned char outhead, outtail;                                 /* Tx 
buffer head & tail */
+       spinlock_t outlock;                                             /* 
Output fifo spinlock */
 
        unsigned claimed;                                               /* 
Claimed by hidinput, hiddev? */      
        unsigned quirks;                                                /* 
Various quirks the device can pull on us */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to