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>