Index: head/sys/dev/usb/controller/usb_controller.c =================================================================== --- head/sys/dev/usb/controller/usb_controller.c (revision 193073) +++ head/sys/dev/usb/controller/usb_controller.c (revision 193074) @@ -1,593 +1,593 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 #include #include #define USB_DEBUG_VAR usb2_ctrl_debug #include #include #include #include #include #include #include #include #include /* function prototypes */ static device_probe_t usb2_probe; static device_attach_t usb2_attach; static device_detach_t usb2_detach; static void usb2_attach_sub(device_t, struct usb_bus *); static void usb2_post_init(void *); /* static variables */ #if USB_DEBUG static int usb2_ctrl_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb2_ctrl_debug, 0, "Debug level"); #endif static uint8_t usb2_post_init_called = 0; static devclass_t usb2_devclass; static device_method_t usb2_methods[] = { DEVMETHOD(device_probe, usb2_probe), DEVMETHOD(device_attach, usb2_attach), DEVMETHOD(device_detach, usb2_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), {0, 0} }; static driver_t usb2_driver = { .name = "usbus", .methods = usb2_methods, .size = 0, }; DRIVER_MODULE(usbus, ohci, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, uhci, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, ehci, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, at91_udp, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, uss820, usb2_driver, usb2_devclass, 0, 0); /*------------------------------------------------------------------------* * usb2_probe * * This function is called from "{ehci,ohci,uhci}_pci_attach()". *------------------------------------------------------------------------*/ static int usb2_probe(device_t dev) { DPRINTF("\n"); return (0); } /*------------------------------------------------------------------------* * usb2_attach *------------------------------------------------------------------------*/ static int usb2_attach(device_t dev) { struct usb_bus *bus = device_get_ivars(dev); DPRINTF("\n"); if (bus == NULL) { DPRINTFN(0, "USB device has no ivars\n"); return (ENXIO); } /* delay vfs_mountroot until the bus is explored */ bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); if (usb2_post_init_called) { mtx_lock(&Giant); usb2_attach_sub(dev, bus); mtx_unlock(&Giant); usb2_needs_explore(bus, 1); } return (0); /* return success */ } /*------------------------------------------------------------------------* * usb2_detach *------------------------------------------------------------------------*/ static int usb2_detach(device_t dev) { struct usb_bus *bus = device_get_softc(dev); DPRINTF("\n"); if (bus == NULL) { /* was never setup properly */ return (0); } /* Stop power watchdog */ usb2_callout_drain(&bus->power_wdog); /* Let the USB explore process detach all devices. */ if (bus->bus_roothold != NULL) { root_mount_rel(bus->bus_roothold); bus->bus_roothold = NULL; } USB_BUS_LOCK(bus); if (usb2_proc_msignal(&bus->explore_proc, &bus->detach_msg[0], &bus->detach_msg[1])) { /* ignore */ } /* Wait for detach to complete */ usb2_proc_mwait(&bus->explore_proc, &bus->detach_msg[0], &bus->detach_msg[1]); USB_BUS_UNLOCK(bus); /* Get rid of USB callback processes */ usb2_proc_free(&bus->giant_callback_proc); usb2_proc_free(&bus->non_giant_callback_proc); /* Get rid of USB explore process */ usb2_proc_free(&bus->explore_proc); /* Get rid of control transfer process */ usb2_proc_free(&bus->control_xfer_proc); return (0); } /*------------------------------------------------------------------------* * usb2_bus_explore * * This function is used to explore the device tree from the root. *------------------------------------------------------------------------*/ static void usb2_bus_explore(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; if (udev && udev->hub) { if (bus->do_probe) { bus->do_probe = 0; bus->driver_added_refcount++; } if (bus->driver_added_refcount == 0) { /* avoid zero, hence that is memory default */ bus->driver_added_refcount = 1; } USB_BUS_UNLOCK(bus); mtx_lock(&Giant); /* * First update the USB power state! */ usb2_bus_powerd(bus); /* * Explore the Root USB HUB. This call can sleep, * exiting Giant, which is actually Giant. */ (udev->hub->explore) (udev); mtx_unlock(&Giant); USB_BUS_LOCK(bus); } if (bus->bus_roothold != NULL) { root_mount_rel(bus->bus_roothold); bus->bus_roothold = NULL; } } /*------------------------------------------------------------------------* * usb2_bus_detach * * This function is used to detach the device tree from the root. *------------------------------------------------------------------------*/ static void usb2_bus_detach(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *udev; device_t dev; bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; dev = bus->bdev; /* clear the softc */ device_set_softc(dev, NULL); USB_BUS_UNLOCK(bus); mtx_lock(&Giant); /* detach children first */ bus_generic_detach(dev); /* * Free USB Root device, but not any sub-devices, hence they * are freed by the caller of this function: */ usb2_free_device(udev, USB_UNCFG_FLAG_FREE_EP0); mtx_unlock(&Giant); USB_BUS_LOCK(bus); /* clear bdev variable last */ bus->bdev = NULL; } static void usb2_power_wdog(void *arg) { struct usb_bus *bus = arg; USB_BUS_LOCK_ASSERT(bus, MA_OWNED); usb2_callout_reset(&bus->power_wdog, 4 * hz, usb2_power_wdog, arg); USB_BUS_UNLOCK(bus); usb2_bus_power_update(bus); USB_BUS_LOCK(bus); } /*------------------------------------------------------------------------* * usb2_bus_attach * * This function attaches USB in context of the explore thread. *------------------------------------------------------------------------*/ static void usb2_bus_attach(struct usb_proc_msg *pm) { struct usb_bus *bus; struct usb_device *child; device_t dev; usb_error_t err; enum usb_dev_speed speed; bus = ((struct usb_bus_msg *)pm)->bus; dev = bus->bdev; DPRINTF("\n"); switch (bus->usbrev) { case USB_REV_1_0: speed = USB_SPEED_FULL; device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); break; case USB_REV_1_1: speed = USB_SPEED_FULL; device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); break; case USB_REV_2_0: speed = USB_SPEED_HIGH; device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); break; case USB_REV_2_5: speed = USB_SPEED_VARIABLE; device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); break; default: device_printf(bus->bdev, "Unsupported USB revision!\n"); return; } USB_BUS_UNLOCK(bus); mtx_lock(&Giant); /* XXX not required by USB */ /* default power_mask value */ bus->hw_power_state = USB_HW_POWER_CONTROL | USB_HW_POWER_BULK | USB_HW_POWER_INTERRUPT | USB_HW_POWER_ISOC | USB_HW_POWER_NON_ROOT_HUB; /* make sure power is set at least once */ if (bus->methods->set_hw_power != NULL) { (bus->methods->set_hw_power) (bus); } /* Allocate the Root USB device */ child = usb2_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, speed, USB_MODE_HOST); if (child) { err = usb2_probe_and_attach(child, USB_IFACE_INDEX_ANY); if (!err) { if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { err = USB_ERR_NO_ROOT_HUB; } } } else { err = USB_ERR_NOMEM; } mtx_unlock(&Giant); USB_BUS_LOCK(bus); if (err) { device_printf(bus->bdev, "Root HUB problem, error=%s\n", usb2_errstr(err)); } /* set softc - we are ready */ device_set_softc(dev, bus); /* start watchdog */ usb2_power_wdog(bus); } /*------------------------------------------------------------------------* * usb2_attach_sub * * This function creates a thread which runs the USB attach code. It * is factored out, hence it can be called at two different places in * time. During bootup this function is called from * "usb2_post_init". During hot-plug it is called directly from the * "usb2_attach()" method. *------------------------------------------------------------------------*/ static void usb2_attach_sub(device_t dev, struct usb_bus *bus) { const char *pname = device_get_nameunit(dev); /* Initialise USB process messages */ bus->explore_msg[0].hdr.pm_callback = &usb2_bus_explore; bus->explore_msg[0].bus = bus; bus->explore_msg[1].hdr.pm_callback = &usb2_bus_explore; bus->explore_msg[1].bus = bus; bus->detach_msg[0].hdr.pm_callback = &usb2_bus_detach; bus->detach_msg[0].bus = bus; bus->detach_msg[1].hdr.pm_callback = &usb2_bus_detach; bus->detach_msg[1].bus = bus; bus->attach_msg[0].hdr.pm_callback = &usb2_bus_attach; bus->attach_msg[0].bus = bus; bus->attach_msg[1].hdr.pm_callback = &usb2_bus_attach; bus->attach_msg[1].bus = bus; /* Create USB explore and callback processes */ if (usb2_proc_create(&bus->giant_callback_proc, &bus->bus_mtx, pname, USB_PRI_MED)) { printf("WARNING: Creation of USB Giant " "callback process failed.\n"); } else if (usb2_proc_create(&bus->non_giant_callback_proc, &bus->bus_mtx, pname, USB_PRI_HIGH)) { printf("WARNING: Creation of USB non-Giant " "callback process failed.\n"); } else if (usb2_proc_create(&bus->explore_proc, &bus->bus_mtx, pname, USB_PRI_MED)) { printf("WARNING: Creation of USB explore " "process failed.\n"); } else if (usb2_proc_create(&bus->control_xfer_proc, &bus->bus_mtx, pname, USB_PRI_MED)) { printf("WARNING: Creation of USB control transfer " "process failed.\n"); } else { /* Get final attach going */ USB_BUS_LOCK(bus); if (usb2_proc_msignal(&bus->explore_proc, &bus->attach_msg[0], &bus->attach_msg[1])) { /* ignore */ } USB_BUS_UNLOCK(bus); } } /*------------------------------------------------------------------------* * usb2_post_init * * This function is called to attach all USB busses that were found * during bootup. *------------------------------------------------------------------------*/ static void usb2_post_init(void *arg) { struct usb_bus *bus; devclass_t dc; device_t dev; int max; int n; mtx_lock(&Giant); usb2_devclass_ptr = devclass_find("usbus"); dc = usb2_devclass_ptr; if (dc) { max = devclass_get_maxunit(dc) + 1; for (n = 0; n != max; n++) { dev = devclass_get_device(dc, n); if (dev && device_is_attached(dev)) { bus = device_get_ivars(dev); if (bus) { mtx_lock(&Giant); usb2_attach_sub(dev, bus); mtx_unlock(&Giant); } } } } else { DPRINTFN(0, "no devclass\n"); } usb2_post_init_called = 1; /* explore all USB busses in parallell */ usb2_needs_explore_all(); mtx_unlock(&Giant); } SYSINIT(usb2_post_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_post_init, NULL); SYSUNINIT(usb2_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb2_bus_unload, NULL); /*------------------------------------------------------------------------* * usb2_bus_mem_flush_all_cb *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA static void usb2_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, - struct usb_page *pg, size_t size, size_t align) + struct usb_page *pg, usb_size_t size, usb_size_t align) { usb2_pc_cpu_flush(pc); } #endif /*------------------------------------------------------------------------* * usb2_bus_mem_flush_all - factored out code *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA void usb2_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) { if (cb) { cb(bus, &usb2_bus_mem_flush_all_cb); } } #endif /*------------------------------------------------------------------------* * usb2_bus_mem_alloc_all_cb *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA static void usb2_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, - struct usb_page *pg, size_t size, size_t align) + struct usb_page *pg, usb_size_t size, usb_size_t align) { /* need to initialize the page cache */ pc->tag_parent = bus->dma_parent_tag; if (usb2_pc_alloc_mem(pc, pg, size, align)) { bus->alloc_failed = 1; } } #endif /*------------------------------------------------------------------------* * usb2_bus_mem_alloc_all - factored out code * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb2_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb) { bus->alloc_failed = 0; mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), NULL, MTX_DEF | MTX_RECURSE); usb2_callout_init_mtx(&bus->power_wdog, &bus->bus_mtx, 0); TAILQ_INIT(&bus->intr_q.head); #if USB_HAVE_BUSDMA usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); #endif if ((bus->devices_max > USB_MAX_DEVICES) || (bus->devices_max < USB_MIN_DEVICES) || (bus->devices == NULL)) { DPRINTFN(0, "Devices field has not been " "initialised properly!\n"); bus->alloc_failed = 1; /* failure */ } #if USB_HAVE_BUSDMA if (cb) { cb(bus, &usb2_bus_mem_alloc_all_cb); } #endif if (bus->alloc_failed) { usb2_bus_mem_free_all(bus, cb); } return (bus->alloc_failed); } /*------------------------------------------------------------------------* * usb2_bus_mem_free_all_cb *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA static void usb2_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, - struct usb_page *pg, size_t size, size_t align) + struct usb_page *pg, usb_size_t size, usb_size_t align) { usb2_pc_free_mem(pc); } #endif /*------------------------------------------------------------------------* * usb2_bus_mem_free_all - factored out code *------------------------------------------------------------------------*/ void usb2_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) { #if USB_HAVE_BUSDMA if (cb) { cb(bus, &usb2_bus_mem_free_all_cb); } usb2_dma_tag_unsetup(bus->dma_parent_tag); #endif mtx_destroy(&bus->bus_mtx); } Index: head/sys/dev/usb/usb_bus.h =================================================================== --- head/sys/dev/usb/usb_bus.h (revision 193073) +++ head/sys/dev/usb/usb_bus.h (revision 193074) @@ -1,108 +1,108 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_BUS_H_ #define _USB2_BUS_H_ /* * The following structure defines the USB explore message sent to the USB * explore process. */ struct usb_bus_msg { struct usb_proc_msg hdr; struct usb_bus *bus; }; /* * The following structure defines the USB statistics structure. */ struct usb_bus_stat { uint32_t uds_requests[4]; }; /* * The following structure defines an USB BUS. There is one USB BUS * for every Host or Device controller. */ struct usb_bus { struct usb_bus_stat stats_err; struct usb_bus_stat stats_ok; struct root_hold_token *bus_roothold; /* * There are two callback processes. One for Giant locked * callbacks. One for non-Giant locked callbacks. This should * avoid congestion and reduce response time in most cases. */ struct usb_process giant_callback_proc; struct usb_process non_giant_callback_proc; /* Explore process */ struct usb_process explore_proc; /* Control request process */ struct usb_process control_xfer_proc; struct usb_bus_msg explore_msg[2]; struct usb_bus_msg detach_msg[2]; struct usb_bus_msg attach_msg[2]; /* * This mutex protects the USB hardware: */ struct mtx bus_mtx; struct usb_xfer_queue intr_q; struct usb_callout power_wdog; /* power management */ device_t parent; device_t bdev; /* filled by HC driver */ #if USB_HAVE_BUSDMA struct usb_dma_parent_tag dma_parent_tag[1]; struct usb_dma_tag dma_tags[USB_BUS_DMA_TAG_MAX]; #endif struct usb_bus_methods *methods; /* filled by HC driver */ struct usb_device **devices; usb_power_mask_t hw_power_state; /* see USB_HW_POWER_XXX */ - size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; + usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; uint16_t isoc_time_last; /* in milliseconds */ uint8_t alloc_failed; /* Set if memory allocation failed. */ uint8_t driver_added_refcount; /* Current driver generation count */ enum usb_revision usbrev; /* USB revision. See "USB_REV_XXX". */ uint8_t devices_max; /* maximum number of USB devices */ uint8_t do_probe; /* set if USB BUS should be re-probed */ union { struct usb_hw_ep_scratch hw_ep_scratch[1]; struct usb_temp_setup temp_setup[1]; uint8_t data[128]; } scratch[1]; }; #endif /* _USB2_BUS_H_ */ Index: head/sys/dev/usb/usb_busdma.c =================================================================== --- head/sys/dev/usb/usb_busdma.c (revision 193073) +++ head/sys/dev/usb/usb_busdma.c (revision 193074) @@ -1,1043 +1,1043 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include #include #include #include #include #include #include #if USB_HAVE_BUSDMA -static void usb2_dma_tag_create(struct usb_dma_tag *, size_t, size_t); +static void usb2_dma_tag_create(struct usb_dma_tag *, usb_size_t, usb_size_t); static void usb2_dma_tag_destroy(struct usb_dma_tag *); static void usb2_dma_lock_cb(void *, bus_dma_lock_op_t); static void usb2_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int); static void usb2_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int); static void usb2_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int, uint8_t); #endif /*------------------------------------------------------------------------* * usb2_get_page - lookup DMA-able memory for the given offset * * NOTE: Only call this function when the "page_cache" structure has * been properly initialized ! *------------------------------------------------------------------------*/ void usb2_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res) { struct usb_page *page; #if USB_HAVE_BUSDMA if (pc->page_start) { /* Case 1 - something has been loaded into DMA */ if (pc->buffer) { /* Case 1a - Kernel Virtual Address */ res->buffer = USB_ADD_BYTES(pc->buffer, offset); } offset += pc->page_offset_buf; /* compute destination page */ page = pc->page_start; if (pc->ismultiseg) { page += (offset / USB_PAGE_SIZE); offset %= USB_PAGE_SIZE; res->length = USB_PAGE_SIZE - offset; res->physaddr = page->physaddr + offset; } else { res->length = 0 - 1; res->physaddr = page->physaddr + offset; } if (!pc->buffer) { /* Case 1b - Non Kernel Virtual Address */ res->buffer = USB_ADD_BYTES(page->buffer, offset); } return; } #endif /* Case 2 - Plain PIO */ res->buffer = USB_ADD_BYTES(pc->buffer, offset); res->length = 0 - 1; #if USB_HAVE_BUSDMA res->physaddr = 0; #endif } /*------------------------------------------------------------------------* * usb2_copy_in - copy directly to DMA-able memory *------------------------------------------------------------------------*/ void usb2_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len) { struct usb_page_search buf_res; while (len != 0) { usb2_get_page(cache, offset, &buf_res); if (buf_res.length > len) { buf_res.length = len; } bcopy(ptr, buf_res.buffer, buf_res.length); offset += buf_res.length; len -= buf_res.length; ptr = USB_ADD_BYTES(ptr, buf_res.length); } } /*------------------------------------------------------------------------* * usb2_copy_in_user - copy directly to DMA-able memory from userland * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_USER_IO int usb2_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len) { struct usb_page_search buf_res; int error; while (len != 0) { usb2_get_page(cache, offset, &buf_res); if (buf_res.length > len) { buf_res.length = len; } error = copyin(ptr, buf_res.buffer, buf_res.length); if (error) return (error); offset += buf_res.length; len -= buf_res.length; ptr = USB_ADD_BYTES(ptr, buf_res.length); } return (0); /* success */ } #endif /*------------------------------------------------------------------------* * usb2_m_copy_in - copy a mbuf chain directly into DMA-able memory *------------------------------------------------------------------------*/ #if USB_HAVE_MBUF struct usb2_m_copy_in_arg { struct usb_page_cache *cache; usb_frlength_t dst_offset; }; static int usb2_m_copy_in_cb(void *arg, void *src, uint32_t count) { register struct usb2_m_copy_in_arg *ua = arg; usb2_copy_in(ua->cache, ua->dst_offset, src, count); ua->dst_offset += count; return (0); } void usb2_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, - struct mbuf *m, size_t src_offset, usb_frlength_t src_len) + struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len) { struct usb2_m_copy_in_arg arg = {cache, dst_offset}; int error; error = m_apply(m, src_offset, src_len, &usb2_m_copy_in_cb, &arg); } #endif /*------------------------------------------------------------------------* * usb2_uiomove - factored out code *------------------------------------------------------------------------*/ #if USB_HAVE_USER_IO int usb2_uiomove(struct usb_page_cache *pc, struct uio *uio, usb_frlength_t pc_offset, usb_frlength_t len) { struct usb_page_search res; int error = 0; while (len != 0) { usb2_get_page(pc, pc_offset, &res); if (res.length > len) { res.length = len; } /* * "uiomove()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things */ error = uiomove(res.buffer, res.length, uio); if (error) { break; } pc_offset += res.length; len -= res.length; } return (error); } #endif /*------------------------------------------------------------------------* * usb2_copy_out - copy directly from DMA-able memory *------------------------------------------------------------------------*/ void usb2_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len) { struct usb_page_search res; while (len != 0) { usb2_get_page(cache, offset, &res); if (res.length > len) { res.length = len; } bcopy(res.buffer, ptr, res.length); offset += res.length; len -= res.length; ptr = USB_ADD_BYTES(ptr, res.length); } } /*------------------------------------------------------------------------* * usb2_copy_out_user - copy directly from DMA-able memory to userland * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_USER_IO int usb2_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len) { struct usb_page_search res; int error; while (len != 0) { usb2_get_page(cache, offset, &res); if (res.length > len) { res.length = len; } error = copyout(res.buffer, ptr, res.length); if (error) return (error); offset += res.length; len -= res.length; ptr = USB_ADD_BYTES(ptr, res.length); } return (0); /* success */ } #endif /*------------------------------------------------------------------------* * usb2_bzero - zero DMA-able memory *------------------------------------------------------------------------*/ void usb2_bzero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len) { struct usb_page_search res; while (len != 0) { usb2_get_page(cache, offset, &res); if (res.length > len) { res.length = len; } bzero(res.buffer, res.length); offset += res.length; len -= res.length; } } #if USB_HAVE_BUSDMA /*------------------------------------------------------------------------* * usb2_dma_lock_cb - dummy callback *------------------------------------------------------------------------*/ static void usb2_dma_lock_cb(void *arg, bus_dma_lock_op_t op) { /* we use "mtx_owned()" instead of this function */ } /*------------------------------------------------------------------------* * usb2_dma_tag_create - allocate a DMA tag * * NOTE: If the "align" parameter has a value of 1 the DMA-tag will * allow multi-segment mappings. Else all mappings are single-segment. *------------------------------------------------------------------------*/ static void usb2_dma_tag_create(struct usb_dma_tag *udt, - size_t size, size_t align) + usb_size_t size, usb_size_t align) { bus_dma_tag_t tag; if (bus_dma_tag_create ( /* parent */ udt->tag_parent->tag, /* alignment */ align, /* boundary */ USB_PAGE_SIZE, /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1, /* highaddr */ BUS_SPACE_MAXADDR, /* filter */ NULL, /* filterarg */ NULL, /* maxsize */ size, /* nsegments */ (align == 1) ? (2 + (size / USB_PAGE_SIZE)) : 1, /* maxsegsz */ (align == 1) ? USB_PAGE_SIZE : size, /* flags */ BUS_DMA_KEEP_PG_OFFSET, /* lockfn */ &usb2_dma_lock_cb, /* lockarg */ NULL, &tag)) { tag = NULL; } udt->tag = tag; } /*------------------------------------------------------------------------* * usb2_dma_tag_free - free a DMA tag *------------------------------------------------------------------------*/ static void usb2_dma_tag_destroy(struct usb_dma_tag *udt) { bus_dma_tag_destroy(udt->tag); } /*------------------------------------------------------------------------* * usb2_pc_alloc_mem_cb - BUS-DMA callback function *------------------------------------------------------------------------*/ static void usb2_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { usb2_pc_common_mem_cb(arg, segs, nseg, error, 0); } /*------------------------------------------------------------------------* * usb2_pc_load_mem_cb - BUS-DMA callback function *------------------------------------------------------------------------*/ static void usb2_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { usb2_pc_common_mem_cb(arg, segs, nseg, error, 1); } /*------------------------------------------------------------------------* * usb2_pc_common_mem_cb - BUS-DMA callback function *------------------------------------------------------------------------*/ static void usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error, uint8_t isload) { struct usb_dma_parent_tag *uptag; struct usb_page_cache *pc; struct usb_page *pg; - size_t rem; + usb_size_t rem; uint8_t owned; pc = arg; uptag = pc->tag_parent; /* * XXX There is sometimes recursive locking here. * XXX We should try to find a better solution. * XXX Until further the "owned" variable does * XXX the trick. */ if (error) { goto done; } pg = pc->page_start; pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); rem = segs->ds_addr & (USB_PAGE_SIZE - 1); pc->page_offset_buf = rem; pc->page_offset_end += rem; nseg--; #if (USB_DEBUG != 0) if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { /* * This check verifies that the physical address is correct: */ DPRINTFN(0, "Page offset was not preserved!\n"); error = 1; goto done; } #endif while (nseg > 0) { nseg--; segs++; pg++; pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); } done: owned = mtx_owned(uptag->mtx); if (!owned) mtx_lock(uptag->mtx); uptag->dma_error = (error ? 1 : 0); if (isload) { (uptag->func) (uptag); } else { usb2_cv_broadcast(uptag->cv); } if (!owned) mtx_unlock(uptag->mtx); } /*------------------------------------------------------------------------* * usb2_pc_alloc_mem - allocate DMA'able memory * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb2_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, - size_t size, size_t align) + usb_size_t size, usb_size_t align) { struct usb_dma_parent_tag *uptag; struct usb_dma_tag *utag; bus_dmamap_t map; void *ptr; int err; uptag = pc->tag_parent; if (align != 1) { /* * The alignment must be greater or equal to the * "size" else the object can be split between two * memory pages and we get a problem! */ while (align < size) { align *= 2; if (align == 0) { goto error; } } #if 1 /* * XXX BUS-DMA workaround - FIXME later: * * We assume that that the aligment at this point of * the code is greater than or equal to the size and * less than two times the size, so that if we double * the size, the size will be greater than the * alignment. * * The bus-dma system has a check for "alignment" * being less than "size". If that check fails we end * up using contigmalloc which is page based even for * small allocations. Try to avoid that to save * memory, hence we sometimes to a large number of * small allocations! */ if (size <= (USB_PAGE_SIZE / 2)) { size *= 2; } #endif } /* get the correct DMA tag */ utag = usb2_dma_tag_find(uptag, size, align); if (utag == NULL) { goto error; } /* allocate memory */ if (bus_dmamem_alloc( utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { goto error; } /* setup page cache */ pc->buffer = ptr; pc->page_start = pg; pc->page_offset_buf = 0; pc->page_offset_end = size; pc->map = map; pc->tag = utag->tag; pc->ismultiseg = (align == 1); mtx_lock(uptag->mtx); /* load memory into DMA */ err = bus_dmamap_load( utag->tag, map, ptr, size, &usb2_pc_alloc_mem_cb, pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); if (err == EINPROGRESS) { usb2_cv_wait(uptag->cv, uptag->mtx); err = 0; } mtx_unlock(uptag->mtx); if (err || uptag->dma_error) { bus_dmamem_free(utag->tag, ptr, map); goto error; } bzero(ptr, size); usb2_pc_cpu_flush(pc); return (0); error: /* reset most of the page cache */ pc->buffer = NULL; pc->page_start = NULL; pc->page_offset_buf = 0; pc->page_offset_end = 0; pc->map = NULL; pc->tag = NULL; return (1); } /*------------------------------------------------------------------------* * usb2_pc_free_mem - free DMA memory * * This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_pc_free_mem(struct usb_page_cache *pc) { if (pc && pc->buffer) { bus_dmamap_unload(pc->tag, pc->map); bus_dmamem_free(pc->tag, pc->buffer, pc->map); pc->buffer = NULL; } } /*------------------------------------------------------------------------* * usb2_pc_load_mem - load virtual memory into DMA * * Return values: * 0: Success * Else: Error *------------------------------------------------------------------------*/ uint8_t -usb2_pc_load_mem(struct usb_page_cache *pc, size_t size, uint8_t sync) +usb2_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync) { /* setup page cache */ pc->page_offset_buf = 0; pc->page_offset_end = size; pc->ismultiseg = 1; mtx_assert(pc->tag_parent->mtx, MA_OWNED); if (size > 0) { if (sync) { struct usb_dma_parent_tag *uptag; int err; uptag = pc->tag_parent; /* * We have to unload the previous loaded DMA * pages before trying to load a new one! */ bus_dmamap_unload(pc->tag, pc->map); /* * Try to load memory into DMA. */ err = bus_dmamap_load( pc->tag, pc->map, pc->buffer, size, &usb2_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK); if (err == EINPROGRESS) { usb2_cv_wait(uptag->cv, uptag->mtx); err = 0; } if (err || uptag->dma_error) { return (1); } } else { /* * We have to unload the previous loaded DMA * pages before trying to load a new one! */ bus_dmamap_unload(pc->tag, pc->map); /* * Try to load memory into DMA. The callback * will be called in all cases: */ if (bus_dmamap_load( pc->tag, pc->map, pc->buffer, size, &usb2_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) { } } } else { if (!sync) { /* * Call callback so that refcount is decremented * properly: */ pc->tag_parent->dma_error = 0; (pc->tag_parent->func) (pc->tag_parent); } } return (0); } /*------------------------------------------------------------------------* * usb2_pc_cpu_invalidate - invalidate CPU cache *------------------------------------------------------------------------*/ void usb2_pc_cpu_invalidate(struct usb_page_cache *pc) { if (pc->page_offset_end == pc->page_offset_buf) { /* nothing has been loaded into this page cache! */ return; } bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); } /*------------------------------------------------------------------------* * usb2_pc_cpu_flush - flush CPU cache *------------------------------------------------------------------------*/ void usb2_pc_cpu_flush(struct usb_page_cache *pc) { if (pc->page_offset_end == pc->page_offset_buf) { /* nothing has been loaded into this page cache! */ return; } bus_dmamap_sync(pc->tag, pc->map, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } /*------------------------------------------------------------------------* * usb2_pc_dmamap_create - create a DMA map * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t -usb2_pc_dmamap_create(struct usb_page_cache *pc, size_t size) +usb2_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size) { struct usb_xfer_root *info; struct usb_dma_tag *utag; /* get info */ info = USB_DMATAG_TO_XROOT(pc->tag_parent); /* sanity check */ if (info == NULL) { goto error; } utag = usb2_dma_tag_find(pc->tag_parent, size, 1); if (utag == NULL) { goto error; } /* create DMA map */ if (bus_dmamap_create(utag->tag, 0, &pc->map)) { goto error; } pc->tag = utag->tag; return 0; /* success */ error: pc->map = NULL; pc->tag = NULL; return 1; /* failure */ } /*------------------------------------------------------------------------* * usb2_pc_dmamap_destroy * * This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_pc_dmamap_destroy(struct usb_page_cache *pc) { if (pc && pc->tag) { bus_dmamap_destroy(pc->tag, pc->map); pc->tag = NULL; pc->map = NULL; } } /*------------------------------------------------------------------------* * usb2_dma_tag_find - factored out code *------------------------------------------------------------------------*/ struct usb_dma_tag * usb2_dma_tag_find(struct usb_dma_parent_tag *udpt, - size_t size, size_t align) + usb_size_t size, usb_size_t align) { struct usb_dma_tag *udt; uint8_t nudt; USB_ASSERT(align > 0, ("Invalid parameter align = 0!\n")); USB_ASSERT(size > 0, ("Invalid parameter size = 0!\n")); udt = udpt->utag_first; nudt = udpt->utag_max; while (nudt--) { if (udt->align == 0) { usb2_dma_tag_create(udt, size, align); if (udt->tag == NULL) { return (NULL); } udt->align = align; udt->size = size; return (udt); } if ((udt->align == align) && (udt->size == size)) { return (udt); } udt++; } return (NULL); } /*------------------------------------------------------------------------* * usb2_dma_tag_setup - initialise USB DMA tags *------------------------------------------------------------------------*/ void usb2_dma_tag_setup(struct usb_dma_parent_tag *udpt, struct usb_dma_tag *udt, bus_dma_tag_t dmat, struct mtx *mtx, usb_dma_callback_t *func, uint8_t ndmabits, uint8_t nudt) { bzero(udpt, sizeof(*udpt)); /* sanity checking */ if ((nudt == 0) || (ndmabits == 0) || (mtx == NULL)) { /* something is corrupt */ return; } /* initialise condition variable */ usb2_cv_init(udpt->cv, "USB DMA CV"); /* store some information */ udpt->mtx = mtx; udpt->func = func; udpt->tag = dmat; udpt->utag_first = udt; udpt->utag_max = nudt; udpt->dma_bits = ndmabits; while (nudt--) { bzero(udt, sizeof(*udt)); udt->tag_parent = udpt; udt++; } } /*------------------------------------------------------------------------* * usb2_bus_tag_unsetup - factored out code *------------------------------------------------------------------------*/ void usb2_dma_tag_unsetup(struct usb_dma_parent_tag *udpt) { struct usb_dma_tag *udt; uint8_t nudt; udt = udpt->utag_first; nudt = udpt->utag_max; while (nudt--) { if (udt->align) { /* destroy the USB DMA tag */ usb2_dma_tag_destroy(udt); udt->align = 0; } udt++; } if (udpt->utag_max) { /* destroy the condition variable */ usb2_cv_destroy(udpt->cv); } } /*------------------------------------------------------------------------* * usb2_bdma_work_loop * * This function handles loading of virtual buffers into DMA and is * only called when "dma_refcount" is zero. *------------------------------------------------------------------------*/ void usb2_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); usb2_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); usb2_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 */ usb2_pc_load_mem(xfer->frbuffers, info->dma_frlength_0, 0); } else { /* default case */ nframes = info->dma_currframe; usb2_pc_load_mem(xfer->frbuffers + nframes, xfer->frlengths[nframes], 0); } /* advance frame index */ info->dma_currframe++; return; } /* go ahead */ usb2_bdma_pre_sync(xfer); /* start loading next USB transfer, if any */ usb2_command_wrapper(pq, NULL); /* finally start the hardware */ usb2_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usb2_bdma_done_event * * This function is called when the BUS-DMA has loaded virtual memory * into DMA, if any. *------------------------------------------------------------------------*/ void usb2_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 */ usb2_command_wrapper(&info->dma_q, info->dma_q.curr); } /*------------------------------------------------------------------------* * usb2_bdma_pre_sync * * This function handles DMA synchronisation that must be done before * an USB transfer is started. *------------------------------------------------------------------------*/ void usb2_bdma_pre_sync(struct usb_xfer *xfer) { struct usb_page_cache *pc; usb_frcount_t nframes; if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ nframes = 1; } else { /* can be multiple frame buffers */ nframes = xfer->nframes; } pc = xfer->frbuffers; while (nframes--) { if (pc->isread) { usb2_pc_cpu_invalidate(pc); } else { usb2_pc_cpu_flush(pc); } pc++; } } /*------------------------------------------------------------------------* * usb2_bdma_post_sync * * This function handles DMA synchronisation that must be done after * an USB transfer is complete. *------------------------------------------------------------------------*/ void usb2_bdma_post_sync(struct usb_xfer *xfer) { struct usb_page_cache *pc; usb_frcount_t nframes; if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ nframes = 1; } else { /* can be multiple frame buffers */ nframes = xfer->nframes; } pc = xfer->frbuffers; while (nframes--) { if (pc->isread) { usb2_pc_cpu_invalidate(pc); } pc++; } } #endif Index: head/sys/dev/usb/usb_busdma.h =================================================================== --- head/sys/dev/usb/usb_busdma.h (revision 193073) +++ head/sys/dev/usb/usb_busdma.h (revision 193074) @@ -1,175 +1,175 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_BUSDMA_H_ #define _USB2_BUSDMA_H_ #include #include #include /* defines */ #define USB_PAGE_SIZE PAGE_SIZE /* use system PAGE_SIZE */ #if (__FreeBSD_version >= 700020) #define USB_GET_DMA_TAG(dev) bus_get_dma_tag(dev) #else #define USB_GET_DMA_TAG(dev) NULL /* XXX */ #endif /* structure prototypes */ struct usb_xfer_root; struct usb_dma_parent_tag; struct usb_dma_tag; /* * The following typedef defines the USB DMA load done callback. */ typedef void (usb_dma_callback_t)(struct usb_dma_parent_tag *udpt); /* * The following structure defines physical and non kernel virtual * address of a memory page having size USB_PAGE_SIZE. */ struct usb_page { #if USB_HAVE_BUSDMA bus_size_t physaddr; void *buffer; /* non Kernel Virtual Address */ #endif }; /* * The following structure is used when needing the kernel virtual * pointer and the physical address belonging to an offset in an USB * page cache. */ struct usb_page_search { void *buffer; #if USB_HAVE_BUSDMA bus_size_t physaddr; #endif - size_t length; + usb_size_t length; }; /* * The following structure is used to keep information about a DMA * memory allocation. */ struct usb_page_cache { #if USB_HAVE_BUSDMA bus_dma_tag_t tag; bus_dmamap_t map; struct usb_page *page_start; #endif struct usb_dma_parent_tag *tag_parent; /* always set */ void *buffer; /* virtual buffer pointer */ #if USB_HAVE_BUSDMA - size_t page_offset_buf; - size_t page_offset_end; + usb_size_t page_offset_buf; + usb_size_t page_offset_end; uint8_t isread:1; /* set if we are currently reading * from the memory. Else write. */ uint8_t ismultiseg:1; /* set if we can have multiple * segments */ #endif }; /* * The following structure describes the parent USB DMA tag. */ #if USB_HAVE_BUSDMA struct usb_dma_parent_tag { struct cv cv[1]; /* internal condition variable */ bus_dma_tag_t tag; /* always set */ struct mtx *mtx; /* private mutex, always set */ usb_dma_callback_t *func; /* load complete callback function */ struct usb_dma_tag *utag_first;/* pointer to first USB DMA tag */ uint8_t dma_error; /* set if DMA load operation failed */ uint8_t dma_bits; /* number of DMA address lines */ uint8_t utag_max; /* number of USB DMA tags */ }; #else struct usb_dma_parent_tag {}; /* empty struct */ #endif /* * The following structure describes an USB DMA tag. */ #if USB_HAVE_BUSDMA struct usb_dma_tag { struct usb_dma_parent_tag *tag_parent; bus_dma_tag_t tag; - size_t align; - size_t size; + usb_size_t align; + usb_size_t size; }; #else struct usb_dma_tag {}; /* empty struct */ #endif /* function prototypes */ int usb2_uiomove(struct usb_page_cache *pc, struct uio *uio, usb_frlength_t pc_offset, usb_frlength_t len); struct usb_dma_tag *usb2_dma_tag_find(struct usb_dma_parent_tag *udpt, - size_t size, size_t align); + usb_size_t size, usb_size_t align); uint8_t usb2_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, - size_t size, size_t align); -uint8_t usb2_pc_dmamap_create(struct usb_page_cache *pc, size_t size); -uint8_t usb2_pc_load_mem(struct usb_page_cache *pc, size_t size, + usb_size_t size, usb_size_t align); +uint8_t usb2_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size); +uint8_t usb2_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync); void usb2_bdma_done_event(struct usb_dma_parent_tag *udpt); void usb2_bdma_post_sync(struct usb_xfer *xfer); void usb2_bdma_pre_sync(struct usb_xfer *xfer); void usb2_bdma_work_loop(struct usb_xfer_queue *pq); void usb2_bzero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len); void usb2_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); int usb2_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); void usb2_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); int usb2_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); void usb2_dma_tag_setup(struct usb_dma_parent_tag *udpt, struct usb_dma_tag *udt, bus_dma_tag_t dmat, struct mtx *mtx, usb_dma_callback_t *func, uint8_t ndmabits, uint8_t nudt); void usb2_dma_tag_unsetup(struct usb_dma_parent_tag *udpt); void usb2_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res); void usb2_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, - struct mbuf *m, size_t src_offset, usb_frlength_t src_len); + struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len); void usb2_pc_cpu_flush(struct usb_page_cache *pc); void usb2_pc_cpu_invalidate(struct usb_page_cache *pc); void usb2_pc_dmamap_destroy(struct usb_page_cache *pc); void usb2_pc_free_mem(struct usb_page_cache *pc); #endif /* _USB2_BUSDMA_H_ */ Index: head/sys/dev/usb/usb_compat_linux.c =================================================================== --- head/sys/dev/usb/usb_compat_linux.c (revision 193073) +++ head/sys/dev/usb/usb_compat_linux.c (revision 193074) @@ -1,1609 +1,1609 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved. * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 #include #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include #include #include #include #include #include #include #include #include struct usb_linux_softc { LIST_ENTRY(usb_linux_softc) sc_attached_list; device_t sc_fbsd_dev; struct usb_device *sc_fbsd_udev; struct usb_interface *sc_ui; struct usb_driver *sc_udrv; }; /* prototypes */ static device_probe_t usb_linux_probe; static device_attach_t usb_linux_attach; static device_detach_t usb_linux_detach; static device_suspend_t usb_linux_suspend; static device_resume_t usb_linux_resume; static usb_callback_t usb_linux_isoc_callback; static usb_callback_t usb_linux_non_isoc_callback; static usb_complete_t usb_linux_wait_complete; static uint16_t usb_max_isoc_frames(struct usb_device *); static int usb_start_wait_urb(struct urb *, usb_timeout_t, uint16_t *); static const struct usb_device_id *usb_linux_lookup_id( const struct usb_device_id *, struct usb_attach_arg *); static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *); static int usb_linux_create_usb_device(struct usb_device *, device_t); static void usb_linux_cleanup_interface(struct usb_device *, struct usb_interface *); static void usb_linux_complete(struct usb_xfer *); static int usb_unlink_urb_sub(struct urb *, uint8_t); /*------------------------------------------------------------------------* * FreeBSD USB interface *------------------------------------------------------------------------*/ static LIST_HEAD(, usb_linux_softc) usb_linux_attached_list; static LIST_HEAD(, usb_driver) usb_linux_driver_list; static device_method_t usb_linux_methods[] = { /* Device interface */ DEVMETHOD(device_probe, usb_linux_probe), DEVMETHOD(device_attach, usb_linux_attach), DEVMETHOD(device_detach, usb_linux_detach), DEVMETHOD(device_suspend, usb_linux_suspend), DEVMETHOD(device_resume, usb_linux_resume), {0, 0} }; static driver_t usb_linux_driver = { .name = "usb_linux", .methods = usb_linux_methods, .size = sizeof(struct usb_linux_softc), }; static devclass_t usb_linux_devclass; DRIVER_MODULE(usb_linux, uhub, usb_linux_driver, usb_linux_devclass, NULL, 0); /*------------------------------------------------------------------------* * usb_linux_lookup_id * * This functions takes an array of "struct usb_device_id" and tries * to match the entries with the information in "struct usb_attach_arg". * If it finds a match the matching entry will be returned. * Else "NULL" will be returned. *------------------------------------------------------------------------*/ static const struct usb_device_id * usb_linux_lookup_id(const struct usb_device_id *id, struct usb_attach_arg *uaa) { if (id == NULL) { goto done; } /* * Keep on matching array entries until we find one with * "match_flags" equal to zero, which indicates the end of the * array: */ for (; id->match_flags; id++) { if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->idVendor != uaa->info.idVendor)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && (id->idProduct != uaa->info.idProduct)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && (id->bcdDevice_lo > uaa->info.bcdDevice)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && (id->bcdDevice_hi < uaa->info.bcdDevice)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && (id->bDeviceClass != uaa->info.bDeviceClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && (id->bDeviceSubClass != uaa->info.bDeviceSubClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && (id->bDeviceProtocol != uaa->info.bDeviceProtocol)) { continue; } if ((uaa->info.bDeviceClass == 0xFF) && !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | USB_DEVICE_ID_MATCH_INT_PROTOCOL))) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && (id->bInterfaceClass != uaa->info.bInterfaceClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && (id->bInterfaceSubClass != uaa->info.bInterfaceSubClass)) { continue; } if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && (id->bInterfaceProtocol != uaa->info.bInterfaceProtocol)) { continue; } /* we found a match! */ return (id); } done: return (NULL); } /*------------------------------------------------------------------------* * usb_linux_probe * * This function is the FreeBSD probe callback. It is called from the * FreeBSD USB stack through the "device_probe_and_attach()" function. *------------------------------------------------------------------------*/ static int usb_linux_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_driver *udrv; int err = ENXIO; if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } mtx_lock(&Giant); LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) { if (usb_linux_lookup_id(udrv->id_table, uaa)) { err = 0; break; } } mtx_unlock(&Giant); return (err); } /*------------------------------------------------------------------------* * usb_linux_get_usb_driver * * This function returns the pointer to the "struct usb_driver" where * the Linux USB device driver "struct usb_device_id" match was found. * We apply a lock before reading out the pointer to avoid races. *------------------------------------------------------------------------*/ static struct usb_driver * usb_linux_get_usb_driver(struct usb_linux_softc *sc) { struct usb_driver *udrv; mtx_lock(&Giant); udrv = sc->sc_udrv; mtx_unlock(&Giant); return (udrv); } /*------------------------------------------------------------------------* * usb_linux_attach * * This function is the FreeBSD attach callback. It is called from the * FreeBSD USB stack through the "device_probe_and_attach()" function. * This function is called when "usb_linux_probe()" returns zero. *------------------------------------------------------------------------*/ static int usb_linux_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv; const struct usb_device_id *id = NULL; mtx_lock(&Giant); LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) { id = usb_linux_lookup_id(udrv->id_table, uaa); if (id) break; } mtx_unlock(&Giant); if (id == NULL) { return (ENXIO); } if (usb_linux_create_usb_device(uaa->device, dev) != 0) return (ENOMEM); device_set_usb2_desc(dev); sc->sc_fbsd_udev = uaa->device; sc->sc_fbsd_dev = dev; sc->sc_udrv = udrv; sc->sc_ui = usb_ifnum_to_if(uaa->device, uaa->info.bIfaceNum); if (sc->sc_ui == NULL) { return (EINVAL); } if (udrv->probe) { if ((udrv->probe) (sc->sc_ui, id)) { return (ENXIO); } } mtx_lock(&Giant); LIST_INSERT_HEAD(&usb_linux_attached_list, sc, sc_attached_list); mtx_unlock(&Giant); /* success */ return (0); } /*------------------------------------------------------------------------* * usb_linux_detach * * This function is the FreeBSD detach callback. It is called from the * FreeBSD USB stack through the "device_detach()" function. *------------------------------------------------------------------------*/ static int usb_linux_detach(device_t dev) { struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv = NULL; mtx_lock(&Giant); if (sc->sc_attached_list.le_prev) { LIST_REMOVE(sc, sc_attached_list); sc->sc_attached_list.le_prev = NULL; udrv = sc->sc_udrv; sc->sc_udrv = NULL; } mtx_unlock(&Giant); if (udrv && udrv->disconnect) { (udrv->disconnect) (sc->sc_ui); } /* * Make sure that we free all FreeBSD USB transfers belonging to * this Linux "usb_interface", hence they will most likely not be * needed any more. */ usb_linux_cleanup_interface(sc->sc_fbsd_udev, sc->sc_ui); return (0); } /*------------------------------------------------------------------------* * usb_linux_suspend * * This function is the FreeBSD suspend callback. Usually it does nothing. *------------------------------------------------------------------------*/ static int usb_linux_suspend(device_t dev) { struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv = usb_linux_get_usb_driver(sc); int err; if (udrv && udrv->suspend) { err = (udrv->suspend) (sc->sc_ui, 0); } return (0); } /*------------------------------------------------------------------------* * usb_linux_resume * * This function is the FreeBSD resume callback. Usually it does nothing. *------------------------------------------------------------------------*/ static int usb_linux_resume(device_t dev) { struct usb_linux_softc *sc = device_get_softc(dev); struct usb_driver *udrv = usb_linux_get_usb_driver(sc); int err; if (udrv && udrv->resume) { err = (udrv->resume) (sc->sc_ui); } return (0); } /*------------------------------------------------------------------------* * Linux emulation layer *------------------------------------------------------------------------*/ /*------------------------------------------------------------------------* * usb_max_isoc_frames * * The following function returns the maximum number of isochronous * frames that we support per URB. It is not part of the Linux USB API. *------------------------------------------------------------------------*/ static uint16_t usb_max_isoc_frames(struct usb_device *dev) { ; /* indent fix */ switch (usb2_get_speed(dev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: return (USB_MAX_FULL_SPEED_ISOC_FRAMES); default: return (USB_MAX_HIGH_SPEED_ISOC_FRAMES); } } /*------------------------------------------------------------------------* * usb_submit_urb * * This function is used to queue an URB after that it has been * initialized. If it returns non-zero, it means that the URB was not * queued. *------------------------------------------------------------------------*/ int usb_submit_urb(struct urb *urb, uint16_t mem_flags) { struct usb_host_endpoint *uhe; if (urb == NULL) { return (-EINVAL); } mtx_assert(&Giant, MA_OWNED); if (urb->pipe == NULL) { return (-EINVAL); } uhe = urb->pipe; /* * Check that we have got a FreeBSD USB transfer that will dequeue * the URB structure and do the real transfer. If there are no USB * transfers, then we return an error. */ if (uhe->bsd_xfer[0] || uhe->bsd_xfer[1]) { /* we are ready! */ TAILQ_INSERT_HEAD(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->status = -EINPROGRESS; usb2_transfer_start(uhe->bsd_xfer[0]); usb2_transfer_start(uhe->bsd_xfer[1]); } else { /* no pipes have been setup yet! */ urb->status = -EINVAL; return (-EINVAL); } return (0); } /*------------------------------------------------------------------------* * usb_unlink_urb * * This function is used to stop an URB after that it is been * submitted, but before the "complete" callback has been called. On *------------------------------------------------------------------------*/ int usb_unlink_urb(struct urb *urb) { return (usb_unlink_urb_sub(urb, 0)); } static void usb_unlink_bsd(struct usb_xfer *xfer, struct urb *urb, uint8_t drain) { if (xfer && usb2_transfer_pending(xfer) && (xfer->priv_fifo == (void *)urb)) { if (drain) { mtx_unlock(&Giant); usb2_transfer_drain(xfer); mtx_lock(&Giant); } else { usb2_transfer_stop(xfer); } usb2_transfer_start(xfer); } } static int usb_unlink_urb_sub(struct urb *urb, uint8_t drain) { struct usb_host_endpoint *uhe; uint16_t x; if (urb == NULL) { return (-EINVAL); } mtx_assert(&Giant, MA_OWNED); if (urb->pipe == NULL) { return (-EINVAL); } uhe = urb->pipe; if (urb->bsd_urb_list.tqe_prev) { /* not started yet, just remove it from the queue */ TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->bsd_urb_list.tqe_prev = NULL; urb->status = -ECONNRESET; urb->actual_length = 0; for (x = 0; x < urb->number_of_packets; x++) { urb->iso_frame_desc[x].actual_length = 0; } if (urb->complete) { (urb->complete) (urb); } } else { /* * If the URB is not on the URB list, then check if one of * the FreeBSD USB transfer are processing the current URB. * If so, re-start that transfer, which will lead to the * termination of that URB: */ usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain); usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain); } return (0); } /*------------------------------------------------------------------------* * usb_clear_halt * * This function must always be used to clear the stall. Stall is when * an USB endpoint returns a stall message to the USB host controller. * Until the stall is cleared, no data can be transferred. *------------------------------------------------------------------------*/ int usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe) { struct usb_config cfg[1]; struct usb_pipe *pipe; uint8_t type; uint8_t addr; if (uhe == NULL) return (-EINVAL); type = uhe->desc.bmAttributes & UE_XFERTYPE; addr = uhe->desc.bEndpointAddress; bzero(cfg, sizeof(cfg)); cfg[0].type = type; cfg[0].endpoint = addr & UE_ADDR; cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); pipe = usb2_get_pipe(dev, uhe->bsd_iface_index, cfg); if (pipe == NULL) return (-EINVAL); usb2_clear_data_toggle(dev, pipe); return (usb_control_msg(dev, &dev->ep0, UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT, UF_ENDPOINT_HALT, addr, NULL, 0, 1000)); } /*------------------------------------------------------------------------* * usb_start_wait_urb * * This is an internal function that is used to perform synchronous * Linux USB transfers. *------------------------------------------------------------------------*/ static int usb_start_wait_urb(struct urb *urb, usb_timeout_t timeout, uint16_t *p_actlen) { int err; /* you must have a timeout! */ if (timeout == 0) { timeout = 1; } urb->complete = &usb_linux_wait_complete; urb->timeout = timeout; urb->transfer_flags |= URB_WAIT_WAKEUP; urb->transfer_flags &= ~URB_IS_SLEEPING; err = usb_submit_urb(urb, 0); if (err) goto done; /* * the URB might have completed before we get here, so check that by * using some flags! */ while (urb->transfer_flags & URB_WAIT_WAKEUP) { urb->transfer_flags |= URB_IS_SLEEPING; usb2_cv_wait(&urb->cv_wait, &Giant); urb->transfer_flags &= ~URB_IS_SLEEPING; } err = urb->status; done: if (err) { *p_actlen = 0; } else { *p_actlen = urb->actual_length; } return (err); } /*------------------------------------------------------------------------* * usb_control_msg * * The following function performs a control transfer sequence one any * control, bulk or interrupt endpoint, specified by "uhe". A control * transfer means that you transfer an 8-byte header first followed by * a data-phase as indicated by the 8-byte header. The "timeout" is * given in milliseconds. * * Return values: * 0: Success * < 0: Failure * > 0: Acutal length *------------------------------------------------------------------------*/ int usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *uhe, uint8_t request, uint8_t requesttype, uint16_t value, uint16_t index, void *data, uint16_t size, usb_timeout_t timeout) { struct usb_device_request req; struct urb *urb; int err; uint16_t actlen; uint8_t type; uint8_t addr; req.bmRequestType = requesttype; req.bRequest = request; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, size); if (uhe == NULL) { return (-EINVAL); } type = (uhe->desc.bmAttributes & UE_XFERTYPE); addr = (uhe->desc.bEndpointAddress & UE_ADDR); if (type != UE_CONTROL) { return (-EINVAL); } if (addr == 0) { /* * The FreeBSD USB stack supports standard control * transfers on control endpoint zero: */ err = usb2_do_request_flags(dev, &Giant, &req, data, USB_SHORT_XFER_OK, &actlen, timeout); if (err) { err = -EPIPE; } else { err = actlen; } return (err); } if (dev->flags.usb_mode != USB_MODE_HOST) { /* not supported */ return (-EINVAL); } err = usb_setup_endpoint(dev, uhe, 1 /* dummy */ ); /* * NOTE: we need to allocate real memory here so that we don't * transfer data to/from the stack! * * 0xFFFF is a FreeBSD specific magic value. */ urb = usb_alloc_urb(0xFFFF, size); if (urb == NULL) return (-ENOMEM); urb->dev = dev; urb->pipe = uhe; bcopy(&req, urb->setup_packet, sizeof(req)); if (size && (!(req.bmRequestType & UT_READ))) { /* move the data to a real buffer */ bcopy(data, USB_ADD_BYTES(urb->setup_packet, sizeof(req)), size); } err = usb_start_wait_urb(urb, timeout, &actlen); if (req.bmRequestType & UT_READ) { if (actlen) { bcopy(USB_ADD_BYTES(urb->setup_packet, sizeof(req)), data, actlen); } } usb_free_urb(urb); if (err == 0) { err = actlen; } return (err); } /*------------------------------------------------------------------------* * usb_set_interface * * The following function will select which alternate setting of an * USB interface you plan to use. By default alternate setting with * index zero is selected. Note that "iface_no" is not the interface * index, but rather the value of "bInterfaceNumber". *------------------------------------------------------------------------*/ int usb_set_interface(struct usb_device *dev, uint8_t iface_no, uint8_t alt_index) { struct usb_interface *p_ui = usb_ifnum_to_if(dev, iface_no); int err; if (p_ui == NULL) return (-EINVAL); if (alt_index >= p_ui->num_altsetting) return (-EINVAL); usb_linux_cleanup_interface(dev, p_ui); err = -usb2_set_alt_interface_index(dev, p_ui->bsd_iface_index, alt_index); if (err == 0) { p_ui->cur_altsetting = p_ui->altsetting + alt_index; } return (err); } /*------------------------------------------------------------------------* * usb_setup_endpoint * * The following function is an extension to the Linux USB API that * allows you to set a maximum buffer size for a given USB endpoint. * The maximum buffer size is per URB. If you don't call this function * to set a maximum buffer size, the endpoint will not be functional. * Note that for isochronous endpoints the maximum buffer size must be * a non-zero dummy, hence this function will base the maximum buffer * size on "wMaxPacketSize". *------------------------------------------------------------------------*/ int usb_setup_endpoint(struct usb_device *dev, - struct usb_host_endpoint *uhe, size_t bufsize) + struct usb_host_endpoint *uhe, usb_size_t bufsize) { struct usb_config cfg[2]; uint8_t type = uhe->desc.bmAttributes & UE_XFERTYPE; uint8_t addr = uhe->desc.bEndpointAddress; if (uhe->fbsd_buf_size == bufsize) { /* optimize */ return (0); } usb2_transfer_unsetup(uhe->bsd_xfer, 2); uhe->fbsd_buf_size = bufsize; if (bufsize == 0) { return (0); } bzero(cfg, sizeof(cfg)); if (type == UE_ISOCHRONOUS) { /* * Isochronous transfers are special in that they don't fit * into the BULK/INTR/CONTROL transfer model. */ cfg[0].type = type; cfg[0].endpoint = addr & UE_ADDR; cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); cfg[0].callback = &usb_linux_isoc_callback; cfg[0].bufsize = 0; /* use wMaxPacketSize */ cfg[0].frames = usb_max_isoc_frames(dev); cfg[0].flags.proxy_buffer = 1; #if 0 /* * The Linux USB API allows non back-to-back * isochronous frames which we do not support. If the * isochronous frames are not back-to-back we need to * do a copy, and then we need a buffer for * that. Enable this at your own risk. */ cfg[0].flags.ext_buffer = 1; #endif cfg[0].flags.short_xfer_ok = 1; bcopy(cfg, cfg + 1, sizeof(*cfg)); /* Allocate and setup two generic FreeBSD USB transfers */ if (usb2_transfer_setup(dev, &uhe->bsd_iface_index, uhe->bsd_xfer, cfg, 2, uhe, &Giant)) { return (-EINVAL); } } else { if (bufsize > (1 << 22)) { /* limit buffer size */ bufsize = (1 << 22); } /* Allocate and setup one generic FreeBSD USB transfer */ cfg[0].type = type; cfg[0].endpoint = addr & UE_ADDR; cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN); cfg[0].callback = &usb_linux_non_isoc_callback; cfg[0].bufsize = bufsize; cfg[0].flags.ext_buffer = 1; /* enable zero-copy */ cfg[0].flags.proxy_buffer = 1; cfg[0].flags.short_xfer_ok = 1; if (usb2_transfer_setup(dev, &uhe->bsd_iface_index, uhe->bsd_xfer, cfg, 1, uhe, &Giant)) { return (-EINVAL); } } return (0); } /*------------------------------------------------------------------------* * usb_linux_create_usb_device * * The following function is used to build up a per USB device * structure tree, that mimics the Linux one. The root structure * is returned by this function. *------------------------------------------------------------------------*/ static int usb_linux_create_usb_device(struct usb_device *udev, device_t dev) { struct usb_config_descriptor *cd = usb2_get_config_descriptor(udev); struct usb_descriptor *desc; struct usb_interface_descriptor *id; struct usb_endpoint_descriptor *ed; struct usb_interface *p_ui = NULL; struct usb_host_interface *p_uhi = NULL; struct usb_host_endpoint *p_uhe = NULL; - size_t size; + usb_size_t size; uint16_t niface_total; uint16_t nedesc; uint16_t iface_no_curr; uint16_t iface_index; uint8_t pass; uint8_t iface_no; /* * We do two passes. One pass for computing necessary memory size * and one pass to initialize all the allocated memory structures. */ for (pass = 0; pass < 2; pass++) { iface_no_curr = 0 - 1; niface_total = 0; iface_index = 0; nedesc = 0; desc = NULL; /* * Iterate over all the USB descriptors. Use the USB config * descriptor pointer provided by the FreeBSD USB stack. */ while ((desc = usb2_desc_foreach(cd, desc))) { /* * Build up a tree according to the descriptors we * find: */ switch (desc->bDescriptorType) { case UDESC_DEVICE: break; case UDESC_ENDPOINT: ed = (void *)desc; if ((ed->bLength < sizeof(*ed)) || (iface_index == 0)) break; if (p_uhe) { bcopy(ed, &p_uhe->desc, sizeof(p_uhe->desc)); p_uhe->bsd_iface_index = iface_index - 1; p_uhe++; } if (p_uhi) { (p_uhi - 1)->desc.bNumEndpoints++; } nedesc++; break; case UDESC_INTERFACE: id = (void *)desc; if (id->bLength < sizeof(*id)) break; if (p_uhi) { bcopy(id, &p_uhi->desc, sizeof(p_uhi->desc)); p_uhi->desc.bNumEndpoints = 0; p_uhi->endpoint = p_uhe; p_uhi->string = ""; p_uhi->bsd_iface_index = iface_index; p_uhi++; } iface_no = id->bInterfaceNumber; niface_total++; if (iface_no_curr != iface_no) { if (p_ui) { p_ui->altsetting = p_uhi - 1; p_ui->cur_altsetting = p_uhi - 1; p_ui->num_altsetting = 1; p_ui->bsd_iface_index = iface_index; p_ui->linux_udev = udev; p_ui++; } iface_no_curr = iface_no; iface_index++; } else { if (p_ui) { (p_ui - 1)->num_altsetting++; } } break; default: break; } } if (pass == 0) { size = (sizeof(*p_uhe) * nedesc) + (sizeof(*p_ui) * iface_index) + (sizeof(*p_uhi) * niface_total); p_uhe = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); p_ui = (void *)(p_uhe + nedesc); p_uhi = (void *)(p_ui + iface_index); udev->linux_iface_start = p_ui; udev->linux_iface_end = p_ui + iface_index; udev->linux_endpoint_start = p_uhe; udev->linux_endpoint_end = p_uhe + nedesc; udev->devnum = device_get_unit(dev); bcopy(&udev->ddesc, &udev->descriptor, sizeof(udev->descriptor)); bcopy(udev->default_pipe.edesc, &udev->ep0.desc, sizeof(udev->ep0.desc)); } } return (0); } /*------------------------------------------------------------------------* * usb_alloc_urb * * This function should always be used when you allocate an URB for * use with the USB Linux stack. In case of an isochronous transfer * you must specifiy the maximum number of "iso_packets" which you * plan to transfer per URB. This function is always blocking, and * "mem_flags" are not regarded like on Linux. *------------------------------------------------------------------------*/ struct urb * usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags) { struct urb *urb; - size_t size; + usb_size_t size; if (iso_packets == 0xFFFF) { /* * FreeBSD specific magic value to ask for control transfer * memory allocation: */ size = sizeof(*urb) + sizeof(struct usb_device_request) + mem_flags; } else { size = sizeof(*urb) + (iso_packets * sizeof(urb->iso_frame_desc[0])); } urb = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); if (urb) { usb2_cv_init(&urb->cv_wait, "URBWAIT"); if (iso_packets == 0xFFFF) { urb->setup_packet = (void *)(urb + 1); urb->transfer_buffer = (void *)(urb->setup_packet + sizeof(struct usb_device_request)); } else { urb->number_of_packets = iso_packets; } } return (urb); } /*------------------------------------------------------------------------* * usb_find_host_endpoint * * The following function will return the Linux USB host endpoint * structure that matches the given endpoint type and endpoint * value. If no match is found, NULL is returned. This function is not * part of the Linux USB API and is only used internally. *------------------------------------------------------------------------*/ struct usb_host_endpoint * usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep) { struct usb_host_endpoint *uhe; struct usb_host_endpoint *uhe_end; struct usb_host_interface *uhi; struct usb_interface *ui; uint8_t ea; uint8_t at; uint8_t mask; if (dev == NULL) { return (NULL); } if (type == UE_CONTROL) { mask = UE_ADDR; } else { mask = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR); } ep &= mask; /* * Iterate over all the interfaces searching the selected alternate * setting only, and all belonging endpoints. */ for (ui = dev->linux_iface_start; ui != dev->linux_iface_end; ui++) { uhi = ui->cur_altsetting; if (uhi) { uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints; for (uhe = uhi->endpoint; uhe != uhe_end; uhe++) { ea = uhe->desc.bEndpointAddress; at = uhe->desc.bmAttributes; if (((ea & mask) == ep) && ((at & UE_XFERTYPE) == type)) { return (uhe); } } } } if ((type == UE_CONTROL) && ((ep & UE_ADDR) == 0)) { return (&dev->ep0); } return (NULL); } /*------------------------------------------------------------------------* * usb_altnum_to_altsetting * * The following function returns a pointer to an alternate setting by * index given a "usb_interface" pointer. If the alternate setting by * index does not exist, NULL is returned. And alternate setting is a * variant of an interface, but usually with slightly different * characteristics. *------------------------------------------------------------------------*/ struct usb_host_interface * usb_altnum_to_altsetting(const struct usb_interface *intf, uint8_t alt_index) { if (alt_index >= intf->num_altsetting) { return (NULL); } return (intf->altsetting + alt_index); } /*------------------------------------------------------------------------* * usb_ifnum_to_if * * The following function searches up an USB interface by * "bInterfaceNumber". If no match is found, NULL is returned. *------------------------------------------------------------------------*/ struct usb_interface * usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no) { struct usb_interface *p_ui; for (p_ui = dev->linux_iface_start; p_ui != dev->linux_iface_end; p_ui++) { if ((p_ui->num_altsetting > 0) && (p_ui->altsetting->desc.bInterfaceNumber == iface_no)) { return (p_ui); } } return (NULL); } /*------------------------------------------------------------------------* * usb_buffer_alloc *------------------------------------------------------------------------*/ void * -usb_buffer_alloc(struct usb_device *dev, size_t size, uint16_t mem_flags, uint8_t *dma_addr) +usb_buffer_alloc(struct usb_device *dev, usb_size_t size, uint16_t mem_flags, uint8_t *dma_addr) { return (malloc(size, M_USBDEV, M_WAITOK | M_ZERO)); } /*------------------------------------------------------------------------* * usb_get_intfdata *------------------------------------------------------------------------*/ void * usb_get_intfdata(struct usb_interface *intf) { return (intf->bsd_priv_sc); } /*------------------------------------------------------------------------* * usb_linux_register * * The following function is used by the "USB_DRIVER_EXPORT()" macro, * and is used to register a Linux USB driver, so that its * "usb_device_id" structures gets searched a probe time. This * function is not part of the Linux USB API, and is for internal use * only. *------------------------------------------------------------------------*/ void usb_linux_register(void *arg) { struct usb_driver *drv = arg; mtx_lock(&Giant); LIST_INSERT_HEAD(&usb_linux_driver_list, drv, linux_driver_list); mtx_unlock(&Giant); usb2_needs_explore_all(); } /*------------------------------------------------------------------------* * usb_linux_deregister * * The following function is used by the "USB_DRIVER_EXPORT()" macro, * and is used to deregister a Linux USB driver. This function will * ensure that all driver instances belonging to the Linux USB device * driver in question, gets detached before the driver is * unloaded. This function is not part of the Linux USB API, and is * for internal use only. *------------------------------------------------------------------------*/ void usb_linux_deregister(void *arg) { struct usb_driver *drv = arg; struct usb_linux_softc *sc; repeat: mtx_lock(&Giant); LIST_FOREACH(sc, &usb_linux_attached_list, sc_attached_list) { if (sc->sc_udrv == drv) { mtx_unlock(&Giant); device_detach(sc->sc_fbsd_dev); goto repeat; } } LIST_REMOVE(drv, linux_driver_list); mtx_unlock(&Giant); } /*------------------------------------------------------------------------* * usb_linux_free_device * * The following function is only used by the FreeBSD USB stack, to * cleanup and free memory after that a Linux USB device was attached. *------------------------------------------------------------------------*/ void usb_linux_free_device(struct usb_device *dev) { struct usb_host_endpoint *uhe; struct usb_host_endpoint *uhe_end; int err; uhe = dev->linux_endpoint_start; uhe_end = dev->linux_endpoint_end; while (uhe != uhe_end) { err = usb_setup_endpoint(dev, uhe, 0); uhe++; } err = usb_setup_endpoint(dev, &dev->ep0, 0); free(uhe, M_USBDEV); } /*------------------------------------------------------------------------* * usb_buffer_free *------------------------------------------------------------------------*/ void -usb_buffer_free(struct usb_device *dev, size_t size, +usb_buffer_free(struct usb_device *dev, usb_size_t size, void *addr, uint8_t dma_addr) { free(addr, M_USBDEV); } /*------------------------------------------------------------------------* * usb_free_urb *------------------------------------------------------------------------*/ void usb_free_urb(struct urb *urb) { if (urb == NULL) { return; } /* make sure that the current URB is not active */ usb_kill_urb(urb); /* destroy condition variable */ usb2_cv_destroy(&urb->cv_wait); /* just free it */ free(urb, M_USBDEV); } /*------------------------------------------------------------------------* * usb_init_urb * * The following function can be used to initialize a custom URB. It * is not recommended to use this function. Use "usb_alloc_urb()" * instead. *------------------------------------------------------------------------*/ void usb_init_urb(struct urb *urb) { if (urb == NULL) { return; } bzero(urb, sizeof(*urb)); } /*------------------------------------------------------------------------* * usb_kill_urb *------------------------------------------------------------------------*/ void usb_kill_urb(struct urb *urb) { if (usb_unlink_urb_sub(urb, 1)) { /* ignore */ } } /*------------------------------------------------------------------------* * usb_set_intfdata * * The following function sets the per Linux USB interface private * data pointer. It is used by most Linux USB device drivers. *------------------------------------------------------------------------*/ void usb_set_intfdata(struct usb_interface *intf, void *data) { intf->bsd_priv_sc = data; } /*------------------------------------------------------------------------* * usb_linux_cleanup_interface * * The following function will release all FreeBSD USB transfers * associated with a Linux USB interface. It is for internal use only. *------------------------------------------------------------------------*/ static void usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface) { struct usb_host_interface *uhi; struct usb_host_interface *uhi_end; struct usb_host_endpoint *uhe; struct usb_host_endpoint *uhe_end; int err; uhi = iface->altsetting; uhi_end = iface->altsetting + iface->num_altsetting; while (uhi != uhi_end) { uhe = uhi->endpoint; uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints; while (uhe != uhe_end) { err = usb_setup_endpoint(dev, uhe, 0); uhe++; } uhi++; } } /*------------------------------------------------------------------------* * usb_linux_wait_complete * * The following function is used by "usb_start_wait_urb()" to wake it * up, when an USB transfer has finished. *------------------------------------------------------------------------*/ static void usb_linux_wait_complete(struct urb *urb) { if (urb->transfer_flags & URB_IS_SLEEPING) { usb2_cv_signal(&urb->cv_wait); } urb->transfer_flags &= ~URB_WAIT_WAKEUP; } /*------------------------------------------------------------------------* * usb_linux_complete *------------------------------------------------------------------------*/ static void usb_linux_complete(struct usb_xfer *xfer) { struct urb *urb; urb = xfer->priv_fifo; xfer->priv_fifo = NULL; if (urb->complete) { (urb->complete) (urb); } } /*------------------------------------------------------------------------* * usb_linux_isoc_callback * * The following is the FreeBSD isochronous USB callback. Isochronous * frames are USB packets transferred 1000 or 8000 times per second, * depending on whether a full- or high- speed USB transfer is * used. *------------------------------------------------------------------------*/ static void usb_linux_isoc_callback(struct usb_xfer *xfer) { usb_frlength_t max_frame = xfer->max_frame_size; usb_frlength_t offset; usb_frcount_t x; struct urb *urb = xfer->priv_fifo; struct usb_host_endpoint *uhe = xfer->priv_sc; struct usb_iso_packet_descriptor *uipd; DPRINTF("\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (urb->bsd_isread) { /* copy in data with regard to the URB */ offset = 0; for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; uipd->actual_length = xfer->frlengths[x]; uipd->status = 0; if (!xfer->flags.ext_buffer) { usb2_copy_out(xfer->frbuffers, offset, USB_ADD_BYTES(urb->transfer_buffer, uipd->offset), uipd->actual_length); } offset += max_frame; } } else { for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; uipd->actual_length = xfer->frlengths[x]; uipd->status = 0; } } urb->actual_length = xfer->actlen; /* check for short transfer */ if (xfer->actlen < xfer->sumlen) { /* short transfer */ if (urb->transfer_flags & URB_SHORT_NOT_OK) { urb->status = -EPIPE; /* XXX should be * EREMOTEIO */ } else { urb->status = 0; } } else { /* success */ urb->status = 0; } /* call callback */ usb_linux_complete(xfer); case USB_ST_SETUP: tr_setup: if (xfer->priv_fifo == NULL) { /* get next transfer */ urb = TAILQ_FIRST(&uhe->bsd_urb_list); if (urb == NULL) { /* nothing to do */ return; } TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->bsd_urb_list.tqe_prev = NULL; x = xfer->max_frame_count; if (urb->number_of_packets > x) { /* XXX simply truncate the transfer */ urb->number_of_packets = x; } } else { DPRINTF("Already got a transfer\n"); /* already got a transfer (should not happen) */ urb = xfer->priv_fifo; } urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0; if (!(urb->bsd_isread)) { /* copy out data with regard to the URB */ offset = 0; for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; xfer->frlengths[x] = uipd->length; if (!xfer->flags.ext_buffer) { usb2_copy_in(xfer->frbuffers, offset, USB_ADD_BYTES(urb->transfer_buffer, uipd->offset), uipd->length); } offset += uipd->length; } } else { /* * compute the transfer length into the "offset" * variable */ offset = urb->number_of_packets * max_frame; /* setup "frlengths" array */ for (x = 0; x < urb->number_of_packets; x++) { uipd = urb->iso_frame_desc + x; xfer->frlengths[x] = max_frame; } } if (xfer->flags.ext_buffer) { /* set virtual address to load */ usb2_set_frame_data(xfer, urb->transfer_buffer, 0); } xfer->priv_fifo = urb; xfer->flags.force_short_xfer = 0; xfer->timeout = urb->timeout; xfer->nframes = urb->number_of_packets; usb2_start_hardware(xfer); return; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { urb->status = -ECONNRESET; } else { urb->status = -EPIPE; /* stalled */ } /* Set zero for "actual_length" */ urb->actual_length = 0; /* Set zero for "actual_length" */ for (x = 0; x < urb->number_of_packets; x++) { urb->iso_frame_desc[x].actual_length = 0; } /* call callback */ usb_linux_complete(xfer); if (xfer->error == USB_ERR_CANCELLED) { /* we need to return in this case */ return; } goto tr_setup; } } /*------------------------------------------------------------------------* * usb_linux_non_isoc_callback * * The following is the FreeBSD BULK/INTERRUPT and CONTROL USB * callback. It dequeues Linux USB stack compatible URB's, transforms * the URB fields into a FreeBSD USB transfer, and defragments the USB * transfer as required. When the transfer is complete the "complete" * callback is called. *------------------------------------------------------------------------*/ static void usb_linux_non_isoc_callback(struct usb_xfer *xfer) { enum { REQ_SIZE = sizeof(struct usb_device_request) }; struct urb *urb = xfer->priv_fifo; struct usb_host_endpoint *uhe = xfer->priv_sc; uint8_t *ptr; usb_frlength_t max_bulk = xfer->max_data_length; uint8_t data_frame = xfer->flags_int.control_xfr ? 1 : 0; DPRINTF("\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->flags_int.control_xfr) { /* don't transfer the setup packet again: */ xfer->frlengths[0] = 0; } if (urb->bsd_isread && (!xfer->flags.ext_buffer)) { /* copy in data with regard to the URB */ usb2_copy_out(xfer->frbuffers + data_frame, 0, urb->bsd_data_ptr, xfer->frlengths[data_frame]); } urb->bsd_length_rem -= xfer->frlengths[data_frame]; urb->bsd_data_ptr += xfer->frlengths[data_frame]; urb->actual_length += xfer->frlengths[data_frame]; /* check for short transfer */ if (xfer->actlen < xfer->sumlen) { urb->bsd_length_rem = 0; /* short transfer */ if (urb->transfer_flags & URB_SHORT_NOT_OK) { urb->status = -EPIPE; } else { urb->status = 0; } } else { /* check remainder */ if (urb->bsd_length_rem > 0) { goto setup_bulk; } /* success */ urb->status = 0; } /* call callback */ usb_linux_complete(xfer); case USB_ST_SETUP: tr_setup: /* get next transfer */ urb = TAILQ_FIRST(&uhe->bsd_urb_list); if (urb == NULL) { /* nothing to do */ return; } TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list); urb->bsd_urb_list.tqe_prev = NULL; xfer->priv_fifo = urb; xfer->flags.force_short_xfer = 0; xfer->timeout = urb->timeout; if (xfer->flags_int.control_xfr) { /* * USB control transfers need special handling. * First copy in the header, then copy in data! */ if (!xfer->flags.ext_buffer) { usb2_copy_in(xfer->frbuffers, 0, urb->setup_packet, REQ_SIZE); } else { /* set virtual address to load */ usb2_set_frame_data(xfer, urb->setup_packet, 0); } xfer->frlengths[0] = REQ_SIZE; ptr = urb->setup_packet; /* setup data transfer direction and length */ urb->bsd_isread = (ptr[0] & UT_READ) ? 1 : 0; urb->bsd_length_rem = ptr[6] | (ptr[7] << 8); } else { /* setup data transfer direction */ urb->bsd_length_rem = urb->transfer_buffer_length; urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0; } urb->bsd_data_ptr = urb->transfer_buffer; urb->actual_length = 0; setup_bulk: if (max_bulk > urb->bsd_length_rem) { max_bulk = urb->bsd_length_rem; } /* check if we need to force a short transfer */ if ((max_bulk == urb->bsd_length_rem) && (urb->transfer_flags & URB_ZERO_PACKET) && (!xfer->flags_int.control_xfr)) { xfer->flags.force_short_xfer = 1; } /* check if we need to copy in data */ if (xfer->flags.ext_buffer) { /* set virtual address to load */ usb2_set_frame_data(xfer, urb->bsd_data_ptr, data_frame); } else if (!urb->bsd_isread) { /* copy out data with regard to the URB */ usb2_copy_in(xfer->frbuffers + data_frame, 0, urb->bsd_data_ptr, max_bulk); } xfer->frlengths[data_frame] = max_bulk; if (xfer->flags_int.control_xfr) { if (max_bulk > 0) { xfer->nframes = 2; } else { xfer->nframes = 1; } } else { xfer->nframes = 1; } usb2_start_hardware(xfer); return; default: if (xfer->error == USB_ERR_CANCELLED) { urb->status = -ECONNRESET; } else { urb->status = -EPIPE; } /* Set zero for "actual_length" */ urb->actual_length = 0; /* call callback */ usb_linux_complete(xfer); if (xfer->error == USB_ERR_CANCELLED) { /* we need to return in this case */ return; } goto tr_setup; } } Index: head/sys/dev/usb/usb_compat_linux.h =================================================================== --- head/sys/dev/usb/usb_compat_linux.h (revision 193073) +++ head/sys/dev/usb/usb_compat_linux.h (revision 193074) @@ -1,344 +1,344 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved. * Copyright (c) 2007 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB_COMPAT_LINUX_H #define _USB_COMPAT_LINUX_H struct usb_device; struct usb_interface; struct usb_driver; struct urb; typedef void *pm_message_t; typedef void (usb_complete_t)(struct urb *); #define USB_MAX_FULL_SPEED_ISOC_FRAMES (60 * 1) #define USB_MAX_HIGH_SPEED_ISOC_FRAMES (60 * 8) /* * Linux compatible USB device drivers put their device information * into the "usb_device_id" structure using the "USB_DEVICE()" macro. * The "MODULE_DEVICE_TABLE()" macro can be used to export this * information to userland. */ struct usb_device_id { /* which fields to match against */ uint16_t match_flags; #define USB_DEVICE_ID_MATCH_VENDOR 0x0001 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 /* Used for product specific matches; the BCD range is inclusive */ uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice_lo; uint16_t bcdDevice_hi; /* Used for device class matches */ uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; /* Used for interface class matches */ uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; /* Hook for driver specific information */ unsigned long driver_info; }; #define USB_DEVICE_ID_MATCH_DEVICE \ (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT) #define USB_DEVICE(vend,prod) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = (vend), \ .idProduct = (prod) /* The "usb_driver" structure holds the Linux USB device driver * callbacks, and a pointer to device ID's which this entry should * match against. Usually this entry is exposed to the USB emulation * layer using the "USB_DRIVER_EXPORT()" macro, which is defined * below. */ struct usb_driver { const char *name; int (*probe) (struct usb_interface *intf, const struct usb_device_id *id); void (*disconnect) (struct usb_interface *intf); int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf); int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); const struct usb_device_id *id_table; void (*shutdown) (struct usb_interface *intf); LIST_ENTRY(usb_driver) linux_driver_list; }; #define USB_DRIVER_EXPORT(id,p_usb_drv) \ SYSINIT(id,SI_SUB_KLD,SI_ORDER_FIRST,usb_linux_register,p_usb_drv); \ SYSUNINIT(id,SI_SUB_KLD,SI_ORDER_ANY,usb_linux_deregister,p_usb_drv) #define USB_DT_ENDPOINT_SIZE 7 #define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* * Endpoints */ #define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ #define USB_ENDPOINT_DIR_MASK 0x80 #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 #define USB_ENDPOINT_XFER_BULK 2 #define USB_ENDPOINT_XFER_INT 3 #define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 /* CONTROL REQUEST SUPPORT */ /* * Definition of direction mask for * "bEndpointAddress" and "bmRequestType": */ #define USB_DIR_MASK 0x80 #define USB_DIR_OUT 0x00 /* write to USB device */ #define USB_DIR_IN 0x80 /* read from USB device */ /* * Definition of type mask for * "bmRequestType": */ #define USB_TYPE_MASK (0x03 << 5) #define USB_TYPE_STANDARD (0x00 << 5) #define USB_TYPE_CLASS (0x01 << 5) #define USB_TYPE_VENDOR (0x02 << 5) #define USB_TYPE_RESERVED (0x03 << 5) /* * Definition of receiver mask for * "bmRequestType": */ #define USB_RECIP_MASK 0x1f #define USB_RECIP_DEVICE 0x00 #define USB_RECIP_INTERFACE 0x01 #define USB_RECIP_ENDPOINT 0x02 #define USB_RECIP_OTHER 0x03 /* * Definition of standard request values for * "bRequest": */ #define USB_REQ_GET_STATUS 0x00 #define USB_REQ_CLEAR_FEATURE 0x01 #define USB_REQ_SET_FEATURE 0x03 #define USB_REQ_SET_ADDRESS 0x05 #define USB_REQ_GET_DESCRIPTOR 0x06 #define USB_REQ_SET_DESCRIPTOR 0x07 #define USB_REQ_GET_CONFIGURATION 0x08 #define USB_REQ_SET_CONFIGURATION 0x09 #define USB_REQ_GET_INTERFACE 0x0A #define USB_REQ_SET_INTERFACE 0x0B #define USB_REQ_SYNCH_FRAME 0x0C #define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */ #define USB_REQ_GET_ENCRYPTION 0x0E #define USB_REQ_SET_HANDSHAKE 0x0F #define USB_REQ_GET_HANDSHAKE 0x10 #define USB_REQ_SET_CONNECTION 0x11 #define USB_REQ_SET_SECURITY_DATA 0x12 #define USB_REQ_GET_SECURITY_DATA 0x13 #define USB_REQ_SET_WUSB_DATA 0x14 #define USB_REQ_LOOPBACK_DATA_WRITE 0x15 #define USB_REQ_LOOPBACK_DATA_READ 0x16 #define USB_REQ_SET_INTERFACE_DS 0x17 /* * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and * are read as a bit array returned by USB_REQ_GET_STATUS. (So there * are at most sixteen features of each type.) */ #define USB_DEVICE_SELF_POWERED 0 /* (read only) */ #define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ #define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */ #define USB_DEVICE_BATTERY 2 /* (wireless) */ #define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */ #define USB_DEVICE_WUSB_DEVICE 3 /* (wireless) */ #define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */ #define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */ #define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ #define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ #define PIPE_ISOCHRONOUS 0x01 /* UE_ISOCHRONOUS */ #define PIPE_INTERRUPT 0x03 /* UE_INTERRUPT */ #define PIPE_CONTROL 0x00 /* UE_CONTROL */ #define PIPE_BULK 0x02 /* UE_BULK */ /* Whenever Linux references an USB endpoint: * a) to initialize "urb->pipe" * b) second argument passed to "usb_control_msg()" * * Then it uses one of the following macros. The "endpoint" argument * is the physical endpoint value masked by 0xF. The "dev" argument * is a pointer to "struct usb_device". */ #define usb_sndctrlpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_CONTROL, (endpoint) | USB_DIR_OUT) #define usb_rcvctrlpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_CONTROL, (endpoint) | USB_DIR_IN) #define usb_sndisocpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_ISOCHRONOUS, (endpoint) | USB_DIR_OUT) #define usb_rcvisocpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_ISOCHRONOUS, (endpoint) | USB_DIR_IN) #define usb_sndbulkpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_BULK, (endpoint) | USB_DIR_OUT) #define usb_rcvbulkpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_BULK, (endpoint) | USB_DIR_IN) #define usb_sndintpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_INTERRUPT, (endpoint) | USB_DIR_OUT) #define usb_rcvintpipe(dev,endpoint) \ usb_find_host_endpoint(dev, PIPE_INTERRUPT, (endpoint) | USB_DIR_IN) /* * The following structure is used to extend "struct urb" when we are * dealing with an isochronous endpoint. It contains information about * the data offset and data length of an isochronous packet. * The "actual_length" field is updated before the "complete" * callback in the "urb" structure is called. */ struct usb_iso_packet_descriptor { uint32_t offset; /* depreciated buffer offset (the * packets are usually back to back) */ uint16_t length; /* expected length */ uint16_t actual_length; uint16_t status; }; /* * The following structure holds various information about an USB * transfer. This structure is used for all kinds of USB transfers. * * URB is short for USB Request Block. */ struct urb { TAILQ_ENTRY(urb) bsd_urb_list; struct cv cv_wait; struct usb_device *dev; /* (in) pointer to associated device */ struct usb_host_endpoint *pipe; /* (in) pipe pointer */ uint8_t *setup_packet; /* (in) setup packet (control only) */ uint8_t *bsd_data_ptr; void *transfer_buffer; /* (in) associated data buffer */ void *context; /* (in) context for completion */ usb_complete_t *complete; /* (in) completion routine */ - size_t transfer_buffer_length;/* (in) data buffer length */ - size_t bsd_length_rem; - size_t actual_length; /* (return) actual transfer length */ + usb_size_t transfer_buffer_length;/* (in) data buffer length */ + usb_size_t bsd_length_rem; + usb_size_t actual_length; /* (return) actual transfer length */ usb_timeout_t timeout; /* FreeBSD specific */ uint16_t transfer_flags; /* (in) */ #define URB_SHORT_NOT_OK 0x0001 /* report short transfers like errors */ #define URB_ISO_ASAP 0x0002 /* ignore "start_frame" field */ #define URB_ZERO_PACKET 0x0004 /* the USB transfer ends with a short * packet */ #define URB_NO_TRANSFER_DMA_MAP 0x0008 /* "transfer_dma" is valid on submit */ #define URB_WAIT_WAKEUP 0x0010 /* custom flags */ #define URB_IS_SLEEPING 0x0020 /* custom flags */ usb_frcount_t start_frame; /* (modify) start frame (ISO) */ usb_frcount_t number_of_packets; /* (in) number of ISO packets */ uint16_t interval; /* (modify) transfer interval * (INT/ISO) */ uint16_t error_count; /* (return) number of ISO errors */ int16_t status; /* (return) status */ uint8_t setup_dma; /* (in) not used on FreeBSD */ uint8_t transfer_dma; /* (in) not used on FreeBSD */ uint8_t bsd_isread; struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */ }; /* various prototypes */ int usb_submit_urb(struct urb *urb, uint16_t mem_flags); int usb_unlink_urb(struct urb *urb); int usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe); int usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *pipe, uint8_t request, uint8_t requesttype, uint16_t value, uint16_t index, void *data, uint16_t size, usb_timeout_t timeout); int usb_set_interface(struct usb_device *dev, uint8_t ifnum, uint8_t alternate); int usb_setup_endpoint(struct usb_device *dev, struct usb_host_endpoint *uhe, usb_frlength_t bufsize); struct usb_host_endpoint *usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep); struct urb *usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags); struct usb_host_interface *usb_altnum_to_altsetting( const struct usb_interface *intf, uint8_t alt_index); struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no); -void *usb_buffer_alloc(struct usb_device *dev, size_t size, +void *usb_buffer_alloc(struct usb_device *dev, usb_size_t size, uint16_t mem_flags, uint8_t *dma_addr); void *usb_get_intfdata(struct usb_interface *intf); -void usb_buffer_free(struct usb_device *dev, size_t size, void *addr, uint8_t dma_addr); +void usb_buffer_free(struct usb_device *dev, usb_size_t size, void *addr, uint8_t dma_addr); void usb_free_urb(struct urb *urb); void usb_init_urb(struct urb *urb); void usb_kill_urb(struct urb *urb); void usb_set_intfdata(struct usb_interface *intf, void *data); void usb_linux_register(void *arg); void usb_linux_deregister(void *arg); #define interface_to_usbdev(intf) (intf)->linux_udev #define interface_to_bsddev(intf) (intf)->linux_udev->bsd_udev #endif /* _USB_COMPAT_LINUX_H */ Index: head/sys/dev/usb/usb_controller.h =================================================================== --- head/sys/dev/usb/usb_controller.h (revision 193073) +++ head/sys/dev/usb/usb_controller.h (revision 193074) @@ -1,192 +1,192 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_CONTROLLER_H_ #define _USB2_CONTROLLER_H_ /* defines */ #define USB_BUS_DMA_TAG_MAX 8 /* structure prototypes */ struct usb_bus; struct usb_page; struct usb_pipe; struct usb_page_cache; struct usb_setup_params; struct usb_hw_ep_profile; struct usb_fs_isoc_schedule; struct usb_config_descriptor; struct usb_endpoint_descriptor; /* typedefs */ -typedef void (usb_bus_mem_sub_cb_t)(struct usb_bus *bus, struct usb_page_cache *pc, struct usb_page *pg, size_t size, size_t align); +typedef void (usb_bus_mem_sub_cb_t)(struct usb_bus *bus, struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align); typedef void (usb_bus_mem_cb_t)(struct usb_bus *bus, usb_bus_mem_sub_cb_t *scb); /* * The following structure is used to define all the USB BUS * callbacks. */ struct usb_bus_methods { /* USB Device and Host mode - Mandatory */ usb_handle_req_t *roothub_exec; void (*pipe_init) (struct usb_device *, struct usb_endpoint_descriptor *, struct usb_pipe *); void (*xfer_setup) (struct usb_setup_params *); void (*xfer_unsetup) (struct usb_xfer *); void (*get_dma_delay) (struct usb_bus *, uint32_t *); void (*device_suspend) (struct usb_device *); void (*device_resume) (struct usb_device *); void (*set_hw_power) (struct usb_bus *); /* * The following flag is set if one or more control transfers are * active: */ #define USB_HW_POWER_CONTROL 0x01 /* * The following flag is set if one or more bulk transfers are * active: */ #define USB_HW_POWER_BULK 0x02 /* * The following flag is set if one or more interrupt transfers are * active: */ #define USB_HW_POWER_INTERRUPT 0x04 /* * The following flag is set if one or more isochronous transfers * are active: */ #define USB_HW_POWER_ISOC 0x08 /* * The following flag is set if one or more non-root-HUB devices * are present on the given USB bus: */ #define USB_HW_POWER_NON_ROOT_HUB 0x10 /* USB Device mode only - Mandatory */ void (*get_hw_ep_profile) (struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr); void (*set_stall) (struct usb_device *udev, struct usb_xfer *xfer, struct usb_pipe *pipe); void (*clear_stall) (struct usb_device *udev, struct usb_pipe *pipe); }; /* * The following structure is used to define all the USB pipe * callbacks. */ struct usb_pipe_methods { /* Mandatory USB Device and Host mode callbacks: */ usb_callback_t *open; usb_callback_t *close; usb_callback_t *enter; usb_callback_t *start; /* Optional */ void *info; }; /* * The following structure keeps information about what a hardware USB * endpoint supports. */ struct usb_hw_ep_profile { uint16_t max_in_frame_size; /* IN-token direction */ uint16_t max_out_frame_size; /* OUT-token direction */ uint8_t is_simplex:1; uint8_t support_multi_buffer:1; uint8_t support_bulk:1; uint8_t support_control:1; uint8_t support_interrupt:1; uint8_t support_isochronous:1; uint8_t support_in:1; /* IN-token is supported */ uint8_t support_out:1; /* OUT-token is supported */ }; /* * The following structure is used when trying to allocate hardware * endpoints for an USB configuration in USB device side mode. */ struct usb_hw_ep_scratch_sub { const struct usb_hw_ep_profile *pf; uint16_t max_frame_size; uint8_t hw_endpoint_out; uint8_t hw_endpoint_in; uint8_t needs_ep_type; uint8_t needs_in:1; uint8_t needs_out:1; }; /* * The following structure is used when trying to allocate hardware * endpoints for an USB configuration in USB device side mode. */ struct usb_hw_ep_scratch { struct usb_hw_ep_scratch_sub ep[USB_EP_MAX]; struct usb_hw_ep_scratch_sub *ep_max; struct usb_config_descriptor *cd; struct usb_device *udev; struct usb_bus_methods *methods; uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16]; uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16]; }; /* * The following structure is used when generating USB descriptors * from USB templates. */ struct usb_temp_setup { void *buf; - size_t size; + usb_size_t size; enum usb_dev_speed usb_speed; uint8_t self_powered; uint8_t bNumEndpoints; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bConfigurationValue; usb_error_t err; }; /* prototypes */ void usb2_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb); uint8_t usb2_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb); void usb2_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb); uint16_t usb2_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr); uint16_t usb2_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev, struct usb_fs_isoc_schedule **pp_start, struct usb_fs_isoc_schedule **pp_end, uint16_t isoc_time); uint8_t usb2_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len); #endif /* _USB2_CONTROLLER_H_ */ Index: head/sys/dev/usb/usb_core.h =================================================================== --- head/sys/dev/usb/usb_core.h (revision 193073) +++ head/sys/dev/usb/usb_core.h (revision 193074) @@ -1,543 +1,547 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ /* * Including this file is mandatory for all USB related c-files in the kernel. */ #ifndef _USB2_CORE_H_ #define _USB2_CORE_H_ #define USB_STACK_VERSION 2000 /* 2.0 */ /* Allow defines in "opt_usb.h" to override configuration */ #include "opt_usb.h" #include "opt_bus.h" /* Default USB configuration */ /* * The following macro defines if the code shall use cv_xxx() instead * of msleep() and wakeup(). */ #ifndef USB_HAVE_CONDVAR #define USB_HAVE_CONDVAR 0 #endif /* * The following macro defines if the code shall support * /dev/usb/x.y.z. */ #ifndef USB_HAVE_UGEN #define USB_HAVE_UGEN 1 #endif /* * The following macro defines if the code shall support BUS-DMA. */ #ifndef USB_HAVE_BUSDMA #define USB_HAVE_BUSDMA 1 #endif /* * The following macro defines if the code shall support the Linux * compatibility layer. */ #ifndef USB_HAVE_COMPAT_LINUX #define USB_HAVE_COMPAT_LINUX 1 #endif /* * The following macro defines if the code shall support * userland data transfer via copyin() and copyout() */ #ifndef USB_HAVE_USER_IO #define USB_HAVE_USER_IO 1 #endif /* * The following macro defines if the code shall support copy in via * bsd-mbufs to USB. */ #ifndef USB_HAVE_MBUF #define USB_HAVE_MBUF 1 #endif /* * The following macro defines if the code shall compile a table * describing USB vendor and product IDs. */ #ifndef USB_VERBOSE #define USB_VERBOSE 1 #endif /* * The following macro defines if USB debugging support shall be * compiled for the USB core and all drivers. */ #ifndef USB_DEBUG #define USB_DEBUG 1 #endif /* * The following macro defines if USB transaction translator support * shall be supported for the USB HUB and USB controller drivers. */ #ifndef USB_HAVE_TT_SUPPORT #define USB_HAVE_TT_SUPPORT 1 #endif /* * The following macro defines if the USB power daemon shall * be supported in the USB core. */ #ifndef USB_HAVE_POWERD #define USB_HAVE_POWERD 1 #endif /* * The following macro defines if the USB autoinstall detection shall * be supported in the USB core. */ #ifndef USB_HAVE_MSCTEST #define USB_HAVE_MSCTEST 1 #endif #ifndef USB_TD_GET_PROC #define USB_TD_GET_PROC(td) (td)->td_proc #endif #ifndef USB_PROC_GET_GID #define USB_PROC_GET_GID(td) (td)->p_pgid #endif /* Include files */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usb_if.h" #ifndef USB_HOST_ALIGN #define USB_HOST_ALIGN 8 /* bytes, must be power of two */ #endif #ifndef USB_FS_ISOC_UFRAME_MAX #define USB_FS_ISOC_UFRAME_MAX 4 /* exclusive unit */ #endif #if (USB_FS_ISOC_UFRAME_MAX > 6) #error "USB_FS_ISOC_UFRAME_MAX cannot be set higher than 6" #endif #ifndef USB_BUS_MAX #define USB_BUS_MAX 256 /* units */ #endif #ifndef USB_MAX_DEVICES #define USB_MAX_DEVICES 128 /* units */ #endif #if (USB_MAX_DEVICES < USB_MIN_DEVICES) #error "Minimum number of devices is greater than maximum number of devices." #endif #ifndef USB_IFACE_MAX #define USB_IFACE_MAX 32 /* units */ #endif #ifndef USB_FIFO_MAX #define USB_FIFO_MAX 128 /* units */ #endif #if (USB_FIFO_MAX & 1) #error "Number of FIFOs must be odd." #endif #define USB_MAX_FS_ISOC_FRAMES_PER_XFER (120) /* units */ #define USB_MAX_HS_ISOC_FRAMES_PER_XFER (8*120) /* units */ #ifndef USB_HUB_MAX_DEPTH #define USB_HUB_MAX_DEPTH 5 #endif #ifndef USB_EP0_BUFSIZE #define USB_EP0_BUFSIZE 1024 /* bytes */ #endif /* USB transfer states */ #define USB_ST_SETUP 0 #define USB_ST_TRANSFERRED 1 #define USB_ST_ERROR 2 /* * The following macro will return the current state of an USB * transfer like defined by the "USB_ST_XXX" enums. */ #define USB_GET_STATE(xfer) ((xfer)->usb2_state) /* * The following macro will tell if an USB transfer is currently * receiving or transferring data. */ #define USB_GET_DATA_ISREAD(xfer) ((xfer)->flags_int.usb_mode == \ USB_MODE_DEVICE ? (((xfer)->endpoint & UE_DIR_IN) ? 0 : 1) : \ (((xfer)->endpoint & UE_DIR_IN) ? 1 : 0)) /* * The following macros are used used to convert milliseconds into * HZ. We use 1024 instead of 1000 milliseconds per second to save a * full division. */ #define USB_MS_HZ 1024 #define USB_MS_TO_TICKS(ms) \ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ) /* macros */ #define usb2_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) #define usb2_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d) #define usb2_callout_stop(c) callout_stop(&(c)->co) #define usb2_callout_drain(c) callout_drain(&(c)->co) #define usb2_callout_pending(c) callout_pending(&(c)->co) #define USB_BUS_LOCK(_b) mtx_lock(&(_b)->bus_mtx) #define USB_BUS_UNLOCK(_b) mtx_unlock(&(_b)->bus_mtx) #define USB_BUS_LOCK_ASSERT(_b, _t) mtx_assert(&(_b)->bus_mtx, _t) #define USB_XFER_LOCK(_x) mtx_lock((_x)->xroot->xfer_mtx) #define USB_XFER_UNLOCK(_x) mtx_unlock((_x)->xroot->xfer_mtx) #define USB_XFER_LOCK_ASSERT(_x, _t) mtx_assert((_x)->xroot->xfer_mtx, _t) /* structure prototypes */ struct file; struct usb_bus; struct usb_device; struct usb_device_request; struct usb_page; struct usb_page_cache; struct usb_xfer; struct usb_xfer_root; /* typedefs */ typedef void (usb_callback_t)(struct usb_xfer *); #ifndef USB_HAVE_USB_ERROR_T typedef uint8_t usb_error_t; /* see "USB_ERR_XXX" */ #endif #ifndef USB_HAVE_TIMEOUT_T typedef uint32_t usb_timeout_t; /* milliseconds */ #endif #ifndef USB_HAVE_FRLENGTH_T typedef uint32_t usb_frlength_t; /* bytes */ #endif #ifndef USB_HAVE_FRCOUNT_T typedef uint32_t usb_frcount_t; /* units */ #endif +#ifndef USB_HAVE_SIZE_T +typedef uint32_t usb_size_t; /* bytes */ +#endif + #ifndef USB_HAVE_TICKS_T typedef uint32_t usb_ticks_t; /* system defined */ #endif #ifndef USB_HAVE_POWER_MASK_T typedef uint16_t usb_power_mask_t; /* see "USB_HW_POWER_XXX" */ #endif typedef usb_error_t (usb_handle_req_t)(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); /* structures */ /* * Common queue structure for USB transfers. */ struct usb_xfer_queue { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *curr; /* current USB transfer processed */ void (*command) (struct usb_xfer_queue *pq); uint8_t recurse_1:1; uint8_t recurse_2:1; }; /* * The following is a wrapper for the callout structure to ease * porting the code to other platforms. */ struct usb_callout { struct callout co; }; /* * The following structure defines a set of USB transfer flags. */ struct usb_xfer_flags { uint8_t force_short_xfer:1; /* force a short transmit transfer * last */ uint8_t short_xfer_ok:1; /* allow short receive transfers */ uint8_t short_frames_ok:1; /* allow short frames */ uint8_t pipe_bof:1; /* block pipe on failure */ uint8_t proxy_buffer:1; /* makes buffer size a factor of * "max_frame_size" */ uint8_t ext_buffer:1; /* uses external DMA buffer */ uint8_t manual_status:1; /* non automatic status stage on * control transfers */ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can * be ignored */ uint8_t stall_pipe:1; /* set if the endpoint belonging to * this USB transfer should be stalled * before starting this transfer! */ }; /* * The following structure defines a set of internal USB transfer * flags. */ struct usb_xfer_flags_int { enum usb_hc_mode usb_mode; /* shadow copy of "udev->usb_mode" */ uint16_t control_rem; /* remainder in bytes */ uint8_t open:1; /* set if USB pipe has been opened */ uint8_t transferring:1; /* set if an USB transfer is in * progress */ uint8_t did_dma_delay:1; /* set if we waited for HW DMA */ uint8_t did_close:1; /* set if we closed the USB transfer */ uint8_t draining:1; /* set if we are draining an USB * transfer */ uint8_t started:1; /* keeps track of started or stopped */ uint8_t bandwidth_reclaimed:1; uint8_t control_xfr:1; /* set if control transfer */ uint8_t control_hdr:1; /* set if control header should be * sent */ uint8_t control_act:1; /* set if control transfer is active */ uint8_t control_stall:1; /* set if control transfer should be stalled */ uint8_t short_frames_ok:1; /* filtered version */ uint8_t short_xfer_ok:1; /* filtered version */ #if USB_HAVE_BUSDMA uint8_t bdma_enable:1; /* filtered version (only set if * hardware supports DMA) */ uint8_t bdma_no_post_sync:1; /* set if the USB callback wrapper * should not do the BUS-DMA post sync * operation */ uint8_t bdma_setup:1; /* set if BUS-DMA has been setup */ #endif uint8_t isochronous_xfr:1; /* set if isochronous transfer */ uint8_t curr_dma_set:1; /* used by USB HC/DC driver */ uint8_t can_cancel_immed:1; /* set if USB transfer can be * cancelled immediately */ }; /* * The following structure define an USB configuration, that basically * is used when setting up an USB transfer. */ struct usb_config { usb_callback_t *callback; /* USB transfer callback */ usb_frlength_t bufsize; /* total pipe buffer size in bytes */ usb_frcount_t frames; /* maximum number of USB frames */ usb_timeout_t interval; /* interval in milliseconds */ #define USB_DEFAULT_INTERVAL 0 usb_timeout_t timeout; /* transfer timeout in milliseconds */ struct usb_xfer_flags flags; /* transfer flags */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t type; /* pipe type */ uint8_t endpoint; /* pipe number */ uint8_t direction; /* pipe direction */ uint8_t ep_index; /* pipe index match to use */ uint8_t if_index; /* "ifaces" index to use */ }; /* * The following structure defines an USB transfer. */ struct usb_xfer { struct usb_callout timeout_handle; TAILQ_ENTRY(usb_xfer) wait_entry; /* used at various places */ struct usb_page_cache *buf_fixup; /* fixup buffer(s) */ struct usb_xfer_queue *wait_queue; /* pointer to queue that we * are waiting on */ struct usb_page *dma_page_ptr; struct usb_pipe *pipe; /* our USB pipe */ struct usb_xfer_root *xroot; /* used by HC driver */ void *qh_start[2]; /* used by HC driver */ void *td_start[2]; /* used by HC driver */ void *td_transfer_first; /* used by HC driver */ void *td_transfer_last; /* used by HC driver */ void *td_transfer_cache; /* used by HC driver */ void *priv_sc; /* device driver data pointer 1 */ void *priv_fifo; /* device driver data pointer 2 */ void *local_buffer; usb_frlength_t *frlengths; struct usb_page_cache *frbuffers; usb_callback_t *callback; usb_frlength_t max_hc_frame_size; usb_frlength_t max_data_length; usb_frlength_t sumlen; /* sum of all lengths in bytes */ usb_frlength_t actlen; /* actual length in bytes */ usb_timeout_t timeout; /* milliseconds */ #define USB_NO_TIMEOUT 0 #define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ usb_frcount_t max_frame_count; /* initial value of "nframes" after * setup */ usb_frcount_t nframes; /* number of USB frames to transfer */ usb_frcount_t aframes; /* actual number of USB frames * transferred */ uint16_t max_packet_size; uint16_t max_frame_size; uint16_t qh_pos; uint16_t isoc_time_complete; /* in ms */ usb_timeout_t interval; /* milliseconds */ uint8_t address; /* physical USB address */ uint8_t endpoint; /* physical USB endpoint */ uint8_t max_packet_count; uint8_t usb2_smask; uint8_t usb2_cmask; uint8_t usb2_uframe; uint8_t usb2_state; usb_error_t error; struct usb_xfer_flags flags; struct usb_xfer_flags_int flags_int; }; /* * The following structure keeps information that is used to match * against an array of "usb_device_id" elements. */ struct usb_lookup_info { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t bIfaceIndex; uint8_t bIfaceNum; uint8_t bConfigIndex; uint8_t bConfigNum; }; /* Structure used by probe and attach */ struct usb_attach_arg { struct usb_lookup_info info; device_t temp_dev; /* for internal use */ const void *driver_info; /* for internal use */ struct usb_device *device; /* current device */ struct usb_interface *iface; /* current interface */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t port; uint8_t use_generic; /* hint for generic drivers */ }; /* external variables */ MALLOC_DECLARE(M_USB); MALLOC_DECLARE(M_USBDEV); MALLOC_DECLARE(M_USBHC); extern struct mtx usb2_ref_lock; /* prototypes */ const char *usb2_errstr(usb_error_t error); const char *usb2_statestr(enum usb_dev_state state); struct usb_config_descriptor *usb2_get_config_descriptor( struct usb_device *udev); struct usb_device_descriptor *usb2_get_device_descriptor( struct usb_device *udev); struct usb_interface *usb2_get_iface(struct usb_device *udev, uint8_t iface_index); struct usb_interface_descriptor *usb2_get_interface_descriptor( struct usb_interface *iface); uint8_t usb2_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2); uint8_t usb2_get_interface_altindex(struct usb_interface *iface); usb_error_t usb2_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index); enum usb_hc_mode usb2_get_mode(struct usb_device *udev); enum usb_dev_speed usb2_get_speed(struct usb_device *udev); uint32_t usb2_get_isoc_fps(struct usb_device *udev); usb_error_t usb2_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **pxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx); void usb2_set_frame_data(struct usb_xfer *xfer, void *ptr, usb_frcount_t frindex); void usb2_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex); void usb2_start_hardware(struct usb_xfer *xfer); void usb2_transfer_clear_stall(struct usb_xfer *xfer); void usb2_transfer_drain(struct usb_xfer *xfer); void usb2_transfer_set_stall(struct usb_xfer *xfer); uint8_t usb2_transfer_pending(struct usb_xfer *xfer); void usb2_transfer_start(struct usb_xfer *xfer); void usb2_transfer_stop(struct usb_xfer *xfer); void usb2_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup); void usb2_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index); uint8_t usb2_get_bus_index(struct usb_device *udev); uint8_t usb2_get_device_index(struct usb_device *udev); void usb2_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usb2_device_attached(struct usb_device *udev); #endif /* _USB2_CORE_H_ */ Index: head/sys/dev/usb/usb_dev.c =================================================================== --- head/sys/dev/usb/usb_dev.c (revision 193073) +++ head/sys/dev/usb/usb_dev.c (revision 193074) @@ -1,2193 +1,2193 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2006-2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. * * * usb2_dev.c - An abstraction layer for creating devices under /dev/... */ #include #include #include #include #define USB_DEBUG_VAR usb2_fifo_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN #if USB_DEBUG static int usb2_fifo_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW, &usb2_fifo_debug, 0, "Debug Level"); #endif #if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \ ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000))) #define USB_UCRED struct ucred *ucred, #else #define USB_UCRED #endif /* prototypes */ static int usb2_fifo_open(struct usb_cdev_privdata *, struct usb_fifo *, int); static void usb2_fifo_close(struct usb_fifo *, int); static void usb2_dev_init(void *); static void usb2_dev_init_post(void *); static void usb2_dev_uninit(void *); static int usb2_fifo_uiomove(struct usb_fifo *, void *, int, struct uio *); static void usb2_fifo_check_methods(struct usb_fifo_methods *); static struct usb_fifo *usb2_fifo_alloc(void); static struct usb_pipe *usb2_dev_get_pipe(struct usb_device *, uint8_t, uint8_t); static void usb2_loc_fill(struct usb_fs_privdata *, struct usb_cdev_privdata *); static void usb2_close(void *); static usb_error_t usb2_ref_device(struct usb_cdev_privdata *, int); static usb_error_t usb2_usb_ref_device(struct usb_cdev_privdata *); static void usb2_unref_device(struct usb_cdev_privdata *); static d_open_t usb2_open; static d_ioctl_t usb2_ioctl; static d_read_t usb2_read; static d_write_t usb2_write; static d_poll_t usb2_poll; static d_ioctl_t usb2_static_ioctl; static usb_fifo_open_t usb2_fifo_dummy_open; static usb_fifo_close_t usb2_fifo_dummy_close; static usb_fifo_ioctl_t usb2_fifo_dummy_ioctl; static usb_fifo_cmd_t usb2_fifo_dummy_cmd; /* character device structure used for devices (/dev/ugenX.Y and /dev/uXXX) */ struct cdevsw usb2_devsw = { .d_version = D_VERSION, .d_open = usb2_open, .d_ioctl = usb2_ioctl, .d_name = "usbdev", .d_flags = D_TRACKCLOSE, .d_read = usb2_read, .d_write = usb2_write, .d_poll = usb2_poll }; static struct cdev* usb2_dev = NULL; /* character device structure used for /dev/usb */ struct cdevsw usb2_static_devsw = { .d_version = D_VERSION, .d_ioctl = usb2_static_ioctl, .d_name = "usb" }; static TAILQ_HEAD(, usb_symlink) usb2_sym_head; static struct sx usb2_sym_lock; struct mtx usb2_ref_lock; /*------------------------------------------------------------------------* * usb2_loc_fill * * This is used to fill out a usb_cdev_privdata structure based on the * device's address as contained in usb_fs_privdata. *------------------------------------------------------------------------*/ static void usb2_loc_fill(struct usb_fs_privdata* pd, struct usb_cdev_privdata *cpd) { cpd->bus_index = pd->bus_index; cpd->dev_index = pd->dev_index; cpd->ep_addr = pd->ep_addr; cpd->fifo_index = pd->fifo_index; } /*------------------------------------------------------------------------* * usb2_ref_device * * This function is used to atomically refer an USB device by its * device location. If this function returns success the USB device * will not dissappear until the USB device is unreferenced. * * Return values: * 0: Success, refcount incremented on the given USB device. * Else: Failure. *------------------------------------------------------------------------*/ usb_error_t usb2_ref_device(struct usb_cdev_privdata* cpd, int need_uref) { struct usb_fifo **ppf; struct usb_fifo *f; DPRINTFN(2, "usb2_ref_device, cpd=%p need uref=%d\n", cpd, need_uref); mtx_lock(&usb2_ref_lock); cpd->bus = devclass_get_softc(usb2_devclass_ptr, cpd->bus_index); if (cpd->bus == NULL) { DPRINTFN(2, "no bus at %u\n", cpd->bus_index); goto error; } cpd->udev = cpd->bus->devices[cpd->dev_index]; if (cpd->udev == NULL) { DPRINTFN(2, "no device at %u\n", cpd->dev_index); goto error; } if (cpd->udev->refcount == USB_DEV_REF_MAX) { DPRINTFN(2, "no dev ref\n"); goto error; } if (need_uref) { DPRINTFN(2, "ref udev - needed\n"); cpd->udev->refcount++; cpd->is_uref = 1; mtx_unlock(&usb2_ref_lock); /* * We need to grab the sx-lock before grabbing the * FIFO refs to avoid deadlock at detach! */ sx_xlock(cpd->udev->default_sx + 1); mtx_lock(&usb2_ref_lock); } /* check if we are doing an open */ if (cpd->fflags == 0) { /* set defaults */ cpd->txfifo = NULL; cpd->rxfifo = NULL; cpd->is_write = 0; cpd->is_read = 0; cpd->is_usbfs = 0; } else { /* initialise "is_usbfs" flag */ cpd->is_usbfs = 0; /* check for write */ if (cpd->fflags & FWRITE) { ppf = cpd->udev->fifo; f = ppf[cpd->fifo_index + USB_FIFO_TX]; cpd->txfifo = f; cpd->is_write = 1; /* ref */ if (f == NULL || f->refcount == USB_FIFO_REF_MAX) goto error; if (f->curr_cpd != cpd) goto error; /* check if USB-FS is active */ if (f->fs_ep_max != 0) { cpd->is_usbfs = 1; } } else { cpd->txfifo = NULL; cpd->is_write = 0; /* no ref */ } /* check for read */ if (cpd->fflags & FREAD) { ppf = cpd->udev->fifo; f = ppf[cpd->fifo_index + USB_FIFO_RX]; cpd->rxfifo = f; cpd->is_read = 1; /* ref */ if (f == NULL || f->refcount == USB_FIFO_REF_MAX) goto error; if (f->curr_cpd != cpd) goto error; /* check if USB-FS is active */ if (f->fs_ep_max != 0) { cpd->is_usbfs = 1; } } else { cpd->rxfifo = NULL; cpd->is_read = 0; /* no ref */ } } /* when everything is OK we increment the refcounts */ if (cpd->is_write) { DPRINTFN(2, "ref write\n"); cpd->txfifo->refcount++; } if (cpd->is_read) { DPRINTFN(2, "ref read\n"); cpd->rxfifo->refcount++; } mtx_unlock(&usb2_ref_lock); if (cpd->is_uref) { mtx_lock(&Giant); /* XXX */ } return (0); error: if (cpd->is_uref) { sx_unlock(cpd->udev->default_sx + 1); if (--(cpd->udev->refcount) == 0) { usb2_cv_signal(cpd->udev->default_cv + 1); } cpd->is_uref = 0; } mtx_unlock(&usb2_ref_lock); DPRINTFN(2, "fail\n"); return (USB_ERR_INVAL); } /*------------------------------------------------------------------------* * usb2_usb_ref_device * * This function is used to upgrade an USB reference to include the * USB device reference on a USB location. * * Return values: * 0: Success, refcount incremented on the given USB device. * Else: Failure. *------------------------------------------------------------------------*/ static usb_error_t usb2_usb_ref_device(struct usb_cdev_privdata *cpd) { /* * Check if we already got an USB reference on this location: */ if (cpd->is_uref) return (0); /* success */ /* * To avoid deadlock at detach we need to drop the FIFO ref * and re-acquire a new ref! */ usb2_unref_device(cpd); return (usb2_ref_device(cpd, 1 /* need uref */)); } /*------------------------------------------------------------------------* * usb2_unref_device * * This function will release the reference count by one unit for the * given USB device. *------------------------------------------------------------------------*/ void usb2_unref_device(struct usb_cdev_privdata *cpd) { if (cpd->is_uref) { mtx_unlock(&Giant); /* XXX */ sx_unlock(cpd->udev->default_sx + 1); } mtx_lock(&usb2_ref_lock); if (cpd->is_read) { if (--(cpd->rxfifo->refcount) == 0) { usb2_cv_signal(&cpd->rxfifo->cv_drain); } cpd->is_read = 0; } if (cpd->is_write) { if (--(cpd->txfifo->refcount) == 0) { usb2_cv_signal(&cpd->txfifo->cv_drain); } cpd->is_write = 0; } if (cpd->is_uref) { if (--(cpd->udev->refcount) == 0) { usb2_cv_signal(cpd->udev->default_cv + 1); } cpd->is_uref = 0; } mtx_unlock(&usb2_ref_lock); } static struct usb_fifo * usb2_fifo_alloc(void) { struct usb_fifo *f; f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); if (f) { usb2_cv_init(&f->cv_io, "FIFO-IO"); usb2_cv_init(&f->cv_drain, "FIFO-DRAIN"); f->refcount = 1; } return (f); } /*------------------------------------------------------------------------* * usb2_fifo_create *------------------------------------------------------------------------*/ static int usb2_fifo_create(struct usb_cdev_privdata *cpd) { struct usb_device *udev = cpd->udev; struct usb_fifo *f; struct usb_pipe *pipe; uint8_t n; uint8_t is_tx; uint8_t is_rx; uint8_t no_null; uint8_t is_busy; int ep = cpd->ep_addr; is_tx = (cpd->fflags & FWRITE) ? 1 : 0; is_rx = (cpd->fflags & FREAD) ? 1 : 0; no_null = 1; is_busy = 0; /* Preallocated FIFO */ if (ep < 0) { DPRINTFN(5, "Preallocated FIFO\n"); if (is_tx) { f = udev->fifo[cpd->fifo_index + USB_FIFO_TX]; if (f == NULL) return (EINVAL); cpd->txfifo = f; } if (is_rx) { f = udev->fifo[cpd->fifo_index + USB_FIFO_RX]; if (f == NULL) return (EINVAL); cpd->rxfifo = f; } return (0); } KASSERT(ep >= 0 && ep <= 15, ("endpoint %d out of range", ep)); /* search for a free FIFO slot */ DPRINTFN(5, "Endpoint device, searching for 0x%02x\n", ep); for (n = 0;; n += 2) { if (n == USB_FIFO_MAX) { if (no_null) { no_null = 0; n = 0; } else { /* end of FIFOs reached */ DPRINTFN(5, "out of FIFOs\n"); return (ENOMEM); } } /* Check for TX FIFO */ if (is_tx) { f = udev->fifo[n + USB_FIFO_TX]; if (f != NULL) { if (f->dev_ep_index != ep) { /* wrong endpoint index */ continue; } if (f->curr_cpd != NULL) { /* FIFO is opened */ is_busy = 1; continue; } } else if (no_null) { continue; } } /* Check for RX FIFO */ if (is_rx) { f = udev->fifo[n + USB_FIFO_RX]; if (f != NULL) { if (f->dev_ep_index != ep) { /* wrong endpoint index */ continue; } if (f->curr_cpd != NULL) { /* FIFO is opened */ is_busy = 1; continue; } } else if (no_null) { continue; } } break; } if (no_null == 0) { if (ep >= (USB_EP_MAX / 2)) { /* we don't create any endpoints in this range */ DPRINTFN(5, "ep out of range\n"); return (is_busy ? EBUSY : EINVAL); } } if ((ep != 0) && is_busy) { /* * Only the default control endpoint is allowed to be * opened multiple times! */ DPRINTFN(5, "busy\n"); return (EBUSY); } /* Check TX FIFO */ if (is_tx && (udev->fifo[n + USB_FIFO_TX] == NULL)) { pipe = usb2_dev_get_pipe(udev, ep, USB_FIFO_TX); DPRINTFN(5, "dev_get_pipe(%d, 0x%x)\n", ep, USB_FIFO_TX); if (pipe == NULL) { DPRINTFN(5, "dev_get_pipe returned NULL\n"); return (EINVAL); } f = usb2_fifo_alloc(); if (f == NULL) { DPRINTFN(5, "could not alloc tx fifo\n"); return (ENOMEM); } /* update some fields */ f->fifo_index = n + USB_FIFO_TX; f->dev_ep_index = ep; f->priv_mtx = udev->default_mtx; f->priv_sc0 = pipe; f->methods = &usb2_ugen_methods; f->iface_index = pipe->iface_index; f->udev = udev; mtx_lock(&usb2_ref_lock); udev->fifo[n + USB_FIFO_TX] = f; mtx_unlock(&usb2_ref_lock); } /* Check RX FIFO */ if (is_rx && (udev->fifo[n + USB_FIFO_RX] == NULL)) { pipe = usb2_dev_get_pipe(udev, ep, USB_FIFO_RX); DPRINTFN(5, "dev_get_pipe(%d, 0x%x)\n", ep, USB_FIFO_RX); if (pipe == NULL) { DPRINTFN(5, "dev_get_pipe returned NULL\n"); return (EINVAL); } f = usb2_fifo_alloc(); if (f == NULL) { DPRINTFN(5, "could not alloc rx fifo\n"); return (ENOMEM); } /* update some fields */ f->fifo_index = n + USB_FIFO_RX; f->dev_ep_index = ep; f->priv_mtx = udev->default_mtx; f->priv_sc0 = pipe; f->methods = &usb2_ugen_methods; f->iface_index = pipe->iface_index; f->udev = udev; mtx_lock(&usb2_ref_lock); udev->fifo[n + USB_FIFO_RX] = f; mtx_unlock(&usb2_ref_lock); } if (is_tx) { cpd->txfifo = udev->fifo[n + USB_FIFO_TX]; } if (is_rx) { cpd->rxfifo = udev->fifo[n + USB_FIFO_RX]; } /* fill out fifo index */ DPRINTFN(5, "fifo index = %d\n", n); cpd->fifo_index = n; /* complete */ return (0); } void usb2_fifo_free(struct usb_fifo *f) { uint8_t n; if (f == NULL) { /* be NULL safe */ return; } /* destroy symlink devices, if any */ for (n = 0; n != 2; n++) { if (f->symlink[n]) { usb2_free_symlink(f->symlink[n]); f->symlink[n] = NULL; } } mtx_lock(&usb2_ref_lock); /* delink ourselves to stop calls from userland */ if ((f->fifo_index < USB_FIFO_MAX) && (f->udev != NULL) && (f->udev->fifo[f->fifo_index] == f)) { f->udev->fifo[f->fifo_index] = NULL; } else { DPRINTFN(0, "USB FIFO %p has not been linked!\n", f); } /* decrease refcount */ f->refcount--; /* prevent any write flush */ f->flag_iserror = 1; /* need to wait until all callers have exited */ while (f->refcount != 0) { mtx_unlock(&usb2_ref_lock); /* avoid LOR */ mtx_lock(f->priv_mtx); /* get I/O thread out of any sleep state */ if (f->flag_sleeping) { f->flag_sleeping = 0; usb2_cv_broadcast(&f->cv_io); } mtx_unlock(f->priv_mtx); mtx_lock(&usb2_ref_lock); /* wait for sync */ usb2_cv_wait(&f->cv_drain, &usb2_ref_lock); } mtx_unlock(&usb2_ref_lock); /* take care of closing the device here, if any */ usb2_fifo_close(f, 0); usb2_cv_destroy(&f->cv_io); usb2_cv_destroy(&f->cv_drain); free(f, M_USBDEV); } static struct usb_pipe * usb2_dev_get_pipe(struct usb_device *udev, uint8_t ep_index, uint8_t dir) { struct usb_pipe *pipe; uint8_t ep_dir; if (ep_index == 0) { pipe = &udev->default_pipe; } else { if (dir == USB_FIFO_RX) { if (udev->flags.usb_mode == USB_MODE_HOST) { ep_dir = UE_DIR_IN; } else { ep_dir = UE_DIR_OUT; } } else { if (udev->flags.usb_mode == USB_MODE_HOST) { ep_dir = UE_DIR_OUT; } else { ep_dir = UE_DIR_IN; } } pipe = usb2_get_pipe_by_addr(udev, ep_index | ep_dir); } if (pipe == NULL) { /* if the pipe does not exist then return */ return (NULL); } if (pipe->edesc == NULL) { /* invalid pipe */ return (NULL); } return (pipe); /* success */ } /*------------------------------------------------------------------------* * usb2_fifo_open * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usb2_fifo_open(struct usb_cdev_privdata *cpd, struct usb_fifo *f, int fflags) { int err; if (f == NULL) { /* no FIFO there */ DPRINTFN(2, "no FIFO\n"); return (ENXIO); } /* remove FWRITE and FREAD flags */ fflags &= ~(FWRITE | FREAD); /* set correct file flags */ if ((f->fifo_index & 1) == USB_FIFO_TX) { fflags |= FWRITE; } else { fflags |= FREAD; } /* check if we are already opened */ /* we don't need any locks when checking this variable */ if (f->curr_cpd != NULL) { err = EBUSY; goto done; } /* reset short flag before open */ f->flag_short = 0; /* call open method */ err = (f->methods->f_open) (f, fflags); if (err) { goto done; } mtx_lock(f->priv_mtx); /* reset sleep flag */ f->flag_sleeping = 0; /* reset error flag */ f->flag_iserror = 0; /* reset complete flag */ f->flag_iscomplete = 0; /* reset select flag */ f->flag_isselect = 0; /* reset flushing flag */ f->flag_flushing = 0; /* reset ASYNC proc flag */ f->async_p = NULL; mtx_lock(&usb2_ref_lock); /* flag the fifo as opened to prevent others */ f->curr_cpd = cpd; mtx_unlock(&usb2_ref_lock); /* reset queue */ usb2_fifo_reset(f); mtx_unlock(f->priv_mtx); done: return (err); } /*------------------------------------------------------------------------* * usb2_fifo_reset *------------------------------------------------------------------------*/ void usb2_fifo_reset(struct usb_fifo *f) { struct usb_mbuf *m; if (f == NULL) { return; } while (1) { USB_IF_DEQUEUE(&f->used_q, m); if (m) { USB_IF_ENQUEUE(&f->free_q, m); } else { break; } } } /*------------------------------------------------------------------------* * usb2_fifo_close *------------------------------------------------------------------------*/ static void usb2_fifo_close(struct usb_fifo *f, int fflags) { int err; /* check if we are not opened */ if (f->curr_cpd == NULL) { /* nothing to do - already closed */ return; } mtx_lock(f->priv_mtx); /* clear current cdev private data pointer */ f->curr_cpd = NULL; /* check if we are selected */ if (f->flag_isselect) { selwakeup(&f->selinfo); f->flag_isselect = 0; } /* check if a thread wants SIGIO */ if (f->async_p != NULL) { PROC_LOCK(f->async_p); psignal(f->async_p, SIGIO); PROC_UNLOCK(f->async_p); f->async_p = NULL; } /* remove FWRITE and FREAD flags */ fflags &= ~(FWRITE | FREAD); /* flush written data, if any */ if ((f->fifo_index & 1) == USB_FIFO_TX) { if (!f->flag_iserror) { /* set flushing flag */ f->flag_flushing = 1; /* start write transfer, if not already started */ (f->methods->f_start_write) (f); /* check if flushed already */ while (f->flag_flushing && (!f->flag_iserror)) { /* wait until all data has been written */ f->flag_sleeping = 1; err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx); if (err) { DPRINTF("signal received\n"); break; } } } fflags |= FWRITE; /* stop write transfer, if not already stopped */ (f->methods->f_stop_write) (f); } else { fflags |= FREAD; /* stop write transfer, if not already stopped */ (f->methods->f_stop_read) (f); } /* check if we are sleeping */ if (f->flag_sleeping) { DPRINTFN(2, "Sleeping at close!\n"); } mtx_unlock(f->priv_mtx); /* call close method */ (f->methods->f_close) (f, fflags); DPRINTF("closed\n"); } /*------------------------------------------------------------------------* * usb2_open - cdev callback *------------------------------------------------------------------------*/ static int usb2_open(struct cdev *dev, int fflags, int devtype, struct thread *td) { struct usb_fs_privdata* pd = (struct usb_fs_privdata*)dev->si_drv1; struct usb_cdev_privdata *cpd; int err, ep; DPRINTFN(2, "%s fflags=0x%08x\n", dev->si_name, fflags); KASSERT(fflags & (FREAD|FWRITE), ("invalid open flags")); if (((fflags & FREAD) && !(pd->mode & FREAD)) || ((fflags & FWRITE) && !(pd->mode & FWRITE))) { DPRINTFN(2, "access mode not supported\n"); return (EPERM); } cpd = malloc(sizeof(*cpd), M_USBDEV, M_WAITOK | M_ZERO); ep = cpd->ep_addr = pd->ep_addr; usb2_loc_fill(pd, cpd); err = usb2_ref_device(cpd, 1); if (err) { DPRINTFN(2, "cannot ref device\n"); free(cpd, M_USBDEV); return (ENXIO); } cpd->fflags = fflags; /* access mode for open lifetime */ /* create FIFOs, if any */ err = usb2_fifo_create(cpd); /* check for error */ if (err) { DPRINTFN(2, "cannot create fifo\n"); usb2_unref_device(cpd); free(cpd, M_USBDEV); return (err); } if (fflags & FREAD) { err = usb2_fifo_open(cpd, cpd->rxfifo, fflags); if (err) { DPRINTFN(2, "read open failed\n"); usb2_unref_device(cpd); free(cpd, M_USBDEV); return (err); } } if (fflags & FWRITE) { err = usb2_fifo_open(cpd, cpd->txfifo, fflags); if (err) { DPRINTFN(2, "write open failed\n"); if (fflags & FREAD) { usb2_fifo_close(cpd->rxfifo, fflags); } usb2_unref_device(cpd); free(cpd, M_USBDEV); return (err); } } usb2_unref_device(cpd); devfs_set_cdevpriv(cpd, usb2_close); return (0); } /*------------------------------------------------------------------------* * usb2_close - cdev callback *------------------------------------------------------------------------*/ static void usb2_close(void *arg) { struct usb_cdev_privdata *cpd = arg; int err; DPRINTFN(2, "cpd=%p\n", cpd); err = usb2_ref_device(cpd, 1); if (err) { free(cpd, M_USBDEV); return; } if (cpd->fflags & FREAD) { usb2_fifo_close(cpd->rxfifo, cpd->fflags); } if (cpd->fflags & FWRITE) { usb2_fifo_close(cpd->txfifo, cpd->fflags); } usb2_unref_device(cpd); free(cpd, M_USBDEV); return; } static void usb2_dev_init(void *arg) { mtx_init(&usb2_ref_lock, "USB ref mutex", NULL, MTX_DEF); sx_init(&usb2_sym_lock, "USB sym mutex"); TAILQ_INIT(&usb2_sym_head); /* check the UGEN methods */ usb2_fifo_check_methods(&usb2_ugen_methods); } SYSINIT(usb2_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb2_dev_init, NULL); static void usb2_dev_init_post(void *arg) { /* * Create /dev/usb - this is needed for usbconfig(8), which * needs a well-known device name to access. */ usb2_dev = make_dev(&usb2_static_devsw, 0, UID_ROOT, GID_OPERATOR, 0644, USB_DEVICE_NAME); if (usb2_dev == NULL) { DPRINTFN(0, "Could not create usb bus device!\n"); } } SYSINIT(usb2_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb2_dev_init_post, NULL); static void usb2_dev_uninit(void *arg) { if (usb2_dev != NULL) { destroy_dev(usb2_dev); usb2_dev = NULL; } mtx_destroy(&usb2_ref_lock); sx_destroy(&usb2_sym_lock); } SYSUNINIT(usb2_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_dev_uninit, NULL); static int usb2_ioctl_f_sub(struct usb_fifo *f, u_long cmd, void *addr, struct thread *td) { int error = 0; switch (cmd) { case FIODTYPE: *(int *)addr = 0; /* character device */ break; case FIONBIO: /* handled by upper FS layer */ break; case FIOASYNC: if (*(int *)addr) { if (f->async_p != NULL) { error = EBUSY; break; } f->async_p = USB_TD_GET_PROC(td); } else { f->async_p = NULL; } break; /* XXX this is not the most general solution */ case TIOCSPGRP: if (f->async_p == NULL) { error = EINVAL; break; } if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) { error = EPERM; break; } break; default: return (ENOIOCTL); } DPRINTFN(3, "cmd 0x%lx = %d\n", cmd, error); return (error); } /*------------------------------------------------------------------------* * usb2_ioctl - cdev callback *------------------------------------------------------------------------*/ static int usb2_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread* td) { struct usb_cdev_privdata* cpd; struct usb_fifo *f; int fflags; int err; DPRINTFN(2, "cmd=0x%lx\n", cmd); err = devfs_get_cdevpriv((void **)&cpd); if (err != 0) return (err); /* * Performance optimistaion: We try to check for IOCTL's that * don't need the USB reference first. Then we grab the USB * reference if we need it! */ err = usb2_ref_device(cpd, 0 /* no uref */ ); if (err) { return (ENXIO); } fflags = cpd->fflags; f = NULL; /* set default value */ err = ENOIOCTL; /* set default value */ if (fflags & FWRITE) { f = cpd->txfifo; err = usb2_ioctl_f_sub(f, cmd, addr, td); } if (fflags & FREAD) { f = cpd->rxfifo; err = usb2_ioctl_f_sub(f, cmd, addr, td); } KASSERT(f != NULL, ("fifo not found")); if (err == ENOIOCTL) { err = (f->methods->f_ioctl) (f, cmd, addr, fflags); DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err); if (err == ENOIOCTL) { if (usb2_usb_ref_device(cpd)) { err = ENXIO; goto done; } err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags); DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err); } } if (err == ENOIOCTL) { err = ENOTTY; } done: usb2_unref_device(cpd); return (err); } /* ARGSUSED */ static int usb2_poll(struct cdev* dev, int events, struct thread* td) { struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; int fflags, revents; if (devfs_get_cdevpriv((void **)&cpd) != 0 || usb2_ref_device(cpd, 0) != 0) return (events & (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); fflags = cpd->fflags; /* Figure out who needs service */ revents = 0; if ((events & (POLLOUT | POLLWRNORM)) && (fflags & FWRITE)) { f = cpd->txfifo; mtx_lock(f->priv_mtx); if (!cpd->is_usbfs) { if (f->flag_iserror) { /* we got an error */ m = (void *)1; } else { if (f->queue_data == NULL) { /* * start write transfer, if not * already started */ (f->methods->f_start_write) (f); } /* check if any packets are available */ USB_IF_POLL(&f->free_q, m); } } else { if (f->flag_iscomplete) { m = (void *)1; } else { m = NULL; } } if (m) { revents |= events & (POLLOUT | POLLWRNORM); } else { f->flag_isselect = 1; selrecord(td, &f->selinfo); } mtx_unlock(f->priv_mtx); } if ((events & (POLLIN | POLLRDNORM)) && (fflags & FREAD)) { f = cpd->rxfifo; mtx_lock(f->priv_mtx); if (!cpd->is_usbfs) { if (f->flag_iserror) { /* we have and error */ m = (void *)1; } else { if (f->queue_data == NULL) { /* * start read transfer, if not * already started */ (f->methods->f_start_read) (f); } /* check if any packets are available */ USB_IF_POLL(&f->used_q, m); } } else { if (f->flag_iscomplete) { m = (void *)1; } else { m = NULL; } } if (m) { revents |= events & (POLLIN | POLLRDNORM); } else { f->flag_isselect = 1; selrecord(td, &f->selinfo); if (!cpd->is_usbfs) { /* start reading data */ (f->methods->f_start_read) (f); } } mtx_unlock(f->priv_mtx); } usb2_unref_device(cpd); return (revents); } static int usb2_read(struct cdev *dev, struct uio *uio, int ioflag) { struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; int fflags; int resid; int io_len; int err; uint8_t tr_data = 0; err = devfs_get_cdevpriv((void **)&cpd); if (err != 0) return (err); err = usb2_ref_device(cpd, 0 /* no uref */ ); if (err) { return (ENXIO); } fflags = cpd->fflags; f = cpd->rxfifo; if (f == NULL) { /* should not happen */ return (EPERM); } resid = uio->uio_resid; mtx_lock(f->priv_mtx); /* check for permanent read error */ if (f->flag_iserror) { err = EIO; goto done; } /* check if USB-FS interface is active */ if (cpd->is_usbfs) { /* * The queue is used for events that should be * retrieved using the "USB_FS_COMPLETE" ioctl. */ err = EINVAL; goto done; } while (uio->uio_resid > 0) { USB_IF_DEQUEUE(&f->used_q, m); if (m == NULL) { /* start read transfer, if not already started */ (f->methods->f_start_read) (f); if (ioflag & IO_NDELAY) { if (tr_data) { /* return length before error */ break; } err = EWOULDBLOCK; break; } DPRINTF("sleeping\n"); err = usb2_fifo_wait(f); if (err) { break; } continue; } if (f->methods->f_filter_read) { /* * Sometimes it is convenient to process data at the * expense of a userland process instead of a kernel * process. */ (f->methods->f_filter_read) (f, m); } tr_data = 1; io_len = MIN(m->cur_data_len, uio->uio_resid); DPRINTFN(2, "transfer %d bytes from %p\n", io_len, m->cur_data_ptr); err = usb2_fifo_uiomove(f, m->cur_data_ptr, io_len, uio); m->cur_data_len -= io_len; m->cur_data_ptr += io_len; if (m->cur_data_len == 0) { uint8_t last_packet; last_packet = m->last_packet; USB_IF_ENQUEUE(&f->free_q, m); if (last_packet) { /* keep framing */ break; } } else { USB_IF_PREPEND(&f->used_q, m); } if (err) { break; } } done: mtx_unlock(f->priv_mtx); usb2_unref_device(cpd); return (err); } static int usb2_write(struct cdev *dev, struct uio *uio, int ioflag) { struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; int fflags; int resid; int io_len; int err; uint8_t tr_data = 0; DPRINTFN(2, "\n"); err = devfs_get_cdevpriv((void **)&cpd); if (err != 0) return (err); err = usb2_ref_device(cpd, 0 /* no uref */ ); if (err) { return (ENXIO); } fflags = cpd->fflags; f = cpd->txfifo; if (f == NULL) { /* should not happen */ usb2_unref_device(cpd); return (EPERM); } resid = uio->uio_resid; mtx_lock(f->priv_mtx); /* check for permanent write error */ if (f->flag_iserror) { err = EIO; goto done; } /* check if USB-FS interface is active */ if (cpd->is_usbfs) { /* * The queue is used for events that should be * retrieved using the "USB_FS_COMPLETE" ioctl. */ err = EINVAL; goto done; } if (f->queue_data == NULL) { /* start write transfer, if not already started */ (f->methods->f_start_write) (f); } /* we allow writing zero length data */ do { USB_IF_DEQUEUE(&f->free_q, m); if (m == NULL) { if (ioflag & IO_NDELAY) { if (tr_data) { /* return length before error */ break; } err = EWOULDBLOCK; break; } DPRINTF("sleeping\n"); err = usb2_fifo_wait(f); if (err) { break; } continue; } tr_data = 1; USB_MBUF_RESET(m); io_len = MIN(m->cur_data_len, uio->uio_resid); m->cur_data_len = io_len; DPRINTFN(2, "transfer %d bytes to %p\n", io_len, m->cur_data_ptr); err = usb2_fifo_uiomove(f, m->cur_data_ptr, io_len, uio); if (err) { USB_IF_ENQUEUE(&f->free_q, m); break; } if (f->methods->f_filter_write) { /* * Sometimes it is convenient to process data at the * expense of a userland process instead of a kernel * process. */ (f->methods->f_filter_write) (f, m); } USB_IF_ENQUEUE(&f->used_q, m); (f->methods->f_start_write) (f); } while (uio->uio_resid > 0); done: mtx_unlock(f->priv_mtx); usb2_unref_device(cpd); return (err); } int usb2_static_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { union { struct usb_read_dir *urd; void* data; } u; int err = ENOTTY; u.data = data; switch (cmd) { case USB_READ_DIR: err = usb2_read_symlink(u.urd->urd_data, u.urd->urd_startentry, u.urd->urd_maxlen); break; case USB_DEV_QUIRK_GET: case USB_QUIRK_NAME_GET: case USB_DEV_QUIRK_ADD: case USB_DEV_QUIRK_REMOVE: err = usb2_quirk_ioctl_p(cmd, data, fflag, td); break; case USB_GET_TEMPLATE: *(int *)data = usb2_template; break; case USB_SET_TEMPLATE: err = priv_check(curthread, PRIV_DRIVER); if (err) break; usb2_template = *(int *)data; break; } return (err); } static int usb2_fifo_uiomove(struct usb_fifo *f, void *cp, int n, struct uio *uio) { int error; mtx_unlock(f->priv_mtx); /* * "uiomove()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things: */ error = uiomove(cp, n, uio); mtx_lock(f->priv_mtx); return (error); } int usb2_fifo_wait(struct usb_fifo *f) { int err; mtx_assert(f->priv_mtx, MA_OWNED); if (f->flag_iserror) { /* we are gone */ return (EIO); } f->flag_sleeping = 1; err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx); if (f->flag_iserror) { /* we are gone */ err = EIO; } return (err); } void usb2_fifo_signal(struct usb_fifo *f) { if (f->flag_sleeping) { f->flag_sleeping = 0; usb2_cv_broadcast(&f->cv_io); } } void usb2_fifo_wakeup(struct usb_fifo *f) { usb2_fifo_signal(f); if (f->flag_isselect) { selwakeup(&f->selinfo); f->flag_isselect = 0; } if (f->async_p != NULL) { PROC_LOCK(f->async_p); psignal(f->async_p, SIGIO); PROC_UNLOCK(f->async_p); } } static int usb2_fifo_dummy_open(struct usb_fifo *fifo, int fflags) { return (0); } static void usb2_fifo_dummy_close(struct usb_fifo *fifo, int fflags) { return; } static int usb2_fifo_dummy_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) { return (ENOIOCTL); } static void usb2_fifo_dummy_cmd(struct usb_fifo *fifo) { fifo->flag_flushing = 0; /* not flushing */ } static void usb2_fifo_check_methods(struct usb_fifo_methods *pm) { /* check that all callback functions are OK */ if (pm->f_open == NULL) pm->f_open = &usb2_fifo_dummy_open; if (pm->f_close == NULL) pm->f_close = &usb2_fifo_dummy_close; if (pm->f_ioctl == NULL) pm->f_ioctl = &usb2_fifo_dummy_ioctl; if (pm->f_ioctl_post == NULL) pm->f_ioctl_post = &usb2_fifo_dummy_ioctl; if (pm->f_start_read == NULL) pm->f_start_read = &usb2_fifo_dummy_cmd; if (pm->f_stop_read == NULL) pm->f_stop_read = &usb2_fifo_dummy_cmd; if (pm->f_start_write == NULL) pm->f_start_write = &usb2_fifo_dummy_cmd; if (pm->f_stop_write == NULL) pm->f_stop_write = &usb2_fifo_dummy_cmd; } /*------------------------------------------------------------------------* * usb2_fifo_attach * * The following function will create a duplex FIFO. * * Return values: * 0: Success. * Else: Failure. *------------------------------------------------------------------------*/ int usb2_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode) { struct usb_fifo *f_tx; struct usb_fifo *f_rx; char devname[32]; uint8_t n; struct usb_fs_privdata* pd; f_sc->fp[USB_FIFO_TX] = NULL; f_sc->fp[USB_FIFO_RX] = NULL; if (pm == NULL) return (EINVAL); /* check the methods */ usb2_fifo_check_methods(pm); if (priv_mtx == NULL) priv_mtx = &Giant; /* search for a free FIFO slot */ for (n = 0;; n += 2) { if (n == USB_FIFO_MAX) { /* end of FIFOs reached */ return (ENOMEM); } /* Check for TX FIFO */ if (udev->fifo[n + USB_FIFO_TX] != NULL) { continue; } /* Check for RX FIFO */ if (udev->fifo[n + USB_FIFO_RX] != NULL) { continue; } break; } f_tx = usb2_fifo_alloc(); f_rx = usb2_fifo_alloc(); if ((f_tx == NULL) || (f_rx == NULL)) { usb2_fifo_free(f_tx); usb2_fifo_free(f_rx); return (ENOMEM); } /* initialise FIFO structures */ f_tx->fifo_index = n + USB_FIFO_TX; f_tx->dev_ep_index = -1; f_tx->priv_mtx = priv_mtx; f_tx->priv_sc0 = priv_sc; f_tx->methods = pm; f_tx->iface_index = iface_index; f_tx->udev = udev; f_rx->fifo_index = n + USB_FIFO_RX; f_rx->dev_ep_index = -1; f_rx->priv_mtx = priv_mtx; f_rx->priv_sc0 = priv_sc; f_rx->methods = pm; f_rx->iface_index = iface_index; f_rx->udev = udev; f_sc->fp[USB_FIFO_TX] = f_tx; f_sc->fp[USB_FIFO_RX] = f_rx; mtx_lock(&usb2_ref_lock); udev->fifo[f_tx->fifo_index] = f_tx; udev->fifo[f_rx->fifo_index] = f_rx; mtx_unlock(&usb2_ref_lock); for (n = 0; n != 4; n++) { if (pm->basename[n] == NULL) { continue; } if (subunit == 0xFFFF) { if (snprintf(devname, sizeof(devname), "%s%u%s", pm->basename[n], unit, pm->postfix[n] ? pm->postfix[n] : "")) { /* ignore */ } } else { if (snprintf(devname, sizeof(devname), "%s%u.%u%s", pm->basename[n], unit, subunit, pm->postfix[n] ? pm->postfix[n] : "")) { /* ignore */ } } /* * Distribute the symbolic links into two FIFO structures: */ if (n & 1) { f_rx->symlink[n / 2] = usb2_alloc_symlink(devname); } else { f_tx->symlink[n / 2] = usb2_alloc_symlink(devname); } /* * Initialize device private data - this is used to find the * actual USB device itself. */ pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); pd->bus_index = device_get_unit(udev->bus->bdev); pd->dev_index = udev->device_index; pd->ep_addr = -1; /* not an endpoint */ pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index; pd->mode = FREAD|FWRITE; /* Now, create the device itself */ f_sc->dev = make_dev(&usb2_devsw, 0, uid, gid, mode, devname); /* XXX setting si_drv1 and creating the device is not atomic! */ f_sc->dev->si_drv1 = pd; } DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); return (0); } /*------------------------------------------------------------------------* * usb2_fifo_alloc_buffer * * Return values: * 0: Success * Else failure *------------------------------------------------------------------------*/ int -usb2_fifo_alloc_buffer(struct usb_fifo *f, size_t bufsize, +usb2_fifo_alloc_buffer(struct usb_fifo *f, usb_size_t bufsize, uint16_t nbuf) { usb2_fifo_free_buffer(f); /* allocate an endpoint */ f->free_q.ifq_maxlen = nbuf; f->used_q.ifq_maxlen = nbuf; f->queue_data = usb2_alloc_mbufs( M_USBDEV, &f->free_q, bufsize, nbuf); if ((f->queue_data == NULL) && bufsize && nbuf) { return (ENOMEM); } return (0); /* success */ } /*------------------------------------------------------------------------* * usb2_fifo_free_buffer * * This function will free the buffers associated with a FIFO. This * function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usb2_fifo_free_buffer(struct usb_fifo *f) { if (f->queue_data) { /* free old buffer */ free(f->queue_data, M_USBDEV); f->queue_data = NULL; } /* reset queues */ bzero(&f->free_q, sizeof(f->free_q)); bzero(&f->used_q, sizeof(f->used_q)); } static void usb2_fifo_cleanup(void* ptr) { free(ptr, M_USBDEV); } void usb2_fifo_detach(struct usb_fifo_sc *f_sc) { if (f_sc == NULL) { return; } usb2_fifo_free(f_sc->fp[USB_FIFO_TX]); usb2_fifo_free(f_sc->fp[USB_FIFO_RX]); f_sc->fp[USB_FIFO_TX] = NULL; f_sc->fp[USB_FIFO_RX] = NULL; if (f_sc->dev != NULL) { destroy_dev_sched_cb(f_sc->dev, usb2_fifo_cleanup, f_sc->dev->si_drv1); f_sc->dev = NULL; } DPRINTFN(2, "detached %p\n", f_sc); } -size_t +usb_size_t usb2_fifo_put_bytes_max(struct usb_fifo *f) { struct usb_mbuf *m; - size_t len; + usb_size_t len; USB_IF_POLL(&f->free_q, m); if (m) { len = m->max_data_len; } else { len = 0; } return (len); } /*------------------------------------------------------------------------* * usb2_fifo_put_data * * what: * 0 - normal operation * 1 - set last packet flag to enforce framing *------------------------------------------------------------------------*/ void usb2_fifo_put_data(struct usb_fifo *f, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what) { struct usb_mbuf *m; usb_frlength_t io_len; while (len || (what == 1)) { USB_IF_DEQUEUE(&f->free_q, m); if (m) { USB_MBUF_RESET(m); io_len = MIN(len, m->cur_data_len); usb2_copy_out(pc, offset, m->cur_data_ptr, io_len); m->cur_data_len = io_len; offset += io_len; len -= io_len; if ((len == 0) && (what == 1)) { m->last_packet = 1; } USB_IF_ENQUEUE(&f->used_q, m); usb2_fifo_wakeup(f); if ((len == 0) || (what == 1)) { break; } } else { break; } } } void usb2_fifo_put_data_linear(struct usb_fifo *f, void *ptr, - size_t len, uint8_t what) + usb_size_t len, uint8_t what) { struct usb_mbuf *m; - size_t io_len; + usb_size_t io_len; while (len || (what == 1)) { USB_IF_DEQUEUE(&f->free_q, m); if (m) { USB_MBUF_RESET(m); io_len = MIN(len, m->cur_data_len); bcopy(ptr, m->cur_data_ptr, io_len); m->cur_data_len = io_len; ptr = USB_ADD_BYTES(ptr, io_len); len -= io_len; if ((len == 0) && (what == 1)) { m->last_packet = 1; } USB_IF_ENQUEUE(&f->used_q, m); usb2_fifo_wakeup(f); if ((len == 0) || (what == 1)) { break; } } else { break; } } } uint8_t -usb2_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, size_t len) +usb2_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->free_q, m); if (m) { m->cur_data_len = len; m->cur_data_ptr = ptr; USB_IF_ENQUEUE(&f->used_q, m); usb2_fifo_wakeup(f); return (1); } return (0); } void usb2_fifo_put_data_error(struct usb_fifo *f) { f->flag_iserror = 1; usb2_fifo_wakeup(f); } /*------------------------------------------------------------------------* * usb2_fifo_get_data * * what: * 0 - normal operation * 1 - only get one "usb_mbuf" * * returns: * 0 - no more data * 1 - data in buffer *------------------------------------------------------------------------*/ uint8_t usb2_fifo_get_data(struct usb_fifo *f, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what) { struct usb_mbuf *m; usb_frlength_t io_len; uint8_t tr_data = 0; actlen[0] = 0; while (1) { USB_IF_DEQUEUE(&f->used_q, m); if (m) { tr_data = 1; io_len = MIN(len, m->cur_data_len); usb2_copy_in(pc, offset, m->cur_data_ptr, io_len); len -= io_len; offset += io_len; actlen[0] += io_len; m->cur_data_ptr += io_len; m->cur_data_len -= io_len; if ((m->cur_data_len == 0) || (what == 1)) { USB_IF_ENQUEUE(&f->free_q, m); usb2_fifo_wakeup(f); if (what == 1) { break; } } else { USB_IF_PREPEND(&f->used_q, m); } } else { if (tr_data) { /* wait for data to be written out */ break; } if (f->flag_flushing) { /* check if we should send a short packet */ if (f->flag_short != 0) { f->flag_short = 0; tr_data = 1; break; } /* flushing complete */ f->flag_flushing = 0; usb2_fifo_wakeup(f); } break; } if (len == 0) { break; } } return (tr_data); } uint8_t usb2_fifo_get_data_linear(struct usb_fifo *f, void *ptr, - size_t len, size_t *actlen, uint8_t what) + usb_size_t len, usb_size_t *actlen, uint8_t what) { struct usb_mbuf *m; - size_t io_len; + usb_size_t io_len; uint8_t tr_data = 0; actlen[0] = 0; while (1) { USB_IF_DEQUEUE(&f->used_q, m); if (m) { tr_data = 1; io_len = MIN(len, m->cur_data_len); bcopy(m->cur_data_ptr, ptr, io_len); len -= io_len; ptr = USB_ADD_BYTES(ptr, io_len); actlen[0] += io_len; m->cur_data_ptr += io_len; m->cur_data_len -= io_len; if ((m->cur_data_len == 0) || (what == 1)) { USB_IF_ENQUEUE(&f->free_q, m); usb2_fifo_wakeup(f); if (what == 1) { break; } } else { USB_IF_PREPEND(&f->used_q, m); } } else { if (tr_data) { /* wait for data to be written out */ break; } if (f->flag_flushing) { /* check if we should send a short packet */ if (f->flag_short != 0) { f->flag_short = 0; tr_data = 1; break; } /* flushing complete */ f->flag_flushing = 0; usb2_fifo_wakeup(f); } break; } if (len == 0) { break; } } return (tr_data); } uint8_t -usb2_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, size_t *plen) +usb2_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen) { struct usb_mbuf *m; USB_IF_POLL(&f->used_q, m); if (m) { *plen = m->cur_data_len; *pptr = m->cur_data_ptr; return (1); } return (0); } void usb2_fifo_get_data_error(struct usb_fifo *f) { f->flag_iserror = 1; usb2_fifo_wakeup(f); } /*------------------------------------------------------------------------* * usb2_alloc_symlink * * Return values: * NULL: Failure * Else: Pointer to symlink entry *------------------------------------------------------------------------*/ struct usb_symlink * usb2_alloc_symlink(const char *target) { struct usb_symlink *ps; ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK); if (ps == NULL) { return (ps); } /* XXX no longer needed */ strlcpy(ps->src_path, target, sizeof(ps->src_path)); ps->src_len = strlen(ps->src_path); strlcpy(ps->dst_path, target, sizeof(ps->dst_path)); ps->dst_len = strlen(ps->dst_path); sx_xlock(&usb2_sym_lock); TAILQ_INSERT_TAIL(&usb2_sym_head, ps, sym_entry); sx_unlock(&usb2_sym_lock); return (ps); } /*------------------------------------------------------------------------* * usb2_free_symlink *------------------------------------------------------------------------*/ void usb2_free_symlink(struct usb_symlink *ps) { if (ps == NULL) { return; } sx_xlock(&usb2_sym_lock); TAILQ_REMOVE(&usb2_sym_head, ps, sym_entry); sx_unlock(&usb2_sym_lock); free(ps, M_USBDEV); } /*------------------------------------------------------------------------* * usb2_read_symlink * * Return value: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ int usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len) { struct usb_symlink *ps; uint32_t temp; uint32_t delta = 0; uint8_t len; int error = 0; sx_xlock(&usb2_sym_lock); TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) { /* * Compute total length of source and destination symlink * strings pluss one length byte and two NUL bytes: */ temp = ps->src_len + ps->dst_len + 3; if (temp > 255) { /* * Skip entry because this length cannot fit * into one byte: */ continue; } if (startentry != 0) { /* decrement read offset */ startentry--; continue; } if (temp > user_len) { /* out of buffer space */ break; } len = temp; /* copy out total length */ error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); if (error) { break; } delta += 1; /* copy out source string */ error = copyout(ps->src_path, USB_ADD_BYTES(user_ptr, delta), ps->src_len); if (error) { break; } len = 0; delta += ps->src_len; error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); if (error) { break; } delta += 1; /* copy out destination string */ error = copyout(ps->dst_path, USB_ADD_BYTES(user_ptr, delta), ps->dst_len); if (error) { break; } len = 0; delta += ps->dst_len; error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); if (error) { break; } delta += 1; user_len -= temp; } /* a zero length entry indicates the end */ if ((user_len != 0) && (error == 0)) { len = 0; error = copyout(&len, USB_ADD_BYTES(user_ptr, delta), 1); } sx_unlock(&usb2_sym_lock); return (error); } void usb2_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff) { if (f == NULL) return; /* send a Zero Length Packet, ZLP, before close */ f->flag_short = onoff; } #endif /* USB_HAVE_UGEN */ Index: head/sys/dev/usb/usb_dev.h =================================================================== --- head/sys/dev/usb/usb_dev.h (revision 193073) +++ head/sys/dev/usb/usb_dev.h (revision 193074) @@ -1,201 +1,201 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_DEV_H_ #define _USB2_DEV_H_ #include #include #include #include #include #include #include #define USB_FIFO_TX 0 #define USB_FIFO_RX 1 struct usb_fifo; struct usb_mbuf; typedef int (usb_fifo_open_t)(struct usb_fifo *fifo, int fflags); typedef void (usb_fifo_close_t)(struct usb_fifo *fifo, int fflags); typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags); typedef void (usb_fifo_cmd_t)(struct usb_fifo *fifo); typedef void (usb_fifo_filter_t)(struct usb_fifo *fifo, struct usb_mbuf *m); struct usb_symlink { TAILQ_ENTRY(usb_symlink) sym_entry; char src_path[32]; /* Source path - including terminating * zero */ char dst_path[32]; /* Destination path - including * terminating zero */ uint8_t src_len; /* String length */ uint8_t dst_len; /* String length */ }; /* * Locking note for the following functions. All the * "usb_fifo_cmd_t" and "usb_fifo_filter_t" functions are called * locked. The others are called unlocked. */ struct usb_fifo_methods { usb_fifo_open_t *f_open; usb_fifo_close_t *f_close; usb_fifo_ioctl_t *f_ioctl; /* * NOTE: The post-ioctl callback is called after the USB reference * gets locked in the IOCTL handler: */ usb_fifo_ioctl_t *f_ioctl_post; usb_fifo_cmd_t *f_start_read; usb_fifo_cmd_t *f_stop_read; usb_fifo_cmd_t *f_start_write; usb_fifo_cmd_t *f_stop_write; usb_fifo_filter_t *f_filter_read; usb_fifo_filter_t *f_filter_write; const char *basename[4]; const char *postfix[4]; }; /* * Private per-device information. */ struct usb_cdev_privdata { struct usb_bus *bus; struct usb_device *udev; struct usb_interface *iface; struct usb_fifo *rxfifo; struct usb_fifo *txfifo; int bus_index; /* bus index */ int dev_index; /* device index */ int ep_addr; /* endpoint address */ int fflags; uint8_t fifo_index; /* FIFO index */ uint8_t is_read; /* location has read access */ uint8_t is_write; /* location has write access */ uint8_t is_uref; /* USB refcount decr. needed */ uint8_t is_usbfs; /* USB-FS is active */ }; struct usb_fs_privdata { int bus_index; int dev_index; int ep_addr; int mode; int fifo_index; struct cdev *cdev; LIST_ENTRY(usb_fs_privdata) pd_next; }; /* * Most of the fields in the "usb_fifo" structure are used by the * generic USB access layer. */ struct usb_fifo { struct usb_ifqueue free_q; struct usb_ifqueue used_q; struct selinfo selinfo; struct cv cv_io; struct cv cv_drain; struct usb_fifo_methods *methods; struct usb_symlink *symlink[2];/* our symlinks */ struct proc *async_p; /* process that wants SIGIO */ struct usb_fs_endpoint *fs_ep_ptr; struct usb_device *udev; struct usb_xfer *xfer[2]; struct usb_xfer **fs_xfer; struct mtx *priv_mtx; /* client data */ /* set if FIFO is opened by a FILE: */ struct usb_cdev_privdata *curr_cpd; void *priv_sc0; /* client data */ void *priv_sc1; /* client data */ void *queue_data; usb_timeout_t timeout; /* timeout in milliseconds */ usb_frlength_t bufsize; /* BULK and INTERRUPT buffer size */ usb_frcount_t nframes; /* for isochronous mode */ uint16_t dev_ep_index; /* our device endpoint index */ uint8_t flag_sleeping; /* set if FIFO is sleeping */ uint8_t flag_iscomplete; /* set if a USB transfer is complete */ uint8_t flag_iserror; /* set if FIFO error happened */ uint8_t flag_isselect; /* set if FIFO is selected */ uint8_t flag_flushing; /* set if FIFO is flushing data */ uint8_t flag_short; /* set if short_ok or force_short * transfer flags should be set */ uint8_t flag_stall; /* set if clear stall should be run */ uint8_t iface_index; /* set to the interface we belong to */ uint8_t fifo_index; /* set to the FIFO index in "struct * usb_device" */ uint8_t fs_ep_max; uint8_t fifo_zlp; /* zero length packet count */ uint8_t refcount; #define USB_FIFO_REF_MAX 0xFF }; struct usb_fifo_sc { struct usb_fifo *fp[2]; struct cdev* dev; }; extern struct cdevsw usb2_devsw; int usb2_fifo_wait(struct usb_fifo *fifo); void usb2_fifo_signal(struct usb_fifo *fifo); int usb2_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize, uint16_t nbuf); void usb2_fifo_free_buffer(struct usb_fifo *f); int usb2_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode); void usb2_fifo_detach(struct usb_fifo_sc *f_sc); uint32_t usb2_fifo_put_bytes_max(struct usb_fifo *fifo); void usb2_fifo_put_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what); void usb2_fifo_put_data_linear(struct usb_fifo *fifo, void *ptr, - size_t len, uint8_t what); -uint8_t usb2_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, size_t len); + usb_size_t len, uint8_t what); +uint8_t usb2_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len); void usb2_fifo_put_data_error(struct usb_fifo *fifo); uint8_t usb2_fifo_get_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what); uint8_t usb2_fifo_get_data_linear(struct usb_fifo *fifo, void *ptr, - size_t len, size_t *actlen, uint8_t what); + usb_size_t len, usb_size_t *actlen, uint8_t what); uint8_t usb2_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, - size_t *plen); + usb_size_t *plen); void usb2_fifo_get_data_error(struct usb_fifo *fifo); uint8_t usb2_fifo_opened(struct usb_fifo *fifo); void usb2_fifo_free(struct usb_fifo *f); void usb2_fifo_reset(struct usb_fifo *f); void usb2_fifo_wakeup(struct usb_fifo *f); struct usb_symlink *usb2_alloc_symlink(const char *target); void usb2_free_symlink(struct usb_symlink *ps); int usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len); void usb2_fifo_set_close_zlp(struct usb_fifo *, uint8_t); #endif /* _USB2_DEV_H_ */ Index: head/sys/dev/usb/usb_device.h =================================================================== --- head/sys/dev/usb/usb_device.h (revision 193073) +++ head/sys/dev/usb/usb_device.h (revision 193074) @@ -1,259 +1,259 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_DEVICE_H_ #define _USB2_DEVICE_H_ struct usb_symlink; /* UGEN */ struct usb_device; /* linux compat */ #define USB_DEFAULT_XFER_MAX 2 /* "usb2_parse_config()" commands */ #define USB_CFG_ALLOC 0 #define USB_CFG_FREE 1 #define USB_CFG_INIT 2 /* "usb2_unconfigure()" flags */ #define USB_UNCFG_FLAG_NONE 0x00 #define USB_UNCFG_FLAG_FREE_SUBDEV 0x01 /* subdevices are freed */ #define USB_UNCFG_FLAG_FREE_EP0 0x02 /* endpoint zero is freed */ struct usb_clear_stall_msg { struct usb_proc_msg hdr; struct usb_device *udev; }; /* The following four structures makes up a tree, where we have the * leaf structure, "usb_host_endpoint", first, and the root structure, * "usb_device", last. The four structures below mirror the structure * of the USB descriptors belonging to an USB configuration. Please * refer to the USB specification for a definition of "endpoints" and * "interfaces". */ struct usb_host_endpoint { struct usb_endpoint_descriptor desc; TAILQ_HEAD(, urb) bsd_urb_list; struct usb_xfer *bsd_xfer[2]; uint8_t *extra; /* Extra descriptors */ usb_frlength_t fbsd_buf_size; uint16_t extralen; uint8_t bsd_iface_index; } __aligned(USB_HOST_ALIGN); struct usb_host_interface { struct usb_interface_descriptor desc; /* the following array has size "desc.bNumEndpoint" */ struct usb_host_endpoint *endpoint; const char *string; /* iInterface string, if present */ uint8_t *extra; /* Extra descriptors */ uint16_t extralen; uint8_t bsd_iface_index; } __aligned(USB_HOST_ALIGN); /* * The following structure defines an USB pipe which is equal to an * USB endpoint. */ struct usb_pipe { struct usb_xfer_queue pipe_q; /* queue of USB transfers */ struct usb_endpoint_descriptor *edesc; struct usb_pipe_methods *methods; /* set by HC driver */ uint16_t isoc_next; uint16_t refcount; uint8_t toggle_next:1; /* next data toggle value */ uint8_t is_stalled:1; /* set if pipe is stalled */ uint8_t is_synced:1; /* set if we a synchronised */ uint8_t unused:5; uint8_t iface_index; /* not used by "default pipe" */ }; /* * The following structure defines an USB interface. */ struct usb_interface { struct usb_interface_descriptor *idesc; device_t subdev; uint8_t alt_index; uint8_t parent_iface_index; /* Linux compat */ struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; struct usb_device *linux_udev; void *bsd_priv_sc; /* device specific information */ uint8_t num_altsetting; /* number of alternate settings */ uint8_t bsd_iface_index; }; /* * The following structure defines the USB device flags. */ struct usb_device_flags { enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t self_powered:1; /* set if USB device is self powered */ uint8_t no_strings:1; /* set if USB device does not support * strings */ uint8_t remote_wakeup:1; /* set if remote wakeup is enabled */ uint8_t uq_bus_powered:1; /* set if BUS powered quirk is present */ /* * NOTE: Although the flags below will reach the same value * over time, but the instant values may differ, and * consequently the flags cannot be merged into one! */ uint8_t peer_suspended:1; /* set if peer is suspended */ uint8_t self_suspended:1; /* set if self is suspended */ }; /* * The following structure is used for power-save purposes. The data * in this structure is protected by the USB BUS lock. */ struct usb_power_save { usb_ticks_t last_xfer_time; /* copy of "ticks" */ - size_t type_refs[4]; /* transfer reference count */ - size_t read_refs; /* data read references */ - size_t write_refs; /* data write references */ + usb_size_t type_refs[4]; /* transfer reference count */ + usb_size_t read_refs; /* data read references */ + usb_size_t write_refs; /* data write references */ }; /* * The following structure defines an USB device. There exists one of * these structures for every USB device. */ struct usb_device { struct usb_clear_stall_msg cs_msg[2]; /* generic clear stall * messages */ struct sx default_sx[2]; struct mtx default_mtx[1]; struct cv default_cv[2]; struct usb_interface *ifaces; struct usb_pipe default_pipe; /* Control Endpoint 0 */ struct usb_pipe *pipes; struct usb_power_save pwr_save;/* power save data */ struct usb_bus *bus; /* our USB BUS */ device_t parent_dev; /* parent device */ struct usb_device *parent_hub; struct usb_device *parent_hs_hub; /* high-speed parent HUB */ struct usb_config_descriptor *cdesc; /* full config descr */ struct usb_hub *hub; /* only if this is a hub */ struct usb_xfer *default_xfer[USB_DEFAULT_XFER_MAX]; struct usb_temp_data *usb2_template_ptr; struct usb_pipe *pipe_curr; /* current clear stall pipe */ #if USB_HAVE_UGEN struct usb_fifo *fifo[USB_FIFO_MAX]; struct usb_symlink *ugen_symlink; /* our generic symlink */ struct cdev *default_dev; /* Control Endpoint 0 device node */ LIST_HEAD(,usb_fs_privdata) pd_list; char ugen_name[20]; /* name of ugenX.X device */ #endif usb_ticks_t plugtime; /* copy of "ticks" */ enum usb_dev_state state; enum usb_dev_speed speed; uint16_t refcount; #define USB_DEV_REF_MAX 0xffff uint16_t power; /* mA the device uses */ uint16_t langid; /* language for strings */ uint8_t address; /* device addess */ uint8_t device_index; /* device index in "bus->devices" */ uint8_t curr_config_index; /* current configuration index */ uint8_t curr_config_no; /* current configuration number */ uint8_t depth; /* distance from root HUB */ uint8_t port_index; /* parent HUB port index */ uint8_t port_no; /* parent HUB port number */ uint8_t hs_hub_addr; /* high-speed HUB address */ uint8_t hs_port_no; /* high-speed HUB port number */ uint8_t driver_added_refcount; /* our driver added generation count */ uint8_t power_mode; /* see USB_POWER_XXX */ uint8_t ifaces_max; /* number of interfaces present */ uint8_t pipes_max; /* number of pipes present */ /* the "flags" field is write-protected by "bus->mtx" */ struct usb_device_flags flags; struct usb_endpoint_descriptor default_ep_desc; /* for pipe 0 */ struct usb_device_descriptor ddesc; /* device descriptor */ char *serial; /* serial number */ char *manufacturer; /* manufacturer string */ char *product; /* product string */ #if USB_HAVE_COMPAT_LINUX /* Linux compat */ struct usb_device_descriptor descriptor; struct usb_host_endpoint ep0; struct usb_interface *linux_iface_start; struct usb_interface *linux_iface_end; struct usb_host_endpoint *linux_endpoint_start; struct usb_host_endpoint *linux_endpoint_end; uint16_t devnum; #endif }; /* globals */ extern int usb2_template; /* function prototypes */ struct usb_device *usb2_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode); struct usb_pipe *usb2_get_pipe(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup); struct usb_pipe *usb2_get_pipe_by_addr(struct usb_device *udev, uint8_t ea_val); usb_error_t usb2_interface_count(struct usb_device *udev, uint8_t *count); usb_error_t usb2_probe_and_attach(struct usb_device *udev, uint8_t iface_index); usb_error_t usb2_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index); usb_error_t usb2_set_config_index(struct usb_device *udev, uint8_t index); usb_error_t usb2_set_endpoint_stall(struct usb_device *udev, struct usb_pipe *pipe, uint8_t do_stall); usb_error_t usb2_suspend_resume(struct usb_device *udev, uint8_t do_suspend); void usb2_devinfo(struct usb_device *udev, char *dst_ptr, uint16_t dst_len); void usb2_free_device(struct usb_device *, uint8_t); void *usb2_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); void usb_linux_free_device(struct usb_device *dev); uint8_t usb2_peer_can_wakeup(struct usb_device *udev); struct usb_pipe *usb2_pipe_foreach(struct usb_device *udev, struct usb_pipe *pipe); void usb2_set_device_state(struct usb_device *udev, enum usb_dev_state state); #endif /* _USB2_DEVICE_H_ */ Index: head/sys/dev/usb/usb_hid.c =================================================================== --- head/sys/dev/usb/usb_hid.c (revision 193073) +++ head/sys/dev/usb/usb_hid.c (revision 193074) @@ -1,726 +1,726 @@ /* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT 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 #include #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include #include #include #include #include static void hid_clear_local(struct hid_item *); static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize); #define MAXUSAGE 64 #define MAXPUSH 4 struct hid_data { const uint8_t *start; const uint8_t *end; const uint8_t *p; struct hid_item cur[MAXPUSH]; int32_t usages_min[MAXUSAGE]; int32_t usages_max[MAXUSAGE]; int32_t usage_last; /* last seen usage */ uint32_t loc_size; /* last seen size */ uint32_t loc_count; /* last seen count */ uint8_t kindset; /* we have 5 kinds so 8 bits are enough */ uint8_t pushlevel; /* current pushlevel */ uint8_t ncount; /* end usage item count */ uint8_t icount; /* current usage item count */ uint8_t nusage; /* end "usages_min/max" index */ uint8_t iusage; /* current "usages_min/max" index */ uint8_t ousage; /* current "usages_min/max" offset */ uint8_t susage; /* usage set flags */ }; /*------------------------------------------------------------------------* * hid_clear_local *------------------------------------------------------------------------*/ static void hid_clear_local(struct hid_item *c) { c->loc.count = 0; c->loc.size = 0; c->usage = 0; c->usage_minimum = 0; c->usage_maximum = 0; c->designator_index = 0; c->designator_minimum = 0; c->designator_maximum = 0; c->string_index = 0; c->string_minimum = 0; c->string_maximum = 0; c->set_delimiter = 0; } /*------------------------------------------------------------------------* * hid_start_parse *------------------------------------------------------------------------*/ struct hid_data * -hid_start_parse(const void *d, size_t len, int kindset) +hid_start_parse(const void *d, usb_size_t len, int kindset) { struct hid_data *s; if ((kindset-1) & kindset) { DPRINTFN(0, "Only one bit can be " "set in the kindset\n"); return (NULL); } s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO); s->start = s->p = d; s->end = ((const uint8_t *)d) + len; s->kindset = kindset; return (s); } /*------------------------------------------------------------------------* * hid_end_parse *------------------------------------------------------------------------*/ void hid_end_parse(struct hid_data *s) { if (s == NULL) return; free(s, M_TEMP); } /*------------------------------------------------------------------------* * get byte from HID descriptor *------------------------------------------------------------------------*/ static uint8_t hid_get_byte(struct hid_data *s, const uint16_t wSize) { const uint8_t *ptr; uint8_t retval; ptr = s->p; /* check if end is reached */ if (ptr == s->end) return (0); /* read out a byte */ retval = *ptr; /* check if data pointer can be advanced by "wSize" bytes */ if ((s->end - ptr) < wSize) ptr = s->end; else ptr += wSize; /* update pointer */ s->p = ptr; return (retval); } /*------------------------------------------------------------------------* * hid_get_item *------------------------------------------------------------------------*/ int hid_get_item(struct hid_data *s, struct hid_item *h) { struct hid_item *c; unsigned int bTag, bType, bSize; uint32_t oldpos; int32_t mask; int32_t dval; if (s == NULL) return (0); c = &s->cur[s->pushlevel]; top: /* check if there is an array of items */ if (s->icount < s->ncount) { /* get current usage */ if (s->iusage < s->nusage) { dval = s->usages_min[s->iusage] + s->ousage; c->usage = dval; s->usage_last = dval; if (dval == s->usages_max[s->iusage]) { s->iusage ++; s->ousage = 0; } else { s->ousage ++; } } else { DPRINTFN(1, "Using last usage\n"); dval = s->usage_last; } s->icount ++; /* * Only copy HID item, increment position and return * if correct kindset! */ if (s->kindset & (1 << c->kind)) { *h = *c; DPRINTFN(1, "%u,%u,%u\n", h->loc.pos, h->loc.size, h->loc.count); c->loc.pos += c->loc.size * c->loc.count; return (1); } } /* reset state variables */ s->icount = 0; s->ncount = 0; s->iusage = 0; s->nusage = 0; s->susage = 0; s->ousage = 0; hid_clear_local(c); /* get next item */ while (s->p != s->end) { bSize = hid_get_byte(s, 1); if (bSize == 0xfe) { /* long item */ bSize = hid_get_byte(s, 1); bSize |= hid_get_byte(s, 1) << 8; bTag = hid_get_byte(s, 1); bType = 0xff; /* XXX what should it be */ } else { /* short item */ bTag = bSize >> 4; bType = (bSize >> 2) & 3; bSize &= 3; if (bSize == 3) bSize = 4; } switch (bSize) { case 0: dval = 0; mask = 0; break; case 1: dval = (int8_t)hid_get_byte(s, 1); mask = 0xFF; break; case 2: dval = hid_get_byte(s, 1); dval |= hid_get_byte(s, 1) << 8; dval = (int16_t)dval; mask = 0xFFFF; break; case 4: dval = hid_get_byte(s, 1); dval |= hid_get_byte(s, 1) << 8; dval |= hid_get_byte(s, 1) << 16; dval |= hid_get_byte(s, 1) << 24; mask = 0xFFFFFFFF; break; default: dval = hid_get_byte(s, bSize); DPRINTFN(0, "bad length %u (data=0x%02x)\n", bSize, dval); continue; } switch (bType) { case 0: /* Main */ switch (bTag) { case 8: /* Input */ c->kind = hid_input; c->flags = dval; ret: c->loc.count = s->loc_count; c->loc.size = s->loc_size; if (c->flags & HIO_VARIABLE) { /* range check usage count */ if (c->loc.count > 255) { DPRINTFN(0, "Number of " "items truncated to 255\n"); s->ncount = 255; } else s->ncount = c->loc.count; /* * The "top" loop will return * one and one item: */ c->loc.count = 1; } else { s->ncount = 1; } goto top; case 9: /* Output */ c->kind = hid_output; c->flags = dval; goto ret; case 10: /* Collection */ c->kind = hid_collection; c->collection = dval; c->collevel++; c->usage = s->usage_last; *h = *c; return (1); case 11: /* Feature */ c->kind = hid_feature; c->flags = dval; goto ret; case 12: /* End collection */ c->kind = hid_endcollection; if (c->collevel == 0) { DPRINTFN(0, "invalid end collection\n"); return (0); } c->collevel--; *h = *c; return (1); default: DPRINTFN(0, "Main bTag=%d\n", bTag); break; } break; case 1: /* Global */ switch (bTag) { case 0: c->_usage_page = dval << 16; break; case 1: c->logical_minimum = dval; break; case 2: c->logical_maximum = dval; break; case 3: c->physical_minimum = dval; break; case 4: c->physical_maximum = dval; break; case 5: c->unit_exponent = dval; break; case 6: c->unit = dval; break; case 7: /* mask because value is unsigned */ s->loc_size = dval & mask; break; case 8: c->report_ID = dval; /* new report - reset position */ c->loc.pos = 0; break; case 9: /* mask because value is unsigned */ s->loc_count = dval & mask; break; case 10: /* Push */ s->pushlevel ++; if (s->pushlevel < MAXPUSH) { s->cur[s->pushlevel] = *c; /* store size and count */ c->loc.size = s->loc_size; c->loc.count = s->loc_count; /* update current item pointer */ c = &s->cur[s->pushlevel]; } else { DPRINTFN(0, "Cannot push " "item @ %d!\n", s->pushlevel); } break; case 11: /* Pop */ s->pushlevel --; if (s->pushlevel < MAXPUSH) { /* preserve position */ oldpos = c->loc.pos; c = &s->cur[s->pushlevel]; /* restore size and count */ s->loc_size = c->loc.size; s->loc_count = c->loc.count; /* set default item location */ c->loc.pos = oldpos; c->loc.size = 0; c->loc.count = 0; } else { DPRINTFN(0, "Cannot pop " "item @ %d!\n", s->pushlevel); } break; default: DPRINTFN(0, "Global bTag=%d\n", bTag); break; } break; case 2: /* Local */ switch (bTag) { case 0: if (bSize != 4) dval = (dval & mask) | c->_usage_page; /* set last usage, in case of a collection */ s->usage_last = dval; if (s->nusage < MAXUSAGE) { s->usages_min[s->nusage] = dval; s->usages_max[s->nusage] = dval; s->nusage ++; } else { DPRINTFN(0, "max usage reached!\n"); } /* clear any pending usage sets */ s->susage = 0; break; case 1: s->susage |= 1; if (bSize != 4) dval = (dval & mask) | c->_usage_page; c->usage_minimum = dval; goto check_set; case 2: s->susage |= 2; if (bSize != 4) dval = (dval & mask) | c->_usage_page; c->usage_maximum = dval; check_set: if (s->susage != 3) break; /* sanity check */ if ((s->nusage < MAXUSAGE) && (c->usage_minimum <= c->usage_maximum)) { /* add usage range */ s->usages_min[s->nusage] = c->usage_minimum; s->usages_max[s->nusage] = c->usage_maximum; s->nusage ++; } else { DPRINTFN(0, "Usage set dropped!\n"); } s->susage = 0; break; case 3: c->designator_index = dval; break; case 4: c->designator_minimum = dval; break; case 5: c->designator_maximum = dval; break; case 7: c->string_index = dval; break; case 8: c->string_minimum = dval; break; case 9: c->string_maximum = dval; break; case 10: c->set_delimiter = dval; break; default: DPRINTFN(0, "Local bTag=%d\n", bTag); break; } break; default: DPRINTFN(0, "default bType=%d\n", bType); break; } } return (0); } /*------------------------------------------------------------------------* * hid_report_size *------------------------------------------------------------------------*/ int -hid_report_size(const void *buf, size_t len, enum hid_kind k, uint8_t *id) +hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) { struct hid_data *d; struct hid_item h; uint32_t temp; uint32_t hpos; uint32_t lpos; uint8_t any_id; any_id = 0; hpos = 0; lpos = 0xFFFFFFFF; for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) { if (h.kind == k) { /* check for ID-byte presense */ if ((h.report_ID != 0) && !any_id) { if (id != NULL) *id = h.report_ID; any_id = 1; } /* compute minimum */ if (lpos > h.loc.pos) lpos = h.loc.pos; /* compute end position */ temp = h.loc.pos + (h.loc.size * h.loc.count); /* compute maximum */ if (hpos < temp) hpos = temp; } } hid_end_parse(d); /* safety check - can happen in case of currupt descriptors */ if (lpos > hpos) temp = 0; else temp = hpos - lpos; /* check for ID byte */ if (any_id) temp += 8; else if (id != NULL) *id = 0; /* return length in bytes rounded up */ return ((temp + 7) / 8); } /*------------------------------------------------------------------------* * hid_locate *------------------------------------------------------------------------*/ int -hid_locate(const void *desc, size_t size, uint32_t u, enum hid_kind k, +hid_locate(const void *desc, usb_size_t size, uint32_t u, enum hid_kind k, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) { struct hid_data *d; struct hid_item h; for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) { if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) { if (index--) continue; if (loc != NULL) *loc = h.loc; if (flags != NULL) *flags = h.flags; if (id != NULL) *id = h.report_ID; hid_end_parse(d); return (1); } } if (loc != NULL) loc->size = 0; if (flags != NULL) *flags = 0; if (id != NULL) *id = 0; hid_end_parse(d); return (0); } /*------------------------------------------------------------------------* * hid_get_data *------------------------------------------------------------------------*/ uint32_t -hid_get_data(const uint8_t *buf, size_t len, struct hid_location *loc) +hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc) { uint32_t hpos = loc->pos; uint32_t hsize = loc->size; uint32_t data; uint32_t rpos; uint8_t n; DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize); /* Range check and limit */ if (hsize == 0) return (0); if (hsize > 32) hsize = 32; /* Get data in a safe way */ data = 0; rpos = (hpos / 8); n = (hsize + 7) / 8; rpos += n; while (n--) { rpos--; if (rpos < len) data |= buf[rpos] << (8 * n); } /* Correctly shift down data */ data = (data >> (hpos % 8)); /* Mask and sign extend in one */ n = 32 - hsize; data = ((int32_t)data << n) >> n; DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n", loc->pos, loc->size, (long)data); return (data); } /*------------------------------------------------------------------------* * hid_is_collection *------------------------------------------------------------------------*/ int -hid_is_collection(const void *desc, size_t size, uint32_t usage) +hid_is_collection(const void *desc, usb_size_t size, uint32_t usage) { struct hid_data *hd; struct hid_item hi; int err; hd = hid_start_parse(desc, size, hid_input); if (hd == NULL) return (0); while ((err = hid_get_item(hd, &hi))) { if (hi.kind == hid_collection && hi.usage == usage) break; } hid_end_parse(hd); return (err); } /*------------------------------------------------------------------------* * hid_get_descriptor_from_usb * * This function will search for a HID descriptor between two USB * interface descriptors. * * Return values: * NULL: No more HID descriptors. * Else: Pointer to HID descriptor. *------------------------------------------------------------------------*/ struct usb_hid_descriptor * hid_get_descriptor_from_usb(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id) { struct usb_descriptor *desc = (void *)id; if (desc == NULL) { return (NULL); } while ((desc = usb2_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_HID) && (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) { return (void *)desc; } if (desc->bDescriptorType == UDESC_INTERFACE) { break; } } return (NULL); } /*------------------------------------------------------------------------* * usb2_req_get_hid_desc * * This function will read out an USB report descriptor from the USB * device. * * Return values: * NULL: Failure. * Else: Success. The pointer should eventually be passed to free(). *------------------------------------------------------------------------*/ usb_error_t usb2_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, struct malloc_type *mem, uint8_t iface_index) { struct usb_interface *iface = usb2_get_iface(udev, iface_index); struct usb_hid_descriptor *hid; usb_error_t err; if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } hid = hid_get_descriptor_from_usb (usb2_get_config_descriptor(udev), iface->idesc); if (hid == NULL) { return (USB_ERR_IOERROR); } *sizep = UGETW(hid->descrs[0].wDescriptorLength); if (*sizep == 0) { return (USB_ERR_IOERROR); } if (mtx) mtx_unlock(mtx); *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK); if (mtx) mtx_lock(mtx); if (*descp == NULL) { return (USB_ERR_NOMEM); } err = usb2_req_get_report_descriptor (udev, mtx, *descp, *sizep, iface_index); if (err) { free(*descp, mem); *descp = NULL; return (err); } return (USB_ERR_NORMAL_COMPLETION); } Index: head/sys/dev/usb/usb_hid.h =================================================================== --- head/sys/dev/usb/usb_hid.h (revision 193073) +++ head/sys/dev/usb/usb_hid.h (revision 193074) @@ -1,95 +1,95 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_CORE_HID_H_ #define _USB2_CORE_HID_H_ struct usb_hid_descriptor; struct usb_config_descriptor; enum hid_kind { hid_input, hid_output, hid_feature, hid_collection, hid_endcollection }; struct hid_location { uint32_t size; uint32_t count; uint32_t pos; }; struct hid_item { /* Global */ int32_t _usage_page; int32_t logical_minimum; int32_t logical_maximum; int32_t physical_minimum; int32_t physical_maximum; int32_t unit_exponent; int32_t unit; int32_t report_ID; /* Local */ int32_t usage; int32_t usage_minimum; int32_t usage_maximum; int32_t designator_index; int32_t designator_minimum; int32_t designator_maximum; int32_t string_index; int32_t string_minimum; int32_t string_maximum; int32_t set_delimiter; /* Misc */ int32_t collection; int collevel; enum hid_kind kind; uint32_t flags; /* Location */ struct hid_location loc; }; /* prototypes from "usb2_hid.c" */ -struct hid_data *hid_start_parse(const void *d, size_t len, int kindset); +struct hid_data *hid_start_parse(const void *d, usb_size_t len, int kindset); void hid_end_parse(struct hid_data *s); int hid_get_item(struct hid_data *s, struct hid_item *h); -int hid_report_size(const void *buf, size_t len, enum hid_kind k, +int hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id); -int hid_locate(const void *desc, size_t size, uint32_t usage, +int hid_locate(const void *desc, usb_size_t size, uint32_t usage, enum hid_kind kind, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id); -uint32_t hid_get_data(const uint8_t *buf, size_t len, +uint32_t hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc); -int hid_is_collection(const void *desc, size_t size, uint32_t usage); +int hid_is_collection(const void *desc, usb_size_t size, uint32_t usage); struct usb_hid_descriptor *hid_get_descriptor_from_usb( struct usb_config_descriptor *cd, struct usb_interface_descriptor *id); usb_error_t usb2_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, struct malloc_type *mem, uint8_t iface_index); #endif /* _USB2_CORE_HID_H_ */ Index: head/sys/dev/usb/usb_hub.c =================================================================== --- head/sys/dev/usb/usb_hub.c (revision 193073) +++ head/sys/dev/usb/usb_hub.c (revision 193074) @@ -1,1890 +1,1890 @@ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ /* * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include #include #include #include #define USB_DEBUG_VAR uhub_debug #include #include #include #include #include #include #include #include #include #include #include #include #define UHUB_INTR_INTERVAL 250 /* ms */ #define UHUB_N_TRANSFER 1 #if USB_DEBUG static int uhub_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB"); SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0, "Debug level"); #endif #if USB_HAVE_POWERD static int usb2_power_timeout = 30; /* seconds */ SYSCTL_INT(_hw_usb, OID_AUTO, power_timeout, CTLFLAG_RW, &usb2_power_timeout, 0, "USB power timeout"); #endif struct uhub_current_state { uint16_t port_change; uint16_t port_status; }; struct uhub_softc { struct uhub_current_state sc_st;/* current state */ device_t sc_dev; /* base device */ struct usb_device *sc_udev; /* USB device */ struct usb_xfer *sc_xfer[UHUB_N_TRANSFER]; /* interrupt xfer */ uint8_t sc_flags; #define UHUB_FLAG_DID_EXPLORE 0x01 char sc_name[32]; }; #define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol) #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) /* prototypes for type checking: */ static device_probe_t uhub_probe; static device_attach_t uhub_attach; static device_detach_t uhub_detach; static device_suspend_t uhub_suspend; static device_resume_t uhub_resume; static bus_driver_added_t uhub_driver_added; static bus_child_location_str_t uhub_child_location_string; static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string; static usb_callback_t uhub_intr_callback; static void usb2_dev_resume_peer(struct usb_device *udev); static void usb2_dev_suspend_peer(struct usb_device *udev); static const struct usb_config uhub_config[UHUB_N_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_ANY, .timeout = 0, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uhub_intr_callback, .interval = UHUB_INTR_INTERVAL, }, }; /* * driver instance for "hub" connected to "usb" * and "hub" connected to "hub" */ static devclass_t uhub_devclass; static device_method_t uhub_methods[] = { DEVMETHOD(device_probe, uhub_probe), DEVMETHOD(device_attach, uhub_attach), DEVMETHOD(device_detach, uhub_detach), DEVMETHOD(device_suspend, uhub_suspend), DEVMETHOD(device_resume, uhub_resume), DEVMETHOD(bus_child_location_str, uhub_child_location_string), DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string), DEVMETHOD(bus_driver_added, uhub_driver_added), {0, 0} }; static driver_t uhub_driver = { .name = "uhub", .methods = uhub_methods, .size = sizeof(struct uhub_softc) }; DRIVER_MODULE(uhub, usbus, uhub_driver, uhub_devclass, 0, 0); DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, NULL, 0); static void uhub_intr_callback(struct usb_xfer *xfer) { struct uhub_softc *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(2, "\n"); /* * This is an indication that some port * has changed status. Notify the bus * event handler thread that we need * to be explored again: */ usb2_needs_explore(sc->sc_udev->bus, 0); case USB_ST_SETUP: xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* * Do a clear-stall. The "stall_pipe" flag * will get cleared before next callback by * the USB stack. */ xfer->flags.stall_pipe = 1; xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } break; } } /*------------------------------------------------------------------------* * uhub_explore_sub - subroutine * * Return values: * 0: Success * Else: A control transaction failed *------------------------------------------------------------------------*/ static usb_error_t uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) { struct usb_bus *bus; struct usb_device *child; uint8_t refcount; usb_error_t err; bus = sc->sc_udev->bus; err = 0; /* get driver added refcount from USB bus */ refcount = bus->driver_added_refcount; /* get device assosiated with the given port */ child = usb2_bus_port_get_device(bus, up); if (child == NULL) { /* nothing to do */ goto done; } /* check if probe and attach should be done */ if (child->driver_added_refcount != refcount) { child->driver_added_refcount = refcount; err = usb2_probe_and_attach(child, USB_IFACE_INDEX_ANY); if (err) { goto done; } } /* start control transfer, if device mode */ if (child->flags.usb_mode == USB_MODE_DEVICE) { usb2_default_transfer_setup(child); } /* if a HUB becomes present, do a recursive HUB explore */ if (child->hub) { err = (child->hub->explore) (child); } done: return (err); } /*------------------------------------------------------------------------* * uhub_read_port_status - factored out code *------------------------------------------------------------------------*/ static usb_error_t uhub_read_port_status(struct uhub_softc *sc, uint8_t portno) { struct usb_port_status ps; usb_error_t err; err = usb2_req_get_port_status( sc->sc_udev, NULL, &ps, portno); /* update status regardless of error */ sc->sc_st.port_status = UGETW(ps.wPortStatus); sc->sc_st.port_change = UGETW(ps.wPortChange); /* debugging print */ DPRINTFN(4, "port %d, wPortStatus=0x%04x, " "wPortChange=0x%04x, err=%s\n", portno, sc->sc_st.port_status, sc->sc_st.port_change, usb2_errstr(err)); return (err); } /*------------------------------------------------------------------------* * uhub_reattach_port * * Returns: * 0: Success * Else: A control transaction failed *------------------------------------------------------------------------*/ static usb_error_t uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) { struct usb_device *child; struct usb_device *udev; enum usb_dev_speed speed; enum usb_hc_mode mode; usb_error_t err; uint8_t timeout; DPRINTF("reattaching port %d\n", portno); err = 0; timeout = 0; udev = sc->sc_udev; child = usb2_bus_port_get_device(udev->bus, udev->hub->ports + portno - 1); repeat: /* first clear the port connection change bit */ err = usb2_req_clear_port_feature(udev, NULL, portno, UHF_C_PORT_CONNECTION); if (err) { goto error; } /* detach any existing devices */ if (child) { usb2_free_device(child, USB_UNCFG_FLAG_FREE_SUBDEV | USB_UNCFG_FLAG_FREE_EP0); child = NULL; } /* get fresh status */ err = uhub_read_port_status(sc, portno); if (err) { goto error; } /* check if nothing is connected to the port */ if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) { goto error; } /* check if there is no power on the port and print a warning */ if (!(sc->sc_st.port_status & UPS_PORT_POWER)) { DPRINTF("WARNING: strange, connected port %d " "has no power\n", portno); } /* check if the device is in Host Mode */ if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) { DPRINTF("Port %d is in Host Mode\n", portno); if (sc->sc_st.port_status & UPS_SUSPEND) { DPRINTF("Port %d was still " "suspended, clearing.\n", portno); err = usb2_req_clear_port_feature(sc->sc_udev, NULL, portno, UHF_PORT_SUSPEND); } /* USB Host Mode */ /* wait for maximum device power up time */ usb2_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_POWERUP_DELAY)); /* reset port, which implies enabling it */ err = usb2_req_reset_port(udev, NULL, portno); if (err) { DPRINTFN(0, "port %d reset " "failed, error=%s\n", portno, usb2_errstr(err)); goto error; } /* get port status again, it might have changed during reset */ err = uhub_read_port_status(sc, portno); if (err) { goto error; } /* check if something changed during port reset */ if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) || (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) { if (timeout) { DPRINTFN(0, "giving up port reset " "- device vanished!\n"); goto error; } timeout = 1; goto repeat; } } else { DPRINTF("Port %d is in Device Mode\n", portno); } /* * Figure out the device speed */ switch (udev->speed) { case USB_SPEED_HIGH: if (sc->sc_st.port_status & UPS_HIGH_SPEED) speed = USB_SPEED_HIGH; else if (sc->sc_st.port_status & UPS_LOW_SPEED) speed = USB_SPEED_LOW; else speed = USB_SPEED_FULL; break; case USB_SPEED_FULL: if (sc->sc_st.port_status & UPS_LOW_SPEED) speed = USB_SPEED_LOW; else speed = USB_SPEED_FULL; break; case USB_SPEED_LOW: speed = USB_SPEED_LOW; break; default: /* same speed like parent */ speed = udev->speed; break; } /* * Figure out the device mode * * NOTE: This part is currently FreeBSD specific. */ if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) mode = USB_MODE_DEVICE; else mode = USB_MODE_HOST; /* need to create a new child */ child = usb2_alloc_device(sc->sc_dev, udev->bus, udev, udev->depth + 1, portno - 1, portno, speed, mode); if (child == NULL) { DPRINTFN(0, "could not allocate new device!\n"); goto error; } return (0); /* success */ error: if (child) { usb2_free_device(child, USB_UNCFG_FLAG_FREE_SUBDEV | USB_UNCFG_FLAG_FREE_EP0); child = NULL; } if (err == 0) { if (sc->sc_st.port_status & UPS_PORT_ENABLED) { err = usb2_req_clear_port_feature( sc->sc_udev, NULL, portno, UHF_PORT_ENABLE); } } if (err) { DPRINTFN(0, "device problem (%s), " "disabling port %d\n", usb2_errstr(err), portno); } return (err); } /*------------------------------------------------------------------------* * uhub_suspend_resume_port * * Returns: * 0: Success * Else: A control transaction failed *------------------------------------------------------------------------*/ static usb_error_t uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) { struct usb_device *child; struct usb_device *udev; uint8_t is_suspend; usb_error_t err; DPRINTF("port %d\n", portno); udev = sc->sc_udev; child = usb2_bus_port_get_device(udev->bus, udev->hub->ports + portno - 1); /* first clear the port suspend change bit */ err = usb2_req_clear_port_feature(udev, NULL, portno, UHF_C_PORT_SUSPEND); if (err) { DPRINTF("clearing suspend failed.\n"); goto done; } /* get fresh status */ err = uhub_read_port_status(sc, portno); if (err) { DPRINTF("reading port status failed.\n"); goto done; } /* get current state */ if (sc->sc_st.port_status & UPS_SUSPEND) { is_suspend = 1; } else { is_suspend = 0; } DPRINTF("suspended=%u\n", is_suspend); /* do the suspend or resume */ if (child) { /* * This code handle two cases: 1) Host Mode - we can only * receive resume here 2) Device Mode - we can receive * suspend and resume here */ if (is_suspend == 0) usb2_dev_resume_peer(child); else if (child->flags.usb_mode == USB_MODE_DEVICE) usb2_dev_suspend_peer(child); } done: return (err); } /*------------------------------------------------------------------------* * uhub_root_interrupt * * This function is called when a Root HUB interrupt has * happened. "ptr" and "len" makes up the Root HUB interrupt * packet. This function is called having the "bus_mtx" locked. *------------------------------------------------------------------------*/ void uhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len) { USB_BUS_LOCK_ASSERT(bus, MA_OWNED); usb2_needs_explore(bus, 0); } /*------------------------------------------------------------------------* * uhub_explore * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static usb_error_t uhub_explore(struct usb_device *udev) { struct usb_hub *hub; struct uhub_softc *sc; struct usb_port *up; usb_error_t err; uint8_t portno; uint8_t x; hub = udev->hub; sc = hub->hubsoftc; DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address); /* ignore hubs that are too deep */ if (udev->depth > USB_HUB_MAX_DEPTH) { return (USB_ERR_TOO_DEEP); } if (udev->flags.self_suspended) { /* need to wait until the child signals resume */ DPRINTF("Device is suspended!\n"); return (0); } for (x = 0; x != hub->nports; x++) { up = hub->ports + x; portno = x + 1; err = uhub_read_port_status(sc, portno); if (err) { /* most likely the HUB is gone */ break; } if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) { DPRINTF("Overcurrent on port %u.\n", portno); err = usb2_req_clear_port_feature( udev, NULL, portno, UHF_C_PORT_OVER_CURRENT); if (err) { /* most likely the HUB is gone */ break; } } if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) { /* * Fake a connect status change so that the * status gets checked initially! */ sc->sc_st.port_change |= UPS_C_CONNECT_STATUS; } if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) { err = usb2_req_clear_port_feature( udev, NULL, portno, UHF_C_PORT_ENABLE); if (err) { /* most likely the HUB is gone */ break; } if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { /* * Ignore the port error if the device * has vanished ! */ } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) { DPRINTFN(0, "illegal enable change, " "port %d\n", portno); } else { if (up->restartcnt == USB_RESTART_MAX) { /* XXX could try another speed ? */ DPRINTFN(0, "port error, giving up " "port %d\n", portno); } else { sc->sc_st.port_change |= UPS_C_CONNECT_STATUS; up->restartcnt++; } } } if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) { err = uhub_reattach_port(sc, portno); if (err) { /* most likely the HUB is gone */ break; } } if (sc->sc_st.port_change & UPS_C_SUSPEND) { err = uhub_suspend_resume_port(sc, portno); if (err) { /* most likely the HUB is gone */ break; } } err = uhub_explore_sub(sc, up); if (err) { /* no device(s) present */ continue; } /* explore succeeded - reset restart counter */ up->restartcnt = 0; } /* initial status checked */ sc->sc_flags |= UHUB_FLAG_DID_EXPLORE; /* return success */ return (USB_ERR_NORMAL_COMPLETION); } static int uhub_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } /* * The subclass for USB HUBs is ignored because it is 0 for * some and 1 for others. */ if ((uaa->info.bConfigIndex == 0) && (uaa->info.bDeviceClass == UDCLASS_HUB)) { return (0); } return (ENXIO); } static int uhub_attach(device_t dev) { struct uhub_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_device *udev = uaa->device; struct usb_device *parent_hub = udev->parent_hub; struct usb_hub *hub; struct usb_hub_descriptor hubdesc; uint16_t pwrdly; uint8_t x; uint8_t nports; uint8_t portno; uint8_t removable; uint8_t iface_index; usb_error_t err; sc->sc_udev = udev; sc->sc_dev = dev; snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); device_set_usb2_desc(dev); DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, " "parent->selfpowered=%d\n", udev->depth, udev->flags.self_powered, parent_hub, parent_hub ? parent_hub->flags.self_powered : 0); if (udev->depth > USB_HUB_MAX_DEPTH) { DPRINTFN(0, "hub depth, %d, exceeded. HUB ignored!\n", USB_HUB_MAX_DEPTH); goto error; } if (!udev->flags.self_powered && parent_hub && (!parent_hub->flags.self_powered)) { DPRINTFN(0, "bus powered HUB connected to " "bus powered HUB. HUB ignored!\n"); goto error; } /* get HUB descriptor */ DPRINTFN(2, "getting HUB descriptor\n"); /* assuming that there is one port */ err = usb2_req_get_hub_descriptor(udev, NULL, &hubdesc, 1); nports = hubdesc.bNbrPorts; if (!err && (nports >= 8)) { /* get complete HUB descriptor */ err = usb2_req_get_hub_descriptor(udev, NULL, &hubdesc, nports); } if (err) { DPRINTFN(0, "getting hub descriptor failed," "error=%s\n", usb2_errstr(err)); goto error; } if (hubdesc.bNbrPorts != nports) { DPRINTFN(0, "number of ports changed!\n"); goto error; } if (nports == 0) { DPRINTFN(0, "portless HUB!\n"); goto error; } hub = malloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports), M_USBDEV, M_WAITOK | M_ZERO); if (hub == NULL) { goto error; } udev->hub = hub; #if USB_HAVE_TT_SUPPORT /* init FULL-speed ISOCHRONOUS schedule */ usb2_fs_isoc_schedule_init_all(hub->fs_isoc_schedule); #endif /* initialize HUB structure */ hub->hubsoftc = sc; hub->explore = &uhub_explore; hub->nports = hubdesc.bNbrPorts; hub->hubudev = udev; /* if self powered hub, give ports maximum current */ if (udev->flags.self_powered) { hub->portpower = USB_MAX_POWER; } else { hub->portpower = USB_MIN_POWER; } /* set up interrupt pipe */ iface_index = 0; if (udev->parent_hub == NULL) { /* root HUB is special */ err = 0; } else { /* normal HUB */ err = usb2_transfer_setup(udev, &iface_index, sc->sc_xfer, uhub_config, UHUB_N_TRANSFER, sc, &Giant); } if (err) { DPRINTFN(0, "cannot setup interrupt transfer, " "errstr=%s!\n", usb2_errstr(err)); goto error; } /* wait with power off for a while */ usb2_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME)); /* * To have the best chance of success we do things in the exact same * order as Windoze98. This should not be necessary, but some * devices do not follow the USB specs to the letter. * * These are the events on the bus when a hub is attached: * Get device and config descriptors (see attach code) * Get hub descriptor (see above) * For all ports * turn on power * wait for power to become stable * (all below happens in explore code) * For all ports * clear C_PORT_CONNECTION * For all ports * get port status * if device connected * wait 100 ms * turn on reset * wait * clear C_PORT_RESET * get port status * proceed with device attachment */ /* XXX should check for none, individual, or ganged power? */ removable = 0; pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME); for (x = 0; x != nports; x++) { /* set up data structures */ struct usb_port *up = hub->ports + x; up->device_index = 0; up->restartcnt = 0; portno = x + 1; /* check if port is removable */ if (!UHD_NOT_REMOV(&hubdesc, portno)) { removable++; } if (!err) { /* turn the power on */ err = usb2_req_set_port_feature(udev, NULL, portno, UHF_PORT_POWER); } if (err) { DPRINTFN(0, "port %d power on failed, %s\n", portno, usb2_errstr(err)); } DPRINTF("turn on port %d power\n", portno); /* wait for stable power */ usb2_pause_mtx(NULL, USB_MS_TO_TICKS(pwrdly)); } device_printf(dev, "%d port%s with %d " "removable, %s powered\n", nports, (nports != 1) ? "s" : "", removable, udev->flags.self_powered ? "self" : "bus"); /* Start the interrupt endpoint, if any */ if (sc->sc_xfer[0] != NULL) { USB_XFER_LOCK(sc->sc_xfer[0]); usb2_transfer_start(sc->sc_xfer[0]); USB_XFER_UNLOCK(sc->sc_xfer[0]); } /* Enable automatic power save on all USB HUBs */ usb2_set_power_mode(udev, USB_POWER_MODE_SAVE); return (0); error: usb2_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); if (udev->hub) { free(udev->hub, M_USBDEV); udev->hub = NULL; } return (ENXIO); } /* * Called from process context when the hub is gone. * Detach all devices on active ports. */ static int uhub_detach(device_t dev) { struct uhub_softc *sc = device_get_softc(dev); struct usb_hub *hub = sc->sc_udev->hub; struct usb_device *child; uint8_t x; /* detach all children first */ bus_generic_detach(dev); if (hub == NULL) { /* must be partially working */ return (0); } for (x = 0; x != hub->nports; x++) { child = usb2_bus_port_get_device(sc->sc_udev->bus, hub->ports + x); if (child == NULL) { continue; } /* * Subdevices are not freed, because the caller of * uhub_detach() will do that. */ usb2_free_device(child, USB_UNCFG_FLAG_FREE_EP0); } usb2_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER); free(hub, M_USBDEV); sc->sc_udev->hub = NULL; return (0); } static int uhub_suspend(device_t dev) { DPRINTF("\n"); /* Sub-devices are not suspended here! */ return (0); } static int uhub_resume(device_t dev) { DPRINTF("\n"); /* Sub-devices are not resumed here! */ return (0); } static void uhub_driver_added(device_t dev, driver_t *driver) { usb2_needs_explore_all(); } struct hub_result { struct usb_device *udev; uint8_t portno; uint8_t iface_index; }; static void uhub_find_iface_index(struct usb_hub *hub, device_t child, struct hub_result *res) { struct usb_interface *iface; struct usb_device *udev; uint8_t nports; uint8_t x; uint8_t i; nports = hub->nports; for (x = 0; x != nports; x++) { udev = usb2_bus_port_get_device(hub->hubudev->bus, hub->ports + x); if (!udev) { continue; } for (i = 0; i != USB_IFACE_MAX; i++) { iface = usb2_get_iface(udev, i); if (iface && (iface->subdev == child)) { res->iface_index = i; res->udev = udev; res->portno = x + 1; return; } } } res->iface_index = 0; res->udev = NULL; res->portno = 0; } static int uhub_child_location_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc = device_get_softc(parent); struct usb_hub *hub = sc->sc_udev->hub; struct hub_result res; mtx_lock(&Giant); uhub_find_iface_index(hub, child, &res); if (!res.udev) { DPRINTF("device not on hub\n"); if (buflen) { buf[0] = '\0'; } goto done; } snprintf(buf, buflen, "port=%u interface=%u", res.portno, res.iface_index); done: mtx_unlock(&Giant); return (0); } static int uhub_child_pnpinfo_string(device_t parent, device_t child, char *buf, size_t buflen) { struct uhub_softc *sc = device_get_softc(parent); struct usb_hub *hub = sc->sc_udev->hub; struct usb_interface *iface; struct hub_result res; mtx_lock(&Giant); uhub_find_iface_index(hub, child, &res); if (!res.udev) { DPRINTF("device not on hub\n"); if (buflen) { buf[0] = '\0'; } goto done; } iface = usb2_get_iface(res.udev, res.iface_index); if (iface && iface->idesc) { snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\" " "intclass=0x%02x intsubclass=0x%02x", UGETW(res.udev->ddesc.idVendor), UGETW(res.udev->ddesc.idProduct), res.udev->ddesc.bDeviceClass, res.udev->ddesc.bDeviceSubClass, res.udev->serial, iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass); } else { if (buflen) { buf[0] = '\0'; } goto done; } done: mtx_unlock(&Giant); return (0); } /* * The USB Transaction Translator: * =============================== * * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT * USB transfers. To utilize bandwidth dynamically the "scatter and * gather" principle must be applied. This means that bandwidth must * be divided into equal parts of bandwidth. With regard to USB all * data is transferred in smaller packets with length * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is * not a constant! * * The bandwidth scheduler which I have implemented will simply pack * the USB transfers back to back until there is no more space in the * schedule. Out of the 8 microframes which the USB 2.0 standard * provides, only 6 are available for non-HIGH-speed devices. I have * reserved the first 4 microframes for ISOCHRONOUS transfers. The * last 2 microframes I have reserved for INTERRUPT transfers. Without * this division, it is very difficult to allocate and free bandwidth * dynamically. * * NOTE about the Transaction Translator in USB HUBs: * * USB HUBs have a very simple Transaction Translator, that will * simply pipeline all the SPLIT transactions. That means that the * transactions will be executed in the order they are queued! * */ /*------------------------------------------------------------------------* * usb2_intr_find_best_slot * * Return value: * The best Transaction Translation slot for an interrupt endpoint. *------------------------------------------------------------------------*/ static uint8_t -usb2_intr_find_best_slot(size_t *ptr, uint8_t start, uint8_t end) +usb2_intr_find_best_slot(usb_size_t *ptr, uint8_t start, uint8_t end) { - size_t max = 0 - 1; + usb_size_t max = 0 - 1; uint8_t x; uint8_t y; y = 0; /* find the last slot with lesser used bandwidth */ for (x = start; x < end; x++) { if (max >= ptr[x]) { max = ptr[x]; y = x; } } return (y); } /*------------------------------------------------------------------------* * usb2_intr_schedule_adjust * * This function will update the bandwith usage for the microframe * having index "slot" by "len" bytes. "len" can be negative. If the * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX" * the "slot" argument will be replaced by the slot having least used * bandwidth. * * Returns: * The slot on which the bandwidth update was done. *------------------------------------------------------------------------*/ uint8_t usb2_intr_schedule_adjust(struct usb_device *udev, int16_t len, uint8_t slot) { struct usb_bus *bus = udev->bus; struct usb_hub *hub; enum usb_dev_speed speed; USB_BUS_LOCK_ASSERT(bus, MA_OWNED); speed = usb2_get_speed(udev); switch (speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: if (speed == USB_SPEED_LOW) { len *= 8; } /* * The Host Controller Driver should have * performed checks so that the lookup * below does not result in a NULL pointer * access. */ hub = udev->parent_hs_hub->hub; if (slot >= USB_HS_MICRO_FRAMES_MAX) { slot = usb2_intr_find_best_slot(hub->uframe_usage, USB_FS_ISOC_UFRAME_MAX, 6); } hub->uframe_usage[slot] += len; bus->uframe_usage[slot] += len; break; default: if (slot >= USB_HS_MICRO_FRAMES_MAX) { slot = usb2_intr_find_best_slot(bus->uframe_usage, 0, USB_HS_MICRO_FRAMES_MAX); } bus->uframe_usage[slot] += len; break; } return (slot); } /*------------------------------------------------------------------------* * usb2_fs_isoc_schedule_init_sub * * This function initialises an USB FULL speed isochronous schedule * entry. *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT static void usb2_fs_isoc_schedule_init_sub(struct usb_fs_isoc_schedule *fss) { fss->total_bytes = (USB_FS_ISOC_UFRAME_MAX * USB_FS_BYTES_PER_HS_UFRAME); fss->frame_bytes = (USB_FS_BYTES_PER_HS_UFRAME); fss->frame_slot = 0; } #endif /*------------------------------------------------------------------------* * usb2_fs_isoc_schedule_init_all * * This function will reset the complete USB FULL speed isochronous * bandwidth schedule. *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT void usb2_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss) { struct usb_fs_isoc_schedule *fss_end = fss + USB_ISOC_TIME_MAX; while (fss != fss_end) { usb2_fs_isoc_schedule_init_sub(fss); fss++; } } #endif /*------------------------------------------------------------------------* * usb2_isoc_time_expand * * This function will expand the time counter from 7-bit to 16-bit. * * Returns: * 16-bit isochronous time counter. *------------------------------------------------------------------------*/ uint16_t usb2_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr) { uint16_t rem; USB_BUS_LOCK_ASSERT(bus, MA_OWNED); rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1); isoc_time_curr &= (USB_ISOC_TIME_MAX - 1); if (isoc_time_curr < rem) { /* the time counter wrapped around */ bus->isoc_time_last += USB_ISOC_TIME_MAX; } /* update the remainder */ bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1); bus->isoc_time_last |= isoc_time_curr; return (bus->isoc_time_last); } /*------------------------------------------------------------------------* * usb2_fs_isoc_schedule_isoc_time_expand * * This function does multiple things. First of all it will expand the * passed isochronous time, which is the return value. Then it will * store where the current FULL speed isochronous schedule is * positioned in time and where the end is. See "pp_start" and * "pp_end" arguments. * * Returns: * Expanded version of "isoc_time". * * NOTE: This function depends on being called regularly with * intervals less than "USB_ISOC_TIME_MAX". *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT uint16_t usb2_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev, struct usb_fs_isoc_schedule **pp_start, struct usb_fs_isoc_schedule **pp_end, uint16_t isoc_time) { struct usb_fs_isoc_schedule *fss_end; struct usb_fs_isoc_schedule *fss_a; struct usb_fs_isoc_schedule *fss_b; struct usb_hub *hs_hub; isoc_time = usb2_isoc_time_expand(udev->bus, isoc_time); hs_hub = udev->parent_hs_hub->hub; if (hs_hub != NULL) { fss_a = hs_hub->fs_isoc_schedule + (hs_hub->isoc_last_time % USB_ISOC_TIME_MAX); hs_hub->isoc_last_time = isoc_time; fss_b = hs_hub->fs_isoc_schedule + (isoc_time % USB_ISOC_TIME_MAX); fss_end = hs_hub->fs_isoc_schedule + USB_ISOC_TIME_MAX; *pp_start = hs_hub->fs_isoc_schedule; *pp_end = fss_end; while (fss_a != fss_b) { if (fss_a == fss_end) { fss_a = hs_hub->fs_isoc_schedule; continue; } usb2_fs_isoc_schedule_init_sub(fss_a); fss_a++; } } else { *pp_start = NULL; *pp_end = NULL; } return (isoc_time); } #endif /*------------------------------------------------------------------------* * usb2_fs_isoc_schedule_alloc * * This function will allocate bandwidth for an isochronous FULL speed * transaction in the FULL speed schedule. The microframe slot where * the transaction should be started is stored in the byte pointed to * by "pstart". The "len" argument specifies the length of the * transaction in bytes. * * Returns: * 0: Success * Else: Error *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT uint8_t usb2_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len) { uint8_t slot = fss->frame_slot; /* Compute overhead and bit-stuffing */ len += 8; len *= 7; len /= 6; if (len > fss->total_bytes) { *pstart = 0; /* set some dummy value */ return (1); /* error */ } if (len > 0) { fss->total_bytes -= len; while (len >= fss->frame_bytes) { len -= fss->frame_bytes; fss->frame_bytes = USB_FS_BYTES_PER_HS_UFRAME; fss->frame_slot++; } fss->frame_bytes -= len; } *pstart = slot; return (0); /* success */ } #endif /*------------------------------------------------------------------------* * usb2_bus_port_get_device * * This function is NULL safe. *------------------------------------------------------------------------*/ struct usb_device * usb2_bus_port_get_device(struct usb_bus *bus, struct usb_port *up) { if ((bus == NULL) || (up == NULL)) { /* be NULL safe */ return (NULL); } if (up->device_index == 0) { /* nothing to do */ return (NULL); } return (bus->devices[up->device_index]); } /*------------------------------------------------------------------------* * usb2_bus_port_set_device * * This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_bus_port_set_device(struct usb_bus *bus, struct usb_port *up, struct usb_device *udev, uint8_t device_index) { if (bus == NULL) { /* be NULL safe */ return; } /* * There is only one case where we don't * have an USB port, and that is the Root Hub! */ if (up) { if (udev) { up->device_index = device_index; } else { device_index = up->device_index; up->device_index = 0; } } /* * Make relationships to our new device */ if (device_index != 0) { #if USB_HAVE_UGEN mtx_lock(&usb2_ref_lock); #endif bus->devices[device_index] = udev; #if USB_HAVE_UGEN mtx_unlock(&usb2_ref_lock); #endif } /* * Debug print */ DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev); } /*------------------------------------------------------------------------* * usb2_needs_explore * * This functions is called when the USB event thread needs to run. *------------------------------------------------------------------------*/ void usb2_needs_explore(struct usb_bus *bus, uint8_t do_probe) { uint8_t do_unlock; DPRINTF("\n"); if (bus == NULL) { DPRINTF("No bus pointer!\n"); return; } if ((bus->devices == NULL) || (bus->devices[USB_ROOT_HUB_ADDR] == NULL)) { DPRINTF("No root HUB\n"); return; } if (mtx_owned(&bus->bus_mtx)) { do_unlock = 0; } else { USB_BUS_LOCK(bus); do_unlock = 1; } if (do_probe) { bus->do_probe = 1; } if (usb2_proc_msignal(&bus->explore_proc, &bus->explore_msg[0], &bus->explore_msg[1])) { /* ignore */ } if (do_unlock) { USB_BUS_UNLOCK(bus); } } /*------------------------------------------------------------------------* * usb2_needs_explore_all * * This function is called whenever a new driver is loaded and will * cause that all USB busses are re-explored. *------------------------------------------------------------------------*/ void usb2_needs_explore_all(void) { struct usb_bus *bus; devclass_t dc; device_t dev; int max; DPRINTFN(3, "\n"); dc = usb2_devclass_ptr; if (dc == NULL) { DPRINTFN(0, "no devclass\n"); return; } /* * Explore all USB busses in parallell. */ max = devclass_get_maxunit(dc); while (max >= 0) { dev = devclass_get_device(dc, max); if (dev) { bus = device_get_softc(dev); if (bus) { usb2_needs_explore(bus, 1); } } max--; } } /*------------------------------------------------------------------------* * usb2_bus_power_update * * This function will ensure that all USB devices on the given bus are * properly suspended or resumed according to the device transfer * state. *------------------------------------------------------------------------*/ #if USB_HAVE_POWERD void usb2_bus_power_update(struct usb_bus *bus) { usb2_needs_explore(bus, 0 /* no probe */ ); } #endif /*------------------------------------------------------------------------* * usb2_transfer_power_ref * * This function will modify the power save reference counts and * wakeup the USB device associated with the given USB transfer, if * needed. *------------------------------------------------------------------------*/ #if USB_HAVE_POWERD void usb2_transfer_power_ref(struct usb_xfer *xfer, int val) { static const usb_power_mask_t power_mask[4] = { [UE_CONTROL] = USB_HW_POWER_CONTROL, [UE_BULK] = USB_HW_POWER_BULK, [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT, [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC, }; struct usb_device *udev; uint8_t needs_explore; uint8_t needs_hw_power; uint8_t xfer_type; udev = xfer->xroot->udev; if (udev->device_index == USB_ROOT_HUB_ADDR) { /* no power save for root HUB */ return; } USB_BUS_LOCK(udev->bus); xfer_type = xfer->pipe->edesc->bmAttributes & UE_XFERTYPE; udev->pwr_save.last_xfer_time = ticks; udev->pwr_save.type_refs[xfer_type] += val; if (xfer->flags_int.control_xfr) { udev->pwr_save.read_refs += val; if (xfer->flags_int.usb_mode == USB_MODE_HOST) { /* * it is not allowed to suspend during a control * transfer */ udev->pwr_save.write_refs += val; } } else if (USB_GET_DATA_ISREAD(xfer)) { udev->pwr_save.read_refs += val; } else { udev->pwr_save.write_refs += val; } if (udev->flags.self_suspended) needs_explore = (udev->pwr_save.write_refs != 0) || ((udev->pwr_save.read_refs != 0) && (usb2_peer_can_wakeup(udev) == 0)); else needs_explore = 0; if (!(udev->bus->hw_power_state & power_mask[xfer_type])) { DPRINTF("Adding type %u to power state\n", xfer_type); udev->bus->hw_power_state |= power_mask[xfer_type]; needs_hw_power = 1; } else { needs_hw_power = 0; } USB_BUS_UNLOCK(udev->bus); if (needs_explore) { DPRINTF("update\n"); usb2_bus_power_update(udev->bus); } else if (needs_hw_power) { DPRINTF("needs power\n"); if (udev->bus->methods->set_hw_power != NULL) { (udev->bus->methods->set_hw_power) (udev->bus); } } } #endif /*------------------------------------------------------------------------* * usb2_bus_powerd * * This function implements the USB power daemon and is called * regularly from the USB explore thread. *------------------------------------------------------------------------*/ #if USB_HAVE_POWERD void usb2_bus_powerd(struct usb_bus *bus) { struct usb_device *udev; usb_ticks_t temp; usb_ticks_t limit; usb_ticks_t mintime; - size_t type_refs[5]; + usb_size_t type_refs[5]; uint8_t x; uint8_t rem_wakeup; limit = usb2_power_timeout; if (limit == 0) limit = hz; else if (limit > 255) limit = 255 * hz; else limit = limit * hz; DPRINTF("bus=%p\n", bus); USB_BUS_LOCK(bus); /* * The root HUB device is never suspended * and we simply skip it. */ for (x = USB_ROOT_HUB_ADDR + 1; x != bus->devices_max; x++) { udev = bus->devices[x]; if (udev == NULL) continue; rem_wakeup = usb2_peer_can_wakeup(udev); temp = ticks - udev->pwr_save.last_xfer_time; if ((udev->power_mode == USB_POWER_MODE_ON) || (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || (udev->pwr_save.write_refs != 0) || ((udev->pwr_save.read_refs != 0) && (rem_wakeup == 0))) { /* check if we are suspended */ if (udev->flags.self_suspended != 0) { USB_BUS_UNLOCK(bus); usb2_dev_resume_peer(udev); USB_BUS_LOCK(bus); } } else if (temp >= limit) { /* check if we are not suspended */ if (udev->flags.self_suspended == 0) { USB_BUS_UNLOCK(bus); usb2_dev_suspend_peer(udev); USB_BUS_LOCK(bus); } } } /* reset counters */ mintime = 0 - 1; type_refs[0] = 0; type_refs[1] = 0; type_refs[2] = 0; type_refs[3] = 0; type_refs[4] = 0; /* Re-loop all the devices to get the actual state */ for (x = USB_ROOT_HUB_ADDR + 1; x != bus->devices_max; x++) { udev = bus->devices[x]; if (udev == NULL) continue; /* we found a non-Root-Hub USB device */ type_refs[4] += 1; /* "last_xfer_time" can be updated by a resume */ temp = ticks - udev->pwr_save.last_xfer_time; /* * Compute minimum time since last transfer for the complete * bus: */ if (temp < mintime) mintime = temp; if (udev->flags.self_suspended == 0) { type_refs[0] += udev->pwr_save.type_refs[0]; type_refs[1] += udev->pwr_save.type_refs[1]; type_refs[2] += udev->pwr_save.type_refs[2]; type_refs[3] += udev->pwr_save.type_refs[3]; } } if (mintime >= (1 * hz)) { /* recompute power masks */ DPRINTF("Recomputing power masks\n"); bus->hw_power_state = 0; if (type_refs[UE_CONTROL] != 0) bus->hw_power_state |= USB_HW_POWER_CONTROL; if (type_refs[UE_BULK] != 0) bus->hw_power_state |= USB_HW_POWER_BULK; if (type_refs[UE_INTERRUPT] != 0) bus->hw_power_state |= USB_HW_POWER_INTERRUPT; if (type_refs[UE_ISOCHRONOUS] != 0) bus->hw_power_state |= USB_HW_POWER_ISOC; if (type_refs[4] != 0) bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB; } USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power != NULL) { /* always update hardware power! */ (bus->methods->set_hw_power) (bus); } return; } #endif /*------------------------------------------------------------------------* * usb2_dev_resume_peer * * This function will resume an USB peer and do the required USB * signalling to get an USB device out of the suspended state. *------------------------------------------------------------------------*/ static void usb2_dev_resume_peer(struct usb_device *udev) { struct usb_bus *bus; int err; /* be NULL safe */ if (udev == NULL) return; /* check if already resumed */ if (udev->flags.self_suspended == 0) return; /* we need a parent HUB to do resume */ if (udev->parent_hub == NULL) return; DPRINTF("udev=%p\n", udev); if ((udev->flags.usb_mode == USB_MODE_DEVICE) && (udev->flags.remote_wakeup == 0)) { /* * If the host did not set the remote wakeup feature, we can * not wake it up either! */ DPRINTF("remote wakeup is not set!\n"); return; } /* get bus pointer */ bus = udev->bus; /* resume parent hub first */ usb2_dev_resume_peer(udev->parent_hub); /* resume current port (Valid in Host and Device Mode) */ err = usb2_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); if (err) { DPRINTFN(0, "Resuming port failed!\n"); return; } /* resume settle time */ usb2_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY)); if (bus->methods->device_resume != NULL) { /* resume USB device on the USB controller */ (bus->methods->device_resume) (udev); } USB_BUS_LOCK(bus); /* set that this device is now resumed */ udev->flags.self_suspended = 0; #if USB_HAVE_POWERD /* make sure that we don't go into suspend right away */ udev->pwr_save.last_xfer_time = ticks; /* make sure the needed power masks are on */ if (udev->pwr_save.type_refs[UE_CONTROL] != 0) bus->hw_power_state |= USB_HW_POWER_CONTROL; if (udev->pwr_save.type_refs[UE_BULK] != 0) bus->hw_power_state |= USB_HW_POWER_BULK; if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0) bus->hw_power_state |= USB_HW_POWER_INTERRUPT; if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) bus->hw_power_state |= USB_HW_POWER_ISOC; #endif USB_BUS_UNLOCK(bus); if (bus->methods->set_hw_power != NULL) { /* always update hardware power! */ (bus->methods->set_hw_power) (bus); } sx_xlock(udev->default_sx + 1); /* notify all sub-devices about resume */ err = usb2_suspend_resume(udev, 0); sx_unlock(udev->default_sx + 1); /* check if peer has wakeup capability */ if (usb2_peer_can_wakeup(udev)) { /* clear remote wakeup */ err = usb2_req_clear_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); if (err) { DPRINTFN(0, "Clearing device " "remote wakeup failed: %s!\n", usb2_errstr(err)); } } return; } /*------------------------------------------------------------------------* * usb2_dev_suspend_peer * * This function will suspend an USB peer and do the required USB * signalling to get an USB device into the suspended state. *------------------------------------------------------------------------*/ static void usb2_dev_suspend_peer(struct usb_device *udev) { struct usb_device *child; int err; uint8_t x; uint8_t nports; repeat: /* be NULL safe */ if (udev == NULL) return; /* check if already suspended */ if (udev->flags.self_suspended) return; /* we need a parent HUB to do suspend */ if (udev->parent_hub == NULL) return; DPRINTF("udev=%p\n", udev); /* check if the current device is a HUB */ if (udev->hub != NULL) { nports = udev->hub->nports; /* check if all devices on the HUB are suspended */ for (x = 0; x != nports; x++) { child = usb2_bus_port_get_device(udev->bus, udev->hub->ports + x); if (child == NULL) continue; if (child->flags.self_suspended) continue; DPRINTFN(1, "Port %u is busy on the HUB!\n", x + 1); return; } } sx_xlock(udev->default_sx + 1); /* notify all sub-devices about suspend */ err = usb2_suspend_resume(udev, 1); sx_unlock(udev->default_sx + 1); if (usb2_peer_can_wakeup(udev)) { /* allow device to do remote wakeup */ err = usb2_req_set_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); if (err) { DPRINTFN(0, "Setting device " "remote wakeup failed!\n"); } } USB_BUS_LOCK(udev->bus); /* * Set that this device is suspended. This variable must be set * before calling USB controller suspend callbacks. */ udev->flags.self_suspended = 1; USB_BUS_UNLOCK(udev->bus); if (udev->bus->methods->device_suspend != NULL) { usb_timeout_t temp; /* suspend device on the USB controller */ (udev->bus->methods->device_suspend) (udev); /* do DMA delay */ temp = usb2_get_dma_delay(udev->bus); usb2_pause_mtx(NULL, USB_MS_TO_TICKS(temp)); } /* suspend current port */ err = usb2_req_set_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); if (err) { DPRINTFN(0, "Suspending port failed\n"); return; } udev = udev->parent_hub; goto repeat; } /*------------------------------------------------------------------------* * usb2_set_power_mode * * This function will set the power mode, see USB_POWER_MODE_XXX for a * USB device. *------------------------------------------------------------------------*/ void usb2_set_power_mode(struct usb_device *udev, uint8_t power_mode) { /* filter input argument */ if ((power_mode != USB_POWER_MODE_ON) && (power_mode != USB_POWER_MODE_OFF)) { power_mode = USB_POWER_MODE_SAVE; } udev->power_mode = power_mode; /* update copy of power mode */ #if USB_HAVE_POWERD usb2_bus_power_update(udev->bus); #endif } Index: head/sys/dev/usb/usb_hub.h =================================================================== --- head/sys/dev/usb/usb_hub.h (revision 193073) +++ head/sys/dev/usb/usb_hub.h (revision 193074) @@ -1,82 +1,82 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_HUB_H_ #define _USB2_HUB_H_ /* * The following structure defines an USB port. */ struct usb_port { uint8_t restartcnt; #define USB_RESTART_MAX 5 uint8_t device_index; /* zero means not valid */ enum usb_hc_mode usb_mode; /* host or device mode */ }; /* * The following structure defines how many bytes are * left in an 1ms USB time slot. */ struct usb_fs_isoc_schedule { uint16_t total_bytes; uint8_t frame_bytes; uint8_t frame_slot; }; /* * The following structure defines an USB HUB. */ struct usb_hub { #if USB_HAVE_TT_SUPPORT struct usb_fs_isoc_schedule fs_isoc_schedule[USB_ISOC_TIME_MAX]; #endif struct usb_device *hubudev; /* the HUB device */ usb_error_t (*explore) (struct usb_device *hub); void *hubsoftc; - size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; + usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; uint16_t portpower; /* mA per USB port */ uint8_t isoc_last_time; uint8_t nports; struct usb_port ports[0]; }; /* function prototypes */ uint8_t usb2_intr_schedule_adjust(struct usb_device *udev, int16_t len, uint8_t slot); void usb2_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss); void usb2_bus_port_set_device(struct usb_bus *bus, struct usb_port *up, struct usb_device *udev, uint8_t device_index); struct usb_device *usb2_bus_port_get_device(struct usb_bus *bus, struct usb_port *up); void usb2_needs_explore(struct usb_bus *bus, uint8_t do_probe); void usb2_needs_explore_all(void); void usb2_bus_power_update(struct usb_bus *bus); void usb2_bus_powerd(struct usb_bus *bus); void uhub_root_intr(struct usb_bus *, const uint8_t *, uint8_t); #endif /* _USB2_HUB_H_ */ Index: head/sys/dev/usb/usb_lookup.c =================================================================== --- head/sys/dev/usb/usb_lookup.c (revision 193073) +++ head/sys/dev/usb/usb_lookup.c (revision 193074) @@ -1,134 +1,134 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 #include /*------------------------------------------------------------------------* * usb2_lookup_id_by_info * * This functions takes an array of "struct usb_device_id" and tries * to match the entries with the information in "struct usb_lookup_info". * * NOTE: The "sizeof_id" parameter must be a multiple of the * usb_device_id structure size. Else the behaviour of this function * is undefined. * * Return values: * NULL: No match found. * Else: Pointer to matching entry. *------------------------------------------------------------------------*/ const struct usb_device_id * -usb2_lookup_id_by_info(const struct usb_device_id *id, size_t sizeof_id, +usb2_lookup_id_by_info(const struct usb_device_id *id, usb_size_t sizeof_id, const struct usb_lookup_info *info) { const struct usb_device_id *id_end; if (id == NULL) { goto done; } id_end = (const void *)(((const uint8_t *)id) + sizeof_id); /* * Keep on matching array entries until we find a match or * until we reach the end of the matching array: */ for (; id != id_end; id++) { if ((id->match_flag_vendor) && (id->idVendor != info->idVendor)) { continue; } if ((id->match_flag_product) && (id->idProduct != info->idProduct)) { continue; } if ((id->match_flag_dev_lo) && (id->bcdDevice_lo > info->bcdDevice)) { continue; } if ((id->match_flag_dev_hi) && (id->bcdDevice_hi < info->bcdDevice)) { continue; } if ((id->match_flag_dev_class) && (id->bDeviceClass != info->bDeviceClass)) { continue; } if ((id->match_flag_dev_subclass) && (id->bDeviceSubClass != info->bDeviceSubClass)) { continue; } if ((id->match_flag_dev_protocol) && (id->bDeviceProtocol != info->bDeviceProtocol)) { continue; } if ((info->bDeviceClass == 0xFF) && (!(id->match_flag_vendor)) && ((id->match_flag_int_class) || (id->match_flag_int_subclass) || (id->match_flag_int_protocol))) { continue; } if ((id->match_flag_int_class) && (id->bInterfaceClass != info->bInterfaceClass)) { continue; } if ((id->match_flag_int_subclass) && (id->bInterfaceSubClass != info->bInterfaceSubClass)) { continue; } if ((id->match_flag_int_protocol) && (id->bInterfaceProtocol != info->bInterfaceProtocol)) { continue; } /* We found a match! */ return (id); } done: return (NULL); } /*------------------------------------------------------------------------* * usb2_lookup_id_by_uaa - factored out code * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ int -usb2_lookup_id_by_uaa(const struct usb_device_id *id, size_t sizeof_id, +usb2_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, struct usb_attach_arg *uaa) { id = usb2_lookup_id_by_info(id, sizeof_id, &uaa->info); if (id) { /* copy driver info */ uaa->driver_info = id->driver_info; return (0); } return (ENXIO); } Index: head/sys/dev/usb/usb_lookup.h =================================================================== --- head/sys/dev/usb/usb_lookup.h (revision 193073) +++ head/sys/dev/usb/usb_lookup.h (revision 193074) @@ -1,122 +1,122 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_LOOKUP_H_ #define _USB2_LOOKUP_H_ struct usb_attach_arg; /* * The following structure is used when looking up an USB driver for * an USB device. It is inspired by the Linux structure called * "usb_device_id". */ struct usb_device_id { /* Hook for driver specific information */ const void *driver_info; /* Used for product specific matches; the BCD range is inclusive */ uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice_lo; uint16_t bcdDevice_hi; /* Used for device class matches */ uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; /* Used for interface class matches */ uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; /* Select which fields to match against */ uint8_t match_flag_vendor:1; uint8_t match_flag_product:1; uint8_t match_flag_dev_lo:1; uint8_t match_flag_dev_hi:1; uint8_t match_flag_dev_class:1; uint8_t match_flag_dev_subclass:1; uint8_t match_flag_dev_protocol:1; uint8_t match_flag_int_class:1; uint8_t match_flag_int_subclass:1; uint8_t match_flag_int_protocol:1; }; #define USB_VENDOR(vend) \ .match_flag_vendor = 1, .idVendor = (vend) #define USB_PRODUCT(prod) \ .match_flag_product = 1, .idProduct = (prod) #define USB_VP(vend,prod) \ USB_VENDOR(vend), USB_PRODUCT(prod) #define USB_VPI(vend,prod,info) \ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info) #define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo) #define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi) #define USB_DEV_CLASS(dc) \ .match_flag_dev_class = 1, .bDeviceClass = (dc) #define USB_DEV_SUBCLASS(dsc) \ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc) #define USB_DEV_PROTOCOL(dp) \ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp) #define USB_IFACE_CLASS(ic) \ .match_flag_int_class = 1, .bInterfaceClass = (ic) #define USB_IFACE_SUBCLASS(isc) \ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc) #define USB_IFACE_PROTOCOL(ip) \ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip) #define USB_IF_CSI(class,subclass,info) \ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info) #define USB_DRIVER_INFO(ptr) \ .driver_info = ((const void *)(ptr)) #define USB_GET_DRIVER_INFO(did) \ (((const uint8_t *)((did)->driver_info)) - ((const uint8_t *)0)) const struct usb_device_id *usb2_lookup_id_by_info( - const struct usb_device_id *id, size_t sizeof_id, + const struct usb_device_id *id, usb_size_t sizeof_id, const struct usb_lookup_info *info); int usb2_lookup_id_by_uaa(const struct usb_device_id *id, - size_t sizeof_id, struct usb_attach_arg *uaa); + usb_size_t sizeof_id, struct usb_attach_arg *uaa); #endif /* _USB2_LOOKUP_H_ */ Index: head/sys/dev/usb/usb_mbuf.c =================================================================== --- head/sys/dev/usb/usb_mbuf.c (revision 193073) +++ head/sys/dev/usb/usb_mbuf.c (revision 193074) @@ -1,77 +1,77 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 #include /*------------------------------------------------------------------------* * usb2_alloc_mbufs - allocate mbufs to an usbd interface queue * * Returns: * A pointer that should be passed to "free()" when the buffer(s) * should be released. *------------------------------------------------------------------------*/ void * usb2_alloc_mbufs(struct malloc_type *type, struct usb_ifqueue *ifq, - size_t block_size, uint16_t nblocks) + usb_size_t block_size, uint16_t nblocks) { struct usb_mbuf *m_ptr; uint8_t *data_ptr; void *free_ptr = NULL; - size_t alloc_size; + usb_size_t alloc_size; /* align data */ block_size += ((-block_size) & (USB_HOST_ALIGN - 1)); if (nblocks && block_size) { alloc_size = (block_size + sizeof(struct usb_mbuf)) * nblocks; free_ptr = malloc(alloc_size, type, M_WAITOK | M_ZERO); if (free_ptr == NULL) { goto done; } m_ptr = free_ptr; data_ptr = (void *)(m_ptr + nblocks); while (nblocks--) { m_ptr->cur_data_ptr = m_ptr->min_data_ptr = data_ptr; m_ptr->cur_data_len = m_ptr->max_data_len = block_size; USB_IF_ENQUEUE(ifq, m_ptr); m_ptr++; data_ptr += block_size; } } done: return (free_ptr); } Index: head/sys/dev/usb/usb_mbuf.h =================================================================== --- head/sys/dev/usb/usb_mbuf.h (revision 193073) +++ head/sys/dev/usb/usb_mbuf.h (revision 193074) @@ -1,102 +1,102 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_MBUF_H_ #define _USB2_MBUF_H_ /* * The following structure defines a minimum re-implementation of the * mbuf system in the kernel. */ struct usb_mbuf { uint8_t *cur_data_ptr; uint8_t *min_data_ptr; struct usb_mbuf *usb2_nextpkt; struct usb_mbuf *usb2_next; - size_t cur_data_len; - size_t max_data_len; + usb_size_t cur_data_len; + usb_size_t max_data_len; uint8_t last_packet:1; uint8_t unused:7; }; /* * The following structure defines a minimum re-implementation of the * ifqueue structure in the kernel. */ struct usb_ifqueue { struct usb_mbuf *ifq_head; struct usb_mbuf *ifq_tail; - size_t ifq_len; - size_t ifq_maxlen; + usb_size_t ifq_len; + usb_size_t ifq_maxlen; }; #define USB_IF_ENQUEUE(ifq, m) do { \ (m)->usb2_nextpkt = NULL; \ if ((ifq)->ifq_tail == NULL) \ (ifq)->ifq_head = (m); \ else \ (ifq)->ifq_tail->usb2_nextpkt = (m); \ (ifq)->ifq_tail = (m); \ (ifq)->ifq_len++; \ } while (0) #define USB_IF_DEQUEUE(ifq, m) do { \ (m) = (ifq)->ifq_head; \ if (m) { \ if (((ifq)->ifq_head = (m)->usb2_nextpkt) == NULL) { \ (ifq)->ifq_tail = NULL; \ } \ (m)->usb2_nextpkt = NULL; \ (ifq)->ifq_len--; \ } \ } while (0) #define USB_IF_PREPEND(ifq, m) do { \ (m)->usb2_nextpkt = (ifq)->ifq_head; \ if ((ifq)->ifq_tail == NULL) { \ (ifq)->ifq_tail = (m); \ } \ (ifq)->ifq_head = (m); \ (ifq)->ifq_len++; \ } while (0) #define USB_IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen) #define USB_IF_QLEN(ifq) ((ifq)->ifq_len) #define USB_IF_POLL(ifq, m) ((m) = (ifq)->ifq_head) #define USB_MBUF_RESET(m) do { \ (m)->cur_data_ptr = (m)->min_data_ptr; \ (m)->cur_data_len = (m)->max_data_len; \ (m)->last_packet = 0; \ } while (0) /* prototypes */ void *usb2_alloc_mbufs(struct malloc_type *type, struct usb_ifqueue *ifq, - size_t block_size, uint16_t nblocks); + usb_size_t block_size, uint16_t nblocks); #endif /* _USB2_MBUF_H_ */ Index: head/sys/dev/usb/usb_msctest.c =================================================================== --- head/sys/dev/usb/usb_msctest.c (revision 193073) +++ head/sys/dev/usb/usb_msctest.c (revision 193074) @@ -1,574 +1,574 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ /* * The following file contains code that will detect USB autoinstall * disks. * * TODO: Potentially we could add code to automatically detect USB * mass storage quirks for not supported SCSI commands! */ #include #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum { ST_COMMAND, ST_DATA_RD, ST_DATA_RD_CS, ST_DATA_WR, ST_DATA_WR_CS, ST_STATUS, ST_MAX, }; enum { DIR_IN, DIR_OUT, DIR_NONE, }; #define BULK_SIZE 64 /* dummy */ /* Command Block Wrapper */ struct bbb_cbw { uDWord dCBWSignature; #define CBWSIGNATURE 0x43425355 uDWord dCBWTag; uDWord dCBWDataTransferLength; uByte bCBWFlags; #define CBWFLAGS_OUT 0x00 #define CBWFLAGS_IN 0x80 uByte bCBWLUN; uByte bCDBLength; #define CBWCDBLENGTH 16 uByte CBWCDB[CBWCDBLENGTH]; } __packed; /* Command Status Wrapper */ struct bbb_csw { uDWord dCSWSignature; #define CSWSIGNATURE 0x53425355 uDWord dCSWTag; uDWord dCSWDataResidue; uByte bCSWStatus; #define CSWSTATUS_GOOD 0x0 #define CSWSTATUS_FAILED 0x1 #define CSWSTATUS_PHASE 0x2 } __packed; struct bbb_transfer { struct mtx mtx; struct cv cv; struct bbb_cbw cbw; struct bbb_csw csw; struct usb_xfer *xfer[ST_MAX]; uint8_t *data_ptr; - size_t data_len; /* bytes */ - size_t data_rem; /* bytes */ + usb_size_t data_len; /* bytes */ + usb_size_t data_rem; /* bytes */ usb_timeout_t data_timeout; /* ms */ usb_frlength_t actlen; /* bytes */ uint8_t cmd_len; /* bytes */ uint8_t dir; uint8_t lun; uint8_t state; uint8_t error; uint8_t status_try; uint8_t buffer[256]; }; static usb_callback_t bbb_command_callback; static usb_callback_t bbb_data_read_callback; static usb_callback_t bbb_data_rd_cs_callback; static usb_callback_t bbb_data_write_callback; static usb_callback_t bbb_data_wr_cs_callback; static usb_callback_t bbb_status_callback; static const struct usb_config bbb_config[ST_MAX] = { [ST_COMMAND] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = sizeof(struct bbb_cbw), .callback = &bbb_command_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, [ST_DATA_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = BULK_SIZE, .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, .callback = &bbb_data_read_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, [ST_DATA_RD_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &bbb_data_rd_cs_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, [ST_DATA_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = BULK_SIZE, .flags = {.proxy_buffer = 1,}, .callback = &bbb_data_write_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, [ST_DATA_WR_CS] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &bbb_data_wr_cs_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, [ST_STATUS] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = sizeof(struct bbb_csw), .flags = {.short_xfer_ok = 1,}, .callback = &bbb_status_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, }; static void bbb_done(struct bbb_transfer *sc, uint8_t error) { struct usb_xfer *xfer; xfer = sc->xfer[sc->state]; /* verify the error code */ if (error) { switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: error = 1; break; default: error = 2; break; } } sc->error = error; sc->state = ST_COMMAND; sc->status_try = 1; usb2_cv_signal(&sc->cv); } static void bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index) { sc->state = xfer_index; usb2_transfer_start(sc->xfer[xfer_index]); } static void bbb_data_clear_stall_callback(struct usb_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer) { struct bbb_transfer *sc = xfer->priv_sc; if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) { switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: bbb_transfer_start(sc, next_xfer); break; default: bbb_done(sc, 1); break; } } } static void bbb_command_callback(struct usb_xfer *xfer) { struct bbb_transfer *sc = xfer->priv_sc; uint32_t tag; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: bbb_transfer_start (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD : (sc->dir == DIR_OUT) ? ST_DATA_WR : ST_STATUS)); break; case USB_ST_SETUP: sc->status_try = 0; tag = UGETDW(sc->cbw.dCBWTag) + 1; USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE); USETDW(sc->cbw.dCBWTag, tag); USETDW(sc->cbw.dCBWDataTransferLength, (uint32_t)sc->data_len); sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT); sc->cbw.bCBWLUN = sc->lun; sc->cbw.bCDBLength = sc->cmd_len; if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) { sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB); DPRINTFN(0, "Truncating long command!\n"); } xfer->frlengths[0] = sizeof(sc->cbw); usb2_set_frame_data(xfer, &sc->cbw, 0); usb2_start_hardware(xfer); break; default: /* Error */ bbb_done(sc, 1); break; } } static void bbb_data_read_callback(struct usb_xfer *xfer) { struct bbb_transfer *sc = xfer->priv_sc; usb_frlength_t max_bulk = xfer->max_data_length; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->data_rem -= xfer->actlen; sc->data_ptr += xfer->actlen; sc->actlen += xfer->actlen; if (xfer->actlen < xfer->sumlen) { /* short transfer */ sc->data_rem = 0; } case USB_ST_SETUP: DPRINTF("max_bulk=%d, data_rem=%d\n", max_bulk, sc->data_rem); if (sc->data_rem == 0) { bbb_transfer_start(sc, ST_STATUS); break; } if (max_bulk > sc->data_rem) { max_bulk = sc->data_rem; } xfer->timeout = sc->data_timeout; xfer->frlengths[0] = max_bulk; usb2_set_frame_data(xfer, sc->data_ptr, 0); usb2_start_hardware(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { bbb_done(sc, 1); } else { bbb_transfer_start(sc, ST_DATA_RD_CS); } break; } } static void bbb_data_rd_cs_callback(struct usb_xfer *xfer) { bbb_data_clear_stall_callback(xfer, ST_STATUS, ST_DATA_RD); } static void bbb_data_write_callback(struct usb_xfer *xfer) { struct bbb_transfer *sc = xfer->priv_sc; usb_frlength_t max_bulk = xfer->max_data_length; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: sc->data_rem -= xfer->actlen; sc->data_ptr += xfer->actlen; sc->actlen += xfer->actlen; if (xfer->actlen < xfer->sumlen) { /* short transfer */ sc->data_rem = 0; } case USB_ST_SETUP: DPRINTF("max_bulk=%d, data_rem=%d\n", max_bulk, sc->data_rem); if (sc->data_rem == 0) { bbb_transfer_start(sc, ST_STATUS); return; } if (max_bulk > sc->data_rem) { max_bulk = sc->data_rem; } xfer->timeout = sc->data_timeout; xfer->frlengths[0] = max_bulk; usb2_set_frame_data(xfer, sc->data_ptr, 0); usb2_start_hardware(xfer); return; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { bbb_done(sc, 1); } else { bbb_transfer_start(sc, ST_DATA_WR_CS); } return; } } static void bbb_data_wr_cs_callback(struct usb_xfer *xfer) { bbb_data_clear_stall_callback(xfer, ST_STATUS, ST_DATA_WR); } static void bbb_status_callback(struct usb_xfer *xfer) { struct bbb_transfer *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* very simple status check */ if (xfer->actlen < sizeof(sc->csw)) { bbb_done(sc, 1);/* error */ } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) { bbb_done(sc, 0);/* success */ } else { bbb_done(sc, 1);/* error */ } break; case USB_ST_SETUP: xfer->frlengths[0] = sizeof(sc->csw); usb2_set_frame_data(xfer, &sc->csw, 0); usb2_start_hardware(xfer); break; default: DPRINTFN(0, "Failed to read CSW: %s, try %d\n", usb2_errstr(xfer->error), sc->status_try); if ((xfer->error == USB_ERR_CANCELLED) || (sc->status_try)) { bbb_done(sc, 1); } else { sc->status_try = 1; bbb_transfer_start(sc, ST_DATA_RD_CS); } break; } } /*------------------------------------------------------------------------* * bbb_command_start - execute a SCSI command synchronously * * Return values * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, - void *data_ptr, size_t data_len, uint8_t cmd_len, + void *data_ptr, usb_size_t data_len, uint8_t cmd_len, usb_timeout_t data_timeout) { sc->lun = lun; sc->dir = data_len ? dir : DIR_NONE; sc->data_ptr = data_ptr; sc->data_len = data_len; sc->data_rem = data_len; sc->data_timeout = (data_timeout + USB_MS_HZ); sc->actlen = 0; sc->cmd_len = cmd_len; usb2_transfer_start(sc->xfer[sc->state]); while (usb2_transfer_pending(sc->xfer[sc->state])) { usb2_cv_wait(&sc->cv, &sc->mtx); } return (sc->error); } /*------------------------------------------------------------------------* * usb2_test_autoinstall * * Return values: * 0: This interface is an auto install disk (CD-ROM) * Else: Not an auto install disk. *------------------------------------------------------------------------*/ usb_error_t usb2_test_autoinstall(struct usb_device *udev, uint8_t iface_index, uint8_t do_eject) { struct usb_interface *iface; struct usb_interface_descriptor *id; usb_error_t err; uint8_t timeout; uint8_t sid_type; struct bbb_transfer *sc; if (udev == NULL) { return (USB_ERR_INVAL); } iface = usb2_get_iface(udev, iface_index); if (iface == NULL) { return (USB_ERR_INVAL); } id = iface->idesc; if (id == NULL) { return (USB_ERR_INVAL); } if (id->bInterfaceClass != UICLASS_MASS) { return (USB_ERR_INVAL); } switch (id->bInterfaceSubClass) { case UISUBCLASS_SCSI: case UISUBCLASS_UFI: break; default: return (USB_ERR_INVAL); } switch (id->bInterfaceProtocol) { case UIPROTO_MASS_BBB_OLD: case UIPROTO_MASS_BBB: break; default: return (USB_ERR_INVAL); } sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO); if (sc == NULL) { return (USB_ERR_NOMEM); } mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF); usb2_cv_init(&sc->cv, "WBBB"); err = usb2_transfer_setup(udev, &iface_index, sc->xfer, bbb_config, ST_MAX, sc, &sc->mtx); if (err) { goto done; } mtx_lock(&sc->mtx); timeout = 4; /* tries */ repeat_inquiry: sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */ sc->cbw.CBWCDB[1] = 0; sc->cbw.CBWCDB[2] = 0; sc->cbw.CBWCDB[3] = 0; sc->cbw.CBWCDB[4] = 0x24; /* length */ sc->cbw.CBWCDB[5] = 0; err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 0x24, 6, USB_MS_HZ); if ((sc->actlen != 0) && (err == 0)) { sid_type = sc->buffer[0] & 0x1F; if (sid_type == 0x05) { /* CD-ROM */ if (do_eject) { /* 0: opcode: SCSI START/STOP */ sc->cbw.CBWCDB[0] = 0x1b; /* 1: byte2: Not immediate */ sc->cbw.CBWCDB[1] = 0x00; /* 2..3: reserved */ sc->cbw.CBWCDB[2] = 0x00; sc->cbw.CBWCDB[3] = 0x00; /* 4: Load/Eject command */ sc->cbw.CBWCDB[4] = 0x02; /* 5: control */ sc->cbw.CBWCDB[5] = 0x00; err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0, 6, USB_MS_HZ); DPRINTFN(0, "Eject CD command " "status: %s\n", usb2_errstr(err)); } err = 0; goto done; } } else if ((err != 2) && --timeout) { usb2_pause_mtx(&sc->mtx, hz); goto repeat_inquiry; } err = USB_ERR_INVAL; goto done; done: mtx_unlock(&sc->mtx); usb2_transfer_unsetup(sc->xfer, ST_MAX); mtx_destroy(&sc->mtx); usb2_cv_destroy(&sc->cv); free(sc, M_USB); return (err); } Index: head/sys/dev/usb/usb_process.c =================================================================== --- head/sys/dev/usb/usb_process.c (revision 193073) +++ head/sys/dev/usb/usb_process.c (revision 193074) @@ -1,426 +1,426 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #define USB_DEBUG_VAR usb2_proc_debug #include #include #include #include #include #include #include #if (__FreeBSD_version < 700000) #define thread_lock(td) mtx_lock_spin(&sched_lock) #define thread_unlock(td) mtx_unlock_spin(&sched_lock) #endif #if (__FreeBSD_version >= 800000) #define USB_THREAD_CREATE(f, s, p, ...) \ kproc_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) #define USB_THREAD_SUSPEND(p) kproc_suspend(p,0) #define USB_THREAD_EXIT(err) kproc_exit(err) #else #define USB_THREAD_CREATE(f, s, p, ...) \ kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) #define USB_THREAD_EXIT(err) kthread_exit(err) #endif #if USB_DEBUG static int usb2_proc_debug; SYSCTL_NODE(_hw_usb, OID_AUTO, proc, CTLFLAG_RW, 0, "USB process"); SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RW, &usb2_proc_debug, 0, "Debug level"); #endif /*------------------------------------------------------------------------* * usb_process * * This function is the USB process dispatcher. *------------------------------------------------------------------------*/ static void usb_process(void *arg) { struct usb_process *up = arg; struct usb_proc_msg *pm; struct thread *td; /* adjust priority */ td = curthread; thread_lock(td); sched_prio(td, up->up_prio); thread_unlock(td); mtx_lock(up->up_mtx); up->up_curtd = td; while (1) { if (up->up_gone) break; /* * NOTE to reimplementors: dequeueing a command from the * "used" queue and executing it must be atomic, with regard * to the "up_mtx" mutex. That means any attempt to queue a * command by another thread must be blocked until either: * * 1) the command sleeps * * 2) the command returns * * Here is a practical example that shows how this helps * solving a problem: * * Assume that you want to set the baud rate on a USB serial * device. During the programming of the device you don't * want to receive nor transmit any data, because it will be * garbage most likely anyway. The programming of our USB * device takes 20 milliseconds and it needs to call * functions that sleep. * * Non-working solution: Before we queue the programming * command, we stop transmission and reception of data. Then * we queue a programming command. At the end of the * programming command we enable transmission and reception * of data. * * Problem: If a second programming command is queued while the * first one is sleeping, we end up enabling transmission * and reception of data too early. * * Working solution: Before we queue the programming command, * we stop transmission and reception of data. Then we queue * a programming command. Then we queue a second command * that only enables transmission and reception of data. * * Why it works: If a second programming command is queued * while the first one is sleeping, then the queueing of a * second command to enable the data transfers, will cause * the previous one, which is still on the queue, to be * removed from the queue, and re-inserted after the last * baud rate programming command, which then gives the * desired result. */ pm = TAILQ_FIRST(&up->up_qhead); if (pm) { DPRINTF("Message pm=%p, cb=%p (enter)\n", pm, pm->pm_callback); (pm->pm_callback) (pm); if (pm == TAILQ_FIRST(&up->up_qhead)) { /* nothing changed */ TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry); pm->pm_qentry.tqe_prev = NULL; } DPRINTF("Message pm=%p (leave)\n", pm); continue; } /* end if messages - check if anyone is waiting for sync */ if (up->up_dsleep) { up->up_dsleep = 0; usb2_cv_broadcast(&up->up_drain); } up->up_msleep = 1; usb2_cv_wait(&up->up_cv, up->up_mtx); } up->up_ptr = NULL; usb2_cv_signal(&up->up_cv); mtx_unlock(up->up_mtx); USB_THREAD_EXIT(0); } /*------------------------------------------------------------------------* * usb2_proc_create * * This function will create a process using the given "prio" that can * execute callbacks. The mutex pointed to by "p_mtx" will be applied * before calling the callbacks and released after that the callback * has returned. The structure pointed to by "up" is assumed to be * zeroed before this function is called. * * Return values: * 0: success * Else: failure *------------------------------------------------------------------------*/ int usb2_proc_create(struct usb_process *up, struct mtx *p_mtx, const char *pmesg, uint8_t prio) { up->up_mtx = p_mtx; up->up_prio = prio; TAILQ_INIT(&up->up_qhead); usb2_cv_init(&up->up_cv, "wmsg"); usb2_cv_init(&up->up_drain, "dmsg"); if (USB_THREAD_CREATE(&usb_process, up, &up->up_ptr, pmesg)) { DPRINTFN(0, "Unable to create USB process."); up->up_ptr = NULL; goto error; } return (0); error: usb2_proc_free(up); return (ENOMEM); } /*------------------------------------------------------------------------* * usb2_proc_free * * NOTE: If the structure pointed to by "up" is all zero, this * function does nothing. * * NOTE: Messages that are pending on the process queue will not be * removed nor called. *------------------------------------------------------------------------*/ void usb2_proc_free(struct usb_process *up) { /* check if not initialised */ if (up->up_mtx == NULL) return; usb2_proc_drain(up); usb2_cv_destroy(&up->up_cv); usb2_cv_destroy(&up->up_drain); /* make sure that we do not enter here again */ up->up_mtx = NULL; } /*------------------------------------------------------------------------* * usb2_proc_msignal * * This function will queue one of the passed USB process messages on * the USB process queue. The first message that is not already queued * will get queued. If both messages are already queued the one queued * last will be removed from the queue and queued in the end. The USB * process mutex must be locked when calling this function. This * function exploits the fact that a process can only do one callback * at a time. The message that was queued is returned. *------------------------------------------------------------------------*/ void * usb2_proc_msignal(struct usb_process *up, void *_pm0, void *_pm1) { struct usb_proc_msg *pm0 = _pm0; struct usb_proc_msg *pm1 = _pm1; struct usb_proc_msg *pm2; - size_t d; + usb_size_t d; uint8_t t; /* check if gone, return dummy value */ if (up->up_gone) return (_pm0); mtx_assert(up->up_mtx, MA_OWNED); t = 0; if (pm0->pm_qentry.tqe_prev) { t |= 1; } if (pm1->pm_qentry.tqe_prev) { t |= 2; } if (t == 0) { /* * No entries are queued. Queue "pm0" and use the existing * message number. */ pm2 = pm0; } else if (t == 1) { /* Check if we need to increment the message number. */ if (pm0->pm_num == up->up_msg_num) { up->up_msg_num++; } pm2 = pm1; } else if (t == 2) { /* Check if we need to increment the message number. */ if (pm1->pm_num == up->up_msg_num) { up->up_msg_num++; } pm2 = pm0; } else if (t == 3) { /* * Both entries are queued. Re-queue the entry closest to * the end. */ d = (pm1->pm_num - pm0->pm_num); /* Check sign after subtraction */ if (d & 0x80000000) { pm2 = pm0; } else { pm2 = pm1; } TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry); } else { pm2 = NULL; /* panic - should not happen */ } DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num); /* Put message last on queue */ pm2->pm_num = up->up_msg_num; TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); /* Check if we need to wakeup the USB process. */ if (up->up_msleep) { up->up_msleep = 0; /* save "cv_signal()" calls */ usb2_cv_signal(&up->up_cv); } return (pm2); } /*------------------------------------------------------------------------* * usb2_proc_is_gone * * Return values: * 0: USB process is running * Else: USB process is tearing down *------------------------------------------------------------------------*/ uint8_t usb2_proc_is_gone(struct usb_process *up) { if (up->up_gone) return (1); mtx_assert(up->up_mtx, MA_OWNED); return (0); } /*------------------------------------------------------------------------* * usb2_proc_mwait * * This function will return when the USB process message pointed to * by "pm" is no longer on a queue. This function must be called * having "up->up_mtx" locked. *------------------------------------------------------------------------*/ void usb2_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) { struct usb_proc_msg *pm0 = _pm0; struct usb_proc_msg *pm1 = _pm1; /* check if gone */ if (up->up_gone) return; mtx_assert(up->up_mtx, MA_OWNED); if (up->up_curtd == curthread) { /* Just remove the messages from the queue. */ if (pm0->pm_qentry.tqe_prev) { TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry); pm0->pm_qentry.tqe_prev = NULL; } if (pm1->pm_qentry.tqe_prev) { TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry); pm1->pm_qentry.tqe_prev = NULL; } } else while (pm0->pm_qentry.tqe_prev || pm1->pm_qentry.tqe_prev) { /* check if config thread is gone */ if (up->up_gone) break; up->up_dsleep = 1; usb2_cv_wait(&up->up_drain, up->up_mtx); } } /*------------------------------------------------------------------------* * usb2_proc_drain * * This function will tear down an USB process, waiting for the * currently executing command to return. * * NOTE: If the structure pointed to by "up" is all zero, * this function does nothing. *------------------------------------------------------------------------*/ void usb2_proc_drain(struct usb_process *up) { /* check if not initialised */ if (up->up_mtx == NULL) return; /* handle special case with Giant */ if (up->up_mtx != &Giant) mtx_assert(up->up_mtx, MA_NOTOWNED); mtx_lock(up->up_mtx); /* Set the gone flag */ up->up_gone = 1; while (up->up_ptr) { /* Check if we need to wakeup the USB process */ if (up->up_msleep || up->up_csleep) { up->up_msleep = 0; up->up_csleep = 0; usb2_cv_signal(&up->up_cv); } /* Check if we are still cold booted */ if (cold) { USB_THREAD_SUSPEND(up->up_ptr); printf("WARNING: A USB process has " "been left suspended!\n"); break; } usb2_cv_wait(&up->up_cv, up->up_mtx); } /* Check if someone is waiting - should not happen */ if (up->up_dsleep) { up->up_dsleep = 0; usb2_cv_broadcast(&up->up_drain); DPRINTF("WARNING: Someone is waiting " "for USB process drain!\n"); } mtx_unlock(up->up_mtx); } Index: head/sys/dev/usb/usb_process.h =================================================================== --- head/sys/dev/usb/usb_process.h (revision 193073) +++ head/sys/dev/usb/usb_process.h (revision 193074) @@ -1,88 +1,88 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_PROCESS_H_ #define _USB2_PROCESS_H_ #include /* defines */ #define USB_PRI_HIGH PI_NET #define USB_PRI_MED PI_DISK #define USB_PROC_WAIT_TIMEOUT 2 #define USB_PROC_WAIT_DRAIN 1 #define USB_PROC_WAIT_NORMAL 0 /* structure prototypes */ struct usb_proc_msg; /* typedefs */ typedef void (usb_proc_callback_t)(struct usb_proc_msg *hdr); /* * The following structure defines the USB process message header. */ struct usb_proc_msg { TAILQ_ENTRY(usb_proc_msg) pm_qentry; usb_proc_callback_t *pm_callback; - size_t pm_num; + usb_size_t pm_num; }; /* * The following structure defines the USB process. */ struct usb_process { TAILQ_HEAD(, usb_proc_msg) up_qhead; struct cv up_cv; struct cv up_drain; struct proc *up_ptr; struct thread *up_curtd; struct mtx *up_mtx; - size_t up_msg_num; + usb_size_t up_msg_num; uint8_t up_prio; uint8_t up_gone; uint8_t up_msleep; uint8_t up_csleep; uint8_t up_dsleep; }; /* prototypes */ uint8_t usb2_proc_is_gone(struct usb_process *up); int usb2_proc_create(struct usb_process *up, struct mtx *p_mtx, const char *pmesg, uint8_t prio); void usb2_proc_drain(struct usb_process *up); void usb2_proc_mwait(struct usb_process *up, void *pm0, void *pm1); void usb2_proc_free(struct usb_process *up); void *usb2_proc_msignal(struct usb_process *up, void *pm0, void *pm1); #endif /* _USB2_PROCESS_H_ */ Index: head/sys/dev/usb/usb_transfer.c =================================================================== --- head/sys/dev/usb/usb_transfer.c (revision 193073) +++ head/sys/dev/usb/usb_transfer.c (revision 193074) @@ -1,2821 +1,2821 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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 #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include #include #include #include #include #include #include struct usb2_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb2_request_callback; static const struct usb_config usb2_control_ep_cfg[USB_DEFAULT_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb2_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb2_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usb2_update_max_frame_size(struct usb_xfer *); static void usb2_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usb2_control_transfer_init(struct usb_xfer *); static uint8_t usb2_start_hardware_sub(struct usb_xfer *); static void usb2_callback_proc(struct usb_proc_msg *); static void usb2_callback_ss_done_defer(struct usb_xfer *); static void usb2_callback_wrapper(struct usb_xfer_queue *); static void usb2_dma_delay_done_cb(void *); static void usb2_transfer_start_cb(void *); static uint8_t usb2_callback_wrapper_sub(struct usb_xfer *); static void usb2_get_std_packet_size(struct usb2_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb2_request_callback *------------------------------------------------------------------------*/ static void usb2_request_callback(struct usb_xfer *xfer) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb2_handle_request_callback(xfer); else usb2_do_request_callback(xfer); } /*------------------------------------------------------------------------* * usb2_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usb2_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ if (xfer->max_packet_count == 2) { xfer->max_frame_size = 2 * xfer->max_packet_size; } else if (xfer->max_packet_count == 3) { xfer->max_frame_size = 3 * xfer->max_packet_size; } else { xfer->max_frame_size = xfer->max_packet_size; } } /*------------------------------------------------------------------------* * usb2_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usb2_get_dma_delay(struct usb_bus *bus) { uint32_t temp = 0; if (bus->methods->get_dma_delay) { (bus->methods->get_dma_delay) (bus, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usb2_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usb2_transfer_setup_sub_malloc(struct usb_setup_params *parm, - struct usb_page_cache **ppc, size_t size, size_t align, - size_t count) + struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, + usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; - size_t n_dma_pc; - size_t n_obj; - size_t x; - size_t y; - size_t r; - size_t z; + usb_size_t n_dma_pc; + usb_size_t n_obj; + usb_size_t x; + usb_size_t y; + usb_size_t r; + usb_size_t z; USB_ASSERT(align > 1, ("Invalid alignment, 0x%08x!\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0!\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (size >= PAGE_SIZE) { n_dma_pc = count; n_obj = 1; } else { /* compute number of objects per page */ n_obj = (PAGE_SIZE / size); /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = ((count + n_obj - 1) / n_obj); } if (parm->buf == NULL) { /* for the future */ parm->dma_page_ptr += n_dma_pc; parm->dma_page_cache_ptr += n_dma_pc; parm->dma_page_ptr += count; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc) { *ppc = parm->xfer_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 (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb2_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and one page */ parm->dma_page_cache_ptr++; pg++; for (y = 0; (y != n_obj); y++, r--, pc++, pg++) { /* Load sub-chunk into DMA */ if (usb2_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb2_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usb2_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usb2_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_descriptor *edesc; struct usb2_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->pipe->edesc; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpoint = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; if (parm->speed == USB_SPEED_HIGH) { xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; xfer->max_packet_size &= 0x7FF; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usb2_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* compute "max_frame_size" */ usb2_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * if a value is specified use that else check the endpoint * descriptor */ if (xfer->interval == 0) { if (type == UE_INTERRUPT) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_SUPER: case USB_SPEED_VARIABLE: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1<<(16-4)); else xfer->interval = (1 << (xfer->interval-4)); break; case USB_SPEED_HIGH: /* 125us -> 1ms */ xfer->interval /= 8; break; default: break; } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to alot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usb2_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* setup "frlengths" */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usb2_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usb2_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb2_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } /*------------------------------------------------------------------------* * usb2_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usb2_transfer_unsetup" after calling * "usb2_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usb2_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, 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_pipe *pipe; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; uint16_t n; uint16_t refcount; parm.err = 0; refcount = 0; info = NULL; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usb2_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == 0) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* 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; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { parm.err = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (parm.err) { goto done; } bzero(&parm, sizeof(parm)); parm.udev = udev; parm.speed = usb2_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 */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; 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]); #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]); usb2_cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb2_dma_tag_setup(&info->dma_parent_tag, parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb2_bdma_done_event, 32, parm.dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usb2_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb2_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb2_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb2_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb2_control_ep_cfg) info->done_p = &udev->bus->control_xfer_proc; else if (xfer_mtx == &Giant) info->done_p = &udev->bus->giant_callback_proc; else info->done_p = &udev->bus->non_giant_callback_proc; } /* reset sizes */ parm.size[0] = 0; parm.buf = buf; parm.size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ pipe = usb2_get_pipe(udev, ifaces[setup->if_index], setup); if ((pipe == NULL) || (pipe->methods == NULL)) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm.err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm.curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm.size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb2_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &dummy; bzero(&dummy, sizeof(dummy)); refcount++; } /* set transfer pipe pointer */ xfer->pipe = pipe; parm.size[0] += sizeof(xfer[0]); parm.methods = xfer->pipe->methods; parm.curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (&parm); /* check for error */ if (parm.err) goto done; if (buf) { /* * Increment the pipe refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "pipe->refcount" if you * want more information. */ xfer->pipe->refcount++; /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } } if (buf || parm.err) { goto done; } if (refcount == 0) { /* no transfers - nothing to do ! */ goto done; } /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[1] = parm.size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is two. */ 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_p += parm.dma_tag_max; 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)); /* store offset temporarily */ parm.size[3] = parm.size[0]; 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)); /* store offset temporarily */ parm.size[4] = parm.size[0]; 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[0] += ((uint8_t *)parm.xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm.size[2] = parm.size[0]; /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); parm.size[6] = parm.size[0]; 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)); /* allocate zeroed memory */ buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm.err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", 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]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usb2_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usb2_transfer_unsetup_sub(info, 0); } } if (parm.err) { usb2_transfer_unsetup(ppxfer, n_setup); } return (parm.err); } /*------------------------------------------------------------------------* * usb2_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usb2_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { struct usb_page_cache *pc; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usb2_get_dma_delay(info->bus); usb2_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } /* make sure that our done messages are not queued anywhere */ usb2_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb2_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb2_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb2_dma_tag_unsetup(&info->dma_parent_tag); #endif usb2_cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usb2_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usb2_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usb2_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usb2_transfer_start(sc->pxfer[0]); * usb2_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usb2_transfer_start and * usb2_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usb2_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default pipe does not have an * interface, even if pipe->iface_index == 0 */ xfer->pipe->refcount--; usb2_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count!\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usb2_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usb2_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usb2_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpoint &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpoint |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usb2_start_hardware_sub * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb2_start_hardware_sub(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usb2_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usb2_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usb2_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length greater than remaining length!\n"); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set!\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usb2_start_hardware - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usb2_start_hardware(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, pipe=%p, nframes=%d, dir=%s\n", xfer, xfer->pipe, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #if USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb2_dump_pipe(xfer->pipe); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->pipe->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usb2_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usb2_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usb2_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb2_command_wrapper(&xfer->pipe->pipe_q, xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usb2_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute total transfer length */ for (x = 0; x != xfer->nframes; x++) { xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usb2_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usb2_start_hardware_sub(xfer)) { USB_BUS_LOCK(bus); usb2_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb2_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usb2_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usb2_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usb2_pipe_enter(struct usb_xfer *xfer) { struct usb_pipe *pipe; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); pipe = xfer->pipe; DPRINTF("enter\n"); /* enter the transfer */ (pipe->methods->enter) (xfer); xfer->flags_int.can_cancel_immed = 1; /* check for transfer error */ if (xfer->error) { /* some error has happened */ usb2_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb2_command_wrapper(&pipe->pipe_q, xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usb2_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usb2_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { xfer->flags_int.started = 1; } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usb2_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usb2_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usb2_transfer_drain()". *------------------------------------------------------------------------*/ void usb2_transfer_stop(struct usb_xfer *xfer) { struct usb_pipe *pipe; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { /* nothing to do except clearing the "started" flag */ xfer->flags_int.started = 0; return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); xfer->error = USB_ERR_CANCELLED;/* override any previous error */ /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->pipe->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->pipe->methods->close) (xfer); /* * Any additional DMA delay is done by * "usb2_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * pipe. */ pipe = xfer->pipe; /* * If the current USB transfer is completing we need * to start the next one: */ if (pipe->pipe_q.curr == xfer) { usb2_command_wrapper(&pipe->pipe_q, NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usb2_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usb2_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usb2_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usb2_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usb2_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usb2_transfer_stop(xfer); while (usb2_transfer_pending(xfer)) { xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ usb2_cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } /*------------------------------------------------------------------------* * usb2_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usb2_set_frame_data(struct usb_xfer *xfer, void *ptr, usb_frcount_t frindex) { /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; } /*------------------------------------------------------------------------* * usb2_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usb2_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { USB_ASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external!\n")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } /*------------------------------------------------------------------------* * usb2_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb2_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb2_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usb2_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usb2_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usb2_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb2_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usb2_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usb2_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if (!mtx_owned(info->xfer_mtx)) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. */ DPRINTFN(3, "case 5\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb2_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb2_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usb2_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usb2_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb2_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb2_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb2_bdma_post_sync(xfer); } #endif } } /* call processing routine */ (xfer->callback) (xfer); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb2_state == USB_ST_ERROR)) { /* try to loop, but not recursivly */ usb2_command_wrapper(&info->done_q, xfer); return; } done: /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usb2_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; usb2_cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb2_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb2_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is ususally only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ static void usb2_dma_delay_done_cb(void *arg) { struct usb_xfer *xfer = arg; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usb2_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usb2_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usb2_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usb2_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usb2_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usb2_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usb2_transfer_done(struct usb_xfer *xfer, usb_error_t error) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("err=%s\n", usb2_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); return; } /* only set transfer error if not already set */ if (!xfer->error) { xfer->error = error; } /* stop any callouts */ usb2_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usb2_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(xfer->xroot->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &xfer->xroot->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb2_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { xfer->xroot->bus->stats_err.uds_requests [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; } else { xfer->xroot->bus->stats_ok.uds_requests [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usb2_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usb2_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usb2_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_pipe *pipe = xfer->pipe; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); /* start the transfer */ (pipe->methods->start) (xfer); xfer->flags_int.can_cancel_immed = 1; /* check for error */ if (xfer->error) { /* some error has happened */ usb2_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usb2_transfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_transfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usb2_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usb2_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usb2_pipe_start(struct usb_xfer_queue *pq) { struct usb_pipe *pipe; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; pipe = xfer->pipe; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the pipe is already stalled we do nothing ! */ if (pipe->is_stalled) { return; } /* * Check if we are supposed to stall the pipe: */ if (xfer->flags.stall_pipe) { /* clear stall command */ xfer->flags.stall_pipe = 0; /* * Only stall BULK and INTERRUPT endpoints. */ type = (pipe->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { struct usb_device *udev; struct usb_xfer_root *info; info = xfer->xroot; udev = info->udev; pipe->is_stalled = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, NULL, pipe); } else if (udev->default_xfer[1]) { info = udev->default_xfer[1]->xroot; if (usb2_proc_msignal( &info->bus->non_giant_callback_proc, &udev->cs_msg[0], &udev->cs_msg[1])) { /* ignore */ } } else { /* should not happen */ DPRINTFN(0, "No stall handler!\n"); } /* * We get started again when the stall is cleared! */ return; } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usb2_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (pipe->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usb2_transfer_timeout_ms(xfer, &usb2_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); /* start USB transfer */ (pipe->methods->start) (xfer); xfer->flags_int.can_cancel_immed = 1; /* check for error */ if (xfer->error) { /* some error has happened */ usb2_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usb2_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usb2_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb2_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms), cb, xfer); } /*------------------------------------------------------------------------* * usb2_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * pipe transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usb2_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_pipe *pipe; usb_frcount_t x; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(xfer->xroot->bus); (xfer->pipe->methods->close) (xfer); USB_BUS_UNLOCK(xfer->xroot->bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (((xfer->error == USB_ERR_CANCELLED) || (xfer->error == USB_ERR_TIMEOUT)) && (!xfer->flags_int.did_dma_delay)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usb2_get_dma_delay(xfer->xroot->bus); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(xfer->xroot->bus); usb2_transfer_timeout_ms(xfer, &usb2_dma_delay_done_cb, temp); USB_BUS_UNLOCK(xfer->xroot->bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d!\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { xfer->frlengths[x] = 0; } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d!\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(6, "xfer=%p pipe=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->pipe, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on pipe=%p\n", xfer, xfer->pipe); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on pipe %p.\n", xfer, xfer->pipe); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on pipe=%p\n", xfer, xfer->pipe); goto done; } } } pipe = xfer->pipe; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(xfer->xroot->bus); if (pipe->pipe_q.curr == xfer) { usb2_command_wrapper(&pipe->pipe_q, NULL); if (pipe->pipe_q.curr || TAILQ_FIRST(&pipe->pipe_q.head)) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->pipe->is_synced = 0; } } USB_BUS_UNLOCK(xfer->xroot->bus); done: return (0); } /*------------------------------------------------------------------------* * usb2_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb2_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usb2_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { do { /* set both recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usb2_default_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usb2_default_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->default_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->default_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usb2_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->default_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX); /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usb2_transfer_setup(udev, &iface_index, udev->default_xfer, usb2_control_ep_cfg, USB_DEFAULT_XFER_MAX, NULL, udev->default_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer!\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usb2_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usb2_clear_data_toggle(struct usb_device *udev, struct usb_pipe *pipe) { DPRINTFN(5, "udev=%p pipe=%p\n", udev, pipe); USB_BUS_LOCK(udev->bus); pipe->toggle_next = 0; USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usb2_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usb2_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usb2_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usb2_clear_data_toggle(xfer2->xroot->udev, xfer2->pipe); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->pipe->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usb2_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usb2_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usb2_start_hardware(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } void usb2_do_poll(struct usb_xfer **ppxfer, uint16_t max) { static uint8_t once = 0; /* polling is currently not supported */ if (!once) { once = 1; printf("usb2_do_poll: USB polling is " "not supported!\n"); } } static void usb2_get_std_packet_size(struct usb2_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* not supported */ [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } Index: head/sys/dev/usb/usb_transfer.h =================================================================== --- head/sys/dev/usb/usb_transfer.h (revision 193073) +++ head/sys/dev/usb/usb_transfer.h (revision 193074) @@ -1,138 +1,138 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * 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. */ #ifndef _USB2_TRANSFER_H_ #define _USB2_TRANSFER_H_ /* * The following structure defines the messages that is used to signal * the "done_p" USB process. */ struct usb_done_msg { struct usb_proc_msg hdr; struct usb_xfer_root *xroot; }; #define USB_DMATAG_TO_XROOT(dpt) \ ((struct usb_xfer_root *)( \ ((uint8_t *)(dpt)) - \ ((uint8_t *)&((struct usb_xfer_root *)0)->dma_parent_tag))) /* * The following structure is used to keep information about memory * that should be automatically freed at the moment all USB transfers * have been freed. */ struct usb_xfer_root { struct usb_dma_parent_tag dma_parent_tag; #if USB_HAVE_BUSDMA struct usb_xfer_queue dma_q; #endif struct usb_xfer_queue done_q; struct usb_done_msg done_m[2]; struct cv cv_drain; struct usb_process *done_p; /* pointer to callback process */ void *memory_base; struct mtx *xfer_mtx; /* cannot be changed during operation */ #if USB_HAVE_BUSDMA struct usb_page_cache *dma_page_cache_start; struct usb_page_cache *dma_page_cache_end; #endif struct usb_page_cache *xfer_page_cache_start; struct usb_page_cache *xfer_page_cache_end; struct usb_bus *bus; /* pointer to USB bus (cached) */ struct usb_device *udev; /* pointer to USB device */ - size_t memory_size; - size_t setup_refcount; + usb_size_t memory_size; + usb_size_t setup_refcount; #if USB_HAVE_BUSDMA usb_frcount_t dma_nframes; /* number of page caches to load */ usb_frcount_t dma_currframe; /* currect page cache number */ usb_frlength_t dma_frlength_0; /* length of page cache zero */ uint8_t dma_error; /* set if virtual memory could not be * loaded */ #endif uint8_t done_sleep; /* set if done thread is sleeping */ }; /* * The following structure is used when setting up an array of USB * transfers. */ struct usb_setup_params { struct usb_dma_tag *dma_tag_p; struct usb_page *dma_page_ptr; struct usb_page_cache *dma_page_cache_ptr; /* these will be * auto-freed */ struct usb_page_cache *xfer_page_cache_ptr; /* these will not be * auto-freed */ struct usb_device *udev; struct usb_xfer *curr_xfer; const struct usb_config *curr_setup; const struct usb_pipe_methods *methods; void *buf; usb_frlength_t *xfer_length_ptr; - size_t size[7]; + usb_size_t size[7]; usb_frlength_t bufsize; usb_frlength_t bufsize_max; uint16_t hc_max_frame_size; uint16_t hc_max_packet_size; uint8_t hc_max_packet_count; enum usb_dev_speed speed; uint8_t dma_tag_max; usb_error_t err; }; /* function prototypes */ uint8_t usb2_transfer_setup_sub_malloc(struct usb_setup_params *parm, - struct usb_page_cache **ppc, size_t size, size_t align, - size_t count); + struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, + usb_size_t count); void usb2_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer); void usb2_pipe_enter(struct usb_xfer *xfer); void usb2_pipe_start(struct usb_xfer_queue *pq); void usb2_transfer_dequeue(struct usb_xfer *xfer); void usb2_transfer_done(struct usb_xfer *xfer, usb_error_t error); void usb2_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer); void usb2_transfer_setup_sub(struct usb_setup_params *parm); void usb2_default_transfer_setup(struct usb_device *udev); void usb2_clear_data_toggle(struct usb_device *udev, struct usb_pipe *pipe); void usb2_do_poll(struct usb_xfer **ppxfer, uint16_t max); usb_callback_t usb2_do_request_callback; usb_callback_t usb2_handle_request_callback; usb_callback_t usb2_do_clear_stall_callback; void usb2_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms); usb_timeout_t usb2_get_dma_delay(struct usb_bus *bus); void usb2_transfer_power_ref(struct usb_xfer *xfer, int val); #endif /* _USB2_TRANSFER_H_ */