PERFORCE change 126732 for review

Hans Petter Selasky hselasky at FreeBSD.org
Sun Sep 23 07:22:58 PDT 2007


http://perforce.freebsd.org/chv.cgi?CH=126732

Change 126732 by hselasky at hselasky_laptop001 on 2007/09/23 14:22:45

	
	FYI; The comments follow the P4 diff from top to bottom.
	
	- check that "index" is zero before returning a match on the default pipe.
	
	- define all valid USB packet sizes in the "usbd_std_packet_size[][]" table.
	
	- new internal function "usbd_compute_max_frame_size()" that
	  computes the frames size from the packet size.
	
	- new global function "usbd_transfer_setup_sub()" which
	  performs common setup of "struct usbd_xfer" cross all
	  host/device drivers.
	
	- changes to "usbd_transfer_setup()" are part of the USB
	  transfer setup refactorisation.
	
	- remove all "usbd_xxx_copy_xxx()" functions.
	
	- new global function "usbd_std_root_transfer()" which is
	  statemachine wrapper for BULK, CONTROL and INTERRUPT USB
	  transfers that uses a linear buffer to transfer data for
	  sake of convenience.
	
	- new internal function "usbd_start_hardware_sub()" which main
	  purpose is to setup the correct state for split USB control
	  transfers. Don't confuse this by the USB transaction
	  translator.  Split USB control transfers is here simply a way
	  to transfer USB control data in smaller parts.
	
	- new internal function "usbd_premature_callback()" which is
	  simply factored out code.
	
	- remove printing of "xfer->length", hence it does not exist any more.
	
	- transform "USBD_DEV_XXX" flags into "flags_int.xxx"
	
	- the check for "xfer->nframes == 0" has been factored out
	  into "usbd_start_hardware()"
	
	- the "xfer->usb_mtx" mutex should not be held when calling
	  "__usbd_callback()".
	
	- "usbd_transfer_done()" is now part of "usbd_transfer_dequeue()"
	
	- "usbd_do_request_callback()" is now an internal function.
	
	- "usbd_do_request()" is now a macro.
	
	- "usbd_do_request_flags()" now allocated a proxy USB transfer
	  and uses this for all subsequent USB transfer. This also makes
	  the control transfer function more reliable in low-memory
	  environment, hence it does no longer depend on any memory
	  allocation after USB enumeration.
	
	- all USB BULK/ISOC/INTR IN-transfers must setup
	  "xfer->frlengths[]" before calling "usbd_start_hardware()".
	  Else the actual length of the previous transfer will be 
	  used for transfer length of the next USB transfer.

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#29 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#29 (text+ko) ====

@@ -199,7 +199,8 @@
 	 */
 	if((setup->endpoint == 0) &&
 	   (setup->type == 0) &&
-	   (udev->default_pipe.edesc)) {
+	   (udev->default_pipe.edesc) &&
+	   (!index)) {
 		pipe = &udev->default_pipe;
 		goto found;
 	}
@@ -220,6 +221,416 @@
 	return (USBD_NORMAL_COMPLETION);
 }
 
+static const struct usbd_std_packet_size {
+    struct {
+      uint16_t min; /* inclusive */
+      uint16_t max; /* inclusive */
+    } range;
+
+    uint16_t fixed[4];
+
+} usbd_std_packet_size[4][USB_SPEED_MAX] = {
+
+  [UE_INTERRUPT] = {
+    [USB_SPEED_LOW] = { .range = { 0, 8 } },
+    [USB_SPEED_FULL] = { .range = { 0, 64 } },
+    [USB_SPEED_HIGH] = { .range = { 0, 1024 } },
+    [USB_SPEED_VARIABLE] = { .range = { 0, 1024 } },
+  },
+
+  [UE_CONTROL] = {
+    [USB_SPEED_LOW] = { .fixed = { 8, 8, 8, 8 } },
+    [USB_SPEED_FULL] = { .fixed = { 8, 16, 32, 64 } },
+    [USB_SPEED_HIGH] = { .fixed = { 64, 64, 64, 64 } },
+    [USB_SPEED_VARIABLE] = { .fixed = { 512, 512, 512, 512 } },
+  },
+
+  [UE_BULK] = {
+    [USB_SPEED_LOW] = { }, /* invalid (all zero) */
+    [USB_SPEED_FULL] = { .fixed = { 8, 16, 32, 64 } },
+    [USB_SPEED_HIGH] = { .fixed = { 512, 512, 512, 512 } },
+    [USB_SPEED_VARIABLE] = { .fixed = { 512, 1024, 1536 } },
+  },
+
+  [UE_ISOCHRONOUS] = {
+    [USB_SPEED_LOW] = { }, /* invalid (all zero) */
+    [USB_SPEED_FULL] = { .range = { 0, 1023 } },
+    [USB_SPEED_HIGH] = { .range = { 0, 1024 } },
+    [USB_SPEED_VARIABLE] = { .range = { 0, 3584 } },
+  },
+};
+
+static void
+usbd_compute_max_frame_size(struct usbd_xfer *xfer)
+{
+	/* compute maximum frame size */
+
+	if (xfer->max_packet_count == 2) {
+	    xfer->max_frame_size = 2 * xfer->max_packet_size;
+	} else if (xfer->max_packet_count == 3) {
+	    xfer->max_frame_size = 3 * xfer->max_packet_size;
+	} else {
+	    xfer->max_frame_size = xfer->max_packet_size;
+	}
+	return;
+}
+
+/*------------------------------------------------------------------------*
+ *  usbd_transfer_setup_sub - transfer setup subroutine
+ *------------------------------------------------------------------------*/
+void
+usbd_transfer_setup_sub(struct usbd_setup_params *parm)
+{
+	enum { REQ_SIZE = 8,
+	       MIN_PKT = 8 };
+	struct usbd_xfer *xfer = parm->curr_xfer;
+	const struct usbd_config *setup = parm->curr_setup;
+	usb_endpoint_descriptor_t *edesc;
+	struct usbd_std_packet_size std_size;
+	uint16_t n_frlengths;
+	uint16_t n_frbuffers;
+	uint8_t type;
+	uint8_t zmps;
+
+	/* sanity check */
+
+	if ((parm->hc_max_packet_size == 0) ||
+	    (parm->hc_max_packet_count == 0) ||
+	    (parm->hc_max_frame_size == 0)) {
+	    parm->err = USBD_INVAL;
+	    goto done;
+	}
+
+	edesc = xfer->pipe->edesc;
+
+	type = (edesc->bmAttributes & UE_XFERTYPE);
+
+	xfer->flags = setup->flags;
+	xfer->nframes = setup->frames;
+	xfer->timeout = setup->timeout;
+	xfer->callback = setup->callback;
+	xfer->interval = setup->interval;
+	xfer->endpoint = edesc->bEndpointAddress;
+	xfer->max_packet_size = UGETW(edesc->wMaxPacketSize);
+	xfer->max_packet_count = 1;
+
+	parm->bufsize = setup->bufsize;
+
+	if (parm->speed == USB_SPEED_HIGH) {
+	    xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3;
+	    xfer->max_packet_size &= 0x7FF;
+	}
+
+	/* range check "max_packet_count" */
+
+	if (xfer->max_packet_count > parm->hc_max_packet_count) {
+	    xfer->max_packet_count = parm->hc_max_packet_count;
+	}
+
+	/* filter "wMaxPacketSize" according to HC capabilities */
+
+	if (xfer->max_packet_size > parm->hc_max_packet_size) {
+	    xfer->max_packet_size = parm->hc_max_packet_size;
+	}
+
+	/* filter "wMaxPacketSize" according to standard sizes */
+
+	std_size = usbd_std_packet_size[type][parm->speed];
+
+	if (std_size.range.min || std_size.range.max) {
+
+	    if (xfer->max_packet_size < std_size.range.min) {
+	        xfer->max_packet_size = std_size.range.min;
+	    }
+
+	    if (xfer->max_packet_size > std_size.range.max) {
+	        xfer->max_packet_size = std_size.range.max;
+	    }
+	} else {
+
+	    if (xfer->max_packet_size >= std_size.fixed[3]) {
+	        xfer->max_packet_size = std_size.fixed[3];
+	    } else if (xfer->max_packet_size >= std_size.fixed[2]) {
+	        xfer->max_packet_size = std_size.fixed[2];
+	    } else if (xfer->max_packet_size >= std_size.fixed[1]) {
+	        xfer->max_packet_size = std_size.fixed[1];
+	    } else {
+	        /* only one possibility left */
+	        xfer->max_packet_size = std_size.fixed[0];
+	    }
+	}
+
+	/* compute "max_frame_size" */
+
+	usbd_compute_max_frame_size(xfer);
+
+	/* check interrupt interval and transfer pre-delay */
+
+	if (type == UE_ISOCHRONOUS) {
+
+	    uint32_t frame_limit;
+
+	    xfer->interval = 0; /* not used, must be zero */
+
+	    if (xfer->timeout == 0) {
+	        /*
+		 * set a default timeout in 
+		 * case something goes wrong!
+		 */
+	        xfer->timeout = 1000 / 4;
+	    }
+
+	    if (parm->speed == USB_SPEED_HIGH) {
+	        frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER;
+	    } else {
+	        frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER;
+	    }
+
+	    if (xfer->nframes > frame_limit) {
+	        /*
+		 * this is not going to work
+		 * cross hardware
+		 */
+	        parm->err = USBD_INVAL;
+		goto done;
+	    }
+
+	    n_frlengths = xfer->nframes;
+	    n_frbuffers = 1;
+
+	} else {
+
+	    /* BSD specific requirement:
+	     *
+	     * In case we are transferring more than one USB frame
+	     * consisting of up to 3 USB packets, make sure that the
+	     * USB frame size is divisible by 8. This is supposed to
+	     * optimize the USB Host Controller by avoiding unaligned
+	     * data accesses!
+	     */
+
+	    if (parm->bufsize > xfer->max_frame_size) {
+
+		while (xfer->max_frame_size & 7) {
+		    if (xfer->max_packet_size == parm->hc_max_packet_size) {
+			/* should not happen */
+			parm->err = USBD_INVAL;
+			goto done;
+		    }
+		    (xfer->max_packet_size) ++;
+		    usbd_compute_max_frame_size(xfer);
+		}
+	    }
+	
+	    /* if a value is specified use that
+	     * else check the endpoint descriptor
+	     */
+	    if (xfer->interval == 0) {
+
+		if (type == UE_INTERRUPT) {
+
+		    xfer->interval = edesc->bInterval;
+
+		    if (parm->speed == USB_SPEED_HIGH) {
+		        xfer->interval /= 8; /* 125us -> 1ms */
+		    }
+
+		    if (xfer->interval == 0) {
+		        /* one millisecond is the smallest interval */
+		        xfer->interval = 1;
+		    }
+		}
+	    }
+
+	    if (type == UE_CONTROL) {
+		xfer->flags_int.control_xfr = 1;
+		if (xfer->nframes == 0) {
+	            xfer->nframes = 2;
+		}
+	    } else {
+		if (xfer->nframes == 0) {
+		    xfer->nframes = 1;
+		}
+	    }
+
+	    n_frlengths = xfer->nframes;
+	    n_frbuffers = xfer->nframes;
+	}
+
+	if (xfer->nframes == 0) {
+	    parm->err = USBD_ZERO_NFRAMES;
+	    goto done;
+	}
+
+	/*
+	 * NOTE: we do not allow "max_packet_size" or "max_frame_size"
+	 * to be equal to zero when setting up USB transfers, hence
+	 * this leads to alot of extra code in the USB kernel.
+	 */
+
+	if ((xfer->max_frame_size == 0) ||
+	    (xfer->max_packet_size == 0)) {
+
+	    zmps = 1;
+
+	    if ((parm->bufsize <= MIN_PKT) &&
+		(type != UE_CONTROL) &&
+		(type != UE_BULK)) {
+
+		/* workaround */
+		xfer->max_packet_size = MIN_PKT;
+		xfer->max_packet_count = 1;
+		parm->bufsize = 0; /* automatic setup length */
+		usbd_compute_max_frame_size(xfer);
+
+	    } else {
+	        parm->err = USBD_ZERO_MAXP;
+		goto done;
+	    }
+
+	} else {
+	    zmps = 0;
+	}
+
+	/*
+	 * check if we should setup a default
+	 * length:
+	 */
+
+	if (parm->bufsize == 0) {
+
+	    parm->bufsize = xfer->max_frame_size;
+
+	    if (type == UE_ISOCHRONOUS) {
+	        parm->bufsize *= xfer->nframes;
+	    }
+	}
+
+	/*
+	 * check if we are about to setup a proxy
+	 * type of buffer:
+	 */
+
+	if (xfer->flags.proxy_buffer) {
+
+	    /* round bufsize up */
+
+	    parm->bufsize += (xfer->max_frame_size-1);
+
+	    if (parm->bufsize < xfer->max_frame_size) {
+		/* length wrapped around */
+		parm->err = USBD_INVAL;
+		goto done;
+	    }
+
+	    /* subtract remainder */
+
+	    parm->bufsize -= (parm->bufsize % xfer->max_frame_size);
+
+	    /* add length of USB device request structure, if any */
+
+	    if (type == UE_CONTROL) {
+	        parm->bufsize += REQ_SIZE; /* SETUP message */
+	    }
+	}
+
+	xfer->max_data_length = parm->bufsize;
+
+	/*
+	 * check if we have room for the
+	 * USB device request structure:
+	 */
+
+	if (type == UE_CONTROL) {
+
+	    if (xfer->max_data_length < REQ_SIZE) {
+	        /* length wrapped around or too small bufsize */
+	        parm->err = USBD_INVAL;
+		goto done;
+	    }
+
+	    xfer->max_data_length -= REQ_SIZE;
+	}
+
+	/* align data */
+	parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN-1));
+
+	xfer->frlengths = USBD_ADD_BYTES(parm->buf,parm->size[0]);
+
+	parm->size[0] += (n_frlengths * sizeof(xfer->frlengths[0]));
+
+	/* align data */
+	parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN-1));
+
+	xfer->frbuffers = USBD_ADD_BYTES(parm->buf,parm->size[0]);
+
+	parm->size[0] += (n_frbuffers * sizeof(xfer->frbuffers[0]));
+
+	/* align data */
+	parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN-1));
+
+	/*
+	 * check if we need to setup
+	 * a local buffer:
+	 */
+
+	if (!xfer->flags.ext_buffer) {
+
+	    /* align data */
+	    parm->size[1] += ((-parm->size[1]) & (USB_HOST_ALIGN-1));
+
+	    if (parm->buf) {
+
+	        usbd_page_cache_init
+		  (xfer->frbuffers + 0, parm->page_ptr,
+		   parm->size[1], parm->bufsize);
+
+		if (type == UE_CONTROL) {
+
+		    usbd_page_cache_init
+		      (xfer->frbuffers + 1, parm->page_ptr,
+		       parm->size[1] + REQ_SIZE, parm->bufsize - REQ_SIZE);
+		}
+
+		/*
+		 * make a copy of the page cache that
+		 * starts at offset zero:
+		 */
+
+		xfer->buf_data = xfer->frbuffers[0];
+	    }
+
+	    parm->size[1] += parm->bufsize;
+
+	    /* align data again */
+	    parm->size[1] += ((-parm->size[1]) & (USB_HOST_ALIGN-1));
+	}
+
+	if (zmps) {
+	    /* correct maximum data length */
+	    xfer->max_data_length = 0;
+	}
+
+	/* subtract USB frame remainder from "hc_max_frame_size" */
+
+	xfer->max_usb_frame_size = 
+	  (parm->hc_max_frame_size - 
+	   (parm->hc_max_frame_size % xfer->max_frame_size));
+
+	if (xfer->max_usb_frame_size == 0) {
+	    parm->err = USBD_INVAL;
+	    goto done;
+	}
+
+ done:
+	if (parm->err) {
+	    xfer->max_usb_frame_size = 1; /* XXX avoid division by zero */
+	    xfer->max_data_length = 0;
+	    xfer->nframes = 0;
+	}
+	return;
+}
+
 /*---------------------------------------------------------------------------*
  *	usbd_transfer_setup - setup an array of USB transfers
  *
@@ -233,19 +644,23 @@
 usbd_status
 usbd_transfer_setup(struct usbd_device *udev, 
 		    u_int8_t iface_index, 
-		    struct usbd_xfer **pxfer,
+		    struct usbd_xfer **ppxfer,
 		    const struct usbd_config *setup_start, 
 		    u_int16_t n_setup, 
 		    void *priv_sc,
 		    struct mtx *priv_mtx)
 {
+	struct usbd_xfer dummy;
+	struct usbd_setup_params parm;
 	const struct usbd_config *setup_end = setup_start + n_setup;
 	const struct usbd_config *setup;
 	struct usbd_memory_info *info;
 	struct usbd_xfer *xfer;
-	usbd_status error = 0;
+	void *buf = NULL;
 	u_int16_t n;
 
+	parm.err = 0;
+
 	WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, 
 		     "usbd_transfer_setup can sleep!");
 
@@ -263,73 +678,172 @@
 		priv_mtx = &usb_global_lock;
 	}
 
-	for(setup = setup_start, n = n_setup; n--; setup++)
+	for (setup = setup_start, n = 0;
+	     setup != setup_end; setup++, n++)
 	{
 		if(setup->bufsize == 0xffffffff)
 		{
-		    error = USBD_BAD_BUFSIZE;
+		    parm.err = USBD_BAD_BUFSIZE;
 		    PRINTF(("invalid bufsize\n"));
 		}
 
-		if(setup->flags & 
-		   (~(0|
-		      USBD_FORCE_SHORT_XFER|
-		      USBD_SHORT_XFER_OK|
-		      USBD_USE_POLLING|
-		      USBD_PIPE_BOF|
-		      USBD_USE_DMA|
-		      0)))
-		{
-		    error = USBD_BAD_FLAG;
-		    PRINTF(("invalid flag(s) specified: "
-			    "0x%08x\n", setup->flags));
-		}
 		if(setup->callback == NULL)
 		{
-		    error = USBD_NO_CALLBACK;
+		    parm.err = USBD_NO_CALLBACK;
 		    PRINTF(("no callback\n"));
 		}
-		pxfer[n] = NULL;
+		ppxfer[n] = NULL;
 	}
 
-	if(error)
-	{
+	if (parm.err) {
 		goto done;
 	}
 
-	error = (udev->bus->methods->xfer_setup)
-	  (udev,iface_index,pxfer,setup_start,setup_end);
+	bzero(&parm, sizeof(parm));
+
+	parm.udev = udev;
+	parm.speed = usbd_get_speed(udev);
+	parm.hc_max_packet_count = 1;
+
+	if (parm.speed >= USB_SPEED_MAX) {
+	    parm.err = USBD_INVAL;
+	    goto done;
+	}
+
+	/* setup all transfers */
+
+	while (1) {
+
+	    parm.size[0] = 0;
+	    parm.size[1] = 0;
+	    parm.buf = buf;
+
+	    /* align data to 8 byte boundary */
+	    parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN-1));
+
+	    if (buf) {
+
+	        info = USBD_ADD_BYTES(buf,parm.size[0]);
+
+		info->memory_base = buf;
+		info->memory_size = parm.total_size[0];
+
+		info->page_base = parm.page_ptr;
+		info->page_size = parm.total_size[1];
+
+		info->usb_mtx = &(udev->bus->mtx);
+
+		usbd_page_cache_init(&(parm.pc), parm.page_ptr, 
+				     0, parm.total_size[1] * USB_PAGE_SIZE);
+	    } else {
+	        info = NULL;
+	    }
+
+	    parm.size[0] += sizeof(info[0]);
+
+	    for (setup = setup_start, n = 0;
+		 setup != setup_end; setup++, n++) {
+
+	        /* store current setup pointer */
+	        parm.curr_setup = setup;
 
+	        /* align data to 8 byte boundary */
+	        parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN-1));
 
-	/* common setup */
+		if (buf) {
 
-	for(setup = setup_start, n = n_setup; n--; setup++)
-	{
-		xfer = pxfer[n];
+		    xfer = USBD_ADD_BYTES(buf,parm.size[0]);
 
-		if(xfer)
-		{
+		    ppxfer[n] = xfer;
+		    xfer->udev = udev;
+		    xfer->address = udev->address;
 		    xfer->priv_sc = priv_sc;
 		    xfer->priv_mtx = priv_mtx;
-		    xfer->udev = udev;
+		    xfer->usb_mtx = &(udev->bus->mtx);
+		    xfer->usb_root = info;
+		    info->memory_refcount++;
+		    info->setup_refcount++;
+
+		    __callout_init_mtx(&xfer->timeout_handle, xfer->usb_mtx,
+				       CALLOUT_RETURNUNLOCKED);
+		} else {
+		    /* dummy xfer */
+		    xfer = &dummy;
+		    bzero(&dummy, sizeof(dummy));
+		}
+
+		parm.size[0] += sizeof(xfer[0]);
+
+		xfer->pipe = usbd_get_pipe(udev, iface_index, setup);
+
+		if (!xfer->pipe) {
+		    parm.err = USBD_NO_PIPE;
+		    goto done;
+		}
+
+		if (buf) {
+		    xfer->pipe->refcount++;
+		}
+
+		parm.methods = xfer->pipe->methods;
+		parm.curr_xfer = xfer;
 
-		    if(xfer->pipe)
-		    {
-		        xfer->pipe->refcount++;
-		    }
+		(udev->bus->methods->xfer_setup)(&parm);
 
-		    info = xfer->usb_root;
-		    info->memory_refcount++;
-		    info->setup_refcount++;
+		if (parm.err) {
+		    goto done;
 		}
+	    }
+
+	    if (buf || parm.err) {
+	        goto done;
+	    }
+
+	    /* compute number of USB pages required */
+	    parm.total_size[1] =
+	      (parm.size[1] + USB_PAGE_SIZE - 1) / USB_PAGE_SIZE;
+
+	    /* align data to 8 byte boundary */
+	    parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN-1));
+
+	    /* store offset temporarily */
+	    parm.size[2] = parm.size[0];
+
+	    parm.size[0] += (sizeof(parm.page_ptr[0]) * parm.total_size[1]);
+
+	    /* store total buffer size */
+	    parm.total_size[0] = parm.size[0];
+
+	    /* allocate zeroed memory */
+	    buf = malloc(parm.total_size[0], M_USB, M_WAITOK|M_ZERO);
+
+	    if (buf == NULL) {
+	        parm.err = USBD_NOMEM;
+		PRINTFN(-1, ("cannot allocate memory block for "
+			     "configuration (%d bytes)\n", 
+			     parm.total_size[0]));
+		goto done;
+	    }
+
+	    parm.page_ptr = USBD_ADD_BYTES(buf,parm.size[2]);
+
+	    if (usbd_page_alloc(udev->bus->dma_tag,
+				parm.page_ptr, parm.total_size[1])) {
+	        free(buf, M_USB);
+		parm.err = USBD_NOMEM;
+		PRINTFN(-1, ("cannot allocate memory block for "
+			     "configuration (%d USB pages)\n", 
+			     parm.total_size[1]));
+		goto done;
+	    }
 	}
 
  done:
-	if(error)
+	if (parm.err)
 	{
-		usbd_transfer_unsetup(pxfer, n_setup);
+		usbd_transfer_unsetup(ppxfer, n_setup);
 	}
-	return (error);
+	return (parm.err);
 }
 
 /*---------------------------------------------------------------------------*
@@ -467,183 +981,278 @@
 	return;
 }
 
-void
-usbd_std_isoc_copy_in(struct usbd_xfer *xfer)
+/*------------------------------------------------------------------------*
+ *	usbd_std_root_transfer - factored out code
+ *
+ * Return values:
+ *   0: Tried to do a callback and unlocked "xfer->usb_mtx"
+ *   Else: Did nothing.
+ *------------------------------------------------------------------------*/
+uint8_t
+usbd_std_root_transfer(struct usbd_std_root_transfer *std,
+		       struct thread *ctd,
+		       usbd_std_root_transfer_func_t *func)
 {
-    u_int32_t x;
-    u_int32_t length;
+	struct usbd_xfer *xlist[2];
+	struct usbd_xfer *xfer;
+	struct thread *td;
+	uint32_t len;
+	uint8_t shortpkt = 0;
+
+	xfer = std->xfer;
+	if (xfer == NULL) {
+	    /* the transfer is gone */
+	    return 1;
+	}
+
+	mtx_assert(xfer->usb_mtx, MA_OWNED);
 
-    if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) {
+	if (xfer->usb_thread != ctd) {
+	    /* we should not call this transfer back */
+	    return 1;
+	}
 
-        length = 0;
+	std->xfer = NULL;
 
-	for (x = 0; x < xfer->nframes; x++) {
-	    length += xfer->frlengths[x];
+	if (ctd == NULL) {
+	    td = curthread;
+	} else {
+	    td = ctd;
 	}
 
-	/* only one copy is needed, hence 
-	 * the frames are back to back:
-	 */
-	usbd_copy_in(&(xfer->buf_data), 0, xfer->buffer, length);
+	/* make sure that the memory does not disappear! */
+	xfer->usb_thread = td;
+	xfer->usb_root->memory_refcount++;
+
+	if (xfer->flags_int.control_xfr &&
+	    xfer->flags_int.control_hdr) {
+
+	    /* copy out the USB request */
+
+	    if (xfer->frlengths[0] == sizeof(std->req)) {
+		usbd_copy_out(xfer->frbuffers + 0, 0, 
+			      &(std->req), sizeof(std->req));
+	    } else {
+	        std->err = USBD_INVAL;
+		goto done;
+	    }
+
+	    xfer->aframes = 1;
+
+	    std->err = 0;
+	    std->state = USBD_STD_ROOT_TR_SETUP;
+
+	    (func)(xfer, std);
 
-    } else {
+	    if (xfer->usb_thread != td) {
+	        goto done;
+	    }
 
-        for (x = 0; x < xfer->nframes; x++) {
-	    xfer->frlengths_old[x] = xfer->frlengths[x];
+	    if (std->err) {
+		goto done;
+	    }
 	}
-    }
-    return;
-}
+
+	std->err = 0;
+	std->state = USBD_STD_ROOT_TR_PRE_DATA;
+
+	(func)(xfer, std);
 
-void
-usbd_std_isoc_copy_out(struct usbd_xfer *xfer)
-{
-    u_int8_t *ptr;
-    u_int32_t x;
-    u_int32_t offset;
+	if (xfer->usb_thread != td) {
+	    goto done;
+	}
 
-    if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+	if (std->err) {
+	    goto done;
+	}
 
-        ptr = xfer->buffer;
-	offset = 0;
+	/* transfer data */
 
-        for (x = 0; x < xfer->nframes; x++) {
+	while (xfer->aframes != xfer->nframes) {
 
-	    usbd_copy_out(&(xfer->buf_data), offset,
-			  ptr, xfer->frlengths[x]);
+	    len = xfer->frlengths[xfer->aframes];
 
-	    ptr += xfer->frlengths_old[x];
-	    offset += xfer->frlengths_old[x];
-	}
-    }
-    return;
-}
+	    if (len > std->len) {
+	        len = std->len;
+		shortpkt = 1;
+	    }
 
-void
-usbd_std_bulk_intr_copy_in(struct usbd_xfer *xfer)
-{
-    if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) {
+	    if (len > 0) {
+	        if ((xfer->endpoint & (UE_DIR_IN|UE_DIR_OUT)) == UE_DIR_IN) {
+		    usbd_copy_in(xfer->frbuffers + xfer->aframes, 0,
+				 std->ptr, len);
+		} else {
+		    usbd_copy_out(xfer->frbuffers + xfer->aframes, 0,
+				  std->ptr, len);
+		}
+	    }
 
-        usbd_copy_in(&(xfer->buf_data), 0, 
-		     xfer->buffer, xfer->length);
-    }
-    return;
-}
+	    std->ptr += len;
+	    std->len -= len;
+	    xfer->frlengths[xfer->aframes] = len;
+	    xfer->aframes ++;
 
-void
-usbd_std_bulk_intr_copy_out(struct usbd_xfer *xfer)
-{
-    if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+	    if (shortpkt) {
+	        break;
+	    }
+	}
 
-        usbd_copy_out(&(xfer->buf_data), 0, 
-		      xfer->buffer, xfer->actlen);
-    }
-    return;
-}
+	std->err = 0;
+	std->state = USBD_STD_ROOT_TR_POST_DATA;
 
-void
-usbd_std_ctrl_copy_in(struct usbd_xfer *xfer)
-{
-    u_int32_t len = xfer->length;
-    usb_device_request_t *req;
+	(func)(xfer, std);
 
-    if (xfer->control_remainder == 0) {
-        req = xfer->buffer;
-	if ((len >= sizeof(*req)) &&
-	    (req->bmRequestType & UT_READ)) {
-	    len = sizeof(*req);
+	if (xfer->usb_thread != td) {
+	    goto done;
 	}
-    } else {
-        if (xfer->control_isread) {
-	    return;
+
+	if (std->err) {
+	    goto done;
 	}
-    }
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
+
+	    std->err = 0;
+	    std->state = USBD_STD_ROOT_TR_STATUS;
 
-    usbd_copy_in(&(xfer->buf_data), 0, 
-		 xfer->buffer, len);
-    return;
-}
+	    (func)(xfer, std);
 
-void
-usbd_std_ctrl_copy_out(struct usbd_xfer *xfer)
-{
-    u_int32_t len = xfer->actlen;
-    usb_device_request_t *req;
+	    if (xfer->usb_thread != td) {
+	        goto done;
+	    }
 
-    if (xfer->flags & USBD_DEV_CONTROL_HEADER) {
-        req = xfer->buffer;
-        if ((len >= sizeof(*req)) &&
-	    (req->bmRequestType & UT_READ)) {
-	    /* copy out everything, but the header */
-	    usbd_copy_out(&(xfer->buf_data), sizeof(*req), 
-			  (req+1), len - sizeof(*req));
+	    if (std->err) {
+	        goto done;
+	    }
 	}
-    } else {
-        if (xfer->control_isread) {
-	    /* copy out everything */
-	    usbd_copy_out(&(xfer->buf_data), 0,
-			  xfer->buffer, len);
+
+ done:
+	/* check if we are still handling this USB transfer */
+
+	if (xfer->usb_thread == td) {
+	    std->state = USBD_STD_ROOT_TR_PRE_CALLBACK;
+	    (func)(xfer, std);
 	}
-    }
-    return;
+
+	/* queue callback */
+	xlist[0] = xfer;
+	xlist[1] = NULL;
+
+	mtx_unlock(xfer->usb_mtx);
+
+	/*
+	 * NOTE: "usbd_do_callback()" will decrement 
+	 * the "memory_refcount" and must be called.
+	 */
+	usbd_do_callback(xlist, td);
+
+	return 0;
 }
 
-uint8_t
-usbd_std_ctrl_enter(struct usbd_xfer *xfer)

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list