/*------------------------------------------------------------------------* * usb_handle_stall * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) { struct usb_device *udev = xfer->xroot->udev; usb_error_t err; USB_XFER_UNLOCK(xfer); err = usbd_set_endpoint_stall(udev, usbd_get_ep_by_addr(udev, ep), do_stall); USB_XFER_LOCK(xfer); return (err); }
/*------------------------------------------------------------------------* * usb_handle_set_config * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) { struct usb_device *udev = xfer->xroot->udev; usb_error_t err = 0; uint8_t do_unlock; /* * We need to protect against other threads doing probe and * attach: */ USB_XFER_UNLOCK(xfer); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); 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(udev, conf_no)) { DPRINTF("set config %d failed\n", conf_no); err = USB_ERR_STALLED; goto done; } if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { DPRINTF("probe and attach failed\n"); err = USB_ERR_STALLED; goto done; } done: if (do_unlock) usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (err); }
/*------------------------------------------------------------------------* * usb_handle_iface_request * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_handle_iface_request(struct usb_xfer *xfer, void **ppdata, uint16_t *plen, struct usb_device_request req, uint16_t off, uint8_t state) { struct usb_interface *iface; struct usb_interface *iface_parent; /* parent interface */ struct usb_device *udev = xfer->xroot->udev; int error; uint8_t iface_index; uint8_t temp_state; if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { iface_index = req.wIndex[0]; /* unicast */ } else { iface_index = 0; /* broadcast */ } /* * We need to protect against other threads doing probe and * attach: */ USB_XFER_UNLOCK(xfer); usbd_enum_lock(udev); error = ENXIO; tr_repeat: iface = usbd_get_iface(udev, iface_index); if ((iface == NULL) || (iface->idesc == NULL)) { /* end of interfaces non-existing interface */ goto tr_stalled; } /* set initial state */ temp_state = state; /* forward request to interface, if any */ if ((error != 0) && (error != ENOTTY) && (iface->subdev != NULL) && device_is_attached(iface->subdev)) { #if 0 DEVMETHOD(usb_handle_request, NULL); /* dummy */ #endif error = USB_HANDLE_REQUEST(iface->subdev, &req, ppdata, plen, off, &temp_state); } iface_parent = usbd_get_iface(udev, iface->parent_iface_index); if ((iface_parent == NULL) || (iface_parent->idesc == NULL)) { /* non-existing interface */ iface_parent = NULL; } /* forward request to parent interface, if any */ if ((error != 0) && (error != ENOTTY) && (iface_parent != NULL) && (iface_parent->subdev != NULL) && ((req.bmRequestType & 0x1F) == UT_INTERFACE) && (iface_parent->subdev != iface->subdev) && device_is_attached(iface_parent->subdev)) { error = USB_HANDLE_REQUEST(iface_parent->subdev, &req, ppdata, plen, off, &temp_state); } if (error == 0) { /* negativly adjust pointer and length */ *ppdata = ((uint8_t *)(*ppdata)) - off; *plen += off; if ((state == USB_HR_NOT_COMPLETE) && (temp_state == USB_HR_COMPLETE_OK)) goto tr_short; else goto tr_valid; } else if (error == ENOTTY) { goto tr_stalled; } if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { iface_index++; /* iterate */ goto tr_repeat; } if (state != USB_HR_NOT_COMPLETE) { /* we are complete */ goto tr_valid; } switch (req.bmRequestType) { case UT_WRITE_INTERFACE: switch (req.bRequest) { case UR_SET_INTERFACE: /* * We assume that the endpoints are the same * accross the alternate settings. * * Reset the endpoints, because re-attaching * only a part of the device is not possible. */ error = usb_check_alt_setting(udev, iface, req.wValue[0]); if (error) { DPRINTF("alt setting does not exist %s\n", usbd_errstr(error)); goto tr_stalled; } error = usb_reset_iface_endpoints(udev, iface_index); if (error) { DPRINTF("alt setting failed %s\n", usbd_errstr(error)); goto tr_stalled; } /* update the current alternate setting */ iface->alt_index = req.wValue[0]; break; default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (req.bRequest) { case UR_GET_INTERFACE: *ppdata = &iface->alt_index; *plen = 1; break; default: goto tr_stalled; } break; default: goto tr_stalled; } tr_valid: usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (0); tr_short: usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (USB_ERR_SHORT_XFER); tr_stalled: usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (USB_ERR_STALLED); }
/*------------------------------------------------------------------------* * usbd_do_request_flags and usbd_do_request * * Description of arguments passed to these functions: * * "udev" - this is the "usb_device" structure pointer on which the * request should be performed. It is possible to call this function * in both Host Side mode and Device Side mode. * * "mtx" - if this argument is non-NULL the mutex pointed to by it * will get dropped and picked up during the execution of this * function, hence this function sometimes needs to sleep. If this * argument is NULL it has no effect. * * "req" - this argument must always be non-NULL and points to an * 8-byte structure holding the USB request to be done. The USB * request structure has a bit telling the direction of the USB * request, if it is a read or a write. * * "data" - if the "wLength" part of the structure pointed to by "req" * is non-zero this argument must point to a valid kernel buffer which * can hold at least "wLength" bytes. If "wLength" is zero "data" can * be NULL. * * "flags" - here is a list of valid flags: * * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than * specified * * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed * at a later point in time. This is tunable by the "hw.usb.ss_delay" * sysctl. This flag is mostly useful for debugging. * * o USB_USER_DATA_PTR: treat the "data" pointer like a userland * pointer. * * "actlen" - if non-NULL the actual transfer length will be stored in * the 16-bit unsigned integer pointed to by "actlen". This * information is mostly useful when the "USB_SHORT_XFER_OK" flag is * used. * * "timeout" - gives the timeout for the control transfer in * milliseconds. A "timeout" value less than 50 milliseconds is * treated like a 50 millisecond timeout. A "timeout" value greater * than 30 seconds is treated like a 30 second timeout. This USB stack * does not allow control requests without a timeout. * * NOTE: This function is thread safe. All calls to * "usbd_do_request_flags" will be serialised by the use of an * internal "sx_lock". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout) { usb_handle_req_t *hr_func; struct usb_xfer *xfer; const void *desc; int err = 0; usb_ticks_t start_ticks; usb_ticks_t delta_ticks; usb_ticks_t max_ticks; uint16_t length; uint16_t temp; if (timeout < 50) { /* timeout is too small */ timeout = 50; } if (timeout > 30000) { /* timeout is too big */ timeout = 30000; } length = UGETW(req->wLength); DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", udev, req->bmRequestType, req->bRequest, req->wValue[1], req->wValue[0], req->wIndex[1], req->wIndex[0], req->wLength[1], req->wLength[0]); /* Check if the device is still alive */ if (udev->state < USB_STATE_POWERED) { DPRINTF("usb device has gone\n"); return (USB_ERR_NOT_CONFIGURED); } /* * Set "actlen" to a known value in case the caller does not * check the return value: */ if (actlen) *actlen = 0; #if (USB_HAVE_USER_IO == 0) if (flags & USB_USER_DATA_PTR) return (USB_ERR_INVAL); #endif if (mtx) { mtx_unlock(mtx); if (mtx != &Giant) { mtx_assert(mtx, MA_NOTOWNED); } } /* * Grab the default sx-lock so that serialisation * is achieved when multiple threads are involved: */ sx_xlock(udev->default_sx); hr_func = usbd_get_hr_func(udev); if (hr_func != NULL) { DPRINTF("Handle Request function is set\n"); desc = NULL; temp = 0; if (!(req->bmRequestType & UT_READ)) { if (length != 0) { DPRINTFN(1, "The handle request function " "does not support writing data!\n"); err = USB_ERR_INVAL; goto done; } } /* The root HUB code needs the BUS lock locked */ USB_BUS_LOCK(udev->bus); err = (hr_func) (udev, req, &desc, &temp); USB_BUS_UNLOCK(udev->bus); if (err) goto done; if (length > temp) { if (!(flags & USB_SHORT_XFER_OK)) { err = USB_ERR_SHORT_XFER; goto done; } length = temp; } if (actlen) *actlen = length; if (length > 0) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { if (copyout(desc, data, length)) { err = USB_ERR_INVAL; goto done; } } else #endif bcopy(desc, data, length); } goto done; /* success */ } /* * Setup a new USB transfer or use the existing one, if any: */ usbd_default_transfer_setup(udev); xfer = udev->default_xfer[0]; if (xfer == NULL) { /* most likely out of memory */ err = USB_ERR_NOMEM; goto done; } USB_XFER_LOCK(xfer); if (flags & USB_DELAY_STATUS_STAGE) xfer->flags.manual_status = 1; else xfer->flags.manual_status = 0; if (flags & USB_SHORT_XFER_OK) xfer->flags.short_xfer_ok = 1; else xfer->flags.short_xfer_ok = 0; xfer->timeout = timeout; start_ticks = ticks; max_ticks = USB_MS_TO_TICKS(timeout); usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(*req)); xfer->nframes = 2; while (1) { temp = length; if (temp > xfer->max_data_length) { temp = usbd_xfer_max_len(xfer); } usbd_xfer_set_frame_len(xfer, 1, temp); if (temp > 0) { if (!(req->bmRequestType & UT_READ)) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { USB_XFER_UNLOCK(xfer); err = usbd_copy_in_user(xfer->frbuffers + 1, 0, data, temp); USB_XFER_LOCK(xfer); if (err) { err = USB_ERR_INVAL; break; } } else #endif usbd_copy_in(xfer->frbuffers + 1, 0, data, temp); } xfer->nframes = 2; } else { if (xfer->frlengths[0] == 0) { if (xfer->flags.manual_status) { #if USB_DEBUG int temp; temp = usb_ss_delay; if (temp > 5000) { temp = 5000; } if (temp > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(temp)); } #endif xfer->flags.manual_status = 0; } else { break; } } xfer->nframes = 1; } usbd_transfer_start(xfer); while (usbd_transfer_pending(xfer)) { cv_wait(udev->default_cv, xfer->xroot->xfer_mtx); } err = xfer->error; if (err) { break; } /* subtract length of SETUP packet, if any */ if (xfer->aframes > 0) { xfer->actlen -= xfer->frlengths[0]; } else { xfer->actlen = 0; } /* check for short packet */ if (temp > xfer->actlen) { temp = xfer->actlen; length = temp; } if (temp > 0) { if (req->bmRequestType & UT_READ) { #if USB_HAVE_USER_IO if (flags & USB_USER_DATA_PTR) { USB_XFER_UNLOCK(xfer); err = usbd_copy_out_user(xfer->frbuffers + 1, 0, data, temp); USB_XFER_LOCK(xfer); if (err) { err = USB_ERR_INVAL; break; } } else #endif usbd_copy_out(xfer->frbuffers + 1, 0, data, temp); } } /* * Clear "frlengths[0]" so that we don't send the setup * packet again: */ usbd_xfer_set_frame_len(xfer, 0, 0); /* update length and data pointer */ length -= temp; data = USB_ADD_BYTES(data, temp); if (actlen) { (*actlen) += temp; } /* check for timeout */ delta_ticks = ticks - start_ticks; if (delta_ticks > max_ticks) { if (!err) { err = USB_ERR_TIMEOUT; } } if (err) { break; } } if (err) { /* * Make sure that the control endpoint is no longer * blocked in case of a non-transfer related error: */ usbd_transfer_stop(xfer); } USB_XFER_UNLOCK(xfer); done: sx_xunlock(udev->default_sx); if (mtx) { mtx_lock(mtx); } return ((usb_error_t)err); }