diff options
Diffstat (limited to 'sys/dev/usb/usb_transfer.c')
-rw-r--r-- | sys/dev/usb/usb_transfer.c | 59 |
1 files changed, 55 insertions, 4 deletions
diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index d99a22f..5650790 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -237,7 +237,11 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, 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: @@ -273,15 +277,33 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, &parm->curr_xfer->xroot->dma_parent_tag; } - if (ppc) { - *ppc = parm->xfer_page_cache_ptr; + 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; - for (x = 0; x != n_dma_pc; x++) { + 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 */ @@ -294,7 +316,7 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; - /* Make room for one DMA page cache and one page */ + /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; @@ -314,6 +336,7 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, } mtx_unlock(pc->tag_parent->mtx); } + } } parm->xfer_page_cache_ptr = pc; @@ -1409,6 +1432,29 @@ usbd_control_transfer_init(struct usb_xfer *xfer) } /*------------------------------------------------------------------------* + * 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 @@ -1513,6 +1559,11 @@ usbd_setup_ctrl_transfer(struct usb_xfer *xfer) 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) { |