Index: head/sys/dev/usb/template/usb_template.c =================================================================== --- head/sys/dev/usb/template/usb_template.c (revision 305420) +++ head/sys/dev/usb/template/usb_template.c (revision 305421) @@ -1,1397 +1,1397 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file contains sub-routines to build up USB descriptors from * USB templates. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ MODULE_DEPEND(usb_template, usb, 1, 1, 1); MODULE_VERSION(usb_template, 1); /* function prototypes */ static void usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *); static void usb_make_endpoint_desc(struct usb_temp_setup *, const struct usb_temp_endpoint_desc *); static void usb_make_interface_desc(struct usb_temp_setup *, const struct usb_temp_interface_desc *); static void usb_make_config_desc(struct usb_temp_setup *, const struct usb_temp_config_desc *); static void usb_make_device_desc(struct usb_temp_setup *, const struct usb_temp_device_desc *); static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t, uint8_t); static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *, struct usb_hw_ep_scratch_sub *, uint8_t); static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t, uint8_t); static usb_error_t usb_hw_ep_resolve(struct usb_device *, struct usb_descriptor *); static const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *); static void *usb_temp_get_device_desc(struct usb_device *); static void *usb_temp_get_qualifier_desc(struct usb_device *); static void *usb_temp_get_config_desc(struct usb_device *, uint16_t *, uint8_t); static const void *usb_temp_get_string_desc(struct usb_device *, uint16_t, uint8_t); static const void *usb_temp_get_vendor_desc(struct usb_device *, const struct usb_device_request *, uint16_t *plen); static const void *usb_temp_get_hub_desc(struct usb_device *); static usb_error_t usb_temp_get_desc(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); static usb_error_t usb_temp_setup_by_index(struct usb_device *, uint16_t index); static void usb_temp_init(void *); /*------------------------------------------------------------------------* * usb_make_raw_desc * * This function will insert a raw USB descriptor into the generated * USB configuration. *------------------------------------------------------------------------*/ static void usb_make_raw_desc(struct usb_temp_setup *temp, const uint8_t *raw) { void *dst; uint8_t len; /* * The first byte of any USB descriptor gives the length. */ if (raw) { len = raw[0]; if (temp->buf) { dst = USB_ADD_BYTES(temp->buf, temp->size); memcpy(dst, raw, len); /* check if we have got a CDC union descriptor */ if ((raw[0] == sizeof(struct usb_cdc_union_descriptor)) && (raw[1] == UDESC_CS_INTERFACE) && (raw[2] == UDESCSUB_CDC_UNION)) { struct usb_cdc_union_descriptor *ud = (void *)dst; /* update the interface numbers */ ud->bMasterInterface += temp->bInterfaceNumber; ud->bSlaveInterface[0] += temp->bInterfaceNumber; } /* check if we have got an interface association descriptor */ if ((raw[0] == sizeof(struct usb_interface_assoc_descriptor)) && (raw[1] == UDESC_IFACE_ASSOC)) { struct usb_interface_assoc_descriptor *iad = (void *)dst; /* update the interface number */ iad->bFirstInterface += temp->bInterfaceNumber; } /* check if we have got a call management descriptor */ if ((raw[0] == sizeof(struct usb_cdc_cm_descriptor)) && (raw[1] == UDESC_CS_INTERFACE) && (raw[2] == UDESCSUB_CDC_CM)) { struct usb_cdc_cm_descriptor *ccd = (void *)dst; /* update the interface number */ ccd->bDataInterface += temp->bInterfaceNumber; } } temp->size += len; } } /*------------------------------------------------------------------------* * usb_make_endpoint_desc * * This function will generate an USB endpoint descriptor from the * given USB template endpoint descriptor, which will be inserted into * the USB configuration. *------------------------------------------------------------------------*/ static void usb_make_endpoint_desc(struct usb_temp_setup *temp, const struct usb_temp_endpoint_desc *ted) { struct usb_endpoint_descriptor *ed; const void **rd; uint16_t old_size; uint16_t mps; uint8_t ea; /* Endpoint Address */ uint8_t et; /* Endpiont Type */ /* Reserve memory */ old_size = temp->size; ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT)); et = (ted->bmAttributes & UE_XFERTYPE); if (et == UE_ISOCHRONOUS) { /* account for extra byte fields */ temp->size += sizeof(*ed) + 2; } else { temp->size += sizeof(*ed); } /* Scan all Raw Descriptors first */ rd = ted->ppRawDesc; if (rd) { while (*rd) { usb_make_raw_desc(temp, *rd); rd++; } } if (ted->pPacketSize == NULL) { /* not initialized */ temp->err = USB_ERR_INVAL; return; } mps = ted->pPacketSize->mps[temp->usb_speed]; if (mps == 0) { /* not initialized */ temp->err = USB_ERR_INVAL; return; } else if (mps == UE_ZERO_MPS) { /* escape for Zero Max Packet Size */ mps = 0; } /* * Fill out the real USB endpoint descriptor * in case there is a buffer present: */ if (temp->buf) { ed = USB_ADD_BYTES(temp->buf, old_size); if (et == UE_ISOCHRONOUS) ed->bLength = sizeof(*ed) + 2; else ed->bLength = sizeof(*ed); ed->bDescriptorType = UDESC_ENDPOINT; ed->bEndpointAddress = ea; ed->bmAttributes = ted->bmAttributes; USETW(ed->wMaxPacketSize, mps); /* setup bInterval parameter */ if (ted->pIntervals && ted->pIntervals->bInterval[temp->usb_speed]) { ed->bInterval = ted->pIntervals->bInterval[temp->usb_speed]; } else { switch (et) { case UE_BULK: case UE_CONTROL: ed->bInterval = 0; /* not used */ break; case UE_INTERRUPT: switch (temp->usb_speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: ed->bInterval = 1; /* 1 ms */ break; default: ed->bInterval = 4; /* 1 ms */ break; } break; default: /* UE_ISOCHRONOUS */ switch (temp->usb_speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: ed->bInterval = 1; /* 1 ms */ break; default: ed->bInterval = 1; /* 125 us */ break; } break; } } } temp->bNumEndpoints++; } /*------------------------------------------------------------------------* * usb_make_interface_desc * * This function will generate an USB interface descriptor from the * given USB template interface descriptor, which will be inserted * into the USB configuration. *------------------------------------------------------------------------*/ static void usb_make_interface_desc(struct usb_temp_setup *temp, const struct usb_temp_interface_desc *tid) { struct usb_interface_descriptor *id; const struct usb_temp_endpoint_desc **ted; const void **rd; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*id); /* Update interface and alternate interface numbers */ if (tid->isAltInterface == 0) { temp->bAlternateSetting = 0; temp->bInterfaceNumber++; } else { temp->bAlternateSetting++; } /* Scan all Raw Descriptors first */ rd = tid->ppRawDesc; if (rd) { while (*rd) { usb_make_raw_desc(temp, *rd); rd++; } } /* Reset some counters */ temp->bNumEndpoints = 0; /* Scan all Endpoint Descriptors second */ ted = tid->ppEndpoints; if (ted) { while (*ted) { usb_make_endpoint_desc(temp, *ted); ted++; } } /* * Fill out the real USB interface descriptor * in case there is a buffer present: */ if (temp->buf) { id = USB_ADD_BYTES(temp->buf, old_size); id->bLength = sizeof(*id); id->bDescriptorType = UDESC_INTERFACE; id->bInterfaceNumber = temp->bInterfaceNumber; id->bAlternateSetting = temp->bAlternateSetting; id->bNumEndpoints = temp->bNumEndpoints; id->bInterfaceClass = tid->bInterfaceClass; id->bInterfaceSubClass = tid->bInterfaceSubClass; id->bInterfaceProtocol = tid->bInterfaceProtocol; id->iInterface = tid->iInterface; } } /*------------------------------------------------------------------------* * usb_make_config_desc * * This function will generate an USB config descriptor from the given * USB template config descriptor, which will be inserted into the USB * configuration. *------------------------------------------------------------------------*/ static void usb_make_config_desc(struct usb_temp_setup *temp, const struct usb_temp_config_desc *tcd) { struct usb_config_descriptor *cd; const struct usb_temp_interface_desc **tid; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*cd); /* Reset some counters */ temp->bInterfaceNumber = 0xFF; temp->bAlternateSetting = 0; /* Scan all the USB interfaces */ tid = tcd->ppIfaceDesc; if (tid) { while (*tid) { usb_make_interface_desc(temp, *tid); tid++; } } /* * Fill out the real USB config descriptor * in case there is a buffer present: */ if (temp->buf) { cd = USB_ADD_BYTES(temp->buf, old_size); /* compute total size */ old_size = temp->size - old_size; cd->bLength = sizeof(*cd); cd->bDescriptorType = UDESC_CONFIG; USETW(cd->wTotalLength, old_size); cd->bNumInterface = temp->bInterfaceNumber + 1; cd->bConfigurationValue = temp->bConfigurationValue; cd->iConfiguration = tcd->iConfiguration; cd->bmAttributes = tcd->bmAttributes; cd->bMaxPower = tcd->bMaxPower; cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED); if (temp->self_powered) { cd->bmAttributes |= UC_SELF_POWERED; } else { cd->bmAttributes &= ~UC_SELF_POWERED; } } } /*------------------------------------------------------------------------* * usb_make_device_desc * * This function will generate an USB device descriptor from the * given USB template device descriptor. *------------------------------------------------------------------------*/ static void usb_make_device_desc(struct usb_temp_setup *temp, const struct usb_temp_device_desc *tdd) { struct usb_temp_data *utd; const struct usb_temp_config_desc **tcd; uint16_t old_size; /* Reserve memory */ old_size = temp->size; temp->size += sizeof(*utd); /* Scan all the USB configs */ temp->bConfigurationValue = 1; tcd = tdd->ppConfigDesc; if (tcd) { while (*tcd) { usb_make_config_desc(temp, *tcd); temp->bConfigurationValue++; tcd++; } } /* * Fill out the real USB device descriptor * in case there is a buffer present: */ if (temp->buf) { utd = USB_ADD_BYTES(temp->buf, old_size); /* Store a pointer to our template device descriptor */ utd->tdd = tdd; /* Fill out USB device descriptor */ utd->udd.bLength = sizeof(utd->udd); utd->udd.bDescriptorType = UDESC_DEVICE; utd->udd.bDeviceClass = tdd->bDeviceClass; utd->udd.bDeviceSubClass = tdd->bDeviceSubClass; utd->udd.bDeviceProtocol = tdd->bDeviceProtocol; USETW(utd->udd.idVendor, tdd->idVendor); USETW(utd->udd.idProduct, tdd->idProduct); USETW(utd->udd.bcdDevice, tdd->bcdDevice); utd->udd.iManufacturer = tdd->iManufacturer; utd->udd.iProduct = tdd->iProduct; utd->udd.iSerialNumber = tdd->iSerialNumber; utd->udd.bNumConfigurations = temp->bConfigurationValue - 1; /* * Fill out the USB device qualifier. Pretend that we * don't support any other speeds by setting * "bNumConfigurations" equal to zero. That saves us * generating an extra set of configuration * descriptors. */ utd->udq.bLength = sizeof(utd->udq); utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER; utd->udq.bDeviceClass = tdd->bDeviceClass; utd->udq.bDeviceSubClass = tdd->bDeviceSubClass; utd->udq.bDeviceProtocol = tdd->bDeviceProtocol; utd->udq.bNumConfigurations = 0; USETW(utd->udq.bcdUSB, 0x0200); utd->udq.bMaxPacketSize0 = 0; switch (temp->usb_speed) { case USB_SPEED_LOW: USETW(utd->udd.bcdUSB, 0x0110); utd->udd.bMaxPacketSize = 8; break; case USB_SPEED_FULL: USETW(utd->udd.bcdUSB, 0x0110); utd->udd.bMaxPacketSize = 32; break; case USB_SPEED_HIGH: USETW(utd->udd.bcdUSB, 0x0200); utd->udd.bMaxPacketSize = 64; break; case USB_SPEED_VARIABLE: USETW(utd->udd.bcdUSB, 0x0250); utd->udd.bMaxPacketSize = 255; /* 512 bytes */ break; case USB_SPEED_SUPER: USETW(utd->udd.bcdUSB, 0x0300); utd->udd.bMaxPacketSize = 9; /* 2**9 = 512 bytes */ break; default: temp->err = USB_ERR_INVAL; break; } } } /*------------------------------------------------------------------------* * usb_hw_ep_match * * Return values: * 0: The endpoint profile does not match the criteria * Else: The endpoint profile matches the criteria *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_match(const struct usb_hw_ep_profile *pf, uint8_t ep_type, uint8_t ep_dir_in) { if (ep_type == UE_CONTROL) { /* special */ return (pf->support_control); } if ((pf->support_in && ep_dir_in) || (pf->support_out && !ep_dir_in)) { if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) || (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) || (pf->support_bulk && (ep_type == UE_BULK))) { return (1); } } return (0); } /*------------------------------------------------------------------------* * usb_hw_ep_find_match * * This function is used to find the best matching endpoint profile * for and endpoint belonging to an USB descriptor. * * Return values: * 0: Success. Got a match. * Else: Failure. No match. *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_find_match(struct usb_hw_ep_scratch *ues, struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex) { const struct usb_hw_ep_profile *pf; uint16_t distance; uint16_t temp; uint16_t max_frame_size; uint8_t n; uint8_t best_n; uint8_t dir_in; uint8_t dir_out; distance = 0xFFFF; best_n = 0; if ((!ep->needs_in) && (!ep->needs_out)) { return (0); /* we are done */ } if (ep->needs_ep_type == UE_CONTROL) { dir_in = 1; dir_out = 1; } else { if (ep->needs_in) { dir_in = 1; dir_out = 0; } else { dir_in = 0; dir_out = 1; } } for (n = 1; n != (USB_EP_MAX / 2); n++) { /* get HW endpoint profile */ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n); if (pf == NULL) { /* end of profiles */ break; } /* check if IN-endpoint is reserved */ if (dir_in || pf->is_simplex) { if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) { /* mismatch */ continue; } } /* check if OUT-endpoint is reserved */ if (dir_out || pf->is_simplex) { if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) { /* mismatch */ continue; } } /* check simplex */ if (pf->is_simplex == is_simplex) { /* mismatch */ continue; } /* check if HW endpoint matches */ if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) { /* mismatch */ continue; } /* get maximum frame size */ if (dir_in) max_frame_size = pf->max_in_frame_size; else max_frame_size = pf->max_out_frame_size; /* check if we have a matching profile */ if (max_frame_size >= ep->max_frame_size) { temp = (max_frame_size - ep->max_frame_size); if (distance > temp) { distance = temp; best_n = n; ep->pf = pf; } } } /* see if we got a match */ if (best_n != 0) { /* get the correct profile */ pf = ep->pf; /* reserve IN-endpoint */ if (dir_in) { ues->bmInAlloc[best_n / 8] |= (1 << (best_n % 8)); ep->hw_endpoint_in = best_n | UE_DIR_IN; ep->needs_in = 0; } /* reserve OUT-endpoint */ if (dir_out) { ues->bmOutAlloc[best_n / 8] |= (1 << (best_n % 8)); ep->hw_endpoint_out = best_n | UE_DIR_OUT; ep->needs_out = 0; } return (0); /* got a match */ } return (1); /* failure */ } /*------------------------------------------------------------------------* * usb_hw_ep_get_needs * * This function will figure out the type and number of endpoints * which are needed for an USB configuration. * * Return values: * 0: Success. * Else: Failure. *------------------------------------------------------------------------*/ static uint8_t usb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues, uint8_t ep_type, uint8_t is_complete) { const struct usb_hw_ep_profile *pf; struct usb_hw_ep_scratch_sub *ep_iface; struct usb_hw_ep_scratch_sub *ep_curr; struct usb_hw_ep_scratch_sub *ep_max; struct usb_hw_ep_scratch_sub *ep_end; struct usb_descriptor *desc; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; enum usb_dev_speed speed; uint16_t wMaxPacketSize; uint16_t temp; uint8_t ep_no; ep_iface = ues->ep_max; ep_curr = ues->ep_max; ep_end = ues->ep + USB_EP_MAX; ep_max = ues->ep_max; desc = NULL; speed = usbd_get_speed(ues->udev); repeat: while ((desc = usb_desc_foreach(ues->cd, desc))) { if ((desc->bDescriptorType == UDESC_INTERFACE) && (desc->bLength >= sizeof(*id))) { id = (void *)desc; if (id->bAlternateSetting == 0) { /* going forward */ ep_iface = ep_max; } else { /* reset */ ep_curr = ep_iface; } } if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (void *)desc; goto handle_endpoint_desc; } } ues->ep_max = ep_max; return (0); handle_endpoint_desc: temp = (ed->bmAttributes & UE_XFERTYPE); if (temp == ep_type) { if (ep_curr == ep_end) { /* too many endpoints */ return (1); /* failure */ } wMaxPacketSize = UGETW(ed->wMaxPacketSize); if ((wMaxPacketSize & 0xF800) && (speed == USB_SPEED_HIGH)) { /* handle packet multiplier */ temp = (wMaxPacketSize >> 11) & 3; wMaxPacketSize &= 0x7FF; if (temp == 1) { wMaxPacketSize *= 2; } else { wMaxPacketSize *= 3; } } /* * Check if we have a fixed endpoint number, else the * endpoint number is allocated dynamically: */ ep_no = (ed->bEndpointAddress & UE_ADDR); if (ep_no != 0) { /* get HW endpoint profile */ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, ep_no); if (pf == NULL) { /* HW profile does not exist - failure */ DPRINTFN(0, "Endpoint profile %u " "does not exist\n", ep_no); return (1); } /* reserve fixed endpoint number */ if (ep_type == UE_CONTROL) { ues->bmInAlloc[ep_no / 8] |= (1 << (ep_no % 8)); ues->bmOutAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if ((pf->max_in_frame_size < wMaxPacketSize) || (pf->max_out_frame_size < wMaxPacketSize)) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } else if (ed->bEndpointAddress & UE_DIR_IN) { ues->bmInAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if (pf->max_in_frame_size < wMaxPacketSize) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } else { ues->bmOutAlloc[ep_no / 8] |= (1 << (ep_no % 8)); if (pf->max_out_frame_size < wMaxPacketSize) { DPRINTFN(0, "Endpoint profile %u " "has too small buffer\n", ep_no); return (1); } } } else if (is_complete) { /* check if we have enough buffer space */ if (wMaxPacketSize > ep_curr->max_frame_size) { return (1); /* failure */ } if (ed->bEndpointAddress & UE_DIR_IN) { ed->bEndpointAddress = ep_curr->hw_endpoint_in; } else { ed->bEndpointAddress = ep_curr->hw_endpoint_out; } } else { /* compute the maximum frame size */ if (ep_curr->max_frame_size < wMaxPacketSize) { ep_curr->max_frame_size = wMaxPacketSize; } if (temp == UE_CONTROL) { ep_curr->needs_in = 1; ep_curr->needs_out = 1; } else { if (ed->bEndpointAddress & UE_DIR_IN) { ep_curr->needs_in = 1; } else { ep_curr->needs_out = 1; } } ep_curr->needs_ep_type = ep_type; } ep_curr++; if (ep_max < ep_curr) { ep_max = ep_curr; } } goto repeat; } /*------------------------------------------------------------------------* * usb_hw_ep_resolve * * This function will try to resolve endpoint requirements by the * given endpoint profiles that the USB hardware reports. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_hw_ep_resolve(struct usb_device *udev, struct usb_descriptor *desc) { struct usb_hw_ep_scratch *ues; struct usb_hw_ep_scratch_sub *ep; const struct usb_hw_ep_profile *pf; const struct usb_bus_methods *methods; struct usb_device_descriptor *dd; uint16_t mps; if (desc == NULL) return (USB_ERR_INVAL); /* get bus methods */ methods = udev->bus->methods; if (methods->get_hw_ep_profile == NULL) return (USB_ERR_INVAL); if (desc->bDescriptorType == UDESC_DEVICE) { if (desc->bLength < sizeof(*dd)) return (USB_ERR_INVAL); dd = (void *)desc; /* get HW control endpoint 0 profile */ (methods->get_hw_ep_profile) (udev, &pf, 0); if (pf == NULL) { return (USB_ERR_INVAL); } if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) { DPRINTFN(0, "Endpoint 0 does not " "support control\n"); return (USB_ERR_INVAL); } mps = dd->bMaxPacketSize; if (udev->speed == USB_SPEED_FULL) { /* * We can optionally choose another packet size ! */ while (1) { /* check if "mps" is ok */ if (pf->max_in_frame_size >= mps) { break; } /* reduce maximum packet size */ mps /= 2; /* check if "mps" is too small */ if (mps < 8) { return (USB_ERR_INVAL); } } dd->bMaxPacketSize = mps; } else { /* We only have one choice */ if (mps == 255) { mps = 512; } /* Check if we support the specified wMaxPacketSize */ if (pf->max_in_frame_size < mps) { return (USB_ERR_INVAL); } } return (0); /* success */ } if (desc->bDescriptorType != UDESC_CONFIG) return (USB_ERR_INVAL); if (desc->bLength < sizeof(*(ues->cd))) return (USB_ERR_INVAL); ues = udev->scratch.hw_ep_scratch; memset(ues, 0, sizeof(*ues)); ues->ep_max = ues->ep; ues->cd = (void *)desc; ues->methods = methods; ues->udev = udev; /* Get all the endpoints we need */ if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) || usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) || usb_hw_ep_get_needs(ues, UE_CONTROL, 0) || usb_hw_ep_get_needs(ues, UE_BULK, 0)) { DPRINTFN(0, "Could not get needs\n"); return (USB_ERR_INVAL); } for (ep = ues->ep; ep != ues->ep_max; ep++) { while (ep->needs_in || ep->needs_out) { /* * First try to use a simplex endpoint. * Then try to use a duplex endpoint. */ if (usb_hw_ep_find_match(ues, ep, 1) && usb_hw_ep_find_match(ues, ep, 0)) { DPRINTFN(0, "Could not find match\n"); return (USB_ERR_INVAL); } } } ues->ep_max = ues->ep; /* Update all endpoint addresses */ if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) || usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) || usb_hw_ep_get_needs(ues, UE_CONTROL, 1) || usb_hw_ep_get_needs(ues, UE_BULK, 1)) { DPRINTFN(0, "Could not update endpoint address\n"); return (USB_ERR_INVAL); } return (0); /* success */ } /*------------------------------------------------------------------------* * usb_temp_get_tdd * * Returns: * NULL: No USB template device descriptor found. * Else: Pointer to the USB template device descriptor. *------------------------------------------------------------------------*/ static const struct usb_temp_device_desc * usb_temp_get_tdd(struct usb_device *udev) { if (udev->usb_template_ptr == NULL) { return (NULL); } return (udev->usb_template_ptr->tdd); } /*------------------------------------------------------------------------* * usb_temp_get_device_desc * * Returns: * NULL: No USB device descriptor found. * Else: Pointer to USB device descriptor. *------------------------------------------------------------------------*/ static void * usb_temp_get_device_desc(struct usb_device *udev) { struct usb_device_descriptor *dd; if (udev->usb_template_ptr == NULL) { return (NULL); } dd = &udev->usb_template_ptr->udd; if (dd->bDescriptorType != UDESC_DEVICE) { /* sanity check failed */ return (NULL); } return (dd); } /*------------------------------------------------------------------------* * usb_temp_get_qualifier_desc * * Returns: * NULL: No USB device_qualifier descriptor found. * Else: Pointer to USB device_qualifier descriptor. *------------------------------------------------------------------------*/ static void * usb_temp_get_qualifier_desc(struct usb_device *udev) { struct usb_device_qualifier *dq; if (udev->usb_template_ptr == NULL) { return (NULL); } dq = &udev->usb_template_ptr->udq; if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) { /* sanity check failed */ return (NULL); } return (dq); } /*------------------------------------------------------------------------* * usb_temp_get_config_desc * * Returns: * NULL: No USB config descriptor found. * Else: Pointer to USB config descriptor having index "index". *------------------------------------------------------------------------*/ static void * usb_temp_get_config_desc(struct usb_device *udev, uint16_t *pLength, uint8_t index) { struct usb_device_descriptor *dd; struct usb_config_descriptor *cd; uint16_t temp; if (udev->usb_template_ptr == NULL) { return (NULL); } dd = &udev->usb_template_ptr->udd; cd = (void *)(udev->usb_template_ptr + 1); if (index >= dd->bNumConfigurations) { /* out of range */ return (NULL); } while (index--) { if (cd->bDescriptorType != UDESC_CONFIG) { /* sanity check failed */ return (NULL); } temp = UGETW(cd->wTotalLength); cd = USB_ADD_BYTES(cd, temp); } if (pLength) { *pLength = UGETW(cd->wTotalLength); } return (cd); } /*------------------------------------------------------------------------* * usb_temp_get_vendor_desc * * Returns: * NULL: No vendor descriptor found. * Else: Pointer to a vendor descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_vendor_desc(struct usb_device *udev, const struct usb_device_request *req, uint16_t *plen) { const struct usb_temp_device_desc *tdd; tdd = usb_temp_get_tdd(udev); if (tdd == NULL) { return (NULL); } if (tdd->getVendorDesc == NULL) { return (NULL); } return ((tdd->getVendorDesc) (req, plen)); } /*------------------------------------------------------------------------* * usb_temp_get_string_desc * * Returns: * NULL: No string descriptor found. * Else: Pointer to a string descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_string_desc(struct usb_device *udev, uint16_t lang_id, uint8_t string_index) { const struct usb_temp_device_desc *tdd; tdd = usb_temp_get_tdd(udev); if (tdd == NULL) { return (NULL); } if (tdd->getStringDesc == NULL) { return (NULL); } return ((tdd->getStringDesc) (lang_id, string_index)); } /*------------------------------------------------------------------------* * usb_temp_get_hub_desc * * Returns: * NULL: No USB HUB descriptor found. * Else: Pointer to a USB HUB descriptor. *------------------------------------------------------------------------*/ static const void * usb_temp_get_hub_desc(struct usb_device *udev) { return (NULL); /* needs to be implemented */ } /*------------------------------------------------------------------------* * usb_temp_get_desc * * This function is a demultiplexer for local USB device side control * endpoint requests. *------------------------------------------------------------------------*/ static usb_error_t usb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req, const void **pPtr, uint16_t *pLength) { const uint8_t *buf; uint16_t len; buf = NULL; len = 0; switch (req->bmRequestType) { case UT_READ_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; default: goto tr_stalled; } case UT_READ_CLASS_DEVICE: switch (req->bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; default: goto tr_stalled; } default: goto tr_stalled; } tr_handle_get_descriptor: switch (req->wValue[1]) { case UDESC_DEVICE: if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_device_desc(udev); goto tr_valid; case UDESC_DEVICE_QUALIFIER: if (udev->speed != USB_SPEED_HIGH) { goto tr_stalled; } if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_qualifier_desc(udev); goto tr_valid; case UDESC_OTHER_SPEED_CONFIGURATION: if (udev->speed != USB_SPEED_HIGH) { goto tr_stalled; } case UDESC_CONFIG: buf = usb_temp_get_config_desc(udev, &len, req->wValue[0]); goto tr_valid; case UDESC_STRING: buf = usb_temp_get_string_desc(udev, UGETW(req->wIndex), req->wValue[0]); goto tr_valid; default: goto tr_stalled; } tr_handle_get_class_descriptor: if (req->wValue[0]) { goto tr_stalled; } buf = usb_temp_get_hub_desc(udev); goto tr_valid; tr_valid: if (buf == NULL) goto tr_stalled; if (len == 0) len = buf[0]; *pPtr = buf; *pLength = len; return (0); /* success */ tr_stalled: /* try to get a vendor specific descriptor */ len = 0; buf = usb_temp_get_vendor_desc(udev, req, &len); if (buf != NULL) goto tr_valid; *pPtr = NULL; *pLength = 0; return (0); /* we ignore failures */ } /*------------------------------------------------------------------------* * usb_temp_setup * * This function generates USB descriptors according to the given USB * template device descriptor. It will also try to figure out the best * matching endpoint addresses using the hardware endpoint profiles. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_temp_setup(struct usb_device *udev, const struct usb_temp_device_desc *tdd) { struct usb_temp_setup *uts; void *buf; usb_error_t error; uint8_t n; uint8_t do_unlock; /* be NULL safe */ if (tdd == NULL) return (0); /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); uts = udev->scratch.temp_setup; memset(uts, 0, sizeof(*uts)); uts->usb_speed = udev->speed; uts->self_powered = udev->flags.self_powered; /* first pass */ usb_make_device_desc(uts, tdd); if (uts->err) { /* some error happened */ goto done; } /* sanity check */ if (uts->size == 0) { uts->err = USB_ERR_INVAL; goto done; } /* allocate zeroed memory */ uts->buf = usbd_alloc_config_desc(udev, uts->size); /* * Allow malloc() to return NULL regardless of M_WAITOK flag. * This helps when porting the software to non-FreeBSD * systems. */ if (uts->buf == NULL) { /* could not allocate memory */ uts->err = USB_ERR_NOMEM; goto done; } /* second pass */ uts->size = 0; usb_make_device_desc(uts, tdd); /* * Store a pointer to our descriptors: */ udev->usb_template_ptr = uts->buf; if (uts->err) { /* some error happened during second pass */ goto done; } /* * Resolve all endpoint addresses ! */ buf = usb_temp_get_device_desc(udev); uts->err = usb_hw_ep_resolve(udev, buf); if (uts->err) { DPRINTFN(0, "Could not resolve endpoints for " "Device Descriptor, error = %s\n", usbd_errstr(uts->err)); goto done; } for (n = 0;; n++) { buf = usb_temp_get_config_desc(udev, NULL, n); if (buf == NULL) { break; } uts->err = usb_hw_ep_resolve(udev, buf); if (uts->err) { DPRINTFN(0, "Could not resolve endpoints for " "Config Descriptor %u, error = %s\n", n, usbd_errstr(uts->err)); goto done; } } done: error = uts->err; if (error) usb_temp_unsetup(udev); if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usb_temp_unsetup * * This function frees any memory associated with the currently * setup template, if any. *------------------------------------------------------------------------*/ void usb_temp_unsetup(struct usb_device *udev) { usbd_free_config_desc(udev, udev->usb_template_ptr); udev->usb_template_ptr = NULL; } static usb_error_t usb_temp_setup_by_index(struct usb_device *udev, uint16_t index) { usb_error_t err; switch (index) { case USB_TEMP_MSC: err = usb_temp_setup(udev, &usb_template_msc); break; case USB_TEMP_CDCE: err = usb_temp_setup(udev, &usb_template_cdce); break; case USB_TEMP_MTP: err = usb_temp_setup(udev, &usb_template_mtp); break; case USB_TEMP_MODEM: err = usb_temp_setup(udev, &usb_template_modem); break; case USB_TEMP_AUDIO: err = usb_temp_setup(udev, &usb_template_audio); break; case USB_TEMP_KBD: err = usb_temp_setup(udev, &usb_template_kbd); break; case USB_TEMP_MOUSE: err = usb_temp_setup(udev, &usb_template_mouse); break; case USB_TEMP_PHONE: err = usb_temp_setup(udev, &usb_template_phone); break; case USB_TEMP_SERIALNET: err = usb_temp_setup(udev, &usb_template_serialnet); break; case USB_TEMP_MIDI: err = usb_temp_setup(udev, &usb_template_midi); break; default: return (USB_ERR_INVAL); } return (err); } static void usb_temp_init(void *arg) { /* register our functions */ usb_temp_get_desc_p = &usb_temp_get_desc; usb_temp_setup_by_index_p = &usb_temp_setup_by_index; usb_temp_unsetup_p = &usb_temp_unsetup; } SYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL); SYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL); Index: head/sys/dev/usb/usb_device.c =================================================================== --- head/sys/dev/usb/usb_device.c (revision 305420) +++ head/sys/dev/usb/usb_device.c (revision 305421) @@ -1,2911 +1,2947 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #endif #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #include #include #endif #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /* function prototypes */ static void usb_init_endpoint(struct usb_device *, uint8_t, struct usb_endpoint_descriptor *, struct usb_endpoint_ss_comp_descriptor *, struct usb_endpoint *); static void usb_unconfigure(struct usb_device *, uint8_t); static void usb_detach_device_sub(struct usb_device *, device_t *, char **, uint8_t); static uint8_t usb_probe_and_attach_sub(struct usb_device *, struct usb_attach_arg *); static void usb_init_attach_arg(struct usb_device *, struct usb_attach_arg *); static void usb_suspend_resume_sub(struct usb_device *, device_t, uint8_t); static usb_proc_callback_t usbd_clear_stall_proc; static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t); static void usbd_set_device_strings(struct usb_device *); #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *); #endif #if USB_HAVE_UGEN static void usb_fifo_free_wrap(struct usb_device *, uint8_t, uint8_t); static void usb_cdev_create(struct usb_device *); static void usb_cdev_free(struct usb_device *); #endif /* This variable is global to allow easy access to it: */ #ifdef USB_TEMPLATE int usb_template = USB_TEMPLATE; #else int usb_template; #endif SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RWTUN, &usb_template, 0, "Selected USB device side template"); /* English is default language */ static int usb_lang_id = 0x0009; static int usb_lang_mask = 0x00FF; SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_id, CTLFLAG_RWTUN, &usb_lang_id, 0, "Preferred USB language ID"); SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_mask, CTLFLAG_RWTUN, &usb_lang_mask, 0, "Preferred USB language mask"); static const char* statestr[USB_STATE_MAX] = { [USB_STATE_DETACHED] = "DETACHED", [USB_STATE_ATTACHED] = "ATTACHED", [USB_STATE_POWERED] = "POWERED", [USB_STATE_ADDRESSED] = "ADDRESSED", [USB_STATE_CONFIGURED] = "CONFIGURED", }; const char * usb_statestr(enum usb_dev_state state) { return ((state < USB_STATE_MAX) ? statestr[state] : "UNKNOWN"); } const char * usb_get_manufacturer(struct usb_device *udev) { return (udev->manufacturer ? udev->manufacturer : "Unknown"); } const char * usb_get_product(struct usb_device *udev) { return (udev->product ? udev->product : ""); } const char * usb_get_serial(struct usb_device *udev) { return (udev->serial ? udev->serial : ""); } /*------------------------------------------------------------------------* * usbd_get_ep_by_addr * * This function searches for an USB ep by endpoint address and * direction. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; enum { EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR), }; /* * According to the USB specification not all bits are used * for the endpoint address. Keep defined bits only: */ ea_val &= EA_MASK; /* * Iterate across all the USB endpoints searching for a match * based on the endpoint address: */ for (; ep != ep_end; ep++) { if (ep->edesc == NULL) { continue; } /* do the mask and check the value */ if ((ep->edesc->bEndpointAddress & EA_MASK) == ea_val) { goto found; } } /* * The default endpoint is always present and is checked separately: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & EA_MASK) == ea_val)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_get_endpoint * * This function searches for an USB endpoint based on the information * given by the passed "struct usb_config" pointer. * * Return values: * NULL: No match. * Else: Pointer to "struct usb_endpoint". *------------------------------------------------------------------------*/ struct usb_endpoint * usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup) { struct usb_endpoint *ep = udev->endpoints; struct usb_endpoint *ep_end = udev->endpoints + udev->endpoints_max; uint8_t index = setup->ep_index; uint8_t ea_mask; uint8_t ea_val; uint8_t type_mask; uint8_t type_val; DPRINTFN(10, "udev=%p iface_index=%d address=0x%x " "type=0x%x dir=0x%x index=%d\n", udev, iface_index, setup->endpoint, setup->type, setup->direction, setup->ep_index); /* check USB mode */ if (setup->usb_mode != USB_MODE_DUAL && udev->flags.usb_mode != setup->usb_mode) { /* wrong mode - no endpoint */ return (NULL); } /* setup expected endpoint direction mask and value */ if (setup->direction == UE_DIR_RX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_OUT : UE_DIR_IN; } else if (setup->direction == UE_DIR_TX) { ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (udev->flags.usb_mode == USB_MODE_DEVICE) ? UE_DIR_IN : UE_DIR_OUT; } else if (setup->direction == UE_DIR_ANY) { /* match any endpoint direction */ ea_mask = 0; ea_val = 0; } else { /* match the given endpoint direction */ ea_mask = (UE_DIR_IN | UE_DIR_OUT); ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT)); } /* setup expected endpoint address */ if (setup->endpoint == UE_ADDR_ANY) { /* match any endpoint address */ } else { /* match the given endpoint address */ ea_mask |= UE_ADDR; ea_val |= (setup->endpoint & UE_ADDR); } /* setup expected endpoint type */ if (setup->type == UE_BULK_INTR) { /* this will match BULK and INTERRUPT endpoints */ type_mask = 2; type_val = 2; } else if (setup->type == UE_TYPE_ANY) { /* match any endpoint type */ type_mask = 0; type_val = 0; } else { /* match the given endpoint type */ type_mask = UE_XFERTYPE; type_val = (setup->type & UE_XFERTYPE); } /* * Iterate across all the USB endpoints searching for a match * based on the endpoint address. Note that we are searching * the endpoints from the beginning of the "udev->endpoints" array. */ for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* do the masks and check the values */ if (((ep->edesc->bEndpointAddress & ea_mask) == ea_val) && ((ep->edesc->bmAttributes & type_mask) == type_val)) { if (!index--) { goto found; } } } /* * Match against default endpoint last, so that "any endpoint", "any * address" and "any direction" returns the first endpoint of the * interface. "iface_index" and "direction" is ignored: */ if ((udev->ctrl_ep.edesc != NULL) && ((udev->ctrl_ep.edesc->bEndpointAddress & ea_mask) == ea_val) && ((udev->ctrl_ep.edesc->bmAttributes & type_mask) == type_val) && (!index)) { ep = &udev->ctrl_ep; goto found; } return (NULL); found: return (ep); } /*------------------------------------------------------------------------* * usbd_interface_count * * This function stores the number of USB interfaces excluding * alternate settings, which the USB config descriptor reports into * the unsigned 8-bit integer pointed to by "count". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count) { if (udev->cdesc == NULL) { *count = 0; return (USB_ERR_NOT_CONFIGURED); } *count = udev->ifaces_max; return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usb_init_endpoint * * This function will initialise the USB endpoint structure pointed to by * the "endpoint" argument. The structure pointed to by "endpoint" must be * zeroed before calling this function. *------------------------------------------------------------------------*/ static void usb_init_endpoint(struct usb_device *udev, uint8_t iface_index, struct usb_endpoint_descriptor *edesc, struct usb_endpoint_ss_comp_descriptor *ecomp, struct usb_endpoint *ep) { const struct usb_bus_methods *methods; usb_stream_t x; methods = udev->bus->methods; (methods->endpoint_init) (udev, edesc, ep); /* initialise USB endpoint structure */ ep->edesc = edesc; ep->ecomp = ecomp; ep->iface_index = iface_index; /* setup USB stream queues */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { TAILQ_INIT(&ep->endpoint_q[x].head); ep->endpoint_q[x].command = &usbd_pipe_start; } /* the pipe is not supported by the hardware */ if (ep->methods == NULL) return; /* check for SUPER-speed streams mode endpoint */ if (udev->speed == USB_SPEED_SUPER && ecomp != NULL && (edesc->bmAttributes & UE_XFERTYPE) == UE_BULK && (UE_GET_BULK_STREAMS(ecomp->bmAttributes) != 0)) { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_STREAMS); } else { usbd_set_endpoint_mode(udev, ep, USB_EP_MODE_DEFAULT); } /* clear stall, if any */ if (methods->clear_stall != NULL) { USB_BUS_LOCK(udev->bus); (methods->clear_stall) (udev, ep); USB_BUS_UNLOCK(udev->bus); } } /*-----------------------------------------------------------------------* * usb_endpoint_foreach * * This function will iterate all the USB endpoints except the control * endpoint. This function is NULL safe. * * Return values: * NULL: End of USB endpoints * Else: Pointer to next USB endpoint *------------------------------------------------------------------------*/ struct usb_endpoint * usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep) { struct usb_endpoint *ep_end; /* be NULL safe */ if (udev == NULL) return (NULL); ep_end = udev->endpoints + udev->endpoints_max; /* get next endpoint */ if (ep == NULL) ep = udev->endpoints; else ep++; /* find next allocated ep */ while (ep != ep_end) { if (ep->edesc != NULL) return (ep); ep++; } return (NULL); } /*------------------------------------------------------------------------* * usb_wait_pending_refs * * This function will wait for any USB references to go away before * returning. This function is used before freeing a USB device. *------------------------------------------------------------------------*/ static void usb_wait_pending_refs(struct usb_device *udev) { #if USB_HAVE_UGEN DPRINTF("Refcount = %d\n", (int)udev->refcount); mtx_lock(&usb_ref_lock); udev->refcount--; while (1) { /* wait for any pending references to go away */ if (udev->refcount == 0) { /* prevent further refs being taken, if any */ udev->refcount = USB_DEV_REF_MAX; break; } cv_wait(&udev->ref_cv, &usb_ref_lock); } mtx_unlock(&usb_ref_lock); #endif } /*------------------------------------------------------------------------* * usb_unconfigure * * This function will free all USB interfaces and USB endpoints belonging * to an USB device. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_unconfigure(struct usb_device *udev, uint8_t flag) { uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); /* detach all interface drivers */ usb_detach_device(udev, USB_IFACE_INDEX_ANY, flag); #if USB_HAVE_UGEN /* free all FIFOs except control endpoint FIFOs */ usb_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, flag); /* * Free all cdev's, if any. */ usb_cdev_free(udev); #endif #if USB_HAVE_COMPAT_LINUX /* free Linux compat device, if any */ if (udev->linux_endpoint_start != NULL) { usb_linux_free_device_p(udev); udev->linux_endpoint_start = NULL; } #endif usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_FREE); /* free "cdesc" after "ifaces" and "endpoints", if any */ if (udev->cdesc != NULL) { if (udev->flags.usb_mode != USB_MODE_DEVICE) usbd_free_config_desc(udev, udev->cdesc); udev->cdesc = NULL; } /* set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; if (do_unlock) usbd_enum_unlock(udev); } /*------------------------------------------------------------------------* * usbd_set_config_index * * This function selects configuration by index, independent of the * actual configuration number. This function should not be used by * USB drivers. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_config_index(struct usb_device *udev, uint8_t index) { struct usb_status ds; struct usb_config_descriptor *cdp; uint16_t power; uint16_t max_power; uint8_t selfpowered; uint8_t do_unlock; usb_error_t err; DPRINTFN(6, "udev=%p index=%d\n", udev, index); /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); usb_unconfigure(udev, 0); if (index == USB_UNCONFIG_INDEX) { /* * Leave unallocated when unconfiguring the * device. "usb_unconfigure()" will also reset * the current config number and index. */ err = usbd_req_set_config(udev, NULL, USB_UNCONFIG_NO); if (udev->state == USB_STATE_CONFIGURED) usb_set_device_state(udev, USB_STATE_ADDRESSED); goto done; } /* get the full config descriptor */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* save some memory */ err = usbd_req_get_descriptor_ptr(udev, &cdp, (UDESC_CONFIG << 8) | index); } else { /* normal request */ err = usbd_req_get_config_desc_full(udev, NULL, &cdp, index); } if (err) { goto done; } /* set the new config descriptor */ udev->cdesc = cdp; /* Figure out if the device is self or bus powered. */ selfpowered = 0; if ((!udev->flags.uq_bus_powered) && (cdp->bmAttributes & UC_SELF_POWERED) && (udev->flags.usb_mode == USB_MODE_HOST)) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ err = usbd_req_get_device_status(udev, NULL, &ds); if (err) { DPRINTFN(0, "could not read " "device status: %s\n", usbd_errstr(err)); } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) { selfpowered = 1; } DPRINTF("status=0x%04x \n", UGETW(ds.wStatus)); } else selfpowered = 1; } DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, " "selfpowered=%d, power=%d\n", udev, cdp, udev->address, cdp->bConfigurationValue, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2); /* Check if we have enough power. */ power = cdp->bMaxPower * 2; if (udev->parent_hub) { max_power = udev->parent_hub->hub->portpower; } else { max_power = USB_MAX_POWER; } if (power > max_power) { DPRINTFN(0, "power exceeded %d > %d\n", power, max_power); err = USB_ERR_NO_POWER; goto done; } /* Only update "self_powered" in USB Host Mode */ if (udev->flags.usb_mode == USB_MODE_HOST) { udev->flags.self_powered = selfpowered; } udev->power = power; udev->curr_config_no = cdp->bConfigurationValue; udev->curr_config_index = index; usb_set_device_state(udev, USB_STATE_CONFIGURED); /* Set the actual configuration value. */ err = usbd_req_set_config(udev, NULL, cdp->bConfigurationValue); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_ALLOC); if (err) { goto done; } err = usb_config_parse(udev, USB_IFACE_INDEX_ANY, USB_CFG_INIT); if (err) { goto done; } #if USB_HAVE_UGEN /* create device nodes for each endpoint */ usb_cdev_create(udev); #endif done: DPRINTF("error=%s\n", usbd_errstr(err)); if (err) { usb_unconfigure(udev, 0); } if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usb_config_parse * * This function will allocate and free USB interfaces and USB endpoints, * parse the USB configuration structure and initialise the USB endpoints * and interfaces. If "iface_index" is not equal to * "USB_IFACE_INDEX_ANY" then the "cmd" parameter is the * alternate_setting to be selected for the given interface. Else the * "cmd" parameter is defined by "USB_CFG_XXX". "iface_index" can be * "USB_IFACE_INDEX_ANY" or a valid USB interface index. This function * is typically called when setting the configuration or when setting * an alternate interface. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) { struct usb_idesc_parse_state ips; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; struct usb_interface *iface; struct usb_endpoint *ep; usb_error_t err; uint8_t ep_curr; uint8_t ep_max; uint8_t temp; uint8_t do_init; uint8_t alt_index; if (iface_index != USB_IFACE_INDEX_ANY) { /* parameter overload */ alt_index = cmd; cmd = USB_CFG_INIT; } else { /* not used */ alt_index = 0; } err = 0; DPRINTFN(5, "iface_index=%d cmd=%d\n", iface_index, cmd); if (cmd == USB_CFG_FREE) goto cleanup; if (cmd == USB_CFG_INIT) { sx_assert(&udev->enum_sx, SA_LOCKED); /* check for in-use endpoints */ ep = udev->endpoints; ep_max = udev->endpoints_max; while (ep_max--) { /* look for matching endpoints */ if ((iface_index == USB_IFACE_INDEX_ANY) || (iface_index == ep->iface_index)) { if (ep->refcount_alloc != 0) { /* * This typically indicates a * more serious error. */ err = USB_ERR_IN_USE; } else { /* reset endpoint */ memset(ep, 0, sizeof(*ep)); /* make sure we don't zero the endpoint again */ ep->iface_index = USB_IFACE_INDEX_ANY; } } ep++; } if (err) return (err); } memset(&ips, 0, sizeof(ips)); ep_curr = 0; ep_max = 0; while ((id = usb_idesc_foreach(udev->cdesc, &ips))) { iface = udev->ifaces + ips.iface_index; /* check for specific interface match */ if (cmd == USB_CFG_INIT) { if ((iface_index != USB_IFACE_INDEX_ANY) && (iface_index != ips.iface_index)) { /* wrong interface */ do_init = 0; } else if (alt_index != ips.iface_index_alt) { /* wrong alternate setting */ do_init = 0; } else { /* initialise interface */ do_init = 1; } } else do_init = 0; /* check for new interface */ if (ips.iface_index_alt == 0) { /* update current number of endpoints */ ep_curr = ep_max; } /* check for init */ if (do_init) { /* setup the USB interface structure */ iface->idesc = id; /* set alternate index */ iface->alt_index = alt_index; /* set default interface parent */ if (iface_index == USB_IFACE_INDEX_ANY) { iface->parent_iface_index = USB_IFACE_INDEX_ANY; } } DPRINTFN(5, "found idesc nendpt=%d\n", id->bNumEndpoints); ed = (struct usb_endpoint_descriptor *)id; temp = ep_curr; /* iterate all the endpoint descriptors */ while ((ed = usb_edesc_foreach(udev->cdesc, ed))) { /* check if endpoint limit has been reached */ if (temp >= USB_MAX_EP_UNITS) { DPRINTF("Endpoint limit reached\n"); break; } ep = udev->endpoints + temp; if (do_init) { void *ecomp; ecomp = usb_ed_comp_foreach(udev->cdesc, (void *)ed); if (ecomp != NULL) DPRINTFN(5, "Found endpoint companion descriptor\n"); usb_init_endpoint(udev, ips.iface_index, ed, ecomp, ep); } temp ++; /* find maximum number of endpoints */ if (ep_max < temp) ep_max = temp; } } /* NOTE: It is valid to have no interfaces and no endpoints! */ if (cmd == USB_CFG_ALLOC) { udev->ifaces_max = ips.iface_index; #if (USB_HAVE_FIXED_IFACE == 0) udev->ifaces = NULL; if (udev->ifaces_max != 0) { udev->ifaces = malloc(sizeof(*iface) * udev->ifaces_max, M_USB, M_WAITOK | M_ZERO); if (udev->ifaces == NULL) { err = USB_ERR_NOMEM; goto done; } } #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) if (ep_max != 0) { udev->endpoints = malloc(sizeof(*ep) * ep_max, M_USB, M_WAITOK | M_ZERO); if (udev->endpoints == NULL) { err = USB_ERR_NOMEM; goto done; } } else { udev->endpoints = NULL; } #endif USB_BUS_LOCK(udev->bus); udev->endpoints_max = ep_max; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); } #if (USB_HAVE_FIXED_IFACE == 0) || (USB_HAVE_FIXED_ENDPOINT == 0) done: #endif if (err) { if (cmd == USB_CFG_ALLOC) { cleanup: USB_BUS_LOCK(udev->bus); udev->endpoints_max = 0; /* reset any ongoing clear-stall */ udev->ep_curr = NULL; USB_BUS_UNLOCK(udev->bus); #if (USB_HAVE_FIXED_IFACE == 0) free(udev->ifaces, M_USB); udev->ifaces = NULL; #endif #if (USB_HAVE_FIXED_ENDPOINT == 0) free(udev->endpoints, M_USB); udev->endpoints = NULL; #endif udev->ifaces_max = 0; } } return (err); } /*------------------------------------------------------------------------* * usbd_set_alt_interface_index * * This function will select an alternate interface index for the * given interface index. The interface should not be in use when this * function is called. That means there should not be any open USB * transfers. Else an error is returned. If the alternate setting is * already set this function will simply return success. This function * is called in Host mode and Device mode! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); usb_error_t err; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (iface == NULL) { err = USB_ERR_INVAL; goto done; } if (iface->alt_index == alt_index) { /* * Optimise away duplicate setting of * alternate setting in USB Host Mode! */ err = 0; goto done; } #if USB_HAVE_UGEN /* * Free all generic FIFOs for this interface, except control * endpoint FIFOs: */ usb_fifo_free_wrap(udev, iface_index, 0); #endif err = usb_config_parse(udev, iface_index, alt_index); if (err) { goto done; } if (iface->alt_index != alt_index) { /* the alternate setting does not exist */ err = USB_ERR_INVAL; goto done; } err = usbd_req_set_alt_interface_no(udev, NULL, iface_index, iface->idesc->bAlternateSetting); done: if (do_unlock) usbd_enum_unlock(udev); return (err); } /*------------------------------------------------------------------------* * usbd_set_endpoint_stall * * This function is used to make a BULK or INTERRUPT endpoint send * STALL tokens in USB device mode. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_set_endpoint_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t do_stall) { struct usb_xfer *xfer; usb_stream_t x; uint8_t et; uint8_t was_stalled; if (ep == NULL) { /* nothing to do */ DPRINTF("Cannot find endpoint\n"); /* * Pretend that the clear or set stall request is * successful else some USB host stacks can do * strange things, especially when a control endpoint * stalls. */ return (0); } et = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((et != UE_BULK) && (et != UE_INTERRUPT)) { /* * Should not stall control * nor isochronous endpoints. */ DPRINTF("Invalid endpoint\n"); return (0); } USB_BUS_LOCK(udev->bus); /* store current stall state */ was_stalled = ep->is_stalled; /* check for no change */ if (was_stalled && do_stall) { /* if the endpoint is already stalled do nothing */ USB_BUS_UNLOCK(udev->bus); DPRINTF("No change\n"); return (0); } /* set stalled state */ ep->is_stalled = 1; if (do_stall || (!was_stalled)) { if (!was_stalled) { for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* lookup the current USB transfer, if any */ xfer = ep->endpoint_q[x].curr; if (xfer != NULL) { /* * The "xfer_stall" method * will complete the USB * transfer like in case of a * timeout setting the error * code "USB_ERR_STALLED". */ (udev->bus->methods->xfer_stall) (xfer); } } } (udev->bus->methods->set_stall) (udev, ep, &do_stall); } if (!do_stall) { ep->toggle_next = 0; /* reset data toggle */ ep->is_stalled = 0; /* clear stalled state */ (udev->bus->methods->clear_stall) (udev, ep); /* start the current or next transfer, if any */ for (x = 0; x != USB_MAX_EP_STREAMS; x++) { usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } USB_BUS_UNLOCK(udev->bus); return (0); } /*------------------------------------------------------------------------* * usb_reset_iface_endpoints - used in USB device side mode *------------------------------------------------------------------------*/ usb_error_t usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index) { struct usb_endpoint *ep; struct usb_endpoint *ep_end; ep = udev->endpoints; ep_end = udev->endpoints + udev->endpoints_max; for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != iface_index)) { continue; } /* simulate a clear stall from the peer */ usbd_set_endpoint_stall(udev, ep, 0); } return (0); } /*------------------------------------------------------------------------* * usb_detach_device_sub * * This function will try to detach an USB device. If it fails a panic * will result. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ static void usb_detach_device_sub(struct usb_device *udev, device_t *ppdev, char **ppnpinfo, uint8_t flag) { device_t dev; char *pnpinfo; int err; dev = *ppdev; if (dev) { /* * NOTE: It is important to clear "*ppdev" before deleting * the child due to some device methods being called late * during the delete process ! */ *ppdev = NULL; if (!rebooting) { device_printf(dev, "at %s, port %d, addr %d " "(disconnected)\n", device_get_nameunit(udev->parent_dev), udev->port_no, udev->address); } if (device_is_attached(dev)) { if (udev->flags.peer_suspended) { err = DEVICE_RESUME(dev); if (err) { device_printf(dev, "Resume failed\n"); } } if (device_detach(dev)) { goto error; } } if (device_delete_child(udev->parent_dev, dev)) { goto error; } } pnpinfo = *ppnpinfo; if (pnpinfo != NULL) { *ppnpinfo = NULL; free(pnpinfo, M_USBDEV); } return; error: /* Detach is not allowed to fail in the USB world */ panic("usb_detach_device_sub: A USB driver would not detach\n"); } /*------------------------------------------------------------------------* * usb_detach_device * * The following function will detach the matching interfaces. * This function is NULL safe. * * Flag values, see "USB_UNCFG_FLAG_XXX". *------------------------------------------------------------------------*/ void usb_detach_device(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return; } DPRINTFN(4, "udev=%p\n", udev); sx_assert(&udev->enum_sx, SA_LOCKED); /* * First detach the child to give the child's detach routine a * chance to detach the sub-devices in the correct order. * Then delete the child using "device_delete_child()" which * will detach all sub-devices from the bottom and upwards! */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; iface_index = i + 1; } else { i = 0; iface_index = USB_IFACE_MAX; } /* do the detach */ for (; i != iface_index; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_detach_device_sub(udev, &iface->subdev, &iface->pnpinfo, flag); } } /*------------------------------------------------------------------------* * usb_probe_and_attach_sub * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb_probe_and_attach_sub(struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; device_t dev; int err; iface = uaa->iface; if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) { /* leave interface alone */ return (0); } dev = iface->subdev; if (dev) { /* clean up after module unload */ if (device_is_attached(dev)) { /* already a device there */ return (0); } /* clear "iface->subdev" as early as possible */ iface->subdev = NULL; if (device_delete_child(udev->parent_dev, dev)) { /* * Panic here, else one can get a double call * to device_detach(). USB devices should * never fail on detach! */ panic("device_delete_child() failed\n"); } } if (uaa->temp_dev == NULL) { /* create a new child */ uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1); if (uaa->temp_dev == NULL) { device_printf(udev->parent_dev, "Device creation failed\n"); return (1); /* failure */ } device_set_ivars(uaa->temp_dev, uaa); device_quiet(uaa->temp_dev); } /* * Set "subdev" before probe and attach so that "devd" gets * the information it needs. */ iface->subdev = uaa->temp_dev; if (device_probe_and_attach(iface->subdev) == 0) { /* * The USB attach arguments are only available during probe * and attach ! */ uaa->temp_dev = NULL; device_set_ivars(iface->subdev, NULL); if (udev->flags.peer_suspended) { err = DEVICE_SUSPEND(iface->subdev); if (err) device_printf(iface->subdev, "Suspend failed\n"); } return (0); /* success */ } else { /* No USB driver found */ iface->subdev = NULL; } return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_set_parent_iface * * Using this function will lock the alternate interface setting on an * interface. It is typically used for multi interface drivers. In USB * device side mode it is assumed that the alternate interfaces all * have the same endpoint descriptors. The default parent index value * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not * locked. *------------------------------------------------------------------------*/ void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index) { struct usb_interface *iface; if (udev == NULL) { /* nothing to do */ return; } iface = usbd_get_iface(udev, iface_index); if (iface != NULL) iface->parent_iface_index = parent_index; } static void usb_init_attach_arg(struct usb_device *udev, struct usb_attach_arg *uaa) { memset(uaa, 0, sizeof(*uaa)); uaa->device = udev; uaa->usb_mode = udev->flags.usb_mode; uaa->port = udev->port_no; uaa->dev_state = UAA_DEV_READY; uaa->info.idVendor = UGETW(udev->ddesc.idVendor); uaa->info.idProduct = UGETW(udev->ddesc.idProduct); uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice); uaa->info.bDeviceClass = udev->ddesc.bDeviceClass; uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass; uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol; uaa->info.bConfigIndex = udev->curr_config_index; uaa->info.bConfigNum = udev->curr_config_no; } /*------------------------------------------------------------------------* * usb_probe_and_attach * * This function is called from "uhub_explore_sub()", * "usb_handle_set_config()" and "usb_handle_request()". * * Returns: * 0: Success * Else: A control transfer failed *------------------------------------------------------------------------*/ usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) { struct usb_attach_arg uaa; struct usb_interface *iface; uint8_t i; uint8_t j; uint8_t do_unlock; if (udev == NULL) { DPRINTF("udev == NULL\n"); return (USB_ERR_INVAL); } /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->curr_config_index == USB_UNCONFIG_INDEX) { /* do nothing - no configuration has been set */ goto done; } /* setup USB attach arguments */ usb_init_attach_arg(udev, &uaa); /* * If the whole USB device is targeted, invoke the USB event * handler(s): */ if (iface_index == USB_IFACE_INDEX_ANY) { if (usb_test_quirk(&uaa, UQ_MSC_DYMO_EJECT) != 0 && usb_dymo_eject(udev, 0) == 0) { /* success, mark the udev as disappearing */ uaa.dev_state = UAA_DEV_EJECTING; } EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa); if (uaa.dev_state != UAA_DEV_READY) { /* leave device unconfigured */ usb_unconfigure(udev, 0); goto done; } } /* Check if only one interface should be probed: */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; j = i + 1; } else { i = 0; j = USB_IFACE_MAX; } /* Do the probe and attach */ for (; i != j; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* * Looks like the end of the USB * interfaces ! */ DPRINTFN(2, "end of interfaces " "at %u\n", i); break; } if (iface->idesc == NULL) { /* no interface descriptor */ continue; } uaa.iface = iface; uaa.info.bInterfaceClass = iface->idesc->bInterfaceClass; uaa.info.bInterfaceSubClass = iface->idesc->bInterfaceSubClass; uaa.info.bInterfaceProtocol = iface->idesc->bInterfaceProtocol; uaa.info.bIfaceIndex = i; uaa.info.bIfaceNum = iface->idesc->bInterfaceNumber; uaa.driver_info = 0; /* reset driver_info */ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", uaa.info.bInterfaceClass, uaa.info.bInterfaceSubClass, uaa.info.bInterfaceProtocol, uaa.info.bIfaceIndex, uaa.info.bIfaceNum); usb_probe_and_attach_sub(udev, &uaa); /* * Remove the leftover child, if any, to enforce that * a new nomatch devd event is generated for the next * interface if no driver is found: */ if (uaa.temp_dev == NULL) continue; if (device_delete_child(udev->parent_dev, uaa.temp_dev)) DPRINTFN(0, "device delete child failed\n"); uaa.temp_dev = NULL; } done: if (do_unlock) usbd_enum_unlock(udev); return (0); } /*------------------------------------------------------------------------* * usb_suspend_resume_sub * * This function is called when the suspend or resume methods should * be executed on an USB device. *------------------------------------------------------------------------*/ static void usb_suspend_resume_sub(struct usb_device *udev, device_t dev, uint8_t do_suspend) { int err; if (dev == NULL) { return; } if (!device_is_attached(dev)) { return; } if (do_suspend) { err = DEVICE_SUSPEND(dev); } else { err = DEVICE_RESUME(dev); } if (err) { device_printf(dev, "%s failed\n", do_suspend ? "Suspend" : "Resume"); } } /*------------------------------------------------------------------------* * usb_suspend_resume * * The following function will suspend or resume the USB device. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend) { struct usb_interface *iface; uint8_t i; if (udev == NULL) { /* nothing to do */ return (0); } DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend); sx_assert(&udev->sr_sx, SA_LOCKED); USB_BUS_LOCK(udev->bus); /* filter the suspend events */ if (udev->flags.peer_suspended == do_suspend) { USB_BUS_UNLOCK(udev->bus); /* nothing to do */ return (0); } udev->flags.peer_suspended = do_suspend; USB_BUS_UNLOCK(udev->bus); /* do the suspend or resume */ for (i = 0; i != USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) { /* looks like the end of the USB interfaces */ break; } usb_suspend_resume_sub(udev, iface->subdev, do_suspend); } return (0); } /*------------------------------------------------------------------------* * usbd_clear_stall_proc * * This function performs generic USB clear stall operations. *------------------------------------------------------------------------*/ static void usbd_clear_stall_proc(struct usb_proc_msg *_pm) { struct usb_udev_msg *pm = (void *)_pm; struct usb_device *udev = pm->udev; /* Change lock */ USB_BUS_UNLOCK(udev->bus); mtx_lock(&udev->device_mtx); /* Start clear stall callback */ usbd_transfer_start(udev->ctrl_xfer[1]); /* Change lock */ mtx_unlock(&udev->device_mtx); USB_BUS_LOCK(udev->bus); } /*------------------------------------------------------------------------* * usb_alloc_device * * This function allocates a new USB device. This function is called * when a new device has been put in the powered state, but not yet in * the addressed state. Get initial descriptor, set the address, get * full descriptor and get strings. * * Return values: * 0: Failure * Else: Success *------------------------------------------------------------------------*/ struct usb_device * usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode) { struct usb_attach_arg uaa; struct usb_device *udev; struct usb_device *adev; struct usb_device *hub; uint8_t *scratch_ptr; usb_error_t err; uint8_t device_index; uint8_t config_index; uint8_t config_quirk; uint8_t set_config_failed; uint8_t do_unlock; DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, " "port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n", parent_dev, bus, parent_hub, depth, port_index, port_no, speed, mode); /* * Find an unused device index. In USB Host mode this is the * same as the device address. * * Device index zero is not used and device index 1 should * always be the root hub. */ for (device_index = USB_ROOT_HUB_ADDR; (device_index != bus->devices_max) && (bus->devices[device_index] != NULL); device_index++) /* nop */; if (device_index == bus->devices_max) { device_printf(bus->bdev, "No free USB device index for new device\n"); return (NULL); } if (depth > 0x10) { device_printf(bus->bdev, "Invalid device depth\n"); return (NULL); } udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO); if (udev == NULL) { return (NULL); } /* initialise our SX-lock */ sx_init_flags(&udev->enum_sx, "USB config SX lock", SX_DUPOK); sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_NOWITNESS); + sx_init_flags(&udev->ctrl_sx, "USB control transfer SX lock", SX_DUPOK); cv_init(&udev->ctrlreq_cv, "WCTRL"); cv_init(&udev->ref_cv, "UGONE"); /* initialise our mutex */ mtx_init(&udev->device_mtx, "USB device mutex", NULL, MTX_DEF); /* initialise generic clear stall */ udev->cs_msg[0].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[0].udev = udev; udev->cs_msg[1].hdr.pm_callback = &usbd_clear_stall_proc; udev->cs_msg[1].udev = udev; /* initialise some USB device fields */ udev->parent_hub = parent_hub; udev->parent_dev = parent_dev; udev->port_index = port_index; udev->port_no = port_no; udev->depth = depth; udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (usb_ticks_t)ticks; /* * We need to force the power mode to "on" because there are plenty * of USB devices out there that do not work very well with * automatic suspend and resume! */ udev->power_mode = usbd_filter_power_mode(udev, USB_POWER_MODE_ON); udev->pwr_save.last_xfer_time = ticks; /* we are not ready yet */ udev->refcount = 1; /* set up default endpoint descriptor */ udev->ctrl_ep_desc.bLength = sizeof(udev->ctrl_ep_desc); udev->ctrl_ep_desc.bDescriptorType = UDESC_ENDPOINT; udev->ctrl_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; udev->ctrl_ep_desc.bmAttributes = UE_CONTROL; udev->ctrl_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET; udev->ctrl_ep_desc.wMaxPacketSize[1] = 0; udev->ctrl_ep_desc.bInterval = 0; /* set up default endpoint companion descriptor */ udev->ctrl_ep_comp_desc.bLength = sizeof(udev->ctrl_ep_comp_desc); udev->ctrl_ep_comp_desc.bDescriptorType = UDESC_ENDPOINT_SS_COMP; udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; udev->speed = speed; udev->flags.usb_mode = mode; /* search for our High Speed USB HUB, if any */ adev = udev; hub = udev->parent_hub; while (hub) { if (hub->speed == USB_SPEED_HIGH) { udev->hs_hub_addr = hub->address; udev->parent_hs_hub = hub; udev->hs_port_no = adev->port_no; break; } adev = hub; hub = hub->parent_hub; } /* init the default endpoint */ usb_init_endpoint(udev, 0, &udev->ctrl_ep_desc, &udev->ctrl_ep_comp_desc, &udev->ctrl_ep); /* set device index */ udev->device_index = device_index; #if USB_HAVE_UGEN /* Create ugen name */ snprintf(udev->ugen_name, sizeof(udev->ugen_name), USB_GENERIC_NAME "%u.%u", device_get_unit(bus->bdev), device_index); LIST_INIT(&udev->pd_list); /* Create the control endpoint device */ udev->ctrl_dev = usb_make_dev(udev, NULL, 0, 0, FREAD|FWRITE, UID_ROOT, GID_OPERATOR, 0600); /* Create a link from /dev/ugenX.X to the default endpoint */ if (udev->ctrl_dev != NULL) make_dev_alias(udev->ctrl_dev->cdev, "%s", udev->ugen_name); #endif /* Initialise device */ if (bus->methods->device_init != NULL) { err = (bus->methods->device_init) (udev); if (err != 0) { DPRINTFN(0, "device init %d failed " "(%s, ignored)\n", device_index, usbd_errstr(err)); goto done; } } /* set powered device state after device init is complete */ usb_set_device_state(udev, USB_STATE_POWERED); if (udev->flags.usb_mode == USB_MODE_HOST) { err = usbd_req_set_address(udev, NULL, device_index); /* * This is the new USB device address from now on, if * the set address request didn't set it already. */ if (udev->address == USB_START_ADDR) udev->address = device_index; /* * We ignore any set-address errors, hence there are * buggy USB devices out there that actually receive * the SETUP PID, but manage to set the address before * the STATUS stage is ACK'ed. If the device responds * to the subsequent get-descriptor at the new * address, then we know that the set-address command * was successful. */ if (err) { DPRINTFN(0, "set address %d failed " "(%s, ignored)\n", udev->address, usbd_errstr(err)); } } else { /* We are not self powered */ udev->flags.self_powered = 0; /* Set unconfigured state */ udev->curr_config_no = USB_UNCONFIG_NO; udev->curr_config_index = USB_UNCONFIG_INDEX; /* Setup USB descriptors */ err = (usb_temp_setup_by_index_p) (udev, usb_template); if (err) { DPRINTFN(0, "setting up USB template failed - " "usb_template(4) not loaded?\n"); goto done; } } usb_set_device_state(udev, USB_STATE_ADDRESSED); /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, NULL); if (err != 0) { /* try to enumerate two more times */ err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { err = usbd_req_re_enumerate(udev, NULL); if (err != 0) { goto done; } } } /* * Setup temporary USB attach args so that we can figure out some * basic quirks for this device. */ usb_init_attach_arg(udev, &uaa); if (usb_test_quirk(&uaa, UQ_BUS_POWERED)) { udev->flags.uq_bus_powered = 1; } if (usb_test_quirk(&uaa, UQ_NO_STRINGS)) { udev->flags.no_strings = 1; } /* * Workaround for buggy USB devices. * * It appears that some string-less USB chips will crash and * disappear if any attempts are made to read any string * descriptors. * * Try to detect such chips by checking the strings in the USB * device descriptor. If no strings are present there we * simply disable all USB strings. */ /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); scratch_ptr = udev->scratch.data; if (udev->flags.no_strings) { err = USB_ERR_INVAL; } else if (udev->ddesc.iManufacturer || udev->ddesc.iProduct || udev->ddesc.iSerialNumber) { /* read out the language ID string */ err = usbd_req_get_string_desc(udev, NULL, (char *)scratch_ptr, 4, 0, USB_LANGUAGE_TABLE); } else { err = USB_ERR_INVAL; } if (err || (scratch_ptr[0] < 4)) { udev->flags.no_strings = 1; } else { uint16_t langid; uint16_t pref; uint16_t mask; uint8_t x; /* load preferred value and mask */ pref = usb_lang_id; mask = usb_lang_mask; /* align length correctly */ scratch_ptr[0] &= ~1U; /* fix compiler warning */ langid = 0; /* search for preferred language */ for (x = 2; (x < scratch_ptr[0]); x += 2) { langid = UGETW(scratch_ptr + x); if ((langid & mask) == pref) break; } if (x >= scratch_ptr[0]) { /* pick the first language as the default */ DPRINTFN(1, "Using first language\n"); langid = UGETW(scratch_ptr + 2); } DPRINTFN(1, "Language selected: 0x%04x\n", langid); udev->langid = langid; } if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); /* assume 100mA bus powered for now. Changed when configured. */ udev->power = USB_MIN_POWER; /* fetch the vendor and product strings from the device */ usbd_set_device_strings(udev); if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* USB device mode setup is complete */ err = 0; goto config_done; } /* * Most USB devices should attach to config index 0 by * default */ if (usb_test_quirk(&uaa, UQ_CFG_INDEX_0)) { config_index = 0; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_1)) { config_index = 1; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_2)) { config_index = 2; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_3)) { config_index = 3; config_quirk = 1; } else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_4)) { config_index = 4; config_quirk = 1; } else { config_index = 0; config_quirk = 0; } set_config_failed = 0; repeat_set_config: DPRINTF("setting config %u\n", config_index); /* get the USB device configured */ err = usbd_set_config_index(udev, config_index); if (err) { if (udev->ddesc.bNumConfigurations != 0) { if (!set_config_failed) { set_config_failed = 1; /* XXX try to re-enumerate the device */ err = usbd_req_re_enumerate(udev, NULL); if (err == 0) goto repeat_set_config; } DPRINTFN(0, "Failure selecting configuration index %u:" "%s, port %u, addr %u (ignored)\n", config_index, usbd_errstr(err), udev->port_no, udev->address); } /* * Some USB devices do not have any configurations. Ignore any * set config failures! */ err = 0; goto config_done; } if (!config_quirk && config_index + 1 < udev->ddesc.bNumConfigurations) { if ((udev->cdesc->bNumInterface < 2) && usbd_get_no_descriptors(udev->cdesc, UDESC_ENDPOINT) == 0) { DPRINTFN(0, "Found no endpoints, trying next config\n"); config_index++; goto repeat_set_config; } #if USB_HAVE_MSCTEST if (config_index == 0) { /* * Try to figure out if we have an * auto-install disk there: */ if (usb_iface_is_cdrom(udev, 0)) { DPRINTFN(0, "Found possible auto-install " "disk (trying next config)\n"); config_index++; goto repeat_set_config; } } #endif } #if USB_HAVE_MSCTEST if (set_config_failed == 0 && config_index == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_SYNC_CACHE) == 0 && usb_test_quirk(&uaa, UQ_MSC_NO_GETMAXLUN) == 0) { /* * Try to figure out if there are any MSC quirks we * should apply automatically: */ err = usb_msc_auto_quirk(udev, 0); if (err != 0) { set_config_failed = 1; goto repeat_set_config; } } #endif config_done: DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n", udev->address, udev, udev->parent_hub); /* register our device - we are ready */ usb_bus_port_set_device(bus, parent_hub ? parent_hub->hub->ports + port_index : NULL, udev, device_index); #if USB_HAVE_UGEN /* Symlink the ugen device name */ udev->ugen_symlink = usb_alloc_symlink(udev->ugen_name); /* Announce device */ printf("%s: <%s> at %s\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(udev->bus->bdev)); #endif #if USB_HAVE_DEVCTL usb_notify_addq("ATTACH", udev); #endif done: if (err) { /* * Free USB device and all subdevices, if any. */ usb_free_device(udev, 0); udev = NULL; } return (udev); } #if USB_HAVE_UGEN struct usb_fs_privdata * usb_make_dev(struct usb_device *udev, const char *devname, int ep, int fi, int rwmode, uid_t uid, gid_t gid, int mode) { struct usb_fs_privdata* pd; struct make_dev_args args; char buffer[32]; /* Store information to locate ourselves again later */ pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); pd->bus_index = device_get_unit(udev->bus->bdev); pd->dev_index = udev->device_index; pd->ep_addr = ep; pd->fifo_index = fi; pd->mode = rwmode; /* Now, create the device itself */ if (devname == NULL) { devname = buffer; snprintf(buffer, sizeof(buffer), USB_DEVICE_DIR "/%u.%u.%u", pd->bus_index, pd->dev_index, pd->ep_addr); } /* Setup arguments for make_dev_s() */ make_dev_args_init(&args); args.mda_devsw = &usb_devsw; args.mda_uid = uid; args.mda_gid = gid; args.mda_mode = mode; args.mda_si_drv1 = pd; if (make_dev_s(&args, &pd->cdev, "%s", devname) != 0) { DPRINTFN(0, "Failed to create device %s\n", devname); free(pd, M_USBDEV); return (NULL); } return (pd); } void usb_destroy_dev_sync(struct usb_fs_privdata *pd) { DPRINTFN(1, "Destroying device at ugen%d.%d\n", pd->bus_index, pd->dev_index); /* * Destroy character device synchronously. After this * all system calls are returned. Can block. */ destroy_dev(pd->cdev); free(pd, M_USBDEV); } void usb_destroy_dev(struct usb_fs_privdata *pd) { struct usb_bus *bus; if (pd == NULL) return; mtx_lock(&usb_ref_lock); bus = devclass_get_softc(usb_devclass_ptr, pd->bus_index); mtx_unlock(&usb_ref_lock); if (bus == NULL) { usb_destroy_dev_sync(pd); return; } /* make sure we can re-use the device name */ delist_dev(pd->cdev); USB_BUS_LOCK(bus); LIST_INSERT_HEAD(&bus->pd_cleanup_list, pd, pd_next); /* get cleanup going */ usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), &bus->cleanup_msg[0], &bus->cleanup_msg[1]); USB_BUS_UNLOCK(bus); } static void usb_cdev_create(struct usb_device *udev) { struct usb_config_descriptor *cd; struct usb_endpoint_descriptor *ed; struct usb_descriptor *desc; struct usb_fs_privdata* pd; int inmode, outmode, inmask, outmask, mode; uint8_t ep; KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("stale cdev entries")); DPRINTFN(2, "Creating device nodes\n"); if (usbd_get_mode(udev) == USB_MODE_DEVICE) { inmode = FWRITE; outmode = FREAD; } else { /* USB_MODE_HOST */ inmode = FREAD; outmode = FWRITE; } inmask = 0; outmask = 0; desc = NULL; /* * Collect all used endpoint numbers instead of just * generating 16 static endpoints. */ cd = usbd_get_config_descriptor(udev); while ((desc = usb_desc_foreach(cd, desc))) { /* filter out all endpoint descriptors */ if ((desc->bDescriptorType == UDESC_ENDPOINT) && (desc->bLength >= sizeof(*ed))) { ed = (struct usb_endpoint_descriptor *)desc; /* update masks */ ep = ed->bEndpointAddress; if (UE_GET_DIR(ep) == UE_DIR_OUT) outmask |= 1 << UE_GET_ADDR(ep); else inmask |= 1 << UE_GET_ADDR(ep); } } /* Create all available endpoints except EP0 */ for (ep = 1; ep < 16; ep++) { mode = (inmask & (1 << ep)) ? inmode : 0; mode |= (outmask & (1 << ep)) ? outmode : 0; if (mode == 0) continue; /* no IN or OUT endpoint */ pd = usb_make_dev(udev, NULL, ep, 0, mode, UID_ROOT, GID_OPERATOR, 0600); if (pd != NULL) LIST_INSERT_HEAD(&udev->pd_list, pd, pd_next); } } static void usb_cdev_free(struct usb_device *udev) { struct usb_fs_privdata* pd; DPRINTFN(2, "Freeing device nodes\n"); while ((pd = LIST_FIRST(&udev->pd_list)) != NULL) { KASSERT(pd->cdev->si_drv1 == pd, ("privdata corrupt")); LIST_REMOVE(pd, pd_next); usb_destroy_dev(pd); } } #endif /*------------------------------------------------------------------------* * usb_free_device * * This function is NULL safe and will free an USB device and its * children devices, if any. * * Flag values: Reserved, set to zero. *------------------------------------------------------------------------*/ void usb_free_device(struct usb_device *udev, uint8_t flag) { struct usb_bus *bus; if (udev == NULL) return; /* already freed */ DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no); bus = udev->bus; /* set DETACHED state to prevent any further references */ usb_set_device_state(udev, USB_STATE_DETACHED); #if USB_HAVE_DEVCTL usb_notify_addq("DETACH", udev); #endif #if USB_HAVE_UGEN if (!rebooting) { printf("%s: <%s> at %s (disconnected)\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(bus->bdev)); } /* Destroy UGEN symlink, if any */ if (udev->ugen_symlink) { usb_free_symlink(udev->ugen_symlink); udev->ugen_symlink = NULL; } usb_destroy_dev(udev->ctrl_dev); #endif if (udev->flags.usb_mode == USB_MODE_DEVICE) { /* stop receiving any control transfers (Device Side Mode) */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); } /* the following will get the device unconfigured in software */ usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0); /* final device unregister after all character devices are closed */ usb_bus_port_set_device(bus, udev->parent_hub ? udev->parent_hub->hub->ports + udev->port_index : NULL, NULL, USB_ROOT_HUB_ADDR); /* unsetup any leftover default USB transfers */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* template unsetup, if any */ (usb_temp_unsetup_p) (udev); /* * Make sure that our clear-stall messages are not queued * anywhere: */ USB_BUS_LOCK(udev->bus); usb_proc_mwait(USB_BUS_CS_PROC(udev->bus), &udev->cs_msg[0], &udev->cs_msg[1]); USB_BUS_UNLOCK(udev->bus); /* wait for all references to go away */ usb_wait_pending_refs(udev); sx_destroy(&udev->enum_sx); sx_destroy(&udev->sr_sx); + sx_destroy(&udev->ctrl_sx); cv_destroy(&udev->ctrlreq_cv); cv_destroy(&udev->ref_cv); mtx_destroy(&udev->device_mtx); #if USB_HAVE_UGEN KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("leaked cdev entries")); #endif /* Uninitialise device */ if (bus->methods->device_uninit != NULL) (bus->methods->device_uninit) (udev); /* free device */ free(udev->serial, M_USB); free(udev->manufacturer, M_USB); free(udev->product, M_USB); free(udev, M_USB); } /*------------------------------------------------------------------------* * usbd_get_iface * * This function is the safe way to get the USB interface structure * pointer by interface index. * * Return values: * NULL: Interface not present. * Else: Pointer to USB interface structure. *------------------------------------------------------------------------*/ struct usb_interface * usbd_get_iface(struct usb_device *udev, uint8_t iface_index) { struct usb_interface *iface = udev->ifaces + iface_index; if (iface_index >= udev->ifaces_max) return (NULL); return (iface); } /*------------------------------------------------------------------------* * usbd_find_descriptor * * This function will lookup the first descriptor that matches the * criteria given by the arguments "type" and "subtype". Descriptors * will only be searched within the interface having the index * "iface_index". If the "id" argument points to an USB descriptor, * it will be skipped before the search is started. This allows * searching for multiple descriptors using the same criteria. Else * the search is started after the interface descriptor. * * Return values: * NULL: End of descriptors * Else: A descriptor matching the criteria *------------------------------------------------------------------------*/ void * usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask) { struct usb_descriptor *desc; struct usb_config_descriptor *cd; struct usb_interface *iface; cd = usbd_get_config_descriptor(udev); if (cd == NULL) { return (NULL); } if (id == NULL) { iface = usbd_get_iface(udev, iface_index); if (iface == NULL) { return (NULL); } id = usbd_get_interface_descriptor(iface); if (id == NULL) { return (NULL); } } desc = (void *)id; while ((desc = usb_desc_foreach(cd, desc))) { if (desc->bDescriptorType == UDESC_INTERFACE) { break; } if (((desc->bDescriptorType & type_mask) == type) && ((desc->bDescriptorSubtype & subtype_mask) == subtype)) { return (desc); } } return (NULL); } /*------------------------------------------------------------------------* * usb_devinfo * * This function will dump information from the device descriptor * belonging to the USB device pointed to by "udev", to the string * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes * including the terminating zero. *------------------------------------------------------------------------*/ void usb_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len) { struct usb_device_descriptor *udd = &udev->ddesc; uint16_t bcdDevice; uint16_t bcdUSB; bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); if (udd->bDeviceClass != 0xFF) { snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), udd->bDeviceClass, udd->bDeviceSubClass, (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } else { snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/" "%x.%02x, addr %d", usb_get_manufacturer(udev), usb_get_product(udev), (bcdUSB >> 8), bcdUSB & 0xFF, (bcdDevice >> 8), bcdDevice & 0xFF, udev->address); } } #ifdef USB_VERBOSE /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { uint16_t vendor; uint16_t product; uint32_t flags; const char *vendorname; const char *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include "usbdevs.h" #include "usbdevs_data.h" #endif /* USB_VERBOSE */ static void usbd_set_device_strings(struct usb_device *udev) { struct usb_device_descriptor *udd = &udev->ddesc; #ifdef USB_VERBOSE const struct usb_knowndev *kdp; #endif char *temp_ptr; size_t temp_size; uint16_t vendor_id; uint16_t product_id; uint8_t do_unlock; /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); temp_ptr = (char *)udev->scratch.data; temp_size = sizeof(udev->scratch.data); vendor_id = UGETW(udd->idVendor); product_id = UGETW(udd->idProduct); /* get serial number string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iSerialNumber); udev->serial = strdup(temp_ptr, M_USB); /* get manufacturer string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iManufacturer); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->manufacturer = strdup(temp_ptr, M_USB); /* get product string */ usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size, udev->ddesc.iProduct); usb_trim_spaces(temp_ptr); if (temp_ptr[0] != '\0') udev->product = strdup(temp_ptr, M_USB); #ifdef USB_VERBOSE if (udev->manufacturer == NULL || udev->product == NULL) { for (kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == vendor_id && (kdp->product == product_id || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { /* XXX should use pointer to knowndevs string */ if (udev->manufacturer == NULL) { udev->manufacturer = strdup(kdp->vendorname, M_USB); } if (udev->product == NULL && (kdp->flags & USB_KNOWNDEV_NOPROD) == 0) { udev->product = strdup(kdp->productname, M_USB); } } } #endif /* Provide default strings if none were found */ if (udev->manufacturer == NULL) { snprintf(temp_ptr, temp_size, "vendor 0x%04x", vendor_id); udev->manufacturer = strdup(temp_ptr, M_USB); } if (udev->product == NULL) { snprintf(temp_ptr, temp_size, "product 0x%04x", product_id); udev->product = strdup(temp_ptr, M_USB); } if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); } /* * Returns: * See: USB_MODE_XXX */ enum usb_hc_mode usbd_get_mode(struct usb_device *udev) { return (udev->flags.usb_mode); } /* * Returns: * See: USB_SPEED_XXX */ enum usb_dev_speed usbd_get_speed(struct usb_device *udev) { return (udev->speed); } uint32_t usbd_get_isoc_fps(struct usb_device *udev) { ; /* indent fix */ switch (udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: return (1000); default: return (8000); } } struct usb_device_descriptor * usbd_get_device_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (&udev->ddesc); } struct usb_config_descriptor * usbd_get_config_descriptor(struct usb_device *udev) { if (udev == NULL) return (NULL); /* be NULL safe */ return (udev->cdesc); } /*------------------------------------------------------------------------* * usb_test_quirk - test a device for a given quirk * * Return values: * 0: The USB device does not have the given quirk. * Else: The USB device has the given quirk. *------------------------------------------------------------------------*/ uint8_t usb_test_quirk(const struct usb_attach_arg *uaa, uint16_t quirk) { uint8_t found; uint8_t x; if (quirk == UQ_NONE) return (0); /* search the automatic per device quirks first */ for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (uaa->device->autoQuirk[x] == quirk) return (1); } /* search global quirk table, if any */ found = (usb_test_quirk_p) (&uaa->info, quirk); return (found); } struct usb_interface_descriptor * usbd_get_interface_descriptor(struct usb_interface *iface) { if (iface == NULL) return (NULL); /* be NULL safe */ return (iface->idesc); } uint8_t usbd_get_interface_altindex(struct usb_interface *iface) { return (iface->alt_index); } uint8_t usbd_get_bus_index(struct usb_device *udev) { return ((uint8_t)device_get_unit(udev->bus->bdev)); } uint8_t usbd_get_device_index(struct usb_device *udev) { return (udev->device_index); } #if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *udev) { struct usb_interface *iface; struct sbuf *sb; int i; /* announce the device */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "port=%u " #if USB_HAVE_UGEN "parent=%s" #endif "", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", udev->port_no #if USB_HAVE_UGEN , udev->parent_hub != NULL ? udev->parent_hub->ugen_name : device_get_nameunit(device_get_parent(udev->bus->bdev)) #endif ); sbuf_finish(sb); devctl_notify("USB", "DEVICE", type, sbuf_data(sb)); sbuf_delete(sb); /* announce each interface */ for (i = 0; i < USB_IFACE_MAX; i++) { iface = usbd_get_iface(udev, i); if (iface == NULL) break; /* end of interfaces */ if (iface->idesc == NULL) continue; /* no interface descriptor */ sb = sbuf_new_auto(); sbuf_printf(sb, #if USB_HAVE_UGEN "ugen=%s " "cdev=%s " #endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " "mode=%s " "interface=%d " "endpoints=%d " "intclass=0x%02x " "intsubclass=0x%02x " "intprotocol=0x%02x", #if USB_HAVE_UGEN udev->ugen_name, udev->ugen_name, #endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", iface->idesc->bInterfaceNumber, iface->idesc->bNumEndpoints, iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass, iface->idesc->bInterfaceProtocol); sbuf_finish(sb); devctl_notify("USB", "INTERFACE", type, sbuf_data(sb)); sbuf_delete(sb); } } #endif #if USB_HAVE_UGEN /*------------------------------------------------------------------------* * usb_fifo_free_wrap * * This function will free the FIFOs. * * Description of "flag" argument: If the USB_UNCFG_FLAG_FREE_EP0 flag * is set and "iface_index" is set to "USB_IFACE_INDEX_ANY", we free * all FIFOs. If the USB_UNCFG_FLAG_FREE_EP0 flag is not set and * "iface_index" is set to "USB_IFACE_INDEX_ANY", we free all non * control endpoint FIFOs. If "iface_index" is not set to * "USB_IFACE_INDEX_ANY" the flag has no effect. *------------------------------------------------------------------------*/ static void usb_fifo_free_wrap(struct usb_device *udev, uint8_t iface_index, uint8_t flag) { struct usb_fifo *f; uint16_t i; /* * Free any USB FIFOs on the given interface: */ for (i = 0; i != USB_FIFO_MAX; i++) { f = udev->fifo[i]; if (f == NULL) { continue; } /* Check if the interface index matches */ if (iface_index == f->iface_index) { if (f->methods != &usb_ugen_methods) { /* * Don't free any non-generic FIFOs in * this case. */ continue; } if ((f->dev_ep_index == 0) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else if (iface_index == USB_IFACE_INDEX_ANY) { if ((f->methods == &usb_ugen_methods) && (f->dev_ep_index == 0) && (!(flag & USB_UNCFG_FLAG_FREE_EP0)) && (f->fs_xfer == NULL)) { /* no need to free this FIFO */ continue; } } else { /* no need to free this FIFO */ continue; } /* free this FIFO */ usb_fifo_free(f); } } #endif /*------------------------------------------------------------------------* * usb_peer_can_wakeup * * Return values: * 0: Peer cannot do resume signalling. * Else: Peer can do resume signalling. *------------------------------------------------------------------------*/ uint8_t usb_peer_can_wakeup(struct usb_device *udev) { const struct usb_config_descriptor *cdp; cdp = udev->cdesc; if ((cdp != NULL) && (udev->flags.usb_mode == USB_MODE_HOST)) { return (cdp->bmAttributes & UC_REMOTE_WAKEUP); } return (0); /* not supported */ } void usb_set_device_state(struct usb_device *udev, enum usb_dev_state state) { KASSERT(state < USB_STATE_MAX, ("invalid udev state")); DPRINTF("udev %p state %s -> %s\n", udev, usb_statestr(udev->state), usb_statestr(state)); #if USB_HAVE_UGEN mtx_lock(&usb_ref_lock); #endif udev->state = state; #if USB_HAVE_UGEN mtx_unlock(&usb_ref_lock); #endif if (udev->bus->methods->device_state_change != NULL) (udev->bus->methods->device_state_change) (udev); } enum usb_dev_state usb_get_device_state(struct usb_device *udev) { if (udev == NULL) return (USB_STATE_DETACHED); return (udev->state); } uint8_t usbd_device_attached(struct usb_device *udev) { return (udev->state > USB_STATE_DETACHED); } /* * The following function locks enumerating the given USB device. If * the lock is already grabbed this function returns zero. Else a * a value of one is returned. */ uint8_t usbd_enum_lock(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); sx_xlock(&udev->enum_sx); sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); return (1); } #if USB_HAVE_UGEN /* * This function is the same like usbd_enum_lock() except a value of * 255 is returned when a signal is pending: */ uint8_t usbd_enum_lock_sig(struct usb_device *udev) { if (sx_xlocked(&udev->enum_sx)) return (0); if (sx_xlock_sig(&udev->enum_sx)) return (255); if (sx_xlock_sig(&udev->sr_sx)) { sx_xunlock(&udev->enum_sx); return (255); } mtx_lock(&Giant); return (1); } #endif /* The following function unlocks enumerating the given USB device. */ void usbd_enum_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->enum_sx); sx_xunlock(&udev->sr_sx); } /* The following function locks suspend and resume. */ void usbd_sr_lock(struct usb_device *udev) { sx_xlock(&udev->sr_sx); /* * NEWBUS LOCK NOTE: We should check if any parent SX locks * are locked before locking Giant. Else the lock can be * locked multiple times. */ mtx_lock(&Giant); } /* The following function unlocks suspend and resume. */ void usbd_sr_unlock(struct usb_device *udev) { mtx_unlock(&Giant); sx_xunlock(&udev->sr_sx); } /* * The following function checks the enumerating lock for the given * USB device. */ uint8_t usbd_enum_is_locked(struct usb_device *udev) { return (sx_xlocked(&udev->enum_sx)); +} + +/* + * The following function is used to serialize access to USB control + * transfers and the USB scratch area. If the lock is already grabbed + * this function returns zero. Else a value of one is returned. + */ +uint8_t +usbd_ctrl_lock(struct usb_device *udev) +{ + if (sx_xlocked(&udev->ctrl_sx)) + return (0); + sx_xlock(&udev->ctrl_sx); + + /* + * We need to allow suspend and resume at this point, else the + * control transfer will timeout if the device is suspended! + */ + if (usbd_enum_is_locked(udev)) + usbd_sr_unlock(udev); + return (1); +} + +void +usbd_ctrl_unlock(struct usb_device *udev) +{ + sx_xunlock(&udev->ctrl_sx); + + /* + * Restore the suspend and resume lock after we have unlocked + * the USB control transfer lock to avoid LOR: + */ + if (usbd_enum_is_locked(udev)) + usbd_sr_lock(udev); } /* * The following function is used to set the per-interface specific * plug and play information. The string referred to by the pnpinfo * argument can safely be freed after calling this function. The * pnpinfo of an interface will be reset at device detach or when * passing a NULL argument to this function. This function * returns zero on success, else a USB_ERR_XXX failure code. */ usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo) { struct usb_interface *iface; iface = usbd_get_iface(udev, iface_index); if (iface == NULL) return (USB_ERR_INVAL); if (iface->pnpinfo != NULL) { free(iface->pnpinfo, M_USBDEV); iface->pnpinfo = NULL; } if (pnpinfo == NULL || pnpinfo[0] == 0) return (0); /* success */ iface->pnpinfo = strdup(pnpinfo, M_USBDEV); if (iface->pnpinfo == NULL) return (USB_ERR_NOMEM); return (0); /* success */ } usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk) { uint8_t x; for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { if (udev->autoQuirk[x] == 0 || udev->autoQuirk[x] == quirk) { udev->autoQuirk[x] = quirk; return (0); /* success */ } } return (USB_ERR_NOMEM); } /* * The following function is used to select the endpoint mode. It * should not be called outside enumeration context. */ usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode) { usb_error_t error; uint8_t do_unlock; /* Prevent re-enumeration */ do_unlock = usbd_enum_lock(udev); if (udev->bus->methods->set_endpoint_mode != NULL) { error = (udev->bus->methods->set_endpoint_mode) ( udev, ep, ep_mode); } else if (ep_mode != USB_EP_MODE_DEFAULT) { error = USB_ERR_INVAL; } else { error = 0; } /* only set new mode regardless of error */ ep->ep_mode = ep_mode; if (do_unlock) usbd_enum_unlock(udev); return (error); } uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep) { return (ep->ep_mode); } Index: head/sys/dev/usb/usb_device.h =================================================================== --- head/sys/dev/usb/usb_device.h (revision 305420) +++ head/sys/dev/usb/usb_device.h (revision 305421) @@ -1,331 +1,334 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _USB_DEVICE_H_ #define _USB_DEVICE_H_ #ifndef USB_GLOBAL_INCLUDE_FILE #include #include #include #endif struct usb_bus_methods; struct usb_config_descriptor; struct usb_device; /* linux compat */ struct usb_fs_privdata; struct usb_hw_ep_profile; struct usb_symlink; /* UGEN */ #define USB_CTRL_XFER_MAX 2 /* "usb_config_parse()" commands */ #define USB_CFG_ALLOC 0 #define USB_CFG_FREE 1 #define USB_CFG_INIT 2 /* "usb_unconfigure()" flags */ #define USB_UNCFG_FLAG_NONE 0x00 #define USB_UNCFG_FLAG_FREE_EP0 0x02 /* endpoint zero is freed */ struct usb_udev_msg { struct usb_proc_msg hdr; struct usb_device *udev; }; /* The following four structures makes up a tree, where we have the * leaf structure, "usb_host_endpoint", first, and the root structure, * "usb_device", last. The four structures below mirror the structure * of the USB descriptors belonging to an USB configuration. Please * refer to the USB specification for a definition of "endpoints" and * "interfaces". */ struct usb_host_endpoint { struct usb_endpoint_descriptor desc; TAILQ_HEAD(, urb) bsd_urb_list; struct usb_xfer *bsd_xfer[2]; uint8_t *extra; /* Extra descriptors */ usb_frlength_t fbsd_buf_size; uint16_t extralen; uint8_t bsd_iface_index; } __aligned(USB_HOST_ALIGN); struct usb_host_interface { struct usb_interface_descriptor desc; /* the following array has size "desc.bNumEndpoint" */ struct usb_host_endpoint *endpoint; const char *string; /* iInterface string, if present */ uint8_t *extra; /* Extra descriptors */ uint16_t extralen; uint8_t bsd_iface_index; } __aligned(USB_HOST_ALIGN); /* * The following structure defines the USB device flags. */ struct usb_device_flags { enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t self_powered:1; /* set if USB device is self powered */ uint8_t no_strings:1; /* set if USB device does not support * strings */ uint8_t remote_wakeup:1; /* set if remote wakeup is enabled */ uint8_t uq_bus_powered:1; /* set if BUS powered quirk is present */ /* * NOTE: Although the flags below will reach the same value * over time, but the instant values may differ, and * consequently the flags cannot be merged into one! */ uint8_t peer_suspended:1; /* set if peer is suspended */ uint8_t self_suspended:1; /* set if self is suspended */ }; /* * The following structure is used for power-save purposes. The data * in this structure is protected by the USB BUS lock. */ struct usb_power_save { usb_ticks_t last_xfer_time; /* copy of "ticks" */ usb_size_t type_refs[4]; /* transfer reference count */ usb_size_t read_refs; /* data read references */ usb_size_t write_refs; /* data write references */ }; /* * The following structure is used when trying to allocate hardware * endpoints for an USB configuration in USB device side mode. */ struct usb_hw_ep_scratch_sub { const struct usb_hw_ep_profile *pf; uint16_t max_frame_size; uint8_t hw_endpoint_out; uint8_t hw_endpoint_in; uint8_t needs_ep_type; uint8_t needs_in:1; uint8_t needs_out:1; }; /* * The following structure is used when trying to allocate hardware * endpoints for an USB configuration in USB device side mode. */ struct usb_hw_ep_scratch { struct usb_hw_ep_scratch_sub ep[USB_EP_MAX]; struct usb_hw_ep_scratch_sub *ep_max; struct usb_config_descriptor *cd; struct usb_device *udev; const struct usb_bus_methods *methods; uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16]; uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16]; }; /* * The following structure is used when generating USB descriptors * from USB templates. */ struct usb_temp_setup { void *buf; usb_size_t size; enum usb_dev_speed usb_speed; uint8_t self_powered; uint8_t bNumEndpoints; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bConfigurationValue; usb_error_t err; }; /* * The scratch area for USB devices. Access to this structure is - * protected by the enumeration SX lock. + * protected by the control SX lock. */ union usb_device_scratch { struct usb_hw_ep_scratch hw_ep_scratch[1]; struct usb_temp_setup temp_setup[1]; struct { struct usb_xfer dummy; struct usb_setup_params parm; } xfer_setup[1]; uint8_t data[255]; }; /* * The following structure defines an USB device. There exists one of * these structures for every USB device. */ struct usb_device { /* generic clear stall message */ struct usb_udev_msg cs_msg[2]; struct sx enum_sx; struct sx sr_sx; + struct sx ctrl_sx; struct mtx device_mtx; struct cv ctrlreq_cv; struct cv ref_cv; #if (USB_HAVE_FIXED_IFACE == 0) struct usb_interface *ifaces; #else struct usb_interface ifaces[USB_IFACE_MAX]; #endif struct usb_endpoint ctrl_ep; /* Control Endpoint 0 */ #if (USB_HAVE_FIXED_ENDPOINT == 0) struct usb_endpoint *endpoints; #else struct usb_endpoint endpoints[USB_MAX_EP_UNITS]; #endif struct usb_power_save pwr_save;/* power save data */ struct usb_bus *bus; /* our USB BUS */ device_t parent_dev; /* parent device */ struct usb_device *parent_hub; struct usb_device *parent_hs_hub; /* high-speed parent HUB */ struct usb_config_descriptor *cdesc; /* full config descr */ struct usb_hub *hub; /* only if this is a hub */ struct usb_xfer *ctrl_xfer[USB_CTRL_XFER_MAX]; struct usb_temp_data *usb_template_ptr; struct usb_endpoint *ep_curr; /* current clear stall endpoint */ #if USB_HAVE_UGEN struct usb_fifo *fifo[USB_FIFO_MAX]; struct usb_symlink *ugen_symlink; /* our generic symlink */ struct usb_fs_privdata *ctrl_dev; /* Control Endpoint 0 device node */ LIST_HEAD(,usb_fs_privdata) pd_list; char ugen_name[20]; /* name of ugenX.X device */ #endif usb_ticks_t plugtime; /* copy of "ticks" */ enum usb_dev_state state; enum usb_dev_speed speed; uint16_t refcount; #define USB_DEV_REF_MAX 0xffff uint16_t power; /* mA the device uses */ uint16_t langid; /* language for strings */ uint16_t autoQuirk[USB_MAX_AUTO_QUIRK]; /* dynamic quirks */ uint8_t address; /* device addess */ uint8_t device_index; /* device index in "bus->devices" */ uint8_t controller_slot_id; /* controller specific value */ uint8_t next_config_index; /* used by USB_RE_ENUM_SET_CONFIG */ uint8_t curr_config_index; /* current configuration index */ uint8_t curr_config_no; /* current configuration number */ uint8_t depth; /* distance from root HUB */ uint8_t port_index; /* parent HUB port index */ uint8_t port_no; /* parent HUB port number */ uint8_t hs_hub_addr; /* high-speed HUB address */ uint8_t hs_port_no; /* high-speed HUB port number */ uint8_t driver_added_refcount; /* our driver added generation count */ uint8_t power_mode; /* see USB_POWER_XXX */ uint8_t re_enumerate_wait; /* set if re-enum. is in progress */ #define USB_RE_ENUM_DONE 0 #define USB_RE_ENUM_START 1 #define USB_RE_ENUM_PWR_OFF 2 #define USB_RE_ENUM_SET_CONFIG 3 uint8_t ifaces_max; /* number of interfaces present */ uint8_t endpoints_max; /* number of endpoints present */ /* the "flags" field is write-protected by "bus->mtx" */ struct usb_device_flags flags; struct usb_endpoint_descriptor ctrl_ep_desc; /* for endpoint 0 */ struct usb_endpoint_ss_comp_descriptor ctrl_ep_comp_desc; /* for endpoint 0 */ struct usb_device_descriptor ddesc; /* device descriptor */ char *serial; /* serial number, can be NULL */ char *manufacturer; /* manufacturer string, can be NULL */ char *product; /* product string, can be NULL */ #if USB_HAVE_COMPAT_LINUX /* Linux compat */ struct usb_device_descriptor descriptor; struct usb_host_endpoint ep0; struct usb_interface *linux_iface_start; struct usb_interface *linux_iface_end; struct usb_host_endpoint *linux_endpoint_start; struct usb_host_endpoint *linux_endpoint_end; uint16_t devnum; #endif uint32_t clear_stall_errors; /* number of clear-stall failures */ union usb_device_scratch scratch; #if (USB_HAVE_FIXED_CONFIG != 0) uint32_t config_data[(USB_CONFIG_MAX + 3) / 4]; #endif }; /* globals */ extern int usb_template; /* function prototypes */ const char *usb_statestr(enum usb_dev_state state); struct usb_device *usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode); #if USB_HAVE_UGEN struct usb_fs_privdata *usb_make_dev(struct usb_device *, const char *, int, int, int, uid_t, gid_t, int); void usb_destroy_dev(struct usb_fs_privdata *); void usb_destroy_dev_sync(struct usb_fs_privdata *); #endif usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index); void usb_detach_device(struct usb_device *, uint8_t, uint8_t); usb_error_t usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index); usb_error_t usbd_set_config_index(struct usb_device *udev, uint8_t index); usb_error_t usbd_set_endpoint_stall(struct usb_device *udev, struct usb_endpoint *ep, uint8_t do_stall); usb_error_t usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend); void usb_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len); void usb_free_device(struct usb_device *, uint8_t); void usb_linux_free_device(struct usb_device *dev); uint8_t usb_peer_can_wakeup(struct usb_device *udev); struct usb_endpoint *usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep); void usb_set_device_state(struct usb_device *, enum usb_dev_state); enum usb_dev_state usb_get_device_state(struct usb_device *); uint8_t usbd_enum_lock(struct usb_device *); #if USB_HAVE_UGEN uint8_t usbd_enum_lock_sig(struct usb_device *); #endif void usbd_enum_unlock(struct usb_device *); void usbd_sr_lock(struct usb_device *); void usbd_sr_unlock(struct usb_device *); +uint8_t usbd_ctrl_lock(struct usb_device *); +void usbd_ctrl_unlock(struct usb_device *); uint8_t usbd_enum_is_locked(struct usb_device *); #if USB_HAVE_TT_SUPPORT void uhub_tt_buffer_reset_async_locked(struct usb_device *, struct usb_endpoint *); #endif uint8_t uhub_count_active_host_ports(struct usb_device *, enum usb_dev_speed); #endif /* _USB_DEVICE_H_ */ Index: head/sys/dev/usb/usb_generic.c =================================================================== --- head/sys/dev/usb/usb_generic.c (revision 305420) +++ head/sys/dev/usb/usb_generic.c (revision 305421) @@ -1,2335 +1,2338 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR ugen_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ #if USB_HAVE_UGEN /* defines */ #define UGEN_BULK_FS_BUFFER_SIZE (64*32) /* bytes */ #define UGEN_BULK_HS_BUFFER_SIZE (1024*32) /* bytes */ #define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */ /* function prototypes */ static usb_callback_t ugen_read_clear_stall_callback; static usb_callback_t ugen_write_clear_stall_callback; static usb_callback_t ugen_ctrl_read_callback; static usb_callback_t ugen_ctrl_write_callback; static usb_callback_t ugen_isoc_read_callback; static usb_callback_t ugen_isoc_write_callback; static usb_callback_t ugen_ctrl_fs_callback; static usb_fifo_open_t ugen_open; static usb_fifo_close_t ugen_close; static usb_fifo_ioctl_t ugen_ioctl; static usb_fifo_ioctl_t ugen_ioctl_post; static usb_fifo_cmd_t ugen_start_read; static usb_fifo_cmd_t ugen_start_write; static usb_fifo_cmd_t ugen_stop_io; static int ugen_transfer_setup(struct usb_fifo *, const struct usb_config *, uint8_t); static int ugen_open_pipe_write(struct usb_fifo *); static int ugen_open_pipe_read(struct usb_fifo *); static int ugen_set_config(struct usb_fifo *, uint8_t); static int ugen_set_interface(struct usb_fifo *, uint8_t, uint8_t); static int ugen_get_cdesc(struct usb_fifo *, struct usb_gen_descriptor *); static int ugen_get_sdesc(struct usb_fifo *, struct usb_gen_descriptor *); static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd); static int usb_gen_fill_deviceinfo(struct usb_fifo *, struct usb_device_info *); static int ugen_re_enumerate(struct usb_fifo *); static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int); static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *); static int ugen_fs_uninit(struct usb_fifo *f); /* structures */ struct usb_fifo_methods usb_ugen_methods = { .f_open = &ugen_open, .f_close = &ugen_close, .f_ioctl = &ugen_ioctl, .f_ioctl_post = &ugen_ioctl_post, .f_start_read = &ugen_start_read, .f_stop_read = &ugen_stop_io, .f_start_write = &ugen_start_write, .f_stop_write = &ugen_stop_io, }; #ifdef USB_DEBUG static int ugen_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic"); SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RWTUN, &ugen_debug, 0, "Debug level"); #endif /* prototypes */ static int ugen_transfer_setup(struct usb_fifo *f, const struct usb_config *setup, uint8_t n_setup) { struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_device *udev = f->udev; uint8_t iface_index = ep->iface_index; int error; mtx_unlock(f->priv_mtx); /* * "usbd_transfer_setup()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things */ error = usbd_transfer_setup(udev, &iface_index, f->xfer, setup, n_setup, f, f->priv_mtx); if (error == 0) { if (f->xfer[0]->nframes == 1) { error = usb_fifo_alloc_buffer(f, f->xfer[0]->max_data_length, 2); } else { error = usb_fifo_alloc_buffer(f, f->xfer[0]->max_frame_size, 2 * f->xfer[0]->nframes); } if (error) { usbd_transfer_unsetup(f->xfer, n_setup); } } mtx_lock(f->priv_mtx); return (error); } static int ugen_open(struct usb_fifo *f, int fflags) { struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; uint8_t type; DPRINTFN(6, "flag=0x%x\n", fflags); mtx_lock(f->priv_mtx); switch (usbd_get_speed(f->udev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: f->nframes = UGEN_HW_FRAMES; f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; break; default: f->nframes = UGEN_HW_FRAMES * 8; f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; break; } type = ed->bmAttributes & UE_XFERTYPE; if (type == UE_INTERRUPT) { f->bufsize = 0; /* use "wMaxPacketSize" */ } f->timeout = USB_NO_TIMEOUT; f->flag_short = 0; f->fifo_zlp = 0; mtx_unlock(f->priv_mtx); return (0); } static void ugen_close(struct usb_fifo *f, int fflags) { DPRINTFN(6, "flag=0x%x\n", fflags); /* cleanup */ mtx_lock(f->priv_mtx); usbd_transfer_stop(f->xfer[0]); usbd_transfer_stop(f->xfer[1]); mtx_unlock(f->priv_mtx); usbd_transfer_unsetup(f->xfer, 2); usb_fifo_free_buffer(f); if (ugen_fs_uninit(f)) { /* ignore any errors - we are closing */ DPRINTFN(6, "no FIFOs\n"); } } static int ugen_open_pipe_write(struct usb_fifo *f) { struct usb_config usb_config[2]; struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; mtx_assert(f->priv_mtx, MA_OWNED); if (f->xfer[0] || f->xfer[1]) { /* transfers are already opened */ return (0); } memset(usb_config, 0, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; usb_config[1].direction = UE_DIR_ANY; usb_config[1].timeout = 1000; /* 1 second */ usb_config[1].interval = 50;/* 50 milliseconds */ usb_config[1].bufsize = sizeof(struct usb_device_request); usb_config[1].callback = &ugen_write_clear_stall_callback; usb_config[1].usb_mode = USB_MODE_HOST; usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].stream_id = 0; /* XXX support more stream ID's */ usb_config[0].direction = UE_DIR_TX; usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ switch (ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: if (f->flag_short) { usb_config[0].flags.force_short_xfer = 1; } usb_config[0].callback = &ugen_ctrl_write_callback; usb_config[0].timeout = f->timeout; usb_config[0].frames = 1; usb_config[0].bufsize = f->bufsize; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } /* first transfer does not clear stall */ f->flag_stall = 0; break; case UE_ISOCHRONOUS: usb_config[0].flags.short_xfer_ok = 1; usb_config[0].bufsize = 0; /* use default */ usb_config[0].frames = f->nframes; usb_config[0].callback = &ugen_isoc_write_callback; usb_config[0].timeout = 0; /* clone configuration */ usb_config[1] = usb_config[0]; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } break; default: return (EINVAL); } return (0); } static int ugen_open_pipe_read(struct usb_fifo *f) { struct usb_config usb_config[2]; struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; mtx_assert(f->priv_mtx, MA_OWNED); if (f->xfer[0] || f->xfer[1]) { /* transfers are already opened */ return (0); } memset(usb_config, 0, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; usb_config[1].direction = UE_DIR_ANY; usb_config[1].timeout = 1000; /* 1 second */ usb_config[1].interval = 50;/* 50 milliseconds */ usb_config[1].bufsize = sizeof(struct usb_device_request); usb_config[1].callback = &ugen_read_clear_stall_callback; usb_config[1].usb_mode = USB_MODE_HOST; usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].stream_id = 0; /* XXX support more stream ID's */ usb_config[0].direction = UE_DIR_RX; usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ switch (ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: if (f->flag_short) { usb_config[0].flags.short_xfer_ok = 1; } usb_config[0].timeout = f->timeout; usb_config[0].frames = 1; usb_config[0].callback = &ugen_ctrl_read_callback; usb_config[0].bufsize = f->bufsize; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } /* first transfer does not clear stall */ f->flag_stall = 0; break; case UE_ISOCHRONOUS: usb_config[0].flags.short_xfer_ok = 1; usb_config[0].bufsize = 0; /* use default */ usb_config[0].frames = f->nframes; usb_config[0].callback = &ugen_isoc_read_callback; usb_config[0].timeout = 0; /* clone configuration */ usb_config[1] = usb_config[0]; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } break; default: return (EINVAL); } return (0); } static void ugen_start_read(struct usb_fifo *f) { /* check that pipes are open */ if (ugen_open_pipe_read(f)) { /* signal error */ usb_fifo_put_data_error(f); } /* start transfers */ usbd_transfer_start(f->xfer[0]); usbd_transfer_start(f->xfer[1]); } static void ugen_start_write(struct usb_fifo *f) { /* check that pipes are open */ if (ugen_open_pipe_write(f)) { /* signal error */ usb_fifo_get_data_error(f); } /* start transfers */ usbd_transfer_start(f->xfer[0]); usbd_transfer_start(f->xfer[1]); } static void ugen_stop_io(struct usb_fifo *f) { /* stop transfers */ usbd_transfer_stop(f->xfer[0]); usbd_transfer_stop(f->xfer[1]); } static void ugen_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_mbuf *m; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen == 0) { if (f->fifo_zlp != 4) { f->fifo_zlp++; } else { /* * Throttle a little bit we have multiple ZLPs * in a row! */ xfer->interval = 64; /* ms */ } } else { /* clear throttle */ xfer->interval = 0; f->fifo_zlp = 0; } usb_fifo_put_data(f, xfer->frbuffers, 0, xfer->actlen, 1); case USB_ST_SETUP: if (f->flag_stall) { usbd_transfer_start(f->xfer[1]); break; } USB_IF_POLL(&f->free_q, m); if (m) { usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* send a zero length packet to userland */ usb_fifo_put_data(f, xfer->frbuffers, 0, 0, 1); f->flag_stall = 1; f->fifo_zlp = 0; usbd_transfer_start(f->xfer[1]); } break; } } static void ugen_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t actlen; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: /* * If writing is in stall, just jump to clear stall * callback and solve the situation. */ if (f->flag_stall) { usbd_transfer_start(f->xfer[1]); break; } /* * Write data, setup and perform hardware transfer. */ if (usb_fifo_get_data(f, xfer->frbuffers, 0, xfer->max_data_length, &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { f->flag_stall = 1; usbd_transfer_start(f->xfer[1]); } break; } } static void ugen_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = f->xfer[0]; if (f->flag_stall == 0) { /* nothing to do */ return; } if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTFN(5, "f=%p: stall cleared\n", f); f->flag_stall = 0; usbd_transfer_start(xfer_other); } } static void ugen_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = f->xfer[0]; if (f->flag_stall == 0) { /* nothing to do */ return; } if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTFN(5, "f=%p: stall cleared\n", f); f->flag_stall = 0; usbd_transfer_start(xfer_other); } } static void ugen_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t offset; usb_frcount_t n; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(6, "actlen=%d\n", xfer->actlen); offset = 0; for (n = 0; n != xfer->aframes; n++) { usb_fifo_put_data(f, xfer->frbuffers, offset, xfer->frlengths[n], 1); offset += xfer->max_frame_size; } case USB_ST_SETUP: tr_setup: for (n = 0; n != xfer->nframes; n++) { /* setup size for next transfer */ usbd_xfer_set_frame_len(xfer, n, xfer->max_frame_size); } usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } } static void ugen_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t actlen; usb_frlength_t offset; usb_frcount_t n; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: offset = 0; for (n = 0; n != xfer->nframes; n++) { if (usb_fifo_get_data(f, xfer->frbuffers, offset, xfer->max_frame_size, &actlen, 1)) { usbd_xfer_set_frame_len(xfer, n, actlen); offset += actlen; } else { break; } } for (; n != xfer->nframes; n++) { /* fill in zero frames */ usbd_xfer_set_frame_len(xfer, n, 0); } usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } } static int ugen_set_config(struct usb_fifo *f, uint8_t index) { DPRINTFN(2, "index %u\n", index); if (f->udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } if (usbd_start_set_config(f->udev, index) != 0) return (EIO); return (0); } static int ugen_set_interface(struct usb_fifo *f, uint8_t iface_index, uint8_t alt_index) { DPRINTFN(2, "%u, %u\n", iface_index, alt_index); if (f->udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } /* change setting - will free generic FIFOs, if any */ if (usbd_set_alt_interface_index(f->udev, iface_index, alt_index)) { return (EIO); } /* probe and attach */ if (usb_probe_and_attach(f->udev, iface_index)) { return (EIO); } return (0); } /*------------------------------------------------------------------------* * ugen_get_cdesc * * This function will retrieve the complete configuration descriptor * at the given index. *------------------------------------------------------------------------*/ static int ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { struct usb_config_descriptor *cdesc; struct usb_device *udev = f->udev; int error; uint16_t len; uint8_t free_data; DPRINTFN(6, "\n"); if (ugd->ugd_data == NULL) { /* userland pointer should not be zero */ return (EINVAL); } if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) || (ugd->ugd_config_index == udev->curr_config_index)) { cdesc = usbd_get_config_descriptor(udev); if (cdesc == NULL) return (ENXIO); free_data = 0; } else { #if (USB_HAVE_FIXED_CONFIG == 0) if (usbd_req_get_config_desc_full(udev, NULL, &cdesc, ugd->ugd_config_index)) { return (ENXIO); } free_data = 1; #else /* configuration descriptor data is shared */ return (EINVAL); #endif } len = UGETW(cdesc->wTotalLength); if (len > ugd->ugd_maxlen) { len = ugd->ugd_maxlen; } DPRINTFN(6, "len=%u\n", len); ugd->ugd_actlen = len; ugd->ugd_offset = 0; error = copyout(cdesc, ugd->ugd_data, len); if (free_data) usbd_free_config_desc(udev, cdesc); return (error); } -/* - * This function is called having the enumeration SX locked which - * protects the scratch area used. - */ static int ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { void *ptr; uint16_t size; int error; + uint8_t do_unlock; + /* Protect scratch area */ + do_unlock = usbd_ctrl_lock(f->udev); + ptr = f->udev->scratch.data; size = sizeof(f->udev->scratch.data); if (usbd_req_get_string_desc(f->udev, NULL, ptr, size, ugd->ugd_lang_id, ugd->ugd_string_index)) { error = EINVAL; } else { if (size > ((uint8_t *)ptr)[0]) { size = ((uint8_t *)ptr)[0]; } if (size > ugd->ugd_maxlen) { size = ugd->ugd_maxlen; } ugd->ugd_actlen = size; ugd->ugd_offset = 0; error = copyout(ptr, ugd->ugd_data, size); } + if (do_unlock) + usbd_ctrl_unlock(f->udev); + return (error); } /*------------------------------------------------------------------------* * ugen_get_iface_driver * * This function generates an USB interface description for userland. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { struct usb_device *udev = f->udev; struct usb_interface *iface; const char *ptr; const char *desc; unsigned int len; unsigned int maxlen; char buf[128]; int error; DPRINTFN(6, "\n"); if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { /* userland pointer should not be zero */ return (EINVAL); } iface = usbd_get_iface(udev, ugd->ugd_iface_index); if ((iface == NULL) || (iface->idesc == NULL)) { /* invalid interface index */ return (EINVAL); } /* read out device nameunit string, if any */ if ((iface->subdev != NULL) && device_is_attached(iface->subdev) && (ptr = device_get_nameunit(iface->subdev)) && (desc = device_get_desc(iface->subdev))) { /* print description */ snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); /* range checks */ maxlen = ugd->ugd_maxlen - 1; len = strlen(buf); if (len > maxlen) len = maxlen; /* update actual length, including terminating zero */ ugd->ugd_actlen = len + 1; /* copy out interface description */ error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); } else { /* zero length string is default */ error = copyout("", ugd->ugd_data, 1); } return (error); } /*------------------------------------------------------------------------* * usb_gen_fill_deviceinfo * * This function dumps information about an USB device to the * structure pointed to by the "di" argument. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usb_gen_fill_deviceinfo(struct usb_fifo *f, struct usb_device_info *di) { struct usb_device *udev; struct usb_device *hub; udev = f->udev; bzero(di, sizeof(di[0])); di->udi_bus = device_get_unit(udev->bus->bdev); di->udi_addr = udev->address; di->udi_index = udev->device_index; strlcpy(di->udi_serial, usb_get_serial(udev), sizeof(di->udi_serial)); strlcpy(di->udi_vendor, usb_get_manufacturer(udev), sizeof(di->udi_vendor)); strlcpy(di->udi_product, usb_get_product(udev), sizeof(di->udi_product)); usb_printbcd(di->udi_release, sizeof(di->udi_release), UGETW(udev->ddesc.bcdDevice)); di->udi_vendorNo = UGETW(udev->ddesc.idVendor); di->udi_productNo = UGETW(udev->ddesc.idProduct); di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); di->udi_class = udev->ddesc.bDeviceClass; di->udi_subclass = udev->ddesc.bDeviceSubClass; di->udi_protocol = udev->ddesc.bDeviceProtocol; di->udi_config_no = udev->curr_config_no; di->udi_config_index = udev->curr_config_index; di->udi_power = udev->flags.self_powered ? 0 : udev->power; di->udi_speed = udev->speed; di->udi_mode = udev->flags.usb_mode; di->udi_power_mode = udev->power_mode; di->udi_suspended = udev->flags.peer_suspended; hub = udev->parent_hub; if (hub) { di->udi_hubaddr = hub->address; di->udi_hubindex = hub->device_index; di->udi_hubport = udev->port_no; } return (0); } /*------------------------------------------------------------------------* * ugen_check_request * * Return values: * 0: Access allowed * Else: No access *------------------------------------------------------------------------*/ static int ugen_check_request(struct usb_device *udev, struct usb_device_request *req) { struct usb_endpoint *ep; int error; /* * Avoid requests that would damage the bus integrity: */ if (((req->bmRequestType == UT_WRITE_DEVICE) && (req->bRequest == UR_SET_ADDRESS)) || ((req->bmRequestType == UT_WRITE_DEVICE) && (req->bRequest == UR_SET_CONFIG)) || ((req->bmRequestType == UT_WRITE_INTERFACE) && (req->bRequest == UR_SET_INTERFACE))) { /* * These requests can be useful for testing USB drivers. */ error = priv_check(curthread, PRIV_DRIVER); if (error) { return (error); } } /* * Special case - handle clearing of stall */ if (req->bmRequestType == UT_WRITE_ENDPOINT) { ep = usbd_get_ep_by_addr(udev, req->wIndex[0]); if (ep == NULL) { return (EINVAL); } if ((req->bRequest == UR_CLEAR_FEATURE) && (UGETW(req->wValue) == UF_ENDPOINT_HALT)) { usbd_clear_data_toggle(udev, ep); } } /* TODO: add more checks to verify the interface index */ return (0); } int ugen_do_request(struct usb_fifo *f, struct usb_ctl_request *ur) { int error; uint16_t len; uint16_t actlen; if (ugen_check_request(f->udev, &ur->ucr_request)) { return (EPERM); } len = UGETW(ur->ucr_request.wLength); /* check if "ucr_data" is valid */ if (len != 0) { if (ur->ucr_data == NULL) { return (EFAULT); } } /* do the USB request */ error = usbd_do_request_flags (f->udev, NULL, &ur->ucr_request, ur->ucr_data, (ur->ucr_flags & USB_SHORT_XFER_OK) | USB_USER_DATA_PTR, &actlen, USB_DEFAULT_TIMEOUT); ur->ucr_actlen = actlen; if (error) { error = EIO; } return (error); } /*------------------------------------------------------------------------ * ugen_re_enumerate *------------------------------------------------------------------------*/ static int ugen_re_enumerate(struct usb_fifo *f) { struct usb_device *udev = f->udev; int error; /* * This request can be useful for testing USB drivers: */ error = priv_check(curthread, PRIV_DRIVER); if (error) { return (error); } if (udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ DPRINTFN(6, "device mode\n"); return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } /* start re-enumeration of device */ usbd_start_re_enumerate(udev); return (0); } int ugen_fs_uninit(struct usb_fifo *f) { if (f->fs_xfer == NULL) { return (EINVAL); } usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); free(f->fs_xfer, M_USB); f->fs_xfer = NULL; f->fs_ep_max = 0; f->fs_ep_ptr = NULL; f->flag_iscomplete = 0; usb_fifo_free_buffer(f); return (0); } static uint8_t ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->used_q, m); if (m) { *pindex = *((uint8_t *)(m->cur_data_ptr)); USB_IF_ENQUEUE(&f->free_q, m); return (0); /* success */ } else { *pindex = 0; /* fix compiler warning */ f->flag_iscomplete = 0; } return (1); /* failure */ } static void ugen_fs_set_complete(struct usb_fifo *f, uint8_t index) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->free_q, m); if (m == NULL) { /* can happen during close */ DPRINTF("out of buffers\n"); return; } USB_MBUF_RESET(m); *((uint8_t *)(m->cur_data_ptr)) = index; USB_IF_ENQUEUE(&f->used_q, m); f->flag_iscomplete = 1; usb_fifo_wakeup(f); } static int ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index) { struct usb_device_request *req; struct usb_xfer *xfer; struct usb_fs_endpoint fs_ep; void *uaddr; /* userland pointer */ void *kaddr; usb_frlength_t offset; usb_frlength_t rem; usb_frcount_t n; uint32_t length; int error; uint8_t isread; if (ep_index >= f->fs_ep_max) { return (EINVAL); } xfer = f->fs_xfer[ep_index]; if (xfer == NULL) { return (EINVAL); } mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ } mtx_unlock(f->priv_mtx); error = copyin(f->fs_ep_ptr + ep_index, &fs_ep, sizeof(fs_ep)); if (error) { return (error); } /* security checks */ if (fs_ep.nFrames > xfer->max_frame_count) { xfer->error = USB_ERR_INVAL; goto complete; } if (fs_ep.nFrames == 0) { xfer->error = USB_ERR_INVAL; goto complete; } error = copyin(fs_ep.ppBuffer, &uaddr, sizeof(uaddr)); if (error) { return (error); } /* reset first frame */ usbd_xfer_set_frame_offset(xfer, 0, 0); if (xfer->flags_int.control_xfr) { req = xfer->frbuffers[0].buffer; error = copyin(fs_ep.pLength, &length, sizeof(length)); if (error) { return (error); } if (length != sizeof(*req)) { xfer->error = USB_ERR_INVAL; goto complete; } if (length != 0) { error = copyin(uaddr, req, length); if (error) { return (error); } } if (ugen_check_request(f->udev, req)) { xfer->error = USB_ERR_INVAL; goto complete; } usbd_xfer_set_frame_len(xfer, 0, length); /* Host mode only ! */ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { isread = 1; } else { isread = 0; } n = 1; offset = sizeof(*req); } else { /* Device and Host mode */ if (USB_GET_DATA_ISREAD(xfer)) { isread = 1; } else { isread = 0; } n = 0; offset = 0; } rem = usbd_xfer_max_len(xfer); xfer->nframes = fs_ep.nFrames; xfer->timeout = fs_ep.timeout; if (xfer->timeout > 65535) { xfer->timeout = 65535; } if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK) xfer->flags.short_xfer_ok = 1; else xfer->flags.short_xfer_ok = 0; if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK) xfer->flags.short_frames_ok = 1; else xfer->flags.short_frames_ok = 0; if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT) xfer->flags.force_short_xfer = 1; else xfer->flags.force_short_xfer = 0; if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL) usbd_xfer_set_stall(xfer); else xfer->flags.stall_pipe = 0; for (; n != xfer->nframes; n++) { error = copyin(fs_ep.pLength + n, &length, sizeof(length)); if (error) { break; } usbd_xfer_set_frame_len(xfer, n, length); if (length > rem) { xfer->error = USB_ERR_INVAL; goto complete; } rem -= length; if (!isread) { /* we need to know the source buffer */ error = copyin(fs_ep.ppBuffer + n, &uaddr, sizeof(uaddr)); if (error) { break; } if (xfer->flags_int.isochronous_xfr) { /* get kernel buffer address */ kaddr = xfer->frbuffers[0].buffer; kaddr = USB_ADD_BYTES(kaddr, offset); } else { /* set current frame offset */ usbd_xfer_set_frame_offset(xfer, offset, n); /* get kernel buffer address */ kaddr = xfer->frbuffers[n].buffer; } /* move data */ error = copyin(uaddr, kaddr, length); if (error) { break; } } offset += length; } return (error); complete: mtx_lock(f->priv_mtx); ugen_fs_set_complete(f, ep_index); mtx_unlock(f->priv_mtx); return (0); } static int ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index) { struct usb_device_request *req; struct usb_xfer *xfer; struct usb_fs_endpoint fs_ep; struct usb_fs_endpoint *fs_ep_uptr; /* userland ptr */ void *uaddr; /* userland ptr */ void *kaddr; usb_frlength_t offset; usb_frlength_t rem; usb_frcount_t n; uint32_t length; uint32_t temp; int error; uint8_t isread; if (ep_index >= f->fs_ep_max) return (EINVAL); xfer = f->fs_xfer[ep_index]; if (xfer == NULL) return (EINVAL); mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ } mtx_unlock(f->priv_mtx); fs_ep_uptr = f->fs_ep_ptr + ep_index; error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep)); if (error) { return (error); } fs_ep.status = xfer->error; fs_ep.aFrames = xfer->aframes; fs_ep.isoc_time_complete = xfer->isoc_time_complete; if (xfer->error) { goto complete; } if (xfer->flags_int.control_xfr) { req = xfer->frbuffers[0].buffer; /* Host mode only ! */ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { isread = 1; } else { isread = 0; } if (xfer->nframes == 0) n = 0; /* should never happen */ else n = 1; } else { /* Device and Host mode */ if (USB_GET_DATA_ISREAD(xfer)) { isread = 1; } else { isread = 0; } n = 0; } /* Update lengths and copy out data */ rem = usbd_xfer_max_len(xfer); offset = 0; for (; n != xfer->nframes; n++) { /* get initial length into "temp" */ error = copyin(fs_ep.pLength + n, &temp, sizeof(temp)); if (error) { return (error); } if (temp > rem) { /* the userland length has been corrupted */ DPRINTF("corrupt userland length " "%u > %u\n", temp, rem); fs_ep.status = USB_ERR_INVAL; goto complete; } rem -= temp; /* get actual transfer length */ length = xfer->frlengths[n]; if (length > temp) { /* data overflow */ fs_ep.status = USB_ERR_INVAL; DPRINTF("data overflow %u > %u\n", length, temp); goto complete; } if (isread) { /* we need to know the destination buffer */ error = copyin(fs_ep.ppBuffer + n, &uaddr, sizeof(uaddr)); if (error) { return (error); } if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ kaddr = USB_ADD_BYTES( xfer->frbuffers[0].buffer, offset); } else { /* multiple frame buffers */ kaddr = xfer->frbuffers[n].buffer; } /* move data */ error = copyout(kaddr, uaddr, length); if (error) { return (error); } } /* * Update offset according to initial length, which is * needed by isochronous transfers! */ offset += temp; /* update length */ error = copyout(&length, fs_ep.pLength + n, sizeof(length)); if (error) { return (error); } } complete: /* update "aFrames" */ error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames, sizeof(fs_ep.aFrames)); if (error) goto done; /* update "isoc_time_complete" */ error = copyout(&fs_ep.isoc_time_complete, &fs_ep_uptr->isoc_time_complete, sizeof(fs_ep.isoc_time_complete)); if (error) goto done; /* update "status" */ error = copyout(&fs_ep.status, &fs_ep_uptr->status, sizeof(fs_ep.status)); done: return (error); } static uint8_t ugen_fifo_in_use(struct usb_fifo *f, int fflags) { struct usb_fifo *f_rx; struct usb_fifo *f_tx; f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; if ((fflags & FREAD) && f_rx && (f_rx->xfer[0] || f_rx->xfer[1])) { return (1); /* RX FIFO in use */ } if ((fflags & FWRITE) && f_tx && (f_tx->xfer[0] || f_tx->xfer[1])) { return (1); /* TX FIFO in use */ } return (0); /* not in use */ } static int ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { struct usb_config usb_config[1]; struct usb_device_request req; union { struct usb_fs_complete *pcomp; struct usb_fs_start *pstart; struct usb_fs_stop *pstop; struct usb_fs_open *popen; struct usb_fs_open_stream *popen_stream; struct usb_fs_close *pclose; struct usb_fs_clear_stall_sync *pstall; void *addr; } u; struct usb_endpoint *ep; struct usb_endpoint_descriptor *ed; struct usb_xfer *xfer; int error = 0; uint8_t iface_index; uint8_t isread; uint8_t ep_index; uint8_t pre_scale; u.addr = addr; DPRINTFN(6, "cmd=0x%08lx\n", cmd); switch (cmd) { case USB_FS_COMPLETE: mtx_lock(f->priv_mtx); error = ugen_fs_get_complete(f, &ep_index); mtx_unlock(f->priv_mtx); if (error) { error = EBUSY; break; } u.pcomp->ep_index = ep_index; error = ugen_fs_copy_out(f, u.pcomp->ep_index); break; case USB_FS_START: error = ugen_fs_copy_in(f, u.pstart->ep_index); if (error) break; mtx_lock(f->priv_mtx); xfer = f->fs_xfer[u.pstart->ep_index]; usbd_transfer_start(xfer); mtx_unlock(f->priv_mtx); break; case USB_FS_STOP: if (u.pstop->ep_index >= f->fs_ep_max) { error = EINVAL; break; } mtx_lock(f->priv_mtx); xfer = f->fs_xfer[u.pstart->ep_index]; if (usbd_transfer_pending(xfer)) { usbd_transfer_stop(xfer); /* * Check if the USB transfer was stopped * before it was even started. Else a cancel * callback will be pending. */ if (!xfer->flags_int.transferring) { ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); } } mtx_unlock(f->priv_mtx); break; case USB_FS_OPEN: case USB_FS_OPEN_STREAM: if (u.popen->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.popen->ep_index] != NULL) { error = EBUSY; break; } if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; } if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { pre_scale = 1; u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; } else { pre_scale = 0; } if (u.popen->max_frames > USB_FS_MAX_FRAMES) { u.popen->max_frames = USB_FS_MAX_FRAMES; break; } if (u.popen->max_frames == 0) { error = EINVAL; break; } ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no); if (ep == NULL) { error = EINVAL; break; } ed = ep->edesc; if (ed == NULL) { error = ENXIO; break; } iface_index = ep->iface_index; memset(usb_config, 0, sizeof(usb_config)); usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; if (pre_scale != 0) usb_config[0].flags.pre_scale_frames = 1; usb_config[0].callback = &ugen_ctrl_fs_callback; usb_config[0].timeout = 0; /* no timeout */ usb_config[0].frames = u.popen->max_frames; usb_config[0].bufsize = u.popen->max_bufsize; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ if (cmd == USB_FS_OPEN_STREAM) usb_config[0].stream_id = u.popen_stream->stream_id; if (usb_config[0].type == UE_CONTROL) { if (f->udev->flags.usb_mode != USB_MODE_HOST) { error = EINVAL; break; } } else { isread = ((usb_config[0].endpoint & (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); if (f->udev->flags.usb_mode != USB_MODE_HOST) { isread = !isread; } /* check permissions */ if (isread) { if (!(fflags & FREAD)) { error = EPERM; break; } } else { if (!(fflags & FWRITE)) { error = EPERM; break; } } } error = usbd_transfer_setup(f->udev, &iface_index, f->fs_xfer + u.popen->ep_index, usb_config, 1, f, f->priv_mtx); if (error == 0) { /* update maximums */ u.popen->max_packet_length = f->fs_xfer[u.popen->ep_index]->max_frame_size; u.popen->max_bufsize = f->fs_xfer[u.popen->ep_index]->max_data_length; /* update number of frames */ u.popen->max_frames = f->fs_xfer[u.popen->ep_index]->nframes; /* store index of endpoint */ f->fs_xfer[u.popen->ep_index]->priv_fifo = ((uint8_t *)0) + u.popen->ep_index; } else { error = ENOMEM; } break; case USB_FS_CLOSE: if (u.pclose->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.pclose->ep_index] == NULL) { error = EINVAL; break; } usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); break; case USB_FS_CLEAR_STALL_SYNC: if (u.pstall->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.pstall->ep_index] == NULL) { error = EINVAL; break; } if (f->udev->flags.usb_mode != USB_MODE_HOST) { error = EINVAL; break; } mtx_lock(f->priv_mtx); error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]); mtx_unlock(f->priv_mtx); if (error) { return (EBUSY); } ep = f->fs_xfer[u.pstall->ep_index]->endpoint; /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = ep->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); error = usbd_do_request(f->udev, NULL, &req, NULL); if (error == 0) { usbd_clear_data_toggle(f->udev, ep); } else { error = ENXIO; } break; default: error = ENOIOCTL; break; } DPRINTFN(6, "error=%d\n", error); return (error); } static int ugen_set_short_xfer(struct usb_fifo *f, void *addr) { uint8_t t; if (*(int *)addr) t = 1; else t = 0; if (f->flag_short == t) { /* same value like before - accept */ return (0); } if (f->xfer[0] || f->xfer[1]) { /* cannot change this during transfer */ return (EBUSY); } f->flag_short = t; return (0); } static int ugen_set_timeout(struct usb_fifo *f, void *addr) { f->timeout = *(int *)addr; if (f->timeout > 65535) { /* limit user input */ f->timeout = 65535; } return (0); } static int ugen_get_frame_size(struct usb_fifo *f, void *addr) { if (f->xfer[0]) { *(int *)addr = f->xfer[0]->max_frame_size; } else { return (EINVAL); } return (0); } static int ugen_set_buffer_size(struct usb_fifo *f, void *addr) { usb_frlength_t t; if (*(int *)addr < 0) t = 0; /* use "wMaxPacketSize" */ else if (*(int *)addr < (256 * 1024)) t = *(int *)addr; else t = 256 * 1024; if (f->bufsize == t) { /* same value like before - accept */ return (0); } if (f->xfer[0] || f->xfer[1]) { /* cannot change this during transfer */ return (EBUSY); } f->bufsize = t; return (0); } static int ugen_get_buffer_size(struct usb_fifo *f, void *addr) { *(int *)addr = f->bufsize; return (0); } static int ugen_get_iface_desc(struct usb_fifo *f, struct usb_interface_descriptor *idesc) { struct usb_interface *iface; iface = usbd_get_iface(f->udev, f->iface_index); if (iface && iface->idesc) { *idesc = *(iface->idesc); } else { return (EIO); } return (0); } static int ugen_get_endpoint_desc(struct usb_fifo *f, struct usb_endpoint_descriptor *ed) { struct usb_endpoint *ep; ep = usb_fifo_softc(f); if (ep && ep->edesc) { *ed = *ep->edesc; } else { return (EINVAL); } return (0); } static int ugen_set_power_mode(struct usb_fifo *f, int mode) { struct usb_device *udev = f->udev; int err; uint8_t old_mode; if ((udev == NULL) || (udev->parent_hub == NULL)) { return (EINVAL); } err = priv_check(curthread, PRIV_DRIVER); if (err) return (err); /* get old power mode */ old_mode = udev->power_mode; /* if no change, then just return */ if (old_mode == mode) return (0); switch (mode) { case USB_POWER_MODE_OFF: if (udev->flags.usb_mode == USB_MODE_HOST && udev->re_enumerate_wait == USB_RE_ENUM_DONE) { udev->re_enumerate_wait = USB_RE_ENUM_PWR_OFF; } /* set power mode will wake up the explore thread */ break; case USB_POWER_MODE_ON: case USB_POWER_MODE_SAVE: break; case USB_POWER_MODE_RESUME: #if USB_HAVE_POWERD /* let USB-powerd handle resume */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs++; udev->pwr_save.last_xfer_time = ticks; USB_BUS_UNLOCK(udev->bus); /* set new power mode */ usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); /* wait for resume to complete */ usb_pause_mtx(NULL, hz / 4); /* clear write reference */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs--; USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; case USB_POWER_MODE_SUSPEND: #if USB_HAVE_POWERD /* let USB-powerd handle suspend */ USB_BUS_LOCK(udev->bus); udev->pwr_save.last_xfer_time = ticks - (256 * hz); USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; default: return (EINVAL); } if (err) return (ENXIO); /* I/O failure */ /* if we are powered off we need to re-enumerate first */ if (old_mode == USB_POWER_MODE_OFF) { if (udev->flags.usb_mode == USB_MODE_HOST && udev->re_enumerate_wait == USB_RE_ENUM_DONE) { udev->re_enumerate_wait = USB_RE_ENUM_START; } /* set power mode will wake up the explore thread */ } /* set new power mode */ usbd_set_power_mode(udev, mode); return (0); /* success */ } static int ugen_get_power_mode(struct usb_fifo *f) { struct usb_device *udev = f->udev; if (udev == NULL) return (USB_POWER_MODE_ON); return (udev->power_mode); } static int ugen_get_port_path(struct usb_fifo *f, struct usb_device_port_path *dpp) { struct usb_device *udev = f->udev; struct usb_device *next; unsigned int nlevel = 0; if (udev == NULL) goto error; dpp->udp_bus = device_get_unit(udev->bus->bdev); dpp->udp_index = udev->device_index; /* count port levels */ next = udev; while (next->parent_hub != NULL) { nlevel++; next = next->parent_hub; } /* check if too many levels */ if (nlevel > USB_DEVICE_PORT_PATH_MAX) goto error; /* store total level of ports */ dpp->udp_port_level = nlevel; /* store port index array */ next = udev; while (next->parent_hub != NULL) { dpp->udp_port_no[--nlevel] = next->port_no; next = next->parent_hub; } return (0); /* success */ error: return (EINVAL); /* failure */ } static int ugen_get_power_usage(struct usb_fifo *f) { struct usb_device *udev = f->udev; if (udev == NULL) return (0); return (udev->power); } static int ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no, uint8_t set, uint16_t feature) { struct usb_device *udev = f->udev; struct usb_hub *hub; int err; err = priv_check(curthread, PRIV_DRIVER); if (err) { return (err); } if (port_no == 0) { return (EINVAL); } if ((udev == NULL) || (udev->hub == NULL)) { return (EINVAL); } hub = udev->hub; if (port_no > hub->nports) { return (EINVAL); } if (set) err = usbd_req_set_port_feature(udev, NULL, port_no, feature); else err = usbd_req_clear_port_feature(udev, NULL, port_no, feature); if (err) return (ENXIO); /* failure */ return (0); /* success */ } static int ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { struct usb_fifo *f_rx; struct usb_fifo *f_tx; int error = 0; f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; switch (cmd) { case USB_SET_RX_SHORT_XFER: if (fflags & FREAD) { error = ugen_set_short_xfer(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_FORCE_SHORT: if (fflags & FWRITE) { error = ugen_set_short_xfer(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_TIMEOUT: if (fflags & FREAD) { error = ugen_set_timeout(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_TIMEOUT: if (fflags & FWRITE) { error = ugen_set_timeout(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_FRAME_SIZE: if (fflags & FREAD) { error = ugen_get_frame_size(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_FRAME_SIZE: if (fflags & FWRITE) { error = ugen_get_frame_size(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_BUFFER_SIZE: if (fflags & FREAD) { error = ugen_set_buffer_size(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_BUFFER_SIZE: if (fflags & FWRITE) { error = ugen_set_buffer_size(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_BUFFER_SIZE: if (fflags & FREAD) { error = ugen_get_buffer_size(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_BUFFER_SIZE: if (fflags & FWRITE) { error = ugen_get_buffer_size(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_INTERFACE_DESC: if (fflags & FREAD) { error = ugen_get_iface_desc(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_INTERFACE_DESC: if (fflags & FWRITE) { error = ugen_get_iface_desc(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_ENDPOINT_DESC: if (fflags & FREAD) { error = ugen_get_endpoint_desc(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_ENDPOINT_DESC: if (fflags & FWRITE) { error = ugen_get_endpoint_desc(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_STALL_FLAG: if ((fflags & FREAD) && (*(int *)addr)) { f_rx->flag_stall = 1; } break; case USB_SET_TX_STALL_FLAG: if ((fflags & FWRITE) && (*(int *)addr)) { f_tx->flag_stall = 1; } break; default: error = ENOIOCTL; break; } return (error); } static int ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { union { struct usb_interface_descriptor *idesc; struct usb_alt_interface *ai; struct usb_device_descriptor *ddesc; struct usb_config_descriptor *cdesc; struct usb_device_stats *stat; struct usb_fs_init *pinit; struct usb_fs_uninit *puninit; struct usb_device_port_path *dpp; uint32_t *ptime; void *addr; int *pint; } u; struct usb_device_descriptor *dtemp; struct usb_config_descriptor *ctemp; struct usb_interface *iface; int error = 0; uint8_t n; u.addr = addr; DPRINTFN(6, "cmd=0x%08lx\n", cmd); switch (cmd) { case USB_DISCOVER: usb_needs_explore_all(); break; case USB_SETDEBUG: if (!(fflags & FWRITE)) { error = EPERM; break; } usb_debug = *(int *)addr; break; case USB_GET_CONFIG: *(int *)addr = f->udev->curr_config_index; break; case USB_SET_CONFIG: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_set_config(f, *(int *)addr); break; case USB_GET_ALTINTERFACE: iface = usbd_get_iface(f->udev, u.ai->uai_interface_index); if (iface && iface->idesc) { u.ai->uai_alt_index = iface->alt_index; } else { error = EINVAL; } break; case USB_SET_ALTINTERFACE: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_set_interface(f, u.ai->uai_interface_index, u.ai->uai_alt_index); break; case USB_GET_DEVICE_DESC: dtemp = usbd_get_device_descriptor(f->udev); if (!dtemp) { error = EIO; break; } *u.ddesc = *dtemp; break; case USB_GET_CONFIG_DESC: ctemp = usbd_get_config_descriptor(f->udev); if (!ctemp) { error = EIO; break; } *u.cdesc = *ctemp; break; case USB_GET_FULL_DESC: error = ugen_get_cdesc(f, addr); break; case USB_GET_STRING_DESC: error = ugen_get_sdesc(f, addr); break; case USB_GET_IFACE_DRIVER: error = ugen_get_iface_driver(f, addr); break; case USB_REQUEST: case USB_DO_REQUEST: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_do_request(f, addr); break; case USB_DEVICEINFO: case USB_GET_DEVICEINFO: error = usb_gen_fill_deviceinfo(f, addr); break; case USB_DEVICESTATS: for (n = 0; n != 4; n++) { u.stat->uds_requests_fail[n] = f->udev->bus->stats_err.uds_requests[n]; u.stat->uds_requests_ok[n] = f->udev->bus->stats_ok.uds_requests[n]; } break; case USB_DEVICEENUMERATE: error = ugen_re_enumerate(f); break; case USB_GET_PLUGTIME: *u.ptime = f->udev->plugtime; break; case USB_CLAIM_INTERFACE: case USB_RELEASE_INTERFACE: /* TODO */ break; case USB_IFACE_DRIVER_ACTIVE: n = *u.pint & 0xFF; iface = usbd_get_iface(f->udev, n); if (iface && iface->subdev) error = 0; else error = ENXIO; break; case USB_IFACE_DRIVER_DETACH: error = priv_check(curthread, PRIV_DRIVER); if (error) break; n = *u.pint & 0xFF; if (n == USB_IFACE_INDEX_ANY) { error = EINVAL; break; } /* * Detach the currently attached driver. */ usb_detach_device(f->udev, n, 0); /* * Set parent to self, this should keep attach away * until the next set configuration event. */ usbd_set_parent_iface(f->udev, n, n); break; case USB_SET_POWER_MODE: error = ugen_set_power_mode(f, *u.pint); break; case USB_GET_POWER_MODE: *u.pint = ugen_get_power_mode(f); break; case USB_GET_DEV_PORT_PATH: error = ugen_get_port_path(f, u.dpp); break; case USB_GET_POWER_USAGE: *u.pint = ugen_get_power_usage(f); break; case USB_SET_PORT_ENABLE: error = ugen_do_port_feature(f, *u.pint, 1, UHF_PORT_ENABLE); break; case USB_SET_PORT_DISABLE: error = ugen_do_port_feature(f, *u.pint, 0, UHF_PORT_ENABLE); break; case USB_FS_INIT: /* verify input parameters */ if (u.pinit->pEndpoints == NULL) { error = EINVAL; break; } if (u.pinit->ep_index_max > 127) { error = EINVAL; break; } if (u.pinit->ep_index_max == 0) { error = EINVAL; break; } if (f->fs_xfer != NULL) { error = EBUSY; break; } if (f->dev_ep_index != 0) { error = EINVAL; break; } if (ugen_fifo_in_use(f, fflags)) { error = EBUSY; break; } error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); if (error) { break; } f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); if (f->fs_xfer == NULL) { usb_fifo_free_buffer(f); error = ENOMEM; break; } f->fs_ep_max = u.pinit->ep_index_max; f->fs_ep_ptr = u.pinit->pEndpoints; break; case USB_FS_UNINIT: if (u.puninit->dummy != 0) { error = EINVAL; break; } error = ugen_fs_uninit(f); break; default: mtx_lock(f->priv_mtx); error = ugen_iface_ioctl(f, cmd, addr, fflags); mtx_unlock(f->priv_mtx); break; } DPRINTFN(6, "error=%d\n", error); return (error); } static void ugen_ctrl_fs_callback(struct usb_xfer *xfer, usb_error_t error) { ; /* workaround for a bug in "indent" */ DPRINTF("st=%u alen=%u aframes=%u\n", USB_GET_STATE(xfer), xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: usbd_transfer_submit(xfer); break; default: ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); break; } } #endif /* USB_HAVE_UGEN */ Index: head/sys/dev/usb/usb_request.c =================================================================== --- head/sys/dev/usb/usb_request.c (revision 305420) +++ head/sys/dev/usb/usb_request.c (revision 305421) @@ -1,2291 +1,2282 @@ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ static int usb_no_cs_fail; SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RWTUN, &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set"); static int usb_full_ddesc; SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RWTUN, &usb_full_ddesc, 0, "USB always read complete device descriptor, if set"); #ifdef USB_DEBUG #ifdef USB_REQ_DEBUG /* The following structures are used in connection to fault injection. */ struct usb_ctrl_debug { int bus_index; /* target bus */ int dev_index; /* target address */ int ds_fail; /* fail data stage */ int ss_fail; /* fail status stage */ int ds_delay; /* data stage delay in ms */ int ss_delay; /* status stage delay in ms */ int bmRequestType_value; int bRequest_value; }; struct usb_ctrl_debug_bits { uint16_t ds_delay; uint16_t ss_delay; uint8_t ds_fail:1; uint8_t ss_fail:1; uint8_t enabled:1; }; /* The default is to disable fault injection. */ static struct usb_ctrl_debug usb_ctrl_debug = { .bus_index = -1, .dev_index = -1, .bmRequestType_value = -1, .bRequest_value = -1, }; SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bus_index, 0, "USB controller index to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.dev_index, 0, "USB device address to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.ds_fail, 0, "USB fail data stage"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.ss_fail, 0, "USB fail status stage"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN, &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN, &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail"); SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN, &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail"); /*------------------------------------------------------------------------* * usbd_get_debug_bits * * This function is only useful in USB host mode. *------------------------------------------------------------------------*/ static void usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req, struct usb_ctrl_debug_bits *dbg) { int temp; memset(dbg, 0, sizeof(*dbg)); /* Compute data stage delay */ temp = usb_ctrl_debug.ds_delay; if (temp < 0) temp = 0; else if (temp > (16*1024)) temp = (16*1024); dbg->ds_delay = temp; /* Compute status stage delay */ temp = usb_ctrl_debug.ss_delay; if (temp < 0) temp = 0; else if (temp > (16*1024)) temp = (16*1024); dbg->ss_delay = temp; /* Check if this control request should be failed */ if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index) return; if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index) return; temp = usb_ctrl_debug.bmRequestType_value; if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255)) return; temp = usb_ctrl_debug.bRequest_value; if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255)) return; temp = usb_ctrl_debug.ds_fail; if (temp) dbg->ds_fail = 1; temp = usb_ctrl_debug.ss_fail; if (temp) dbg->ss_fail = 1; dbg->enabled = 1; } #endif /* USB_REQ_DEBUG */ #endif /* USB_DEBUG */ /*------------------------------------------------------------------------* * usbd_do_request_callback * * This function is the USB callback for generic USB Host control * transfers. *------------------------------------------------------------------------*/ void usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error) { ; /* workaround for a bug in "indent" */ DPRINTF("st=%u\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: usbd_transfer_submit(xfer); break; default: cv_signal(&xfer->xroot->udev->ctrlreq_cv); break; } } /*------------------------------------------------------------------------* * usb_do_clear_stall_callback * * This function is the USB callback for generic clear stall requests. *------------------------------------------------------------------------*/ void usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_device_request req; struct usb_device *udev; struct usb_endpoint *ep; struct usb_endpoint *ep_end; struct usb_endpoint *ep_first; usb_stream_t x; uint8_t to; udev = xfer->xroot->udev; USB_BUS_LOCK(udev->bus); /* round robin endpoint clear stall */ ep = udev->ep_curr; ep_end = udev->endpoints + udev->endpoints_max; ep_first = udev->endpoints; to = udev->endpoints_max; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: /* reset error counter */ udev->clear_stall_errors = 0; if (ep == NULL) goto tr_setup; /* device was unconfigured */ if (ep->edesc && ep->is_stalled) { ep->toggle_next = 0; ep->is_stalled = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); for (x = 0; x != USB_MAX_EP_STREAMS; x++) { /* start the current or next transfer, if any */ usb_command_wrapper(&ep->endpoint_q[x], ep->endpoint_q[x].curr); } } ep++; case USB_ST_SETUP: tr_setup: if (to == 0) break; /* no endpoints - nothing to do */ if ((ep < ep_first) || (ep >= ep_end)) ep = ep_first; /* endpoint wrapped around */ if (ep->edesc && ep->is_stalled) { /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = ep->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* copy in the transfer */ usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); /* set length */ usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); xfer->nframes = 1; USB_BUS_UNLOCK(udev->bus); usbd_transfer_submit(xfer); USB_BUS_LOCK(udev->bus); break; } ep++; to--; goto tr_setup; default: if (error == USB_ERR_CANCELLED) break; DPRINTF("Clear stall failed.\n"); /* * Some VMs like VirtualBox always return failure on * clear-stall which we sometimes should just ignore. */ if (usb_no_cs_fail) goto tr_transferred; if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) goto tr_setup; if (error == USB_ERR_TIMEOUT) { udev->clear_stall_errors = USB_CS_RESET_LIMIT; DPRINTF("Trying to re-enumerate.\n"); usbd_start_re_enumerate(udev); } else { udev->clear_stall_errors++; if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { DPRINTF("Trying to re-enumerate.\n"); usbd_start_re_enumerate(udev); } } goto tr_setup; } /* store current endpoint */ udev->ep_curr = ep; USB_BUS_UNLOCK(udev->bus); } static usb_handle_req_t * usbd_get_hr_func(struct usb_device *udev) { /* figure out if there is a Handle Request function */ if (udev->flags.usb_mode == USB_MODE_DEVICE) return (usb_temp_get_desc_p); else if (udev->parent_hub == NULL) return (udev->bus->methods->roothub_exec); else return (NULL); } /*------------------------------------------------------------------------* * 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 serialized by the use of the USB device enumeration 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) { #ifdef USB_REQ_DEBUG struct usb_ctrl_debug_bits dbg; #endif 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; uint16_t acttemp; uint8_t do_unlock; 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 != NULL) && (mtx != &Giant)) { mtx_unlock(mtx); mtx_assert(mtx, MA_NOTOWNED); } /* - * Grab the USB device enumeration SX-lock serialization is - * achieved when multiple threads are involved: + * Serialize access to this function: */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); - /* - * We need to allow suspend and resume at this point, else the - * control transfer will timeout if the device is suspended! - */ - usbd_sr_unlock(udev); - 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 memcpy(data, desc, length); } goto done; /* success */ } /* * Setup a new USB transfer or use the existing one, if any: */ usbd_ctrl_transfer_setup(udev); xfer = udev->ctrl_xfer[0]; if (xfer == NULL) { /* most likely out of memory */ err = USB_ERR_NOMEM; goto done; } #ifdef USB_REQ_DEBUG /* Get debug bits */ usbd_get_debug_bits(udev, req, &dbg); /* Check for fault injection */ if (dbg.enabled) flags |= USB_DELAY_STATUS_STAGE; #endif 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)); while (1) { temp = length; if (temp > usbd_xfer_max_len(xfer)) { temp = usbd_xfer_max_len(xfer); } #ifdef USB_REQ_DEBUG if (xfer->flags.manual_status) { if (usbd_xfer_frame_len(xfer, 0) != 0) { /* Execute data stage separately */ temp = 0; } else if (temp > 0) { if (dbg.ds_fail) { err = USB_ERR_INVAL; break; } if (dbg.ds_delay > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(dbg.ds_delay)); /* make sure we don't time out */ start_ticks = ticks; } } } #endif 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); } usbd_xfer_set_frames(xfer, 2); } else { if (usbd_xfer_frame_len(xfer, 0) == 0) { if (xfer->flags.manual_status) { #ifdef USB_REQ_DEBUG if (dbg.ss_fail) { err = USB_ERR_INVAL; break; } if (dbg.ss_delay > 0) { usb_pause_mtx( xfer->xroot->xfer_mtx, USB_MS_TO_TICKS(dbg.ss_delay)); /* make sure we don't time out */ start_ticks = ticks; } #endif xfer->flags.manual_status = 0; } else { break; } } usbd_xfer_set_frames(xfer, 1); } usbd_transfer_start(xfer); while (usbd_transfer_pending(xfer)) { cv_wait(&udev->ctrlreq_cv, xfer->xroot->xfer_mtx); } err = xfer->error; if (err) { break; } /* get actual length of DATA stage */ if (xfer->aframes < 2) { acttemp = 0; } else { acttemp = usbd_xfer_frame_len(xfer, 1); } /* check for short packet */ if (temp > acttemp) { temp = acttemp; 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: - usbd_sr_lock(udev); - if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); if ((mtx != NULL) && (mtx != &Giant)) mtx_lock(mtx); switch (err) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: break; default: DPRINTF("I/O error - waiting a bit for TT cleanup\n"); usb_pause_mtx(mtx, hz / 16); break; } return ((usb_error_t)err); } /*------------------------------------------------------------------------* * usbd_do_request_proc - factored out code * * This function is factored out code. It does basically the same like * usbd_do_request_flags, except it will check the status of the * passed process argument before doing the USB request. If the * process is draining the USB_ERR_IOERROR code will be returned. It * is assumed that the mutex associated with the process is locked * when calling this function. *------------------------------------------------------------------------*/ usb_error_t usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout) { usb_error_t err; uint16_t len; /* get request data length */ len = UGETW(req->wLength); /* check if the device is being detached */ if (usb_proc_is_gone(pproc)) { err = USB_ERR_IOERROR; goto done; } /* forward the USB request */ err = usbd_do_request_flags(udev, pproc->up_mtx, req, data, flags, actlen, timeout); done: /* on failure we zero the data */ /* on short packet we zero the unused data */ if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) { if (err) memset(data, 0, len); else if (actlen && *actlen != len) memset(((uint8_t *)data) + *actlen, 0, len - *actlen); } return (err); } /*------------------------------------------------------------------------* * usbd_req_reset_port * * This function will instruct a USB HUB to perform a reset sequence * on the specified port number. * * Returns: * 0: Success. The USB device should now be at address zero. * Else: Failure. No USB device is present and the USB port should be * disabled. *------------------------------------------------------------------------*/ usb_error_t usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; uint16_t status; uint16_t change; DPRINTF("\n"); /* clear any leftover port reset changes first */ usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); /* assert port reset on the given port */ err = usbd_req_set_port_feature( udev, mtx, port, UHF_PORT_RESET); /* check for errors */ if (err) goto done; n = 0; while (1) { /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); n += usb_port_reset_delay; err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); change = UGETW(ps.wPortChange); /* if the device disappeared, just give up */ if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; /* check if reset is complete */ if (change & UPS_C_PORT_RESET) break; /* * Some Virtual Machines like VirtualBox 4.x fail to * generate a port reset change event. Check if reset * is no longer asserted. */ if (!(status & UPS_RESET)) break; /* check for timeout */ if (n > 1000) { n = 0; break; } } /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); if (err) goto done; /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d reset returning error=%s\n", port, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * usbd_req_warm_reset_port * * This function will instruct an USB HUB to perform a warm reset * sequence on the specified port number. This kind of reset is not * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted * for SUPER-speed USB HUBs. * * Returns: * 0: Success. The USB device should now be available again. * Else: Failure. No USB device is present and the USB port should be * disabled. *------------------------------------------------------------------------*/ usb_error_t usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; uint16_t status; uint16_t change; DPRINTF("\n"); err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); switch (UPS_PORT_LINK_STATE_GET(status)) { case UPS_PORT_LS_U3: case UPS_PORT_LS_COMP_MODE: case UPS_PORT_LS_LOOPBACK: case UPS_PORT_LS_SS_INA: break; default: DPRINTF("Wrong state for warm reset\n"); return (0); } /* clear any leftover warm port reset changes first */ usbd_req_clear_port_feature(udev, mtx, port, UHF_C_BH_PORT_RESET); /* set warm port reset */ err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET); if (err) goto done; n = 0; while (1) { /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); n += usb_port_reset_delay; err = usbd_req_get_port_status(udev, mtx, &ps, port); if (err) goto done; status = UGETW(ps.wPortStatus); change = UGETW(ps.wPortChange); /* if the device disappeared, just give up */ if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; /* check if reset is complete */ if (change & UPS_C_BH_PORT_RESET) break; /* check for timeout */ if (n > 1000) { n = 0; break; } } /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_BH_PORT_RESET); if (err) goto done; /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } /* wait for the device to recover from reset */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d warm reset returning error=%s\n", port, usbd_errstr(err)); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_desc * * This function can be used to retrieve USB descriptors. It contains * some additional logic like zeroing of missing descriptor bytes and * retrying an USB descriptor in case of failure. The "min_len" * argument specifies the minimum descriptor length. The "max_len" * argument specifies the maximum descriptor length. If the real * descriptor length is less than the minimum length the missing * byte(s) will be zeroed. The type field, the second byte of the USB * descriptor, will get forced to the correct type. If the "actlen" * pointer is non-NULL, the actual length of the transfer will get * stored in the 16-bit unsigned integer which it is pointing to. The * first byte of the descriptor will not get updated. If the "actlen" * pointer is NULL the first byte of the descriptor will get updated * to reflect the actual length instead. If "min_len" is not equal to * "max_len" then this function will try to retrive the beginning of * the descriptor and base the maximum length on the first byte of the * descriptor. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_desc(struct usb_device *udev, struct mtx *mtx, uint16_t *actlen, void *desc, uint16_t min_len, uint16_t max_len, uint16_t id, uint8_t type, uint8_t index, uint8_t retries) { struct usb_device_request req; uint8_t *buf; usb_error_t err; DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n", id, type, index, max_len); req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, type, index); USETW(req.wIndex, id); while (1) { if ((min_len < 2) || (max_len < 2)) { err = USB_ERR_INVAL; goto done; } USETW(req.wLength, min_len); err = usbd_do_request_flags(udev, mtx, &req, desc, 0, NULL, 500 /* ms */); if (err) { if (!retries) { goto done; } retries--; usb_pause_mtx(mtx, hz / 5); continue; } buf = desc; if (min_len == max_len) { /* enforce correct length */ if ((buf[0] > min_len) && (actlen == NULL)) buf[0] = min_len; /* enforce correct type */ buf[1] = type; goto done; } /* range check */ if (max_len > buf[0]) { max_len = buf[0]; } /* zero minimum data */ while (min_len > max_len) { min_len--; buf[min_len] = 0; } /* set new minimum length */ min_len = max_len; } done: if (actlen != NULL) { if (err) *actlen = 0; else *actlen = min_len; } return (err); } /*------------------------------------------------------------------------* * usbd_req_get_string_any * * This function will return the string given by "string_index" * using the first language ID. The maximum length "len" includes * the terminating zero. The "len" argument should be twice as * big pluss 2 bytes, compared with the actual maximum string length ! * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, uint16_t len, uint8_t string_index) { char *s; uint8_t *temp; uint16_t i; uint16_t n; uint16_t c; uint8_t swap; usb_error_t err; if (len == 0) { /* should not happen */ return (USB_ERR_NORMAL_COMPLETION); } if (string_index == 0) { /* this is the language table */ buf[0] = 0; return (USB_ERR_INVAL); } if (udev->flags.no_strings) { buf[0] = 0; return (USB_ERR_STALLED); } err = usbd_req_get_string_desc (udev, mtx, buf, len, udev->langid, string_index); if (err) { buf[0] = 0; return (err); } temp = (uint8_t *)buf; if (temp[0] < 2) { /* string length is too short */ buf[0] = 0; return (USB_ERR_INVAL); } /* reserve one byte for terminating zero */ len--; /* find maximum length */ s = buf; n = (temp[0] / 2) - 1; if (n > len) { n = len; } /* skip descriptor header */ temp += 2; /* reset swap state */ swap = 3; /* convert and filter */ for (i = 0; (i != n); i++) { c = UGETW(temp + (2 * i)); /* convert from Unicode, handle buggy strings */ if (((c & 0xff00) == 0) && (swap & 1)) { /* Little Endian, default */ *s = c; swap = 1; } else if (((c & 0x00ff) == 0) && (swap & 2)) { /* Big Endian */ *s = c >> 8; swap = 2; } else { /* silently skip bad character */ continue; } /* * Filter by default - We only allow alphanumerical * and a few more to avoid any problems with scripts * and daemons. */ if (isalpha(*s) || isdigit(*s) || *s == '-' || *s == '+' || *s == ' ' || *s == '.' || *s == ',') { /* allowed */ s++; } /* silently skip bad character */ } *s = 0; /* zero terminate resulting string */ return (USB_ERR_NORMAL_COMPLETION); } /*------------------------------------------------------------------------* * usbd_req_get_string_desc * * If you don't know the language ID, consider using * "usbd_req_get_string_any()". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc, uint16_t max_len, uint16_t lang_id, uint8_t string_index) { return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id, UDESC_STRING, string_index, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_config_desc_ptr * * This function is used in device side mode to retrieve the pointer * to the generated config descriptor. This saves allocating space for * an additional config descriptor when setting the configuration. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_descriptor_ptr(struct usb_device *udev, struct usb_config_descriptor **ppcd, uint16_t wValue) { struct usb_device_request req; usb_handle_req_t *hr_func; const void *ptr; uint16_t len; usb_error_t err; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW(req.wValue, wValue); USETW(req.wIndex, 0); USETW(req.wLength, 0); ptr = NULL; len = 0; hr_func = usbd_get_hr_func(udev); if (hr_func == NULL) err = USB_ERR_INVAL; else { USB_BUS_LOCK(udev->bus); err = (hr_func) (udev, &req, &ptr, &len); USB_BUS_UNLOCK(udev->bus); } if (err) ptr = NULL; else if (ptr == NULL) err = USB_ERR_INVAL; *ppcd = __DECONST(struct usb_config_descriptor *, ptr); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_config_desc * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, struct usb_config_descriptor *d, uint8_t conf_index) { usb_error_t err; DPRINTFN(4, "confidx=%d\n", conf_index); err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), sizeof(*d), 0, UDESC_CONFIG, conf_index, 0); if (err) { goto done; } /* Extra sanity checking */ if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) { err = USB_ERR_INVAL; } done: return (err); } /*------------------------------------------------------------------------* * usbd_alloc_config_desc * * This function is used to allocate a zeroed configuration * descriptor. * * Returns: * NULL: Failure * Else: Success *------------------------------------------------------------------------*/ void * usbd_alloc_config_desc(struct usb_device *udev, uint32_t size) { if (size > USB_CONFIG_MAX) { DPRINTF("Configuration descriptor too big\n"); return (NULL); } #if (USB_HAVE_FIXED_CONFIG == 0) return (malloc(size, M_USBDEV, M_ZERO | M_WAITOK)); #else memset(udev->config_data, 0, sizeof(udev->config_data)); return (udev->config_data); #endif } /*------------------------------------------------------------------------* * usbd_alloc_config_desc * * This function is used to free a configuration descriptor. *------------------------------------------------------------------------*/ void usbd_free_config_desc(struct usb_device *udev, void *ptr) { #if (USB_HAVE_FIXED_CONFIG == 0) free(ptr, M_USBDEV); #endif } /*------------------------------------------------------------------------* * usbd_req_get_config_desc_full * * This function gets the complete USB configuration descriptor and * ensures that "wTotalLength" is correct. The returned configuration * descriptor is freed by calling "usbd_free_config_desc()". * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx, struct usb_config_descriptor **ppcd, uint8_t index) { struct usb_config_descriptor cd; struct usb_config_descriptor *cdesc; uint32_t len; usb_error_t err; DPRINTFN(4, "index=%d\n", index); *ppcd = NULL; err = usbd_req_get_config_desc(udev, mtx, &cd, index); if (err) return (err); /* get full descriptor */ len = UGETW(cd.wTotalLength); if (len < (uint32_t)sizeof(*cdesc)) { /* corrupt descriptor */ return (USB_ERR_INVAL); } else if (len > USB_CONFIG_MAX) { DPRINTF("Configuration descriptor was truncated\n"); len = USB_CONFIG_MAX; } cdesc = usbd_alloc_config_desc(udev, len); if (cdesc == NULL) return (USB_ERR_NOMEM); err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0, UDESC_CONFIG, index, 3); if (err) { usbd_free_config_desc(udev, cdesc); return (err); } /* make sure that the device is not fooling us: */ USETW(cdesc->wTotalLength, len); *ppcd = cdesc; return (0); /* success */ } /*------------------------------------------------------------------------* * usbd_req_get_device_desc * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx, struct usb_device_descriptor *d) { DPRINTFN(4, "\n"); return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d), sizeof(*d), 0, UDESC_DEVICE, 0, 3)); } /*------------------------------------------------------------------------* * usbd_req_get_alt_interface_no * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx, uint8_t *alt_iface_no, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 1); return (usbd_do_request(udev, mtx, &req, alt_iface_no)); } /*------------------------------------------------------------------------* * usbd_req_set_alt_interface_no * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t alt_no) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; req.wValue[0] = alt_no; req.wValue[1] = 0; req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_device_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx, struct usb_status *st) { struct usb_device_request req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(*st)); return (usbd_do_request(udev, mtx, &req, st)); } /*------------------------------------------------------------------------* * usbd_req_get_hub_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_descriptor *hd, uint8_t nports) { struct usb_device_request req; uint16_t len = (nports + 7 + (8 * 8)) / 8; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, hd)); } /*------------------------------------------------------------------------* * usbd_req_get_ss_hub_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx, struct usb_hub_ss_descriptor *hd, uint8_t nports) { struct usb_device_request req; uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8); req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_SS_HUB, 0); USETW(req.wIndex, 0); USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, hd)); } /*------------------------------------------------------------------------* * usbd_req_get_hub_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx, struct usb_hub_status *st) { struct usb_device_request req; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(struct usb_hub_status)); return (usbd_do_request(udev, mtx, &req, st)); } /*------------------------------------------------------------------------* * usbd_req_set_address * * This function is used to set the address for an USB device. After * port reset the USB device will respond at address zero. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) { struct usb_device_request req; usb_error_t err; DPRINTFN(6, "setting device address=%d\n", addr); req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = USB_ERR_INVAL; /* check if USB controller handles set address */ if (udev->bus->methods->set_address != NULL) err = (udev->bus->methods->set_address) (udev, mtx, addr); if (err != USB_ERR_INVAL) goto done; /* Setting the address should not take more than 1 second ! */ err = usbd_do_request_flags(udev, mtx, &req, NULL, USB_DELAY_STATUS_STAGE, NULL, 1000); done: /* allow device time to set new address */ usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_set_address_settle)); return (err); } /*------------------------------------------------------------------------* * usbd_req_get_port_status * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx, struct usb_port_status *ps, uint8_t port) { struct usb_device_request req; req.bmRequestType = UT_READ_CLASS_OTHER; req.bRequest = UR_GET_STATUS; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, sizeof *ps); return (usbd_do_request(udev, mtx, &req, ps)); } /*------------------------------------------------------------------------* * usbd_req_clear_hub_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_u1_timeout * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_U1_TIMEOUT); req.wIndex[0] = port; req.wIndex[1] = timeout; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_u2_timeout * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_U2_TIMEOUT); req.wIndex[0] = port; req.wIndex[1] = timeout; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_hub_depth * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, uint16_t depth) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_DEVICE; req.bRequest = UR_SET_HUB_DEPTH; USETW(req.wValue, depth); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_clear_port_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_port_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_protocol * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint16_t report) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n", iface, report, iface->idesc->bInterfaceNumber); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_PROTOCOL; USETW(req.wValue, report); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_report * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, type, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, data)); } /*------------------------------------------------------------------------* * usbd_req_get_report * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_REPORT; USETW2(req.wValue, type, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, len); return (usbd_do_request(udev, mtx, &req, data)); } /*------------------------------------------------------------------------* * usbd_req_set_idle * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t duration, uint8_t id) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "%d %d\n", duration, id); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_IDLE; USETW2(req.wValue, duration, id); req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_report_descriptor * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx, void *d, uint16_t size, uint8_t iface_index) { struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ req.wIndex[0] = iface->idesc->bInterfaceNumber; req.wIndex[1] = 0; USETW(req.wLength, size); return (usbd_do_request(udev, mtx, &req, d)); } /*------------------------------------------------------------------------* * usbd_req_set_config * * This function is used to select the current configuration number in * both USB device side mode and USB host side mode. When setting the * configuration the function of the interfaces can change. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf) { struct usb_device_request req; DPRINTF("setting config %d\n", conf); /* do "set configuration" request */ req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_CONFIG; req.wValue[0] = conf; req.wValue[1] = 0; USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_get_config * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf) { struct usb_device_request req; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_CONFIG; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 1); return (usbd_do_request(udev, mtx, &req, pconf)); } /*------------------------------------------------------------------------* * usbd_setup_device_desc *------------------------------------------------------------------------*/ usb_error_t usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx) { usb_error_t err; /* * Get the first 8 bytes of the device descriptor ! * * NOTE: "usbd_do_request()" will check the device descriptor * next time we do a request to see if the maximum packet size * changed! The 8 first bytes of the device descriptor * contains the maximum packet size to use on control endpoint * 0. If this value is different from "USB_MAX_IPACKET" a new * USB control request will be setup! */ switch (udev->speed) { case USB_SPEED_FULL: if (usb_full_ddesc != 0) { /* get full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); if (err == 0) break; } /* get partial device descriptor, some devices crash on this */ err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc, USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0); if (err != 0) break; /* get the full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); break; default: DPRINTF("Minimum bMaxPacketSize is large enough " "to hold the complete device descriptor or " "only one bMaxPacketSize choice\n"); /* get the full device descriptor */ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); /* try one more time, if error */ if (err != 0) err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc); break; } if (err != 0) { DPRINTFN(0, "getting device descriptor " "at addr %d failed, %s\n", udev->address, usbd_errstr(err)); return (err); } DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", udev->address, UGETW(udev->ddesc.bcdUSB), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, udev->ddesc.bDeviceProtocol, udev->ddesc.bMaxPacketSize, udev->ddesc.bLength, udev->speed); return (err); } /*------------------------------------------------------------------------* * usbd_req_re_enumerate * * NOTE: After this function returns the hardware is in the * unconfigured state! The application is responsible for setting a * new configuration. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) { struct usb_device *parent_hub; usb_error_t err; uint8_t old_addr; uint8_t do_retry = 1; if (udev->flags.usb_mode != USB_MODE_HOST) { return (USB_ERR_INVAL); } old_addr = udev->address; parent_hub = udev->parent_hub; if (parent_hub == NULL) { return (USB_ERR_INVAL); } retry: #if USB_HAVE_TT_SUPPORT /* * Try to reset the High Speed parent HUB of a LOW- or FULL- * speed device, if any. */ if (udev->parent_hs_hub != NULL && udev->speed != USB_SPEED_HIGH) { DPRINTF("Trying to reset parent High Speed TT.\n"); if (udev->parent_hs_hub == parent_hub && (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) + uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) { /* we can reset the whole TT */ err = usbd_req_reset_tt(parent_hub, NULL, udev->hs_port_no); } else { /* only reset a particular device and endpoint */ err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL, udev->hs_port_no, old_addr, UE_CONTROL, 0); } if (err) { DPRINTF("Resetting parent High " "Speed TT failed (%s).\n", usbd_errstr(err)); } } #endif /* Try to warm reset first */ if (parent_hub->speed == USB_SPEED_SUPER) usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no); /* Try to reset the parent HUB port. */ err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); if (err) { DPRINTFN(0, "addr=%d, port reset failed, %s\n", old_addr, usbd_errstr(err)); goto done; } /* * After that the port has been reset our device should be at * address zero: */ udev->address = USB_START_ADDR; /* reset "bMaxPacketSize" */ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET; /* reset USB state */ usb_set_device_state(udev, USB_STATE_POWERED); /* * Restore device address: */ err = usbd_req_set_address(udev, mtx, old_addr); if (err) { /* XXX ignore any errors! */ DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n", old_addr, usbd_errstr(err)); } /* * Restore device address, if the controller driver did not * set a new one: */ if (udev->address == USB_START_ADDR) udev->address = old_addr; /* setup the device descriptor and the initial "wMaxPacketSize" */ err = usbd_setup_device_desc(udev, mtx); done: if (err && do_retry) { /* give the USB firmware some time to load */ usb_pause_mtx(mtx, hz / 2); /* no more retries after this retry */ do_retry = 0; /* try again */ goto retry; } /* restore address */ if (udev->address == USB_START_ADDR) udev->address = old_addr; /* update state, if successful */ if (err == 0) usb_set_device_state(udev, USB_STATE_ADDRESSED); return (err); } /*------------------------------------------------------------------------* * usbd_req_clear_device_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_device_feature * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, sel); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_reset_tt * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, uint8_t port) { struct usb_device_request req; /* For single TT HUBs the port should be 1 */ if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_RESET_TT; USETW(req.wValue, 0); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_clear_tt_buffer * * For single TT HUBs the port should be 1. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) { struct usb_device_request req; uint16_t wValue; /* For single TT HUBs the port should be 1 */ if (udev->ddesc.bDeviceClass == UDCLASS_HUB && udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) port = 1; wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | ((endpoint & 0x80) << 8) | ((type & 3) << 12); req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_CLEAR_TT_BUFFER; USETW(req.wValue, wValue); req.wIndex[0] = port; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_port_link_state * * USB 3.0 specific request * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t link_state) { struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UHF_PORT_LINK_STATE); req.wIndex[0] = port; req.wIndex[1] = link_state; USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } /*------------------------------------------------------------------------* * usbd_req_set_lpm_info * * USB 2.0 specific request for Link Power Management. * * Returns: * 0: Success * USB_ERR_PENDING_REQUESTS: NYET * USB_ERR_TIMEOUT: TIMEOUT * USB_ERR_STALL: STALL * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_req_set_lpm_info(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t besl, uint8_t addr, uint8_t rwe) { struct usb_device_request req; usb_error_t err; uint8_t buf[1]; req.bmRequestType = UT_WRITE_CLASS_OTHER; req.bRequest = UR_SET_AND_TEST; USETW(req.wValue, UHF_PORT_L1); req.wIndex[0] = (port & 0xF) | ((besl & 0xF) << 4); req.wIndex[1] = (addr & 0x7F) | (rwe ? 0x80 : 0x00); USETW(req.wLength, sizeof(buf)); /* set default value in case of short transfer */ buf[0] = 0x00; err = usbd_do_request(udev, mtx, &req, buf); if (err) return (err); switch (buf[0]) { case 0x00: /* SUCCESS */ break; case 0x10: /* NYET */ err = USB_ERR_PENDING_REQUESTS; break; case 0x11: /* TIMEOUT */ err = USB_ERR_TIMEOUT; break; case 0x30: /* STALL */ err = USB_ERR_STALLED; break; default: /* reserved */ err = USB_ERR_IOERROR; break; } return (err); } Index: head/sys/dev/usb/usb_transfer.c =================================================================== --- head/sys/dev/usb/usb_transfer.c (revision 305420) +++ head/sys/dev/usb/usb_transfer.c (revision 305421) @@ -1,3538 +1,3538 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { const struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * If the "align" argument is equal to "1" a non-contiguous allocation * can happen. Else if the "align" argument is greater than "1", the * allocation will always be contiguous in memory. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_dma_pg; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (align == 1) { /* special case - non-cached multi page DMA memory */ n_dma_pc = count; n_dma_pg = (2 + (size / USB_PAGE_SIZE)); n_obj = 1; } else if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_dma_pg = 1; n_obj = 1; } else { /* compute number of objects per page */ #ifdef USB_DMA_SINGLE_ALLOC n_obj = 1; #else n_obj = (USB_PAGE_SIZE / size); #endif /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = howmany(count, n_obj); n_dma_pg = 1; } /* * DMA memory is allocated once, but mapped twice. That's why * there is one list for auto-free and another list for * non-auto-free which only holds the mapping and not the * allocation. */ if (parm->buf == NULL) { /* reserve memory (auto-free) */ parm->dma_page_ptr += n_dma_pc * n_dma_pg; parm->dma_page_cache_ptr += n_dma_pc; /* reserve memory (no-auto-free) */ parm->dma_page_ptr += count * n_dma_pg; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc != NULL) { if (n_obj != 1) *ppc = parm->xfer_page_cache_ptr; else *ppc = parm->dma_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; if (n_obj == 1) { /* * Avoid mapping memory twice if only a single object * should be allocated per page cache: */ for (x = 0; x != n_dma_pc; x++) { if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; } } else { for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint16_t maxp_old; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; 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->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* store max packet size value before filtering */ maxp_old = xfer->max_packet_size; /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_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]; } } /* * Check if the max packet size was outside its allowed range * and clamped to a valid value: */ if (maxp_old != xfer->max_packet_size) xfer->flags_int.maxp_was_clamped = 1; /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; if (xfer->flags.pre_scale_frames != 0) xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * 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 a lot 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_update_max_frame_size(xfer); } else { parm->err = USB_ERR_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 = USB_ERR_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; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * 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 = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* * Setup "frlengths" and shadow "frlengths" for keeping the * initial frame lengths when a USB transfer is complete. This * information is useful when computing isochronous offsets. */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { #if USB_HAVE_BUSDMA struct usb_page_search page_info; struct usb_page_cache *pc; if (usbd_transfer_setup_sub_malloc(parm, &pc, parm->bufsize, 1, 1)) { parm->err = USB_ERR_NOMEM; } else if (parm->buf != NULL) { usbd_get_page(pc, 0, &page_info); xfer->local_buffer = page_info.buffer; usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } #else /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf != NULL) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); #endif } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } static uint8_t usbd_transfer_setup_has_bulk(const struct usb_config *setup_start, uint16_t n_setup) { while (n_setup--) { uint8_t type = setup_start[n_setup].type; if (type == UE_BULK || type == UE_BULK_INTR || type == UE_TYPE_ANY) return (1); } return (0); } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; usb_error_t error = 0; uint16_t n; uint16_t refcount; uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == NULL) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* more sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (error) return (error); /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); refcount = 0; info = NULL; parm = &udev->scratch.xfer_setup[0].parm; memset(parm, 0, 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 = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits, parm->dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg) info->done_p = USB_BUS_CONTROL_XFER_PROC(udev->bus); else if (xfer_mtx == &Giant) info->done_p = USB_BUS_GIANT_PROC(udev->bus); else if (usbd_transfer_setup_has_bulk(setup_start, n_setup)) info->done_p = USB_BUS_NON_GIANT_BULK_PROC(udev->bus); else info->done_p = USB_BUS_NON_GIANT_ISOC_PROC(udev->bus); } /* reset sizes */ parm->size[0] = 0; parm->buf = buf; parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); /* * Check that the USB PIPE is valid and that * the endpoint mode is proper. * * Make sure we don't allocate a streams * transfer when such a combination is not * valid. */ if ((ep == NULL) || (ep->methods == NULL) || ((ep->ep_mode != USB_EP_MODE_STREAMS) && (ep->ep_mode != USB_EP_MODE_DEFAULT)) || (setup->stream_id != 0 && (setup->stream_id >= USB_MAX_EP_STREAMS || (ep->ep_mode != USB_EP_MODE_STREAMS)))) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &udev->scratch.xfer_setup[0].dummy; memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; /* set transfer stream ID */ xfer->stream_id = setup->stream_id; parm->size[0] += sizeof(xfer[0]); parm->methods = xfer->endpoint->methods; parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (parm); /* check for error */ if (parm->err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm->err) goto done; } if (buf != NULL || parm->err != 0) goto done; /* if no transfers, nothing to do */ if (refcount == 0) goto done; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is three: * 1) for loading memory * 2) for allocating memory * 3) for fixing memory [UHCI] */ parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm->dma_tag_max += 8; parm->dma_tag_p += parm->dma_tag_max; parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[3] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[4] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[5] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[2] = parm->size[0]; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); parm->size[6] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm->size[0]); goto done; } parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } /* check if any errors happened */ if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); error = parm->err; if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usbd_control_transfer_did_data * * This function returns non-zero if a control endpoint has * transferred the first DATA packet after the SETUP packet. * Else it returns zero. *------------------------------------------------------------------------*/ static uint8_t usbd_control_transfer_did_data(struct usb_xfer *xfer) { struct usb_device_request req; /* SETUP packet is not yet sent */ if (xfer->flags_int.control_hdr != 0) return (0); /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* compare remainder to the initial value */ return (xfer->flags_int.control_rem != UGETW(req.wLength)); } /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* update did data flag */ xfer->flags_int.control_did_data = usbd_control_transfer_did_data(xfer); /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint-> endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { /* make a copy of the frlenghts[] */ xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* enter the transfer */ (ep->methods->enter) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper( &ep->endpoint_q[xfer->stream_id], NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } void * usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) { struct usb_page_search page_info; KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); return (page_info.buffer); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } /*------------------------------------------------------------------------* * usbd_xfer_old_frame_length * * This function returns the framelength of the given frame at the * time the transfer was submitted. This function can be used to * compute the starting data pointer of the next isochronous frame * when an isochronous transfer has completed. *------------------------------------------------------------------------*/ usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex + xfer->max_frame_count]); } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if ((pq->recurse_3 != 0 || mtx_owned(info->xfer_mtx) == 0) && SCHEDULER_STOPPED() == 0) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. * 6) HW completed transfer during callback */ DPRINTFN(3, "case 5 and 6\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed! * * Postponing the callback also ensures that other USB * transfer queues get a chance. */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } #if USB_HAVE_PF if (xfer->usb_state != USB_ST_SETUP) { USB_BUS_LOCK(info->bus); usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); USB_BUS_UNLOCK(info->bus); } #endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is usually only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error, if not already set */ if (xfer->error == USB_ERR_NORMAL_COMPLETION) xfer->error = error; /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(info->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &info->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { info->bus->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { info->bus->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( USB_BUS_CS_PROC(info->bus), &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; #if USB_HAVE_TT_SUPPORT switch (xfer->error) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: /* nothing to do */ break; default: /* try to reset the TT, if any */ USB_BUS_LOCK(bus); uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint); USB_BUS_UNLOCK(bus); break; } #endif /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL); if (ep->endpoint_q[xfer->stream_id].curr != NULL || TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { /* clear third recurse flag */ pq->recurse_3 = 0; do { /* set two first recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); /* * Set third recurse flag to indicate * recursion happened: */ pq->recurse_3 = 1; } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_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); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Reset clear stall error counter. */ udev->clear_stall_errors = 0; /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; uint16_t n; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ if (udev->bus == NULL) continue; /* no BUS structure */ if (udev->bus->methods == NULL) continue; /* no BUS methods */ if (udev->bus->methods->xfer_poll == NULL) continue; /* no poll method */ /* make sure that the BUS mutex is not locked */ drop_bus = 0; while (mtx_owned(&xroot->udev->bus->bus_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(&xroot->udev->bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ drop_xfer = 0; while (mtx_owned(xroot->xfer_mtx) && !SCHEDULER_STOPPED()) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } /* Make sure cv_signal() and cv_broadcast() is not called */ USB_BUS_CONTROL_XFER_PROC(udev->bus)->up_msleep = 0; USB_BUS_EXPLORE_PROC(udev->bus)->up_msleep = 0; USB_BUS_GIANT_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_ISOC_PROC(udev->bus)->up_msleep = 0; USB_BUS_NON_GIANT_BULK_PROC(udev->bus)->up_msleep = 0; /* poll USB hardware */ (udev->bus->methods->xfer_poll) (udev->bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&xroot->udev->bus->bus_mtx); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } /* * The following function returns non-zero if the max packet size * field was clamped to a valid value. Else it returns zero. */ uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer) { return (xfer->flags_int.maxp_was_clamped); } Index: head/sys/dev/usb/usb_util.c =================================================================== --- head/sys/dev/usb/usb_util.c (revision 305420) +++ head/sys/dev/usb/usb_util.c (revision 305421) @@ -1,223 +1,223 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ /*------------------------------------------------------------------------* * device_set_usb_desc * * This function can be called at probe or attach to set the USB * device supplied textual description for the given device. *------------------------------------------------------------------------*/ void device_set_usb_desc(device_t dev) { struct usb_attach_arg *uaa; struct usb_device *udev; struct usb_interface *iface; char *temp_p; usb_error_t err; uint8_t do_unlock; if (dev == NULL) { /* should not happen */ return; } uaa = device_get_ivars(dev); if (uaa == NULL) { /* can happen if called at the wrong time */ return; } udev = uaa->device; iface = uaa->iface; if ((iface == NULL) || (iface->idesc == NULL) || (iface->idesc->iInterface == 0)) { err = USB_ERR_INVAL; } else { err = 0; } /* Protect scratch area */ - do_unlock = usbd_enum_lock(udev); + do_unlock = usbd_ctrl_lock(udev); temp_p = (char *)udev->scratch.data; if (err == 0) { /* try to get the interface string ! */ err = usbd_req_get_string_any(udev, NULL, temp_p, sizeof(udev->scratch.data), iface->idesc->iInterface); } if (err != 0) { /* use default description */ usb_devinfo(udev, temp_p, sizeof(udev->scratch.data)); } if (do_unlock) - usbd_enum_unlock(udev); + usbd_ctrl_unlock(udev); device_set_desc_copy(dev, temp_p); device_printf(dev, "<%s> on %s\n", temp_p, device_get_nameunit(udev->bus->bdev)); } /*------------------------------------------------------------------------* * usb_pause_mtx - factored out code * * This function will delay the code by the passed number of system * ticks. The passed mutex "mtx" will be dropped while waiting, if * "mtx" is different from NULL. *------------------------------------------------------------------------*/ void usb_pause_mtx(struct mtx *mtx, int timo) { if (mtx != NULL) mtx_unlock(mtx); /* * Add one tick to the timeout so that we don't return too * early! Note that pause() will assert that the passed * timeout is positive and non-zero! */ pause("USBWAIT", timo + 1); if (mtx != NULL) mtx_lock(mtx); } /*------------------------------------------------------------------------* * usb_printbcd * * This function will print the version number "bcd" to the string * pointed to by "p" having a maximum length of "p_len" bytes * including the terminating zero. *------------------------------------------------------------------------*/ void usb_printbcd(char *p, uint16_t p_len, uint16_t bcd) { if (snprintf(p, p_len, "%x.%02x", bcd >> 8, bcd & 0xff)) { /* ignore any errors */ } } /*------------------------------------------------------------------------* * usb_trim_spaces * * This function removes spaces at the beginning and the end of the string * pointed to by the "p" argument. *------------------------------------------------------------------------*/ void usb_trim_spaces(char *p) { char *q; char *e; if (p == NULL) return; q = e = p; while (*q == ' ') /* skip leading spaces */ q++; while ((*p = *q++)) /* copy string */ if (*p++ != ' ') /* remember last non-space */ e = p; *e = 0; /* kill trailing spaces */ } /*------------------------------------------------------------------------* * usb_make_str_desc - convert an ASCII string into a UNICODE string *------------------------------------------------------------------------*/ uint8_t usb_make_str_desc(void *ptr, uint16_t max_len, const char *s) { struct usb_string_descriptor *p = ptr; uint8_t totlen; int j; if (max_len < 2) { /* invalid length */ return (0); } max_len = ((max_len / 2) - 1); j = strlen(s); if (j < 0) { j = 0; } if (j > 126) { j = 126; } if (max_len > j) { max_len = j; } totlen = (max_len + 1) * 2; p->bLength = totlen; p->bDescriptorType = UDESC_STRING; while (max_len--) { USETW2(p->bString[max_len], 0, s[max_len]); } return (totlen); } Index: head/sys/sys/param.h =================================================================== --- head/sys/sys/param.h (revision 305420) +++ head/sys/sys/param.h (revision 305421) @@ -1,363 +1,363 @@ /*- * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)param.h 8.3 (Berkeley) 4/4/95 * $FreeBSD$ */ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ #include #define BSD 199506 /* System version (year & month). */ #define BSD4_3 1 #define BSD4_4 1 /* * __FreeBSD_version numbers are documented in the Porter's Handbook. * If you bump the version for any reason, you should update the documentation * there. * Currently this lives here in the doc/ repository: * * head/en_US.ISO8859-1/books/porters-handbook/versions/chapter.xml * * scheme is: Rxx * 'R' is in the range 0 to 4 if this is a release branch or * x.0-CURRENT before RELENG_*_0 is created, otherwise 'R' is * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 1200006 /* Master, propagated to newvers */ +#define __FreeBSD_version 1200007 /* Master, propagated to newvers */ /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, * which by definition is always true on FreeBSD. This macro is also defined * on other systems that use the kernel of FreeBSD, such as GNU/kFreeBSD. * * It is tempting to use this macro in userland code when we want to enable * kernel-specific routines, and in fact it's fine to do this in code that * is part of FreeBSD itself. However, be aware that as presence of this * macro is still not widespread (e.g. older FreeBSD versions, 3rd party * compilers, etc), it is STRONGLY DISCOURAGED to check for this macro in * external applications without also checking for __FreeBSD__ as an * alternative. */ #undef __FreeBSD_kernel__ #define __FreeBSD_kernel__ #ifdef _KERNEL #define P_OSREL_SIGWAIT 700000 #define P_OSREL_SIGSEGV 700004 #define P_OSREL_MAP_ANON 800104 #define P_OSREL_MAP_FSTRICT 1100036 #define P_OSREL_SHUTDOWN_ENOTCONN 1100077 #define P_OSREL_MAJOR(x) ((x) / 100000) #endif #ifndef LOCORE #include #endif /* * Machine-independent constants (some used in following include files). * Redefined constants are from POSIX 1003.1 limits file. * * MAXCOMLEN should be >= sizeof(ac_comm) (see ) */ #include #define MAXCOMLEN 19 /* max command name remembered */ #define MAXINTERP PATH_MAX /* max interpreter file name length */ #define MAXLOGNAME 33 /* max login name length (incl. NUL) */ #define MAXUPRC CHILD_MAX /* max simultaneous processes */ #define NCARGS ARG_MAX /* max bytes for an exec function */ #define NGROUPS (NGROUPS_MAX+1) /* max number groups */ #define NOFILE OPEN_MAX /* max open files per process */ #define NOGROUP 65535 /* marker for empty group set member */ #define MAXHOSTNAMELEN 256 /* max hostname size */ #define SPECNAMELEN 63 /* max length of devicename */ /* More types and definitions used throughout the kernel. */ #ifdef _KERNEL #include #include #ifndef LOCORE #include #include #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #endif #ifndef _KERNEL /* Signals. */ #include #endif /* Machine type dependent parameters. */ #include #ifndef _KERNEL #include #endif #ifndef DEV_BSHIFT #define DEV_BSHIFT 9 /* log2(DEV_BSIZE) */ #endif #define DEV_BSIZE (1<>PAGE_SHIFT) #endif /* * btodb() is messy and perhaps slow because `bytes' may be an off_t. We * want to shift an unsigned type to avoid sign extension and we don't * want to widen `bytes' unnecessarily. Assume that the result fits in * a daddr_t. */ #ifndef btodb #define btodb(bytes) /* calculates (bytes / DEV_BSIZE) */ \ (sizeof (bytes) > sizeof(long) \ ? (daddr_t)((unsigned long long)(bytes) >> DEV_BSHIFT) \ : (daddr_t)((unsigned long)(bytes) >> DEV_BSHIFT)) #endif #ifndef dbtob #define dbtob(db) /* calculates (db * DEV_BSIZE) */ \ ((off_t)(db) << DEV_BSHIFT) #endif #define PRIMASK 0x0ff #define PCATCH 0x100 /* OR'd with pri for tsleep to check signals */ #define PDROP 0x200 /* OR'd with pri to stop re-entry of interlock mutex */ #define NZERO 0 /* default "nice" */ #define NBBY 8 /* number of bits in a byte */ #define NBPW sizeof(int) /* number of bytes per word (integer) */ #define CMASK 022 /* default file mask: S_IWGRP|S_IWOTH */ #define NODEV (dev_t)(-1) /* non-existent device */ /* * File system parameters and macros. * * MAXBSIZE - Filesystems are made out of blocks of at most MAXBSIZE bytes * per block. MAXBSIZE may be made larger without effecting * any existing filesystems as long as it does not exceed MAXPHYS, * and may be made smaller at the risk of not being able to use * filesystems which require a block size exceeding MAXBSIZE. * * MAXBCACHEBUF - Maximum size of a buffer in the buffer cache. This must * be >= MAXBSIZE and can be set differently for different * architectures by defining it in . * Making this larger allows NFS to do larger reads/writes. * * BKVASIZE - Nominal buffer space per buffer, in bytes. BKVASIZE is the * minimum KVM memory reservation the kernel is willing to make. * Filesystems can of course request smaller chunks. Actual * backing memory uses a chunk size of a page (PAGE_SIZE). * The default value here can be overridden on a per-architecture * basis by defining it in . This should * probably be done to increase its value, when MAXBCACHEBUF is * defined as a larger value in . * * If you make BKVASIZE too small you risk seriously fragmenting * the buffer KVM map which may slow things down a bit. If you * make it too big the kernel will not be able to optimally use * the KVM memory reserved for the buffer cache and will wind * up with too-few buffers. * * The default is 16384, roughly 2x the block size used by a * normal UFS filesystem. */ #define MAXBSIZE 65536 /* must be power of 2 */ #ifndef MAXBCACHEBUF #define MAXBCACHEBUF MAXBSIZE /* must be a power of 2 >= MAXBSIZE */ #endif #ifndef BKVASIZE #define BKVASIZE 16384 /* must be power of 2 */ #endif #define BKVAMASK (BKVASIZE-1) /* * MAXPATHLEN defines the longest permissible path length after expanding * symbolic links. It is used to allocate a temporary buffer from the buffer * pool in which to do the name expansion, hence should be a power of two, * and must be less than or equal to MAXBSIZE. MAXSYMLINKS defines the * maximum number of symbolic links that may be expanded in a path name. * It should be set high enough to allow all legitimate uses, but halt * infinite loops reasonably quickly. */ #define MAXPATHLEN PATH_MAX #define MAXSYMLINKS 32 /* Bit map related macros. */ #define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a,i) \ (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a,i) \ ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) /* Macros for counting and rounding. */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #define rounddown(x, y) (((x)/(y))*(y)) #define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */ #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #define powerof2(x) ((((x)-1)&(x))==0) /* Macros for min/max. */ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #ifdef _KERNEL /* * Basic byte order function prototypes for non-inline functions. */ #ifndef LOCORE #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED __BEGIN_DECLS __uint32_t htonl(__uint32_t); __uint16_t htons(__uint16_t); __uint32_t ntohl(__uint32_t); __uint16_t ntohs(__uint16_t); __END_DECLS #endif #endif #ifndef lint #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif /* !_BYTEORDER_FUNC_DEFINED */ #endif /* lint */ #endif /* _KERNEL */ /* * Scale factor for scaled integers used to count %cpu time and load avgs. * * The number of CPU `tick's that map to a unique `%age' can be expressed * by the formula (1 / (2 ^ (FSHIFT - 11))). The maximum load average that * can be calculated (assuming 32 bits) can be closely approximated using * the formula (2 ^ (2 * (16 - FSHIFT))) for (FSHIFT < 15). * * For the scheduler to maintain a 1:1 mapping of CPU `tick' to `%age', * FSHIFT must be at least 11; this gives us a maximum load avg of ~1024. */ #define FSHIFT 11 /* bits to right of fixed binary point */ #define FSCALE (1<> (PAGE_SHIFT - DEV_BSHIFT)) #define ctodb(db) /* calculates pages to devblks */ \ ((db) << (PAGE_SHIFT - DEV_BSHIFT)) /* * Old spelling of __containerof(). */ #define member2struct(s, m, x) \ ((struct s *)(void *)((char *)(x) - offsetof(struct s, m))) /* * Access a variable length array that has been declared as a fixed * length array. */ #define __PAST_END(array, offset) (((__typeof__(*(array)) *)(array))[offset]) #endif /* _SYS_PARAM_H_ */