From 77a9c6987873a6489a3f74e40d27336fa8498efc Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Thu, 31 Jan 2013 11:00:57 +0000 Subject: [PATCH] Initial version of libusbboot, a fully stand-alone, single threaded and functional compilation of the FreeBSD USB stack for use with boot loaders and such. Discussed with: Hiroki Sato, hrs @ EuroBSDCon --- sys/boot/usb/Makefile | 150 ++++ sys/boot/usb/Makefile.test | 61 ++ sys/boot/usb/bsd_busspace.c | 207 +++++ sys/boot/usb/bsd_global.h | 63 ++ sys/boot/usb/bsd_kernel.c | 1269 +++++++++++++++++++++++++++++ sys/boot/usb/bsd_kernel.h | 458 +++++++++++ sys/boot/usb/bsd_usbloader_test.c | 80 ++ sys/boot/usb/tools/sysinit.c | 331 ++++++++ sys/boot/usb/tools/sysinit.h | 57 ++ sys/boot/usb/usb_busdma_loader.c | 619 ++++++++++++++ 10 files changed, 3295 insertions(+) create mode 100644 sys/boot/usb/Makefile create mode 100644 sys/boot/usb/Makefile.test create mode 100644 sys/boot/usb/bsd_busspace.c create mode 100644 sys/boot/usb/bsd_global.h create mode 100644 sys/boot/usb/bsd_kernel.c create mode 100644 sys/boot/usb/bsd_kernel.h create mode 100644 sys/boot/usb/bsd_usbloader_test.c create mode 100644 sys/boot/usb/tools/sysinit.c create mode 100644 sys/boot/usb/tools/sysinit.h create mode 100644 sys/boot/usb/usb_busdma_loader.c diff --git a/sys/boot/usb/Makefile b/sys/boot/usb/Makefile new file mode 100644 index 00000000000..b71b10df40d --- /dev/null +++ b/sys/boot/usb/Makefile @@ -0,0 +1,150 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2013 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. +# + +T=${.CURDIR}/tools +S=${.CURDIR}/../.. + +.PATH: \ + ${.CURDIR} \ + ${S}/dev/usb \ + ${S}/dev/usb/controller \ + ${S}/dev/usb/serial \ + ${S}/dev/usb/storage \ + ${S}/dev/usb/template + +LIB= usbboot +INTERNALLIB= +OBJCOPY?= objcopy +SYSCC?= cc + +CFLAGS+= -DBOOTPROG=\"usbloader\" +CFLAGS+= -DUSB_GLOBAL_INCLUDE_FILE="\"bsd_global.h\"" +CFLAGS+= -ffunction-sections -fdata-sections +CFLAGS+= -ffreestanding +CFLAGS+= -Wformat -Wall +CFLAGS+= -I ${S} +CFLAGS+= -I ${T} +CFLAGS+= -I ${.CURDIR} +CFLAGS+= -g + +.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -march=i386 +CFLAGS+= -mpreferred-stack-boundary=2 +.endif +.if ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -m32 +.endif + +# +# Single threaded BSD kernel +# +SRCS+= bsd_kernel.c + +# +# BUSSPACE implementation +# +SRCS+= bsd_busspace.c + +# +# BUSDMA implementation +# +SRCS+= usb_busdma_loader.c + +# +# USB controller drivers +# +SRCS+= at91dci.c +SRCS+= atmegadci.c +SRCS+= avr32dci.c +SRCS+= dwc_otg.c +SRCS+= ehci.c +SRCS+= musb_otg.c +SRCS+= ohci.c +SRCS+= uhci.c +SRCS+= uss820dci.c +SRCS+= xhci.c +SRCS+= usb_controller.c + +CFLAGS += -DUSB_PROBE_LIST="\"xhci\", \"ehci\", \"uhci\", \"ohci\"" + +# +# USB core and templates +# +SRCS+= usb_core.c +SRCS+= usb_debug.c +SRCS+= usb_device.c +SRCS+= usb_dynamic.c +SRCS+= usb_error.c +SRCS+= usb_handle_request.c +SRCS+= usb_hid.c +SRCS+= usb_hub.c +SRCS+= usb_lookup.c +SRCS+= usb_msctest.c +SRCS+= usb_parse.c +SRCS+= usb_request.c +SRCS+= usb_transfer.c +SRCS+= usb_util.c +SRCS+= usb_template.c +SRCS+= usb_template_cdce.c +SRCS+= usb_template_msc.c +SRCS+= usb_template_mtp.c +SRCS+= usb_template_modem.c +SRCS+= usb_template_mouse.c +SRCS+= usb_template_kbd.c +SRCS+= usb_template_audio.c +SRCS+= sysinit_data.c +SRCS+= sysuninit_data.c + +CLEANFILES+= sysinit +CLEANFILES+= sysinit.bin +CLEANFILES+= sysinit_data.c +CLEANFILES+= sysuninit_data.c + +CLEANFILES+= ${SRCS:C/\.c/.osys/g} + +.include + +# +# SYSINIT() and SYSUNINIT() handling +# +sysinit: ${T}/sysinit.c + ${SYSCC} -Wall -o ${.TARGET} ${.ALLSRC} + +sysinit_data.c: sysinit.bin sysinit + ${.OBJDIR}/sysinit -i sysinit.bin -o ${.TARGET} -k sysinit -s sysinit_data + +sysuninit_data.c: sysinit.bin sysinit + ${.OBJDIR}/sysinit -i sysinit.bin -o ${.TARGET} -R -k sysuninit -s sysuninit_data + +.for F in ${OBJS} +${F}sys: ${F} + ${OBJCOPY} -j ".debug.sysinit" -O binary ${F} ${.TARGET} + [ -f ${.TARGET} ] || touch ${.TARGET} +.endfor + +sysinit.bin: ${OBJS:C/\.o/.osys/g:C/sysinit_data.osys//g:C/sysuninit_data.osys//g} + cat ${.ALLSRC} > sysinit.bin diff --git a/sys/boot/usb/Makefile.test b/sys/boot/usb/Makefile.test new file mode 100644 index 00000000000..a366d36aa43 --- /dev/null +++ b/sys/boot/usb/Makefile.test @@ -0,0 +1,61 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2013 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 test application +# + +.PATH: ${.CURDIR} + +PROG= usbloader +MAN= +SRCS= + +CFLAGS+= -Wall +CFLAGS+= -g + +.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -march=i386 +CFLAGS+= -mpreferred-stack-boundary=2 +.endif +.if ${MACHINE_CPUARCH} == "amd64" +CFLAGS+= -m32 +.endif + +LDFLAGS+= -Wl,--gc-sections + +SRCS+= bsd_usbloader_test.c + +LDADD+= libusbboot.a +DPADD+= libusbboot.a + +all: libusbboot.a + +.include + +libusbboot.a: + make -f Makefile diff --git a/sys/boot/usb/bsd_busspace.c b/sys/boot/usb/bsd_busspace.c new file mode 100644 index 00000000000..c9ba09f5e69 --- /dev/null +++ b/sys/boot/usb/bsd_busspace.c @@ -0,0 +1,207 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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 + +struct burst { + uint32_t dw0; + uint32_t dw1; + uint32_t dw2; + uint32_t dw3; + uint32_t dw4; + uint32_t dw5; + uint32_t dw6; + uint32_t dw7; +}; + +void +bus_space_read_multi_1(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint8_t *datap, bus_size_t count) +{ + while (count--) { + *datap++ = bus_space_read_1(t, h, offset); + } +} + +void +bus_space_read_multi_2(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint16_t *datap, bus_size_t count) +{ + while (count--) { + *datap++ = bus_space_read_2(t, h, offset); + } +} + +void +bus_space_read_multi_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint32_t *datap, bus_size_t count) +{ + h += offset; + + while (count--) { + *datap++ = *((volatile uint32_t *)h); + } +} + +void +bus_space_write_multi_1(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint8_t *datap, bus_size_t count) +{ + while (count--) { + uint8_t temp = *datap++; + + bus_space_write_1(t, h, offset, temp); + } +} + +void +bus_space_write_multi_2(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint16_t *datap, bus_size_t count) +{ + while (count--) { + uint16_t temp = *datap++; + + bus_space_write_2(t, h, offset, temp); + } +} + +void +bus_space_write_multi_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint32_t *datap, bus_size_t count) +{ + h += offset; + + while (count--) { + *((volatile uint32_t *)h) = *datap++; + } +} + +void +bus_space_write_1(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint8_t data) +{ + *((volatile uint8_t *)(h + offset)) = data; +} + +void +bus_space_write_2(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint16_t data) +{ + *((volatile uint16_t *)(h + offset)) = data; +} + +void +bus_space_write_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint32_t data) +{ + *((volatile uint32_t *)(h + offset)) = data; +} + +uint8_t +bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset) +{ + return (*((volatile uint8_t *)(h + offset))); +} + +uint16_t +bus_space_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset) +{ + return (*((volatile uint16_t *)(h + offset))); +} + +uint32_t +bus_space_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset) +{ + return (*((volatile uint32_t *)(h + offset))); +} + +void +bus_space_read_region_1(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint8_t *datap, bus_size_t count) +{ + h += offset; + + while (count--) { + *datap++ = *((volatile uint8_t *)h); + h += 1; + } +} + +void +bus_space_write_region_1(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint8_t *datap, bus_size_t count) +{ + h += offset; + + while (count--) { + *((volatile uint8_t *)h) = *datap++; + h += 1; + } +} + +void +bus_space_read_region_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint32_t *datap, bus_size_t count) +{ + enum { BURST = sizeof(struct burst) / 4 }; + + h += offset; + + while (count >= BURST) { + *(struct burst *)datap = *((/* volatile */ struct burst *)h); + + h += BURST * 4; + datap += BURST; + count -= BURST; + } + + while (count--) { + *datap++ = *((volatile uint32_t *)h); + h += 4; + } +} + +void +bus_space_write_region_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t offset, uint32_t *datap, bus_size_t count) +{ + enum { BURST = sizeof(struct burst) / 4 }; + + h += offset; + + while (count >= BURST) { + *((/* volatile */ struct burst *)h) = *(struct burst *)datap; + + h += BURST * 4; + datap += BURST; + count -= BURST; + } + + while (count--) { + *((volatile uint32_t *)h) = *datap++; + h += 4; + } +} diff --git a/sys/boot/usb/bsd_global.h b/sys/boot/usb/bsd_global.h new file mode 100644 index 00000000000..9fe1c8cf244 --- /dev/null +++ b/sys/boot/usb/bsd_global.h @@ -0,0 +1,63 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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 _BSD_GLOBAL_H_ +#define _BSD_GLOBAL_H_ + +#include + +#define USB_DEBUG_VAR usb_debug +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* _BSD_GLOBAL_H_ */ diff --git a/sys/boot/usb/bsd_kernel.c b/sys/boot/usb/bsd_kernel.c new file mode 100644 index 00000000000..5f24c2d7982 --- /dev/null +++ b/sys/boot/usb/bsd_kernel.c @@ -0,0 +1,1269 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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 + +static struct usb_process usb_process[USB_PROC_MAX]; +static device_t usb_pci_root; + +/*------------------------------------------------------------------------* + * Implementation of mutex API + *------------------------------------------------------------------------*/ + +struct mtx Giant; + +static void +mtx_system_init(void *arg) +{ + mtx_init(&Giant, "Giant", NULL, MTX_DEF | MTX_RECURSE); +} +SYSINIT(mtx_system_init, SI_SUB_LOCK, SI_ORDER_MIDDLE, mtx_system_init, NULL); + +void +mtx_init(struct mtx *mtx, const char *name, const char *type, int opt) +{ + mtx->owned = 0; + mtx->parent = mtx; +} + +void +mtx_lock(struct mtx *mtx) +{ + mtx = mtx->parent; + mtx->owned++; +} + +void +mtx_unlock(struct mtx *mtx) +{ + mtx = mtx->parent; + mtx->owned--; +} + +int +mtx_owned(struct mtx *mtx) +{ + mtx = mtx->parent; + return (mtx->owned != 0); +} + +void +mtx_destroy(struct mtx *mtx) +{ + /* NOP */ +} + +/*------------------------------------------------------------------------* + * Implementation of shared/exclusive mutex API + *------------------------------------------------------------------------*/ + +void +sx_init_flags(struct sx *sx, const char *name, int flags) +{ + sx->owned = 0; +} + +void +sx_destroy(struct sx *sx) +{ + /* NOP */ +} + +void +sx_xlock(struct sx *sx) +{ + sx->owned++; +} + +void +sx_xunlock(struct sx *sx) +{ + sx->owned--; +} + +int +sx_xlocked(struct sx *sx) +{ + return (sx->owned != 0); +} + +/*------------------------------------------------------------------------* + * Implementaiton of condition variable API + *------------------------------------------------------------------------*/ + +void +cv_init(struct cv *cv, const char *desc) +{ + cv->sleeping = 0; +} + +void +cv_destroy(struct cv *cv) +{ + /* NOP */ +} + +void +cv_wait(struct cv *cv, struct mtx *mtx) +{ + cv_timedwait(cv, mtx, -1); +} + +int +cv_timedwait(struct cv *cv, struct mtx *mtx, int timo) +{ + int start = ticks; + int delta; + + if (cv->sleeping) + return (EWOULDBLOCK); /* not allowed */ + + cv->sleeping = 1; + + while (cv->sleeping) { + if (timo >= 0) { + delta = ticks - start; + if (delta >= timo || delta < 0) + break; + } + mtx_unlock(mtx); + + usb_idle(); + + mtx_lock(mtx); + } + + if (cv->sleeping) { + cv->sleeping = 0; + return (EWOULDBLOCK); /* not allowed */ + } + return (0); +} + +void +cv_signal(struct cv *cv) +{ + cv->sleeping = 0; +} + +void +cv_broadcast(struct cv *cv) +{ + cv->sleeping = 0; +} + +/*------------------------------------------------------------------------* + * Implementation of callout API + *------------------------------------------------------------------------*/ + +static void callout_proc_msg(struct usb_proc_msg *); + +volatile int ticks = 0; + +static LIST_HEAD(, callout) head_callout = LIST_HEAD_INITIALIZER(&head_callout); + +static struct mtx mtx_callout; +static struct usb_proc_msg callout_msg[2]; + +static void +callout_system_init(void *arg) +{ + mtx_init(&mtx_callout, "callout-mtx", NULL, MTX_DEF | MTX_RECURSE); + + callout_msg[0].pm_callback = &callout_proc_msg; + callout_msg[1].pm_callback = &callout_proc_msg; +} +SYSINIT(callout_system_init, SI_SUB_LOCK, SI_ORDER_MIDDLE, callout_system_init, NULL); + +static void +callout_callback(struct callout *c) +{ + mtx_lock(c->mtx); + + mtx_lock(&mtx_callout); + if (c->entry.le_prev != NULL) { + LIST_REMOVE(c, entry); + c->entry.le_prev = NULL; + } + mtx_unlock(&mtx_callout); + + if (c->func) + (c->func) (c->arg); + + if (!(c->flags & CALLOUT_RETURNUNLOCKED)) + mtx_unlock(c->mtx); +} + +void +callout_process(int timeout) +{ + ticks += timeout; + usb_proc_msignal(usb_process + 2, &callout_msg[0], &callout_msg[1]); +} + +static void +callout_proc_msg(struct usb_proc_msg *pmsg) +{ + struct callout *c; + int delta; + +repeat: + mtx_lock(&mtx_callout); + + LIST_FOREACH(c, &head_callout, entry) { + + delta = c->timeout - ticks; + if (delta < 0) { + mtx_unlock(&mtx_callout); + + callout_callback(c); + + goto repeat; + } + } + mtx_unlock(&mtx_callout); +} + +void +callout_init_mtx(struct callout *c, struct mtx *mtx, int flags) +{ + memset(c, 0, sizeof(*c)); + + if (mtx == NULL) + mtx = &Giant; + + c->mtx = mtx; + c->flags = (flags & CALLOUT_RETURNUNLOCKED); +} + +void +callout_reset(struct callout *c, int to_ticks, + void (*func) (void *), void *arg) +{ + callout_stop(c); + + c->func = func; + c->arg = arg; + c->timeout = ticks + to_ticks; + + mtx_lock(&mtx_callout); + LIST_INSERT_HEAD(&head_callout, c, entry); + mtx_unlock(&mtx_callout); +} + +void +callout_stop(struct callout *c) +{ + mtx_lock(&mtx_callout); + + if (c->entry.le_prev != NULL) { + LIST_REMOVE(c, entry); + c->entry.le_prev = NULL; + } + mtx_unlock(&mtx_callout); + + c->func = NULL; + c->arg = NULL; +} + +void +callout_drain(struct callout *c) +{ + if (c->mtx == NULL) + return; /* not initialised */ + + mtx_lock(c->mtx); + callout_stop(c); + mtx_unlock(c->mtx); +} + +int +callout_pending(struct callout *c) +{ + int retval; + + mtx_lock(&mtx_callout); + retval = (c->entry.le_prev != NULL); + mtx_unlock(&mtx_callout); + + return (retval); +} + +/*------------------------------------------------------------------------* + * Implementation of device API + *------------------------------------------------------------------------*/ + +static const char unknown_string[] = { "unknown" }; + +static TAILQ_HEAD(, module_data) module_head = + TAILQ_HEAD_INITIALIZER(module_head); + +static uint8_t +devclass_equal(const char *a, const char *b) +{ + char ta, tb; + + if (a == b) + return (1); + + while (1) { + ta = *a; + tb = *b; + if (ta != tb) + return (0); + if (ta == 0) + break; + a++; + b++; + } + return (1); +} + +int +bus_generic_resume(device_t dev) +{ + return (0); +} + +int +bus_generic_shutdown(device_t dev) +{ + return (0); +} + +int +bus_generic_suspend(device_t dev) +{ + return (0); +} + +int +bus_generic_print_child(device_t dev, device_t child) +{ + return (0); +} + +void +bus_generic_driver_added(device_t dev, driver_t *driver) +{ + return; +} + +device_t +device_get_parent(device_t dev) +{ + return (dev ? dev->dev_parent : NULL); +} + +void +device_set_interrupt(device_t dev, intr_fn_t *fn, void *arg) +{ + dev->dev_irq_fn = fn; + dev->dev_irq_arg = arg; +} + +void +device_run_interrupts(device_t parent) +{ + device_t child; + + if (parent == NULL) + return; + + TAILQ_FOREACH(child, &parent->dev_children, dev_link) { + if (child->dev_irq_fn != NULL) + (child->dev_irq_fn) (child->dev_irq_arg); + } +} + +void +device_set_ivars(device_t dev, void *ivars) +{ + dev->dev_aux = ivars; +} + +void * +device_get_ivars(device_t dev) +{ + return (dev ? dev->dev_aux : NULL); +} + +int +device_get_unit(device_t dev) +{ + return (dev ? dev->dev_unit : 0); +} + +int +bus_generic_detach(device_t dev) +{ + device_t child; + int error; + + if (!dev->dev_attached) + return (EBUSY); + + TAILQ_FOREACH(child, &dev->dev_children, dev_link) { + if ((error = device_detach(child)) != 0) + return (error); + } + return (0); +} + +const char * +device_get_nameunit(device_t dev) +{ + if (dev && dev->dev_nameunit[0]) + return (dev->dev_nameunit); + + return (unknown_string); +} + +static uint8_t +devclass_create(devclass_t *dc_pp) +{ + if (dc_pp == NULL) { + return (1); + } + if (dc_pp[0] == NULL) { + dc_pp[0] = malloc(sizeof(**(dc_pp)), + M_DEVBUF, M_WAITOK | M_ZERO); + + if (dc_pp[0] == NULL) { + return (1); + } + } + return (0); +} + +static const struct module_data * +devclass_find_create(const char *classname) +{ + const struct module_data *mod; + + TAILQ_FOREACH(mod, &module_head, entry) { + if (devclass_equal(mod->mod_name, classname)) { + if (devclass_create(mod->devclass_pp)) { + continue; + } + return (mod); + } + } + return (NULL); +} + +static uint8_t +devclass_add_device(const struct module_data *mod, device_t dev) +{ + device_t *pp_dev; + device_t *end; + uint8_t unit; + + pp_dev = mod->devclass_pp[0]->dev_list; + end = pp_dev + DEVCLASS_MAXUNIT; + unit = 0; + + while (pp_dev != end) { + if (*pp_dev == NULL) { + *pp_dev = dev; + dev->dev_unit = unit; + dev->dev_module = mod; + snprintf(dev->dev_nameunit, + sizeof(dev->dev_nameunit), + "%s%d", device_get_name(dev), unit); + return (0); + } + pp_dev++; + unit++; + } + DPRINTF("Could not add device to devclass.\n"); + return (1); +} + +static void +devclass_delete_device(const struct module_data *mod, device_t dev) +{ + if (mod == NULL) { + return; + } + mod->devclass_pp[0]->dev_list[dev->dev_unit] = NULL; + dev->dev_module = NULL; +} + +static device_t +make_device(device_t parent, const char *name) +{ + device_t dev = NULL; + const struct module_data *mod = NULL; + + if (name) { + + mod = devclass_find_create(name); + + if (!mod) { + + DPRINTF("%s:%d:%s: can't find device " + "class %s\n", __FILE__, __LINE__, + __FUNCTION__, name); + + goto done; + } + } + dev = malloc(sizeof(*dev), + M_DEVBUF, M_WAITOK | M_ZERO); + + if (dev == NULL) + goto done; + + dev->dev_parent = parent; + TAILQ_INIT(&dev->dev_children); + + if (name) { + dev->dev_fixed_class = 1; + if (devclass_add_device(mod, dev)) { + goto error; + } + } +done: + return (dev); + +error: + if (dev) { + free(dev, M_DEVBUF); + } + return (NULL); +} + +device_t +device_add_child(device_t dev, const char *name, int unit) +{ + device_t child; + + if (unit != -1) { + device_printf(dev, "Unit is not -1\n"); + } + child = make_device(dev, name); + if (child == NULL) { + device_printf(dev, "Could not add child '%s'\n", name); + goto done; + } + if (dev == NULL) { + /* no parent */ + goto done; + } + TAILQ_INSERT_TAIL(&dev->dev_children, child, dev_link); +done: + return (child); +} + +int +device_delete_child(device_t dev, device_t child) +{ + int error = 0; + device_t grandchild; + + /* remove children first */ + + while ((grandchild = TAILQ_FIRST(&child->dev_children))) { + error = device_delete_child(child, grandchild); + if (error) { + device_printf(dev, "Error deleting child!\n"); + goto done; + } + } + + error = device_detach(child); + + if (error) + goto done; + + devclass_delete_device(child->dev_module, child); + + if (dev != NULL) { + /* remove child from parent */ + TAILQ_REMOVE(&dev->dev_children, child, dev_link); + } + free(child, M_DEVBUF); + +done: + return (error); +} + +int +device_delete_children(device_t dev) +{ + device_t child; + int error = 0; + + while ((child = TAILQ_FIRST(&dev->dev_children))) { + error = device_delete_child(dev, child); + if (error) { + device_printf(dev, "Error deleting child!\n"); + break; + } + } + return (error); +} + +void +device_quiet(device_t dev) +{ + dev->dev_quiet = 1; +} + +const char * +device_get_desc(device_t dev) +{ + if (dev) + return &(dev->dev_desc[0]); + return (unknown_string); +} + +static int +default_method(void) +{ + /* do nothing */ + DPRINTF("Default method called\n"); + return (0); +} + +void * +device_get_method(device_t dev, const char *what) +{ + const struct device_method *mtod; + + mtod = dev->dev_module->driver->methods; + while (mtod->func != NULL) { + if (devclass_equal(mtod->desc, what)) { + return (mtod->func); + } + mtod++; + } + return ((void *)&default_method); +} + +const char * +device_get_name(device_t dev) +{ + if (dev == NULL) + return (unknown_string); + + return (dev->dev_module->driver->name); +} + +static int +device_allocate_softc(device_t dev) +{ + const struct module_data *mod; + + mod = dev->dev_module; + + if ((dev->dev_softc_alloc == 0) && + (mod->driver->size != 0)) { + dev->dev_sc = malloc(mod->driver->size, + M_DEVBUF, M_WAITOK | M_ZERO); + + if (dev->dev_sc == NULL) + return (ENOMEM); + + dev->dev_softc_alloc = 1; + } + return (0); +} + +int +device_probe_and_attach(device_t dev) +{ + const struct module_data *mod; + const char *bus_name_parent; + + bus_name_parent = device_get_name(device_get_parent(dev)); + + if (dev->dev_attached) + return (0); /* fail-safe */ + + if (dev->dev_fixed_class) { + + mod = dev->dev_module; + + if (DEVICE_PROBE(dev) <= 0) { + + if (device_allocate_softc(dev) == 0) { + + if (DEVICE_ATTACH(dev) == 0) { + /* success */ + dev->dev_attached = 1; + return (0); + } + } + } + device_detach(dev); + + goto error; + } + /* + * Else find a module for our device, if any + */ + + TAILQ_FOREACH(mod, &module_head, entry) { + if (devclass_equal(mod->bus_name, bus_name_parent)) { + if (devclass_create(mod->devclass_pp)) { + continue; + } + if (devclass_add_device(mod, dev)) { + continue; + } + if (DEVICE_PROBE(dev) <= 0) { + + if (device_allocate_softc(dev) == 0) { + + if (DEVICE_ATTACH(dev) == 0) { + /* success */ + dev->dev_attached = 1; + return (0); + } + } + } + /* else try next driver */ + + device_detach(dev); + } + } + +error: + return (ENODEV); +} + +int +device_detach(device_t dev) +{ + const struct module_data *mod = dev->dev_module; + int error; + + if (dev->dev_attached) { + + error = DEVICE_DETACH(dev); + if (error) { + return error; + } + dev->dev_attached = 0; + } + device_set_softc(dev, NULL); + + if (dev->dev_fixed_class == 0) + devclass_delete_device(mod, dev); + + return (0); +} + +void +device_set_softc(device_t dev, void *softc) +{ + if (dev->dev_softc_alloc) { + free(dev->dev_sc, M_DEVBUF); + dev->dev_sc = NULL; + } + dev->dev_sc = softc; + dev->dev_softc_alloc = 0; +} + +void * +device_get_softc(device_t dev) +{ + if (dev == NULL) + return (NULL); + + return (dev->dev_sc); +} + +int +device_is_attached(device_t dev) +{ + return (dev->dev_attached); +} + +void +device_set_desc(device_t dev, const char *desc) +{ + snprintf(dev->dev_desc, sizeof(dev->dev_desc), "%s", desc); +} + +void +device_set_desc_copy(device_t dev, const char *desc) +{ + device_set_desc(dev, desc); +} + +void * +devclass_get_softc(devclass_t dc, int unit) +{ + return (device_get_softc(devclass_get_device(dc, unit))); +} + +int +devclass_get_maxunit(devclass_t dc) +{ + int max_unit = 0; + + if (dc) { + max_unit = DEVCLASS_MAXUNIT; + while (max_unit--) { + if (dc->dev_list[max_unit]) { + break; + } + } + max_unit++; + } + return (max_unit); +} + +device_t +devclass_get_device(devclass_t dc, int unit) +{ + return (((unit < 0) || (unit >= DEVCLASS_MAXUNIT) || (dc == NULL)) ? + NULL : dc->dev_list[unit]); +} + +devclass_t +devclass_find(const char *classname) +{ + const struct module_data *mod; + + TAILQ_FOREACH(mod, &module_head, entry) { + if (devclass_equal(mod->mod_name, classname)) + return (mod->devclass_pp[0]); + } + return (NULL); +} + +void +module_register(void *data) +{ + struct module_data *mdata = data; + + TAILQ_INSERT_TAIL(&module_head, mdata, entry); +} + +/*------------------------------------------------------------------------* + * System startup + *------------------------------------------------------------------------*/ + +static void +sysinit_run(const void **ppdata) +{ + const struct sysinit *psys; + + while ((psys = *ppdata) != NULL) { + (psys->func) (psys->data); + ppdata++; + } +} + +/*------------------------------------------------------------------------* + * USB process API + *------------------------------------------------------------------------*/ + +static int usb_do_process(struct usb_process *); +static int usb_proc_level = -1; +static struct mtx usb_proc_mtx; + +void +usb_idle(void) +{ + int old_level = usb_proc_level; + int old_giant = Giant.owned; + int worked; + + device_run_interrupts(usb_pci_root); + + do { + worked = 0; + Giant.owned = 0; + + while (++usb_proc_level < USB_PROC_MAX) + worked |= usb_do_process(usb_process + usb_proc_level); + + usb_proc_level = old_level; + Giant.owned = old_giant; + + } while (worked); +} + +void +usb_init(void) +{ + sysinit_run(sysinit_data); +} + +void +usb_uninit(void) +{ + sysinit_run(sysuninit_data); +} + +static void +usb_process_init_sub(struct usb_process *up) +{ + TAILQ_INIT(&up->up_qhead); + + cv_init(&up->up_cv, "-"); + cv_init(&up->up_drain, "usbdrain"); + + up->up_mtx = &usb_proc_mtx; +} + +static void +usb_process_init(void *arg) +{ + uint8_t x; + + mtx_init(&usb_proc_mtx, "usb-proc-mtx", NULL, MTX_DEF | MTX_RECURSE); + + for (x = 0; x != USB_PROC_MAX; x++) + usb_process_init_sub(&usb_process[x]); + +} +SYSINIT(usb_process_init, SI_SUB_LOCK, SI_ORDER_MIDDLE, usb_process_init, NULL); + +static int +usb_do_process(struct usb_process *up) +{ + struct usb_proc_msg *pm; + int worked = 0; + + mtx_lock(&usb_proc_mtx); + +repeat: + pm = TAILQ_FIRST(&up->up_qhead); + + if (pm != NULL) { + + worked = 1; + + (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; + } + goto repeat; + } + mtx_unlock(&usb_proc_mtx); + + return (worked); +} + +int +usb_proc_create(struct usb_process *up, struct mtx *p_mtx, + const char *pmesg, uint8_t prio) +{ +#define USB_PROC_OFFSET(a,b) \ + ((int)(((long)&((struct usb_bus *)0)->a) - \ + ((long)&((struct usb_bus *)0)->b))) + + /* figure out which process we are creating */ + switch ((int)((long)up - (long)p_mtx)) { + case USB_PROC_OFFSET(giant_callback_proc, bus_mtx): + up->up_ptr = (void *)(usb_process + 2); + break; + case USB_PROC_OFFSET(non_giant_callback_proc, bus_mtx): + up->up_ptr = (void *)(usb_process + 2); + break; + case USB_PROC_OFFSET(explore_proc, bus_mtx): + up->up_ptr = (void *)(usb_process + 0); + break; + case USB_PROC_OFFSET(control_xfer_proc, bus_mtx): + up->up_ptr = (void *)(usb_process + 1); + break; + default: + up->up_ptr = (void *)(usb_process + 1); + break; + } + return (0); /* success */ +} + +void +usb_proc_free(struct usb_process *up) +{ + /* NOP */ +} + +void * +usb_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; + usb_size_t d; + uint8_t t; + + /* find the correct parent */ + while (up->up_ptr != NULL) + up = (struct usb_process *)up->up_ptr; + + 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 */ + } + + /* Put message last on queue */ + + pm2->pm_num = up->up_msg_num; + TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry); + + return (pm2); +} + +/*------------------------------------------------------------------------* + * usb_proc_is_gone + * + * Return values: + * 0: USB process is running + * Else: USB process is tearing down + *------------------------------------------------------------------------*/ +uint8_t +usb_proc_is_gone(struct usb_process *up) +{ + return (0); +} + +/*------------------------------------------------------------------------* + * usb_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 "usb_proc_mtx" locked. + *------------------------------------------------------------------------*/ +void +usb_proc_mwait(struct usb_process *up, void *_pm0, void *_pm1) +{ + struct usb_proc_msg *pm0 = _pm0; + struct usb_proc_msg *pm1 = _pm1; + + /* find the correct parent */ + while (up->up_ptr != NULL) + up = (struct usb_process *)up->up_ptr; + + /* 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; + } +} + +/*------------------------------------------------------------------------* + * SYSTEM attach + *------------------------------------------------------------------------*/ + +static device_method_t pci_methods[] = { + DEVMETHOD_END +}; + +static driver_t pci_driver = { + .name = "pci", + .methods = pci_methods, +}; + +static devclass_t pci_devclass; + +DRIVER_MODULE(pci, pci, pci_driver, pci_devclass, 0, 0); + +static const char *usb_pci_devices[] = { +#ifdef USB_PROBE_LIST + USB_PROBE_LIST +#endif +}; + +#define USB_PCI_USB_MAX (sizeof(usb_pci_devices) / sizeof(void *)) + +static device_t usb_pci_dev[USB_PCI_USB_MAX]; + +static void +usb_pci_mod_load(void *arg) +{ + uint32_t x; + + usb_pci_root = device_add_child(NULL, "pci", -1); + if (usb_pci_root == NULL) + return; + + for (x = 0; x != USB_PCI_USB_MAX; x++) { + usb_pci_dev[x] = device_add_child(usb_pci_root, usb_pci_devices[x], -1); + if (usb_pci_dev[x] == NULL) + continue; + if (device_probe_and_attach(usb_pci_dev[x])) { + device_printf(usb_pci_dev[x], + "WARNING: Probe and attach failed!\n"); + } + } +} +SYSINIT(usb_pci_mod_load, SI_SUB_RUN_SCHEDULER, SI_ORDER_MIDDLE, usb_pci_mod_load, 0); + +static void +usb_pci_mod_unload(void *arg) +{ + uint32_t x; + + for (x = 0; x != USB_PCI_USB_MAX; x++) { + if (usb_pci_dev[x]) { + device_detach(usb_pci_dev[x]); + device_delete_child(usb_pci_root, usb_pci_dev[x]); + } + } + if (usb_pci_root) + device_delete_child(NULL, usb_pci_root); +} +SYSUNINIT(usb_pci_mod_unload, SI_SUB_RUN_SCHEDULER, SI_ORDER_MIDDLE, usb_pci_mod_unload, 0); + +/*------------------------------------------------------------------------* + * MALLOC API + *------------------------------------------------------------------------*/ + +#define USB_POOL_ALIGN 8 + +static uint8_t usb_pool[USB_POOL_SIZE] __aligned(USB_POOL_ALIGN); +static uint32_t usb_pool_rem = USB_POOL_SIZE; +static uint32_t usb_pool_entries; + +struct malloc_hdr { + TAILQ_ENTRY(malloc_hdr) entry; + uint32_t size; +} __aligned(USB_POOL_ALIGN); + +static TAILQ_HEAD(, malloc_hdr) malloc_head = + TAILQ_HEAD_INITIALIZER(malloc_head); + +void * +usb_malloc(unsigned long size) +{ + struct malloc_hdr *hdr; + + size = (size + USB_POOL_ALIGN - 1) & ~(USB_POOL_ALIGN - 1); + size += sizeof(struct malloc_hdr); + + TAILQ_FOREACH(hdr, &malloc_head, entry) { + if (hdr->size == size) + break; + } + + if (hdr) { + printf("MALLOC: Entries = %d; Remainder = %d; Size = %d\n", + (int)usb_pool_entries, (int)usb_pool_rem, (int)size); + + TAILQ_REMOVE(&malloc_head, hdr, entry); + memset(hdr + 1, 0, hdr->size - sizeof(*hdr)); + return (hdr + 1); + } + if (usb_pool_rem >= size) { + hdr = (void *)(usb_pool + USB_POOL_SIZE - usb_pool_rem); + hdr->size = size; + + usb_pool_rem -= size; + usb_pool_entries++; + + printf("MALLOC: Entries = %d; Remainder = %d; Size = %d\n", + (int)usb_pool_entries, (int)usb_pool_rem, (int)size); + + memset(hdr + 1, 0, hdr->size - sizeof(*hdr)); + return (hdr + 1); + } + return (NULL); +} + +void +usb_free(void *arg) +{ + struct malloc_hdr *hdr; + + if (arg == NULL) + return; + + hdr = arg; + hdr--; + + TAILQ_INSERT_TAIL(&malloc_head, hdr, entry); +} + +char * +usb_strdup(const char *str) +{ + char *tmp; + int len; + + len = 1 + strlen(str); + + tmp = usb_malloc(len); + if (tmp == NULL) + return (NULL); + + memcpy(tmp, str, len); + return (tmp); +} diff --git a/sys/boot/usb/bsd_kernel.h b/sys/boot/usb/bsd_kernel.h new file mode 100644 index 00000000000..4c947216561 --- /dev/null +++ b/sys/boot/usb/bsd_kernel.h @@ -0,0 +1,458 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2011 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 _BSD_KERNEL_H_ +#define _BSD_KERNEL_H_ + +#define _KERNEL +#define __FreeBSD_version 1000000 + +#include +#include +#include + +#define isalpha(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z')) +#define isdigit(x) ((x) >= '0' && (x) <= '9') +#define panic(...) do { printf("USB PANIC: " __VA_ARGS__); while (1) ; } while (0) +#define M_USB 0 +#define M_USBDEV 0 +#define USB_PROC_MAX 3 +#define SYSCTL_DECL(...) +#define SYSCTL_NODE(name,...) struct { } name __used +#define SYSCTL_INT(...) +#define TUNABLE_INT(...) +#define MALLOC_DECLARE(...) +#define MALLOC_DEFINE(...) +#define EVENTHANDLER_DECLARE(...) +#define EVENTHANDLER_INVOKE(...) +#define KASSERT(...) +#define SCHEDULER_STOPPED(x) (0) +#define PI_SWI(...) (0) +#define UNIQ_NAME(x) x +#define UNIQ_NAME_STR(x) #x +#define DEVCLASS_MAXUNIT 32 +#define MOD_LOAD 1 +#define MOD_UNLOAD 2 +#define DEVMETHOD(what,func) { #what, (void *)&func } +#define DEVMETHOD_END {0,0} +#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \ + static struct module_data bsd_##name##_##busname##_driver_mod = { \ + evh, arg, #busname, #name, #busname "/" #name, \ + &driver, &devclass, { 0, 0 } }; \ +SYSINIT(bsd_##name##_##busname##_driver_mod, SI_SUB_DRIVERS, \ + SI_ORDER_MIDDLE, module_register, \ + &bsd_##name##_##busname##_driver_mod) +#define SYSINIT(uniq, subs, order, _func, _data) \ +const struct sysinit UNIQ_NAME(sysinit_##uniq) = { \ + .func = (_func), \ + .data = __DECONST(void *, _data) \ +}; \ +SYSINIT_ENTRY(uniq##_entry, "sysinit", (subs), \ + (order), "const struct sysinit", \ + UNIQ_NAME_STR(sysinit_##uniq), "SYSINIT") + +#define SYSUNINIT(uniq, subs, order, _func, _data) \ +const struct sysinit UNIQ_NAME(sysuninit_##uniq) = { \ + .func = (_func), \ + .data = __DECONST(void *, _data) \ +}; \ +SYSINIT_ENTRY(uniq##_entry, "sysuninit", (subs), \ + (order), "const struct sysuninit", \ + UNIQ_NAME_STR(sysuninit_##uniq), "SYSUNINIT") +#define MODULE_DEPEND(...) +#define MODULE_VERSION(...) +#define NULL ((void *)0) +#define BUS_SPACE_BARRIER_READ 0x01 +#define BUS_SPACE_BARRIER_WRITE 0x02 +#define hz 1000 +#define PAGE_SIZE 4096 +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MTX_DEF 0 +#define MTX_RECURSE 0 +#define SX_DUPOK 0 +#define SX_NOWITNESS 0 +#define WITNESS_WARN(...) +#define cold 0 +#define BUS_PROBE_GENERIC 0 +#define CALLOUT_RETURNUNLOCKED 0x1 +#define va_list __builtin_va_list +#define va_size(type) __builtin_va_size(type) +#define va_start(ap, last) __builtin_va_start(ap, last) +#define va_end(ap) __builtin_va_end(ap) +#define va_arg(ap, type) __builtin_va_arg((ap), type) +#define DEVICE_ATTACH(dev, ...) \ + (((device_attach_t *)(device_get_method(dev, "device_attach")))(dev,## __VA_ARGS__)) +#define DEVICE_DETACH(dev, ...) \ + (((device_detach_t *)(device_get_method(dev, "device_detach")))(dev,## __VA_ARGS__)) +#define DEVICE_PROBE(dev, ...) \ + (((device_probe_t *)(device_get_method(dev, "device_probe")))(dev,## __VA_ARGS__)) +#define DEVICE_RESUME(dev, ...) \ + (((device_resume_t *)(device_get_method(dev, "device_resume")))(dev,## __VA_ARGS__)) +#define DEVICE_SHUTDOWN(dev, ...) \ + (((device_shutdown_t *)(device_get_method(dev, "device_shutdown")))(dev,## __VA_ARGS__)) +#define DEVICE_SUSPEND(dev, ...) \ + (((device_suspend_t *)(device_get_method(dev, "device_suspend")))(dev,## __VA_ARGS__)) +#define USB_HANDLE_REQUEST(dev, ...) \ + (((usb_handle_request_t *)(device_get_method(dev, "usb_handle_request")))(dev,## __VA_ARGS__)) +#define USB_TAKE_CONTROLLER(dev, ...) \ + (((usb_take_controller_t *)(device_get_method(dev, "usb_take_controller")))(dev,## __VA_ARGS__)) + +enum { + SI_SUB_DUMMY = 0x0000000, + SI_SUB_LOCK = 0x1B00000, + SI_SUB_KLD = 0x2000000, + SI_SUB_DRIVERS = 0x3100000, + SI_SUB_PSEUDO = 0x7000000, + SI_SUB_KICK_SCHEDULER = 0xa000000, + SI_SUB_RUN_SCHEDULER = 0xfffffff +}; + +enum { + SI_ORDER_FIRST = 0x0000000, + SI_ORDER_SECOND = 0x0000001, + SI_ORDER_THIRD = 0x0000002, + SI_ORDER_FOURTH = 0x0000003, + SI_ORDER_MIDDLE = 0x1000000, + SI_ORDER_ANY = 0xfffffff /* last */ +}; + +struct uio; +struct thread; +struct malloc_type; +struct usb_process; + +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef unsigned short uint16_t; +typedef signed short int16_t; + +typedef unsigned int uint32_t; +typedef signed int int32_t; + +typedef unsigned long long uint64_t; +typedef signed long long int64_t; + +typedef unsigned long bus_addr_t; +typedef unsigned long bus_size_t; + +typedef unsigned long size_t; +typedef unsigned long u_long; + +typedef void *bus_dmamap_t; +typedef void *bus_dma_tag_t; + +typedef void *bus_space_tag_t; +typedef uint8_t *bus_space_handle_t; + +typedef uint16_t uid_t; +typedef uint16_t gid_t; +typedef uint16_t mode_t; + +typedef uint8_t *caddr_t; +typedef unsigned long __uintptr_t; +typedef unsigned long uintptr_t; + +/* SYSINIT API */ + +#include + +struct sysinit { + void (*func) (void *arg); + void *data; +}; + +/* MUTEX API */ + +struct mtx { + int owned; + struct mtx *parent; +}; + +#define mtx_assert(...) do { } while (0) +void mtx_init(struct mtx *, const char *, const char *, int); +void mtx_lock(struct mtx *); +void mtx_unlock(struct mtx *); +int mtx_owned(struct mtx *); +void mtx_destroy(struct mtx *); + +extern struct mtx Giant; + +/* SX API */ + +struct sx { + int owned; +}; + +#define sx_assert(...) do { } while (0) +#define sx_init(...) sx_init_flags(__VA_ARGS__, 0) +void sx_init_flags(struct sx *, const char *, int); +void sx_destroy(struct sx *); +void sx_xlock(struct sx *); +void sx_xunlock(struct sx *); +int sx_xlocked(struct sx *); + +/* CONDVAR API */ + +struct cv { + int sleeping; +}; + +void cv_init(struct cv *, const char *desc); +void cv_destroy(struct cv *); +void cv_wait(struct cv *, struct mtx *); +int cv_timedwait(struct cv *, struct mtx *, int); +void cv_signal(struct cv *); +void cv_broadcast(struct cv *); + +/* CALLOUT API */ + +typedef void callout_fn_t (void *); + +extern volatile int ticks; + +struct callout { + LIST_ENTRY(callout) entry; + callout_fn_t *func; + void *arg; + struct mtx *mtx; + int flags; + int timeout; +}; + +void callout_init_mtx(struct callout *, struct mtx *, int); +void callout_reset(struct callout *, int, callout_fn_t *, void *); +void callout_stop(struct callout *); +void callout_drain(struct callout *); +int callout_pending(struct callout *); +void callout_process(int timeout); + +/* DEVICE API */ + +struct driver; +struct devclass; +struct device; +struct module; +struct module_data; + +typedef struct driver driver_t; +typedef struct devclass *devclass_t; +typedef struct device *device_t; +typedef void (intr_fn_t)(void *arg); + +typedef int device_attach_t (device_t dev); +typedef int device_detach_t (device_t dev); +typedef int device_resume_t (device_t dev); +typedef int device_shutdown_t (device_t dev); +typedef int device_probe_t (device_t dev); +typedef int device_suspend_t (device_t dev); + +typedef int bus_child_location_str_t (device_t parent, device_t child, char *buf, size_t buflen); +typedef int bus_child_pnpinfo_str_t (device_t parent, device_t child, char *buf, size_t buflen); +typedef void bus_driver_added_t (device_t dev, driver_t *driver); + +struct device_method { + const char *desc; + void *const func; +}; + +typedef struct device_method device_method_t; + +struct device { + TAILQ_HEAD(device_list, device) dev_children; + TAILQ_ENTRY(device) dev_link; + + struct device *dev_parent; + const struct module_data *dev_module; + void *dev_sc; + void *dev_aux; + intr_fn_t *dev_irq_fn; + void *dev_irq_arg; + + uint16_t dev_unit; + + char dev_nameunit[64]; + char dev_desc[64]; + + uint8_t dev_res_alloc:1; + uint8_t dev_quiet:1; + uint8_t dev_softc_set:1; + uint8_t dev_softc_alloc:1; + uint8_t dev_attached:1; + uint8_t dev_fixed_class:1; + uint8_t dev_unit_manual:1; +}; + +struct devclass { + device_t dev_list[DEVCLASS_MAXUNIT]; +}; + +struct driver { + const char *name; + const struct device_method *methods; + uint32_t size; +}; + +struct module_data { + int (*callback) (struct module *, int, void *arg); + void *arg; + const char *bus_name; + const char *mod_name; + const char *long_name; + const struct driver *driver; + struct devclass **devclass_pp; + TAILQ_ENTRY(module_data) entry; +}; + +device_t device_get_parent(device_t dev); +void *device_get_method(device_t dev, const char *what); +const char *device_get_name(device_t dev); +const char *device_get_nameunit(device_t dev); + +#define device_printf(dev, fmt,...) \ + printf("%s: " fmt, device_get_nameunit(dev),## __VA_ARGS__) +device_t device_add_child(device_t dev, const char *name, int unit); +void device_quiet(device_t dev); +void device_set_interrupt(device_t dev, intr_fn_t *fn, void *arg); +void device_run_interrupts(device_t parent); +void device_set_ivars(device_t dev, void *ivars); +void *device_get_ivars(device_t dev); +const char *device_get_desc(device_t dev); +int device_probe_and_attach(device_t dev); +int device_detach(device_t dev); +void *device_get_softc(device_t dev); +void device_set_softc(device_t dev, void *softc); +int device_delete_child(device_t dev, device_t child); +int device_delete_children(device_t dev); +int device_is_attached(device_t dev); +void device_set_desc(device_t dev, const char *desc); +void device_set_desc_copy(device_t dev, const char *desc); +int device_get_unit(device_t dev); +void *devclass_get_softc(devclass_t dc, int unit); +int devclass_get_maxunit(devclass_t dc); +device_t devclass_get_device(devclass_t dc, int unit); +devclass_t devclass_find(const char *classname); + +#define bus_get_dma_tag(...) (NULL) +int bus_generic_detach(device_t dev); +int bus_generic_resume(device_t dev); +int bus_generic_shutdown(device_t dev); +int bus_generic_suspend(device_t dev); +int bus_generic_print_child(device_t dev, device_t child); +void bus_generic_driver_added(device_t dev, driver_t *driver); + +/* BUS SPACE API */ + +void bus_space_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint8_t data); +void bus_space_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint16_t data); +void bus_space_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint32_t data); + +uint8_t bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset); +uint16_t bus_space_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset); +uint32_t bus_space_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset); + +void bus_space_read_multi_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint8_t *datap, bus_size_t count); +void bus_space_read_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint16_t *datap, bus_size_t count); +void bus_space_read_multi_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint32_t *datap, bus_size_t count); + +void bus_space_write_multi_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint8_t *datap, bus_size_t count); +void bus_space_write_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint16_t *datap, bus_size_t count); +void bus_space_write_multi_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, uint32_t *datap, bus_size_t count); + +void bus_space_read_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint8_t *datap, bus_size_t count); +void bus_space_write_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint8_t *datap, bus_size_t count); +void bus_space_read_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint32_t *datap, bus_size_t count); +void bus_space_write_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, uint32_t *datap, bus_size_t count); + +void bus_space_barrier(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, bus_size_t length, int flags); + +void module_register(void *); + +/* LIB-C */ + +void *memset(void *, int, size_t len); +void *memcpy(void *, const void *, size_t len); +int printf(const char *,...) __printflike(1, 2); +int snprintf(char *restrict str, size_t size, const char *restrict format,...) __printflike(3, 4); +size_t strlen(const char *s); + +/* MALLOC API */ + +#define malloc(s,x,f) usb_malloc(s) +void *usb_malloc(size_t); + +#define free(p,x) usb_free(p) +void usb_free(void *); + +#define strdup(p,x) usb_strdup(p) +char *usb_strdup(const char *str); + +/* ENDIANNESS */ + +/* Assume little endian */ + +#define htole64(x) ((uint64_t)(x)) +#define le64toh(x) ((uint64_t)(x)) + +#define htole32(x) ((uint32_t)(x)) +#define le32toh(x) ((uint32_t)(x)) + +#define htole16(x) ((uint16_t)(x)) +#define le16toh(x) ((uint16_t)(x)) + +#define be32toh(x) ((uint32_t)(x)) +#define htobe32(x) ((uint32_t)(x)) + +/* USB */ + +typedef int usb_handle_request_t (device_t dev, const void *req, void **pptr, uint16_t *plen, uint16_t offset, uint8_t *pstate); +typedef int usb_take_controller_t (device_t dev); + +void usb_idle(void); +void usb_init(void); +void usb_uninit(void); + +/* set some defaults */ + +#ifndef USB_POOL_SIZE +#define USB_POOL_SIZE (1024*1024) /* 1 MByte */ +#endif + +int pause(const char *, int); +void DELAY(unsigned int); + +/* OTHER */ + +struct selinfo { +}; + +/* SYSTEM STARTUP API */ + +extern const void *sysinit_data[]; +extern const void *sysuninit_data[]; + +#endif /* _BSD_KERNEL_H_ */ diff --git a/sys/boot/usb/bsd_usbloader_test.c b/sys/boot/usb/bsd_usbloader_test.c new file mode 100644 index 00000000000..929e2e99b64 --- /dev/null +++ b/sys/boot/usb/bsd_usbloader_test.c @@ -0,0 +1,80 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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 + +extern int usleep(int); +extern void callout_process(int); +extern void usb_idle(void); +extern void usb_init(void); +extern void usb_uninit(void); + +#define hz 1000 + +void +DELAY(unsigned int delay) +{ + usleep(delay); +} + +int +pause(const char *what, int timeout) +{ + if (timeout == 0) + timeout = 1; + + usleep((1000000 / hz) * timeout); + + return (0); +} + +int +main(int argc, char **argv) +{ + uint32_t time; + + usb_init(); + + time = 0; + + while (1) { + + usb_idle(); + + usleep(1000); + + if (++time >= (1000 / hz)) { + time = 0; + callout_process(1); + } + } + + usb_uninit(); + + return (0); +} diff --git a/sys/boot/usb/tools/sysinit.c b/sys/boot/usb/tools/sysinit.c new file mode 100644 index 00000000000..b968fe0ab85 --- /dev/null +++ b/sys/boot/usb/tools/sysinit.c @@ -0,0 +1,331 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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. + */ + +/* + * This utility sorts sysinit structure entries in binary format and + * prints out the result in C-format. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sysinit.h" + +static int opt_R; +static const char *input_f; +static const char *output_f; +static const char *struct_name; +static const char *keyword; +static struct sysinit_data **start; +static struct sysinit_data **stop; + +static int input_file = -1; +static int output_file = -1; + +static uint8_t *input_ptr; +static uint32_t input_len; + +static uint32_t endian32; + +static char scratch_buf[4096]; + +static int success; + +static void do_sysinit(void); + +/* the following function converts the numbers into host endian format */ + +static uint32_t +read32(uint32_t val) +{ + uint32_t temp; + uint32_t endian; + + endian = endian32; + temp = 0; + + while (val) { + temp |= (val & 0xF) << ((endian & 0xF) * 4); + endian >>= 4; + val >>= 4; + } + return (temp); +} + +static void +do_write(int fd, const char *buf) +{ + int len = strlen(buf); + + if (write(fd, buf, len) != len) + err(EX_SOFTWARE, "Could not write to output file"); +} + +static void * +do_malloc(int size) +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) + errx(EX_SOFTWARE, "Could not allocate memory"); + return (ptr); +} + +static void +usage(void) +{ + errx(EX_USAGE, "sysinit -i sysinit.bin -o sysinit_data.c \\\n" + "\t" "-k sysinit -s sysinit_data [ -R (reverse)]"); +} + +static void +cleanup(void) +{ + if (output_file >= 0) + close(output_file); + if (input_file >= 0) + close(input_file); + if (success == 0) { + if (output_f) + unlink(output_f); + } +} + +static int +compare(const void *_pa, const void *_pb) +{ + const struct sysinit_data * const *pa = _pa; + const struct sysinit_data * const *pb = _pb; + + if ((*pa)->dw_msb_value > (*pb)->dw_msb_value) + return (1); + + if ((*pa)->dw_msb_value < (*pb)->dw_msb_value) + return (-1); + + if ((*pa)->dw_lsb_value > (*pb)->dw_lsb_value) + return (1); + + if ((*pa)->dw_lsb_value < (*pb)->dw_lsb_value) + return (-1); + + return (0); /* equal */ +} + +static int +compare_R(const void *_pa, const void *_pb) +{ + const struct sysinit_data * const *pa = _pa; + const struct sysinit_data * const *pb = _pb; + + if ((*pa)->dw_msb_value > (*pb)->dw_msb_value) + return (-1); + + if ((*pa)->dw_msb_value < (*pb)->dw_msb_value) + return (1); + + if ((*pa)->dw_lsb_value > (*pb)->dw_lsb_value) + return (-1); + + if ((*pa)->dw_lsb_value < (*pb)->dw_lsb_value) + return (1); + + return (0); /* equal */ +} + +int +main(int argc, char **argv) +{ + struct sysinit_data **sipp; + int c; + int entries; + off_t off; + + while ((c = getopt(argc, argv, "k:s:i:o:Rh")) != -1) { + switch (c) { + case 'i': + input_f = optarg; + break; + case 'o': + output_f = optarg; + break; + case 'R': + opt_R = 1; + break; + case 'k': + keyword = optarg; + break; + case 's': + struct_name = optarg; + break; + default: + usage(); + } + } + + if (input_f == NULL || output_f == NULL || + struct_name == NULL || keyword == NULL) + usage(); + + atexit(&cleanup); + + cleanup(); + + input_file = open(input_f, O_RDONLY); + if (input_file < 0) + err(EX_SOFTWARE, "Could not open input file: %s", input_f); + + output_file = open(output_f, O_TRUNC | O_CREAT | O_RDWR, 0600); + if (output_file < 0) + err(EX_SOFTWARE, "Could not open output file: %s", output_f); + + off = lseek(input_file, 0, SEEK_END); + + input_ptr = do_malloc(off); + input_len = off; + + if (input_len % (uint32_t)sizeof(struct sysinit_data)) { + errx(EX_SOFTWARE, "Input file size is not divisible by %u", + (unsigned int)sizeof(struct sysinit_data)); + } + off = lseek(input_file, 0, SEEK_SET); + if (off < 0) + err(EX_SOFTWARE, "Could not seek to start of input file"); + + if (read(input_file, input_ptr, input_len) != input_len) + err(EX_SOFTWARE, "Could not read input file"); + + entries = input_len / (uint32_t)sizeof(struct sysinit_data); + + start = do_malloc(sizeof(void *) * entries); + stop = start + entries; + + for (c = 0; c != entries; c++) + start[c] = &((struct sysinit_data *)input_ptr)[c]; + + if (start != stop) + endian32 = (*start)->dw_endian32; + + /* switch all fields to host endian order */ + for (sipp = start; sipp < stop; sipp++) { + (*sipp)->dw_lsb_value = read32((*sipp)->dw_lsb_value); + (*sipp)->dw_msb_value = read32((*sipp)->dw_msb_value); + (*sipp)->dw_file_line = read32((*sipp)->dw_file_line); + } + + if (opt_R == 0) { + /* sort entries, rising numerical order */ + qsort(start, entries, sizeof(void *), &compare); + } else { + /* sort entries, falling numerical order */ + qsort(start, entries, sizeof(void *), &compare_R); + } + + /* safe all strings */ + for (sipp = start; sipp < stop; sipp++) { + (*sipp)->b_keyword_name[sizeof((*sipp)->b_keyword_name) - 1] = 0; + (*sipp)->b_global_type[sizeof((*sipp)->b_global_type) - 1] = 0; + (*sipp)->b_global_name[sizeof((*sipp)->b_global_name) - 1] = 0; + (*sipp)->b_file_name[sizeof((*sipp)->b_file_name) - 1] = 0; + (*sipp)->b_debug_info[sizeof((*sipp)->b_debug_info) - 1] = 0; + } + + if (strcmp(keyword, "sysinit") == 0) + do_sysinit(); + else if (strcmp(keyword, "sysuninit") == 0) + do_sysinit(); + else + errx(EX_USAGE, "Unknown keyword '%s'", keyword); + + success = 1; + + return (0); +} + +static void +do_sysinit(void) +{ + struct sysinit_data **sipp; + int c; + + snprintf(scratch_buf, sizeof(scratch_buf), + "/*\n" + " * This file was automatically generated.\n" + " * Please do not edit.\n" + " */\n\n"); + + /* write out externals */ + for (c = 0, sipp = start; sipp < stop; c++, sipp++) { + if (strcmp((const char *)(*sipp)->b_keyword_name, keyword)) + continue; + if ((*sipp)->dw_msb_value == 0) + continue; + + snprintf(scratch_buf, sizeof(scratch_buf), + "/* #%04u: %s entry at %s:%u */\n", + c, (*sipp)->b_debug_info, (*sipp)->b_file_name, + (unsigned int)(*sipp)->dw_file_line); + + do_write(output_file, scratch_buf); + + snprintf(scratch_buf, sizeof(scratch_buf), + "extern %s %s;\n\n", (*sipp)->b_global_type, + (*sipp)->b_global_name); + + do_write(output_file, scratch_buf); + } + + snprintf(scratch_buf, sizeof(scratch_buf), + "const void *%s[] = {\n", struct_name); + + do_write(output_file, scratch_buf); + + /* write out actual table */ + for (c = 0, sipp = start; sipp < stop; c++, sipp++) { + if (strcmp((const char *)(*sipp)->b_keyword_name, keyword)) + continue; + if ((*sipp)->dw_msb_value == 0) + continue; + + snprintf(scratch_buf, sizeof(scratch_buf), + "\t&%s, /* #%04u */\n", + (*sipp)->b_global_name, (unsigned int)c); + + do_write(output_file, scratch_buf); + } + + snprintf(scratch_buf, sizeof(scratch_buf), + "\t(const void *)0\n" + "};\n"); + + do_write(output_file, scratch_buf); +} diff --git a/sys/boot/usb/tools/sysinit.h b/sys/boot/usb/tools/sysinit.h new file mode 100644 index 00000000000..a7a450ee4cf --- /dev/null +++ b/sys/boot/usb/tools/sysinit.h @@ -0,0 +1,57 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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 _SYSINIT_H_ +#define _SYSINIT_H_ + +struct sysinit_data { + uint8_t b_keyword_name[64]; + uint8_t b_debug_info[128]; + uint8_t b_global_type[128]; + uint8_t b_global_name[128]; + uint8_t b_file_name[256]; + uint32_t dw_endian32; + uint32_t dw_msb_value; /* must be non-zero, else entry is skipped */ + uint32_t dw_lsb_value; + uint32_t dw_file_line; +} __attribute__((__packed__)); + +#define SYSINIT_ENTRY(uniq, keyword, msb, lsb, g_type, g_name, debug) \ + static const struct sysinit_data sysinit_##uniq \ + __attribute__((__section__(".debug.sysinit"), \ + __used__, __aligned__(1))) = { \ + .b_keyword_name = { keyword }, \ + .b_debug_info = { debug }, \ + .b_global_type = { g_type }, \ + .b_global_name = { g_name }, \ + .b_file_name = { __FILE__ }, \ + .dw_endian32 = 0x76543210U, \ + .dw_msb_value = (msb), \ + .dw_lsb_value = (lsb), \ + .dw_file_line = __LINE__ \ +} + +#endif /* _SYSINIT_H_ */ diff --git a/sys/boot/usb/usb_busdma_loader.c b/sys/boot/usb/usb_busdma_loader.c new file mode 100644 index 00000000000..c7d1ecb71d2 --- /dev/null +++ b/sys/boot/usb/usb_busdma_loader.c @@ -0,0 +1,619 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2013 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 + +#if USB_HAVE_BUSDMA +static void usb_pc_common_mem_cb(struct usb_page_cache *pc, + void *vaddr, uint32_t length); +#endif + +/*------------------------------------------------------------------------* + * usbd_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 +usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, + struct usb_page_search *res) +{ +#if USB_HAVE_BUSDMA + struct usb_page *page; + + 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 = (usb_size_t)-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 = (usb_size_t)-1; +#if USB_HAVE_BUSDMA + res->physaddr = 0; +#endif +} + +/*------------------------------------------------------------------------* + * usbd_copy_in - copy directly to DMA-able memory + *------------------------------------------------------------------------*/ +void +usbd_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) { + + usbd_get_page(cache, offset, &buf_res); + + if (buf_res.length > len) { + buf_res.length = len; + } + memcpy(buf_res.buffer, ptr, buf_res.length); + + offset += buf_res.length; + len -= buf_res.length; + ptr = USB_ADD_BYTES(ptr, buf_res.length); + } +} + +/*------------------------------------------------------------------------* + * usbd_copy_out - copy directly from DMA-able memory + *------------------------------------------------------------------------*/ +void +usbd_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) { + + usbd_get_page(cache, offset, &res); + + if (res.length > len) { + res.length = len; + } + memcpy(ptr, res.buffer, res.length); + + offset += res.length; + len -= res.length; + ptr = USB_ADD_BYTES(ptr, res.length); + } +} + +/*------------------------------------------------------------------------* + * usbd_frame_zero - zero DMA-able memory + *------------------------------------------------------------------------*/ +void +usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, + usb_frlength_t len) +{ + struct usb_page_search res; + + while (len != 0) { + + usbd_get_page(cache, offset, &res); + + if (res.length > len) { + res.length = len; + } + memset(res.buffer, 0, res.length); + + offset += res.length; + len -= res.length; + } +} + +#if USB_HAVE_BUSDMA + +/*------------------------------------------------------------------------* + * usb_pc_common_mem_cb - BUS-DMA callback function + *------------------------------------------------------------------------*/ +static void +usb_pc_common_mem_cb(struct usb_page_cache *pc, + void *vaddr, uint32_t length) +{ + struct usb_page *pg; + usb_size_t rem; + bus_size_t off; + bus_addr_t phys = (uintptr_t)vaddr; /* XXX */ + uint32_t nseg; + + if (length == 0) + nseg = 1; + else + nseg = ((length + USB_PAGE_SIZE - 1) / USB_PAGE_SIZE); + + pg = pc->page_start; + pg->physaddr = phys & ~(USB_PAGE_SIZE - 1); + rem = phys & (USB_PAGE_SIZE - 1); + pc->page_offset_buf = rem; + pc->page_offset_end += rem; + length += rem; + + for (off = USB_PAGE_SIZE; off < length; off += USB_PAGE_SIZE) { + pg++; + pg->physaddr = (phys + off) & ~(USB_PAGE_SIZE - 1); + } +} + +/*------------------------------------------------------------------------* + * usb_pc_alloc_mem - allocate DMA'able memory + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, + usb_size_t size, usb_size_t align) +{ + void *ptr; + uint32_t rem; + + /* allocate zeroed memory */ + + if (align != 1) { + ptr = usb_malloc(size + align); + if (ptr == NULL) + goto error; + + rem = (-((uintptr_t)ptr)) & (align - 1); + } else { + ptr = usb_malloc(size); + if (ptr == NULL) + goto error; + rem = 0; + } + + /* setup page cache */ + pc->buffer = ((uint8_t *)ptr) + rem; + pc->page_start = pg; + pc->page_offset_buf = 0; + pc->page_offset_end = size; + pc->map = NULL; + pc->tag = ptr; + pc->ismultiseg = (align == 1); + + /* compute physical address */ + usb_pc_common_mem_cb(pc, ptr, size); + + usb_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); +} + +/*------------------------------------------------------------------------* + * usb_pc_free_mem - free DMA memory + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb_pc_free_mem(struct usb_page_cache *pc) +{ + if (pc != NULL && pc->buffer != NULL) { + usb_free(pc->tag); + pc->buffer = NULL; + } +} + +/*------------------------------------------------------------------------* + * usb_pc_load_mem - load virtual memory into DMA + * + * Return values: + * 0: Success + * Else: Error + *------------------------------------------------------------------------*/ +uint8_t +usb_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) { + /* compute physical address */ + usb_pc_common_mem_cb(pc, pc->buffer, size); + } + if (sync == 0) { + /* + * Call callback so that refcount is decremented + * properly: + */ + pc->tag_parent->dma_error = 0; + (pc->tag_parent->func) (pc->tag_parent); + } + return (0); +} + +/*------------------------------------------------------------------------* + * usb_pc_cpu_invalidate - invalidate CPU cache + *------------------------------------------------------------------------*/ +void +usb_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; + } + /* NOP */ +} + +/*------------------------------------------------------------------------* + * usb_pc_cpu_flush - flush CPU cache + *------------------------------------------------------------------------*/ +void +usb_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; + } + /* NOP */ +} + +/*------------------------------------------------------------------------* + * usb_pc_dmamap_create - create a DMA map + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usb_pc_dmamap_create(struct usb_page_cache *pc, usb_size_t size) +{ + return (0); /* NOP, success */ +} + +/*------------------------------------------------------------------------* + * usb_pc_dmamap_destroy + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usb_pc_dmamap_destroy(struct usb_page_cache *pc) +{ + /* NOP */ +} + +/*------------------------------------------------------------------------* + * usb_dma_tag_setup - initialise USB DMA tags + *------------------------------------------------------------------------*/ +void +usb_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) +{ + memset(udpt, 0, sizeof(*udpt)); + + /* sanity checking */ + if ((nudt == 0) || + (ndmabits == 0) || + (mtx == NULL)) { + /* something is corrupt */ + return; + } + /* initialise condition variable */ + 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--) { + memset(udt, 0, sizeof(*udt)); + udt->tag_parent = udpt; + udt++; + } +} + +/*------------------------------------------------------------------------* + * usb_bus_tag_unsetup - factored out code + *------------------------------------------------------------------------*/ +void +usb_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--) { + udt->align = 0; + udt++; + } + + if (udpt->utag_max) { + /* destroy the condition variable */ + cv_destroy(udpt->cv); + } +} + +/*------------------------------------------------------------------------* + * usb_bdma_work_loop + * + * This function handles loading of virtual buffers into DMA and is + * only called when "dma_refcount" is zero. + *------------------------------------------------------------------------*/ +void +usb_bdma_work_loop(struct usb_xfer_queue *pq) +{ + struct usb_xfer_root *info; + struct usb_xfer *xfer; + usb_frcount_t nframes; + + xfer = pq->curr; + info = xfer->xroot; + + mtx_assert(info->xfer_mtx, MA_OWNED); + + if (xfer->error) { + /* some error happened */ + USB_BUS_LOCK(info->bus); + usbd_transfer_done(xfer, 0); + USB_BUS_UNLOCK(info->bus); + return; + } + if (!xfer->flags_int.bdma_setup) { + struct usb_page *pg; + usb_frlength_t frlength_0; + uint8_t isread; + + xfer->flags_int.bdma_setup = 1; + + /* reset BUS-DMA load state */ + + info->dma_error = 0; + + if (xfer->flags_int.isochronous_xfr) { + /* only one frame buffer */ + nframes = 1; + frlength_0 = xfer->sumlen; + } else { + /* can be multiple frame buffers */ + nframes = xfer->nframes; + frlength_0 = xfer->frlengths[0]; + } + + /* + * Set DMA direction first. This is needed to + * select the correct cache invalidate and cache + * flush operations. + */ + isread = USB_GET_DATA_ISREAD(xfer); + pg = xfer->dma_page_ptr; + + if (xfer->flags_int.control_xfr && + xfer->flags_int.control_hdr) { + /* special case */ + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { + /* The device controller writes to memory */ + xfer->frbuffers[0].isread = 1; + } else { + /* The host controller reads from memory */ + xfer->frbuffers[0].isread = 0; + } + } else { + /* default case */ + xfer->frbuffers[0].isread = isread; + } + + /* + * Setup the "page_start" pointer which points to an array of + * USB pages where information about the physical address of a + * page will be stored. Also initialise the "isread" field of + * the USB page caches. + */ + xfer->frbuffers[0].page_start = pg; + + info->dma_nframes = nframes; + info->dma_currframe = 0; + info->dma_frlength_0 = frlength_0; + + pg += (frlength_0 / USB_PAGE_SIZE); + pg += 2; + + while (--nframes > 0) { + xfer->frbuffers[nframes].isread = isread; + xfer->frbuffers[nframes].page_start = pg; + + pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE); + pg += 2; + } + + } + if (info->dma_error) { + USB_BUS_LOCK(info->bus); + usbd_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED); + USB_BUS_UNLOCK(info->bus); + return; + } + if (info->dma_currframe != info->dma_nframes) { + + if (info->dma_currframe == 0) { + /* special case */ + usb_pc_load_mem(xfer->frbuffers, + info->dma_frlength_0, 0); + } else { + /* default case */ + nframes = info->dma_currframe; + usb_pc_load_mem(xfer->frbuffers + nframes, + xfer->frlengths[nframes], 0); + } + + /* advance frame index */ + info->dma_currframe++; + + return; + } + /* go ahead */ + usb_bdma_pre_sync(xfer); + + /* start loading next USB transfer, if any */ + usb_command_wrapper(pq, NULL); + + /* finally start the hardware */ + usbd_pipe_enter(xfer); +} + +/*------------------------------------------------------------------------* + * usb_bdma_done_event + * + * This function is called when the BUS-DMA has loaded virtual memory + * into DMA, if any. + *------------------------------------------------------------------------*/ +void +usb_bdma_done_event(struct usb_dma_parent_tag *udpt) +{ + struct usb_xfer_root *info; + + info = USB_DMATAG_TO_XROOT(udpt); + + mtx_assert(info->xfer_mtx, MA_OWNED); + + /* copy error */ + info->dma_error = udpt->dma_error; + + /* enter workloop again */ + usb_command_wrapper(&info->dma_q, + info->dma_q.curr); +} + +/*------------------------------------------------------------------------* + * usb_bdma_pre_sync + * + * This function handles DMA synchronisation that must be done before + * an USB transfer is started. + *------------------------------------------------------------------------*/ +void +usb_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) { + usb_pc_cpu_invalidate(pc); + } else { + usb_pc_cpu_flush(pc); + } + pc++; + } +} + +/*------------------------------------------------------------------------* + * usb_bdma_post_sync + * + * This function handles DMA synchronisation that must be done after + * an USB transfer is complete. + *------------------------------------------------------------------------*/ +void +usb_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) { + usb_pc_cpu_invalidate(pc); + } + pc++; + } +} +#endif