PERFORCE change 130735 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Wed Dec 12 13:10:38 PST 2007
http://perforce.freebsd.org/chv.cgi?CH=130735
Change 130735 by hselasky at 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) <<<
More information about the p4-projects
mailing list