Commit f78ed24b authored by Vojtech Pavlik's avatar Vojtech Pavlik

input: Make sure the HID request queue survives report transfer failures gracefully.

Signed-off-by: default avatarVojtech Pavlik <vojtech@suse.cz>
Problem-spotted-by: default avatarAlan Stern <stern@rowland.harvard.edu>
parent 73df7566
...@@ -219,17 +219,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -219,17 +219,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
return -1; return -1;
} }
usages = parser->local.usage_index;
if (!(usages = max_t(int, parser->local.usage_index, parser->global.report_count)))
return 0; /* Ignore padding fields */
offset = report->size; offset = report->size;
report->size += parser->global.report_size * parser->global.report_count; report->size += parser->global.report_size * parser->global.report_count;
if (usages < parser->global.report_count)
usages = parser->global.report_count;
if (usages == 0)
return 0; /* ignore padding fields */
if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL) if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
return 0; return 0;
...@@ -923,20 +919,20 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs) ...@@ -923,20 +919,20 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
int status; int status;
switch (urb->status) { switch (urb->status) {
case 0: /* success */ case 0: /* success */
hid_input_report(HID_INPUT_REPORT, urb, regs); hid_input_report(HID_INPUT_REPORT, urb, regs);
break; break;
case -ECONNRESET: /* unlink */ case -ECONNRESET: /* unlink */
case -ENOENT: case -ENOENT:
case -ESHUTDOWN: case -ESHUTDOWN:
return; return;
default: /* error */ default: /* error */
dbg("nonzero status in input irq %d", urb->status); warn("input irq status %d received", urb->status);
} }
status = usb_submit_urb (urb, SLAB_ATOMIC); status = usb_submit_urb(urb, SLAB_ATOMIC);
if (status) if (status)
err ("can't resubmit intr, %s-%s/input%d, status %d", err("can't resubmit intr, %s-%s/input%d, status %d",
hid->dev->bus->bus_name, hid->dev->devpath, hid->dev->bus->bus_name, hid->dev->devpath,
hid->ifnum, status); hid->ifnum, status);
} }
...@@ -1137,23 +1133,31 @@ static void hid_irq_out(struct urb *urb, struct pt_regs *regs) ...@@ -1137,23 +1133,31 @@ static void hid_irq_out(struct urb *urb, struct pt_regs *regs)
struct hid_device *hid = urb->context; struct hid_device *hid = urb->context;
unsigned long flags; unsigned long flags;
if (urb->status) switch (urb->status) {
warn("output irq status %d received", urb->status); case 0: /* success */
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
break;
default: /* error */
warn("output irq status %d received", urb->status);
}
spin_lock_irqsave(&hid->outlock, flags); spin_lock_irqsave(&hid->outlock, flags);
hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
if (hid->outhead != hid->outtail) { if (hid->outhead != hid->outtail) {
hid_submit_out(hid); if (hid_submit_out(hid)) {
clear_bit(HID_OUT_RUNNING, &hid->iofl);;
wake_up(&hid->wait);
}
spin_unlock_irqrestore(&hid->outlock, flags); spin_unlock_irqrestore(&hid->outlock, flags);
return; return;
} }
clear_bit(HID_OUT_RUNNING, &hid->iofl); clear_bit(HID_OUT_RUNNING, &hid->iofl);
spin_unlock_irqrestore(&hid->outlock, flags); spin_unlock_irqrestore(&hid->outlock, flags);
wake_up(&hid->wait); wake_up(&hid->wait);
} }
...@@ -1166,26 +1170,34 @@ static void hid_ctrl(struct urb *urb, struct pt_regs *regs) ...@@ -1166,26 +1170,34 @@ static void hid_ctrl(struct urb *urb, struct pt_regs *regs)
struct hid_device *hid = urb->context; struct hid_device *hid = urb->context;
unsigned long flags; unsigned long flags;
if (urb->status)
warn("ctrl urb status %d received", urb->status);
spin_lock_irqsave(&hid->ctrllock, flags); spin_lock_irqsave(&hid->ctrllock, flags);
if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN) switch (urb->status) {
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs); case 0: /* success */
if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs);
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
case -EPIPE: /* report not available */
break;
default: /* error */
warn("ctrl urb status %d received", urb->status);
}
hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
if (hid->ctrlhead != hid->ctrltail) { if (hid->ctrlhead != hid->ctrltail) {
hid_submit_ctrl(hid); if (hid_submit_ctrl(hid)) {
clear_bit(HID_CTRL_RUNNING, &hid->iofl);
wake_up(&hid->wait);
}
spin_unlock_irqrestore(&hid->ctrllock, flags); spin_unlock_irqrestore(&hid->ctrllock, flags);
return; return;
} }
clear_bit(HID_CTRL_RUNNING, &hid->iofl); clear_bit(HID_CTRL_RUNNING, &hid->iofl);
spin_unlock_irqrestore(&hid->ctrllock, flags); spin_unlock_irqrestore(&hid->ctrllock, flags);
wake_up(&hid->wait); wake_up(&hid->wait);
} }
...@@ -1211,7 +1223,8 @@ void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsign ...@@ -1211,7 +1223,8 @@ void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsign
hid->outhead = head; hid->outhead = head;
if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl)) if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl))
hid_submit_out(hid); if (hid_submit_out(hid))
clear_bit(HID_OUT_RUNNING, &hid->iofl);
spin_unlock_irqrestore(&hid->outlock, flags); spin_unlock_irqrestore(&hid->outlock, flags);
return; return;
...@@ -1230,7 +1243,8 @@ void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsign ...@@ -1230,7 +1243,8 @@ void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsign
hid->ctrlhead = head; hid->ctrlhead = head;
if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl)) if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl))
hid_submit_ctrl(hid); if (hid_submit_ctrl(hid))
clear_bit(HID_CTRL_RUNNING, &hid->iofl);
spin_unlock_irqrestore(&hid->ctrllock, flags); spin_unlock_irqrestore(&hid->ctrllock, flags);
} }
...@@ -1282,7 +1296,7 @@ int hid_open(struct hid_device *hid) ...@@ -1282,7 +1296,7 @@ int hid_open(struct hid_device *hid)
void hid_close(struct hid_device *hid) void hid_close(struct hid_device *hid)
{ {
if (!--hid->open) if (!--hid->open)
usb_unlink_urb(hid->urbin); usb_kill_urb(hid->urbin);
} }
/* /*
...@@ -1643,7 +1657,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -1643,7 +1657,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, len, usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, len,
hid_irq_in, hid, endpoint->bInterval); hid_irq_in, hid, endpoint->bInterval);
hid->urbin->transfer_dma = hid->inbuf_dma; hid->urbin->transfer_dma = hid->inbuf_dma;
hid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; hid->urbin->transfer_flags |=(URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
} else { } else {
if (hid->urbout) if (hid->urbout)
continue; continue;
...@@ -1653,7 +1667,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -1653,7 +1667,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0, usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0,
hid_irq_out, hid, 1); hid_irq_out, hid, 1);
hid->urbout->transfer_dma = hid->outbuf_dma; hid->urbout->transfer_dma = hid->outbuf_dma;
hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; hid->urbout->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
} }
} }
...@@ -1703,8 +1717,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -1703,8 +1717,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
hid->ctrlbuf, 1, hid_ctrl, hid); hid->ctrlbuf, 1, hid_ctrl, hid);
hid->urbctrl->setup_dma = hid->cr_dma; hid->urbctrl->setup_dma = hid->cr_dma;
hid->urbctrl->transfer_dma = hid->ctrlbuf_dma; hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP | URB_ASYNC_UNLINK);
| URB_NO_SETUP_DMA_MAP);
return hid; return hid;
...@@ -1730,9 +1743,9 @@ static void hid_disconnect(struct usb_interface *intf) ...@@ -1730,9 +1743,9 @@ static void hid_disconnect(struct usb_interface *intf)
return; return;
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
usb_unlink_urb(hid->urbin); usb_kill_urb(hid->urbin);
usb_unlink_urb(hid->urbout); usb_kill_urb(hid->urbout);
usb_unlink_urb(hid->urbctrl); usb_kill_urb(hid->urbctrl);
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid); hidinput_disconnect(hid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment