/** * @brief makes a buffer of TDs and returs the top TD * @param host struct uhci_host * @param buf_phys phys32_t * @param size size_t * @param deviceaddress u8 * @param epdesc struct usb_endpoint_descriptor * @param maxpktsz size_t * @param status u32 */ static struct uhci_td_meta * prepare_buffer_tds(struct uhci_host *host, phys32_t buf_phys, size_t size, u8 deviceaddress, struct usb_endpoint_descriptor *epdesc, size_t maxpktsz, u32 status) { struct uhci_td_meta *tdm = NULL, *next_tdm = NULL; int n_td, i; size_t pktsz; n_td = size / maxpktsz; pktsz = size % maxpktsz; /* rounding them for the final one */ if (pktsz == 0) { pktsz = maxpktsz; } else { n_td += 1; } if (size == 0) { /* for ZERO OUT */ n_td += 1; pktsz = 0; } /* create TDs in reverse (from the final to the 1st) */ buf_phys += maxpktsz * n_td; for (i = n_td; i > 0; i--) { tdm = uhci_new_td_meta(host, NULL); if (!tdm) return 0ULL; tdm->td->link = (next_tdm) ? next_tdm->td_phys : UHCI_TD_LINK_TE; tdm->next = next_tdm; tdm->td->status = tdm->status_copy = status; tdm->td->token = tdm->token_copy = uhci_td_explen(pktsz) | UHCI_TD_TOKEN_ENDPOINT(epdesc->bEndpointAddress) | UHCI_TD_TOKEN_DEVADDRESS(deviceaddress) | uhci_pid_from_ep(epdesc); buf_phys = tdm->td->buffer = buf_phys - maxpktsz; next_tdm = tdm; pktsz = maxpktsz; /* for other packets, except for the final */ } return tdm; }
/** * @brief makes a buffer of TDs and returs the top TD * @param host struct uhci_host * @param buf_phys phys32_t * @param size size_t * @param deviceaddress u8 * @param epdesc struct usb_endpoint_descriptor * @param status u32 */ static struct uhci_td_meta * prepare_buffer_tds(struct uhci_host *host, phys32_t buf_phys, size_t size, u8 deviceaddress, struct usb_endpoint_descriptor *epdesc, u32 status) { struct uhci_td_meta *tdm = NULL, *next_tdm = NULL; int n_td, i; size_t pktsize, maxlen; pktsize = epdesc->wMaxPacketSize; n_td = (size + (pktsize - 1)) / pktsize; maxlen = (size % pktsize) ? (size % pktsize) : pktsize; if (!size) { /* for ZERO OUT */ n_td += 1; maxlen = 0; } buf_phys += pktsize * n_td; for (i=n_td; i>0; i--) { tdm = uhci_new_td_meta(host, NULL); if (!tdm) return 0ULL; tdm->td->link = (next_tdm) ? next_tdm->td_phys : UHCI_TD_LINK_TE; tdm->next = next_tdm; tdm->td->status = tdm->status_copy = status; tdm->td->token = tdm->token_copy = uhci_td_explen(maxlen) | UHCI_TD_TOKEN_ENDPOINT(epdesc->bEndpointAddress) | UHCI_TD_TOKEN_DEVADDRESS(deviceaddress) | uhci_pid_from_ep(epdesc); buf_phys = tdm->td->buffer = buf_phys - pktsize; next_tdm = tdm; maxlen = pktsize; } return tdm; }
/** * @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 initialize host fram list * @param host struct uhci_host */ int init_hframelist(struct uhci_host *host) { struct usb_request_block *urb; u32 frid; phys32_t *frame_p; virt_t framelist_virt; phys_t framelist_phys; int n_skels; /* allocate a page for frame list */ alloc_page((void *)&framelist_virt, &framelist_phys); if (!framelist_phys) return -1; host->hframelist = framelist_phys; host->hframelist_virt = (phys32_t *)framelist_virt; spinlock_lock(&host->lock_hfl); /* create a TD for termination */ host->term_tdm = uhci_new_td_meta(host, NULL); if (!host->term_tdm) return -1; host->term_tdm->td->link = UHCI_TD_LINK_TE; host->term_tdm->td->status = 0U; host->term_tdm->td->token = UHCI_TD_TOKEN_DEVADDRESS(0x7f) | UHCI_TD_TOKEN_ENDPOINT(0) | UHCI_TD_TOKEN_PID_IN | uhci_td_explen(0); host->term_tdm->td->buffer = 0U; /* create skelton QHs */ for (n_skels = 0; n_skels<UHCI_NUM_SKELTYPES; n_skels++) { urb = uhci_create_urb(host); if (!urb) break; urb->address = URB_ADDRESS_SKELTON; URB_UHCI(urb)->qh = uhci_alloc_qh(host, &URB_UHCI(urb)->qh_phys); if (!URB_UHCI(urb)->qh) break; if (n_skels == 0) { URB_UHCI(urb)->tdm_head = host->term_tdm; URB_UHCI(urb)->qh->element = (phys32_t) URB_UHCI(urb)->tdm_head->td_phys; URB_UHCI(urb)->qh->link = UHCI_QH_LINK_TE; } else { URB_UHCI(urb)->qh->element = UHCI_QH_LINK_TE; URB_UHCI(urb)->qh->link = (phys32_t) URB_UHCI(host->host_skelton [n_skels - 1])->qh_phys | UHCI_QH_LINK_QH; urb->link_next = host->host_skelton[n_skels - 1]; } host->host_skelton[n_skels] = urb; } /* make link to a QH in each frame list entry according to intervals */ for (frid = 0U; frid < UHCI_NUM_FRAMES; frid++) { frame_p = (phys32_t *) (framelist_virt + frid * sizeof(phys32_t)); n_skels = __ffs((frid + 1) | (1 << (UHCI_NUM_SKELTYPES - 1))); *frame_p = (phys32_t) URB_UHCI(host->host_skelton[n_skels])->qh_phys | UHCI_FRAME_LINK_QH; } for (n_skels = 0; n_skels < 2; n_skels++) host->tailurb[n_skels] = host->host_skelton[0]; spinlock_unlock(&host->lock_hfl); return 0; }
/** * @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; }