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

Reply via email to