static void usbhub_remove(struct usb_device *dev) { dprintft(1, "HUB(%02x): HUB device disconnect.\n", dev->devnum); if (!dev || !dev->handle) { dprintft(1, "HUB(%02x): No device entry.\n", dev->devnum); return; } free(dev->handle); dev->handle = NULL; usbhub_clean_list (dev); return; }
/** * @brief distroy urb * @param host struct uhci_host * @param urb struct usb_request_block */ void uhci_destroy_urb(struct uhci_host *host, struct usb_request_block *urb) { struct uhci_td_meta *tdm, *nexttdm; struct usb_buffer_list *b; if (urb->status != URB_STATUS_UNLINKED) { dprintft(2, "%s: the target urb(%p) is still linked(%02x)?\n", __FUNCTION__, urb, urb->status); return; } if (URB_UHCI(urb)->qh) { URB_UHCI(urb)->qh->link = URB_UHCI(urb)->qh->element = UHCI_QH_LINK_TE; free(URB_UHCI(urb)->qh); } tdm = URB_UHCI(urb)->tdm_head; while (tdm) { if (tdm->td) { tdm->td->link = UHCI_TD_LINK_TE; tdm->td->buffer = 0U; free(tdm->td); } nexttdm = tdm->next; free(tdm); tdm = nexttdm; } while (urb->buffers) { b = urb->buffers; if (b->vadr) free((void *)b->vadr); urb->buffers = b->next; free(b); } dprintft(3, "%04x: %s: urb(%p) destroyed.\n", host->iobase, __FUNCTION__, urb); free(urb->hcpriv); free(urb); return; }
static int usbhub_set_portno(struct usb_host *usbhc, u8 devadr, u64 port) { usbhc->last_changed_port = port; dprintft(3, "HUB(%02x): HUB PORT(%d) status connect.\n", devadr, port & USB_PORT_MASK); return USB_HOOK_PASS; }
static int usbhub_device_disconnect(struct usb_host *usbhc, u8 devadr, u64 port) { struct usb_device *dev; dprintft(3, "HUB(%02x): HUB PORT(%d) status disconnect.\n", devadr, (int)(port & USB_PORT_MASK)); dev = get_device_by_port (usbhc, port); if (dev) free_device (usbhc, dev); return USB_HOOK_PASS; }
/** * @brief submit bulk urb * @param host struct uhci_host * @param device struct usb_device * @param endpoint u8 * @param data void * * @param size u16 * @param callback int * * @param arg void * * @param ioc int */ struct usb_request_block * uhci_submit_bulk(struct uhci_host *host, struct usb_device *device, u8 endpoint, void *data, u16 size, int (*callback)(struct usb_host *, struct usb_request_block *, void *), void *arg, int ioc) { struct usb_endpoint_descriptor *epdesc; epdesc = usb_epdesc(device, endpoint); if (!epdesc || (USB_EP_TRANSTYPE(epdesc) != USB_ENDPOINT_TYPE_BULK)) { if (!epdesc) dprintft(2, "%04x: %s: no endpoint(%02x) found.\n", host->iobase, __FUNCTION__, endpoint); else dprintft(2, "%04x: %s: wrong endpoint(%02x).\n", host->iobase, __FUNCTION__, USB_EP_TRANSTYPE(epdesc)); return (struct usb_request_block *)NULL; } return uhci_submit_async(host, device, epdesc, data, size, callback, arg, ioc); }
/** * @brief submit bulk urb * @param host struct uhci_host * @param device struct usb_device * @param endpoint u8 * @param data void * * @param size u16 * @param callback int * * @param arg void * * @param ioc int */ struct usb_request_block * uhci_submit_bulk(struct usb_host *usbhc, struct usb_device *device, struct usb_endpoint_descriptor *epdesc, void *data, u16 size, int (*callback)(struct usb_host *, struct usb_request_block *, void *), void *arg, int ioc) { struct uhci_host *host = (struct uhci_host *)usbhc->private; if (USB_EP_TRANSTYPE(epdesc) != USB_ENDPOINT_TYPE_BULK) { dprintft(2, "%04x: %s: wrong endpoint(%02x).\n", host->iobase, __FUNCTION__, USB_EP_TRANSTYPE(epdesc)); return (struct usb_request_block *)NULL; } return uhci_submit_async(host, device, epdesc, data, size, callback, arg, ioc); }
static void usbhub_clean_list (struct usb_device *hubdev) { struct usb_device *dev; struct usb_host *usbhc; usbhc = hubdev->host; if (!usbhc) { dprintft (1, "hubdev->host NULL.\n"); return; } again: for (dev = usbhc->device; dev; dev = dev->next) { if (dev->parent == hubdev) { /* if dev is hub, dev->next may be freed * during free_device() */ free_device (usbhc, dev); goto again; } } }
/** * @brief chek_advance * @param host struct uhci_host */ int uhci_check_advance(struct usb_host *usbhc) { struct uhci_host *host = (struct uhci_host *)usbhc->private; struct usb_request_block *urb, *nexturb; int advance = 0; int ucfn = -1; if (cmpxchgl(&host->incheck, 0U, 1U)) return 0; #if 0 in16(host->iobase + UHCI_REG_USBSTS, &usbsts); if (usbsts) dprintft(2, "%04x: %s: usbsts = %04x\n", host->iobase, __FUNCTION__, usbsts); #endif /* 0 */ spinlock_lock(&host->lock_hfl); recheck: for (urb = LIST4_HEAD (host->inproc_urbs, list); urb; urb = nexturb) { urb->prevent_del = true; spinlock_unlock(&host->lock_hfl); /* update urb->status */ if (urb->status == URB_STATUS_RUN) { if (ucfn < 0) ucfn = uhci_current_frame_number (host); uhci_check_urb_advance_sub (host, ucfn, host->hc, urb); } switch (urb->status) { default: /* errors */ dprintft(2, "%04x: %s: got some errors(%s) " "for urb(%p).\n", host->iobase, __FUNCTION__, uhci_error_status_string(urb->status), urb); /* through */ case URB_STATUS_ADVANCED: if (urb->callback) (urb->callback) (host->hc, urb, urb->cb_arg); advance++; break; case URB_STATUS_NAK: dprintft(2, "%04x: %s: got an NAK for urb(%p).\n", host->iobase, __FUNCTION__, urb); urb->status = URB_STATUS_RUN; case URB_STATUS_RUN: case URB_STATUS_FINALIZED: case URB_STATUS_UNLINKED: break; } spinlock_lock(&host->lock_hfl); nexturb = LIST4_NEXT (urb, list); urb->prevent_del = false; if (urb->deferred_del) { urb->deferred_del = false; spinlock_unlock(&host->lock_hfl); uhci_deactivate_urb(host->hc, urb); spinlock_lock(&host->lock_hfl); goto recheck; } } spinlock_unlock(&host->lock_hfl); #if 0 if (advance) { dprintft(3, "%s: USBSTS register cleared.\n", __FUNCTION__); out16(host->iobase + UHCI_REG_USBSTS, usbsts); } #endif host->incheck = 0U; return advance; }
/** * @brief submit the control messagie * @param host struct uhci_host * @param device struct usb_device * @param endpoint u8 * @param csetup struct usb_device * @param callback int * * @param arg void* * @param ioc int */ struct usb_request_block * uhci_submit_control(struct usb_host *usbhc, struct usb_device *device, u8 endpoint, u16 pktsz, struct usb_ctrl_setup *csetup, int (*callback)(struct usb_host *, struct usb_request_block *, void *), void *arg, int ioc) { struct uhci_host *host = (struct uhci_host *)usbhc->private; struct usb_request_block *urb; struct usb_endpoint_descriptor *epdesc; struct uhci_td_meta *tdm; struct usb_buffer_list *b; u32 lospeed = 0; epdesc = get_edesc_by_address(device, endpoint); if (!epdesc) { if (endpoint != 0) { dprintft(2, "%04x: %s: no endpoint(%d) found.\n", host->iobase, __FUNCTION__, endpoint); return (struct usb_request_block *)NULL; } /* use the default endpoint */ epdesc = &default_ep0; } /* determine if we are dealing with a low speed device or not */ if (device) { if (device->speed == UD_SPEED_UNDEF) { u16 portsc; ASSERT (device->portno <= UHCI_NUM_PORTS_HC); portsc = host->portsc[(device->portno-1)]; device->speed = (portsc & UHCI_PORTSC_LOSPEED) ? UD_SPEED_LOW : UD_SPEED_FULL; } lospeed = (device->speed == UD_SPEED_LOW) ? UHCI_TD_STAT_LS : 0; } dprintft(5, "%s: epdesc->wMaxPacketSize = %d\n", __FUNCTION__, epdesc->wMaxPacketSize); urb = uhci_create_urb(host); if (!urb) return (struct usb_request_block *)NULL; if (device) { spinlock_lock(&device->lock_dev); init_urb(urb, device->devnum, epdesc, callback, arg); spinlock_unlock(&device->lock_dev); } /* create a QH */ URB_UHCI(urb)->qh = uhci_alloc_qh(host, &URB_UHCI(urb)->qh_phys); if (!URB_UHCI(urb)->qh) goto fail_submit_control; URB_UHCI(urb)->qh->link = UHCI_QH_LINK_TE; if (pktsz == 0) pktsz = epdesc->wMaxPacketSize; /* SETUP TD */ URB_UHCI(urb)->tdm_head = tdm = uhci_new_td_meta(host, NULL); if (!tdm) goto fail_submit_control; URB_UHCI(urb)->qh->element = URB_UHCI(urb)->qh_element_copy = tdm->td_phys; b = zalloc_usb_buffer_list(); b->pid = USB_PID_SETUP; b->len = sizeof(*csetup); b->vadr = (virt_t)alloc2_aligned(b->len, &b->padr); if (!b->vadr) { free(b); goto fail_submit_control; } urb->buffers = b; memcpy((void *)b->vadr, (void *)csetup, b->len); tdm->td->status = tdm->status_copy = UHCI_TD_STAT_AC | lospeed | uhci_td_maxerr(3); if (device) { spinlock_lock(&device->lock_dev); tdm->td->token = tdm->token_copy = uhci_td_explen(sizeof(*csetup)) | UHCI_TD_TOKEN_ENDPOINT(epdesc->bEndpointAddress) | UHCI_TD_TOKEN_DEVADDRESS(device->devnum) | UHCI_TD_TOKEN_PID_SETUP; spinlock_unlock(&device->lock_dev); } tdm->td->buffer = (phys32_t)b->padr; if (csetup->wLength > 0) { b = zalloc_usb_buffer_list(); b->pid = USB_PID_IN; b->len = csetup->wLength; b->vadr = (virt_t)alloc2_aligned(b->len, &b->padr); if (!b->vadr) { free(b); goto fail_submit_control; } b->next = urb->buffers; urb->buffers = b; if (device) { spinlock_lock(&device->lock_dev); tdm = prepare_buffer_tds(host, (phys32_t)b->padr, b->len, device->devnum, epdesc, (size_t)pktsz, UHCI_TD_STAT_AC | UHCI_TD_STAT_SP | lospeed | uhci_td_maxerr(3)); spinlock_unlock(&device->lock_dev); } if (!tdm) goto fail_submit_control; dprintft(5, "%s: tdm->td_phys = %llx\n", __FUNCTION__, tdm->td_phys); URB_UHCI(urb)->tdm_head->next = tdm; URB_UHCI(urb)->tdm_head->td->link = tdm->td_phys; } /* The 1st toggle for SETUP must be 0. */ uhci_fixup_toggles(URB_UHCI(urb)->tdm_head, epdesc->toggle); /* append one more TD for the status stage */ for (tdm = URB_UHCI(urb)->tdm_head; tdm->next; tdm = tdm->next); tdm->next = uhci_new_td_meta(host, NULL); if (!tdm->next) goto fail_submit_control; tdm->next->td->link = UHCI_TD_LINK_TE; tdm->next->td->status = UHCI_TD_STAT_AC | lospeed | uhci_td_maxerr(3); if (ioc) tdm->next->td->status |= UHCI_TD_STAT_IC; if (device) { spinlock_lock(&device->lock_dev); tdm->next->td->token = uhci_td_explen(0) | UHCI_TD_TOKEN_ENDPOINT(epdesc->bEndpointAddress) | UHCI_TD_TOKEN_DEVADDRESS(device->devnum) | UHCI_TD_TOKEN_DT1_TOGGLE; spinlock_unlock(&device->lock_dev); } tdm->next->td->token |= (csetup->wLength > 0) ? UHCI_TD_TOKEN_PID_OUT : UHCI_TD_TOKEN_PID_IN; tdm->next->td->buffer = 0U; tdm->td->link = (phys32_t)tdm->next->td_phys; /* link the QH into the frame list */ if (uhci_activate_urb(host, urb) != URB_STATUS_RUN) goto fail_submit_control; URB_UHCI(urb)->tdm_acttail = NULL; return urb; fail_submit_control: uhci_destroy_urb(host, urb); return (struct usb_request_block *)NULL; }
/** * @brief activate urb * @param host struct uhci_host *host * @param urb struct usb_request_block */ u8 uhci_activate_urb(struct uhci_host *host, struct usb_request_block *urb) { u8 status, type; int n; type = (urb->endpoint) ? USB_EP_TRANSTYPE(urb->endpoint) : USB_ENDPOINT_TYPE_CONTROL; spinlock_lock(&host->lock_hfl); switch (type) { case USB_ENDPOINT_TYPE_INTERRUPT: n = __ffs(urb->endpoint->bInterval | (1 << (UHCI_NUM_SKELTYPES - 1))); /* MEMO: a new interrupt urb must be inserted just after a skelton anytime. */ urb->link_prev = host->host_skelton[n]; if (host->host_skelton[n] == host->tailurb[URB_TAIL_CONTROL]) host->tailurb[URB_TAIL_CONTROL] = urb; if (host->host_skelton[n] == host->tailurb[URB_TAIL_BULK]) host->tailurb[URB_TAIL_BULK] = urb; break; case USB_ENDPOINT_TYPE_CONTROL: urb->link_prev = host->tailurb[URB_TAIL_CONTROL]; if (host->tailurb[URB_TAIL_CONTROL] == host->tailurb[URB_TAIL_BULK]) host->tailurb[URB_TAIL_BULK] = urb; host->tailurb[URB_TAIL_CONTROL] = urb; break; case USB_ENDPOINT_TYPE_BULK: urb->link_prev = host->tailurb[URB_TAIL_BULK]; host->tailurb[URB_TAIL_BULK] = urb; break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: default: printf("%s: transfer type(%02x) unsupported.\n", __FUNCTION__, type); status = urb->status; return status; } /* initialize qh_element_copy for detecting advance after NAK */ URB_UHCI(urb)->qh_element_copy = URB_UHCI(urb)->qh->element; /* urb link */ urb->link_next = urb->link_prev->link_next; urb->link_prev->link_next = urb; if (urb->link_next) { /* make a backward pointer */ urb->link_next->link_prev = urb; } else if (type == USB_ENDPOINT_TYPE_BULK) { if (host->fsbr) { dprintft(2, "%04x: %s: append it to the " "FSBR loopback.\n", host->iobase, __FUNCTION__); host->fsbr_loop_tail = urb; } else { dprintft(2, "%04x: %s: make a FSBR loopback.\n", host->iobase, __FUNCTION__); host->fsbr = 1; host->fsbr_loop_head = urb; host->fsbr_loop_tail = urb; } } /* record the current frame number */ URB_UHCI(urb)->frnum_issued = uhci_current_frame_number(host); /* qh link */ URB_UHCI(urb)->qh->link = URB_UHCI(urb->link_prev)->qh->link; if (host->fsbr_loop_tail) URB_UHCI(host->fsbr_loop_tail)->qh->link = (phys32_t) URB_UHCI(host->fsbr_loop_head)->qh_phys | UHCI_QH_LINK_QH; URB_UHCI(urb->link_prev)->qh->link = URB_UHCI(urb)->qh_phys | UHCI_QH_LINK_QH; urb->status = URB_STATUS_RUN; dprintft(3, "%s: The urb link is %p <- %p -> %p.\n", __FUNCTION__, urb->link_prev, urb, urb->link_next); status = urb->status; LIST4_PUSH (host->inproc_urbs, list, urb); spinlock_unlock(&host->lock_hfl); return status; }
/** * @brief deactivates the urb * @param host struct uhci_host * @param urb struct usb_request_block */ u8 uhci_deactivate_urb(struct usb_host *usbhc, struct usb_request_block *urb) { struct uhci_host *host = (struct uhci_host *)usbhc->private; u8 status, type; /* nothing to do if already unlinked */ if (urb->status == URB_STATUS_UNLINKED) return urb->status; dprintft(5, "%s: The urb link is %p <- %p -> %p.\n", __FUNCTION__, urb->link_prev, urb, urb->link_next); spinlock_lock(&host->lock_hfl); if (urb->prevent_del) { urb->deferred_del = true; spinlock_unlock(&host->lock_hfl); return 0U; } /* urb link */ if ((urb == host->fsbr_loop_head) && (urb == host->fsbr_loop_tail)) { dprintft(2, "%04x: %s: FSBR unlooped \n", host->iobase, __FUNCTION__); host->fsbr = 0; host->fsbr_loop_head = host->fsbr_loop_tail = (struct usb_request_block *)NULL; /* qh */ URB_UHCI(urb->link_prev)->qh->link = UHCI_QH_LINK_TE; } else if (urb == host->fsbr_loop_tail) { /* tail of a FSBR loopback */ dprintft(2, "%04x: %s: the tail of a FSBR loopback\n", host->iobase, __FUNCTION__); host->fsbr_loop_tail = urb->link_prev; /* qh */ URB_UHCI(host->fsbr_loop_tail)->qh->link = (phys32_t)URB_UHCI(host->fsbr_loop_head)->qh_phys | UHCI_QH_LINK_QH; } else if (host->fsbr_loop_head == urb) { /* head of a FSBR loopback */ dprintft(2, "%04x: %s: the head of a FSBR loopback\n", host->iobase, __FUNCTION__); host->fsbr_loop_head = urb->link_next; /* qh */ URB_UHCI(host->fsbr_loop_tail)->qh->link = (phys32_t)URB_UHCI(host->fsbr_loop_head)->qh_phys | UHCI_QH_LINK_QH; URB_UHCI(urb->link_prev)->qh->link = URB_UHCI(urb)->qh->link; } else { /* qh */ URB_UHCI(urb->link_prev)->qh->link = URB_UHCI(urb)->qh->link; } URB_UHCI(urb)->qh->link = UHCI_QH_LINK_TE; /* MEMO: There must exist urb->link_prev because of the skelton. */ urb->link_prev->link_next = urb->link_next; if (urb->link_next) urb->link_next->link_prev = urb->link_prev; urb->status = URB_STATUS_UNLINKED; type = (urb->endpoint) ? USB_EP_TRANSTYPE(urb->endpoint) : USB_ENDPOINT_TYPE_CONTROL; switch (type) { case USB_ENDPOINT_TYPE_INTERRUPT: /* through */ case USB_ENDPOINT_TYPE_CONTROL: if (host->tailurb[URB_TAIL_CONTROL] == urb) host->tailurb[URB_TAIL_CONTROL] = urb->link_prev; /* through */ case USB_ENDPOINT_TYPE_BULK: if (host->tailurb[URB_TAIL_BULK] == urb) host->tailurb[URB_TAIL_BULK] = urb->link_prev; break; case USB_ENDPOINT_TYPE_ISOCHRONOUS: default: printf("%s: transfer type(%02x) unsupported.\n", __FUNCTION__, type); } status = urb->status; LIST4_DEL (host->inproc_urbs, list, urb); LIST4_ADD (host->unlinked_urbs[host->unlinked_urbs_index], list, urb); spinlock_unlock(&host->lock_hfl); return status; }
/** * @brief chek_advance * @param host struct uhci_host */ int check_advance(struct uhci_host *host) { struct usb_request_block *urb, *nexturb; int advance = 0, ret = 0; u16 usbsts = 0U; if (cmpxchgl(&host->incheck, 0U, 1U)) return 0; #if 0 in16(host->iobase + UHCI_REG_USBSTS, &usbsts); if (usbsts) dprintft(2, "%04x: %s: usbsts = %04x\n", host->iobase, __FUNCTION__, usbsts); #endif /* 0 */ urb = host->inproc_urbs; while (urb) { /* update urb->status */ if (urb->status == URB_STATUS_RUN) check_urb_advance(host, urb, usbsts); switch (urb->status) { case URB_STATUS_UNLINKED: spinlock_lock(&host->lock_hfl); nexturb = urb->next; remove_urb(&host->inproc_urbs, urb); destroy_urb(host, urb); dprintft(3, "%04x: %s: urb(%p) destroyed.\n", host->iobase, __FUNCTION__, urb); urb = nexturb; spinlock_unlock(&host->lock_hfl); continue; default: /* errors */ dprintft(2, "%04x: %s: got some errors(%s) " "for urb(%p).\n", host->iobase, __FUNCTION__, uhci_error_status_string(urb->status), urb); uhci_dump_all(3, host, urb); /* through */ case URB_STATUS_ADVANCED: if (urb->callback) ret = (urb->callback)(host->hc, urb, urb->cb_arg); advance++; break; case URB_STATUS_NAK: dprintft(2, "%04x: %s: got an NAK for urb(%p).\n", host->iobase, __FUNCTION__, urb); if (urb->shadow) uhci_force_copyback(host, urb); urb->status = URB_STATUS_RUN; case URB_STATUS_RUN: case URB_STATUS_FINALIZED: break; } urb = urb->next; } #if 0 if (advance) { dprintft(3, "%s: USBSTS register cleared.\n", __FUNCTION__); out16(host->iobase + UHCI_REG_USBSTS, usbsts); } #endif host->incheck = 0U; return advance; }
/** * @brief submit the control messagie * @param host struct uhci_host * @param device struct usb_device * @param endpoint u8 * @param csetup struct usb_device * @param callback int * * @param arg void* * @param ioc int */ struct usb_request_block * uhci_submit_control(struct uhci_host *host, struct usb_device *device, u8 endpoint, struct usb_ctrl_setup *csetup, int (*callback)(struct usb_host *, struct usb_request_block *, void *), void *arg, int ioc) { struct usb_request_block *urb; struct usb_endpoint_descriptor *epdesc; struct uhci_td_meta *tdm; struct usb_buffer_list *b; size_t pktsize; epdesc = usb_epdesc(device, endpoint); if (!epdesc) { dprintft(2, "%04x: %s: no endpoint(%d) found.\n", host->iobase, __FUNCTION__, endpoint); return (struct usb_request_block *)NULL; } dprintft(5, "%s: epdesc->wMaxPacketSize = %d\n", __FUNCTION__, epdesc->wMaxPacketSize); urb = create_urb(host); if (!urb) return (struct usb_request_block *)NULL; if (device){ spinlock_lock(&device->lock_dev); init_urb(urb, device->devnum, epdesc, callback, arg); spinlock_unlock(&device->lock_dev); } /* create a QH */ URB_UHCI(urb)->qh = uhci_alloc_qh(host, &URB_UHCI(urb)->qh_phys); if (!URB_UHCI(urb)->qh) goto fail_submit_control; URB_UHCI(urb)->qh->link = UHCI_QH_LINK_TE; pktsize = epdesc->wMaxPacketSize; /* SETUP TD */ URB_UHCI(urb)->tdm_head = tdm = uhci_new_td_meta(host, NULL); if (!tdm) goto fail_submit_control; URB_UHCI(urb)->qh->element = URB_UHCI(urb)->qh_element_copy = tdm->td_phys; b = zalloc_usb_buffer_list(); b->len = sizeof(*csetup); b->vadr = malloc_from_pool(host->pool, b->len, &b->padr); if (!b->vadr) { free(b); goto fail_submit_control; } urb->buffers = b; memcpy((void *)b->vadr, (void *)csetup, b->len); tdm->td->status = tdm->status_copy = UHCI_TD_STAT_AC | uhci_td_maxerr(3); if (device){ spinlock_lock(&device->lock_dev); tdm->td->token = tdm->token_copy = uhci_td_explen(sizeof(*csetup)) | UHCI_TD_TOKEN_ENDPOINT(epdesc->bEndpointAddress) | UHCI_TD_TOKEN_DEVADDRESS(device->devnum) | UHCI_TD_TOKEN_PID_SETUP; spinlock_unlock(&device->lock_dev); } tdm->td->buffer = (phys32_t)b->padr; if (csetup->wLength > 0) { b = zalloc_usb_buffer_list(); b->len = csetup->wLength; b->vadr = malloc_from_pool(host->pool, b->len, &b->padr); if (!b->vadr) { free(b); goto fail_submit_control; } b->next = urb->buffers; urb->buffers = b; if(device){ spinlock_lock(&device->lock_dev); tdm = prepare_buffer_tds(host, (phys32_t)b->padr, b->len, device->devnum, epdesc, UHCI_TD_STAT_AC | UHCI_TD_STAT_SP | uhci_td_maxerr(3)); spinlock_unlock(&device->lock_dev); } if (!tdm) goto fail_submit_control; dprintft(5, "%s: tdm->td_phys = %llx\n", __FUNCTION__, tdm->td_phys); URB_UHCI(urb)->tdm_head->next = tdm; URB_UHCI(urb)->tdm_head->td->link = tdm->td_phys; } /* The 1st toggle for SETUP must be 0. */ uhci_fixup_toggles(URB_UHCI(urb)->tdm_head, epdesc->toggle); /* append one more TD for the status stage */ for (tdm = URB_UHCI(urb)->tdm_head; tdm->next; tdm = tdm->next); tdm->next = uhci_new_td_meta(host, NULL); if (!tdm->next) goto fail_submit_control; tdm->next->td->link = UHCI_TD_LINK_TE; tdm->next->td->status = UHCI_TD_STAT_AC | uhci_td_maxerr(3); if (ioc) tdm->next->td->status |= UHCI_TD_STAT_IC; if (device){ spinlock_lock(&device->lock_dev); tdm->next->td->token = uhci_td_explen(0) | UHCI_TD_TOKEN_ENDPOINT(epdesc->bEndpointAddress) | UHCI_TD_TOKEN_DEVADDRESS(device->devnum) | UHCI_TD_TOKEN_DT1_TOGGLE; spinlock_unlock(&device->lock_dev); } tdm->next->td->token |= (csetup->wLength > 0) ? UHCI_TD_TOKEN_PID_OUT : UHCI_TD_TOKEN_PID_IN; tdm->next->td->buffer = 0U; tdm->td->link = (phys32_t)tdm->next->td_phys; /* link the QH into the frame list */ if (uhci_activate_urb(host, urb) != URB_STATUS_RUN) goto fail_submit_control; link_urb(&host->inproc_urbs, urb); return urb; fail_submit_control: destroy_urb(host, urb); return (struct usb_request_block *)NULL; }
static int init_hub_device(struct usb_host *usbhc, struct usb_request_block *urb, void *arg) { u8 devadr, cls; struct usb_device *dev; struct usb_device_handle *handler; static const struct usb_hook_pattern pat_clrpf = { .pid = USB_PID_SETUP, .mask = 0x000000000000ffffULL, .pattern = 0x000000000000000123ULL, .offset = 0, .next = NULL }; devadr = urb->address; dev = urb->dev; /* an interface descriptor must exists */ if (!dev || !dev->config || !dev->config->interface || !dev->config->interface->altsetting) { dprintft(1, "HUB(%02x): interface descriptor not found.\n", devadr); return USB_HOOK_PASS; } /* only Hub devices interests */ cls = dev->config->interface->altsetting->bInterfaceClass; if (cls != 0x09) return USB_HOOK_PASS; dprintft(1, "HUB(%02x): A Hub Class device found\n", devadr); if (dev->handle) { dprintft(1, "HUB(%02x): maybe reset.\n", devadr); return USB_HOOK_PASS; } handler = usb_new_dev_handle (usbhc, dev); handler->remove = usbhub_remove; dev->handle = handler; /* notify whenever ClearPortFeature() issued. */ spinlock_lock(&usbhc->lock_hk); usb_hook_register (usbhc, USB_HOOK_REPLY, USB_HOOK_MATCH_DEV | USB_HOOK_MATCH_ENDP | USB_HOOK_MATCH_DATA, devadr, 0, &pat_clrpf, usbhub_connect_changed, NULL, dev); spinlock_unlock(&usbhc->lock_hk); return USB_HOOK_PASS; } void usbhub_init_handle(struct usb_host *host) { static const struct usb_hook_pattern pat_setconf = { .pid = USB_PID_SETUP, .mask = 0x000000000000ffffULL, .pattern = 0x0000000000000900ULL, .offset = 0, .next = NULL }; /* check a device class whenever SetConfigration() issued. */ spinlock_lock(&host->lock_hk); usb_hook_register(host, USB_HOOK_REPLY, USB_HOOK_MATCH_ENDP | USB_HOOK_MATCH_DATA, 0, 0, &pat_setconf, init_hub_device, NULL, NULL); spinlock_unlock(&host->lock_hk); printf("USB HUB Class handler registered.\n"); return; } void hub_portdevice_register(struct usb_host *host, u64 hub_port, struct usb_device *dev) { struct usb_device *hubdev; for (hubdev = host->device; hubdev; hubdev = hubdev->next) if (hub_port == hubdev->portno) { dev->parent = hubdev; dprintft(3, "HUB(%02x): HUB PORT(%d) device " "checked and registered.\n", hubdev->devnum, (int)dev->portno & USB_PORT_MASK); break; } if (!hubdev) dprintft(1, "HUB(%02x): HUB device not found!?!?\n", dev->devnum); return; }