/*------------------------------------------------------------------------* * usb_bdma_done_event * * This function is called when the BUS-DMA has loaded virtual memory * into DMA, if any. *------------------------------------------------------------------------*/ void usb_bdma_done_event(struct usb_dma_parent_tag *udpt) { struct usb_xfer_root *info; info = USB_DMATAG_TO_XROOT(udpt); mtx_assert(info->xfer_mtx, MA_OWNED); /* copy error */ info->dma_error = udpt->dma_error; /* enter workloop again */ usb_command_wrapper(&info->dma_q, info->dma_q.curr); }
/*------------------------------------------------------------------------* * usb_bdma_done_event * * This function is called when the BUS-DMA has loaded virtual memory * into DMA, if any. *------------------------------------------------------------------------*/ void usb_bdma_done_event(struct usb_dma_parent_tag *udpt) { struct usb_xfer_root *info; info = USB_DMATAG_TO_XROOT(udpt); KKASSERT(lockowned(info->xfer_lock)); /* copy error */ info->dma_error = udpt->dma_error; /* enter workloop again */ usb_command_wrapper(&info->dma_q, info->dma_q.curr); }
/*------------------------------------------------------------------------* * usb_bdma_work_loop * * This function handles loading of virtual buffers into DMA and is * only called when "dma_refcount" is zero. *------------------------------------------------------------------------*/ void usb_bdma_work_loop(struct usb_xfer_queue *pq) { struct usb_xfer_root *info; struct usb_xfer *xfer; usb_frcount_t nframes; xfer = pq->curr; info = xfer->xroot; mtx_assert(info->xfer_mtx, MA_OWNED); if (xfer->error) { /* some error happened */ USB_BUS_LOCK(info->bus); usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(info->bus); return; } if (!xfer->flags_int.bdma_setup) { struct usb_page *pg; usb_frlength_t frlength_0; uint8_t isread; xfer->flags_int.bdma_setup = 1; /* reset BUS-DMA load state */ info->dma_error = 0; if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ nframes = 1; frlength_0 = xfer->sumlen; } else { /* can be multiple frame buffers */ nframes = xfer->nframes; frlength_0 = xfer->frlengths[0]; } /* * Set DMA direction first. This is needed to * select the correct cache invalidate and cache * flush operations. */ isread = USB_GET_DATA_ISREAD(xfer); pg = xfer->dma_page_ptr; if (xfer->flags_int.control_xfr && xfer->flags_int.control_hdr) { /* special case */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* The device controller writes to memory */ xfer->frbuffers[0].isread = 1; } else { /* The host controller reads from memory */ xfer->frbuffers[0].isread = 0; } } else { /* default case */ xfer->frbuffers[0].isread = isread; } /* * Setup the "page_start" pointer which points to an array of * USB pages where information about the physical address of a * page will be stored. Also initialise the "isread" field of * the USB page caches. */ xfer->frbuffers[0].page_start = pg; info->dma_nframes = nframes; info->dma_currframe = 0; info->dma_frlength_0 = frlength_0; pg += (frlength_0 / USB_PAGE_SIZE); pg += 2; while (--nframes > 0) { xfer->frbuffers[nframes].isread = isread; xfer->frbuffers[nframes].page_start = pg; pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); pg += 2; } } if (info->dma_error) { USB_BUS_LOCK(info->bus); usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); USB_BUS_UNLOCK(info->bus); return; } if (info->dma_currframe != info->dma_nframes) { if (info->dma_currframe == 0) { /* special case */ usb_pc_load_mem(xfer->frbuffers, info->dma_frlength_0, 0); } else { /* default case */ nframes = info->dma_currframe; usb_pc_load_mem(xfer->frbuffers + nframes, xfer->frlengths[nframes], 0); } /* advance frame index */ info->dma_currframe++; return; } /* go ahead */ usb_bdma_pre_sync(xfer); /* start loading next USB transfer, if any */ usb_command_wrapper(pq, NULL); /* finally start the hardware */ usbd_pipe_enter(xfer); }
/*------------------------------------------------------------------------* * 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; 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: if (ep == NULL) goto tr_setup; /* device was unconfigured */ if (ep->edesc && ep->is_stalled) { ep->toggle_next = 0; ep->is_stalled = 0; /* start up the current or next transfer, if any */ usb_command_wrapper(&ep->endpoint_q, ep->endpoint_q.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 (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } /* store current endpoint */ udev->ep_curr = ep; USB_BUS_UNLOCK(udev->bus); }