Index: stable/4/share/man/man4/dcons.4 =================================================================== --- stable/4/share/man/man4/dcons.4 (nonexistent) +++ stable/4/share/man/man4/dcons.4 (revision 122530) @@ -0,0 +1,96 @@ +.\" Copyright (c) 2003 Hidetoshi Shimokawa +.\" 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 ``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 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. +.\" +.\" $FreeBSD$ +.\" +.\" +.Dd February 11, 2003 +.Dt DCONS 4 +.Os +.Sh NAME +.Nm dcons +.Nd dumb console device driver +.Sh SYNOPSIS +.Cd device dcons +.Pp +.Cd options DDB +.Cd options ALT_BREAK_TO_DEBUGGER +.Pp +.Cd device firewire +.Sh DESCRIPTION +The +.Nm +device is the simple console device which just reads from and writes to +an allocated buffer for input and output respectivly. +It is no use by itself and it is supposed that the buffer is accessed +via a bus like +.Xr FireWire 4 +or +.Xr kvm 3 +for interaction. +.Pp +The buffer consists of 4 channels. +There are 2 ports, one for console tty and other is GDB ports then each port +has a input channel and a output channel. +.Sh EXAMPLE +If you want to run +.Xr getty 8 +on dcons, insert following line into +.Xr /etc/ttys 5 +and +send a HUP signal to +.Xr init 8 +using +.Xr kill 1 . +.Bd -literal -offset indent +dcons "/usr/libexec/getty std.9600" vt100 on secure +.Ed +.Pp +Once +.Xr fwochi 4 +device is initialized to allow physical access, +the buffer can be accessed from another host via FireWire bus using +.Xr dconschat 8 +application. See +.Xr dconschat 8 +for more details. +.Pp +.Sh FILES +.Bl -tag -width indent -compact +.It Pa /dev/dcons +.It Pa /etc/ttys +.El +.Sh SEE ALSO +.Xr dcons_crom 4 , +.Xr ddb 4 , +.Xr firewire 4 , +.Xr fwohci 4 , +.Xr dconschat 8 , +.Xr fwcontrol 8 , +.Xr ttys 5 +.Sh AUTHORS +.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org +.Sh BUGS +This driver is still under development. +.Pp Property changes on: stable/4/share/man/man4/dcons.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/share/man/man4/dcons_crom.4 =================================================================== --- stable/4/share/man/man4/dcons_crom.4 (nonexistent) +++ stable/4/share/man/man4/dcons_crom.4 (revision 122530) @@ -0,0 +1,63 @@ +.\" Copyright (c) 2003 Hidetoshi Shimokawa +.\" 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 ``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 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. +.\" +.\" $FreeBSD$ +.\" +.\" +.Dd June 16, 2003 +.Dt DCONS_CROM 4 +.Os +.Sh NAME +.Nm dcons_crom +.Nd Configuration ROM stub for +.Xr dcons 4 +.Sh SYNOPSIS +.Cd device dcons_crom +.Cd device dcons +.Cd device firewire +.Pp +.Sh DESCRIPTION +The +.Nm +exposes buffer address of +.Xr dcons 4 +through Configuration ROM of +.Xr FireWire 4 . +This address is supposed to be used by +.Xr dconschat 8 . +.El +.Sh SEE ALSO +.Xr dcons 4 , +.Xr firewire 4 , +.Xr fwohci 4 , +.Xr dconschat 8 , +.Xr fwcontrol 8 . +.Sh AUTHORS +.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org +.Sh BUGS +If you load dcons_crom.ko manually after booted, you may have to initiate +a bus reset by +.Dq Nm fwcontrol -r +to update Configuration ROM. +.Pp Property changes on: stable/4/share/man/man4/dcons_crom.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/share/man/man4/sbp_targ.4 =================================================================== --- stable/4/share/man/man4/sbp_targ.4 (nonexistent) +++ stable/4/share/man/man4/sbp_targ.4 (revision 122530) @@ -0,0 +1,81 @@ +.\" Copyright (c) 2003 Hidetoshi Shimokawa +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the acknowledgement as bellow: +.\" +.\" This product includes software developed by H. Shimokawa +.\" +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Nov 7, 2003 +.Dt SBP_TARG 4 +.Os +.Sh NAME +.Nm sbp_targ +.Nd Serial Bus Protocol 2 (SBP-2) Target Mode devices driver +.Sh SYNOPSIS +.Cd "kldload firewire" +.Cd "kldload cam" +.Cd "kldload sbp_targ" +.Pp +or +.Pp +.Cd "device sbp_targ" +.Cd "device firewire" +.Cd "device scbus" +.Cd "device targ" +.Sh DESCRIPTION +The +.Nm +driver provides support for SBP-2 target mode. +This driver is supposed to work with cam(4), targ(4) and firewire(4). +You also need to use scsi_target(8), which can be found in +/usr/share/example/scsi_target, to provide actual devices. +.Sh EXAMPLE +.Bd -literal -offset indent +# mdconfig -a -t malloc -s 10m +md0 +# scsi_target 0:0:0 /dev/md0 +(Assuming sbp_targ0 on scbus0) +.Ed +.Sh SEE ALSO +.Xr cam 4 , +.Xr firewire 4 , +.Xr targ 4 , +.Xr camcontrol 8 , +.Xr fwcontrol 8 , +.Xr kldload 8 , +.Xr scsi_target 8 , +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Hidetoshi Shimokawa . +.Sh BUGS +This driver is still under development. +This doesn't work correctly in multi-initiator environment or after the bus topology has been changed. Property changes on: stable/4/share/man/man4/sbp_targ.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/share/man/man4/uaudio.4 =================================================================== --- stable/4/share/man/man4/uaudio.4 (nonexistent) +++ stable/4/share/man/man4/uaudio.4 (revision 122530) @@ -0,0 +1,96 @@ +.\" $FreeBSD$ +.\" $NetBSD: uaudio.4,v 1.15 2002/02/12 19:53:57 jdolecek Exp $ +.\" +.\" Copyright (c) 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Lennart Augustsson. +.\" +.\" 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. +.\" +.Dd November 10, 2003 +.Dt UAUDIO 4 +.Os +.Sh NAME +.Nm uaudio +.Nd USB audio device driver +.Sh SYNOPSIS +.Cd "device pcm" +.Cd "device usb" +.Sh DESCRIPTION +The +.Nm +driver provides support for +.Tn USB +audio class devices. +.Pp +A +.Tn USB +audio device consists of a number of components: +input terminals (e.g. USB digital input), output terminals (e.g. +speakers), and a number of units in between (e.g. volume control). +.Pp +Refer to the +.Ql USB Audio Class Specification +for more information. +.Sh SEE ALSO +.Xr pcm 4 , +.Xr usb 4 +.Rs +.%T "USB Audio Class Specifications" +.%O http://www.usb.org/developers/devclass_docs/ +.Re +.Sh HISTORY +The +.Nm +driver first appeared in +appeared in +.Fx 4.7 . +.Sh AUTHORS +This manual page was adopted from +.Nx 1.6 +and modified for +.Fx +by +.An Hiten Pandya Aq hmp@FreeBSD.org . +.Sh BUGS +The +.Tn PCM +framework in +.Fx , +as of this writing, does not handle device un-registrations in a properly +abstracted manner, i.e. a detach request is refused by the +.Tn PCM +framework if the device is in use. +For +.Tn USB +and supposedly other detach-able busses, it is necessary to allow the +device un-registration to complete successfully, otherwise the driver +leaves wild pointers to invalid data structures and thus leading to a panic. Property changes on: stable/4/share/man/man4/uaudio.4 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/dev/dcons/dcons_crom.c =================================================================== --- stable/4/sys/dev/dcons/dcons_crom.c (nonexistent) +++ stable/4/sys/dev/dcons/dcons_crom.c (revision 122530) @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2003 + * Hidetoshi Shimokawa. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Hidetoshi Shimokawa. + * + * 4. Neither the name of the author 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 REGENTS 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 REGENTS 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. + * + * $Id: dcons_crom.c,v 1.8 2003/10/23 15:47:21 simokawa Exp $ + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +static bus_addr_t dcons_paddr; + +#ifndef CSRVAL_VENDOR_PRIVATE +#define NEED_NEW_DRIVER +#endif + +#define ADDR_HI(x) (((x) >> 24) & 0xffffff) +#define ADDR_LO(x) ((x) & 0xffffff) + +struct dcons_crom_softc { + struct firewire_dev_comm fd; + struct crom_chunk unit; + struct crom_chunk spec; + struct crom_chunk ver; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t bus_addr; +}; + +static void +dcons_crom_identify(driver_t *driver, device_t parent) +{ + BUS_ADD_CHILD(parent, 0, "dcons_crom", device_get_unit(parent)); +} + +static int +dcons_crom_probe(device_t dev) +{ + device_t pa; + + pa = device_get_parent(dev); + if(device_get_unit(dev) != device_get_unit(pa)){ + return(ENXIO); + } + + device_set_desc(dev, "dcons configuration ROM"); + return (0); +} + +#ifndef NEED_NEW_DRIVER +static void +dcons_crom_post_busreset(void *arg) +{ + struct dcons_crom_softc *sc; + struct crom_src *src; + struct crom_chunk *root; + + sc = (struct dcons_crom_softc *) arg; + src = sc->fd.fc->crom_src; + root = sc->fd.fc->crom_root; + + bzero(&sc->unit, sizeof(struct crom_chunk)); + + crom_add_chunk(src, root, &sc->unit, CROM_UDIR); + crom_add_entry(&sc->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE); + crom_add_simple_text(src, &sc->unit, &sc->spec, "FreeBSD"); + crom_add_entry(&sc->unit, CSRKEY_VER, DCONS_CSR_VAL_VER); + crom_add_simple_text(src, &sc->unit, &sc->ver, "dcons"); + crom_add_entry(&sc->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr)); + crom_add_entry(&sc->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr)); +} +#endif + +static void +dmamap_cb(void *arg, bus_dma_segment_t *segments, int seg, int error) +{ + struct dcons_crom_softc *sc; + + if (error) + printf("dcons_dmamap_cb: error=%d\n", error); + + sc = (struct dcons_crom_softc *)arg; + sc->bus_addr = segments[0].ds_addr; + + bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_PREWRITE); + device_printf(sc->fd.dev, +#if __FreeBSD_version < 500000 + "bus_addr 0x%x\n", sc->bus_addr); +#else + "bus_addr 0x%jx\n", (uintmax_t)sc->bus_addr); +#endif + if (dcons_paddr != 0) { + /* XXX */ + device_printf(sc->fd.dev, "dcons_paddr is already set\n"); + return; + } + dcons_dma_tag = sc->dma_tag; + dcons_dma_map = sc->dma_map; + dcons_paddr = sc->bus_addr; +} + +static int +dcons_crom_attach(device_t dev) +{ +#ifdef NEED_NEW_DRIVER + printf("dcons_crom: you need newer firewire driver\n"); + return (-1); +#else + struct dcons_crom_softc *sc; + + sc = (struct dcons_crom_softc *) device_get_softc(dev); + sc->fd.fc = device_get_ivars(dev); + sc->fd.dev = dev; + sc->fd.post_explore = NULL; + sc->fd.post_busreset = (void *) dcons_crom_post_busreset; + + /* map dcons buffer */ + bus_dma_tag_create( + /*parent*/ sc->fd.fc->dmat, + /*alignment*/ sizeof(u_int32_t), + /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ dcons_bufsize, + /*nsegments*/ 1, + /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, + /*flags*/ BUS_DMA_ALLOCNOW, +#if __FreeBSD_version >= 501102 + /*lockfunc*/busdma_lock_mutex, + /*lockarg*/&Giant, +#endif + &sc->dma_tag); + bus_dmamap_create(sc->dma_tag, 0, &sc->dma_map); + bus_dmamap_load(sc->dma_tag, sc->dma_map, + (void *)dcons_buf, dcons_bufsize, + dmamap_cb, sc, 0); + return (0); +#endif +} + +static int +dcons_crom_detach(device_t dev) +{ + struct dcons_crom_softc *sc; + + sc = (struct dcons_crom_softc *) device_get_softc(dev); + sc->fd.post_busreset = NULL; + + /* XXX */ + if (dcons_dma_tag == sc->dma_tag) + dcons_dma_tag = NULL; + + bus_dmamap_unload(sc->dma_tag, sc->dma_map); + bus_dmamap_destroy(sc->dma_tag, sc->dma_map); + bus_dma_tag_destroy(sc->dma_tag); + + return 0; +} + +static devclass_t dcons_crom_devclass; + +static device_method_t dcons_crom_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, dcons_crom_identify), + DEVMETHOD(device_probe, dcons_crom_probe), + DEVMETHOD(device_attach, dcons_crom_attach), + DEVMETHOD(device_detach, dcons_crom_detach), + { 0, 0 } +}; + +static driver_t dcons_crom_driver = { + "dcons_crom", + dcons_crom_methods, + sizeof(struct dcons_crom_softc), +}; + +DRIVER_MODULE(dcons_crom, firewire, dcons_crom_driver, + dcons_crom_devclass, 0, 0); +MODULE_VERSION(dcons_crom, 1); +MODULE_DEPEND(dcons_crom, dcons, + DCONS_VERSION, DCONS_VERSION, DCONS_VERSION); +MODULE_DEPEND(dcons_crom, firewire, 1, 1, 1); Property changes on: stable/4/sys/dev/dcons/dcons_crom.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/dev/dcons/dcons.c =================================================================== --- stable/4/sys/dev/dcons/dcons.c (nonexistent) +++ stable/4/sys/dev/dcons/dcons.c (revision 122530) @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2003 + * Hidetoshi Shimokawa. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Hidetoshi Shimokawa. + * + * 4. Neither the name of the author 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 REGENTS 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 REGENTS 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. + * + * $Id: dcons.c,v 1.65 2003/10/24 03:24:55 simokawa Exp $ + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +#include "opt_ddb.h" +#include "opt_comconsole.h" +#include "opt_dcons.h" + +#ifndef DCONS_POLL_HZ +#define DCONS_POLL_HZ 100 +#endif + +#ifndef DCONS_BUF_SIZE +#define DCONS_BUF_SIZE (16*1024) +#endif + +#ifndef DCONS_FORCE_CONSOLE +#define DCONS_FORCE_CONSOLE 0 /* mostly for FreeBSD-4 */ +#endif + +#ifndef DCONS_FORCE_GDB +#define DCONS_FORCE_GDB 1 +#endif + +#if __FreeBSD_version >= 500101 +#define CONS_NODEV 1 /* for latest current */ +static struct consdev gdbconsdev; +#endif + +#define CDEV_MAJOR 184 + +static d_open_t dcons_open; +static d_close_t dcons_close; +static d_ioctl_t dcons_ioctl; + +static struct cdevsw dcons_cdevsw = { +#if __FreeBSD_version >= 500104 + .d_open = dcons_open, + .d_close = dcons_close, + .d_read = ttyread, + .d_write = ttywrite, + .d_ioctl = dcons_ioctl, + .d_poll = ttypoll, + .d_name = "dcons", + .d_maj = CDEV_MAJOR, +#else + /* open */ dcons_open, + /* close */ dcons_close, + /* read */ ttyread, + /* write */ ttywrite, + /* ioctl */ dcons_ioctl, + /* poll */ ttypoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "dcons", + /* major */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +#endif +}; + +#ifndef KLD_MODULE +static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */ +#endif +struct dcons_buf *dcons_buf; +size_t dcons_bufsize; +bus_dma_tag_t dcons_dma_tag = NULL; +bus_dmamap_t dcons_dma_map = NULL; + +static int poll_hz = DCONS_POLL_HZ; +SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console"); +SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0, + "dcons polling rate"); + +static int drv_init = 0; +static struct callout dcons_callout; + +/* per device data */ +static struct dcons_softc { + dev_t dev; + struct dcons_ch o, i; + int brk_state; +#define DC_GDB 1 + int flags; +} sc[DCONS_NPORT]; +static void dcons_tty_start(struct tty *); +static int dcons_tty_param(struct tty *, struct termios *); +static void dcons_timeout(void *); +static int dcons_drv_init(int); +static int dcons_getc(struct dcons_softc *); +static int dcons_checkc(struct dcons_softc *); +static void dcons_putc(struct dcons_softc *, int); + +static cn_probe_t dcons_cnprobe; +static cn_init_t dcons_cninit; +static cn_getc_t dcons_cngetc; +static cn_checkc_t dcons_cncheckc; +static cn_putc_t dcons_cnputc; + +CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc, + dcons_cncheckc, dcons_cnputc, NULL); + +#if __FreeBSD_version < 500000 +#define THREAD proc +#else +#define THREAD thread +#endif + +static int +dcons_open(dev_t dev, int flag, int mode, struct THREAD *td) +{ + struct tty *tp; + int unit, error, s; + + unit = minor(dev); + if (unit != 0) + return (ENXIO); + + tp = dev->si_tty = ttymalloc(dev->si_tty); + tp->t_oproc = dcons_tty_start; + tp->t_param = dcons_tty_param; + tp->t_stop = nottystop; + tp->t_dev = dev; + + error = 0; + + s = spltty(); + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_CARR_ON; + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG|CLOCAL; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + ttsetwater(tp); + } else if ((tp->t_state & TS_XCLUDE) && suser(td)) { + splx(s); + return (EBUSY); + } + splx(s); + + error = (*linesw[tp->t_line].l_open)(dev, tp); + + return (error); +} + +static int +dcons_close(dev_t dev, int flag, int mode, struct THREAD *td) +{ + int unit; + struct tty *tp; + + unit = minor(dev); + if (unit != 0) + return (ENXIO); + + tp = dev->si_tty; + if (tp->t_state & TS_ISOPEN) { + (*linesw[tp->t_line].l_close)(tp, flag); + ttyclose(tp); + } + + return (0); +} + +static int +dcons_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct THREAD *td) +{ + int unit; + struct tty *tp; + int error; + + unit = minor(dev); + if (unit != 0) + return (ENXIO); + + tp = dev->si_tty; + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); + if (error != ENOIOCTL) + return (error); + + error = ttioctl(tp, cmd, data, flag); + if (error != ENOIOCTL) + return (error); + + return (ENOTTY); +} + +static int +dcons_tty_param(struct tty *tp, struct termios *t) +{ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + return 0; +} + +static void +dcons_tty_start(struct tty *tp) +{ + struct dcons_softc *dc; + int s; + + dc = (struct dcons_softc *)tp->t_dev->si_drv1; + s = spltty(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { + ttwwakeup(tp); + return; + } + + tp->t_state |= TS_BUSY; + while (tp->t_outq.c_cc != 0) + dcons_putc(dc, getc(&tp->t_outq)); + tp->t_state &= ~TS_BUSY; + + ttwwakeup(tp); + splx(s); +} + +static void +dcons_timeout(void *v) +{ + struct tty *tp; + struct dcons_softc *dc; + int i, c, polltime; + + for (i = 0; i < DCONS_NPORT; i ++) { + dc = &sc[i]; + tp = dc->dev->si_tty; + while ((c = dcons_checkc(dc)) != -1) + if (tp->t_state & TS_ISOPEN) + (*linesw[tp->t_line].l_rint)(c, tp); + } + polltime = hz / poll_hz; + if (polltime < 1) + polltime = 1; + callout_reset(&dcons_callout, polltime, dcons_timeout, tp); +} + +static void +dcons_cnprobe(struct consdev *cp) +{ +#if __FreeBSD_version >= 501109 + sprintf(cp->cn_name, "dcons"); +#else + cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON); +#endif +#if DCONS_FORCE_CONSOLE + cp->cn_pri = CN_REMOTE; +#else + cp->cn_pri = CN_NORMAL; +#endif +} + +static void +dcons_cninit(struct consdev *cp) +{ + dcons_drv_init(0); +#if CONS_NODEV + cp->cn_arg +#else + cp->cn_dev->si_drv1 +#endif + = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */ +} + +#if CONS_NODEV +static int +dcons_cngetc(struct consdev *cp) +{ + return(dcons_getc((struct dcons_softc *)cp->cn_arg)); +} +static int +dcons_cncheckc(struct consdev *cp) +{ + return(dcons_checkc((struct dcons_softc *)cp->cn_arg)); +} +static void +dcons_cnputc(struct consdev *cp, int c) +{ + dcons_putc((struct dcons_softc *)cp->cn_arg, c); +} +#else +static int +dcons_cngetc(dev_t dev) +{ + return(dcons_getc((struct dcons_softc *)dev->si_drv1)); +} +static int +dcons_cncheckc(dev_t dev) +{ + return(dcons_checkc((struct dcons_softc *)dev->si_drv1)); +} +static void +dcons_cnputc(dev_t dev, int c) +{ + dcons_putc((struct dcons_softc *)dev->si_drv1, c); +} +#endif + +static int +dcons_getc(struct dcons_softc *dc) +{ + int c; + + while ((c = dcons_checkc(dc)) == -1); + + return (c & 0xff); +} + +static int +dcons_checkc(struct dcons_softc *dc) +{ + unsigned char c; + u_int32_t ptr, pos, gen, next_gen; + struct dcons_ch *ch; + + ch = &dc->i; + + if (dcons_dma_tag != NULL) + bus_dmamap_sync(dcons_dma_tag, dcons_dma_map, + BUS_DMASYNC_POSTREAD); + ptr = ntohl(*ch->ptr); + gen = ptr >> DCONS_GEN_SHIFT; + pos = ptr & DCONS_POS_MASK; + if (gen == ch->gen && pos == ch->pos) + return (-1); + + next_gen = DCONS_NEXT_GEN(ch->gen); + /* XXX sanity check */ + if ((gen != ch->gen && gen != next_gen) + || (gen == ch->gen && pos < ch->pos)) { + /* generation skipped !! */ + /* XXX discard */ + ch->gen = gen; + ch->pos = pos; + return (-1); + } + + c = ch->buf[ch->pos]; + ch->pos ++; + if (ch->pos >= ch->size) { + ch->gen = next_gen; + ch->pos = 0; + } + +#if DDB && ALT_BREAK_TO_DEBUGGER + switch (dc->brk_state) { + case STATE1: + if (c == KEY_TILDE) + dc->brk_state = STATE2; + else + dc->brk_state = STATE0; + break; + case STATE2: + dc->brk_state = STATE0; + if (c == KEY_CTRLB) { +#if DCONS_FORCE_GDB + if (dc->flags & DC_GDB) + boothowto |= RB_GDB; +#endif + breakpoint(); + } + } + if (c == KEY_CR) + dc->brk_state = STATE1; +#endif + return (c); +} + +static void +dcons_putc(struct dcons_softc *dc, int c) +{ + struct dcons_ch *ch; + + ch = &dc->o; + + ch->buf[ch->pos] = c; + ch->pos ++; + if (ch->pos >= ch->size) { + ch->gen = DCONS_NEXT_GEN(ch->gen); + ch->pos = 0; + } + *ch->ptr = DCONS_MAKE_PTR(ch); + if (dcons_dma_tag != NULL) + bus_dmamap_sync(dcons_dma_tag, dcons_dma_map, + BUS_DMASYNC_PREWRITE); +} + +static int +dcons_init_port(int port, int offset, int size) +{ + int osize; + struct dcons_softc *dc; + + dc = &sc[port]; + + osize = size * 3 / 4; + + dc->o.size = osize; + dc->i.size = size - osize; + dc->o.buf = (char *)dcons_buf + offset; + dc->i.buf = dc->o.buf + osize; + dc->o.gen = dc->i.gen = 0; + dc->o.pos = dc->i.pos = 0; + dc->o.ptr = &dcons_buf->optr[port]; + dc->i.ptr = &dcons_buf->iptr[port]; + dc->brk_state = STATE0; + dcons_buf->osize[port] = htonl(osize); + dcons_buf->isize[port] = htonl(size - osize); + dcons_buf->ooffset[port] = htonl(offset); + dcons_buf->ioffset[port] = htonl(offset + osize); + dcons_buf->optr[port] = DCONS_MAKE_PTR(&dc->o); + dcons_buf->iptr[port] = DCONS_MAKE_PTR(&dc->i); + + return(0); +} + +static int +dcons_drv_init(int stage) +{ + int size, size0, offset; + + if (drv_init) + return(drv_init); + + drv_init = -1; + + dcons_bufsize = DCONS_BUF_SIZE; + +#ifndef KLD_MODULE + if (stage == 0) /* XXX or cold */ + /* + * DCONS_FORCE_CONSOLE == 1 and statically linked. + * called from cninit(). can't use contigmalloc yet . + */ + dcons_buf = (struct dcons_buf *) bssbuf; + else +#endif + /* + * DCONS_FORCE_CONSOLE == 0 or kernel module case. + * if the module is loaded after boot, + * dcons_buf could be non-continuous. + */ + dcons_buf = (struct dcons_buf *) contigmalloc(dcons_bufsize, + M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul); + + offset = DCONS_HEADER_SIZE; + size = (dcons_bufsize - offset); + size0 = size * 3 / 4; + + dcons_init_port(0, offset, size0); + offset += size0; + dcons_init_port(1, offset, size - size0); + dcons_buf->version = htonl(DCONS_VERSION); + dcons_buf->magic = ntohl(DCONS_MAGIC); + +#if DDB && DCONS_FORCE_GDB +#if CONS_NODEV + gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB]; +#if __FreeBSD_version >= 501109 + sprintf(gdbconsdev.cn_name, "dgdb"); +#endif + gdb_arg = &gdbconsdev; +#else + gdbdev = makedev(CDEV_MAJOR, DCONS_GDB); +#endif + gdb_getc = dcons_cngetc; + gdb_putc = dcons_cnputc; +#endif + drv_init = 1; + + return 0; +} + + +static int +dcons_attach_port(int port, char *name, int flags) +{ + struct dcons_softc *dc; + struct tty *tp; + + dc = &sc[port]; + dc->flags = flags; + dc->dev = make_dev(&dcons_cdevsw, port, + UID_ROOT, GID_WHEEL, 0600, name); + tp = ttymalloc(NULL); + + dc->dev->si_drv1 = (void *)dc; + dc->dev->si_tty = tp; + + tp->t_oproc = dcons_tty_start; + tp->t_param = dcons_tty_param; + tp->t_stop = nottystop; + tp->t_dev = dc->dev; + + return(0); +} + +static int +dcons_attach(void) +{ + int polltime; + + dcons_attach_port(DCONS_CON, "dcons", 0); + dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB); +#if __FreeBSD_version < 500000 + callout_init(&dcons_callout); +#else + callout_init(&dcons_callout, 0); +#endif + polltime = hz / poll_hz; + if (polltime < 1) + polltime = 1; + callout_reset(&dcons_callout, polltime, dcons_timeout, NULL); + return(0); +} + +static int +dcons_detach(int port) +{ + struct tty *tp; + struct dcons_softc *dc; + + dc = &sc[port]; + + tp = dc->dev->si_tty; + + if (tp->t_state & TS_ISOPEN) { + printf("dcons: still opened\n"); + (*linesw[tp->t_line].l_close)(tp, 0); + tp->t_gen++; + ttyclose(tp); + ttwakeup(tp); + ttwwakeup(tp); + } + /* XXX + * must wait until all device are closed. + */ + tsleep((void *)dc, PWAIT, "dcodtc", hz/4); + destroy_dev(dc->dev); + + return(0); +} + + +/* cnXXX works only for FreeBSD-5 */ +static int +dcons_modevent(module_t mode, int type, void *data) +{ + int err = 0, ret; + + switch (type) { + case MOD_LOAD: + ret = dcons_drv_init(1); + dcons_attach(); +#if __FreeBSD_version >= 500000 + if (ret == 0) { + dcons_cnprobe(&dcons_consdev); + dcons_cninit(&dcons_consdev); + cnadd(&dcons_consdev); + } +#endif + break; + case MOD_UNLOAD: + printf("dcons: unload\n"); + callout_stop(&dcons_callout); +#if DDB && DCONS_FORCE_GDB +#if CONS_NODEV + gdb_arg = NULL; +#else + gdbdev = NODEV; +#endif +#endif +#if __FreeBSD_version >= 500000 + cnremove(&dcons_consdev); +#endif + dcons_detach(DCONS_CON); + dcons_detach(DCONS_GDB); + dcons_buf->magic = 0; + + contigfree(dcons_buf, DCONS_BUF_SIZE, M_DEVBUF); + + break; + case MOD_SHUTDOWN: + break; + } + return(err); +} + +DEV_MODULE(dcons, dcons_modevent, NULL); +MODULE_VERSION(dcons, DCONS_VERSION); Property changes on: stable/4/sys/dev/dcons/dcons.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/dev/dcons/dcons.h =================================================================== --- stable/4/sys/dev/dcons/dcons.h (nonexistent) +++ stable/4/sys/dev/dcons/dcons.h (revision 122530) @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2002 + * Hidetoshi Shimokawa. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Hidetoshi Shimokawa. + * + * 4. Neither the name of the author 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 REGENTS 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 REGENTS 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. + * + * $Id: dcons.h,v 1.15 2003/10/23 15:05:31 simokawa Exp $ + * $FreeBSD$ + */ + +#ifdef _KERNEL +#define V volatile +#else +#define V +#endif + +#define DCONS_NPORT 2 +#define DCONS_CON 0 +#define DCONS_GDB 1 + +struct dcons_buf { +#define DCONS_VERSION 2 + V u_int32_t version; + V u_int32_t ooffset[DCONS_NPORT]; + V u_int32_t ioffset[DCONS_NPORT]; + V u_int32_t osize[DCONS_NPORT]; + V u_int32_t isize[DCONS_NPORT]; +#define DCONS_MAGIC 0x64636f6e /* "dcon" */ + V u_int32_t magic; +#define DCONS_GEN_SHIFT (24) +#define DCONS_GEN_MASK (0xff) +#define DCONS_POS_MASK ((1<< DCONS_GEN_SHIFT) - 1) + V u_int32_t optr[DCONS_NPORT]; + V u_int32_t iptr[DCONS_NPORT]; + V char buf[0]; +}; + +#define DCONS_CSR_VAL_VER 0x64636f /* "dco" */ +#define DCONS_CSR_KEY_HI 0x3a +#define DCONS_CSR_KEY_LO 0x3b + +#define DCONS_HEADER_SIZE sizeof(struct dcons_buf) +#define DCONS_MAKE_PTR(x) htonl(((x)->gen << DCONS_GEN_SHIFT) | (x)->pos) +#define DCONS_NEXT_GEN(x) (((x) + 1) & DCONS_GEN_MASK) + +struct dcons_ch { + u_int32_t size; + u_int32_t gen; + u_int32_t pos; +#ifdef _KERNEL + V u_int32_t *ptr; + V char *buf; +#else + off_t buf; +#endif +}; + +#define KEY_CTRLB 2 /* ^B */ +#define KEY_CR 13 /* CR '\r' */ +#define KEY_TILDE 126 /* ~ */ +#define STATE0 0 +#define STATE1 1 +#define STATE2 2 + +#ifdef _KERNEL +extern struct dcons_buf *dcons_buf; +extern size_t dcons_bufsize; +extern bus_dma_tag_t dcons_dma_tag; +extern bus_dmamap_t dcons_dma_map; +#endif Property changes on: stable/4/sys/dev/dcons/dcons.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/dev/firewire/sbp.h =================================================================== --- stable/4/sys/dev/firewire/sbp.h (nonexistent) +++ stable/4/sys/dev/firewire/sbp.h (revision 122530) @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2003 Hidetoshi Shimokawa + * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the acknowledgement as bellow: + * + * This product includes software developed by K. Kobayashi and H. Shimokawa + * + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + * $FreeBSD$ + * + */ + +#define ORB_NOTIFY (1 << 31) +#define ORB_FMT_STD (0 << 29) +#define ORB_FMT_VED (2 << 29) +#define ORB_FMT_NOP (3 << 29) +#define ORB_FMT_MSK (3 << 29) +#define ORB_EXV (1 << 28) +/* */ +#define ORB_CMD_IN (1 << 27) +/* */ +#define ORB_CMD_SPD(x) ((x) << 24) +#define ORB_CMD_MAXP(x) ((x) << 20) +#define ORB_RCN_TMO(x) ((x) << 20) +#define ORB_CMD_PTBL (1 << 19) +#define ORB_CMD_PSZ(x) ((x) << 16) + +#define ORB_FUN_LGI (0 << 16) +#define ORB_FUN_QLG (1 << 16) +#define ORB_FUN_RCN (3 << 16) +#define ORB_FUN_LGO (7 << 16) +#define ORB_FUN_ATA (0xb << 16) +#define ORB_FUN_ATS (0xc << 16) +#define ORB_FUN_LUR (0xe << 16) +#define ORB_FUN_RST (0xf << 16) +#define ORB_FUN_MSK (0xf << 16) +#define ORB_FUN_RUNQUEUE 0xffff + +#define ORB_RES_CMPL 0 +#define ORB_RES_FAIL 1 +#define ORB_RES_ILLE 2 +#define ORB_RES_VEND 3 + +#define SBP_DEBUG(x) if (debug > x) { +#define END_DEBUG } + +struct ind_ptr { + u_int32_t hi,lo; +}; + + +#define SBP_RECV_LEN 32 + +struct sbp_login_res{ + u_int16_t len; + u_int16_t id; + u_int16_t res0; + u_int16_t cmd_hi; + u_int32_t cmd_lo; + u_int16_t res1; + u_int16_t recon_hold; +}; + +struct sbp_status{ +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t src:2, + resp:2, + dead:1, + len:3; +#else + u_int8_t len:3, + dead:1, + resp:2, + src:2; +#endif + u_int8_t status; + u_int16_t orb_hi; + u_int32_t orb_lo; + u_int32_t data[6]; +}; +/* src */ +#define SRC_NEXT_EXISTS 0 +#define SRC_NO_NEXT 1 +#define SRC_UNSOL 2 + +/* resp */ +#define SBP_REQ_CMP 0 /* request complete */ +#define SBP_TRANS_FAIL 1 /* transport failure */ +#define SBP_ILLE_REQ 2 /* illegal request */ +#define SBP_VEND_DEP 3 /* vendor dependent */ + +/* status (resp == 0) */ +/* 0: No additional Information to report */ +/* 1: Request Type not supported */ +/* 2: Speed not supported */ +/* 3: Page size not supported */ +/* 4: Access denied */ +#define STATUS_ACCESS_DENY 4 +/* 5: Logical unit not supported */ +/* 6: Maximum payload too small */ +/* 7: Reserved for future standardization */ +/* 8: Resource unavailabe */ +/* 9: Function Rejected */ +/* 10: Login ID not recognized */ +/* 11: Dummy ORB completed */ +/* 12: Request aborted */ +/* 255: Unspecified error */ + +/* status (resp == 1) */ +/* Referenced object */ +#define OBJ_ORB (0 << 6) /* 0: ORB */ +#define OBJ_DATA (1 << 6) /* 1: Data buffer */ +#define OBJ_PT (2 << 6) /* 2: Page table */ +#define OBJ_UNSPEC (3 << 6) /* 3: Unable to specify */ +/* Serial bus error */ +/* 0: Missing acknowledge */ +/* 1: Reserved; not to be used */ +/* 2: Time-out error */ +#define SBE_TIMEOUT 2 +/* 3: Reserved; not to be used */ +/* 4: Busy retry limit exceeded: ack_busy_X */ +/* 5: Busy retry limit exceeded: ack_busy_A */ +/* 6: Busy retry limit exceeded: ack_busy_B */ +/* 7-A: Reserved for future standardization */ +/* B: Tardy retry limit exceeded */ +/* C: Confilict error */ +/* D: Data error */ +/* E: Type error */ +/* F: Address error */ + + +struct sbp_cmd_status{ +#define SBP_SFMT_CURR 0 +#define SBP_SFMT_DEFER 1 +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t sfmt:2, + status:6; + u_int8_t valid:1, + mark:1, + eom:1, + ill_len:1, + s_key:4; +#else + u_int8_t status:6, + sfmt:2; + u_int8_t s_key:4, + ill_len:1, + eom:1, + mark:1, + valid:1; +#endif + u_int8_t s_code; + u_int8_t s_qlfr; + u_int32_t info; + u_int32_t cdb; + u_int8_t fru; + u_int8_t s_keydep[3]; + u_int32_t vend[2]; +}; + +#define ORB_FUN_NAMES \ + /* 0 */ "LOGIN", \ + /* 1 */ "QUERY LOGINS", \ + /* 2 */ "Reserved", \ + /* 3 */ "RECONNECT", \ + /* 4 */ "SET PASSWORD", \ + /* 5 */ "Reserved", \ + /* 6 */ "Reserved", \ + /* 7 */ "LOGOUT", \ + /* 8 */ "Reserved", \ + /* 9 */ "Reserved", \ + /* A */ "Reserved", \ + /* B */ "ABORT TASK", \ + /* C */ "ABORT TASK SET", \ + /* D */ "Reserved", \ + /* E */ "LOGICAL UNIT RESET", \ + /* F */ "TARGET RESET" Property changes on: stable/4/sys/dev/firewire/sbp.h ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/dev/firewire/sbp_targ.c =================================================================== --- stable/4/sys/dev/firewire/sbp_targ.c (nonexistent) +++ stable/4/sys/dev/firewire/sbp_targ.c (revision 122530) @@ -0,0 +1,1550 @@ +/* + * Copyright (C) 2003 + * Hidetoshi Shimokawa. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Hidetoshi Shimokawa. + * + * 4. Neither the name of the author 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 REGENTS 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 REGENTS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#if __FreeBSD_version < 500000 +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SBP_TARG_RECV_LEN (8) +#define MAX_LUN 63 +/* + * management/command block agent registers + * + * BASE 0xffff f001 0000 management port + * BASE 0xffff f001 0020 command port for lun0 + * BASE 0xffff f001 0040 command port for lun1 + * + */ +#define SBP_TARG_MGM 0x10000 /* offset from 0xffff f000 000 */ +#define SBP_TARG_BIND_HI 0xffff +#define SBP_TARG_BIND_LO(l) (0xf0000000 + SBP_TARG_MGM + 0x20 * ((l) + 1)) +#define SBP_TARG_BIND_START (((u_int64_t)SBP_TARG_BIND_HI << 32) | \ + SBP_TARG_BIND_LO(-1)) +#define SBP_TARG_BIND_END (((u_int64_t)SBP_TARG_BIND_HI << 32) | \ + SBP_TARG_BIND_LO(MAX_LUN)) +#define SBP_TARG_LUN(lo) (((lo) - SBP_TARG_BIND_LO(0))/0x20) + +#define FETCH_MGM 0 +#define FETCH_CMD 1 +#define FETCH_POINTER 2 + +MALLOC_DEFINE(M_SBP_TARG, "sbp_targ", "SBP-II/FireWire target mode"); + +static int debug = 0; + +SYSCTL_INT(_debug, OID_AUTO, sbp_targ_debug, CTLFLAG_RW, &debug, 0, + "SBP target mode debug flag"); + +struct sbp_targ_softc { + struct firewire_dev_comm fd; + struct cam_sim *sim; + struct cam_path *path; + struct fw_bind fwb; + int ndevs; + struct crom_chunk unit; + struct sbp_targ_lstate *lstate[MAX_LUN]; + struct sbp_targ_lstate *black_hole; +}; + +struct sbp_targ_lstate { + struct sbp_targ_softc *sc; + struct cam_path *path; + struct ccb_hdr_slist accept_tios; + struct ccb_hdr_slist immed_notifies; + struct crom_chunk model; + /* XXX per initiater data */ + struct fw_device *fwdev; + struct sbp_login_res loginres; + u_int32_t flags; +#define LINK_ACTIVE 1 +#define ATIO_STARVED 2 + u_int16_t fifo_hi; + u_int16_t last_hi; + u_int32_t fifo_lo; + u_int32_t last_lo; + STAILQ_HEAD(, orb_info) orbs; + u_int16_t login_id; + u_int16_t lun; +}; + +struct corb4 { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t n:1, + rq_fmt:2, + :1, + dir:1, + spd:3, + max_payload:4, + page_table_present:1, + page_size:3, + data_size:16; +#else + u_int32_t data_size:16, + page_size:3, + page_table_present:1, + max_payload:4, + spd:3, + dir:1, + :1, + rq_fmt:2, + n:1; +#endif +}; + +struct morb4 { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t n:1, + rq_fmt:2, + :9, + fun:4, + id:16; +#else + u_int32_t id:16, + fun:4, + :9, + rq_fmt:2, + n:1; +#endif +}; + +struct orb_info { + struct sbp_targ_softc *sc; + struct fw_device *fwdev; + struct sbp_targ_lstate *lstate; + union ccb *ccb; + struct ccb_accept_tio *atio; + u_int8_t state; +#define ORBI_STATUS_NONE 0 +#define ORBI_STATUS_FETCH 1 +#define ORBI_STATUS_ATIO 2 +#define ORBI_STATUS_CTIO 3 +#define ORBI_STATUS_STATUS 4 +#define ORBI_STATUS_POINTER 5 +#define ORBI_STATUS_ABORTED 7 + u_int8_t refcount; + u_int16_t orb_hi; + u_int32_t orb_lo; + u_int32_t data_hi; + u_int32_t data_lo; + struct corb4 orb4; + STAILQ_ENTRY(orb_info) link; + u_int32_t orb[8]; + u_int32_t *page_table; + struct sbp_status status; +}; + +static char *orb_fun_name[] = { + ORB_FUN_NAMES +}; + +static void sbp_targ_recv(struct fw_xfer *); +static void sbp_targ_fetch_orb(struct sbp_targ_softc *, struct fw_device *, + u_int16_t, u_int32_t, struct sbp_targ_lstate *, int); + +static void +sbp_targ_identify(driver_t *driver, device_t parent) +{ + BUS_ADD_CHILD(parent, 0, "sbp_targ", device_get_unit(parent)); +} + +static int +sbp_targ_probe(device_t dev) +{ + device_t pa; + + pa = device_get_parent(dev); + if(device_get_unit(dev) != device_get_unit(pa)){ + return(ENXIO); + } + + device_set_desc(dev, "SBP-2/SCSI over FireWire target mode"); + return (0); +} + +static void +sbp_targ_post_busreset(void *arg) +{ + struct sbp_targ_softc *sc; + struct crom_src *src; + struct crom_chunk *root; + struct crom_chunk *unit; + struct sbp_targ_lstate *lstate; + int i; + + sc = (struct sbp_targ_softc *) arg; + src = sc->fd.fc->crom_src; + root = sc->fd.fc->crom_root; + + unit = &sc->unit; + + bzero(unit, sizeof(struct crom_chunk)); + + crom_add_chunk(src, root, unit, CROM_UDIR); + crom_add_entry(unit, CSRKEY_SPEC, CSRVAL_ANSIT10); + crom_add_entry(unit, CSRKEY_VER, CSRVAL_T10SBP2); + crom_add_entry(unit, CSRKEY_COM_SPEC, CSRVAL_ANSIT10); + crom_add_entry(unit, CSRKEY_COM_SET, CSRVAL_SCSI); + + crom_add_entry(unit, CROM_MGM, SBP_TARG_MGM >> 2); + crom_add_entry(unit, CSRKEY_UNIT_CH, (10<<8) | 8); + + for (i = 0; i < MAX_LUN; i ++) { + lstate = sc->lstate[i]; + if (lstate == NULL) + continue; + crom_add_entry(unit, CSRKEY_FIRM_VER, 1); + crom_add_entry(unit, CROM_LUN, i); + crom_add_entry(unit, CSRKEY_MODEL, 1); + crom_add_simple_text(src, unit, &lstate->model, "TargetMode"); + } +} + +static cam_status +sbp_targ_find_devs(struct sbp_targ_softc *sc, union ccb *ccb, + struct sbp_targ_lstate **lstate, int notfound_failure) +{ + u_int lun; + + /* XXX 0 is the only vaild target_id */ + if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD && + ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) { + *lstate = sc->black_hole; + return (CAM_REQ_CMP); + } + + if (ccb->ccb_h.target_id != 0) + return (CAM_TID_INVALID); + + lun = ccb->ccb_h.target_lun; + if (lun >= MAX_LUN) + return (CAM_LUN_INVALID); + + *lstate = sc->lstate[lun]; + + if (notfound_failure != 0 && *lstate == NULL) + return (CAM_PATH_INVALID); + + return (CAM_REQ_CMP); +} + +static void +sbp_targ_en_lun(struct sbp_targ_softc *sc, union ccb *ccb) +{ + struct ccb_en_lun *cel = &ccb->cel; + struct sbp_targ_lstate *lstate; + struct orb_info *orbi, *next; + cam_status status; + + status = sbp_targ_find_devs(sc, ccb, &lstate, 0); + if (status != CAM_REQ_CMP) { + ccb->ccb_h.status = status; + return; + } + + if (cel->enable != 0) { + if (lstate != NULL) { + xpt_print_path(ccb->ccb_h.path); + printf("Lun already enabled\n"); + ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; + return; + } + if (cel->grp6_len != 0 || cel->grp7_len != 0) { + ccb->ccb_h.status = CAM_REQ_INVALID; + printf("Non-zero Group Codes\n"); + return; + } + lstate = (struct sbp_targ_lstate *) + malloc(sizeof(*lstate), M_SBP_TARG, M_NOWAIT | M_ZERO); + if (lstate == NULL) { + xpt_print_path(ccb->ccb_h.path); + printf("Couldn't allocate lstate\n"); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + return; + } + if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD) + sc->black_hole = lstate; + else + sc->lstate[ccb->ccb_h.target_lun] = lstate; + memset(lstate, 0, sizeof(*lstate)); + lstate->sc = sc; + status = xpt_create_path(&lstate->path, /*periph*/NULL, + xpt_path_path_id(ccb->ccb_h.path), + xpt_path_target_id(ccb->ccb_h.path), + xpt_path_lun_id(ccb->ccb_h.path)); + if (status != CAM_REQ_CMP) { + free(lstate, M_SBP_TARG); + xpt_print_path(ccb->ccb_h.path); + printf("Couldn't allocate path\n"); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + return; + } + SLIST_INIT(&lstate->accept_tios); + SLIST_INIT(&lstate->immed_notifies); + STAILQ_INIT(&lstate->orbs); + lstate->last_hi = 0xffff; + lstate->last_lo = 0xffffffff; + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_print_path(ccb->ccb_h.path); + printf("Lun now enabled for target mode\n"); + /* bus reset */ + sc->fd.fc->ibr(sc->fd.fc); + } else { + if (lstate == NULL) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return; + } + ccb->ccb_h.status = CAM_REQ_CMP; + + if (SLIST_FIRST(&lstate->accept_tios) != NULL) { + printf("ATIOs pending\n"); + ccb->ccb_h.status = CAM_REQ_INVALID; + } + + if (SLIST_FIRST(&lstate->immed_notifies) != NULL) { + printf("INOTs pending\n"); + ccb->ccb_h.status = CAM_REQ_INVALID; + } + + if (ccb->ccb_h.status != CAM_REQ_CMP) { + return; + } + + xpt_print_path(ccb->ccb_h.path); + printf("Target mode disabled\n"); + xpt_free_path(lstate->path); + + for (orbi = STAILQ_FIRST(&lstate->orbs); orbi != NULL; + orbi = next) { + next = STAILQ_NEXT(orbi, link); + free(orbi, M_SBP_TARG); + } + + if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD) + sc->black_hole = NULL; + else + sc->lstate[ccb->ccb_h.target_lun] = NULL; + free(lstate, M_SBP_TARG); + + /* bus reset */ + sc->fd.fc->ibr(sc->fd.fc); + } +} + +static void +sbp_targ_send_lstate_events(struct sbp_targ_softc *sc, + struct sbp_targ_lstate *lstate) +{ +#if 0 + struct ccb_hdr *ccbh; + struct ccb_immed_notify *inot; + + printf("%s: not implemented yet\n", __FUNCTION__); +#endif +} + +static __inline void +sbp_targ_remove_orb_info(struct sbp_targ_lstate *lstate, struct orb_info *orbi) +{ + STAILQ_REMOVE(&lstate->orbs, orbi, orb_info, link); +} + +/* + * tag_id/init_id encoding + * + * tag_id and init_id has only 32bit for each. + * scsi_target can handle very limited number(up to 15) of init_id. + * we have to encode 48bit orb and 64bit EUI64 into these + * variables. + * + * tag_id represents lower 32bit of ORB address. + * init_id represents node_id now. + * + */ + +static struct orb_info * +sbp_targ_get_orb_info(struct sbp_targ_lstate *lstate, + u_int tag_id, u_int init_id) +{ + struct orb_info *orbi; + + STAILQ_FOREACH(orbi, &lstate->orbs, link) + if (orbi->orb_lo == tag_id && +#if 0 + orbi->orb_hi == (init_id & 0xffff) && + orbi->fwdev->dst == (init_id >> 16)) +#else + orbi->fwdev->dst == init_id) +#endif + goto found; + printf("%s: orb not found\n", __FUNCTION__); + return (NULL); +found: + return (orbi); +} + +static void +sbp_targ_abort(struct orb_info *orbi) +{ + struct orb_info *norbi; + + for (; orbi != NULL; orbi = norbi) { + printf("%s: status=%d\n", __FUNCTION__, orbi->state); + norbi = STAILQ_NEXT(orbi, link); + if (orbi->state != ORBI_STATUS_ABORTED) { + if (orbi->ccb != NULL) { + orbi->ccb->ccb_h.status = CAM_REQ_ABORTED; + xpt_done(orbi->ccb); + orbi->ccb = NULL; + } + if (orbi->state <= ORBI_STATUS_ATIO) { + sbp_targ_remove_orb_info(orbi->lstate, orbi); + free(orbi, M_SBP_TARG); + } else + orbi->state = ORBI_STATUS_ABORTED; + } + } +} + +static void +sbp_targ_free_orbi(struct fw_xfer *xfer) +{ + struct orb_info *orbi; + + orbi = (struct orb_info *)xfer->sc; + if (xfer->resp != 0) { + /* XXX */ + printf("%s: xfer->resp = %d\n", __FUNCTION__, xfer->resp); + } + free(orbi, M_SBP_TARG); + fw_xfer_free(xfer); +} + +static void +sbp_targ_status_FIFO(struct orb_info *orbi, + u_int32_t fifo_hi, u_int32_t fifo_lo, int dequeue) +{ + struct fw_xfer *xfer; + + if (dequeue) + sbp_targ_remove_orb_info(orbi->lstate, orbi); + + xfer = fwmem_write_block(orbi->fwdev, (void *)orbi, + /*spd*/2, fifo_hi, fifo_lo, + sizeof(u_int32_t) * (orbi->status.len + 1), (char *)&orbi->status, + sbp_targ_free_orbi); + + if (xfer == NULL) { + /* XXX */ + printf("%s: xfer == NULL\n", __FUNCTION__); + } +} + +static void +sbp_targ_send_status(struct orb_info *orbi, union ccb *ccb) +{ + struct sbp_status *sbp_status; + + sbp_status = &orbi->status; + + orbi->state = ORBI_STATUS_STATUS; + + sbp_status->resp = 0; /* XXX */ + sbp_status->status = 0; /* XXX */ + sbp_status->dead = 0; /* XXX */ + + switch (ccb->csio.scsi_status) { + case SCSI_STATUS_OK: + if (debug) + printf("%s: STATUS_OK\n", __FUNCTION__); + sbp_status->len = 1; + break; + case SCSI_STATUS_CHECK_COND: + case SCSI_STATUS_BUSY: + case SCSI_STATUS_CMD_TERMINATED: + { + struct sbp_cmd_status *sbp_cmd_status; + struct scsi_sense_data *sense; + + if (debug) + printf("%s: STATUS %d\n", __FUNCTION__, + ccb->csio.scsi_status); + sbp_cmd_status = (struct sbp_cmd_status *)&sbp_status->data[0]; + sbp_cmd_status->status = ccb->csio.scsi_status; + sense = &ccb->csio.sense_data; + + sbp_targ_abort(STAILQ_NEXT(orbi, link)); + + if ((sense->error_code & SSD_ERRCODE) == SSD_CURRENT_ERROR) + sbp_cmd_status->sfmt = SBP_SFMT_CURR; + else + sbp_cmd_status->sfmt = SBP_SFMT_DEFER; + + sbp_cmd_status->valid = (sense->error_code & SSD_ERRCODE_VALID) + ? 1 : 0; + sbp_cmd_status->s_key = sense->flags & SSD_KEY; + sbp_cmd_status->mark = (sense->flags & SSD_FILEMARK)? 1 : 0; + sbp_cmd_status->eom = (sense->flags & SSD_EOM) ? 1 : 0; + sbp_cmd_status->ill_len = (sense->flags & SSD_ILI) ? 1 : 0; + + bcopy(&sense->info[0], &sbp_cmd_status->info, 4); + + if (sense->extra_len <= 6) + /* add_sense_code(_qual), info, cmd_spec_info */ + sbp_status->len = 4; + else + /* fru, sense_key_spec */ + sbp_status->len = 5; + + bcopy(&sense->cmd_spec_info[0], &sbp_cmd_status->cdb, 4); + + sbp_cmd_status->s_code = sense->add_sense_code; + sbp_cmd_status->s_qlfr = sense->add_sense_code_qual; + sbp_cmd_status->fru = sense->fru; + + bcopy(&sense->sense_key_spec[0], + &sbp_cmd_status->s_keydep[0], 3); + + break; + } + default: + printf("%s: unknown scsi status 0x%x\n", __FUNCTION__, + sbp_status->status); + } + + sbp_targ_status_FIFO(orbi, + orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/1); + + if (orbi->page_table != NULL) + free(orbi->page_table, M_SBP_TARG); +} + +static void +sbp_targ_cam_done(struct fw_xfer *xfer) +{ + struct orb_info *orbi; + union ccb *ccb; + + orbi = (struct orb_info *)xfer->sc; + + if (debug) + printf("%s: resp=%d refcount=%d\n", __FUNCTION__, + xfer->resp, orbi->refcount); + + if (xfer->resp != 0) { + printf("%s: xfer->resp = %d\n", __FUNCTION__, xfer->resp); + orbi->status.resp = SBP_TRANS_FAIL; + orbi->status.status = htonl(OBJ_DATA | SBE_TIMEOUT /*XXX*/); + orbi->status.dead = 1; + sbp_targ_abort(STAILQ_NEXT(orbi, link)); + } + + orbi->refcount --; + + ccb = orbi->ccb; + if (orbi->refcount == 0) { + if (orbi->state == ORBI_STATUS_ABORTED) { + if (debug) + printf("%s: orbi aborted\n", __FUNCTION__); + sbp_targ_remove_orb_info(orbi->lstate, orbi); + if (orbi->page_table != NULL) + free(orbi->page_table, M_SBP_TARG); + free(orbi, M_SBP_TARG); + } else if (orbi->status.resp == 0) { + if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) + sbp_targ_send_status(orbi, ccb); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + } else { + orbi->status.len = 1; + sbp_targ_status_FIFO(orbi, + orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, + /*dequeue*/1); + ccb->ccb_h.status = CAM_REQ_ABORTED; + xpt_done(ccb); + } + } + + fw_xfer_free(xfer); +} + +static cam_status +sbp_targ_abort_ccb(struct sbp_targ_softc *sc, union ccb *ccb) +{ + union ccb *accb; + struct sbp_targ_lstate *lstate; + struct ccb_hdr_slist *list; + struct ccb_hdr *curelm; + int found; + cam_status status; + + status = sbp_targ_find_devs(sc, ccb, &lstate, 0); + if (status != CAM_REQ_CMP) + return (status); + + accb = ccb->cab.abort_ccb; + + if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) + list = &lstate->accept_tios; + else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY) + list = &lstate->immed_notifies; + else + return (CAM_UA_ABORT); + + curelm = SLIST_FIRST(list); + found = 0; + if (curelm == &accb->ccb_h) { + found = 1; + SLIST_REMOVE_HEAD(list, sim_links.sle); + } else { + while(curelm != NULL) { + struct ccb_hdr *nextelm; + + nextelm = SLIST_NEXT(curelm, sim_links.sle); + if (nextelm == &accb->ccb_h) { + found = 1; + SLIST_NEXT(curelm, sim_links.sle) = + SLIST_NEXT(nextelm, sim_links.sle); + break; + } + curelm = nextelm; + } + } + if (found) { + accb->ccb_h.status = CAM_REQ_ABORTED; + xpt_done(accb); + return (CAM_REQ_CMP); + } + printf("%s: not found\n", __FUNCTION__); + return (CAM_PATH_INVALID); +} + +static void +sbp_targ_xfer_buf(struct orb_info *orbi, u_int offset, + u_int16_t dst_hi, u_int32_t dst_lo, u_int size, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + u_int len, ccb_dir, off = 0; + char *ptr; + + if (debug) + printf("%s: offset=%d size=%d\n", __FUNCTION__, offset, size); + ccb_dir = orbi->ccb->ccb_h.flags & CAM_DIR_MASK; + ptr = (char *)orbi->ccb->csio.data_ptr + offset; + + while (size > 0) { + /* XXX assume dst_lo + off doesn't overflow */ + len = MIN(size, 2048 /* XXX */); + size -= len; + orbi->refcount ++; + if (ccb_dir == CAM_DIR_OUT) + xfer = fwmem_read_block(orbi->fwdev, + (void *)orbi, /*spd*/2, + dst_hi, dst_lo + off, len, + ptr + off, hand); + else + xfer = fwmem_write_block(orbi->fwdev, + (void *)orbi, /*spd*/2, + dst_hi, dst_lo + off, len, + ptr + off, hand); + if (xfer == NULL) { + printf("%s: xfer == NULL", __FUNCTION__); + /* XXX what should we do?? */ + orbi->refcount --; + } + off += len; + } +} + +static void +sbp_targ_pt_done(struct fw_xfer *xfer) +{ + struct orb_info *orbi; + union ccb *ccb; + u_int i, offset, res, len; + u_int32_t t1, t2, *p; + + orbi = (struct orb_info *)xfer->sc; + ccb = orbi->ccb; + if (orbi->state == ORBI_STATUS_ABORTED) { + if (debug) + printf("%s: orbi aborted\n", __FUNCTION__); + sbp_targ_remove_orb_info(orbi->lstate, orbi); + free(orbi->page_table, M_SBP_TARG); + free(orbi, M_SBP_TARG); + fw_xfer_free(xfer); + return; + } + if (xfer->resp != 0) { + printf("%s: xfer->resp = %d\n", __FUNCTION__, xfer->resp); + orbi->status.resp = SBP_TRANS_FAIL; + orbi->status.status = htonl(OBJ_PT | SBE_TIMEOUT /*XXX*/); + orbi->status.dead = 1; + orbi->status.len = 1; + sbp_targ_abort(STAILQ_NEXT(orbi, link)); + + sbp_targ_status_FIFO(orbi, + orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/1); + free(orbi->page_table, M_SBP_TARG); + fw_xfer_free(xfer); + return; + } + res = ccb->csio.dxfer_len; + offset = 0; + if (debug) + printf("%s: dxfer_len=%d\n", __FUNCTION__, res); + orbi->refcount ++; + for (p = orbi->page_table, i = orbi->orb4.data_size; i > 0; i --) { + t1 = ntohl(*p++); + t2 = ntohl(*p++); + if (debug) + printf("page_table: %04x:%08x %d\n", + t1 & 0xffff, t2, t1>>16); + len = MIN(t1 >> 16, res); + res -= len; + sbp_targ_xfer_buf(orbi, offset, t1 & 0xffff, t2, len, + sbp_targ_cam_done); + offset += len; + if (res == 0) + break; + } + orbi->refcount --; + if (orbi->refcount == 0) + printf("%s: refcount == 0\n", __FUNCTION__); + if (res !=0) + /* XXX handle res != 0 case */ + printf("%s: page table is too small(%d)\n", __FUNCTION__, res); + + fw_xfer_free(xfer); + return; +} + +static void +sbp_targ_fetch_pt(struct orb_info *orbi) +{ + struct fw_xfer *xfer; + + if (debug) + printf("%s: page_table_size=%d\n", + __FUNCTION__, orbi->orb4.data_size); + orbi->page_table = malloc(orbi->orb4.data_size*8, M_SBP_TARG, M_NOWAIT); + if (orbi->page_table == NULL) + goto error; + xfer = fwmem_read_block(orbi->fwdev, (void *)orbi, /*spd*/2, + orbi->data_hi, orbi->data_lo, orbi->orb4.data_size*8, + (void *)orbi->page_table, sbp_targ_pt_done); + if (xfer != NULL) + return; +error: + orbi->ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(orbi->ccb); + return; +} + +static void +sbp_targ_action1(struct cam_sim *sim, union ccb *ccb) +{ + struct sbp_targ_softc *sc; + struct sbp_targ_lstate *lstate; + cam_status status; + u_int ccb_dir; + + sc = (struct sbp_targ_softc *)cam_sim_softc(sim); + + status = sbp_targ_find_devs(sc, ccb, &lstate, TRUE); + + switch (ccb->ccb_h.func_code) { + case XPT_CONT_TARGET_IO: + { + struct orb_info *orbi; + + if (debug) + printf("%s: XPT_CONT_TARGET_IO\n", __FUNCTION__); + + if (status != CAM_REQ_CMP) { + ccb->ccb_h.status = status; + xpt_done(ccb); + break; + } + /* XXX transfer from/to initiator */ + orbi = sbp_targ_get_orb_info(lstate, + ccb->csio.tag_id, ccb->csio.init_id); + if (orbi == NULL) { + printf("%s: no such ORB found, aborted?\n", + __FUNCTION__); + ccb->ccb_h.status = CAM_REQ_ABORTED; /* XXX */ + xpt_done(ccb); + break; + } + if (orbi->state == ORBI_STATUS_ABORTED) { + if (debug) + printf("%s: ctio aborted\n", __FUNCTION__); + sbp_targ_remove_orb_info(orbi->lstate, orbi); + free(orbi, M_SBP_TARG); + break; + } + orbi->state = ORBI_STATUS_CTIO; + + orbi->ccb = ccb; + ccb_dir = ccb->ccb_h.flags & CAM_DIR_MASK; + + /* XXX */ + if (ccb->csio.dxfer_len == 0) + ccb_dir = CAM_DIR_NONE; + + /* Sanity check */ + if (ccb_dir == CAM_DIR_IN && orbi->orb4.dir == 0) + printf("%s: direction mismatch\n", __FUNCTION__); + + /* check page table */ + if (ccb_dir != CAM_DIR_NONE && orbi->orb4.page_table_present) { + if (debug) + printf("%s: page_table_present\n", + __FUNCTION__); + if (orbi->orb4.page_size != 0) { + printf("%s: unsupported pagesize %d != 0\n", + __FUNCTION__, orbi->orb4.page_size); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + sbp_targ_fetch_pt(orbi); + break; + } + + /* Sanity check */ + if (ccb_dir != CAM_DIR_NONE && + orbi->orb4.data_size != ccb->csio.dxfer_len) + printf("%s: data_size(%d) != dxfer_len(%d)\n", + __FUNCTION__, orbi->orb4.data_size, + ccb->csio.dxfer_len); + + if (ccb_dir != CAM_DIR_NONE) + sbp_targ_xfer_buf(orbi, 0, orbi->data_hi, + orbi->data_lo, + MIN(orbi->orb4.data_size, ccb->csio.dxfer_len), + sbp_targ_cam_done); + + if (ccb_dir == CAM_DIR_NONE) { + if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) + sbp_targ_send_status(orbi, ccb); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + } + break; + } + case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */ + if (status != CAM_REQ_CMP) { + ccb->ccb_h.status = status; + xpt_done(ccb); + break; + } + SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h, + sim_links.sle); + ccb->ccb_h.status = CAM_REQ_INPROG; + if ((lstate->flags & ATIO_STARVED) != 0) { + if (debug) + printf("%s: new atio arrived\n", __FUNCTION__); + lstate->flags &= ~ATIO_STARVED; + sbp_targ_fetch_orb(lstate->sc, lstate->fwdev, + lstate->last_hi, lstate->last_lo, + lstate, FETCH_CMD); + } + break; + case XPT_NOTIFY_ACK: /* recycle notify ack */ + case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */ + if (status != CAM_REQ_CMP) { + ccb->ccb_h.status = status; + xpt_done(ccb); + break; + } + SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h, + sim_links.sle); + ccb->ccb_h.status = CAM_REQ_INPROG; + sbp_targ_send_lstate_events(sc, lstate); + break; + case XPT_EN_LUN: + sbp_targ_en_lun(sc, ccb); + xpt_done(ccb); + break; + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; /* XXX??? */ + cpi->hba_inquiry = PI_TAG_ABLE; + cpi->target_sprt = PIT_PROCESSOR + | PIT_DISCONNECT + | PIT_TERM_IO; + cpi->hba_misc = PIM_NOBUSRESET | PIM_NO_6_BYTE; + cpi->hba_eng_cnt = 0; + cpi->max_target = 7; /* XXX */ + cpi->max_lun = MAX_LUN - 1; + cpi->initiator_id = 7; /* XXX */ + cpi->bus_id = sim->bus_id; + cpi->base_transfer_speed = 400 * 1000 / 8; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "SBP_TARG", HBA_IDLEN); + strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); + cpi->unit_number = sim->unit_number; + + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_ABORT: + { + union ccb *accb = ccb->cab.abort_ccb; + + switch (accb->ccb_h.func_code) { + case XPT_ACCEPT_TARGET_IO: + case XPT_IMMED_NOTIFY: + ccb->ccb_h.status = sbp_targ_abort_ccb(sc, ccb); + break; + case XPT_CONT_TARGET_IO: + /* XXX */ + ccb->ccb_h.status = CAM_UA_ABORT; + break; + default: + printf("%s: aborting unknown function %d\n", + __FUNCTION__, accb->ccb_h.func_code); + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); + break; + } + default: + printf("%s: unknown function %d\n", + __FUNCTION__, ccb->ccb_h.func_code); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + return; +} + +static void +sbp_targ_action(struct cam_sim *sim, union ccb *ccb) +{ + int s; + + s = splfw(); + sbp_targ_action1(sim, ccb); + splx(s); +} + +static void +sbp_targ_poll(struct cam_sim *sim) +{ + /* XXX */ + return; +} + +static void +sbp_targ_cmd_handler(struct fw_xfer *xfer) +{ + struct fw_pkt *fp; + u_int32_t *orb; + struct corb4 *orb4; + struct orb_info *orbi; + struct ccb_accept_tio *atio; + struct sbp_targ_lstate *lstate; + u_char *bytes; + int i; + + orbi = (struct orb_info *)xfer->sc; + if (xfer->resp != 0) { + printf("%s: xfer->resp = %d\n", __FUNCTION__, xfer->resp); + orbi->status.resp = SBP_TRANS_FAIL; + orbi->status.status = htonl(OBJ_ORB | SBE_TIMEOUT /*XXX*/); + orbi->status.dead = 1; + orbi->status.len = 1; + sbp_targ_abort(STAILQ_NEXT(orbi, link)); + + sbp_targ_status_FIFO(orbi, + orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/1); + fw_xfer_free(xfer); + return; + } + fp = &xfer->recv.hdr; + + if (orbi->state == ORBI_STATUS_ABORTED) { + printf("%s: aborted\n", __FUNCTION__); + sbp_targ_remove_orb_info(orbi->lstate, orbi); + free(orbi, M_SBP_TARG); + goto done0; + } + orbi->state = ORBI_STATUS_ATIO; + + lstate = orbi->lstate; + + orb = orbi->orb; + /* swap payload except SCSI command */ + for (i = 0; i < 5; i ++) + orb[i] = ntohl(orb[i]); + + orb4 = (struct corb4 *)&orb[4]; + if (orb4->rq_fmt != 0) { + /* XXX */ + printf("%s: rq_fmt(%d) != 0\n", __FUNCTION__, orb4->rq_fmt); + } + + atio = orbi->atio; + atio->ccb_h.target_id = 0; /* XXX */ + atio->ccb_h.target_lun = lstate->lun; + atio->sense_len = 0; + atio->tag_action = 1; /* XXX */ + atio->tag_id = orbi->orb_lo; +#if 0 + atio->init_id = (orbi->fwdev->dst << 16) | (orbi->orb_hi & 0xffff); +#else + atio->init_id = orbi->fwdev->dst; +#endif + atio->ccb_h.flags = CAM_TAG_ACTION_VALID; + bytes = (char *)&orb[5]; + if (debug) + printf("%s: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + __FUNCTION__, + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], + bytes[5], bytes[6], bytes[7], bytes[8], bytes[9]); + switch (bytes[0] >> 5) { + case 0: + atio->cdb_len = 6; + break; + case 1: + case 2: + atio->cdb_len = 10; + break; + case 4: + atio->cdb_len = 16; + break; + case 5: + atio->cdb_len = 12; + break; + case 3: + default: + /* Only copy the opcode. */ + atio->cdb_len = 1; + printf("Reserved or VU command code type encountered\n"); + break; + } + + memcpy(atio->cdb_io.cdb_bytes, bytes, atio->cdb_len); + + atio->ccb_h.status |= CAM_CDB_RECVD; + + /* next ORB */ + if ((orb[0] & (1<<31)) == 0) { + if (debug) + printf("%s: fetch next orb\n", __FUNCTION__); + orbi->status.src = SRC_NEXT_EXISTS; + sbp_targ_fetch_orb(orbi->sc, orbi->fwdev, + orb[0], orb[1], orbi->lstate, FETCH_CMD); + } else { + orbi->status.src = SRC_NO_NEXT; + orbi->lstate->flags &= ~LINK_ACTIVE; + } + + orbi->data_hi = orb[2]; + orbi->data_lo = orb[3]; + orbi->orb4 = *orb4; + + xpt_done((union ccb*)atio); +done0: + fw_xfer_free(xfer); + return; +} + +static void +sbp_targ_mgm_handler(struct fw_xfer *xfer) +{ + struct sbp_targ_lstate *lstate; + struct fw_pkt *fp; + u_int32_t *orb; + struct morb4 *orb4; + struct orb_info *orbi; + int i; + + orbi = (struct orb_info *)xfer->sc; + if (xfer->resp != 0) { + printf("%s: xfer->resp = %d\n", __FUNCTION__, xfer->resp); + orbi->status.resp = SBP_TRANS_FAIL; + orbi->status.status = htonl(OBJ_ORB | SBE_TIMEOUT /*XXX*/); + orbi->status.dead = 1; + orbi->status.len = 1; + sbp_targ_abort(STAILQ_NEXT(orbi, link)); + + sbp_targ_status_FIFO(orbi, + orbi->lstate->fifo_hi, orbi->lstate->fifo_lo, /*dequeue*/0); + fw_xfer_free(xfer); + return; + } + fp = &xfer->recv.hdr; + + orb = orbi->orb; + /* swap payload */ + for (i = 0; i < 8; i ++) { + orb[i] = ntohl(orb[i]); + } + orb4 = (struct morb4 *)&orb[4]; + if (debug) + printf("%s: %s\n", __FUNCTION__, orb_fun_name[orb4->fun]); + + orbi->status.src = SRC_NO_NEXT; + + switch (orb4->fun << 16) { + case ORB_FUN_LGI: + { + + if (orb4->id >= MAX_LUN || orbi->sc->lstate[orb4->id] == NULL) { + /* error */ + orbi->status.dead = 1; + orbi->status.status = STATUS_ACCESS_DENY; + orbi->status.len = 1; + sbp_targ_status_FIFO(orbi, orb[6], orb[7], + /*dequeue*/0); + break; + } + /* XXX check exclusive login */ + lstate = orbi->sc->lstate[orb4->id]; + lstate->fifo_hi = orb[6]; + lstate->fifo_lo = orb[7]; + lstate->login_id = 0; /* XXX random number? */ + lstate->lun = orb4->id; + lstate->loginres.len = htons(sizeof(u_int32_t) * 4); + lstate->loginres.id = htons(lstate->login_id); + lstate->loginres.cmd_hi = htons(SBP_TARG_BIND_HI); + lstate->loginres.cmd_lo = htonl(SBP_TARG_BIND_LO(orb4->id)); + lstate->loginres.recon_hold = htons(0); /* XXX */ + fwmem_write_block(orbi->fwdev, NULL, /*spd*/2, orb[2], orb[3], + sizeof(struct sbp_login_res), (void *)&lstate->loginres, + fw_asy_callback_free); + break; + } + case ORB_FUN_RCN: + orbi->status.dead = 1; + orbi->status.status = STATUS_ACCESS_DENY; + break; + default: + printf("%s: %s not implemented yet\n", + __FUNCTION__, orb_fun_name[orb4->fun]); + break; + } + orbi->status.len = 1; + sbp_targ_status_FIFO(orbi, orb[6], orb[7], /*dequeue*/0); + fw_xfer_free(xfer); + return; +} + +static void +sbp_targ_pointer_handler(struct fw_xfer *xfer) +{ + struct orb_info *orbi; + u_int32_t orb0, orb1; + + orbi = (struct orb_info *)xfer->sc; + if (xfer->resp != 0) { + printf("%s: xfer->resp = %d\n", __FUNCTION__, xfer->resp); + goto done; + } + + orb0 = ntohl(orbi->orb[0]); + orb1 = ntohl(orbi->orb[1]); + if ((orb0 & (1 << 31)) != 0) { + printf("%s: invalid pointer\n", __FUNCTION__); + goto done; + } + sbp_targ_fetch_orb(orbi->lstate->sc, orbi->fwdev, + (u_int16_t)orb0, orb1, orbi->lstate, FETCH_CMD); +done: + free(orbi, M_SBP_TARG); + fw_xfer_free(xfer); + return; +} + +static void +sbp_targ_fetch_orb(struct sbp_targ_softc *sc, struct fw_device *fwdev, + u_int16_t orb_hi, u_int32_t orb_lo, struct sbp_targ_lstate *lstate, + int mode) +{ + struct orb_info *orbi; + + if (debug) + printf("%s: fetch orb %04x:%08x\n", __FUNCTION__, orb_hi, orb_lo); + orbi = malloc(sizeof(struct orb_info), M_SBP_TARG, M_NOWAIT | M_ZERO); + if (orbi == NULL) { + printf("%s: malloc failed\n", __FUNCTION__); + return; + } + orbi->sc = sc; + orbi->fwdev = fwdev; + orbi->lstate = lstate; + orbi->orb_hi = orb_hi; + orbi->orb_lo = orb_lo; + orbi->status.orb_hi = htons(orb_hi); + orbi->status.orb_lo = htonl(orb_lo); + + switch (mode) { + case FETCH_MGM: + fwmem_read_block(fwdev, (void *)orbi, /*spd*/2, orb_hi, orb_lo, + sizeof(u_int32_t) * 8, &orbi->orb[0], + sbp_targ_mgm_handler); + break; + case FETCH_CMD: + orbi->state = ORBI_STATUS_FETCH; + lstate->last_hi = orb_hi; + lstate->last_lo = orb_lo; + lstate->flags |= LINK_ACTIVE; + /* dequeue */ + orbi->atio = (struct ccb_accept_tio *) + SLIST_FIRST(&lstate->accept_tios); + if (orbi->atio == NULL) { + printf("%s: no free atio\n", __FUNCTION__); + lstate->flags |= ATIO_STARVED; + lstate->fwdev = fwdev; + break; + } + SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle); + fwmem_read_block(fwdev, (void *)orbi, /*spd*/2, orb_hi, orb_lo, + sizeof(u_int32_t) * 8, &orbi->orb[0], + sbp_targ_cmd_handler); + STAILQ_INSERT_TAIL(&lstate->orbs, orbi, link); + break; + case FETCH_POINTER: + orbi->state = ORBI_STATUS_POINTER; + fwmem_read_block(fwdev, (void *)orbi, /*spd*/2, orb_hi, orb_lo, + sizeof(u_int32_t) * 2, &orbi->orb[0], + sbp_targ_pointer_handler); + break; + default: + printf("%s: invalid mode %d\n", __FUNCTION__, mode); + } +} + +static void +sbp_targ_resp_callback(struct fw_xfer *xfer) +{ + struct sbp_targ_softc *sc; + int s; + + if (debug) + printf("%s: xfer=%p\n", __FUNCTION__, xfer); + sc = (struct sbp_targ_softc *)xfer->sc; + fw_xfer_unload(xfer); + xfer->recv.pay_len = SBP_TARG_RECV_LEN; + xfer->act.hand = sbp_targ_recv; + s = splfw(); + STAILQ_INSERT_TAIL(&sc->fwb.xferlist, xfer, link); + splx(s); +} + +static int +sbp_targ_cmd(struct fw_xfer *xfer, struct fw_device *fwdev, int lun, int reg) +{ + struct sbp_targ_lstate *lstate; + struct sbp_targ_softc *sc; + int rtcode = 0; + + if (lun < 0 || lun >= MAX_LUN) + return(RESP_ADDRESS_ERROR); + + sc = (struct sbp_targ_softc *)xfer->sc; + lstate = sc->lstate[lun]; + if (lstate == NULL) + return(RESP_ADDRESS_ERROR); + + /* XXX check logined? */ + switch (reg) { + case 0x08: /* ORB_POINTER */ + if (debug) + printf("%s: ORB_POINTER\n", __FUNCTION__); + sbp_targ_fetch_orb(lstate->sc, fwdev, + ntohl(xfer->recv.payload[0]), + ntohl(xfer->recv.payload[1]), + lstate, FETCH_CMD); + break; + case 0x04: /* AGENT_RESET */ + if (debug) + printf("%s: AGENT RESET\n", __FUNCTION__); + lstate->last_hi = 0xffff; + lstate->last_lo = 0xffffffff; + sbp_targ_abort(STAILQ_FIRST(&lstate->orbs)); + break; + case 0x10: /* DOORBELL */ + if (debug) + printf("%s: DOORBELL\n", __FUNCTION__); + if (lstate->last_hi == 0xffff && + lstate->last_lo == 0xffffffff) { + printf("%s: no previous pointer(DOORBELL)\n", + __FUNCTION__); + break; + } + if ((lstate->flags & LINK_ACTIVE) != 0) { + if (debug) + printf("link active (DOORBELL)\n"); + break; + } + lstate->flags |= LINK_ACTIVE; + sbp_targ_fetch_orb(lstate->sc, fwdev, + lstate->last_hi, lstate->last_lo, + lstate, FETCH_POINTER); + break; + case 0x00: /* AGENT_STATE */ + printf("%s: AGENT_STATE (ignore)\n", __FUNCTION__); + break; + case 0x14: /* UNSOLICITED_STATE_ENABLE */ + printf("%s: UNSOLICITED_STATE_ENABLE (ignore)\n", __FUNCTION__); + break; + default: + printf("%s: invalid register %d\n", __FUNCTION__, reg); + rtcode = RESP_ADDRESS_ERROR; + } + + return (rtcode); +} + +static int +sbp_targ_mgm(struct fw_xfer *xfer, struct fw_device *fwdev) +{ + struct sbp_targ_softc *sc; + struct fw_pkt *fp; + + sc = (struct sbp_targ_softc *)xfer->sc; + + fp = &xfer->recv.hdr; + if (fp->mode.wreqb.tcode != FWTCODE_WREQB){ + printf("%s: tcode = %d\n", __FUNCTION__, fp->mode.wreqb.tcode); + return(RESP_TYPE_ERROR); + } + + sbp_targ_fetch_orb(sc, fwdev, + ntohl(xfer->recv.payload[0]), + ntohl(xfer->recv.payload[1]), + NULL, FETCH_MGM); + + return(0); +} + + +static void +sbp_targ_recv(struct fw_xfer *xfer) +{ + struct fw_pkt *fp, *sfp; + struct fw_device *fwdev; + u_int32_t lo; + int s, rtcode; + struct sbp_targ_softc *sc; + + s = splfw(); + sc = (struct sbp_targ_softc *)xfer->sc; + fp = &xfer->recv.hdr; + fwdev = fw_noderesolve_nodeid(sc->fd.fc, fp->mode.wreqb.src & 0x3f); + if (fwdev == NULL) { + printf("%s: cannot resolve nodeid=%d\n", + __FUNCTION__, fp->mode.wreqb.src & 0x3f); + rtcode = RESP_TYPE_ERROR; /* XXX */ + goto done; + } + lo = fp->mode.wreqb.dest_lo; + if (lo == SBP_TARG_BIND_LO(-1)) + rtcode = sbp_targ_mgm(xfer, fwdev); + else if (lo >= SBP_TARG_BIND_LO(0)) + rtcode = sbp_targ_cmd(xfer, fwdev, SBP_TARG_LUN(lo), lo % 0x20); + else + rtcode = RESP_ADDRESS_ERROR; + +done: + if (rtcode != 0) + printf("%s: rtcode = %d\n", __FUNCTION__, rtcode); + sfp = &xfer->send.hdr; + xfer->send.spd = 2; /* XXX */ + xfer->act.hand = sbp_targ_resp_callback; + xfer->retry_req = fw_asybusy; + sfp->mode.wres.dst = fp->mode.wreqb.src; + sfp->mode.wres.tlrt = fp->mode.wreqb.tlrt; + sfp->mode.wres.tcode = FWTCODE_WRES; + sfp->mode.wres.rtcode = rtcode; + sfp->mode.wres.pri = 0; + + fw_asyreq(xfer->fc, -1, xfer); + splx(s); +} + +static int +sbp_targ_attach(device_t dev) +{ + struct sbp_targ_softc *sc; + struct cam_devq *devq; + struct fw_xfer *xfer; + int i; + + sc = (struct sbp_targ_softc *) device_get_softc(dev); + bzero((void *)sc, sizeof(struct sbp_targ_softc)); + + sc->fd.fc = device_get_ivars(dev); + sc->fd.dev = dev; + sc->fd.post_explore = NULL; + sc->fd.post_busreset = (void *) sbp_targ_post_busreset; + + devq = cam_simq_alloc(/*maxopenings*/1); + if (devq == NULL) + return (ENXIO); + + sc->sim = cam_sim_alloc(sbp_targ_action, sbp_targ_poll, + "sbp_targ", sc, device_get_unit(dev), + /*untagged*/ 1, /*tagged*/ 1, devq); + if (sc->sim == NULL) { + cam_simq_free(devq); + return (ENXIO); + } + + if (xpt_bus_register(sc->sim, /*bus*/0) != CAM_SUCCESS) + goto fail; + + if (xpt_create_path(&sc->path, /*periph*/ NULL, cam_sim_path(sc->sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sc->sim)); + goto fail; + } + + sc->fwb.start = SBP_TARG_BIND_START; + sc->fwb.end = SBP_TARG_BIND_END; + sc->fwb.act_type = FWACT_XFER; + + /* pre-allocate xfer */ + STAILQ_INIT(&sc->fwb.xferlist); + for (i = 0; i < MAX_LUN /* XXX */; i ++) { + xfer = fw_xfer_alloc_buf(M_SBP_TARG, + /* send */ 0, + /* recv */ SBP_TARG_RECV_LEN); + xfer->act.hand = sbp_targ_recv; + xfer->fc = sc->fd.fc; + xfer->sc = (caddr_t)sc; + STAILQ_INSERT_TAIL(&sc->fwb.xferlist, xfer, link); + } + fw_bindadd(sc->fd.fc, &sc->fwb); + return 0; + +fail: + cam_sim_free(sc->sim, /*free_devq*/TRUE); + return (ENXIO); +} + +static int +sbp_targ_detach(device_t dev) +{ + struct sbp_targ_softc *sc; + struct sbp_targ_lstate *lstate; + struct fw_xfer *xfer, *next; + int i; + + sc = (struct sbp_targ_softc *)device_get_softc(dev); + sc->fd.post_busreset = NULL; + + xpt_free_path(sc->path); + xpt_bus_deregister(cam_sim_path(sc->sim)); + cam_sim_free(sc->sim, /*free_devq*/TRUE); + + for (i = 0; i < MAX_LUN; i ++) { + lstate = sc->lstate[i]; + if (lstate != NULL) { + xpt_free_path(lstate->path); + free(lstate, M_SBP_TARG); + } + } + if (sc->black_hole != NULL) { + xpt_free_path(sc->black_hole->path); + free(sc->black_hole, M_SBP_TARG); + } + + for (xfer = STAILQ_FIRST(&sc->fwb.xferlist); + xfer != NULL; xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free_buf(xfer); + } + STAILQ_INIT(&sc->fwb.xferlist); + fw_bindremove(sc->fd.fc, &sc->fwb); + + return 0; +} + +static devclass_t sbp_targ_devclass; + +static device_method_t sbp_targ_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, sbp_targ_identify), + DEVMETHOD(device_probe, sbp_targ_probe), + DEVMETHOD(device_attach, sbp_targ_attach), + DEVMETHOD(device_detach, sbp_targ_detach), + { 0, 0 } +}; + +static driver_t sbp_targ_driver = { + "sbp_targ", + sbp_targ_methods, + sizeof(struct sbp_targ_softc), +}; + +DRIVER_MODULE(sbp_targ, firewire, sbp_targ_driver, sbp_targ_devclass, 0, 0); +MODULE_VERSION(sbp_targ, 1); +MODULE_DEPEND(sbp_targ, firewire, 1, 1, 1); +MODULE_DEPEND(sbp_targ, cam, 1, 1, 1); Property changes on: stable/4/sys/dev/firewire/sbp_targ.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/dev/usb/ehci.c =================================================================== --- stable/4/sys/dev/usb/ehci.c (nonexistent) +++ stable/4/sys/dev/usb/ehci.c (revision 122530) @@ -0,0 +1,2819 @@ +/* $NetBSD: ehci.c,v 1.46 2003/03/09 19:51:13 augustss Exp $ */ + +/* Also ported from NetBSD: + * $NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $ + */ + +/* + * TODO + * hold off explorations by companion controllers until ehci has started. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net). + * + * 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. + */ + +/* + * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. + * + * The EHCI 1.0 spec can be found at + * http://developer.intel.com/technology/usb/download/ehci-r10.pdf + * and the USB 2.0 spec at + * http://www.usb.org/developers/docs/usb_20.zip + * + */ + +#include +#include +#include +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#elif defined(__FreeBSD__) +#include +#include +#include +#include +#include +#include +#if defined(DIAGNOSTIC) && defined(__i386__) && defined(__FreeBSD__) +#include +#endif +#endif +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#if defined(__FreeBSD__) +#include + +#define delay(d) DELAY(d) +#endif + +#ifdef USB_DEBUG +#define DPRINTF(x) if (ehcidebug) logprintf x +#define DPRINTFN(n,x) if (ehcidebug>(n)) logprintf x +int ehcidebug = 0; +SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, + &ehcidebug, 0, "ehci debug level"); +#ifndef __NetBSD__ +#define bitmask_snprintf(q,f,b,l) snprintf((b), (l), "%b", (q), (f)) +#endif +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +struct ehci_pipe { + struct usbd_pipe pipe; + ehci_soft_qh_t *sqh; + union { + ehci_soft_qtd_t *qtd; + /* ehci_soft_itd_t *itd; */ + } tail; + union { + /* Control pipe */ + struct { + usb_dma_t reqdma; + u_int length; + /*ehci_soft_qtd_t *setup, *data, *stat;*/ + } ctl; + /* Interrupt pipe */ + /* XXX */ + /* Bulk pipe */ + struct { + u_int length; + } bulk; + /* Iso pipe */ + /* XXX */ + } u; +}; + +#if defined(__NetBSD__) || defined(__OpenBSD__) +Static void ehci_shutdown(void *); +Static void ehci_power(int, void *); +#endif + +Static usbd_status ehci_open(usbd_pipe_handle); +Static void ehci_poll(struct usbd_bus *); +Static void ehci_softintr(void *); +Static int ehci_intr1(ehci_softc_t *); +Static void ehci_waitintr(ehci_softc_t *, usbd_xfer_handle); +Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *); +Static void ehci_idone(struct ehci_xfer *); +Static void ehci_timeout(void *); +Static void ehci_timeout_task(void *); + +Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t); +Static void ehci_freem(struct usbd_bus *, usb_dma_t *); + +Static usbd_xfer_handle ehci_allocx(struct usbd_bus *); +Static void ehci_freex(struct usbd_bus *, usbd_xfer_handle); + +Static usbd_status ehci_root_ctrl_transfer(usbd_xfer_handle); +Static usbd_status ehci_root_ctrl_start(usbd_xfer_handle); +Static void ehci_root_ctrl_abort(usbd_xfer_handle); +Static void ehci_root_ctrl_close(usbd_pipe_handle); +Static void ehci_root_ctrl_done(usbd_xfer_handle); + +Static usbd_status ehci_root_intr_transfer(usbd_xfer_handle); +Static usbd_status ehci_root_intr_start(usbd_xfer_handle); +Static void ehci_root_intr_abort(usbd_xfer_handle); +Static void ehci_root_intr_close(usbd_pipe_handle); +Static void ehci_root_intr_done(usbd_xfer_handle); + +Static usbd_status ehci_device_ctrl_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_ctrl_start(usbd_xfer_handle); +Static void ehci_device_ctrl_abort(usbd_xfer_handle); +Static void ehci_device_ctrl_close(usbd_pipe_handle); +Static void ehci_device_ctrl_done(usbd_xfer_handle); + +Static usbd_status ehci_device_bulk_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_bulk_start(usbd_xfer_handle); +Static void ehci_device_bulk_abort(usbd_xfer_handle); +Static void ehci_device_bulk_close(usbd_pipe_handle); +Static void ehci_device_bulk_done(usbd_xfer_handle); + +Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_intr_start(usbd_xfer_handle); +Static void ehci_device_intr_abort(usbd_xfer_handle); +Static void ehci_device_intr_close(usbd_pipe_handle); +Static void ehci_device_intr_done(usbd_xfer_handle); + +Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_isoc_start(usbd_xfer_handle); +Static void ehci_device_isoc_abort(usbd_xfer_handle); +Static void ehci_device_isoc_close(usbd_pipe_handle); +Static void ehci_device_isoc_done(usbd_xfer_handle); + +Static void ehci_device_clear_toggle(usbd_pipe_handle pipe); +Static void ehci_noop(usbd_pipe_handle pipe); + +Static int ehci_str(usb_string_descriptor_t *, int, char *); +Static void ehci_pcd(ehci_softc_t *, usbd_xfer_handle); +Static void ehci_pcd_able(ehci_softc_t *, int); +Static void ehci_pcd_enable(void *); +Static void ehci_disown(ehci_softc_t *, int, int); + +Static ehci_soft_qh_t *ehci_alloc_sqh(ehci_softc_t *); +Static void ehci_free_sqh(ehci_softc_t *, ehci_soft_qh_t *); + +Static ehci_soft_qtd_t *ehci_alloc_sqtd(ehci_softc_t *); +Static void ehci_free_sqtd(ehci_softc_t *, ehci_soft_qtd_t *); +Static usbd_status ehci_alloc_sqtd_chain(struct ehci_pipe *, + ehci_softc_t *, int, int, usbd_xfer_handle, + ehci_soft_qtd_t **, ehci_soft_qtd_t **); +Static void ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *, + ehci_soft_qtd_t *); + +Static usbd_status ehci_device_request(usbd_xfer_handle xfer); + +Static void ehci_add_qh(ehci_soft_qh_t *, ehci_soft_qh_t *); +Static void ehci_rem_qh(ehci_softc_t *, ehci_soft_qh_t *, + ehci_soft_qh_t *); +Static void ehci_set_qh_qtd(ehci_soft_qh_t *, ehci_soft_qtd_t *); +Static void ehci_sync_hc(ehci_softc_t *); + +Static void ehci_close_pipe(usbd_pipe_handle, ehci_soft_qh_t *); +Static void ehci_abort_xfer(usbd_xfer_handle, usbd_status); + +#ifdef USB_DEBUG +Static void ehci_dump_regs(ehci_softc_t *); +void ehci_dump(void); +Static ehci_softc_t *theehci; +Static void ehci_dump_link(ehci_link_t, int); +Static void ehci_dump_sqtds(ehci_soft_qtd_t *); +Static void ehci_dump_sqtd(ehci_soft_qtd_t *); +Static void ehci_dump_qtd(ehci_qtd_t *); +Static void ehci_dump_sqh(ehci_soft_qh_t *); +#ifdef DIAGNOSTIC +Static void ehci_dump_exfer(struct ehci_xfer *); +#endif +#endif + +#define EHCI_NULL htole32(EHCI_LINK_TERMINATE) + +#define EHCI_INTR_ENDPT 1 + +#define ehci_add_intr_list(sc, ex) \ + LIST_INSERT_HEAD(&(sc)->sc_intrhead, (ex), inext); +#define ehci_del_intr_list(ex) \ + do { \ + LIST_REMOVE((ex), inext); \ + (ex)->inext.le_prev = NULL; \ + } while (0) +#define ehci_active_intr_list(ex) ((ex)->inext.le_prev != NULL) + +Static struct usbd_bus_methods ehci_bus_methods = { + ehci_open, + ehci_softintr, + ehci_poll, + ehci_allocm, + ehci_freem, + ehci_allocx, + ehci_freex, +}; + +Static struct usbd_pipe_methods ehci_root_ctrl_methods = { + ehci_root_ctrl_transfer, + ehci_root_ctrl_start, + ehci_root_ctrl_abort, + ehci_root_ctrl_close, + ehci_noop, + ehci_root_ctrl_done, +}; + +Static struct usbd_pipe_methods ehci_root_intr_methods = { + ehci_root_intr_transfer, + ehci_root_intr_start, + ehci_root_intr_abort, + ehci_root_intr_close, + ehci_noop, + ehci_root_intr_done, +}; + +Static struct usbd_pipe_methods ehci_device_ctrl_methods = { + ehci_device_ctrl_transfer, + ehci_device_ctrl_start, + ehci_device_ctrl_abort, + ehci_device_ctrl_close, + ehci_noop, + ehci_device_ctrl_done, +}; + +Static struct usbd_pipe_methods ehci_device_intr_methods = { + ehci_device_intr_transfer, + ehci_device_intr_start, + ehci_device_intr_abort, + ehci_device_intr_close, + ehci_device_clear_toggle, + ehci_device_intr_done, +}; + +Static struct usbd_pipe_methods ehci_device_bulk_methods = { + ehci_device_bulk_transfer, + ehci_device_bulk_start, + ehci_device_bulk_abort, + ehci_device_bulk_close, + ehci_device_clear_toggle, + ehci_device_bulk_done, +}; + +Static struct usbd_pipe_methods ehci_device_isoc_methods = { + ehci_device_isoc_transfer, + ehci_device_isoc_start, + ehci_device_isoc_abort, + ehci_device_isoc_close, + ehci_noop, + ehci_device_isoc_done, +}; + +usbd_status +ehci_init(ehci_softc_t *sc) +{ + u_int32_t version, sparams, cparams, hcr; + u_int i; + usbd_status err; + ehci_soft_qh_t *sqh; + + DPRINTF(("ehci_init: start\n")); +#ifdef USB_DEBUG + theehci = sc; +#endif + + sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); + + version = EREAD2(sc, EHCI_HCIVERSION); + printf("%s: EHCI version %x.%x\n", USBDEVNAME(sc->sc_bus.bdev), + version >> 8, version & 0xff); + + sparams = EREAD4(sc, EHCI_HCSPARAMS); + DPRINTF(("ehci_init: sparams=0x%x\n", sparams)); + sc->sc_npcomp = EHCI_HCS_N_PCC(sparams); + if (EHCI_HCS_N_CC(sparams) != sc->sc_ncomp) { + printf("%s: wrong number of companions (%d != %d)\n", + USBDEVNAME(sc->sc_bus.bdev), + EHCI_HCS_N_CC(sparams), sc->sc_ncomp); + return (USBD_IOERROR); + } + if (sc->sc_ncomp > 0) { + printf("%s: companion controller%s, %d port%s each:", + USBDEVNAME(sc->sc_bus.bdev), sc->sc_ncomp!=1 ? "s" : "", + EHCI_HCS_N_PCC(sparams), + EHCI_HCS_N_PCC(sparams)!=1 ? "s" : ""); + for (i = 0; i < sc->sc_ncomp; i++) + printf(" %s", USBDEVNAME(sc->sc_comps[i]->bdev)); + printf("\n"); + } + sc->sc_noport = EHCI_HCS_N_PORTS(sparams); + cparams = EREAD4(sc, EHCI_HCCPARAMS); + DPRINTF(("ehci_init: cparams=0x%x\n", cparams)); + + if (EHCI_HCC_64BIT(cparams)) { + /* MUST clear segment register if 64 bit capable. */ + EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); + } + + sc->sc_bus.usbrev = USBREV_2_0; + + /* Reset the controller */ + DPRINTF(("%s: resetting\n", USBDEVNAME(sc->sc_bus.bdev))); + EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ + usb_delay_ms(&sc->sc_bus, 1); + EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); + for (i = 0; i < 100; i++) { + usb_delay_ms(&sc->sc_bus, 1); + hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; + if (!hcr) + break; + } + if (hcr) { + printf("%s: reset timeout\n", + USBDEVNAME(sc->sc_bus.bdev)); + return (USBD_IOERROR); + } + + /* frame list size at default, read back what we got and use that */ + switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) { + case 0: sc->sc_flsize = 1024*4; break; + case 1: sc->sc_flsize = 512*4; break; + case 2: sc->sc_flsize = 256*4; break; + case 3: return (USBD_IOERROR); + } + err = usb_allocmem(&sc->sc_bus, sc->sc_flsize, + EHCI_FLALIGN_ALIGN, &sc->sc_fldma); + if (err) + return (err); + DPRINTF(("%s: flsize=%d\n", USBDEVNAME(sc->sc_bus.bdev),sc->sc_flsize)); + + /* Set up the bus struct. */ + sc->sc_bus.methods = &ehci_bus_methods; + sc->sc_bus.pipe_size = sizeof(struct ehci_pipe); + +#if defined(__NetBSD__) || defined(__OpenBSD__) + sc->sc_powerhook = powerhook_establish(ehci_power, sc); + sc->sc_shutdownhook = shutdownhook_establish(ehci_shutdown, sc); +#endif + + sc->sc_eintrs = EHCI_NORMAL_INTRS; + + /* Allocate dummy QH that starts the async list. */ + sqh = ehci_alloc_sqh(sc); + if (sqh == NULL) { + err = USBD_NOMEM; + goto bad1; + } + /* Fill the QH */ + sqh->qh.qh_endp = + htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); + sqh->qh.qh_link = + htole32(sqh->physaddr | EHCI_LINK_QH); + sqh->qh.qh_curqtd = EHCI_NULL; + sqh->next = NULL; + /* Fill the overlay qTD */ + sqh->qh.qh_qtd.qtd_next = EHCI_NULL; + sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; + sqh->qh.qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); + sqh->sqtd = NULL; +#ifdef USB_DEBUG + if (ehcidebug) { + ehci_dump_sqh(sqh); + } +#endif + + /* Point to async list */ + sc->sc_async_head = sqh; + EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH); + + usb_callout_init(sc->sc_tmo_pcd); + + lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0); + + /* Enable interrupts */ + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + /* Turn on controller */ + EOWRITE4(sc, EHCI_USBCMD, + EHCI_CMD_ITC_8 | /* 8 microframes */ + (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | + EHCI_CMD_ASE | + /* EHCI_CMD_PSE | */ + EHCI_CMD_RS); + + /* Take over port ownership */ + EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); + + for (i = 0; i < 100; i++) { + usb_delay_ms(&sc->sc_bus, 1); + hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; + if (!hcr) + break; + } + if (hcr) { + printf("%s: run timeout\n", USBDEVNAME(sc->sc_bus.bdev)); + return (USBD_IOERROR); + } + + return (USBD_NORMAL_COMPLETION); + +#if 0 + bad2: + ehci_free_sqh(sc, sc->sc_async_head); +#endif + bad1: + usb_freemem(&sc->sc_bus, &sc->sc_fldma); + return (err); +} + +int +ehci_intr(void *v) +{ + ehci_softc_t *sc = v; + + if (sc == NULL || sc->sc_dying) + return (0); + + /* If we get an interrupt while polling, then just ignore it. */ + if (sc->sc_bus.use_polling) { +#ifdef DIAGNOSTIC + printf("ehci_intr: ignored interrupt while polling\n"); +#endif + return (0); + } + + return (ehci_intr1(sc)); +} + +Static int +ehci_intr1(ehci_softc_t *sc) +{ + u_int32_t intrs, eintrs; + + DPRINTFN(20,("ehci_intr1: enter\n")); + + /* In case the interrupt occurs before initialization has completed. */ + if (sc == NULL) { +#ifdef DIAGNOSTIC + printf("ehci_intr: sc == NULL\n"); +#endif + return (0); + } + + intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + + if (!intrs) + return (0); + + EOWRITE4(sc, EHCI_USBSTS, intrs); /* Acknowledge */ + eintrs = intrs & sc->sc_eintrs; + DPRINTFN(7, ("ehci_intr: sc=%p intrs=0x%x(0x%x) eintrs=0x%x\n", + sc, (u_int)intrs, EOREAD4(sc, EHCI_USBSTS), + (u_int)eintrs)); + if (!eintrs) + return (0); + + sc->sc_bus.intr_context++; + sc->sc_bus.no_intrs++; + if (eintrs & EHCI_STS_IAA) { + DPRINTF(("ehci_intr1: door bell\n")); + wakeup(&sc->sc_async_head); + eintrs &= ~EHCI_STS_IAA; + } + if (eintrs & (EHCI_STS_INT | EHCI_STS_ERRINT)) { + DPRINTFN(5,("ehci_intr1: %s %s\n", + eintrs & EHCI_STS_INT ? "INT" : "", + eintrs & EHCI_STS_ERRINT ? "ERRINT" : "")); + usb_schedsoftintr(&sc->sc_bus); + eintrs &= ~(EHCI_STS_INT | EHCI_STS_ERRINT); + } + if (eintrs & EHCI_STS_HSE) { + printf("%s: unrecoverable error, controller halted\n", + USBDEVNAME(sc->sc_bus.bdev)); + /* XXX what else */ + } + if (eintrs & EHCI_STS_PCD) { + ehci_pcd(sc, sc->sc_intrxfer); + /* + * Disable PCD interrupt for now, because it will be + * on until the port has been reset. + */ + ehci_pcd_able(sc, 0); + /* Do not allow RHSC interrupts > 1 per second */ + usb_callout(sc->sc_tmo_pcd, hz, ehci_pcd_enable, sc); + eintrs &= ~EHCI_STS_PCD; + } + + sc->sc_bus.intr_context--; + + if (eintrs != 0) { + /* Block unprocessed interrupts. */ + sc->sc_eintrs &= ~eintrs; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + printf("%s: blocking intrs 0x%x\n", + USBDEVNAME(sc->sc_bus.bdev), eintrs); + } + + return (1); +} + +void +ehci_pcd_able(ehci_softc_t *sc, int on) +{ + DPRINTFN(4, ("ehci_pcd_able: on=%d\n", on)); + if (on) + sc->sc_eintrs |= EHCI_STS_PCD; + else + sc->sc_eintrs &= ~EHCI_STS_PCD; + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); +} + +void +ehci_pcd_enable(void *v_sc) +{ + ehci_softc_t *sc = v_sc; + + ehci_pcd_able(sc, 1); +} + +void +ehci_pcd(ehci_softc_t *sc, usbd_xfer_handle xfer) +{ + usbd_pipe_handle pipe; + u_char *p; + int i, m; + + if (xfer == NULL) { + /* Just ignore the change. */ + return; + } + + pipe = xfer->pipe; + + p = KERNADDR(&xfer->dmabuf, 0); + m = min(sc->sc_noport, xfer->length * 8 - 1); + memset(p, 0, xfer->length); + for (i = 1; i <= m; i++) { + /* Pick out CHANGE bits from the status reg. */ + if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) + p[i/8] |= 1 << (i%8); + } + DPRINTF(("ehci_pcd: change=0x%02x\n", *p)); + xfer->actlen = xfer->length; + xfer->status = USBD_NORMAL_COMPLETION; + + usb_transfer_complete(xfer); +} + +void +ehci_softintr(void *v) +{ + ehci_softc_t *sc = v; + struct ehci_xfer *ex; + + DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev), + sc->sc_bus.intr_context)); + + sc->sc_bus.intr_context++; + + /* + * The only explanation I can think of for why EHCI is as brain dead + * as UHCI interrupt-wise is that Intel was involved in both. + * An interrupt just tells us that something is done, we have no + * clue what, so we need to scan through all active transfers. :-( + */ + for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = LIST_NEXT(ex, inext)) + ehci_check_intr(sc, ex); + +#ifdef USB_USE_SOFTINTR + if (sc->sc_softwake) { + sc->sc_softwake = 0; + wakeup(&sc->sc_softwake); + } +#endif /* USB_USE_SOFTINTR */ + + sc->sc_bus.intr_context--; +} + +/* Check for an interrupt. */ +void +ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex) +{ + ehci_soft_qtd_t *sqtd, *lsqtd; + u_int32_t status; + + DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex)); + + if (ex->sqtdstart == NULL) { + printf("ehci_check_intr: sqtdstart=NULL\n"); + return; + } + lsqtd = ex->sqtdend; +#ifdef DIAGNOSTIC + if (lsqtd == NULL) { + printf("ehci_check_intr: sqtd==0\n"); + return; + } +#endif + /* + * If the last TD is still active we need to check whether there + * is a an error somewhere in the middle, or whether there was a + * short packet (SPD and not ACTIVE). + */ + if (le32toh(lsqtd->qtd.qtd_status) & EHCI_QTD_ACTIVE) { + DPRINTFN(12, ("ehci_check_intr: active ex=%p\n", ex)); + for (sqtd = ex->sqtdstart; sqtd != lsqtd; sqtd=sqtd->nextqtd) { + status = le32toh(sqtd->qtd.qtd_status); + /* If there's an active QTD the xfer isn't done. */ + if (status & EHCI_QTD_ACTIVE) + break; + /* Any kind of error makes the xfer done. */ + if (status & EHCI_QTD_HALTED) + goto done; + /* We want short packets, and it is short: it's done */ + if (EHCI_QTD_SET_BYTES(status) != 0) + goto done; + } + DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", + ex, ex->sqtdstart)); + return; + } + done: + DPRINTFN(12, ("ehci_check_intr: ex=%p done\n", ex)); + usb_uncallout(ex->xfer.timeout_handle, ehci_timeout, ex); + ehci_idone(ex); +} + +void +ehci_idone(struct ehci_xfer *ex) +{ + usbd_xfer_handle xfer = &ex->xfer; +#ifdef USB_DEBUG + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; +#endif + ehci_soft_qtd_t *sqtd; + u_int32_t status = 0, nstatus; + int actlen; + + DPRINTFN(/*12*/2, ("ehci_idone: ex=%p\n", ex)); +#ifdef DIAGNOSTIC + { + int s = splhigh(); + if (ex->isdone) { + splx(s); +#ifdef USB_DEBUG + printf("ehci_idone: ex is done!\n "); + ehci_dump_exfer(ex); +#else + printf("ehci_idone: ex=%p is done!\n", ex); +#endif + return; + } + ex->isdone = 1; + splx(s); + } +#endif + + if (xfer->status == USBD_CANCELLED || + xfer->status == USBD_TIMEOUT) { + DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer)); + return; + } + +#ifdef USB_DEBUG + DPRINTFN(/*10*/2, ("ehci_idone: xfer=%p, pipe=%p ready\n", xfer, epipe)); + if (ehcidebug > 10) + ehci_dump_sqtds(ex->sqtdstart); +#endif + + /* The transfer is done, compute actual length and status. */ + actlen = 0; + for (sqtd = ex->sqtdstart; sqtd != NULL; sqtd = sqtd->nextqtd) { + nstatus = le32toh(sqtd->qtd.qtd_status); + if (nstatus & EHCI_QTD_ACTIVE) + break; + + status = nstatus; + if (EHCI_QTD_GET_PID(status) != EHCI_QTD_PID_SETUP) + actlen += sqtd->len - EHCI_QTD_GET_BYTES(status); + } + + /* If there are left over TDs we need to update the toggle. */ + if (sqtd != NULL) { + if (!(xfer->rqflags & URQ_REQUEST)) + printf("ehci_idone: need toggle update\n"); +#if 0 + epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token)); +#endif + } + + status &= EHCI_QTD_STATERRS; + DPRINTFN(/*10*/2, ("ehci_idone: len=%d, actlen=%d, status=0x%x\n", + xfer->length, actlen, status)); + xfer->actlen = actlen; + if (status != 0) { +#ifdef USB_DEBUG + char sbuf[128]; + + bitmask_snprintf((u_int32_t)status, + "\20\3MISSEDMICRO\4XACT\5BABBLE\6BABBLE" + "\7HALTED", + sbuf, sizeof(sbuf)); + + DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2, + ("ehci_idone: error, addr=%d, endpt=0x%02x, " + "status 0x%s\n", + xfer->pipe->device->address, + xfer->pipe->endpoint->edesc->bEndpointAddress, + sbuf)); + if (ehcidebug > 2) { + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(ex->sqtdstart); + } +#endif + if (status == EHCI_QTD_HALTED) + xfer->status = USBD_STALLED; + else + xfer->status = USBD_IOERROR; /* more info XXX */ + } else { + xfer->status = USBD_NORMAL_COMPLETION; + } + + usb_transfer_complete(xfer); + DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex)); +} + +/* + * Wait here until controller claims to have an interrupt. + * Then call ehci_intr and return. Use timeout to avoid waiting + * too long. + */ +void +ehci_waitintr(ehci_softc_t *sc, usbd_xfer_handle xfer) +{ + int timo = xfer->timeout; + int usecs; + u_int32_t intrs; + + xfer->status = USBD_IN_PROGRESS; + for (usecs = timo * 1000000 / hz; usecs > 0; usecs -= 1000) { + usb_delay_ms(&sc->sc_bus, 1); + if (sc->sc_dying) + break; + intrs = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)) & + sc->sc_eintrs; + DPRINTFN(15,("ehci_waitintr: 0x%04x\n", intrs)); +#ifdef USB_DEBUG + if (ehcidebug > 15) + ehci_dump_regs(sc); +#endif + if (intrs) { + ehci_intr1(sc); + if (xfer->status != USBD_IN_PROGRESS) + return; + } + } + + /* Timeout */ + DPRINTF(("ehci_waitintr: timeout\n")); + xfer->status = USBD_TIMEOUT; + usb_transfer_complete(xfer); + /* XXX should free TD */ +} + +void +ehci_poll(struct usbd_bus *bus) +{ + ehci_softc_t *sc = (ehci_softc_t *)bus; +#ifdef USB_DEBUG + static int last; + int new; + new = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); + if (new != last) { + DPRINTFN(10,("ehci_poll: intrs=0x%04x\n", new)); + last = new; + } +#endif + + if (EOREAD4(sc, EHCI_USBSTS) & sc->sc_eintrs) + ehci_intr1(sc); +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +ehci_detach(struct ehci_softc *sc, int flags) +{ + int rv = 0; + + if (sc->sc_child != NULL) + rv = config_detach(sc->sc_child, flags); + + if (rv != 0) + return (rv); + + usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc); + + if (sc->sc_powerhook != NULL) + powerhook_disestablish(sc->sc_powerhook); + if (sc->sc_shutdownhook != NULL) + shutdownhook_disestablish(sc->sc_shutdownhook); + + usb_delay_ms(&sc->sc_bus, 300); /* XXX let stray task complete */ + + /* XXX free other data structures XXX */ + + return (rv); +} +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +ehci_activate(device_ptr_t self, enum devact act) +{ + struct ehci_softc *sc = (struct ehci_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + + case DVACT_DEACTIVATE: + if (sc->sc_child != NULL) + rv = config_deactivate(sc->sc_child); + sc->sc_dying = 1; + break; + } + return (rv); +} +#endif + +/* + * Handle suspend/resume. + * + * We need to switch to polling mode here, because this routine is + * called from an intterupt context. This is all right since we + * are almost suspended anyway. + */ +#if defined(__NetBSD__) || defined(__OpenBSD__) +void +ehci_power(int why, void *v) +{ + ehci_softc_t *sc = v; + //u_int32_t ctl; + int s; + +#ifdef USB_DEBUG + DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why)); + ehci_dump_regs(sc); +#endif + + s = splhardusb(); + switch (why) { + case PWR_SUSPEND: + case PWR_STANDBY: + sc->sc_bus.use_polling++; +#if 0 +OOO + ctl = OREAD4(sc, EHCI_CONTROL) & ~EHCI_HCFS_MASK; + if (sc->sc_control == 0) { + /* + * Preserve register values, in case that APM BIOS + * does not recover them. + */ + sc->sc_control = ctl; + sc->sc_intre = OREAD4(sc, EHCI_INTERRUPT_ENABLE); + } + ctl |= EHCI_HCFS_SUSPEND; + OWRITE4(sc, EHCI_CONTROL, ctl); +#endif + usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT); + sc->sc_bus.use_polling--; + break; + case PWR_RESUME: + sc->sc_bus.use_polling++; +#if 0 +OOO + /* Some broken BIOSes do not recover these values */ + OWRITE4(sc, EHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0)); + OWRITE4(sc, EHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr); + OWRITE4(sc, EHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr); + if (sc->sc_intre) + OWRITE4(sc, EHCI_INTERRUPT_ENABLE, + sc->sc_intre & (EHCI_ALL_INTRS | EHCI_MIE)); + if (sc->sc_control) + ctl = sc->sc_control; + else + ctl = OREAD4(sc, EHCI_CONTROL); + ctl |= EHCI_HCFS_RESUME; + OWRITE4(sc, EHCI_CONTROL, ctl); + usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); + ctl = (ctl & ~EHCI_HCFS_MASK) | EHCI_HCFS_OPERATIONAL; + OWRITE4(sc, EHCI_CONTROL, ctl); + usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); + sc->sc_control = sc->sc_intre = 0; +#endif + sc->sc_bus.use_polling--; + break; + case PWR_SOFTSUSPEND: + case PWR_SOFTSTANDBY: + case PWR_SOFTRESUME: + break; + } + splx(s); +} +#endif + +/* + * Shut down the controller when the system is going down. + */ +#if defined(__NetBSD__) || defined(__OpenBSD__) +void +ehci_shutdown(void *v) +{ + ehci_softc_t *sc = v; + + DPRINTF(("ehci_shutdown: stopping the HC\n")); + EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ + EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); +} +#endif + +usbd_status +ehci_allocm(struct usbd_bus *bus, usb_dma_t *dma, u_int32_t size) +{ + usbd_status err; + + err = usb_allocmem(bus, size, 0, dma); +#ifdef USB_DEBUG + if (err) + printf("ehci_allocm: usb_allocmem()=%d\n", err); +#endif + return (err); +} + +void +ehci_freem(struct usbd_bus *bus, usb_dma_t *dma) +{ + usb_freemem(bus, dma); +} + +usbd_xfer_handle +ehci_allocx(struct usbd_bus *bus) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + usbd_xfer_handle xfer; + + xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); + if (xfer != NULL) { + SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next); +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_FREE) { + printf("uhci_allocx: xfer=%p not free, 0x%08x\n", xfer, + xfer->busy_free); + } +#endif + } else { + xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT); + } + if (xfer != NULL) { + memset(xfer, 0, sizeof (struct ehci_xfer)); +#ifdef DIAGNOSTIC + EXFER(xfer)->isdone = 1; + xfer->busy_free = XFER_BUSY; +#endif + } + return (xfer); +} + +void +ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) +{ + struct ehci_softc *sc = (struct ehci_softc *)bus; + +#ifdef DIAGNOSTIC + if (xfer->busy_free != XFER_BUSY) { + printf("ehci_freex: xfer=%p not busy, 0x%08x\n", xfer, + xfer->busy_free); + return; + } + xfer->busy_free = XFER_FREE; + if (!EXFER(xfer)->isdone) { + printf("ehci_freex: !isdone\n"); + return; + } +#endif + SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); +} + +Static void +ehci_device_clear_toggle(usbd_pipe_handle pipe) +{ + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + + DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n", + epipe, epipe->sqh->qh.qh_qtd.qtd_status)); +#ifdef USB_DEBUG + if (ehcidebug) + usbd_dump_pipe(pipe); +#endif + epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); +} + +Static void +ehci_noop(usbd_pipe_handle pipe) +{ +} + +#ifdef USB_DEBUG +void +ehci_dump_regs(ehci_softc_t *sc) +{ + int i; + printf("cmd=0x%08x, sts=0x%08x, ien=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), + EOREAD4(sc, EHCI_USBSTS), + EOREAD4(sc, EHCI_USBINTR)); + printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", + EOREAD4(sc, EHCI_FRINDEX), + EOREAD4(sc, EHCI_CTRLDSSEGMENT), + EOREAD4(sc, EHCI_PERIODICLISTBASE), + EOREAD4(sc, EHCI_ASYNCLISTADDR)); + for (i = 1; i <= sc->sc_noport; i++) + printf("port %d status=0x%08x\n", i, + EOREAD4(sc, EHCI_PORTSC(i))); +} + +/* + * Unused function - this is meant to be called from a kernel + * debugger. + */ +void +ehci_dump() +{ + ehci_dump_regs(theehci); +} + +void +ehci_dump_link(ehci_link_t link, int type) +{ + link = le32toh(link); + printf("0x%08x", link); + if (link & EHCI_LINK_TERMINATE) + printf(""); + else { + printf("<"); + if (type) { + switch (EHCI_LINK_TYPE(link)) { + case EHCI_LINK_ITD: printf("ITD"); break; + case EHCI_LINK_QH: printf("QH"); break; + case EHCI_LINK_SITD: printf("SITD"); break; + case EHCI_LINK_FSTN: printf("FSTN"); break; + } + } + printf(">"); + } +} + +void +ehci_dump_sqtds(ehci_soft_qtd_t *sqtd) +{ + int i; + u_int32_t stop; + + stop = 0; + for (i = 0; sqtd && i < 20 && !stop; sqtd = sqtd->nextqtd, i++) { + ehci_dump_sqtd(sqtd); + stop = sqtd->qtd.qtd_next & EHCI_LINK_TERMINATE; + } + if (sqtd) + printf("dump aborted, too many TDs\n"); +} + +void +ehci_dump_sqtd(ehci_soft_qtd_t *sqtd) +{ + printf("QTD(%p) at 0x%08x:\n", sqtd, sqtd->physaddr); + ehci_dump_qtd(&sqtd->qtd); +} + +void +ehci_dump_qtd(ehci_qtd_t *qtd) +{ + u_int32_t s; + char sbuf[128]; + + printf(" next="); ehci_dump_link(qtd->qtd_next, 0); + printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0); + printf("\n"); + s = le32toh(qtd->qtd_status); + bitmask_snprintf(EHCI_QTD_GET_STATUS(s), + "\20\10ACTIVE\7HALTED\6BUFERR\5BABBLE\4XACTERR" + "\3MISSED\2SPLIT\1PING", sbuf, sizeof(sbuf)); + printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", + s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), + EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); + printf(" cerr=%d pid=%d stat=0x%s\n", EHCI_QTD_GET_CERR(s), + EHCI_QTD_GET_PID(s), sbuf); + for (s = 0; s < 5; s++) + printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s])); +} + +void +ehci_dump_sqh(ehci_soft_qh_t *sqh) +{ + ehci_qh_t *qh = &sqh->qh; + u_int32_t endp, endphub; + + printf("QH(%p) at 0x%08x:\n", sqh, sqh->physaddr); + printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n"); + endp = le32toh(qh->qh_endp); + printf(" endp=0x%08x\n", endp); + printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", + EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), + EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), + EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); + printf(" mpl=0x%x ctl=%d nrl=%d\n", + EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), + EHCI_QH_GET_NRL(endp)); + endphub = le32toh(qh->qh_endphub); + printf(" endphub=0x%08x\n", endphub); + printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", + EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), + EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), + EHCI_QH_GET_MULT(endphub)); + printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n"); + printf("Overlay qTD:\n"); + ehci_dump_qtd(&qh->qh_qtd); +} + +#ifdef DIAGNOSTIC +Static void +ehci_dump_exfer(struct ehci_xfer *ex) +{ + printf("ehci_dump_exfer: ex=%p\n", ex); +} +#endif +#endif + +usbd_status +ehci_open(usbd_pipe_handle pipe) +{ + usbd_device_handle dev = pipe->device; + ehci_softc_t *sc = (ehci_softc_t *)dev->bus; + usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; + u_int8_t addr = dev->address; + u_int8_t xfertype = ed->bmAttributes & UE_XFERTYPE; + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_soft_qh_t *sqh; + usbd_status err; + int s; + int speed, naks; + + DPRINTFN(1, ("ehci_open: pipe=%p, addr=%d, endpt=%d (%d)\n", + pipe, addr, ed->bEndpointAddress, sc->sc_addr)); + + if (sc->sc_dying) + return (USBD_IOERROR); + + if (addr == sc->sc_addr) { + switch (ed->bEndpointAddress) { + case USB_CONTROL_ENDPOINT: + pipe->methods = &ehci_root_ctrl_methods; + break; + case UE_DIR_IN | EHCI_INTR_ENDPT: + pipe->methods = &ehci_root_intr_methods; + break; + default: + return (USBD_INVAL); + } + return (USBD_NORMAL_COMPLETION); + } + + /* XXX All this stuff is only valid for async. */ + switch (dev->speed) { + case USB_SPEED_LOW: speed = EHCI_QH_SPEED_LOW; break; + case USB_SPEED_FULL: speed = EHCI_QH_SPEED_FULL; break; + case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; + default: panic("ehci_open: bad device speed %d", dev->speed); + } + naks = 8; /* XXX */ + sqh = ehci_alloc_sqh(sc); + if (sqh == NULL) + goto bad0; + /* qh_link filled when the QH is added */ + sqh->qh.qh_endp = htole32( + EHCI_QH_SET_ADDR(addr) | + EHCI_QH_SET_ENDPT(ed->bEndpointAddress) | + EHCI_QH_SET_EPS(speed) | /* XXX */ + /* XXX EHCI_QH_DTC ? */ + EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | + (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? + EHCI_QH_CTL : 0) | + EHCI_QH_SET_NRL(naks) + ); + sqh->qh.qh_endphub = htole32( + EHCI_QH_SET_MULT(1) + /* XXX TT stuff */ + /* XXX interrupt mask */ + ); + sqh->qh.qh_curqtd = EHCI_NULL; + /* Fill the overlay qTD */ + sqh->qh.qh_qtd.qtd_next = EHCI_NULL; + sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL; + sqh->qh.qh_qtd.qtd_status = htole32(0); + + epipe->sqh = sqh; + + switch (xfertype) { + case UE_CONTROL: + err = usb_allocmem(&sc->sc_bus, sizeof(usb_device_request_t), + 0, &epipe->u.ctl.reqdma); +#ifdef USB_DEBUG + if (err) + printf("ehci_open: usb_allocmem()=%d\n", err); +#endif + if (err) + goto bad1; + pipe->methods = &ehci_device_ctrl_methods; + s = splusb(); + ehci_add_qh(sqh, sc->sc_async_head); + splx(s); + break; + case UE_BULK: + pipe->methods = &ehci_device_bulk_methods; + s = splusb(); + ehci_add_qh(sqh, sc->sc_async_head); + splx(s); + break; + case UE_INTERRUPT: + pipe->methods = &ehci_device_intr_methods; + return (USBD_INVAL); + case UE_ISOCHRONOUS: + pipe->methods = &ehci_device_isoc_methods; + return (USBD_INVAL); + default: + return (USBD_INVAL); + } + return (USBD_NORMAL_COMPLETION); + + bad1: + ehci_free_sqh(sc, sqh); + bad0: + return (USBD_NOMEM); +} + +/* + * Add an ED to the schedule. Called at splusb(). + */ +void +ehci_add_qh(ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) +{ + SPLUSBCHECK; + + sqh->next = head->next; + sqh->qh.qh_link = head->qh.qh_link; + head->next = sqh; + head->qh.qh_link = htole32(sqh->physaddr | EHCI_LINK_QH); + +#ifdef USB_DEBUG + if (ehcidebug > 5) { + printf("ehci_add_qh:\n"); + ehci_dump_sqh(sqh); + } +#endif +} + +/* + * Remove an ED from the schedule. Called at splusb(). + */ +void +ehci_rem_qh(ehci_softc_t *sc, ehci_soft_qh_t *sqh, ehci_soft_qh_t *head) +{ + ehci_soft_qh_t *p; + + SPLUSBCHECK; + /* XXX */ + for (p = head; p != NULL && p->next != sqh; p = p->next) + ; + if (p == NULL) + panic("ehci_rem_qh: ED not found"); + p->next = sqh->next; + p->qh.qh_link = sqh->qh.qh_link; + + ehci_sync_hc(sc); +} + +void +ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) +{ + /* Halt while we are messing. */ + sqh->qh.qh_qtd.qtd_status |= htole32(EHCI_QTD_HALTED); + sqh->qh.qh_curqtd = 0; + sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); + sqh->sqtd = sqtd; + /* Keep toggle, clear the rest, including length. */ + sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_TOGGLE); +} + +/* + * Ensure that the HC has released all references to the QH. We do this + * by asking for a Async Advance Doorbell interrupt and then we wait for + * the interrupt. + * To make this easier we first obtain exclusive use of the doorbell. + */ +void +ehci_sync_hc(ehci_softc_t *sc) +{ + int s, error; + + if (sc->sc_dying) { + DPRINTFN(2,("ehci_sync_hc: dying\n")); + return; + } + DPRINTFN(2,("ehci_sync_hc: enter\n")); + /* get doorbell */ + lockmgr(&sc->sc_doorbell_lock, LK_EXCLUSIVE, NULL, NULL); + s = splhardusb(); + /* ask for doorbell */ + EOWRITE4(sc, EHCI_USBCMD, EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); + DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); + error = tsleep(&sc->sc_async_head, PZERO, "ehcidi", hz); /* bell wait */ + DPRINTFN(1,("ehci_sync_hc: cmd=0x%08x sts=0x%08x\n", + EOREAD4(sc, EHCI_USBCMD), EOREAD4(sc, EHCI_USBSTS))); + splx(s); + /* release doorbell */ + lockmgr(&sc->sc_doorbell_lock, LK_RELEASE, NULL, NULL); +#ifdef DIAGNOSTIC + if (error) + printf("ehci_sync_hc: tsleep() = %d\n", error); +#endif + DPRINTFN(2,("ehci_sync_hc: exit\n")); +} + +/***********/ + +/* + * Data structures and routines to emulate the root hub. + */ +Static usb_device_descriptor_t ehci_devd = { + USB_DEVICE_DESCRIPTOR_SIZE, + UDESC_DEVICE, /* type */ + {0x00, 0x02}, /* USB version */ + UDCLASS_HUB, /* class */ + UDSUBCLASS_HUB, /* subclass */ + UDPROTO_HSHUBSTT, /* protocol */ + 64, /* max packet */ + {0},{0},{0x00,0x01}, /* device id */ + 1,2,0, /* string indicies */ + 1 /* # of configurations */ +}; + +Static usb_device_qualifier_t ehci_odevd = { + USB_DEVICE_DESCRIPTOR_SIZE, + UDESC_DEVICE_QUALIFIER, /* type */ + {0x00, 0x02}, /* USB version */ + UDCLASS_HUB, /* class */ + UDSUBCLASS_HUB, /* subclass */ + UDPROTO_FSHUB, /* protocol */ + 64, /* max packet */ + 1, /* # of configurations */ + 0 +}; + +Static usb_config_descriptor_t ehci_confd = { + USB_CONFIG_DESCRIPTOR_SIZE, + UDESC_CONFIG, + {USB_CONFIG_DESCRIPTOR_SIZE + + USB_INTERFACE_DESCRIPTOR_SIZE + + USB_ENDPOINT_DESCRIPTOR_SIZE}, + 1, + 1, + 0, + UC_SELF_POWERED, + 0 /* max power */ +}; + +Static usb_interface_descriptor_t ehci_ifcd = { + USB_INTERFACE_DESCRIPTOR_SIZE, + UDESC_INTERFACE, + 0, + 0, + 1, + UICLASS_HUB, + UISUBCLASS_HUB, + UIPROTO_HSHUBSTT, + 0 +}; + +Static usb_endpoint_descriptor_t ehci_endpd = { + USB_ENDPOINT_DESCRIPTOR_SIZE, + UDESC_ENDPOINT, + UE_DIR_IN | EHCI_INTR_ENDPT, + UE_INTERRUPT, + {8, 0}, /* max packet */ + 255 +}; + +Static usb_hub_descriptor_t ehci_hubd = { + USB_HUB_DESCRIPTOR_SIZE, + UDESC_HUB, + 0, + {0,0}, + 0, + 0, + {0}, +}; + +Static int +ehci_str(p, l, s) + usb_string_descriptor_t *p; + int l; + char *s; +{ + int i; + + if (l == 0) + return (0); + p->bLength = 2 * strlen(s) + 2; + if (l == 1) + return (1); + p->bDescriptorType = UDESC_STRING; + l -= 2; + for (i = 0; s[i] && l > 1; i++, l -= 2) + USETW2(p->bString[i], 0, s[i]); + return (2*i+2); +} + +/* + * Simulate a hardware hub by handling all the necessary requests. + */ +Static usbd_status +ehci_root_ctrl_transfer(usbd_xfer_handle xfer) +{ + usbd_status err; + + /* Insert last in queue. */ + err = usb_insert_transfer(xfer); + if (err) + return (err); + + /* Pipe isn't running, start first */ + return (ehci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +Static usbd_status +ehci_root_ctrl_start(usbd_xfer_handle xfer) +{ + ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; + usb_device_request_t *req; + void *buf = NULL; + int port, i; + int s, len, value, index, l, totlen = 0; + usb_port_status_t ps; + usb_hub_descriptor_t hubd; + usbd_status err; + u_int32_t v; + + if (sc->sc_dying) + return (USBD_IOERROR); + +#ifdef DIAGNOSTIC + if (!(xfer->rqflags & URQ_REQUEST)) + /* XXX panic */ + return (USBD_INVAL); +#endif + req = &xfer->request; + + DPRINTFN(4,("ehci_root_ctrl_control type=0x%02x request=%02x\n", + req->bmRequestType, req->bRequest)); + + len = UGETW(req->wLength); + value = UGETW(req->wValue); + index = UGETW(req->wIndex); + + if (len != 0) + buf = KERNADDR(&xfer->dmabuf, 0); + +#define C(x,y) ((x) | ((y) << 8)) + switch(C(req->bRequest, req->bmRequestType)) { + case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): + case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): + case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): + /* + * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops + * for the integrated root hub. + */ + break; + case C(UR_GET_CONFIG, UT_READ_DEVICE): + if (len > 0) { + *(u_int8_t *)buf = sc->sc_conf; + totlen = 1; + } + break; + case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): + DPRINTFN(8,("ehci_root_ctrl_control wValue=0x%04x\n", value)); + switch(value >> 8) { + case UDESC_DEVICE: + if ((value & 0xff) != 0) { + err = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); + USETW(ehci_devd.idVendor, sc->sc_id_vendor); + memcpy(buf, &ehci_devd, l); + break; + /* + * We can't really operate at another speed, but the spec says + * we need this descriptor. + */ + case UDESC_DEVICE_QUALIFIER: + if ((value & 0xff) != 0) { + err = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); + memcpy(buf, &ehci_odevd, l); + break; + /* + * We can't really operate at another speed, but the spec says + * we need this descriptor. + */ + case UDESC_OTHER_SPEED_CONFIGURATION: + case UDESC_CONFIG: + if ((value & 0xff) != 0) { + err = USBD_IOERROR; + goto ret; + } + totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE); + memcpy(buf, &ehci_confd, l); + ((usb_config_descriptor_t *)buf)->bDescriptorType = + value >> 8; + buf = (char *)buf + l; + len -= l; + l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE); + totlen += l; + memcpy(buf, &ehci_ifcd, l); + buf = (char *)buf + l; + len -= l; + l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); + totlen += l; + memcpy(buf, &ehci_endpd, l); + break; + case UDESC_STRING: + if (len == 0) + break; + *(u_int8_t *)buf = 0; + totlen = 1; + switch (value & 0xff) { + case 1: /* Vendor */ + totlen = ehci_str(buf, len, sc->sc_vendor); + break; + case 2: /* Product */ + totlen = ehci_str(buf, len, "EHCI root hub"); + break; + } + break; + default: + err = USBD_IOERROR; + goto ret; + } + break; + case C(UR_GET_INTERFACE, UT_READ_INTERFACE): + if (len > 0) { + *(u_int8_t *)buf = 0; + totlen = 1; + } + break; + case C(UR_GET_STATUS, UT_READ_DEVICE): + if (len > 1) { + USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED); + totlen = 2; + } + break; + case C(UR_GET_STATUS, UT_READ_INTERFACE): + case C(UR_GET_STATUS, UT_READ_ENDPOINT): + if (len > 1) { + USETW(((usb_status_t *)buf)->wStatus, 0); + totlen = 2; + } + break; + case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): + if (value >= USB_MAX_DEVICES) { + err = USBD_IOERROR; + goto ret; + } + sc->sc_addr = value; + break; + case C(UR_SET_CONFIG, UT_WRITE_DEVICE): + if (value != 0 && value != 1) { + err = USBD_IOERROR; + goto ret; + } + sc->sc_conf = value; + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): + break; + case C(UR_SET_FEATURE, UT_WRITE_DEVICE): + case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): + case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): + err = USBD_IOERROR; + goto ret; + case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): + break; + case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): + break; + /* Hub requests */ + case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): + break; + case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): + DPRINTFN(8, ("ehci_root_ctrl_control: UR_CLEAR_PORT_FEATURE " + "port=%d feature=%d\n", + index, value)); + if (index < 1 || index > sc->sc_noport) { + err = USBD_IOERROR; + goto ret; + } + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; + switch(value) { + case UHF_PORT_ENABLE: + EOWRITE4(sc, port, v &~ EHCI_PS_PE); + break; + case UHF_PORT_SUSPEND: + EOWRITE4(sc, port, v &~ EHCI_PS_SUSP); + break; + case UHF_PORT_POWER: + EOWRITE4(sc, port, v &~ EHCI_PS_PP); + break; + case UHF_PORT_TEST: + DPRINTFN(2,("ehci_root_ctrl_transfer: clear port test " + "%d\n", index)); + break; + case UHF_PORT_INDICATOR: + DPRINTFN(2,("ehci_root_ctrl_transfer: clear port ind " + "%d\n", index)); + EOWRITE4(sc, port, v &~ EHCI_PS_PIC); + break; + case UHF_C_PORT_CONNECTION: + EOWRITE4(sc, port, v | EHCI_PS_CSC); + break; + case UHF_C_PORT_ENABLE: + EOWRITE4(sc, port, v | EHCI_PS_PEC); + break; + case UHF_C_PORT_SUSPEND: + /* how? */ + break; + case UHF_C_PORT_OVER_CURRENT: + EOWRITE4(sc, port, v | EHCI_PS_OCC); + break; + case UHF_C_PORT_RESET: + sc->sc_isreset = 0; + break; + default: + err = USBD_IOERROR; + goto ret; + } +#if 0 + switch(value) { + case UHF_C_PORT_CONNECTION: + case UHF_C_PORT_ENABLE: + case UHF_C_PORT_SUSPEND: + case UHF_C_PORT_OVER_CURRENT: + case UHF_C_PORT_RESET: + /* Enable RHSC interrupt if condition is cleared. */ + if ((OREAD4(sc, port) >> 16) == 0) + ehci_pcd_able(sc, 1); + break; + default: + break; + } +#endif + break; + case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): + if (value != 0) { + err = USBD_IOERROR; + goto ret; + } + hubd = ehci_hubd; + hubd.bNbrPorts = sc->sc_noport; + v = EOREAD4(sc, EHCI_HCSPARAMS); + USETW(hubd.wHubCharacteristics, + EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH | + EHCI_HCS_P_INCICATOR(EREAD4(sc, EHCI_HCSPARAMS)) + ? UHD_PORT_IND : 0); + hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ + for (i = 0, l = sc->sc_noport; l > 0; i++, l -= 8, v >>= 8) + hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */ + hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; + l = min(len, hubd.bDescLength); + totlen = l; + memcpy(buf, &hubd, l); + break; + case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): + if (len != 4) { + err = USBD_IOERROR; + goto ret; + } + memset(buf, 0, len); /* ? XXX */ + totlen = len; + break; + case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): + DPRINTFN(8,("ehci_root_ctrl_transfer: get port status i=%d\n", + index)); + if (index < 1 || index > sc->sc_noport) { + err = USBD_IOERROR; + goto ret; + } + if (len != 4) { + err = USBD_IOERROR; + goto ret; + } + v = EOREAD4(sc, EHCI_PORTSC(index)); + DPRINTFN(8,("ehci_root_ctrl_transfer: port status=0x%04x\n", + v)); + i = UPS_HIGH_SPEED; + if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; + if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; + if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND; + if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; + if (v & EHCI_PS_PR) i |= UPS_RESET; + if (v & EHCI_PS_PP) i |= UPS_PORT_POWER; + USETW(ps.wPortStatus, i); + i = 0; + if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; + if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; + if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; + if (sc->sc_isreset) i |= UPS_C_PORT_RESET; + USETW(ps.wPortChange, i); + l = min(len, sizeof ps); + memcpy(buf, &ps, l); + totlen = l; + break; + case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): + err = USBD_IOERROR; + goto ret; + case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): + break; + case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): + if (index < 1 || index > sc->sc_noport) { + err = USBD_IOERROR; + goto ret; + } + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; + switch(value) { + case UHF_PORT_ENABLE: + EOWRITE4(sc, port, v | EHCI_PS_PE); + break; + case UHF_PORT_SUSPEND: + EOWRITE4(sc, port, v | EHCI_PS_SUSP); + break; + case UHF_PORT_RESET: + DPRINTFN(5,("ehci_root_ctrl_transfer: reset port %d\n", + index)); + if (EHCI_PS_IS_LOWSPEED(v)) { + /* Low speed device, give up ownership. */ + ehci_disown(sc, index, 1); + break; + } + /* Start reset sequence. */ + v &= ~ (EHCI_PS_PE | EHCI_PS_PR); + EOWRITE4(sc, port, v | EHCI_PS_PR); + /* Wait for reset to complete. */ + usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY); + if (sc->sc_dying) { + err = USBD_IOERROR; + goto ret; + } + /* Terminate reset sequence. */ + EOWRITE4(sc, port, v); + /* Wait for HC to complete reset. */ + usb_delay_ms(&sc->sc_bus, EHCI_PORT_RESET_COMPLETE); + if (sc->sc_dying) { + err = USBD_IOERROR; + goto ret; + } + v = EOREAD4(sc, port); + DPRINTF(("ehci after reset, status=0x%08x\n", v)); + if (v & EHCI_PS_PR) { + printf("%s: port reset timeout\n", + USBDEVNAME(sc->sc_bus.bdev)); + return (USBD_TIMEOUT); + } + if (!(v & EHCI_PS_PE)) { + /* Not a high speed device, give up ownership.*/ + ehci_disown(sc, index, 0); + break; + } + sc->sc_isreset = 1; + DPRINTF(("ehci port %d reset, status = 0x%08x\n", + index, v)); + break; + case UHF_PORT_POWER: + DPRINTFN(2,("ehci_root_ctrl_transfer: set port power " + "%d\n", index)); + EOWRITE4(sc, port, v | EHCI_PS_PP); + break; + case UHF_PORT_TEST: + DPRINTFN(2,("ehci_root_ctrl_transfer: set port test " + "%d\n", index)); + break; + case UHF_PORT_INDICATOR: + DPRINTFN(2,("ehci_root_ctrl_transfer: set port ind " + "%d\n", index)); + EOWRITE4(sc, port, v | EHCI_PS_PIC); + break; + default: + err = USBD_IOERROR; + goto ret; + } + break; + case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): + case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): + case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): + case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): + break; + default: + err = USBD_IOERROR; + goto ret; + } + xfer->actlen = totlen; + err = USBD_NORMAL_COMPLETION; + ret: + xfer->status = err; + s = splusb(); + usb_transfer_complete(xfer); + splx(s); + return (USBD_IN_PROGRESS); +} + +void +ehci_disown(ehci_softc_t *sc, int index, int lowspeed) +{ + int port; + u_int32_t v; + + DPRINTF(("ehci_disown: index=%d lowspeed=%d\n", index, lowspeed)); +#ifdef DIAGNOSTIC + if (sc->sc_npcomp != 0) { + int i = (index-1) / sc->sc_npcomp; + if (i >= sc->sc_ncomp) + printf("%s: strange port\n", + USBDEVNAME(sc->sc_bus.bdev)); + else + printf("%s: handing over %s speed device on " + "port %d to %s\n", + USBDEVNAME(sc->sc_bus.bdev), + lowspeed ? "low" : "full", + index, USBDEVNAME(sc->sc_comps[i]->bdev)); + } else { + printf("%s: npcomp == 0\n", USBDEVNAME(sc->sc_bus.bdev)); + } +#endif + port = EHCI_PORTSC(index); + v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR; + EOWRITE4(sc, port, v | EHCI_PS_PO); +} + +/* Abort a root control request. */ +Static void +ehci_root_ctrl_abort(usbd_xfer_handle xfer) +{ + /* Nothing to do, all transfers are synchronous. */ +} + +/* Close the root pipe. */ +Static void +ehci_root_ctrl_close(usbd_pipe_handle pipe) +{ + DPRINTF(("ehci_root_ctrl_close\n")); + /* Nothing to do. */ +} + +void +ehci_root_intr_done(usbd_xfer_handle xfer) +{ + xfer->hcpriv = NULL; +} + +Static usbd_status +ehci_root_intr_transfer(usbd_xfer_handle xfer) +{ + usbd_status err; + + /* Insert last in queue. */ + err = usb_insert_transfer(xfer); + if (err) + return (err); + + /* Pipe isn't running, start first */ + return (ehci_root_intr_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +Static usbd_status +ehci_root_intr_start(usbd_xfer_handle xfer) +{ + usbd_pipe_handle pipe = xfer->pipe; + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + + if (sc->sc_dying) + return (USBD_IOERROR); + + sc->sc_intrxfer = xfer; + + return (USBD_IN_PROGRESS); +} + +/* Abort a root interrupt request. */ +Static void +ehci_root_intr_abort(usbd_xfer_handle xfer) +{ + int s; + + if (xfer->pipe->intrxfer == xfer) { + DPRINTF(("ehci_root_intr_abort: remove\n")); + xfer->pipe->intrxfer = NULL; + } + xfer->status = USBD_CANCELLED; + s = splusb(); + usb_transfer_complete(xfer); + splx(s); +} + +/* Close the root pipe. */ +Static void +ehci_root_intr_close(usbd_pipe_handle pipe) +{ + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + + DPRINTF(("ehci_root_intr_close\n")); + + sc->sc_intrxfer = NULL; +} + +void +ehci_root_ctrl_done(usbd_xfer_handle xfer) +{ + xfer->hcpriv = NULL; +} + +/************************/ + +ehci_soft_qh_t * +ehci_alloc_sqh(ehci_softc_t *sc) +{ + ehci_soft_qh_t *sqh; + usbd_status err; + int i, offs; + usb_dma_t dma; + + if (sc->sc_freeqhs == NULL) { + DPRINTFN(2, ("ehci_alloc_sqh: allocating chunk\n")); + err = usb_allocmem(&sc->sc_bus, EHCI_SQH_SIZE * EHCI_SQH_CHUNK, + EHCI_PAGE_SIZE, &dma); +#ifdef USB_DEBUG + if (err) + printf("ehci_alloc_sqh: usb_allocmem()=%d\n", err); +#endif + if (err) + return (NULL); + for(i = 0; i < EHCI_SQH_CHUNK; i++) { + offs = i * EHCI_SQH_SIZE; + sqh = KERNADDR(&dma, offs); + sqh->physaddr = DMAADDR(&dma, offs); + sqh->next = sc->sc_freeqhs; + sc->sc_freeqhs = sqh; + } + } + sqh = sc->sc_freeqhs; + sc->sc_freeqhs = sqh->next; + memset(&sqh->qh, 0, sizeof(ehci_qh_t)); + sqh->next = NULL; + return (sqh); +} + +void +ehci_free_sqh(ehci_softc_t *sc, ehci_soft_qh_t *sqh) +{ + sqh->next = sc->sc_freeqhs; + sc->sc_freeqhs = sqh; +} + +ehci_soft_qtd_t * +ehci_alloc_sqtd(ehci_softc_t *sc) +{ + ehci_soft_qtd_t *sqtd; + usbd_status err; + int i, offs; + usb_dma_t dma; + int s; + + if (sc->sc_freeqtds == NULL) { + DPRINTFN(2, ("ehci_alloc_sqtd: allocating chunk\n")); + err = usb_allocmem(&sc->sc_bus, EHCI_SQTD_SIZE*EHCI_SQTD_CHUNK, + EHCI_PAGE_SIZE, &dma); +#ifdef USB_DEBUG + if (err) + printf("ehci_alloc_sqtd: usb_allocmem()=%d\n", err); +#endif + if (err) + return (NULL); + s = splusb(); + for(i = 0; i < EHCI_SQTD_CHUNK; i++) { + offs = i * EHCI_SQTD_SIZE; + sqtd = KERNADDR(&dma, offs); + sqtd->physaddr = DMAADDR(&dma, offs); + sqtd->nextqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd; + } + splx(s); + } + + s = splusb(); + sqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd->nextqtd; + memset(&sqtd->qtd, 0, sizeof(ehci_qtd_t)); + sqtd->nextqtd = NULL; + sqtd->xfer = NULL; + splx(s); + + return (sqtd); +} + +void +ehci_free_sqtd(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd) +{ + int s; + + s = splusb(); + sqtd->nextqtd = sc->sc_freeqtds; + sc->sc_freeqtds = sqtd; + splx(s); +} + +usbd_status +ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, + int alen, int rd, usbd_xfer_handle xfer, + ehci_soft_qtd_t **sp, ehci_soft_qtd_t **ep) +{ + ehci_soft_qtd_t *next, *cur; + ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys; + u_int32_t qtdstatus; + int len, curlen, offset; + int i; + usb_dma_t *dma = &xfer->dmabuf; + + DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); + + offset = 0; + len = alen; + dataphys = DMAADDR(dma, 0); + dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1)); + qtdstatus = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | + EHCI_QTD_SET_CERR(3) + /* IOC set below */ + /* BYTES set below */ + /* XXX Data toggle */ + ); + + cur = ehci_alloc_sqtd(sc); + *sp = cur; + if (cur == NULL) + goto nomem; + for (;;) { + dataphyspage = EHCI_PAGE(dataphys); + /* The EHCI hardware can handle at most 5 pages. */ +#if defined(__NetBSD__) || defined(__OpenBSD__) + if (dataphyslastpage - dataphyspage < + EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE) { + /* we can handle it in this QTD */ + curlen = len; +#elif defined(__FreeBSD__) + /* XXX This is pretty broken: Because we do not allocate + * a contiguous buffer (contiguous in physical pages) we + * can only transfer one page in one go. + * So check whether the start and end of the buffer are on + * the same page. + */ + if (dataphyspage == dataphyslastpage) { + curlen = len; +#endif + } else { +#if defined(__NetBSD__) || defined(__OpenBSD__) + /* must use multiple TDs, fill as much as possible. */ + curlen = EHCI_QTD_NBUFFERS * EHCI_PAGE_SIZE - + EHCI_PAGE_OFFSET(dataphys); +#ifdef DIAGNOSTIC + if (curlen > len) { + printf("ehci_alloc_sqtd_chain: curlen=0x%x " + "len=0x%x offs=0x%x\n", curlen, len, + EHCI_PAGE_OFFSET(dataphys)); + printf("lastpage=0x%x page=0x%x phys=0x%x\n", + dataphyslastpage, dataphyspage, + dataphys); + curlen = len; + } +#endif +#elif defined(__FreeBSD__) + /* See comment above (XXX) */ + curlen = EHCI_PAGE_SIZE - + EHCI_PAGE_MASK(dataphys); +#endif + + /* XXX true for EHCI? */ + /* the length must be a multiple of the max size */ + curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " + "curlen=%d\n", curlen)); +#ifdef DIAGNOSTIC + if (curlen == 0) + panic("ehci_alloc_std: curlen == 0"); +#endif + } + DPRINTFN(4,("ehci_alloc_sqtd_chain: dataphys=0x%08x " + "dataphyslastpage=0x%08x len=%d curlen=%d\n", + dataphys, dataphyslastpage, + len, curlen)); + len -= curlen; + + if (len != 0) { + next = ehci_alloc_sqtd(sc); + if (next == NULL) + goto nomem; + nextphys = next->physaddr; + } else { + next = NULL; + nextphys = EHCI_NULL; + } + + for (i = 0; i * EHCI_PAGE_SIZE < curlen; i++) { + ehci_physaddr_t a = dataphys + i * EHCI_PAGE_SIZE; + if (i != 0) /* use offset only in first buffer */ + a = EHCI_PAGE(a); + cur->qtd.qtd_buffer[i] = htole32(a); +#ifdef DIAGNOSTIC + if (i >= EHCI_QTD_NBUFFERS) { + printf("ehci_alloc_sqtd_chain: i=%d\n", i); + goto nomem; + } +#endif + } + cur->nextqtd = next; + cur->qtd.qtd_next = cur->qtd.qtd_altnext = htole32(nextphys); + cur->qtd.qtd_status = + qtdstatus | htole32(EHCI_QTD_SET_BYTES(curlen)); + cur->xfer = xfer; + cur->len = curlen; + DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n", + dataphys, dataphys + curlen)); + if (len == 0) + break; + DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); + offset += curlen; + dataphys = DMAADDR(dma, offset); + cur = next; + } + cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); + *ep = cur; + + DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n", + *sp, *ep)); + + return (USBD_NORMAL_COMPLETION); + + nomem: + /* XXX free chain */ + DPRINTFN(-1,("ehci_alloc_sqtd_chain: no memory\n")); + return (USBD_NOMEM); +} + +Static void +ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd, + ehci_soft_qtd_t *sqtdend) +{ + ehci_soft_qtd_t *p; + int i; + + DPRINTFN(10,("ehci_free_sqtd_chain: sqtd=%p sqtdend=%p\n", + sqtd, sqtdend)); + + for (i = 0; sqtd != sqtdend; sqtd = p, i++) { + p = sqtd->nextqtd; + ehci_free_sqtd(sc, sqtd); + } +} + +/****************/ + +/* + * Close a reqular pipe. + * Assumes that there are no pending transactions. + */ +void +ehci_close_pipe(usbd_pipe_handle pipe, ehci_soft_qh_t *head) +{ + struct ehci_pipe *epipe = (struct ehci_pipe *)pipe; + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + ehci_soft_qh_t *sqh = epipe->sqh; + int s; + + s = splusb(); + ehci_rem_qh(sc, sqh, head); + splx(s); + ehci_free_sqh(sc, epipe->sqh); +} + +/* + * Abort a device request. + * If this routine is called at splusb() it guarantees that the request + * will be removed from the hardware scheduling and that the callback + * for it will be called with USBD_CANCELLED status. + * It's impossible to guarantee that the requested transfer will not + * have happened since the hardware runs concurrently. + * If the transaction has already happened we rely on the ordinary + * interrupt processing to process it. + * XXX This is most probably wrong. + */ +void +ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status) +{ +#define exfer EXFER(xfer) + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; + ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus; + ehci_soft_qh_t *sqh = epipe->sqh; + ehci_soft_qtd_t *sqtd; + ehci_physaddr_t cur; + u_int32_t qhstatus; + int s; + int hit; + + DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe)); + + if (sc->sc_dying) { + /* If we're dying, just do the software part. */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer); + usb_transfer_complete(xfer); + splx(s); + return; + } + + if (xfer->device->bus->intr_context || !curproc) + panic("ehci_abort_xfer: not in process context"); + + /* + * Step 1: Make interrupt routine and hardware ignore xfer. + */ + s = splusb(); + xfer->status = status; /* make software ignore it */ + usb_uncallout(xfer->timeout_handle, ehci_timeout, xfer); + qhstatus = sqh->qh.qh_qtd.qtd_status; + sqh->qh.qh_qtd.qtd_status = qhstatus | htole32(EHCI_QTD_HALTED); + for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { + sqtd->qtd.qtd_status |= htole32(EHCI_QTD_HALTED); + if (sqtd == exfer->sqtdend) + break; + } + splx(s); + + /* + * Step 2: Wait until we know hardware has finished any possible + * use of the xfer. Also make sure the soft interrupt routine + * has run. + */ + ehci_sync_hc(sc); + s = splusb(); +#ifdef USB_USE_SOFTINTR + sc->sc_softwake = 1; +#endif /* USB_USE_SOFTINTR */ + usb_schedsoftintr(&sc->sc_bus); +#ifdef USB_USE_SOFTINTR + tsleep(&sc->sc_softwake, PZERO, "ehciab", 0); +#endif /* USB_USE_SOFTINTR */ + splx(s); + + /* + * Step 3: Remove any vestiges of the xfer from the hardware. + * The complication here is that the hardware may have executed + * beyond the xfer we're trying to abort. So as we're scanning + * the TDs of this xfer we check if the hardware points to + * any of them. + */ + s = splusb(); /* XXX why? */ + cur = EHCI_LINK_ADDR(le32toh(sqh->qh.qh_curqtd)); + hit = 0; + for (sqtd = exfer->sqtdstart; ; sqtd = sqtd->nextqtd) { + hit |= cur == sqtd->physaddr; + if (sqtd == exfer->sqtdend) + break; + } + sqtd = sqtd->nextqtd; + /* Zap curqtd register if hardware pointed inside the xfer. */ + if (hit && sqtd != NULL) { + DPRINTFN(1,("ehci_abort_xfer: cur=0x%08x\n", sqtd->physaddr)); + sqh->qh.qh_curqtd = htole32(sqtd->physaddr); /* unlink qTDs */ + sqh->qh.qh_qtd.qtd_status = qhstatus; + } else { + DPRINTFN(1,("ehci_abort_xfer: no hit\n")); + } + + /* + * Step 4: Execute callback. + */ +#ifdef DIAGNOSTIC + exfer->isdone = 1; +#endif + usb_transfer_complete(xfer); + + splx(s); +#undef exfer +} + +void +ehci_timeout(void *addr) +{ + struct ehci_xfer *exfer = addr; + struct ehci_pipe *epipe = (struct ehci_pipe *)exfer->xfer.pipe; + ehci_softc_t *sc = (ehci_softc_t *)epipe->pipe.device->bus; + + DPRINTF(("ehci_timeout: exfer=%p\n", exfer)); +#ifdef USB_DEBUG + if (ehcidebug > 1) + usbd_dump_pipe(exfer->xfer.pipe); +#endif + + if (sc->sc_dying) { + ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT); + return; + } + + /* Execute the abort in a process context. */ + usb_init_task(&exfer->abort_task, ehci_timeout_task, addr); + usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task); +} + +void +ehci_timeout_task(void *addr) +{ + usbd_xfer_handle xfer = addr; + int s; + + DPRINTF(("ehci_timeout_task: xfer=%p\n", xfer)); + + s = splusb(); + ehci_abort_xfer(xfer, USBD_TIMEOUT); + splx(s); +} + +/************************/ + +Static usbd_status +ehci_device_ctrl_transfer(usbd_xfer_handle xfer) +{ + usbd_status err; + + /* Insert last in queue. */ + err = usb_insert_transfer(xfer); + if (err) + return (err); + + /* Pipe isn't running, start first */ + return (ehci_device_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +Static usbd_status +ehci_device_ctrl_start(usbd_xfer_handle xfer) +{ + ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; + usbd_status err; + + if (sc->sc_dying) + return (USBD_IOERROR); + +#ifdef DIAGNOSTIC + if (!(xfer->rqflags & URQ_REQUEST)) { + /* XXX panic */ + printf("ehci_device_ctrl_transfer: not a request\n"); + return (USBD_INVAL); + } +#endif + + err = ehci_device_request(xfer); + if (err) + return (err); + + if (sc->sc_bus.use_polling) + ehci_waitintr(sc, xfer); + return (USBD_IN_PROGRESS); +} + +void +ehci_device_ctrl_done(usbd_xfer_handle xfer) +{ + struct ehci_xfer *ex = EXFER(xfer); + ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; + /*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/ + + DPRINTFN(10,("ehci_ctrl_done: xfer=%p\n", xfer)); + +#ifdef DIAGNOSTIC + if (!(xfer->rqflags & URQ_REQUEST)) { + panic("ehci_ctrl_done: not a request"); + } +#endif + + if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { + ehci_del_intr_list(ex); /* remove from active list */ + ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + } + + DPRINTFN(5, ("ehci_ctrl_done: length=%d\n", xfer->actlen)); +} + +/* Abort a device control request. */ +Static void +ehci_device_ctrl_abort(usbd_xfer_handle xfer) +{ + DPRINTF(("ehci_device_ctrl_abort: xfer=%p\n", xfer)); + ehci_abort_xfer(xfer, USBD_CANCELLED); +} + +/* Close a device control pipe. */ +Static void +ehci_device_ctrl_close(usbd_pipe_handle pipe) +{ + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + /*struct ehci_pipe *epipe = (struct ehci_pipe *)pipe;*/ + + DPRINTF(("ehci_device_ctrl_close: pipe=%p\n", pipe)); + ehci_close_pipe(pipe, sc->sc_async_head); +} + +usbd_status +ehci_device_request(usbd_xfer_handle xfer) +{ +#define exfer EXFER(xfer) + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; + usb_device_request_t *req = &xfer->request; + usbd_device_handle dev = epipe->pipe.device; + ehci_softc_t *sc = (ehci_softc_t *)dev->bus; + int addr = dev->address; + ehci_soft_qtd_t *setup, *stat, *next; + ehci_soft_qh_t *sqh; + int isread; + int len; + usbd_status err; + int s; + + isread = req->bmRequestType & UT_READ; + len = UGETW(req->wLength); + + DPRINTFN(3,("ehci_device_control type=0x%02x, request=0x%02x, " + "wValue=0x%04x, wIndex=0x%04x len=%d, addr=%d, endpt=%d\n", + req->bmRequestType, req->bRequest, UGETW(req->wValue), + UGETW(req->wIndex), len, addr, + epipe->pipe.endpoint->edesc->bEndpointAddress)); + + setup = ehci_alloc_sqtd(sc); + if (setup == NULL) { + err = USBD_NOMEM; + goto bad1; + } + stat = ehci_alloc_sqtd(sc); + if (stat == NULL) { + err = USBD_NOMEM; + goto bad2; + } + + sqh = epipe->sqh; + epipe->u.ctl.length = len; + + /* XXX + * Since we're messing with the QH we must know the HC is in sync. + * This needs to go away since it slows down control transfers. + * Removing it entails: + * - fill the QH only once with addr & wMaxPacketSize + * - put the correct data toggles in the qtds and set DTC + */ + /* ehci_sync_hc(sc); */ + /* Update device address and length since they may have changed. */ + /* XXX This only needs to be done once, but it's too early in open. */ + /* XXXX Should not touch ED here! */ + sqh->qh.qh_endp = + (sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QG_MPLMASK))) | + htole32( + EHCI_QH_SET_ADDR(addr) | + /* EHCI_QH_DTC | */ + EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)) + ); + /* Clear toggle */ + sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); + + /* Set up data transaction */ + if (len != 0) { + ehci_soft_qtd_t *end; + + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, + &next, &end); + if (err) + goto bad3; + end->nextqtd = stat; + end->qtd.qtd_next = + end->qtd.qtd_altnext = htole32(stat->physaddr); + /* Start toggle at 1. */ + /*next->qtd.td_flags |= htole32(EHCI_QTD_TOGGLE);*/ + } else { + next = stat; + } + + memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); + + setup->qtd.qtd_status = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_BYTES(sizeof *req) + ); + setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0)); + setup->nextqtd = next; + setup->qtd.qtd_next = setup->qtd.qtd_altnext = htole32(next->physaddr); + setup->xfer = xfer; + setup->len = sizeof *req; + + stat->qtd.qtd_status = htole32( + EHCI_QTD_ACTIVE | + EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | + EHCI_QTD_SET_CERR(3) | + EHCI_QTD_IOC + ); + stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ + stat->nextqtd = NULL; + stat->qtd.qtd_next = stat->qtd.qtd_altnext = EHCI_NULL; + stat->xfer = xfer; + stat->len = 0; + +#ifdef USB_DEBUG + if (ehcidebug > 5) { + DPRINTF(("ehci_device_request:\n")); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(setup); + } +#endif + + exfer->sqtdstart = setup; + exfer->sqtdend = stat; +#ifdef DIAGNOSTIC + if (!exfer->isdone) { + printf("ehci_device_request: not done, exfer=%p\n", exfer); + } + exfer->isdone = 0; +#endif + + /* Insert qTD in QH list. */ + s = splusb(); + ehci_set_qh_qtd(sqh, setup); + if (xfer->timeout && !sc->sc_bus.use_polling) { + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + ehci_timeout, xfer); + } + ehci_add_intr_list(sc, exfer); + xfer->status = USBD_IN_PROGRESS; + splx(s); + +#ifdef USB_DEBUG + if (ehcidebug > 10) { + DPRINTF(("ehci_device_request: status=%x\n", + EOREAD4(sc, EHCI_USBSTS))); + delay(10000); + ehci_dump_regs(sc); + ehci_dump_sqh(sc->sc_async_head); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(setup); + } +#endif + + return (USBD_NORMAL_COMPLETION); + + bad3: + ehci_free_sqtd(sc, stat); + bad2: + ehci_free_sqtd(sc, setup); + bad1: + DPRINTFN(-1,("ehci_device_request: no memory\n")); + xfer->status = err; + usb_transfer_complete(xfer); + return (err); +#undef exfer +} + +/************************/ + +Static usbd_status +ehci_device_bulk_transfer(usbd_xfer_handle xfer) +{ + usbd_status err; + + /* Insert last in queue. */ + err = usb_insert_transfer(xfer); + if (err) + return (err); + + /* Pipe isn't running, start first */ + return (ehci_device_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); +} + +usbd_status +ehci_device_bulk_start(usbd_xfer_handle xfer) +{ +#define exfer EXFER(xfer) + struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; + usbd_device_handle dev = epipe->pipe.device; + ehci_softc_t *sc = (ehci_softc_t *)dev->bus; + ehci_soft_qtd_t *data, *dataend; + ehci_soft_qh_t *sqh; + usbd_status err; + int len, isread, endpt; + int s; + + DPRINTFN(2, ("ehci_device_bulk_transfer: xfer=%p len=%d flags=%d\n", + xfer, xfer->length, xfer->flags)); + + if (sc->sc_dying) + return (USBD_IOERROR); + +#ifdef DIAGNOSTIC + if (xfer->rqflags & URQ_REQUEST) + panic("ehci_device_bulk_transfer: a request"); +#endif + + len = xfer->length; + endpt = epipe->pipe.endpoint->edesc->bEndpointAddress; + isread = UE_GET_DIR(endpt) == UE_DIR_IN; + sqh = epipe->sqh; + + epipe->u.bulk.length = len; + + err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &data, + &dataend); + if (err) { + DPRINTFN(-1,("ehci_device_bulk_transfer: no memory\n")); + xfer->status = err; + usb_transfer_complete(xfer); + return (err); + } + +#ifdef USB_DEBUG + if (ehcidebug > 5) { + DPRINTF(("ehci_device_bulk_transfer: data(1)\n")); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(data); + } +#endif + + /* Set up interrupt info. */ + exfer->sqtdstart = data; + exfer->sqtdend = dataend; +#ifdef DIAGNOSTIC + if (!exfer->isdone) { + printf("ehci_device_bulk_transfer: not done, ex=%p\n", exfer); + } + exfer->isdone = 0; +#endif + + s = splusb(); + ehci_set_qh_qtd(sqh, data); + if (xfer->timeout && !sc->sc_bus.use_polling) { + usb_callout(xfer->timeout_handle, MS_TO_TICKS(xfer->timeout), + ehci_timeout, xfer); + } + ehci_add_intr_list(sc, exfer); + xfer->status = USBD_IN_PROGRESS; + splx(s); + +#ifdef USB_DEBUG + if (ehcidebug > 10) { + DPRINTF(("ehci_device_bulk_transfer: data(2)\n")); + delay(10000); + DPRINTF(("ehci_device_bulk_transfer: data(3)\n")); + ehci_dump_regs(sc); +#if 0 + printf("async_head:\n"); + ehci_dump_sqh(sc->sc_async_head); +#endif + printf("sqh:\n"); + ehci_dump_sqh(sqh); + ehci_dump_sqtds(data); + } +#endif + + if (sc->sc_bus.use_polling) + ehci_waitintr(sc, xfer); + + return (USBD_IN_PROGRESS); +#undef exfer +} + +Static void +ehci_device_bulk_abort(usbd_xfer_handle xfer) +{ + DPRINTF(("ehci_device_bulk_abort: xfer=%p\n", xfer)); + ehci_abort_xfer(xfer, USBD_CANCELLED); +} + +/* + * Close a device bulk pipe. + */ +Static void +ehci_device_bulk_close(usbd_pipe_handle pipe) +{ + ehci_softc_t *sc = (ehci_softc_t *)pipe->device->bus; + + DPRINTF(("ehci_device_bulk_close: pipe=%p\n", pipe)); + ehci_close_pipe(pipe, sc->sc_async_head); +} + +void +ehci_device_bulk_done(usbd_xfer_handle xfer) +{ + struct ehci_xfer *ex = EXFER(xfer); + ehci_softc_t *sc = (ehci_softc_t *)xfer->pipe->device->bus; + /*struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe;*/ + + DPRINTFN(10,("ehci_bulk_done: xfer=%p, actlen=%d\n", + xfer, xfer->actlen)); + + if (xfer->status != USBD_NOMEM && ehci_active_intr_list(ex)) { + ehci_del_intr_list(ex); /* remove from active list */ + ehci_free_sqtd_chain(sc, ex->sqtdstart, NULL); + } + + DPRINTFN(5, ("ehci_bulk_done: length=%d\n", xfer->actlen)); +} + +/************************/ + +Static usbd_status ehci_device_intr_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static usbd_status ehci_device_intr_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static void ehci_device_intr_abort(usbd_xfer_handle xfer) { } +Static void ehci_device_intr_close(usbd_pipe_handle pipe) { } +Static void ehci_device_intr_done(usbd_xfer_handle xfer) { } + +/************************/ + +Static usbd_status ehci_device_isoc_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static usbd_status ehci_device_isoc_start(usbd_xfer_handle xfer) { return USBD_IOERROR; } +Static void ehci_device_isoc_abort(usbd_xfer_handle xfer) { } +Static void ehci_device_isoc_close(usbd_pipe_handle pipe) { } +Static void ehci_device_isoc_done(usbd_xfer_handle xfer) { } Property changes on: stable/4/sys/dev/usb/ehci.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/modules/dcons/Makefile =================================================================== --- stable/4/sys/modules/dcons/Makefile (nonexistent) +++ stable/4/sys/modules/dcons/Makefile (revision 122530) @@ -0,0 +1,18 @@ +# $Id: Makefile,v 1.6 2003/10/24 15:41:26 simokawa Exp $ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/dcons + +KMOD = dcons +SRCS = dcons.c dcons.h \ + opt_dcons.h opt_ddb.h opt_comconsole.h + +opt_ddb.h: + echo "#define DDB 1" > $@ + +opt_comconsole.h: + echo "#define ALT_BREAK_TO_DEBUGGER 1" > $@ + +CFLAGS+= -I${.CURDIR}/../.. + +.include Property changes on: stable/4/sys/modules/dcons/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/sys/modules/dcons_crom/Makefile =================================================================== --- stable/4/sys/modules/dcons_crom/Makefile (nonexistent) +++ stable/4/sys/modules/dcons_crom/Makefile (revision 122530) @@ -0,0 +1,14 @@ +# $Id: Makefile,v 1.6 2003/10/24 15:43:24 simokawa Exp $ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/dcons + +KMOD = dcons_crom +SRCS = dcons_crom.c dcons.h \ + bus_if.h device_if.h + +#KMODDEPS = firewire dcons + +CFLAGS+= -I${.CURDIR}/../.. + +.include Property changes on: stable/4/sys/modules/dcons_crom/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/usr.sbin/dconschat/dconschat.c =================================================================== --- stable/4/usr.sbin/dconschat/dconschat.c (nonexistent) +++ stable/4/usr.sbin/dconschat/dconschat.c (revision 122530) @@ -0,0 +1,958 @@ +/* + * Copyright (C) 2003 + * Hidetoshi Shimokawa. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Hidetoshi Shimokawa. + * + * 4. Neither the name of the author 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 REGENTS 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 REGENTS 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. + * + * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $ + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define DCONS_POLL_HZ 100 + +#define RETRY 3 + +#ifdef CSRVAL_VENDOR_PRIVATE +#define USE_CROM 1 +#else +#define USE_CROM 0 +#endif + +int verbose = 0; +int tc_set = 0; + +#define IS_CONSOLE(p) ((p)->port == 0) +#define IS_GDB(p) ((p)->port == 1) + +static struct dcons_state { + int fd; + kvm_t *kd; + int kq; + off_t paddr; +#define F_READY (1 << 1) +#define F_RD_ONLY (1 << 2) +#define F_ALT_BREAK (1 << 3) +#define F_TELNET (1 << 4) +#define F_USE_CROM (1 << 5) +#define F_ONE_SHOT (1 << 6) +#define F_REPLAY (1 << 7) + int flags; + enum { + TYPE_KVM, + TYPE_FW + } type; + int escape_state; + struct dcons_port { + int port; + struct dcons_ch o; + struct dcons_ch i; + u_int32_t optr; + u_int32_t iptr; + int s; + int infd; + int outfd; + struct addrinfo *res; + int skip_read; + } port[DCONS_NPORT]; + struct timespec to; + struct timespec zero; + struct termios tsave; +} sc; + +static int +dread(struct dcons_state *dc, void *buf, size_t n, off_t offset) +{ + switch (dc->type) { + case TYPE_FW: + return (pread(dc->fd, buf, n, offset)); + case TYPE_KVM: + return (kvm_read(dc->kd, offset, buf, n)); + } + return (-1); +} + +static int +dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset) +{ + if ((dc->flags & F_RD_ONLY) != 0) + return (n); + + switch (dc->type) { + case TYPE_FW: + return (pwrite(dc->fd, buf, n, offset)); + case TYPE_KVM: + return (kvm_write(dc->kd, offset, buf, n)); + } + return (-1); +} + +static void +dconschat_cleanup(int sig) +{ + struct dcons_state *dc; + + dc = ≻ + if (tc_set != 0) + tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave); + + if (sig > 0) + printf("\n[dconschat exiting with signal %d ...]\n", sig); + else + printf("\n[dconschat exiting...]\n"); + exit(0); +} + +#if USE_CROM +static int +dconschat_get_crom(struct dcons_state *dc) +{ + off_t addr; + int i, state = 0; + u_int32_t buf, hi = 0, lo = 0; + struct csrreg *reg; + + reg = (struct csrreg *)&buf; + addr = 0xffff; + addr = (addr << 32) | 0xf0000400; + for (i = 20; i < 0x400; i += 4) { + if (dread(dc, &buf, 4, addr + i) < 0) { + if (verbose) + warn("crom read faild"); + return (-1); + } + buf = ntohl(buf); + if (verbose) + printf("%d %02x %06x\n", state, reg->key, reg->val); + switch (state) { + case 0: + if (reg->key == CSRKEY_SPEC && + reg->val == CSRVAL_VENDOR_PRIVATE) + state = 1; + break; + case 1: + if (reg->key == CSRKEY_VER && + reg->val == DCONS_CSR_VAL_VER) + state = 2; + break; + case 2: + if (reg->key == DCONS_CSR_KEY_HI) + hi = reg->val; + else if (reg->key == DCONS_CSR_KEY_LO) { + lo = reg->val; + goto out; + } + break; + } + } + /* not found */ + return (-1); +out: + if (verbose) + printf("addr: %06x %06x\n", hi, lo); + dc->paddr = ((off_t)hi << 24) | lo; + return (0); +} +#endif + +static void +dconschat_ready(struct dcons_state *dc, int ready, char *reason) +{ + static char oldreason[64] = ""; + int old; + + old = (dc->flags & F_READY) ? 1 : 0; + + if (ready) { + dc->flags |= F_READY; + if (ready != old) + printf("[dcons connected]\r\n"); + oldreason[0] = 0; + } else { + dc->flags &= ~F_READY; + if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) { + printf("[dcons disconnected (%s)]\r\n", reason); + strlcpy(oldreason, reason, sizeof(oldreason)); + } + } +} + +static int +dconschat_fetch_header(struct dcons_state *dc) +{ + char ebuf[64]; + struct dcons_buf dbuf; + int j; + +#if USE_CROM + if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) { + if (dconschat_get_crom(dc)) { + dconschat_ready(dc, 0, "get crom failed"); + return (-1); + } + } +#endif + + if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) { + dconschat_ready(dc, 0, "read header failed"); + return (-1); + } + if (dbuf.magic != htonl(DCONS_MAGIC)) { + if ((dc->flags & F_USE_CROM) !=0) + dc->paddr = 0; + snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic); + dconschat_ready(dc, 0, ebuf); + return (-1); + } + if (ntohl(dbuf.version) != DCONS_VERSION) { + snprintf(ebuf, sizeof(ebuf), +#if __FreeBSD_version < 500000 + "wrong version %ld,%d", +#else + "wrong version %d,%d", +#endif + ntohl(dbuf.version), DCONS_VERSION); + /* XXX exit? */ + dconschat_ready(dc, 0, ebuf); + return (-1); + } + + for (j = 0; j < DCONS_NPORT; j++) { + struct dcons_ch *o, *i; + off_t newbuf; + int new = 0; + + o = &dc->port[j].o; + newbuf = dc->paddr + ntohl(dbuf.ooffset[j]); + o->size = ntohl(dbuf.osize[j]); + + if (newbuf != o->buf) { + /* buffer address has changes */ + new = 1; + o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT; + o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK; + o->buf = newbuf; + } + + i = &dc->port[j].i; + i->size = ntohl(dbuf.isize[j]); + i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT; + i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK; + i->buf = dc->paddr + ntohl(dbuf.ioffset[j]); + + if (verbose) { + printf("port %d size offset gen pos\n", j); +#if __FreeBSD_version < 500000 + printf("output: %5d %6ld %5d %5d\n" + "input : %5d %6ld %5d %5d\n", +#else + printf("output: %5d %6d %5d %5d\n" + "input : %5d %6d %5d %5d\n", +#endif + o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos, + i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos); + } + + if (IS_CONSOLE(&dc->port[j]) && new && + (dc->flags & F_REPLAY) !=0) { + if (o->gen > 0) + o->gen --; + else + o->pos = 0; + } + } + dconschat_ready(dc, 1, NULL); + return(0); +} + +static int +dconschat_get_ptr (struct dcons_state *dc) { + int dlen, i; + u_int32_t ptr[DCONS_NPORT*2+1]; + static int retry = RETRY; + +again: + dlen = dread(dc, &ptr, sizeof(ptr), + dc->paddr + __offsetof(struct dcons_buf, magic)); + + if (dlen < 0) { + if (errno == ETIMEDOUT) + if (retry -- > 0) + goto again; + dconschat_ready(dc, 0, "get ptr failed"); + return(-1); + } + if (ptr[0] != htonl(DCONS_MAGIC)) { + dconschat_ready(dc, 0, "wrong magic"); + return(-1); + } + retry = RETRY; + for (i = 0; i < DCONS_NPORT; i ++) { + dc->port[i].optr = ntohl(ptr[i + 1]); + dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]); + } + return(0); +} + +#define MAX_XFER 2048 +static int +dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len) +{ + struct dcons_ch *ch; + u_int32_t ptr, pos, gen, next_gen; + int rlen, dlen, lost; + int retry = RETRY; + + ch = &dc->port[port].o; + ptr = dc->port[port].optr; + gen = ptr >> DCONS_GEN_SHIFT; + pos = ptr & DCONS_POS_MASK; + if (gen == ch->gen && pos == ch->pos) + return (-1); + + next_gen = DCONS_NEXT_GEN(ch->gen); + /* XXX sanity check */ + if (gen == ch->gen) { + if (pos > ch->pos) + goto ok; + lost = ch->size * DCONS_GEN_MASK - ch->pos; + ch->pos = 0; + } else if (gen == next_gen) { + if (pos <= ch->pos) + goto ok; + lost = pos - ch->pos; + ch->pos = pos; + } else { + lost = gen - ch->gen; + if (lost < 0) + lost += DCONS_GEN_MASK; + if (verbose) + printf("[genskip %d]", lost); + lost = lost * ch->size - ch->pos; + ch->pos = 0; + ch->gen = gen; + } + /* generation skipped !! */ + /* XXX discard */ + if (verbose) + printf("[lost %d]", lost); +ok: + if (gen == ch->gen) + rlen = pos - ch->pos; + else + rlen = ch->size - ch->pos; + + if (rlen > MAX_XFER) + rlen = MAX_XFER; + if (rlen > len) + rlen = len; + +#if 1 + if (verbose) + printf("[%d]", rlen); fflush(stdout); +#endif + +again: + dlen = dread(dc, buf, rlen, ch->buf + ch->pos); + if (dlen < 0) { + if (errno == ETIMEDOUT) + if (retry -- > 0) + goto again; + dconschat_ready(dc, 0, "read buffer failed"); + return(-1); + } + if (dlen != rlen) + warnx("dlen(%d) != rlen(%d)\n", dlen, rlen); + ch->pos += dlen; + if (ch->pos >= ch->size) { + ch->gen = next_gen; + ch->pos = 0; + if (verbose) + printf("read_dcons: gen=%d", ch->gen); + } + return (dlen); +} + +static int +dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen) +{ + struct dcons_ch *ch; + u_int32_t ptr; + int len, wlen; + int retry = RETRY; + + ch = &dc->port[port].i; + ptr = dc->port[port].iptr; + + /* the others may advance the pointer sync with it */ + ch->gen = ptr >> DCONS_GEN_SHIFT; + ch->pos = ptr & DCONS_POS_MASK; + + while(blen > 0) { + wlen = MIN(blen, ch->size - ch->pos); + wlen = MIN(wlen, MAX_XFER); + len = dwrite(dc, buf, wlen, ch->buf + ch->pos); + if (len < 0) { + if (errno == ETIMEDOUT) + if (retry -- > 0) + continue; /* try again */ + dconschat_ready(dc, 0, "write buffer failed"); + return(-1); + } + ch->pos += len; + buf += len; + blen -= len; + if (ch->pos >= ch->size) { + ch->gen = DCONS_NEXT_GEN(ch->gen); + ch->pos = 0; + if (verbose) + printf("write_dcons: gen=%d", ch->gen); + + } + } + + ptr = DCONS_MAKE_PTR(ch); + dc->port[port].iptr = ptr; + + if (verbose > 2) + printf("(iptr: 0x%x)", ptr); +again: + len = dwrite(dc, &ptr, sizeof(u_int32_t), + dc->paddr + __offsetof(struct dcons_buf, iptr[port])); + if (len < 0) { + if (errno == ETIMEDOUT) + if (retry -- > 0) + goto again; + dconschat_ready(dc, 0, "write ptr failed"); + return(-1); + } + return(0); +} + +static int +dconschat_write_socket(int fd, char *buf, int len) +{ + write(fd, buf, len); + if (verbose > 1) { + buf[len] = 0; + printf("[%s]", buf); + } + return (0); +} + +static void +dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport) +{ + struct addrinfo hints, *res; + int on = 1, error; + char service[10]; + struct kevent kev; + struct dcons_port *p; + + p = &dc->port[port]; + p->port = port; + p->infd = p->outfd = -1; + + if (sport < 0) + return; + + if (sport == 0) { + + /* Use stdin and stdout */ + p->infd = STDIN_FILENO; + p->outfd = STDOUT_FILENO; + p->s = -1; + if (tc_set == 0 && + tcgetattr(STDIN_FILENO, &dc->tsave) == 0) { + struct termios traw; + + traw = dc->tsave; + cfmakeraw(&traw); + tcsetattr(STDIN_FILENO, TCSADRAIN, &traw); + tc_set = 1; + } + EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, + (void *)p); + kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); + return; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; +#if 1 /* gdb can talk v4 only */ + hints.ai_family = PF_INET; +#else + hints.ai_family = PF_UNSPEC; +#endif + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (verbose) + printf("%s:%d for port %d\n", + host == NULL ? "*" : host, sport, port); + snprintf(service, sizeof(service), "%d", sport); + error = getaddrinfo(host, service, &hints, &res); + if (error) + errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); + p->res = res; + p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (p->s < 0) + err(1, "socket"); + setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) { + err(1, "bind"); + } + if (listen(p->s, 1) < 0) + err(1, "listen"); + EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p); + error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to); + if (error < 0) + err(1, "kevent"); + return; +} + +static int +dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p) +{ + int foo, ns, flags; + struct kevent kev; + + /* accept connection */ + foo = p->res->ai_addrlen; + ns = accept(p->s, p->res->ai_addr, &foo); + if (ns < 0) + err(1, "accept"); + if (verbose) + printf("port%d accepted\n", p->port); + + flags = fcntl(ns, F_GETFL, 0); + flags |= O_NDELAY; + fcntl(ns, F_SETFL, flags); +#if 1 + if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) { + char sga[] = {IAC, WILL, TELOPT_SGA}; + char linemode[] = {IAC, DONT, TELOPT_LINEMODE}; + char echo[] = {IAC, WILL, TELOPT_ECHO}; + char bin[] = {IAC, DO, TELOPT_BINARY}; + + write(ns, sga, sizeof(sga)); + write(ns, linemode, sizeof(linemode)); + write(ns, echo, sizeof(echo)); + write(ns, bin, sizeof(bin)); + p->skip_read = 0; + } +#endif + + p->infd = p->outfd = ns; + EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p); + kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); + return(0); +} + +static int +dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p, + u_char *sp, int slen, u_char *dp, int *dlen) +{ + static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */}; + + while (slen > 0) { + if (IS_CONSOLE(p)) { + if ((dc->flags & F_TELNET) != 0) { + /* XXX Telent workarounds */ + if (p->skip_read -- > 0) { + sp ++; + slen --; + continue; + } + if (*sp == IAC) { + if (verbose) + printf("(IAC)"); + p->skip_read = 2; + sp ++; + slen --; + continue; + } + if (*sp == 0) { + if (verbose) + printf("(0 stripped)"); + sp ++; + slen --; + continue; + } + } + switch (dc->escape_state) { + case STATE1: + if (*sp == KEY_TILDE) + dc->escape_state = STATE2; + else + dc->escape_state = STATE0; + break; + case STATE2: + dc->escape_state = STATE0; + if (*sp == '.') + dconschat_cleanup(0); + } + if (*sp == KEY_CR) + dc->escape_state = STATE1; + } else if (IS_GDB(p)) { + /* GDB: ^C -> CR+~+^B */ + if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) { + bcopy(abreak, dp, 3); + dp += 3; + sp ++; + *dlen += 3; + /* discard rest of the packet */ + slen = 0; + break; + } + } + *dp++ = *sp++; + (*dlen) ++; + slen --; + } + return (*dlen); + +} + +static int +dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p) +{ + struct kevent kev; + int len, wlen; + char rbuf[MAX_XFER], wbuf[MAX_XFER+2]; + + if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) { + wlen = 0; + dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen); + /* XXX discard if not ready*/ + if (wlen > 0 && (dc->flags & F_READY) != 0) { + dconschat_write_dcons(dc, p->port, wbuf, wlen); + if (verbose > 1) { + wbuf[wlen] = 0; + printf("(%s)\n", wbuf); + } + if (verbose) { + printf("(%d)", wlen); + fflush(stdout); + } + } + } else { + if (verbose) { + if (len == 0) + warnx("port%d: closed", p->port); + else + warn("port%d: read", p->port); + } + EV_SET(&kev, p->infd, EVFILT_READ, + EV_DELETE, 0, 0, NULL); + kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); + close(p->infd); + close(p->outfd); + /* XXX exit for pipe case XXX */ + EV_SET(&kev, p->s, EVFILT_READ, + EV_ADD | EV_ONESHOT, 0, 0, (void *) p); + kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero); + p->infd = p->outfd = -1; + } + return(0); +} +#define NEVENT 5 +static int +dconschat_proc_socket(struct dcons_state *dc) +{ + struct kevent elist[NEVENT], *e; + int i, n; + struct dcons_port *p; + + n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to); + for (i = 0; i < n; i ++) { + e = &elist[i]; + p = (struct dcons_port *)e->udata; + if (e->ident == p->s) { + dconschat_accept_socket(dc, p); + } else { + dconschat_read_socket(dc, p); + } + } + return(0); +} + +static int +dconschat_proc_dcons(struct dcons_state *dc) +{ + int port, len, err; + char buf[MAX_XFER]; + struct dcons_port *p; + + err = dconschat_get_ptr(dc); + if (err) { + /* XXX we should stop write operation too. */ + return err; + } + for (port = 0; port < DCONS_NPORT; port ++) { + p = &dc->port[port]; + if (p->infd < 0) + continue; + while ((len = dconschat_read_dcons(dc, port, buf, + sizeof(buf))) > 0) { + dconschat_write_socket(p->outfd, buf, len); + dconschat_get_ptr(dc); + } + if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0) + dconschat_cleanup(0); + } + return 0; +} + +static int +dconschat_start_session(struct dcons_state *dc) +{ + int counter = 0; + + while (1) { + if ((dc->flags & F_READY) == 0 && (++counter % 200) == 0) + dconschat_fetch_header(dc); + if ((dc->flags & F_READY) != 0) + dconschat_proc_dcons(dc); + dconschat_proc_socket(dc); + } + return (0); +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n" + "\t\t\t[-M core] [-N system]\n" + "\t\t\t[-u unit] [-a address] [-t target_eui64]\n" + "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n" + "\t-v verbose\n" + "\t-w listen on wildcard address rather than localhost\n" + "\t-r replay old buffer on connection\n" + "\t-R read-only\n" + "\t-T enable Telnet protocol workaround on console port\n" + "\t-1 one shot: read buffer and exit\n" + "\t-h polling rate\n" + "\t-C port number for console port\n" + "\t-G port number for gdb port\n" + "\t(for KVM)\n" + "\t-M core file\n" + "\t-N system file\n" + "\t(for FireWire)\n" + "\t-u specify unit number of the bus\n" + "\t-t EUI64 of target host (must be specified)\n" + "\t-a physical address of dcons buffer on target host\n" + ); + exit(0); +} +int +main(int argc, char **argv) +{ + struct dcons_state *dc; + struct fw_eui64 eui; + char devname[256], *core = NULL, *system = NULL; + int i, ch, error; + int unit=0, wildcard=0, poll_hz = DCONS_POLL_HZ; + int port[DCONS_NPORT]; + u_int64_t target = 0; + + bzero(&sc, sizeof(sc)); + dc = ≻ + dc->flags |= USE_CROM ? F_USE_CROM : 0; + + /* defualt ports */ + port[0] = 0; /* stdin/out for console */ + port[1] = -1; /* disable gdb port */ + + while ((ch = getopt(argc, argv, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) { + switch(ch) { + case 'a': + dc->paddr = strtoull(optarg, NULL, 0); + dc->flags &= ~F_USE_CROM; + break; + case 'b': + dc->flags |= F_ALT_BREAK; + break; + case 'h': + poll_hz = strtoul(optarg, NULL, 0); + if (poll_hz == 0) + poll_hz = DCONS_POLL_HZ; + break; + case 'r': + dc->flags |= F_REPLAY; + break; + case 't': + target = strtoull(optarg, NULL, 0); + eui.hi = target >> 32; + eui.lo = target & (((u_int64_t)1 << 32) - 1); + dc->type = TYPE_FW; + break; + case 'u': + unit = strtol(optarg, NULL, 0); + break; + case 'v': + verbose ++; + break; + case 'w': + wildcard = 1; + break; + case 'C': + port[0] = strtol(optarg, NULL, 0); + break; + case 'G': + port[1] = strtol(optarg, NULL, 0); + break; + case 'M': + core = optarg; + break; + case 'N': + system = optarg; + break; + case 'R': + dc->flags |= F_RD_ONLY; + break; + case 'T': + dc->flags |= F_TELNET; + break; + case '1': + dc->flags |= F_ONE_SHOT | F_REPLAY; + break; + default: + usage(); + } + } + if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) { + warnx("no address specified"); + usage(); + } + + if (port[0] < 0 && port[1] < 0) { + warnx("no port specified"); + usage(); + } + + /* set signal handler */ + signal(SIGHUP, dconschat_cleanup); + signal(SIGINT, dconschat_cleanup); + signal(SIGPIPE, dconschat_cleanup); + signal(SIGTERM, dconschat_cleanup); + + /* init firewire */ + switch (dc->type) { + case TYPE_FW: +#define MAXDEV 10 + for (i = 0; i < MAXDEV; i ++) { + snprintf(devname, sizeof(devname), + "/dev/fwmem%d.%d", unit, i); + dc->fd = open(devname, O_RDWR); + if (dc->fd >= 0) + goto found; + } + err(1, "open"); +found: + error = ioctl(dc->fd, FW_SDEUI64, &eui); + if (error) + err(1, "ioctl"); + break; + case TYPE_KVM: + { + struct nlist nl[] = {{"dcons_buf"}, {""}}; + void *dcons_buf; + + dc->kd = kvm_open(system, core, NULL, + (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat"); + if (dc->kd == NULL) + errx(1, "kvm_open"); + + if (kvm_nlist(dc->kd, nl) < 0) + errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd)); + + if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf, + sizeof(void *)) < 0) + errx(1, "kvm_read: %s", kvm_geterr(dc->kd)); + dc->paddr = (uintptr_t)dcons_buf; + if (verbose) + printf("dcons_buf: 0x%x\n", (uint)dc->paddr); + break; + } + } + dconschat_fetch_header(dc); + + /* iniit sockets */ + dc->kq = kqueue(); + if (poll_hz == 1) { + dc->to.tv_sec = 1; + dc->to.tv_nsec = 0; + } else { + dc->to.tv_sec = 0; + dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz; + } + dc->zero.tv_sec = 0; + dc->zero.tv_nsec = 0; + for (i = 0; i < DCONS_NPORT; i++) + dconschat_init_socket(dc, i, + wildcard ? NULL : "localhost", port[i]); + + dconschat_start_session(dc); + + for (i = 0; i < DCONS_NPORT; i++) { + freeaddrinfo(dc->port[i].res); + } + return (0); +} Property changes on: stable/4/usr.sbin/dconschat/dconschat.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/usr.sbin/dconschat/dconschat.8 =================================================================== --- stable/4/usr.sbin/dconschat/dconschat.8 (nonexistent) +++ stable/4/usr.sbin/dconschat/dconschat.8 (revision 122530) @@ -0,0 +1,237 @@ +.\" Copyright (c) 2003 Hidetoshi Shimokawa +.\" 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 ``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 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. +.\" +.\" $FreeBSD$ +.\" +.\" +.Dd February 11, 2003 +.Dt DCONSCHAT 8 +.Os +.Sh NAME +.Nm dconschat +.Nd user interface to dcons +.Sh SYNOPSIS +.Nm +.Op Fl brvwRT1 +.Op Fl h Ar hz +.Op Fl C Ar console_port +.Op Fl G Ar gdb_port +.Op Fl M Ar core +.Op Fl N Ar system +.Nm +.Op Fl brvwR1 +.Op Fl h Ar hz +.Op Fl C Ar console_port +.Op Fl G Ar gdb_port +.Op Fl a Ar address +.Op Fl u Ar bus_num +.Fl t Ar target_eui64 +.Sh DESCRIPTION +The +.Nm +utility is designed to provide a way for users to access +.Xr dcons 4 +(dumb console device) on a local or remote system. +The +.Nm +interacts with +.Xr dcons 4 +using +.Xr kvm 3 +or +.Xr firewire 4 +and interact with a user over tty or TCP/IP. +To access remote +.Xr dcons 4 +using +.Xr firewire 4 , +you have to specify target EUI64 address by +.Fl t +option. +.Pp +The +.Nm +and +.Xr dcons 4 +communicate using 2 port, one for console port and the other for +remote gdb port. +Users are supposed to access +.Nm +using tty, telnet and gdb. +You can specify listen ports for console and gdb port by +.Fl C +and +.Fl G +options respectively. The port number 0 has special meaning that +current tty(stdin/out) is used instead of TCP/IP. +A negative port number will disable the port. +To quit dconschat, send a CR + '~' + '.' sequence to the console port +or send signal to the process. +.Pp +By analogy with +.Xr pty 4 +device, the +.Xr dcons 4 +acts as a slave device and +.Nm +acts as a master device with +.Xr telnetd 8 . +.Pp +.Bl -tag -width indent +.It Fl b +Translate Ctrl-C to ALT_BREAK(CR + '~' + Ctrl-B) on gdb port. +.It Fl r +Replay old buffer on connection. +.It Fl v +Verbose debug output. Multiple '-v' increase verbosity. +.It Fl w +Listen on wildcard address rather than localhost. +.It Fl R +Read-only. Don't write anything on dcons buffer. +.It Fl T +Enable ad hoc workaround for telnet protocol to +remove unnecessary byte sequences. +It should be set when you access dconschat using telnet. +.It Fl 1 +One-Shot. Read available buffer then exit. This implies +.Fl r +option. +.It Fl h Ar hz +Specify polling rate. The default value is 100. +.It Fl C Ar console_port +Specify console port. The default value is 0(stdin/stdout). +.It Fl G Ar gdb_port +Specify gdb port.. The default value is -1(disabled). +.It Fl M Ar core +Specify core file. +.It Fl N Ar system +Specify system file such as /boot/kernel/kernel. +.It Fl t Ar target_eui64 +Specify the 64bit extended unique identifier of the target and use FireWire to access remote +.Xr dcons 4 . +.It Fl a Ar address +Specify the physical/IO address of the dcons buffer. See +.Xr dcons 4 +for details. +If this option is not specified, +.Nm +tries to get the address from the Configuration ROM on the target. +You are supposed to enable +.Xr dcons_crom 4 +on the target to omit this option. +.It Fl u Ar bus_num +Specify FireWire bus number. The default is 0. +.El +.Sh EXAMPLE +To use +.Nm +with FireWire for remote +.Xr dcons , +you have to specify the eui64 of the target. +You can obtain EUI64 by running +.Xr fwcontorl 4 +without options. +The first EUI64 is of the host running fwcontrol and others on the +bus follow. +.Bd -literal -offset indent +# fwcontrol +2 devices (info_len=2) +node EUI64 status + 1 0x7766554433221100 0 + 0 0x0011223344556677 1 +.Ed +.Pp +The EUI64 doesn't change unless you change the hardware +as the ethernet address. +.Pp +Now we can run the +.Nm . +.Bd -literal -offset indent +# dconschat -br -G 12345 -t 0x00112233445566677 +.Ed +.Pp +You'll get console output of the target and login prompt if a getty is +running on dcons. You can break to DDB with ALT_BREAK (CR + '~' + Ctrl-B) +if DDB and ALT_BREAK_TO_DEBUGGER is enabled in the target kernel. +To quit the session, type CR + '~' + '.' in the console port. +.Pp +Using gdb port is almost the same as remote gdb over serial line except +using TCP/IP instead of /dev/cu*. See +"On-line Kernel Debugging Using Remote GDB" +section of The FreeBSD Developers Handbook. +.Bd -literal -offset indent +% gdb -k kernel.debug +(kgdb) target remote :12345 +.Ed +.Pp +Once gdb is attached and you specified '-b' option to dconschat, +typing "Ctrl-C" on gdb causes break to debugger. +.Pp +The following command get console log from crash dump: +.Bd -literal -offset indent +# dconschat -1 -M vmcore.0 -N kernel.0 +.Ed +.Pp +If you want access to the console using telnet, try the following: +.Bd -literal -offset indent +# dconschat -rTC 5555 & +# telnet localhost 5555 +.Ed +.Pp +You may want to keep logging console output of several machines. Conserve-com +in the ports collection should help you. Insert the following lines +in the conserver.cf +.Bd -literal -offset indent +console local { + master localhost; + type exec; + exec /usr/sbin/dconschat -rh 25; +} +console remote { + master localhost; + type exec; + exec /usr/sbin/dconschat -rh 25 -t 0x0011223344556677; +} +.Ed +.Sh FILES +.Bl -tag -width indent +.It Pa /dev/fwmem0.0 +.It Pa /dev/mem +.It Pa /dev/kmem +.El +.Sh SEE ALSO +.Xr gdb 1 , +.Xr telnet 1 , +.Xr kvm 3 , +.Xr dcons 4 , +.Xr dcons_crom 4 , +.Xr ddb 4 , +.Xr firewire 4 , +.Xr fwohci 4 , +.Xr fwcontrol 8 +.Sh AUTHORS +.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org +.Sh BUGS +This utility is still under development. +.Pp Property changes on: stable/4/usr.sbin/dconschat/dconschat.8 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: stable/4/usr.sbin/dconschat/Makefile =================================================================== --- stable/4/usr.sbin/dconschat/Makefile (nonexistent) +++ stable/4/usr.sbin/dconschat/Makefile (revision 122530) @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PROG= dconschat +SRCS= dconschat.c +MAN= dconschat.8 + +CFLAGS+= -I${.CURDIR}/../../sys + +DPADD= ${LIBKVM} +LDADD= -lkvm + +.include Property changes on: stable/4/usr.sbin/dconschat/Makefile ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property