Ok, here is a patch that adds interrupts (via interrupt URBs, instead of bulk
URBs) to usbdevfs. This takes a different approach than the last
(usbdevfs-interrupt) patch. It allows using interrupt-type URBs via usbdevfs,
and maintains the correct polling interval based on the endpoint's bInterval.
The URB is not automatically resubmitted by the HCD.
-A flag USB_NO_RESUBMIT is added, for use in urb->transfer_flags.
-All 3 HCD are modified to treat interrupt URBs with this flag as one-shot
(regardless of their polling interval).
-devio.c is modified in proc_submiturb() to handle USBDEVFS_URB_TYPE_INTERRUPTs.
uhci.c and usb-ohci.c were easy to follow; I'm pretty confident they are ok.
usb-uhci.c was rather difficult to follow, and has many complicated paths
depending on certain states of the URB. I think I got it right tho. If
the authors of the HCDs could review the patch I'd appreciate it :-)
I have tested all 3 HCDs using interrupts through usbdevfs, no problems I could
find. Also tested normal (resubmitting) interrupts using HID, no problems.
Note that usb-ohci.c was not treating 0-interval interrupts as one-shot; I
changed it to treat 0-interval interrupts as 1-shot. However the documentation
(URB.txt) doesn't mention 0-interval/one-shot interrupts at all! So either the
uhci HCDs or the documentation is wrong...?
<opinion>
I think if the HCDs are overhauled in 2.5, I would suggest some changes:
-Allow all URB types to be resubmitted (have a urb->resubmit flag).
-Use the flag for resubmission (don't resubmit based on URB type, or URB
interval).
-Use resubmission to indicate a ring, not a manual ring check. i.e.,
if the last URB in the ->next list has resubmission enabled, resubmit
the whole batch (i.e., it's a 'ring'); otherwise it was a one-shot batch.
That saves time and complexity. No driver should submit an actual 'ring',
i.e. it should *always* be NULL-terminated.
-Allow all URB types to be queued! (I think OHCI already does this?)
-Last opinion: if one of the UHCI HCDs is dropped, I think 'uhci' is cleaner
than 'usb-uhci'. I would recommend keeping 'uhci'.
</opinion>
This is against 2.4.4.
Any comments would be greatly appreciated!
diff -ur 2.4.4-clean/drivers/usb/devio.c linux/drivers/usb/devio.c
--- 2.4.4-clean/drivers/usb/devio.c Fri Apr 6 18:51:50 2001
+++ linux/drivers/usb/devio.c Fri May 11 19:17:36 2001
@@ -756,7 +756,7 @@
struct async *as;
devrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
- int ret;
+ int ret, interval = 0;
if (copy_from_user(&uurb, arg, sizeof(uurb)))
return -EFAULT;
@@ -815,6 +815,16 @@
return -EFAULT;
break;
+ case USBDEVFS_URB_TYPE_INTERRUPT:
+ uurb.number_of_packets = 0;
+ if (!(ep_desc = usb_epnum_to_ep_desc(ps->dev, uurb.endpoint)))
+ return -ENOENT;
+ if (uurb.buffer_length > ep_desc->wMaxPacketSize)
+ return -EINVAL;
+ interval = ep_desc->bInterval;
+ uurb.flags |= USB_NO_RESUBMIT;
+ break;
+
case USBDEVFS_URB_TYPE_ISO:
/* arbitrary limit */
if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128)
@@ -863,6 +873,7 @@
as->urb.pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint &
0xf) | (uurb.endpoint & USB_DIR_IN);
as->urb.transfer_flags = uurb.flags;
as->urb.transfer_buffer_length = uurb.buffer_length;
+ as->urb.interval = interval;
as->urb.setup_packet = (unsigned char*)dr;
as->urb.start_frame = uurb.start_frame;
as->urb.number_of_packets = uurb.number_of_packets;
diff -ur 2.4.4-clean/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- 2.4.4-clean/drivers/usb/uhci.c Wed Apr 25 17:10:49 2001
+++ linux/drivers/usb/uhci.c Sat May 12 00:38:43 2001
@@ -1638,7 +1638,7 @@
break;
case PIPE_INTERRUPT:
/* Interrupts are an exception */
- if (urb->interval) {
+ if (urb->interval && !(USB_NO_RESUBMIT & urb->transfer_flags)) {
uhci_add_complete(urb);
return; /* <-- note return */
}
@@ -2214,6 +2214,7 @@
killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
urb->status == -ECONNRESET);
resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
+ !(USB_NO_RESUBMIT & urb->transfer_flags) &&
urb->interval && !killed);
nurb = urb->next;
diff -ur 2.4.4-clean/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- 2.4.4-clean/drivers/usb/usb-ohci.c Wed Apr 25 17:10:49 2001
+++ linux/drivers/usb/usb-ohci.c Sat May 12 01:11:27 2001
@@ -466,12 +466,17 @@
: PCI_DMA_FROMDEVICE);
urb->complete (urb);
- /* implicitly requeued */
- urb->actual_length = 0;
- urb->status = USB_ST_URB_PENDING;
- if (urb_priv->state != URB_DEL)
- td_submit_urb (urb);
- break;
+ if (urb->interval && !(USB_NO_RESUBMIT & urb->transfer_flags))
+{
+ /* implicitly requeued */
+ urb->actual_length = 0;
+ urb->status = USB_ST_URB_PENDING;
+ if (urb_priv->state != URB_DEL)
+ td_submit_urb (urb);
+ } else {
+ urb_rm_priv(urb);
+ }
+ break;
+
case PIPE_ISOCHRONOUS:
for (urbt = urb->next; urbt && (urbt != urb); urbt =
urbt->next);
diff -ur 2.4.4-clean/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- 2.4.4-clean/drivers/usb/usb-uhci.c Fri Apr 27 18:13:07 2001
+++ linux/drivers/usb/usb-uhci.c Fri May 11 23:31:04 2001
@@ -2423,10 +2423,9 @@
if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) &&
(urb->status != -ENOENT)) {
- urb->status = -EINPROGRESS;
-
// Recycle INT-TD if interval!=0, else mark TD as one-shot
- if (urb->interval) {
+ if (urb->interval && !(USB_NO_RESUBMIT & urb->transfer_flags))
+{
+ urb->status = -EINPROGRESS;
desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
if (status==0) {
@@ -2443,8 +2442,11 @@
mb();
}
else {
- uhci_unlink_urb_async(s, urb);
- desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD
+ /* The URB is finished. Clean up the TD, and the URB
+will be cleaned up when we return. */
+ list_del(&desc->desc_list);
+ unlink_td(s, desc, 1);
+ delete_desc(desc);
+ usb_dec_dev_use(urb->dev);
}
}
}
@@ -2566,6 +2568,7 @@
dbg("dequeued urb: %p", urb);
dequeue_urb (s, urb);
+ urb->dev = NULL;
#ifdef DEBUG_SLAB
kmem_cache_free(urb_priv_kmem, urb->hcpriv);
@@ -2573,7 +2576,8 @@
kfree (urb->hcpriv);
#endif
- if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { //
process_interrupt does completion on its own
+ /* process_interrupt does completion on its own */
+ if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {
urb_t *next_urb = urb->next;
int is_ring = 0;
int contains_killed = 0;
@@ -2632,7 +2636,6 @@
// Completion
if (urb->complete) {
int was_unlinked = (urb->status == -ENOENT);
- urb->dev = NULL;
spin_unlock(&s->urb_list_lock);
urb->complete ((struct urb *) urb);
// Re-submit the URB if ring-linked
diff -ur 2.4.4-clean/include/linux/usb.h linux/include/linux/usb.h
--- 2.4.4-clean/include/linux/usb.h Fri Apr 27 18:49:27 2001
+++ linux/include/linux/usb.h Fri May 11 13:32:15 2001
@@ -425,6 +425,7 @@
#define USB_QUEUE_BULK 0x0010
#define USB_NO_FSBR 0x0020
#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length
packet
+#define USB_NO_RESUBMIT 0x0080 // Do not automatically resubmit URB (only
+interrupt URBs automatically resubmit)
#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD!
typedef struct
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
http://lists.sourceforge.net/lists/listinfo/linux-usb-devel