Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 12 Dec 2007 21:10:37 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 130735 for review
Message-ID:  <200712122110.lBCLAbv9062933@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=130735

Change 130735 by hselasky@hselasky_laptop001 on 2007/12/12 21:09:59

	
	This commit is related to USB device side support.
	
	FYI: The comments below follow the diff.
	
	o New globel function "usbd_get_pipe_by_addr".
	
	o Skip setting up USB transfers with missing callback.
	
	o Added some extra state handling when "stall_pipe" is set.
	
	o A new set of functions "usbd_handle_xxx" where added
	  that are directly related to handling of control
	  endpoint messages in USB device mode.
	
	o "usbd_default_transfer_setup" needed to be reworked
	  to handle some races that can happen when a new
	  USB device address is set.
	
	o Else some small optimisations.

Affected files ...

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

Differences ...

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

@@ -60,6 +60,7 @@
 static void usbd_bdma_work_loop(struct usbd_memory_info *info);
 static void usbd_bdma_cancel_event(struct usbd_xfer *xfer);
 static void usbd_callback_wrapper(struct usbd_xfer *xfer, uint8_t context);
+static usbd_status_t usbd_handle_request(struct usbd_xfer *xfer);
 
 #ifdef USB_DEBUG
 void
@@ -85,8 +86,8 @@
 	    " address=%d config=%d depth=%d speed=%d self_powered=%d\n"
 	    " power=%d langid=%d\n",
 	    udev->bus,
-	    udev->address, udev->config, udev->depth, udev->speed,
-	    udev->self_powered, udev->power, udev->langid);
+	    udev->address, udev->curr_config_no, udev->depth, udev->speed,
+	    udev->flags.self_powered, udev->power, udev->langid);
 	return;
 }
 
@@ -156,7 +157,46 @@
 	return ((uaa->vendor << 16) | (uaa->product));
 }
 
+/*------------------------------------------------------------------------*
+ *	usbd_get_pipe_by_addr
+ *
+ * This function searches for an USB pipe by endpoint address.
+ *------------------------------------------------------------------------*/
 struct usbd_pipe *
+usbd_get_pipe_by_addr(struct usbd_device *udev, uint8_t ea_val)
+{
+	struct usbd_pipe *pipe = udev->pipes;
+	struct usbd_pipe *pipe_end = udev->pipes_end;
+	enum {
+		EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR),
+	};
+
+	ea_val &= EA_MASK;
+
+	for (; pipe != pipe_end; pipe++) {
+
+		if (pipe->edesc == NULL) {
+			continue;
+		}
+		/* do the mask and check the value */
+		if ((pipe->edesc->bEndpointAddress & EA_MASK) == ea_val) {
+			goto found;
+		}
+	}
+
+	/* do the mask and check the value */
+	if ((udev->default_pipe.edesc) &&
+	    ((udev->default_pipe.edesc->bEndpointAddress & EA_MASK) == ea_val)) {
+		pipe = &udev->default_pipe;
+		goto found;
+	}
+	return (NULL);
+
+found:
+	return (pipe);
+}
+
+struct usbd_pipe *
 usbd_get_pipe(struct usbd_device *udev, uint8_t iface_index,
     const struct usbd_config *setup)
 {
@@ -349,12 +389,12 @@
 	xfer->flags = setup->flags;
 	xfer->nframes = setup->frames;
 	xfer->timeout = setup->timeout;
-	xfer->callback = setup->cb[parm->udev->usb_mode];
+	xfer->callback = setup->cb[parm->udev->flags.usb_mode];
 	xfer->interval = setup->interval;
 	xfer->endpoint = edesc->bEndpointAddress;
 	xfer->max_packet_size = UGETW(edesc->wMaxPacketSize);
 	xfer->max_packet_count = 1;
-	xfer->flags_int.usb_mode = parm->udev->usb_mode;	/* make a shadow copy */
+	xfer->flags_int.usb_mode = parm->udev->flags.usb_mode;	/* make a shadow copy */
 
 	parm->bufsize = setup->bufsize;
 
@@ -745,7 +785,8 @@
 			parm.err = USBD_BAD_BUFSIZE;
 			PRINTF(("invalid bufsize\n"));
 		}
-		if (setup->cb[udev->usb_mode] == NULL) {
+		if ((setup->cb[USB_MODE_HOST] == NULL) &&
+		    (setup->cb[USB_MODE_DEVICE] == NULL)) {
 			parm.err = USBD_NO_CALLBACK;
 			PRINTF(("no callback\n"));
 		}
@@ -799,6 +840,13 @@
 		for (setup = setup_start, n = 0;
 		    setup != setup_end; setup++, n++) {
 
+			if (setup->cb[udev->flags.usb_mode] == NULL) {
+				/*
+				 * Skip USB transfers without
+				 * callbacks !
+				 */
+				continue;
+			}
 			/* see if there is a matching endpoint */
 			pipe = usbd_get_pipe(udev, iface_index, setup);
 			if (!pipe) {
@@ -1289,7 +1337,16 @@
 	uint32_t len;
 
 	/*
-         * check if there is a control
+	 * USB control endpoints are not just straight forward ...
+	 * Check for STALL:
+	 */
+	if (xfer->flags.stall_pipe &&
+	    (xfer->flags_int.usb_mode == USB_MODE_DEVICE)) {
+		/* no longer active */
+		xfer->flags_int.control_act = 0;
+	}
+	/*
+         * Check if there is a control
          * transfer in progress:
          */
 	if (xfer->flags_int.control_act) {
@@ -2127,13 +2184,36 @@
 	mtx_assert(xfer->usb_mtx, MA_OWNED);
 
 	/*
-	 * if the transfer is not inserted, insert the transfer into the
-	 * transfer queue
+	 * If the transfer is not inserted, insert the transfer into the
+	 * transfer queue:
 	 */
 	if (xfer->pipe_list.le_prev == NULL) {
 		LIST_INSERT_HEAD(&xfer->pipe->list_head, xfer, pipe_list);
 	}
 	/*
+	 * If the pipe is already stalled we do nothing except putting
+	 * the transfer on the queue !
+	 */
+	if (xfer->pipe->is_stalled) {
+		goto done;
+	}
+	/*
+	 * Check if we are supposed to stall the pipe:
+	 */
+	if (xfer->flags.stall_pipe &&
+	    (xfer->flags_int.usb_mode == USB_MODE_DEVICE)) {
+		/*
+	         * We can not stall ISOCHRONOUS endpoints !
+	         */
+		type = (xfer->pipe->edesc->bmAttributes & UE_XFERTYPE);
+		if (type != UE_ISOCHRONOUS) {
+			xfer->pipe->is_stalled = 1;
+			(xfer->pipe->methods->set_stall) (
+			    xfer->udev, NULL, xfer->pipe);
+			goto done;
+		}
+	}
+	/*
 	 * Handled cases:
 	 *
 	 * 1) Start the first transfer queued. This transfer is always last on
@@ -2279,13 +2359,12 @@
 
 		/* remove the transfer from pipe transfer list */
 		LIST_REMOVE(xfer, pipe_list);
+		xfer->pipe_list.le_prev = 0;
 
 		/* start "next" transfer, if any */
-		if (xfer_prev &&
-		    (xfer_prev->pipe_list.le_next == NULL)) {
-			(xfer_prev->pipe->methods->start) (xfer_prev);
+		if (xfer_prev) {
+			usbd_transfer_enqueue(xfer_prev);
 		}
-		xfer->pipe_list.le_prev = 0;
 	}
 done:
 	return;
@@ -2313,66 +2392,714 @@
 }
 
 /*------------------------------------------------------------------------*
- *	usbd_serve_request_callback
+ *	usbd_handle_request_callback
  *------------------------------------------------------------------------*/
 static void
-usbd_serve_request_callback(struct usbd_xfer *xfer)
+usbd_handle_request_callback(struct usbd_xfer *xfer)
 {
-	;				/* workaround for a bug in "indent" */
+	usbd_status_t err;
+
+	/* check the current transfer state */
 
 	switch (USBD_GET_STATE(xfer)) {
 	case USBD_ST_SETUP:
 	case USBD_ST_TRANSFERRED:
+
+		/* handle the request */
+		err = usbd_handle_request(xfer);
+
+		if (err == USBD_BAD_CONTEXT) {
+			/*
+			 * Currently we get a "start" context by
+			 * waking up the explore thread.
+			 */
+			usb_needs_explore(xfer->udev->bus,
+			    USB_BUS_EXPLORE_TREE);
+			return;
+		}
+		if ((!xfer->flags_int.control_act) || err) {
+			/*
+		         * If no control transfer is active,
+		         * receive the next SETUP message:
+		         */
+			goto tr_start;
+		}
+		usbd_start_hardware(xfer);
+		return;
+
 	default:
+		if (xfer->error != USBD_CANCELLED) {
+			/* should not happen - try stalling */
+			err = USBD_STALLED;
+			goto tr_start;
+		}
 		break;
 	}
 	return;
+
+tr_start:
+	xfer->frlengths[0] = sizeof(usb_device_request_t);
+	xfer->nframes = 1;
+	xfer->flags.manual_status = 1;
+	xfer->flags.force_short_xfer = 0;
+	xfer->flags.short_xfer_ok = 0;
+	if (err) {
+		xfer->flags.stall_pipe = 1;
+	} else {
+		xfer->flags.stall_pipe = 0;
+	}
+	usbd_start_hardware(xfer);
+	return;
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_set_config
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_set_config(struct usbd_xfer *xfer, uint8_t conf_no)
+{
+	mtx_unlock(xfer->priv_mtx);
+
+	usbd_detach_device(xfer->udev, USB_IFACE_INDEX_ANY, 1);
+
+	if (conf_no == USB_UNCONFIG_NO) {
+		conf_no = USB_UNCONFIG_INDEX;
+	} else {
+		/*
+		 * The relationship between config number and config index
+		 * is very simple in our case:
+		 */
+		conf_no--;
+	}
+
+	if (usbd_set_config_index(xfer->udev, conf_no, 0)) {
+		mtx_lock(xfer->priv_mtx);
+		return (USBD_STALLED);
+	}
+	if (usbd_probe_and_attach(xfer->udev, USB_IFACE_INDEX_ANY)) {
+		mtx_lock(xfer->priv_mtx);
+		return (USBD_STALLED);
+	}
+	mtx_lock(xfer->priv_mtx);
+
+	return (0);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_set_alt_setting
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_set_alt_setting(struct usbd_xfer *xfer,
+    uint8_t iface_index, uint8_t alt_index)
+{
+	if (iface_index >= USB_MAX_INTERFACES) {
+		return (USBD_STALLED);
+	}
+	mtx_unlock(xfer->priv_mtx);
+
+	usbd_detach_device(xfer->udev, iface_index, 1);
+
+	if (usbd_set_alt_interface_index(xfer->udev,
+	    iface_index, alt_index)) {
+		mtx_lock(xfer->priv_mtx);
+		return (USBD_STALLED);
+	}
+	if (usbd_probe_and_attach(xfer->udev, iface_index)) {
+		mtx_lock(xfer->priv_mtx);
+		return (USBD_STALLED);
+	}
+	mtx_lock(xfer->priv_mtx);
+
+	return (0);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_get_alt_setting
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_get_alt_setting(struct usbd_xfer *xfer,
+    uint8_t iface_index, uint8_t *ptr)
+{
+	struct usbd_interface *iface;
+
+	iface = usbd_get_iface(xfer->udev, iface_index);
+	if (iface) {
+		*ptr = iface->alt_index;
+		return (0);
+	}
+	return (USBD_STALLED);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_set_stall_sub
+ *
+ * This function is used to make a BULK or INTERRUPT endpoint
+ * send STALL tokens.
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_set_stall_sub(struct usbd_device *udev, uint8_t ea_val, uint8_t do_stall)
+{
+	struct usbd_pipe *pipe;
+	struct usbd_xfer *xfer[2];
+	struct thread *td = curthread;
+	uint8_t et;
+
+	pipe = usbd_get_pipe_by_addr(udev, ea_val);
+	if (pipe == NULL) {
+		/* nothing to do */
+		return (USBD_INVAL);
+	}
+	et = (pipe->edesc->bmAttributes & UE_XFERTYPE);
+
+	if ((et != UE_BULK) &&
+	    (et != UE_INTERRUPT)) {
+		/*
+	         * Should not stall control
+	         * nor isochronous endpoints.
+	         */
+		return (0);
+	}
+	mtx_lock(&(udev->bus->mtx));
+
+	if (pipe->is_stalled == do_stall) {
+		/* if the pipe is already stalled do nothing */
+		mtx_unlock(&(udev->bus->mtx));
+		return (0);
+	}
+	/* update stalled state */
+	pipe->is_stalled = do_stall;
+
+	/* lookup the current USB transfer */
+	xfer[0] = LIST_FIRST(&(pipe->list_head));
+	if (xfer[0]) {
+
+		/* search to the end of the LIST */
+		while (LIST_NEXT(xfer[0], pipe_list)) {
+			xfer[0] = LIST_NEXT(xfer[0], pipe_list);
+		}
+
+		if (do_stall) {
+			/*
+		         * Set "usb_thread" so that no other
+		         * thread will call this callback!
+		         */
+			xfer[0]->usb_thread = td;
+		}
+	}
+	if (do_stall) {
+		/*
+		 * If "xfer[0]" is non-NULL the "set_stall" method will
+		 * complete the USB transfer like in case of a timeout
+		 * setting the error code "USBD_STALLED". The "set_stall"
+		 * method should not call the USB transfer callback.
+		 */
+		(pipe->methods->set_stall) (udev, xfer[0], pipe);
+	} else {
+		pipe->toggle_next = 0;	/* reset data toggle */
+
+		(pipe->methods->clear_stall) (udev, pipe);
+
+		/* start up the first transfer, if any */
+		if (xfer[0]) {
+			usbd_transfer_enqueue(xfer[0]);
+		}
+	}
+
+	mtx_unlock(&(udev->bus->mtx));
+
+	if (xfer[0]) {
+		if (do_stall) {
+			xfer[1] = NULL;	/* set endmark */
+			usbd_do_callback(xfer, td);
+		}
+	}
+	return (0);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_stall
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_set_stall(struct usbd_xfer *xfer, uint8_t ep, uint8_t do_stall)
+{
+	usbd_status_t err;
+
+	mtx_unlock(xfer->priv_mtx);
+	err = usbd_handle_set_stall_sub(xfer->udev, ep, do_stall);
+	mtx_lock(xfer->priv_mtx);
+	return (err);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_get_stall
+ *------------------------------------------------------------------------*/
+static uint8_t
+usbd_handle_get_stall(struct usbd_device *udev, uint8_t ea_val)
+{
+	struct usbd_pipe *pipe;
+	uint8_t halted;
+
+	pipe = usbd_get_pipe_by_addr(udev, ea_val);
+	if (pipe == NULL) {
+		/* nothing to do */
+		return (0);
+	}
+	mtx_lock(&(udev->bus->mtx));
+	halted = pipe->is_stalled;
+	mtx_unlock(&(udev->bus->mtx));
+
+	return (halted);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_remote_wakeup
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_remote_wakeup(struct usbd_xfer *xfer, uint8_t do_suspend)
+{
+	usbd_status_t err;
+
+	mtx_unlock(xfer->priv_mtx);
+	err = usbd_suspend_resume(xfer->udev, do_suspend);
+	mtx_lock(xfer->priv_mtx);
+	return (err);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_handle_request
+ *------------------------------------------------------------------------*/
+static usbd_status_t
+usbd_handle_request(struct usbd_xfer *xfer)
+{
+	enum {
+		ST_DATA,
+		ST_CONTEXT_START,
+		ST_POST_STATUS,
+	};
+
+	/*
+         * State sequence:
+         *
+         * ST_DATA [ -> ST_CONTEXT_START ] -> ST_POST_STATUS
+         */
+	usb_device_request_t req;
+	struct usbd_device *udev;
+	const void *ptr;
+	uint16_t off;			/* data offset */
+	uint16_t rem;			/* data remainder */
+	uint16_t max_len;		/* max fragment length */
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint8_t state;
+	union {
+		uWord	wStatus;
+		uint8_t	buf[2];
+	}     temp;
+
+	/*
+	 * Filter the USB transfer state into
+	 * something which we understand:
+	 */
+
+	switch (USBD_GET_STATE(xfer)) {
+	case USBD_ST_SETUP:
+		if (!xfer->flags_int.control_act) {
+			/* nothing to do */
+			return (0);
+		}
+		if (xfer->flags_int.context != USBD_CONTEXT_START) {
+			/* wrong context */
+			goto tr_bad_context;
+		}
+		state = ST_CONTEXT_START;
+		break;
+
+	default:			/* USBD_ST_TRANSFERRED */
+		if (!xfer->flags_int.control_act) {
+			state = ST_POST_STATUS;
+		} else {
+			state = ST_DATA;
+		}
+		break;
+	}
+
+	/* reset frame stuff */
+
+	xfer->frlengths[0] = 0;
+	xfer->frlengths[1] = 0;
+
+	usbd_set_frame_offset(xfer, 0, 0);
+	usbd_set_frame_offset(xfer, sizeof(req), 1);
+
+	/* get the current request, if any */
+
+	usbd_copy_out(xfer->frbuffers + 0, 0, &req, sizeof(req));
+
+	/* get the remainder of the control transfer */
+
+	rem = (xfer->flags_int.control_rem == 0xFFFF) ? 0 :
+	    xfer->flags_int.control_rem;
+
+	/* compute the current offset */
+
+	off = UGETW(req.wLength) - rem;
+
+	/* set some defaults */
+
+	max_len = 0;
+	ptr = &temp;
+	udev = xfer->udev;
+
+	/* get some request fields decoded */
+
+	wValue = UGETW(req.wValue);
+	wIndex = UGETW(req.wIndex);
+
+	/* demultiplex the control request */
+
+	switch (req.bmRequestType) {
+	case UT_READ_DEVICE:
+		if (state != ST_DATA) {
+			break;
+		}
+		switch (req.bRequest) {
+		case UR_GET_DESCRIPTOR:
+			goto tr_handle_get_descriptor;
+		case UR_GET_CONFIG:
+			goto tr_handle_get_config;
+		case UR_GET_STATUS:
+			goto tr_handle_get_status;
+		default:
+			goto tr_stalled;
+		}
+		break;
+
+	case UT_WRITE_DEVICE:
+		switch (req.bRequest) {
+		case UR_SET_ADDRESS:
+			goto tr_handle_set_address;
+		case UR_SET_CONFIG:
+			goto tr_handle_set_config;
+		default:
+			goto tr_stalled;
+		}
+		break;
+
+	case UT_WRITE_ENDPOINT:
+		switch (req.bRequest) {
+		case UR_CLEAR_FEATURE:
+			switch (UGETW(req.wValue)) {
+			case UF_ENDPOINT_HALT:
+				goto tr_handle_clear_halt;
+			case UF_DEVICE_REMOTE_WAKEUP:
+				goto tr_handle_clear_wakeup;
+			default:
+				goto tr_stalled;
+			}
+			break;
+
+		case UR_SET_FEATURE:
+			switch (UGETW(req.wValue)) {
+			case UF_ENDPOINT_HALT:
+				goto tr_handle_set_halt;
+			case UF_DEVICE_REMOTE_WAKEUP:
+				goto tr_handle_set_wakeup;
+			default:
+				goto tr_stalled;
+			}
+			break;
+		default:
+			goto tr_stalled;
+		}
+		break;
+
+	case UT_READ_ENDPOINT:
+		switch (req.bRequest) {
+		case UR_GET_STATUS:
+			goto tr_handle_get_ep_status;
+		default:
+			goto tr_stalled;
+		}
+		break;
+
+	case UT_WRITE_INTERFACE:
+		switch (req.bRequest) {
+		case UR_SET_INTERFACE:
+			goto tr_handle_set_interface;
+		default:
+			goto tr_stalled;
+		}
+		break;
+
+	case UT_READ_INTERFACE:
+		switch (req.bRequest) {
+		case UR_GET_INTERFACE:
+			goto tr_handle_get_interface;
+		default:
+			goto tr_stalled;
+		}
+		break;
+
+	case UT_WRITE_CLASS_INTERFACE:
+	case UT_WRITE_VENDOR_INTERFACE:
+		/* XXX forward */
+		break;
+
+	case UT_READ_CLASS_INTERFACE:
+	case UT_READ_VENDOR_INTERFACE:
+		/* XXX forward */
+		break;
+	default:
+		goto tr_stalled;
+	}
+	goto tr_valid;
+
+tr_handle_get_descriptor:
+	(udev->usb_temp_get_desc) (
+	    udev, xfer->priv_mtx, &req, &ptr, &max_len);
+	if (ptr == NULL) {
+		goto tr_stalled;
+	}
+	/* use zero copy */
+	usbd_set_frame_data(xfer, USBD_ADD_BYTES(ptr, off), 1);
+	ptr = NULL;
+	/* adjust maximum length according to offset */
+	max_len -= off;
+	goto tr_valid;
+
+tr_handle_get_config:
+	max_len = 1;
+	temp.buf[0] = udev->curr_config_no;
+	goto tr_valid;
+
+tr_handle_get_status:
+	max_len = sizeof(temp.wStatus);
+	/* XXX FIXME */
+	USETW(temp.wStatus, UDS_SELF_POWERED);
+	goto tr_valid;
+
+tr_handle_set_address:
+	if (state == ST_DATA) {
+		if (wValue >= 0x80) {
+			/* invalid value */
+			goto tr_stalled;
+		} else if (udev->curr_config_no != 0) {
+			/* we are configured ! */
+			goto tr_stalled;
+		}
+	} else if (state == ST_POST_STATUS) {
+		udev->address = (wValue & 0x7F);
+		goto tr_bad_context;
+	}
+	goto tr_valid;
+
+tr_handle_set_config:
+	if (state == ST_DATA) {
+		goto tr_bad_context;
+	} else if (state == ST_CONTEXT_START) {
+		if (usbd_handle_set_config(xfer, wValue)) {
+			goto tr_stalled;
+		}
+	}
+	goto tr_valid;
+
+tr_handle_clear_halt:
+	if (state == ST_DATA) {
+		if (usbd_handle_set_stall(xfer, wIndex, 0)) {
+			goto tr_stalled;
+		}
+	}
+	goto tr_valid;
+
+tr_handle_clear_wakeup:
+	if (state == ST_DATA) {
+		goto tr_bad_context;
+	} else if (state == ST_CONTEXT_START) {
+		if (usbd_handle_remote_wakeup(xfer, 0)) {
+			goto tr_stalled;
+		}
+	}
+	goto tr_valid;
+
+tr_handle_set_halt:
+	if (state == ST_DATA) {
+		if (usbd_handle_set_stall(xfer, wIndex, 1)) {
+			goto tr_stalled;
+		}
+	}
+	goto tr_valid;
+
+tr_handle_set_wakeup:
+	if (state == ST_DATA) {
+		goto tr_bad_context;
+	} else if (state == ST_CONTEXT_START) {
+		if (usbd_handle_remote_wakeup(xfer, 1)) {
+			goto tr_stalled;
+		}
+	}
+	goto tr_valid;
+
+tr_handle_get_ep_status:
+	if (state == ST_DATA) {
+		max_len = sizeof(temp.wStatus);
+		temp.wStatus[0] = usbd_handle_get_stall(udev, req.wIndex[0]);
+		temp.wStatus[1] = 0;
+	}
+	goto tr_valid;
+
+tr_handle_set_interface:
+	if (state == ST_DATA) {
+		goto tr_bad_context;
+	} else if ((state == ST_CONTEXT_START) &&
+		    usbd_handle_set_alt_setting(
+	    xfer, wValue, wIndex)) {
+		goto tr_stalled;
+	}
+	goto tr_valid;
+
+tr_handle_get_interface:
+	if (state == ST_DATA) {
+		if (usbd_handle_get_alt_setting(xfer, wIndex, temp.buf)) {
+			goto tr_stalled;
+		}
+		max_len = 1;
+	}
+	goto tr_valid;
+
+tr_valid:
+
+	/* Compute the real maximum data length */
+
+	if (max_len > xfer->max_data_length) {
+		max_len = xfer->max_data_length;
+	}
+	if (max_len > rem) {
+		max_len = rem;
+	}
+	/*
+	 * If the remainder is greater than the maximum data length,
+	 * we need to truncate the value for the sake of the
+	 * comparison below:
+	 */
+	if (rem > xfer->max_data_length) {
+		rem = xfer->max_data_length;
+	}
+	if (rem != max_len) {
+		/*
+	         * If we don't transfer the data we can transfer, then
+	         * the transfer is short !
+	         */
+		xfer->flags.force_short_xfer = 1;
+		xfer->nframes = 2;
+	} else {
+		/*
+		 * Default case
+		 */
+		xfer->flags.force_short_xfer = 0;
+		xfer->nframes = max_len ? 2 : 1;
+	}
+	if (max_len > 0) {
+		if (ptr) {
+			usbd_copy_in(xfer->frbuffers + 1, 0, ptr, max_len);
+		}
+		xfer->frlengths[1] = max_len;
+	}
+	return (0);			/* success */
+
+tr_stalled:
+	return (USBD_STALLED);
+
+tr_bad_context:
+	return (USBD_BAD_CONTEXT);
 }
 
+static const struct usbd_config usbd_control_ep_cfg[1] = {
+	[0] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,	/* Control endpoint */
+		.direction = UE_DIR_ANY,
+		.bufsize = 1024,	/* bytes */
+		.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+		.cb[USB_MODE_HOST] = &usbd_do_request_callback,
+		.cb[USB_MODE_DEVICE] = &usbd_handle_request_callback,
+	},
+};
+
 /*------------------------------------------------------------------------*
  *	usbd_default_transfer_setup
+ *
+ * This function is used to setup the default USB control endpoint
+ * transfer.
  *------------------------------------------------------------------------*/
 void
 usbd_default_transfer_setup(struct usbd_device *udev)
 {
-	struct usbd_config uc[1];
+	struct usbd_xfer *xfer;
+	uint8_t no_resetup;
 
-	if ((udev->default_xfer[0] == NULL) ||
-	    (udev->default_xfer[0]->address != udev->address) ||
-	    (udev->default_ep_desc.wMaxPacketSize[0] !=
-	    udev->ddesc.bMaxPacketSize)) {
+repeat:
 
-		udev->default_ep_desc.wMaxPacketSize[0] =
-		    udev->ddesc.bMaxPacketSize;
+	xfer = udev->default_xfer[0];
+	if (xfer) {
+		mtx_lock(xfer->priv_mtx);
+		no_resetup =
+		    ((xfer->address == udev->address) &&
+		    (udev->default_ep_desc.wMaxPacketSize[0] ==
+		    udev->ddesc.bMaxPacketSize));
+		if (udev->flags.usb_mode == USB_MODE_DEVICE) {
+			if (no_resetup) {
+				/*
+				 * NOTE: checking "xfer->address" and
+				 * starting the USB transfer must be
+				 * atomic!
+				 */
+				usbd_transfer_start(xfer);
+			}
+		}
+		mtx_unlock(xfer->priv_mtx);
+	} else {
+		no_resetup = 0;
+	}
 
-		bzero(uc, sizeof(uc));
+	if (no_resetup) {
+		/*
+	         * All parameters are exactly the same like before.
+	         * Just return.
+	         */
+		return;
+	}
+	/*
+	 * Update wMaxPacketSize for the default control endpoint:
+	 */
+	udev->default_ep_desc.wMaxPacketSize[0] =
+	    udev->ddesc.bMaxPacketSize;
 
-		uc[0].type = UE_CONTROL;
-		uc[0].endpoint = 0x00;	/* Control pipe */
-		uc[0].direction = UE_DIR_ANY;
-		uc[0].bufsize = 1024;	/* bytes */
-		uc[0].flags.proxy_buffer = 1;
-		uc[0].flags.short_xfer_ok = 1;
-		if (udev->usb_mode == USB_MODE_DEVICE) {
-			uc[0].flags.manual_status = 1;
-		}
-		uc[0].cb[USB_MODE_HOST] = &usbd_do_request_callback;
-		uc[0].cb[USB_MODE_DEVICE] = &usbd_serve_request_callback;
+	/*
+	 * Unsetup any existing USB transfer:
+	 */
+	usbd_transfer_unsetup(udev->default_xfer, 1);
 
-		usbd_transfer_unsetup(udev->default_xfer, 1);
-
-		if (usbd_transfer_setup
-		    (udev, 0, udev->default_xfer, uc, 1,
-		    NULL, udev->default_mtx)) {
-			PRINTFN(0, ("Could not setup default "
-			    "USB transfer!\n"));
-		}
+	/*
+	 * Try to setup a new USB transfer for the
+	 * default control endpoint:
+	 */
+	if (usbd_transfer_setup
+	    (udev, 0, udev->default_xfer, usbd_control_ep_cfg, 1,
+	    NULL, udev->default_mtx)) {
+		PRINTFN(-1, ("could not setup default "
+		    "USB transfer!\n"));
+	} else {
+		goto repeat;
 	}
 	return;
 }
 
 /*------------------------------------------------------------------------*
  *	usbd_do_request_flags and usbd_do_request
+ *
+ * Returns:
+ *    0: Success
+ * Else: Failure
  *------------------------------------------------------------------------*/
 usbd_status_t
 usbd_do_request_flags(struct usbd_device *udev, struct mtx *mtx,
@@ -2409,11 +3136,18 @@
 	if (actlen) {
 		*actlen = 0;
 	}
-	if (udev->usb_mode == USB_MODE_DEVICE) {
+	if (udev->flags.usb_mode == USB_MODE_DEVICE) {
 		PRINTFN(0, ("USB device mode\n"));
-		(udev->usb_temp_get_desc) (udev, mtx, req, &desc, &temp);
-		if (desc == NULL) {
-			return (USBD_INVAL);
+		if ((req->bmRequestType == UT_READ_DEVICE) &&
+		    (req->bRequest == UR_GET_DESCRIPTOR)) {
+			(udev->usb_temp_get_desc) (
+			    udev, mtx, req, &desc, &temp);
+			if (desc == NULL) {
+				return (USBD_INVAL);
+			}
+		} else {
+			/* the rest we don't care about */
+			temp = 0;
 		}
 		if (length > temp) {
 			if (!(flags & USBD_SHORT_XFER_OK)) {
@@ -2424,7 +3158,9 @@
 		if (actlen) {
 			*actlen = length;
 		}
-		bcopy(desc, data, length);
+		if (length > 0) {
+			bcopy(desc, data, length);
+		}
 		return (0);		/* success */
 	}
 	/*
@@ -2666,15 +3402,15 @@
 		return (0);
 
 	case USBD_ST_TRANSFERRED:
-tr_transferred:
-		return (1);
+		break;
 
 	default:			/* Error */
 		if (xfer1->error == USBD_CANCELLED) {
 			return (0);

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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200712122110.lBCLAbv9062933>