summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/usb/usb_transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/dev/usb/usb_transfer.c')
-rw-r--r--freebsd/sys/dev/usb/usb_transfer.c260
1 files changed, 167 insertions, 93 deletions
diff --git a/freebsd/sys/dev/usb/usb_transfer.c b/freebsd/sys/dev/usb/usb_transfer.c
index 17a492e5..a8b0b0ac 100644
--- a/freebsd/sys/dev/usb/usb_transfer.c
+++ b/freebsd/sys/dev/usb/usb_transfer.c
@@ -24,7 +24,7 @@
* 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.
- */
+ */
#include <sys/stdint.h>
#include <sys/stddef.h>
@@ -34,7 +34,6 @@
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
-#include <sys/linker_set.h>
#include <sys/module.h>
#include <rtems/bsd/sys/lock.h>
#include <sys/mutex.h>
@@ -45,6 +44,7 @@
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
+#include <sys/proc.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
@@ -62,6 +62,7 @@
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
+#include <dev/usb/usb_pf.h>
#ifdef __rtems__
#include <machine/rtems-bsd-cache.h>
#endif /* __rtems__ */
@@ -228,12 +229,12 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
* Try multi-allocation chunks to reduce the number of DMA
* allocations, hence DMA allocations are slow.
*/
- if (size >= PAGE_SIZE) {
+ if (size >= USB_PAGE_SIZE) {
n_dma_pc = count;
n_obj = 1;
} else {
/* compute number of objects per page */
- n_obj = (PAGE_SIZE / size);
+ n_obj = (USB_PAGE_SIZE / size);
/*
* Compute number of DMA chunks, rounded up
* to nearest one:
@@ -483,6 +484,8 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
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;
}
@@ -674,9 +677,13 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
}
xfer->max_data_length -= REQ_SIZE;
}
- /* setup "frlengths" */
+ /*
+ * 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 += n_frlengths;
+ parm->xfer_length_ptr += 2 * n_frlengths;
/* setup "frbuffers" */
xfer->frbuffers = parm->xfer_page_cache_ptr;
@@ -833,20 +840,17 @@ usbd_transfer_setup(struct usb_device *udev,
const struct usb_config *setup_start, uint16_t n_setup,
void *priv_sc, struct mtx *xfer_mtx)
{
- struct usb_xfer dummy;
- struct usb_setup_params parm;
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;
-
- parm.err = 0;
- refcount = 0;
- info = NULL;
+ uint8_t do_unlock;
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"usbd_transfer_setup can sleep!");
@@ -865,31 +869,40 @@ usbd_transfer_setup(struct usb_device *udev,
DPRINTFN(6, "using global lock\n");
xfer_mtx = &Giant;
}
- /* sanity checks */
+
+ /* more sanity checks */
+
for (setup = setup_start, n = 0;
setup != setup_end; setup++, n++) {
if (setup->bufsize == (usb_frlength_t)-1) {
- parm.err = USB_ERR_BAD_BUFSIZE;
+ error = USB_ERR_BAD_BUFSIZE;
DPRINTF("invalid bufsize\n");
}
if (setup->callback == NULL) {
- parm.err = USB_ERR_NO_CALLBACK;
+ error = USB_ERR_NO_CALLBACK;
DPRINTF("no callback\n");
}
ppxfer[n] = NULL;
}
- if (parm.err) {
- goto done;
- }
- bzero(&parm, sizeof(parm));
+ if (error)
+ return (error);
+
+ /* Protect scratch area */
+ do_unlock = usbd_enum_lock(udev);
+
+ refcount = 0;
+ info = NULL;
- parm.udev = udev;
- parm.speed = usbd_get_speed(udev);
- parm.hc_max_packet_count = 1;
+ parm = &udev->scratch.xfer_setup[0].parm;
+ memset(parm, 0, sizeof(*parm));
- if (parm.speed >= USB_SPEED_MAX) {
- parm.err = USB_ERR_INVAL;
+ 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 */
@@ -904,22 +917,22 @@ usbd_transfer_setup(struct usb_device *udev,
info = USB_ADD_BYTES(buf, 0);
info->memory_base = buf;
- info->memory_size = parm.size[0];
+ 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]);
+ 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]);
+ 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, 32, parm.dma_tag_max);
+ parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag,
+ xfer_mtx, &usb_bdma_done_event, 32, parm->dma_tag_max);
#endif
info->bus = udev->bus;
@@ -954,9 +967,9 @@ usbd_transfer_setup(struct usb_device *udev,
}
/* reset sizes */
- parm.size[0] = 0;
- parm.buf = buf;
- parm.size[0] += sizeof(info[0]);
+ parm->size[0] = 0;
+ parm->buf = buf;
+ parm->size[0] += sizeof(info[0]);
for (setup = setup_start, n = 0;
setup != setup_end; setup++, n++) {
@@ -975,22 +988,22 @@ usbd_transfer_setup(struct usb_device *udev,
if ((setup->usb_mode != USB_MODE_DUAL) &&
(setup->usb_mode != udev->flags.usb_mode))
continue;
- parm.err = USB_ERR_NO_PIPE;
+ parm->err = USB_ERR_NO_PIPE;
goto done;
}
/* align data properly */
- parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
/* store current setup pointer */
- parm.curr_setup = setup;
+ parm->curr_setup = setup;
if (buf) {
/*
* Common initialization of the
* "usb_xfer" structure.
*/
- xfer = USB_ADD_BYTES(buf, parm.size[0]);
+ xfer = USB_ADD_BYTES(buf, parm->size[0]);
xfer->address = udev->address;
xfer->priv_sc = priv_sc;
xfer->xroot = info;
@@ -1005,26 +1018,26 @@ usbd_transfer_setup(struct usb_device *udev,
* before we have allocated any
* memory:
*/
- xfer = &dummy;
- bzero(&dummy, sizeof(dummy));
+ xfer = &udev->scratch.xfer_setup[0].dummy;
+ memset(xfer, 0, sizeof(*xfer));
refcount++;
}
/* set transfer endpoint pointer */
xfer->endpoint = ep;
- parm.size[0] += sizeof(xfer[0]);
- parm.methods = xfer->endpoint->methods;
- parm.curr_xfer = xfer;
+ 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);
+ (udev->bus->methods->xfer_setup) (parm);
/* check for error */
- if (parm.err)
+ if (parm->err)
goto done;
if (buf) {
@@ -1039,7 +1052,7 @@ usbd_transfer_setup(struct usb_device *udev,
*/
USB_BUS_LOCK(info->bus);
if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX)
- parm.err = USB_ERR_INVAL;
+ parm->err = USB_ERR_INVAL;
xfer->endpoint->refcount_alloc++;
@@ -1062,22 +1075,22 @@ usbd_transfer_setup(struct usb_device *udev,
}
/* check for error */
- if (parm.err)
+ if (parm->err)
goto done;
}
- if (buf || parm.err) {
+ if (buf != NULL || parm->err != 0)
goto done;
- }
- if (refcount == 0) {
- /* no transfers - nothing to do ! */
+
+ /* if no transfers, nothing to do */
+ if (refcount == 0)
goto done;
- }
+
/* align data properly */
- parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
/* store offset temporarily */
- parm.size[1] = parm.size[0];
+ parm->size[1] = parm->size[0];
/*
* The number of DMA tags required depends on
@@ -1085,72 +1098,72 @@ usbd_transfer_setup(struct usb_device *udev,
* for maximum number of DMA tags per endpoint
* is two.
*/
- parm.dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX);
+ parm->dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX);
/*
* DMA tags for QH, TD, Data and more.
*/
- parm.dma_tag_max += 8;
+ parm->dma_tag_max += 8;
- parm.dma_tag_p += parm.dma_tag_max;
+ parm->dma_tag_p += parm->dma_tag_max;
- parm.size[0] += ((uint8_t *)parm.dma_tag_p) -
+ 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));
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
/* store offset temporarily */
- parm.size[3] = parm.size[0];
+ parm->size[3] = parm->size[0];
- parm.size[0] += ((uint8_t *)parm.dma_page_ptr) -
+ 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));
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
/* store offset temporarily */
- parm.size[4] = parm.size[0];
+ parm->size[4] = parm->size[0];
- parm.size[0] += ((uint8_t *)parm.dma_page_cache_ptr) -
+ 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[5] = parm->size[0];
- parm.size[0] += ((uint8_t *)parm.xfer_page_cache_ptr) -
+ parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) -
((uint8_t *)0);
/* store end offset temporarily */
- parm.size[2] = parm.size[0];
+ parm->size[2] = parm->size[0];
/* align data properly */
- parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
- parm.size[6] = parm.size[0];
+ parm->size[6] = parm->size[0];
- parm.size[0] += ((uint8_t *)parm.xfer_length_ptr) -
+ 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));
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
/* allocate zeroed memory */
- buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO);
+ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO);
if (buf == NULL) {
- parm.err = USB_ERR_NOMEM;
+ parm->err = USB_ERR_NOMEM;
DPRINTFN(0, "cannot allocate memory block for "
"configuration (%d bytes)\n",
- parm.size[0]);
+ 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]);
+ 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:
@@ -1166,10 +1179,17 @@ done:
usbd_transfer_unsetup_sub(info, 0);
}
}
- if (parm.err) {
+
+ /* check if any errors happened */
+ if (parm->err)
usbd_transfer_unsetup(ppxfer, n_setup);
- }
- return (parm.err);
+
+ error = parm->err;
+
+ if (do_unlock)
+ usbd_enum_unlock(udev);
+
+ return (error);
}
/*------------------------------------------------------------------------*
@@ -1178,7 +1198,9 @@ done:
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);
@@ -1605,9 +1627,12 @@ usbd_transfer_submit(struct usb_xfer *xfer)
USB_BUS_UNLOCK(bus);
return;
}
- /* compute total transfer length */
+ /* 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 */
@@ -1699,11 +1724,12 @@ usbd_pipe_enter(struct usb_xfer *xfer)
DPRINTF("enter\n");
+ /* the transfer can now be cancelled */
+ xfer->flags_int.can_cancel_immed = 1;
+
/* enter the transfer */
(ep->methods->enter) (xfer);
- xfer->flags_int.can_cancel_immed = 1;
-
/* check for transfer error */
if (xfer->error) {
/* some error has happened */
@@ -1940,6 +1966,17 @@ usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex)
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
*
@@ -1996,6 +2033,22 @@ usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
*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)
@@ -2226,6 +2279,10 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq)
}
}
+#if USB_HAVE_PF
+ if (xfer->usb_state != USB_ST_SETUP)
+ usbpf_xfertap(xfer, USBPF_XFERTAP_DONE);
+#endif
/* call processing routine */
(xfer->callback) (xfer, xfer->error);
@@ -2413,12 +2470,18 @@ usbd_transfer_start_cb(void *arg)
DPRINTF("start\n");
- /* start the transfer */
- (ep->methods->start) (xfer);
+#if USB_HAVE_PF
+ usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT);
+#endif
+ /* the transfer can now be cancelled */
xfer->flags_int.can_cancel_immed = 1;
- /* check for error */
+ /* 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);
@@ -2590,12 +2653,17 @@ usbd_pipe_start(struct usb_xfer_queue *pq)
}
DPRINTF("start\n");
- /* start USB transfer */
- (ep->methods->start) (xfer);
-
+#if USB_HAVE_PF
+ usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT);
+#endif
+ /* the transfer can now be cancelled */
xfer->flags_int.can_cancel_immed = 1;
- /* check for error */
+ /* 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);
@@ -2690,7 +2758,8 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer)
(bus->methods->start_dma_delay) (xfer);
} else {
usbd_transfer_timeout_ms(xfer,
- (void *)&usb_dma_delay_done_cb, temp);
+ (void (*)(void *))&usb_dma_delay_done_cb,
+ temp);
}
USB_BUS_UNLOCK(bus);
return (1); /* wait for new callback */
@@ -2921,6 +2990,11 @@ repeat:
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:
*/