diff options
Diffstat (limited to 'sys/dev/firewire')
-rw-r--r-- | sys/dev/firewire/00README | 155 | ||||
-rw-r--r-- | sys/dev/firewire/firewire.c | 2169 | ||||
-rw-r--r-- | sys/dev/firewire/firewire.h | 403 | ||||
-rw-r--r-- | sys/dev/firewire/firewire_phy.h | 85 | ||||
-rw-r--r-- | sys/dev/firewire/firewirereg.h | 306 | ||||
-rw-r--r-- | sys/dev/firewire/fwcrom.c | 615 | ||||
-rw-r--r-- | sys/dev/firewire/fwdev.c | 703 | ||||
-rw-r--r-- | sys/dev/firewire/fwdma.c | 208 | ||||
-rw-r--r-- | sys/dev/firewire/fwdma.h | 112 | ||||
-rw-r--r-- | sys/dev/firewire/fwmem.c | 424 | ||||
-rw-r--r-- | sys/dev/firewire/fwmem.h | 52 | ||||
-rw-r--r-- | sys/dev/firewire/fwohci.c | 2853 | ||||
-rw-r--r-- | sys/dev/firewire/fwohci_pci.c | 438 | ||||
-rw-r--r-- | sys/dev/firewire/fwohcireg.h | 431 | ||||
-rw-r--r-- | sys/dev/firewire/fwohcivar.h | 90 | ||||
-rw-r--r-- | sys/dev/firewire/iec13213.h | 251 | ||||
-rw-r--r-- | sys/dev/firewire/iec68113.h | 127 | ||||
-rw-r--r-- | sys/dev/firewire/if_fwe.c | 657 | ||||
-rw-r--r-- | sys/dev/firewire/if_fwevar.h | 54 | ||||
-rw-r--r-- | sys/dev/firewire/sbp.c | 2683 |
20 files changed, 12816 insertions, 0 deletions
diff --git a/sys/dev/firewire/00README b/sys/dev/firewire/00README new file mode 100644 index 0000000..d879f31 --- /dev/null +++ b/sys/dev/firewire/00README @@ -0,0 +1,155 @@ +$FreeBSD$ + +IEEE 1394 support for FreeBSD-5.X and 4.X. + +1. Introduction + + This tarball contains IEEE1394(FireWire) driver which is first + written by Katsushi Kobayashi[1] <ikob@koganei.wide.ad.jp> and + modified by Hidetoshi Shimokawa <simokawa@freebsd.org>. + Please note this driver is still under development. + You can find latest snapshots under: + http://people.freebsd.org/~simokawa/ + named firewire-2002XXXX.tar.gz + + The driver consists of 6 parts: + + - fwohci.c/fwohci_pci.c + OHCI[2] driver + - IEEE1394 link/phy chip control + - firewire.c + Chip independent driver + - CSR + - Transaction + - Character devices for userland + - fwmem.c + /dev/fwmem0: physical memory of a remote node. + - sbp.c + SBP-II[3] (a.k.a. SCSI over FireWire) driver + + - if_fwe.c + NON-Standard implementation of Ethernet over FireWire. + + - bus_mgm.c (userland) + Bus management function for user. + show topology map, change gap count, bus reset, etc. + +2. Installation + + Suppose you have kernel source at /sys. + + - Extract tarball at root directory. + - cd /sys/dev/firewire + - make + - make install + - make load + + For FreeBSD-4 user: + + - ./MAKEDEV + +3. SBP-II support (sbp) + + - You need CAM(SCSI) support in your kernel. + If you are using FreeBSD-5 before 2002/03/23 or FreeBSD-4 before + 2002/4/8, you need to apply CAM-patch in this archive + to handle HDD's(T_RBC or T_DIRECT which doesn't support READ_6). + + - If you connect a few firewire devices only, try the following to + reduce gap overhead. + + - ./bus_mgm -g 8 + +4. Ethernet over FireWire (if_fwe) + + This is a sample driver for ethernet emulation. Please note this + does NOT conform to any standards like IP over FireWire(RFC2734[4]). + It just sends ethernet frames encapsulated in asynchronous stream + packets. It doesn't scale because it does something like unicast over multicast, but it's easy to be implemented and you can use any + facilities what ethernet can do. (ipv6, bridging, vlan etc.) + + It also has DEVICE_POLLING[5] support. To enable it, edit your + kernel config file and Makefile.fwe then rebuild kernel and if_fwe.ko. + Note this driver checks kern.polling.enable only when enabling the + interface. When you enable polling after the interface is up, + try 'ifconfig fwe0 down;ifconfig fwe0 up'. + +5. FireWire for Kernel Hackers + + As you know, IEEE1394 is a bus and OHCI supports physical access + to the host memory. This means that you can access the remote + host over firewire without software support at the remote host. + In other words, you can investigate remote host's physical memory + whether its OS is alive or crashed or hangs up. + + You need to apply KVMLIB-patch and rebuild libkvm then rebuild ps, + dmesg and gdb those are statically linked. + You may want to apply GDB-patch in this archive to get same behavior + as gdb with /dev/mem or want to insert savectx(&dumppcb) into panic(), + breakpoint() and so on to emulation crash dump. + + You have to determine target node_id manually at this point. + (guess using bus_mgm -t or dmesg) + (Targets should be specified by EUI64 in the future) + + # sysctl kern.firewire.fwmem_node=[node_id] + + # ps -agx -M /dev/fwmem0 -N /sys/i386/compile/GENERIC/kernel + # dmesg -M /dev/fwmem0 -N /sys/i386/compile/GENERIC/kernel + # gdb -k -c /dev/fwmem0 /sys/i386/compile/GENERIC/kernel.debug + # dd if=/dev/fwmem0 of=vmcore bs=1m count=[phys. memory in MB] + + remote gdb at 400,000,000 bps :-) + + +6. DV + I have not tested yet. + +7. Tested HW + + OS + - FreeBSD-4/i386 + - FreeBSD-4/alpha + - FreeBSD-5/i386 + + * Not tested on SMP. + * Not tested on big-endian machine... + + OHCI + - Texas Instruments TSB12LV26 (PCI) + - Texas Instruments TSB43AA22 (PCI/Cardbus) + + * There might be phy probing problem but most of the OHCI + chips should work. + * Tested with multiple firewire buses. + + SBP-II + - HDD: Logitec USB/FireWire LHD-P30FU + - HDD: Yano A-dish 120GB + - HDD: Yano B-Max 320GB + The repository of cvsup2.jp.freebsd.org is on this device. + - HDD: Personal Storage 3000XT 160GB + The last sector of this drive cannot be accessed.. + - DVD-RAM: Panasonic LF-D340JD + - SCSI-FireWire converter: Yano FWSCSI-01 + We can recognize only 1 device/lun at this point + - HDD: iPod, PowerBook G4 (target mode) + Reported by ikob + - Scanner: Epson GT-9700F + Now works!! + Sane-backend needs a patch(SANE-patch in this archive). + + if_fwe + - IPv4, IPv6, bridging, vlan. + - You need at least two FreeBSD machines with this driver to use. + +References: +[1] ftp://ftp.uec.ac.jp/pub/firewire/beta/ +[2] http://developer.intel.com/technology/1394/download/ohci_11.htm +[3] http://www.t10.org/scsi-3.htm +[4] http://www.faqs.org/rfcs/rfc2734.html +[5] http://info.iet.unipi.it/~luigi/polling/ + + +Hidetoshi Shimokawa +simokawa@freebsd.org diff --git a/sys/dev/firewire/firewire.c b/sys/dev/firewire/firewire.c new file mode 100644 index 0000000..3b100c2 --- /dev/null +++ b/sys/dev/firewire/firewire.c @@ -0,0 +1,2169 @@ +/* + * 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$ + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> + +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/conf.h> +#include <sys/sysctl.h> + +#include <machine/cpufunc.h> /* for rdtsc proto for clock.h below */ +#include <machine/clock.h> + +#include <sys/bus.h> /* used by smbus and newbus */ +#include <machine/bus.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/fwmem.h> +#include <dev/firewire/iec13213.h> +#include <dev/firewire/iec68113.h> + +struct crom_src_buf { + struct crom_src src; + struct crom_chunk root; + struct crom_chunk vendor; + struct crom_chunk hw; +}; + +int firewire_debug=0, try_bmr=1; +SYSCTL_INT(_debug, OID_AUTO, firewire_debug, CTLFLAG_RW, &firewire_debug, 0, + "FireWire driver debug flag"); +SYSCTL_NODE(_hw, OID_AUTO, firewire, CTLFLAG_RD, 0, "FireWire Subsystem"); +SYSCTL_INT(_hw_firewire, OID_AUTO, try_bmr, CTLFLAG_RW, &try_bmr, 0, + "Try to be a bus manager"); + +MALLOC_DEFINE(M_FW, "firewire", "FireWire"); +MALLOC_DEFINE(M_FWXFER, "fw_xfer", "XFER/FireWire"); + +#define FW_MAXASYRTY 4 +#define FW_MAXDEVRCNT 4 + +devclass_t firewire_devclass; + +static int firewire_match __P((device_t)); +static int firewire_attach __P((device_t)); +static int firewire_detach __P((device_t)); +#if 0 +static int firewire_shutdown __P((device_t)); +#endif +static device_t firewire_add_child __P((device_t, int, const char *, int)); +static void fw_try_bmr __P((void *)); +static void fw_try_bmr_callback __P((struct fw_xfer *)); +static void fw_asystart __P((struct fw_xfer *)); +static int fw_get_tlabel __P((struct firewire_comm *, struct fw_xfer *)); +static void fw_bus_probe __P((struct firewire_comm *)); +static void fw_bus_explore __P((struct firewire_comm *)); +static void fw_bus_explore_callback __P((struct fw_xfer *)); +static void fw_attach_dev __P((struct firewire_comm *)); +#ifdef FW_VMACCESS +static void fw_vmaccess __P((struct fw_xfer *)); +#endif +struct fw_xfer *asyreqq __P((struct firewire_comm *, u_int8_t, u_int8_t, u_int8_t, + u_int32_t, u_int32_t, void (*)__P((struct fw_xfer *)))); +static int fw_bmr __P((struct firewire_comm *)); + +static device_method_t firewire_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, firewire_match), + DEVMETHOD(device_attach, firewire_attach), + DEVMETHOD(device_detach, firewire_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_add_child, firewire_add_child), + DEVMETHOD(bus_print_child, bus_generic_print_child), + + { 0, 0 } +}; +char linkspeed[7][0x10]={"S100","S200","S400","S800","S1600","S3200","Unknown"}; + +/* IEEE-1394a Table C-2 Gap count as a function of hops*/ +#define MAX_GAPHOP 15 +u_int gap_cnt[] = { 5, 5, 7, 8, 10, 13, 16, 18, + 21, 24, 26, 29, 32, 35, 37, 40}; + +extern struct cdevsw firewire_cdevsw; + +static driver_t firewire_driver = { + "firewire", + firewire_methods, + sizeof(struct firewire_softc), +}; + +/* + * Lookup fwdev by node id. + */ +struct fw_device * +fw_noderesolve_nodeid(struct firewire_comm *fc, int dst) +{ + struct fw_device *fwdev; + int s; + + s = splfw(); + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (fwdev->dst == dst) + break; + splx(s); + + if(fwdev == NULL) return NULL; + if(fwdev->status == FWDEVINVAL) return NULL; + return fwdev; +} + +/* + * Lookup fwdev by EUI64. + */ +struct fw_device * +fw_noderesolve_eui64(struct firewire_comm *fc, struct fw_eui64 *eui) +{ + struct fw_device *fwdev; + int s; + + s = splfw(); + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (FW_EUI64_EQUAL(fwdev->eui, *eui)) + break; + splx(s); + + if(fwdev == NULL) return NULL; + if(fwdev->status == FWDEVINVAL) return NULL; + return fwdev; +} + +/* + * Async. request procedure for userland application. + */ +int +fw_asyreq(struct firewire_comm *fc, int sub, struct fw_xfer *xfer) +{ + int err = 0; + struct fw_xferq *xferq; + int tl = 0, len; + struct fw_pkt *fp; + int tcode; + struct tcode_info *info; + + if(xfer == NULL) return EINVAL; + if(xfer->send.len > MAXREC(fc->maxrec)){ + printf("send.len > maxrec\n"); + return EINVAL; + } + if(xfer->act.hand == NULL){ + printf("act.hand == NULL\n"); + return EINVAL; + } + fp = (struct fw_pkt *)xfer->send.buf; + + tcode = fp->mode.common.tcode & 0xf; + info = &fc->tcode[tcode]; + if (info->flag == 0) { + printf("invalid tcode=%d\n", tcode); + return EINVAL; + } + if (info->flag & FWTI_REQ) + xferq = fc->atq; + else + xferq = fc->ats; + len = info->hdr_len; + if (info->flag & FWTI_BLOCK_STR) + len += fp->mode.stream.len; + else if (info->flag & FWTI_BLOCK_ASY) + len += fp->mode.rresb.len; + if( len > xfer->send.len ){ + printf("len(%d) > send.len(%d) (tcode=%d)\n", + len, xfer->send.len, tcode); + return EINVAL; + } + xfer->send.len = len; + + if(xferq->start == NULL){ + printf("xferq->start == NULL\n"); + return EINVAL; + } + if(!(xferq->queued < xferq->maxq)){ + device_printf(fc->bdev, "Discard a packet (queued=%d)\n", + xferq->queued); + return EINVAL; + } + + + if (info->flag & FWTI_TLABEL) { + if((tl = fw_get_tlabel(fc, xfer)) == -1 ) + return EIO; + fp->mode.hdr.tlrt = tl << 2; + } + + xfer->tl = tl; + xfer->resp = 0; + xfer->fc = fc; + xfer->q = xferq; + xfer->retry_req = fw_asybusy; + + fw_asystart(xfer); + return err; +} +/* + * Wakeup blocked process. + */ +void +fw_asy_callback(struct fw_xfer *xfer){ + wakeup(xfer); + return; +} +/* + * Postpone to later retry. + */ +void fw_asybusy(struct fw_xfer *xfer){ + printf("fw_asybusy\n"); +/* + xfer->ch = timeout((timeout_t *)fw_asystart, (void *)xfer, 20000); +*/ + DELAY(20000); + fw_asystart(xfer); + return; +} + +/* + * Async. request with given xfer structure. + */ +static void +fw_asystart(struct fw_xfer *xfer) +{ + struct firewire_comm *fc = xfer->fc; + int s; + if(xfer->retry++ >= fc->max_asyretry){ + device_printf(fc->bdev, "max_asyretry exceeded\n"); + xfer->resp = EBUSY; + xfer->state = FWXF_BUSY; + xfer->act.hand(xfer); + return; + } +#if 0 /* XXX allow bus explore packets only after bus rest */ + if (fc->status < FWBUSEXPLORE) { + xfer->resp = EAGAIN; + xfer->state = FWXF_BUSY; + if (xfer->act.hand != NULL) + xfer->act.hand(xfer); + return; + } +#endif + s = splfw(); + xfer->state = FWXF_INQ; + STAILQ_INSERT_TAIL(&xfer->q->q, xfer, link); + xfer->q->queued ++; + splx(s); + /* XXX just queue for mbuf */ + if (xfer->mbuf == NULL) + xfer->q->start(fc); + return; +} + +static int +firewire_match( device_t dev ) +{ + device_set_desc(dev, "IEEE1394(FireWire) bus"); + return -140; +} + +static void +firewire_xfer_timeout(struct firewire_comm *fc) +{ + struct fw_xfer *xfer; + struct tlabel *tl; + struct timeval tv; + struct timeval split_timeout; + int i, s; + + split_timeout.tv_sec = 6; + split_timeout.tv_usec = 0; + + microtime(&tv); + timevalsub(&tv, &split_timeout); + + s = splfw(); + for (i = 0; i < 0x40; i ++) { + while ((tl = STAILQ_FIRST(&fc->tlabels[i])) != NULL) { + xfer = tl->xfer; + if (timevalcmp(&xfer->tv, &tv, >)) + /* the rests are newer than this */ + break; + device_printf(fc->bdev, + "split transaction timeout dst=0x%x tl=0x%x\n", + xfer->dst, i); + xfer->resp = ETIMEDOUT; + STAILQ_REMOVE_HEAD(&fc->tlabels[i], link); + fw_xfer_done(xfer); + } + } + splx(s); +} + +static void +firewire_watchdog(void *arg) +{ + struct firewire_comm *fc; + + fc = (struct firewire_comm *)arg; + firewire_xfer_timeout(fc); + fc->timeout(fc); + callout_reset(&fc->timeout_callout, hz, + (void *)firewire_watchdog, (void *)fc); +} + +/* + * The attach routine. + */ +static int +firewire_attach( device_t dev ) +{ + int i, unitmask, mn; + struct firewire_softc *sc = device_get_softc(dev); + device_t pa = device_get_parent(dev); + struct firewire_comm *fc; + dev_t d; + + fc = (struct firewire_comm *)device_get_softc(pa); + sc->fc = fc; + fc->status = -1; + + unitmask = UNIT2MIN(device_get_unit(dev)); + + if( fc->nisodma > FWMAXNDMA) fc->nisodma = FWMAXNDMA; + for ( i = 0 ; i < fc->nisodma ; i++ ){ + mn = unitmask | i; + /* XXX device name should be improved */ + d = make_dev(&firewire_cdevsw, unit2minor(mn), + UID_ROOT, GID_OPERATOR, 0660, + "fw%x", mn); +#if __FreeBSD_version >= 500000 + if (i == 0) + sc->dev = d; + else + dev_depends(sc->dev, d); +#else + sc->dev[i] = d; +#endif + } + d = make_dev(&firewire_cdevsw, unit2minor(unitmask | FWMEM_FLAG), + UID_ROOT, GID_OPERATOR, 0660, + "fwmem%d", device_get_unit(dev)); +#if __FreeBSD_version >= 500000 + dev_depends(sc->dev, d); +#else + sc->dev[i] = d; +#endif + CALLOUT_INIT(&sc->fc->timeout_callout); + CALLOUT_INIT(&sc->fc->bmr_callout); + CALLOUT_INIT(&sc->fc->retry_probe_callout); + CALLOUT_INIT(&sc->fc->busprobe_callout); + + callout_reset(&sc->fc->timeout_callout, hz, + (void *)firewire_watchdog, (void *)sc->fc); + + /* Locate our children */ + bus_generic_probe(dev); + + /* launch attachement of the added children */ + bus_generic_attach(dev); + + /* bus_reset */ + fc->ibr(fc); + + return 0; +} + +/* + * Attach it as child. + */ +static device_t +firewire_add_child(device_t dev, int order, const char *name, int unit) +{ + device_t child; + struct firewire_softc *sc; + + sc = (struct firewire_softc *)device_get_softc(dev); + child = device_add_child(dev, name, unit); + if (child) { + device_set_ivars(child, sc->fc); + device_probe_and_attach(child); + } + + return child; +} + +/* + * Dettach it. + */ +static int +firewire_detach( device_t dev ) +{ + struct firewire_softc *sc; + struct csrdir *csrd, *next; + struct fw_device *fwdev, *fwdev_next; + + sc = (struct firewire_softc *)device_get_softc(dev); + + bus_generic_detach(dev); + + callout_stop(&sc->fc->timeout_callout); + callout_stop(&sc->fc->bmr_callout); + callout_stop(&sc->fc->retry_probe_callout); + callout_stop(&sc->fc->busprobe_callout); + +#if __FreeBSD_version >= 500000 + destroy_dev(sc->dev); +#else + { + int j; + for (j = 0 ; j < sc->fc->nisodma + 1; j++) + destroy_dev(sc->dev[j]); + } +#endif + /* XXX xfree_free and untimeout on all xfers */ + for (fwdev = STAILQ_FIRST(&sc->fc->devices); fwdev != NULL; + fwdev = fwdev_next) { + fwdev_next = STAILQ_NEXT(fwdev, link); + free(fwdev, M_FW); + } + for (csrd = SLIST_FIRST(&sc->fc->csrfree); csrd != NULL; csrd = next) { + next = SLIST_NEXT(csrd, link); + free(csrd, M_FW); + } + free(sc->fc->topology_map, M_FW); + free(sc->fc->speed_map, M_FW); + free(sc->fc->crom_src_buf, M_FW); + return(0); +} +#if 0 +static int +firewire_shutdown( device_t dev ) +{ + return 0; +} +#endif + + +static void +fw_xferq_drain(struct fw_xferq *xferq) +{ + struct fw_xfer *xfer; + + while ((xfer = STAILQ_FIRST(&xferq->q)) != NULL) { + STAILQ_REMOVE_HEAD(&xferq->q, link); + xferq->queued --; + xfer->resp = EAGAIN; + fw_xfer_done(xfer); + } +} + +void +fw_drain_txq(struct firewire_comm *fc) +{ + int i; + + fw_xferq_drain(fc->atq); + fw_xferq_drain(fc->ats); + for(i = 0; i < fc->nisodma; i++) + fw_xferq_drain(fc->it[i]); +} + +static void +fw_reset_csr(struct firewire_comm *fc) +{ + int i; + + CSRARC(fc, STATE_CLEAR) + = 1 << 23 | 0 << 17 | 1 << 16 | 1 << 15 | 1 << 14 ; + CSRARC(fc, STATE_SET) = CSRARC(fc, STATE_CLEAR); + CSRARC(fc, NODE_IDS) = 0x3f; + + CSRARC(fc, TOPO_MAP + 8) = 0; + fc->irm = -1; + + fc->max_node = -1; + + for(i = 2; i < 0x100/4 - 2 ; i++){ + CSRARC(fc, SPED_MAP + i * 4) = 0; + } + CSRARC(fc, STATE_CLEAR) = 1 << 23 | 0 << 17 | 1 << 16 | 1 << 15 | 1 << 14 ; + CSRARC(fc, STATE_SET) = CSRARC(fc, STATE_CLEAR); + CSRARC(fc, RESET_START) = 0; + CSRARC(fc, SPLIT_TIMEOUT_HI) = 0; + CSRARC(fc, SPLIT_TIMEOUT_LO) = 800 << 19; + CSRARC(fc, CYCLE_TIME) = 0x0; + CSRARC(fc, BUS_TIME) = 0x0; + CSRARC(fc, BUS_MGR_ID) = 0x3f; + CSRARC(fc, BANDWIDTH_AV) = 4915; + CSRARC(fc, CHANNELS_AV_HI) = 0xffffffff; + CSRARC(fc, CHANNELS_AV_LO) = 0xffffffff; + CSRARC(fc, IP_CHANNELS) = (1 << 31); + + CSRARC(fc, CONF_ROM) = 0x04 << 24; + CSRARC(fc, CONF_ROM + 4) = 0x31333934; /* means strings 1394 */ + CSRARC(fc, CONF_ROM + 8) = 1 << 31 | 1 << 30 | 1 << 29 | + 1 << 28 | 0xff << 16 | 0x09 << 8; + CSRARC(fc, CONF_ROM + 0xc) = 0; + +/* DV depend CSRs see blue book */ + CSRARC(fc, oPCR) &= ~DV_BROADCAST_ON; + CSRARC(fc, iPCR) &= ~DV_BROADCAST_ON; + + CSRARC(fc, STATE_CLEAR) &= ~(1 << 23 | 1 << 15 | 1 << 14 ); + CSRARC(fc, STATE_SET) = CSRARC(fc, STATE_CLEAR); +} + +static void +fw_init_crom(struct firewire_comm *fc) +{ + struct crom_src *src; + + fc->crom_src_buf = (struct crom_src_buf *) + malloc(sizeof(struct crom_src_buf), M_FW, M_WAITOK | M_ZERO); + if (fc->crom_src_buf == NULL) + return; + + src = &fc->crom_src_buf->src; + bzero(src, sizeof(struct crom_src)); + + /* BUS info sample */ + src->hdr.info_len = 4; + + src->businfo.bus_name = CSR_BUS_NAME_IEEE1394; + + src->businfo.irmc = 1; + src->businfo.cmc = 1; + src->businfo.isc = 1; + src->businfo.bmc = 1; + src->businfo.pmc = 0; + src->businfo.cyc_clk_acc = 100; + src->businfo.max_rec = fc->maxrec; + src->businfo.max_rom = MAXROM_4; + src->businfo.generation = 0; + src->businfo.link_spd = fc->speed; + + src->businfo.eui64.hi = fc->eui.hi; + src->businfo.eui64.lo = fc->eui.lo; + + STAILQ_INIT(&src->chunk_list); + + fc->crom_src = src; + fc->crom_root = &fc->crom_src_buf->root; +} + +static void +fw_reset_crom(struct firewire_comm *fc) +{ + struct crom_src_buf *buf; + struct crom_src *src; + struct crom_chunk *root; + + if (fc->crom_src_buf == NULL) + fw_init_crom(fc); + + buf = fc->crom_src_buf; + src = fc->crom_src; + root = fc->crom_root; + + src->businfo.generation ++; + STAILQ_INIT(&src->chunk_list); + + bzero(root, sizeof(struct crom_chunk)); + crom_add_chunk(src, NULL, root, 0); + crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */ + /* private company_id */ + crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE); + crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project"); + crom_add_entry(root, CSRKEY_HW, __FreeBSD_version); + crom_add_simple_text(src, root, &buf->hw, hostname); +} + +/* + * Called after bus reset. + */ +void +fw_busreset(struct firewire_comm *fc) +{ + struct firewire_dev_comm *fdc; + device_t *devlistp; + int i, devcnt; + + switch(fc->status){ + case FWBUSMGRELECT: + callout_stop(&fc->bmr_callout); + break; + default: + break; + } + fc->status = FWBUSRESET; + fw_reset_csr(fc); + fw_reset_crom(fc); + + if (device_get_children(fc->bdev, &devlistp, &devcnt) == 0) { + for( i = 0 ; i < devcnt ; i++) + if (device_get_state(devlistp[i]) >= DS_ATTACHED) { + fdc = device_get_softc(devlistp[i]); + if (fdc->post_busreset != NULL) + fdc->post_busreset(fdc); + } + free(devlistp, M_TEMP); + } + + crom_load(&fc->crom_src_buf->src, fc->config_rom, CROMSIZE); +} + +/* Call once after reboot */ +void fw_init(struct firewire_comm *fc) +{ + int i; + struct csrdir *csrd; +#ifdef FW_VMACCESS + struct fw_xfer *xfer; + struct fw_bind *fwb; +#endif + + fc->max_asyretry = FW_MAXASYRTY; + + fc->arq->queued = 0; + fc->ars->queued = 0; + fc->atq->queued = 0; + fc->ats->queued = 0; + + fc->arq->buf = NULL; + fc->ars->buf = NULL; + fc->atq->buf = NULL; + fc->ats->buf = NULL; + + fc->arq->flag = 0; + fc->ars->flag = 0; + fc->atq->flag = 0; + fc->ats->flag = 0; + + STAILQ_INIT(&fc->atq->q); + STAILQ_INIT(&fc->ats->q); + + for( i = 0 ; i < fc->nisodma ; i ++ ){ + fc->it[i]->queued = 0; + fc->ir[i]->queued = 0; + + fc->it[i]->start = NULL; + fc->ir[i]->start = NULL; + + fc->it[i]->buf = NULL; + fc->ir[i]->buf = NULL; + + fc->it[i]->flag = FWXFERQ_STREAM; + fc->ir[i]->flag = FWXFERQ_STREAM; + + STAILQ_INIT(&fc->it[i]->q); + STAILQ_INIT(&fc->ir[i]->q); + + STAILQ_INIT(&fc->it[i]->binds); + STAILQ_INIT(&fc->ir[i]->binds); + } + + fc->arq->maxq = FWMAXQUEUE; + fc->ars->maxq = FWMAXQUEUE; + fc->atq->maxq = FWMAXQUEUE; + fc->ats->maxq = FWMAXQUEUE; + + for( i = 0 ; i < fc->nisodma ; i++){ + fc->ir[i]->maxq = FWMAXQUEUE; + fc->it[i]->maxq = FWMAXQUEUE; + } +/* Initialize csr registers */ + fc->topology_map = (struct fw_topology_map *)malloc( + sizeof(struct fw_topology_map), + M_FW, M_NOWAIT | M_ZERO); + fc->speed_map = (struct fw_speed_map *)malloc( + sizeof(struct fw_speed_map), + M_FW, M_NOWAIT | M_ZERO); + CSRARC(fc, TOPO_MAP) = 0x3f1 << 16; + CSRARC(fc, TOPO_MAP + 4) = 1; + CSRARC(fc, SPED_MAP) = 0x3f1 << 16; + CSRARC(fc, SPED_MAP + 4) = 1; + + STAILQ_INIT(&fc->devices); + STAILQ_INIT(&fc->pending); + +/* Initialize csr ROM work space */ + SLIST_INIT(&fc->ongocsr); + SLIST_INIT(&fc->csrfree); + for( i = 0 ; i < FWMAXCSRDIR ; i++){ + csrd = (struct csrdir *) malloc(sizeof(struct csrdir), M_FW,M_NOWAIT); + if(csrd == NULL) break; + SLIST_INSERT_HEAD(&fc->csrfree, csrd, link); + } + +/* Initialize Async handlers */ + STAILQ_INIT(&fc->binds); + for( i = 0 ; i < 0x40 ; i++){ + STAILQ_INIT(&fc->tlabels[i]); + } + +/* DV depend CSRs see blue book */ +#if 0 + CSRARC(fc, oMPR) = 0x3fff0001; /* # output channel = 1 */ + CSRARC(fc, oPCR) = 0x8000007a; + for(i = 4 ; i < 0x7c/4 ; i+=4){ + CSRARC(fc, i + oPCR) = 0x8000007a; + } + + CSRARC(fc, iMPR) = 0x00ff0001; /* # input channel = 1 */ + CSRARC(fc, iPCR) = 0x803f0000; + for(i = 4 ; i < 0x7c/4 ; i+=4){ + CSRARC(fc, i + iPCR) = 0x0; + } +#endif + + fc->crom_src_buf = NULL; + +#ifdef FW_VMACCESS + xfer = fw_xfer_alloc(); + if(xfer == NULL) return; + + fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT); + if(fwb == NULL){ + fw_xfer_free(xfer); + } + xfer->act.hand = fw_vmaccess; + xfer->fc = fc; + xfer->sc = NULL; + + fwb->start_hi = 0x2; + fwb->start_lo = 0; + fwb->addrlen = 0xffffffff; + fwb->xfer = xfer; + fw_bindadd(fc, fwb); +#endif +} + +/* + * To lookup binded process from IEEE1394 address. + */ +struct fw_bind * +fw_bindlookup(struct firewire_comm *fc, u_int32_t dest_hi, u_int32_t dest_lo) +{ + struct fw_bind *tfw; + for(tfw = STAILQ_FIRST(&fc->binds) ; tfw != NULL ; + tfw = STAILQ_NEXT(tfw, fclist)){ + if (tfw->act_type != FWACT_NULL && + tfw->start_hi == dest_hi && + tfw->start_lo <= dest_lo && + (tfw->start_lo + tfw->addrlen) > dest_lo){ + return(tfw); + } + } + return(NULL); +} + +/* + * To bind IEEE1394 address block to process. + */ +int +fw_bindadd(struct firewire_comm *fc, struct fw_bind *fwb) +{ + struct fw_bind *tfw, *tfw2 = NULL; + int err = 0; + tfw = STAILQ_FIRST(&fc->binds); + if(tfw == NULL){ + STAILQ_INSERT_HEAD(&fc->binds, fwb, fclist); + goto out; + } + if((tfw->start_hi > fwb->start_hi) || + (tfw->start_hi == fwb->start_hi && + (tfw->start_lo > (fwb->start_lo + fwb->addrlen)))){ + STAILQ_INSERT_HEAD(&fc->binds, fwb, fclist); + goto out; + } + for(; tfw != NULL; tfw = STAILQ_NEXT(tfw, fclist)){ + if((tfw->start_hi < fwb->start_hi) || + (tfw->start_hi == fwb->start_hi && + (tfw->start_lo + tfw->addrlen) < fwb->start_lo)){ + tfw2 = STAILQ_NEXT(tfw, fclist); + if(tfw2 == NULL) + break; + if((tfw2->start_hi > fwb->start_hi) || + (tfw2->start_hi == fwb->start_hi && + tfw2->start_lo > (fwb->start_lo + fwb->addrlen))){ + break; + }else{ + err = EBUSY; + goto out; + } + } + } + if(tfw != NULL){ + STAILQ_INSERT_AFTER(&fc->binds, tfw, fwb, fclist); + }else{ + STAILQ_INSERT_TAIL(&fc->binds, fwb, fclist); + } +out: + if (!err && fwb->act_type == FWACT_CH) + STAILQ_INSERT_HEAD(&fc->ir[fwb->sub]->binds, fwb, chlist); + return err; +} + +/* + * To free IEEE1394 address block. + */ +int +fw_bindremove(struct firewire_comm *fc, struct fw_bind *fwb) +{ + int s; + struct fw_xfer *xfer, *next; + + s = splfw(); + /* shall we check the existance? */ + STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist); + /* shall we do this? */ + for (xfer = STAILQ_FIRST(&fwb->xferlist); xfer != NULL; xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free(xfer); + } + STAILQ_INIT(&fwb->xferlist); + + splx(s); + return 0; +} + +/* + * To free transaction label. + */ +static void +fw_tl_free(struct firewire_comm *fc, struct fw_xfer *xfer) +{ + struct tlabel *tl; + int s = splfw(); + + for( tl = STAILQ_FIRST(&fc->tlabels[xfer->tl]); tl != NULL; + tl = STAILQ_NEXT(tl, link)){ + if(tl->xfer == xfer){ + STAILQ_REMOVE(&fc->tlabels[xfer->tl], tl, tlabel, link); + free(tl, M_FW); + splx(s); + return; + } + } + splx(s); + return; +} + +/* + * To obtain XFER structure by transaction label. + */ +static struct fw_xfer * +fw_tl2xfer(struct firewire_comm *fc, int node, int tlabel) +{ + struct fw_xfer *xfer; + struct tlabel *tl; + int s = splfw(); + + for( tl = STAILQ_FIRST(&fc->tlabels[tlabel]); tl != NULL; + tl = STAILQ_NEXT(tl, link)){ + if(tl->xfer->dst == node){ + xfer = tl->xfer; + splx(s); + if (firewire_debug > 2) + printf("fw_tl2xfer: found tl=%d\n", tlabel); + return(xfer); + } + } + if (firewire_debug > 1) + printf("fw_tl2xfer: not found tl=%d\n", tlabel); + splx(s); + return(NULL); +} + +/* + * To allocate IEEE1394 XFER structure. + */ +struct fw_xfer * +fw_xfer_alloc(struct malloc_type *type) +{ + struct fw_xfer *xfer; + + xfer = malloc(sizeof(struct fw_xfer), type, M_NOWAIT | M_ZERO); + if (xfer == NULL) + return xfer; + + microtime(&xfer->tv); + xfer->malloc = type; + + return xfer; +} + +struct fw_xfer * +fw_xfer_alloc_buf(struct malloc_type *type, int send_len, int recv_len) +{ + struct fw_xfer *xfer; + + xfer = fw_xfer_alloc(type); + xfer->send.len = send_len; + xfer->recv.len = recv_len; + if (xfer == NULL) + return(NULL); + if (send_len) { + xfer->send.buf = malloc(send_len, type, M_NOWAIT | M_ZERO); + if (xfer->send.buf == NULL) { + fw_xfer_free(xfer); + return(NULL); + } + } + if (recv_len) { + xfer->recv.buf = malloc(recv_len, type, M_NOWAIT); + if (xfer->recv.buf == NULL) { + if (xfer->send.buf != NULL) + free(xfer->send.buf, type); + fw_xfer_free(xfer); + return(NULL); + } + } + return(xfer); +} + +/* + * IEEE1394 XFER post process. + */ +void +fw_xfer_done(struct fw_xfer *xfer) +{ + if (xfer->act.hand == NULL) + return; + + if (xfer->fc->status != FWBUSRESET) + xfer->act.hand(xfer); + else { + printf("fw_xfer_done: pending\n"); + if (xfer->fc != NULL) + STAILQ_INSERT_TAIL(&xfer->fc->pending, xfer, link); + else + panic("fw_xfer_done: why xfer->fc is NULL?"); + } +} + +void +fw_xfer_unload(struct fw_xfer* xfer) +{ + int s; + + if(xfer == NULL ) return; + if(xfer->state == FWXF_INQ){ + printf("fw_xfer_free FWXF_INQ\n"); + s = splfw(); + STAILQ_REMOVE(&xfer->q->q, xfer, fw_xfer, link); + xfer->q->queued --; + splx(s); + } + if (xfer->fc != NULL) { +#if 1 + if(xfer->state == FWXF_START) + /* + * This could happen if: + * 1. We call fwohci_arcv() before fwohci_txd(). + * 2. firewire_watch() is called. + */ + printf("fw_xfer_free FWXF_START\n"); +#endif + fw_tl_free(xfer->fc, xfer); + } + xfer->state = FWXF_INIT; + xfer->resp = 0; + xfer->retry = 0; +} +/* + * To free IEEE1394 XFER structure. + */ +void +fw_xfer_free( struct fw_xfer* xfer) +{ + if(xfer == NULL ) return; + fw_xfer_unload(xfer); + if(xfer->send.buf != NULL){ + free(xfer->send.buf, xfer->malloc); + } + if(xfer->recv.buf != NULL){ + free(xfer->recv.buf, xfer->malloc); + } + free(xfer, xfer->malloc); +} + +static void +fw_asy_callback_free(struct fw_xfer *xfer) +{ +#if 0 + printf("asyreq done state=%d resp=%d\n", + xfer->state, xfer->resp); +#endif + fw_xfer_free(xfer); +} + +/* + * To configure PHY. + */ +static void +fw_phy_config(struct firewire_comm *fc, int root_node, int gap_count) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + fc->status = FWBUSPHYCONF; + + xfer = fw_xfer_alloc_buf(M_FWXFER, 12, 0); + if (xfer == NULL) + return; + xfer->fc = fc; + xfer->retry_req = fw_asybusy; + xfer->act.hand = fw_asy_callback_free; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.ld[1] = 0; + if (root_node >= 0) + fp->mode.ld[1] |= (root_node & 0x3f) << 24 | 1 << 23; + if (gap_count >= 0) + fp->mode.ld[1] |= 1 << 22 | (gap_count & 0x3f) << 16; + fp->mode.ld[2] = ~fp->mode.ld[1]; +/* XXX Dangerous, how to pass PHY packet to device driver */ + fp->mode.common.tcode |= FWTCODE_PHY; + + if (firewire_debug) + printf("send phy_config root_node=%d gap_count=%d\n", + root_node, gap_count); + fw_asyreq(fc, -1, xfer); +} + +#if 0 +/* + * Dump self ID. + */ +static void +fw_print_sid(u_int32_t sid) +{ + union fw_self_id *s; + s = (union fw_self_id *) &sid; + printf("node:%d link:%d gap:%d spd:%d del:%d con:%d pwr:%d" + " p0:%d p1:%d p2:%d i:%d m:%d\n", + s->p0.phy_id, s->p0.link_active, s->p0.gap_count, + s->p0.phy_speed, s->p0.phy_delay, s->p0.contender, + s->p0.power_class, s->p0.port0, s->p0.port1, + s->p0.port2, s->p0.initiated_reset, s->p0.more_packets); +} +#endif + +/* + * To receive self ID. + */ +void fw_sidrcv(struct firewire_comm* fc, u_int32_t *sid, u_int len) +{ + u_int32_t *p; + union fw_self_id *self_id; + u_int i, j, node, c_port = 0, i_branch = 0; + + fc->sid_cnt = len /(sizeof(u_int32_t) * 2); + fc->status = FWBUSINIT; + fc->max_node = fc->nodeid & 0x3f; + CSRARC(fc, NODE_IDS) = ((u_int32_t)fc->nodeid) << 16; + fc->status = FWBUSCYMELECT; + fc->topology_map->crc_len = 2; + fc->topology_map->generation ++; + fc->topology_map->self_id_count = 0; + fc->topology_map->node_count = 0; + fc->speed_map->generation ++; + fc->speed_map->crc_len = 1 + (64*64 + 3) / 4; + self_id = &fc->topology_map->self_id[0]; + for(i = 0; i < fc->sid_cnt; i ++){ + if (sid[1] != ~sid[0]) { + printf("fw_sidrcv: invalid self-id packet\n"); + sid += 2; + continue; + } + *self_id = *((union fw_self_id *)sid); + fc->topology_map->crc_len++; + if(self_id->p0.sequel == 0){ + fc->topology_map->node_count ++; + c_port = 0; +#if 0 + fw_print_sid(sid[0]); +#endif + node = self_id->p0.phy_id; + if(fc->max_node < node){ + fc->max_node = self_id->p0.phy_id; + } + /* XXX I'm not sure this is the right speed_map */ + fc->speed_map->speed[node][node] + = self_id->p0.phy_speed; + for (j = 0; j < node; j ++) { + fc->speed_map->speed[j][node] + = fc->speed_map->speed[node][j] + = min(fc->speed_map->speed[j][j], + self_id->p0.phy_speed); + } + if ((fc->irm == -1 || self_id->p0.phy_id > fc->irm) && + (self_id->p0.link_active && self_id->p0.contender)) { + fc->irm = self_id->p0.phy_id; + } + if(self_id->p0.port0 >= 0x2){ + c_port++; + } + if(self_id->p0.port1 >= 0x2){ + c_port++; + } + if(self_id->p0.port2 >= 0x2){ + c_port++; + } + } + if(c_port > 2){ + i_branch += (c_port - 2); + } + sid += 2; + self_id++; + fc->topology_map->self_id_count ++; + } + device_printf(fc->bdev, "%d nodes", fc->max_node + 1); + /* CRC */ + fc->topology_map->crc = fw_crc16( + (u_int32_t *)&fc->topology_map->generation, + fc->topology_map->crc_len * 4); + fc->speed_map->crc = fw_crc16( + (u_int32_t *)&fc->speed_map->generation, + fc->speed_map->crc_len * 4); + /* byteswap and copy to CSR */ + p = (u_int32_t *)fc->topology_map; + for (i = 0; i <= fc->topology_map->crc_len; i++) + CSRARC(fc, TOPO_MAP + i * 4) = htonl(*p++); + p = (u_int32_t *)fc->speed_map; + CSRARC(fc, SPED_MAP) = htonl(*p++); + CSRARC(fc, SPED_MAP + 4) = htonl(*p++); + /* don't byte-swap u_int8_t array */ + bcopy(p, &CSRARC(fc, SPED_MAP + 8), (fc->speed_map->crc_len - 1)*4); + + fc->max_hop = fc->max_node - i_branch; + printf(", maxhop <= %d", fc->max_hop); + + if(fc->irm == -1 ){ + printf(", Not found IRM capable node"); + }else{ + printf(", cable IRM = %d", fc->irm); + if (fc->irm == fc->nodeid) + printf(" (me)"); + } + printf("\n"); + + if (try_bmr && (fc->irm != -1) && (CSRARC(fc, BUS_MGR_ID) == 0x3f)) { + if (fc->irm == fc->nodeid) { + fc->status = FWBUSMGRDONE; + CSRARC(fc, BUS_MGR_ID) = fc->set_bmr(fc, fc->irm); + fw_bmr(fc); + } else { + fc->status = FWBUSMGRELECT; + callout_reset(&fc->bmr_callout, hz/8, + (void *)fw_try_bmr, (void *)fc); + } + } else + fc->status = FWBUSMGRDONE; + + callout_reset(&fc->busprobe_callout, hz/4, + (void *)fw_bus_probe, (void *)fc); +} + +/* + * To probe devices on the IEEE1394 bus. + */ +static void +fw_bus_probe(struct firewire_comm *fc) +{ + int s; + struct fw_device *fwdev, *next; + + s = splfw(); + fc->status = FWBUSEXPLORE; + fc->retry_count = 0; + +/* + * Invalidate all devices, just after bus reset. Devices + * to be removed has not been seen longer time. + */ + for (fwdev = STAILQ_FIRST(&fc->devices); fwdev != NULL; fwdev = next) { + next = STAILQ_NEXT(fwdev, link); + if (fwdev->status != FWDEVINVAL) { + fwdev->status = FWDEVINVAL; + fwdev->rcnt = 0; + } else if(fwdev->rcnt < FW_MAXDEVRCNT) { + fwdev->rcnt ++; + } else { + STAILQ_REMOVE(&fc->devices, fwdev, fw_device, link); + free(fwdev, M_FW); + } + } + fc->ongonode = 0; + fc->ongoaddr = CSRROMOFF; + fc->ongodev = NULL; + fc->ongoeui.hi = 0xffffffff; fc->ongoeui.lo = 0xffffffff; + fw_bus_explore(fc); + splx(s); +} + +/* + * To collect device informations on the IEEE1394 bus. + */ +static void +fw_bus_explore(struct firewire_comm *fc ) +{ + int err = 0; + struct fw_device *fwdev, *pfwdev, *tfwdev; + u_int32_t addr; + struct fw_xfer *xfer; + struct fw_pkt *fp; + + if(fc->status != FWBUSEXPLORE) + return; + +loop: + if(fc->ongonode == fc->nodeid) fc->ongonode++; + + if(fc->ongonode > fc->max_node) goto done; + if(fc->ongonode >= 0x3f) goto done; + + /* check link */ + /* XXX we need to check phy_id first */ + if (!fc->topology_map->self_id[fc->ongonode].p0.link_active) { + if (firewire_debug) + printf("node%d: link down\n", fc->ongonode); + fc->ongonode++; + goto loop; + } + + if(fc->ongoaddr <= CSRROMOFF && + fc->ongoeui.hi == 0xffffffff && + fc->ongoeui.lo == 0xffffffff ){ + fc->ongoaddr = CSRROMOFF; + addr = 0xf0000000 | fc->ongoaddr; + }else if(fc->ongoeui.hi == 0xffffffff ){ + fc->ongoaddr = CSRROMOFF + 0xc; + addr = 0xf0000000 | fc->ongoaddr; + }else if(fc->ongoeui.lo == 0xffffffff ){ + fc->ongoaddr = CSRROMOFF + 0x10; + addr = 0xf0000000 | fc->ongoaddr; + }else if(fc->ongodev == NULL){ + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (FW_EUI64_EQUAL(fwdev->eui, fc->ongoeui)) + break; + if(fwdev != NULL){ + fwdev->dst = fc->ongonode; + fwdev->status = FWDEVATTACHED; + fc->ongonode++; + fc->ongoaddr = CSRROMOFF; + fc->ongodev = NULL; + fc->ongoeui.hi = 0xffffffff; fc->ongoeui.lo = 0xffffffff; + goto loop; + } + fwdev = malloc(sizeof(struct fw_device), M_FW, + M_NOWAIT | M_ZERO); + if(fwdev == NULL) + return; + fwdev->fc = fc; + fwdev->rommax = 0; + fwdev->dst = fc->ongonode; + fwdev->eui.hi = fc->ongoeui.hi; fwdev->eui.lo = fc->ongoeui.lo; + fwdev->status = FWDEVINIT; + fwdev->speed = fc->speed_map->speed[fc->nodeid][fc->ongonode]; + + pfwdev = NULL; + STAILQ_FOREACH(tfwdev, &fc->devices, link) { + if (tfwdev->eui.hi > fwdev->eui.hi || + (tfwdev->eui.hi == fwdev->eui.hi && + tfwdev->eui.lo > fwdev->eui.lo)) + break; + pfwdev = tfwdev; + } + if (pfwdev == NULL) + STAILQ_INSERT_HEAD(&fc->devices, fwdev, link); + else + STAILQ_INSERT_AFTER(&fc->devices, pfwdev, fwdev, link); + + device_printf(fc->bdev, "New %s device ID:%08x%08x\n", + linkspeed[fwdev->speed], + fc->ongoeui.hi, fc->ongoeui.lo); + + fc->ongodev = fwdev; + fc->ongoaddr = CSRROMOFF; + addr = 0xf0000000 | fc->ongoaddr; + }else{ + addr = 0xf0000000 | fc->ongoaddr; + } +#if 0 + xfer = asyreqq(fc, FWSPD_S100, 0, 0, + ((FWLOCALBUS | fc->ongonode) << 16) | 0xffff , addr, + fw_bus_explore_callback); + if(xfer == NULL) goto done; +#else + xfer = fw_xfer_alloc_buf(M_FWXFER, 16, 16); + if(xfer == NULL){ + goto done; + } + xfer->spd = 0; + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.rreqq.dest_hi = 0xffff; + fp->mode.rreqq.tlrt = 0; + fp->mode.rreqq.tcode = FWTCODE_RREQQ; + fp->mode.rreqq.pri = 0; + fp->mode.rreqq.src = 0; + xfer->dst = FWLOCALBUS | fc->ongonode; + fp->mode.rreqq.dst = xfer->dst; + fp->mode.rreqq.dest_lo = addr; + xfer->act.hand = fw_bus_explore_callback; + + if (firewire_debug) + printf("node%d: explore addr=0x%x\n", + fc->ongonode, fc->ongoaddr); + err = fw_asyreq(fc, -1, xfer); + if(err){ + fw_xfer_free( xfer); + return; + } +#endif + return; +done: + /* fw_attach_devs */ + fc->status = FWBUSEXPDONE; + if (firewire_debug) + printf("bus_explore done\n"); + fw_attach_dev(fc); + return; + +} + +/* Portable Async. request read quad */ +struct fw_xfer * +asyreqq(struct firewire_comm *fc, u_int8_t spd, u_int8_t tl, u_int8_t rt, + u_int32_t addr_hi, u_int32_t addr_lo, + void (*hand) __P((struct fw_xfer*))) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + int err; + + xfer = fw_xfer_alloc_buf(M_FWXFER, 16, 16); + if (xfer == NULL) + return NULL; + + xfer->spd = spd; /* XXX:min(spd, fc->spd) */ + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.rreqq.dest_hi = addr_hi & 0xffff; + if(tl & FWP_TL_VALID){ + fp->mode.rreqq.tlrt = (tl & 0x3f) << 2; + }else{ + fp->mode.rreqq.tlrt = 0; + } + fp->mode.rreqq.tlrt |= rt & 0x3; + fp->mode.rreqq.tcode = FWTCODE_RREQQ; + fp->mode.rreqq.pri = 0; + fp->mode.rreqq.src = 0; + xfer->dst = addr_hi >> 16; + fp->mode.rreqq.dst = xfer->dst; + fp->mode.rreqq.dest_lo = addr_lo; + xfer->act.hand = hand; + + err = fw_asyreq(fc, -1, xfer); + if(err){ + fw_xfer_free( xfer); + return NULL; + } + return xfer; +} + +/* + * Callback for the IEEE1394 bus information collection. + */ +static void +fw_bus_explore_callback(struct fw_xfer *xfer) +{ + struct firewire_comm *fc; + struct fw_pkt *sfp,*rfp; + struct csrhdr *chdr; + struct csrdir *csrd; + struct csrreg *csrreg; + u_int32_t offset; + + + if(xfer == NULL) { + printf("xfer == NULL\n"); + return; + } + fc = xfer->fc; + + if (firewire_debug) + printf("node%d: callback addr=0x%x\n", + fc->ongonode, fc->ongoaddr); + + if(xfer->resp != 0){ + printf("node%d: resp=%d addr=0x%x\n", + fc->ongonode, xfer->resp, fc->ongoaddr); + goto errnode; + } + + if(xfer->send.buf == NULL){ + printf("node%d: send.buf=NULL addr=0x%x\n", + fc->ongonode, fc->ongoaddr); + goto errnode; + } + sfp = (struct fw_pkt *)xfer->send.buf; + + if(xfer->recv.buf == NULL){ + printf("node%d: recv.buf=NULL addr=0x%x\n", + fc->ongonode, fc->ongoaddr); + goto errnode; + } + rfp = (struct fw_pkt *)xfer->recv.buf; +#if 0 + { + u_int32_t *qld; + int i; + qld = (u_int32_t *)xfer->recv.buf; + printf("len:%d\n", xfer->recv.len); + for( i = 0 ; i <= xfer->recv.len && i < 32; i+= 4){ + printf("0x%08x ", rfp->mode.ld[i/4]); + if((i % 16) == 15) printf("\n"); + } + if((i % 16) != 15) printf("\n"); + } +#endif + if(fc->ongodev == NULL){ + if(sfp->mode.rreqq.dest_lo == (0xf0000000 | CSRROMOFF)){ + rfp->mode.rresq.data = ntohl(rfp->mode.rresq.data); + chdr = (struct csrhdr *)(&rfp->mode.rresq.data); +/* If CSR is minimal confinguration, more investgation is not needed. */ + if(chdr->info_len == 1){ + if (firewire_debug) + printf("node%d: minimal config\n", + fc->ongonode); + goto nextnode; + }else{ + fc->ongoaddr = CSRROMOFF + 0xc; + } + }else if(sfp->mode.rreqq.dest_lo == (0xf0000000 |(CSRROMOFF + 0xc))){ + fc->ongoeui.hi = ntohl(rfp->mode.rresq.data); + fc->ongoaddr = CSRROMOFF + 0x10; + }else if(sfp->mode.rreqq.dest_lo == (0xf0000000 |(CSRROMOFF + 0x10))){ + fc->ongoeui.lo = ntohl(rfp->mode.rresq.data); + if (fc->ongoeui.hi == 0 && fc->ongoeui.lo == 0) { + if (firewire_debug) + printf("node%d: eui64 is zero.\n", + fc->ongonode); + goto nextnode; + } + fc->ongoaddr = CSRROMOFF; + } + }else{ + fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4] = ntohl(rfp->mode.rresq.data); + if(fc->ongoaddr > fc->ongodev->rommax){ + fc->ongodev->rommax = fc->ongoaddr; + } + csrd = SLIST_FIRST(&fc->ongocsr); + if((csrd = SLIST_FIRST(&fc->ongocsr)) == NULL){ + chdr = (struct csrhdr *)(fc->ongodev->csrrom); + offset = CSRROMOFF; + }else{ + chdr = (struct csrhdr *)&fc->ongodev->csrrom[(csrd->off - CSRROMOFF)/4]; + offset = csrd->off; + } + if(fc->ongoaddr > (CSRROMOFF + 0x14) && fc->ongoaddr != offset){ + csrreg = (struct csrreg *)&fc->ongodev->csrrom[(fc->ongoaddr - CSRROMOFF)/4]; + if( csrreg->key == 0x81 || csrreg->key == 0xd1){ + csrd = SLIST_FIRST(&fc->csrfree); + if(csrd == NULL){ + goto nextnode; + }else{ + csrd->ongoaddr = fc->ongoaddr; + fc->ongoaddr += csrreg->val * 4; + csrd->off = fc->ongoaddr; + SLIST_REMOVE_HEAD(&fc->csrfree, link); + SLIST_INSERT_HEAD(&fc->ongocsr, csrd, link); + goto nextaddr; + } + } + } + fc->ongoaddr += 4; + if(((fc->ongoaddr - offset)/4 > chdr->crc_len) && + (fc->ongodev->rommax < 0x414)){ + if(fc->ongodev->rommax <= 0x414){ + csrd = SLIST_FIRST(&fc->csrfree); + if(csrd == NULL) goto nextnode; + csrd->off = fc->ongoaddr; + csrd->ongoaddr = fc->ongoaddr; + SLIST_REMOVE_HEAD(&fc->csrfree, link); + SLIST_INSERT_HEAD(&fc->ongocsr, csrd, link); + } + goto nextaddr; + } + + while(((fc->ongoaddr - offset)/4 > chdr->crc_len)){ + if(csrd == NULL){ + goto nextnode; + }; + fc->ongoaddr = csrd->ongoaddr + 4; + SLIST_REMOVE_HEAD(&fc->ongocsr, link); + SLIST_INSERT_HEAD(&fc->csrfree, csrd, link); + csrd = SLIST_FIRST(&fc->ongocsr); + if((csrd = SLIST_FIRST(&fc->ongocsr)) == NULL){ + chdr = (struct csrhdr *)(fc->ongodev->csrrom); + offset = CSRROMOFF; + }else{ + chdr = (struct csrhdr *)&(fc->ongodev->csrrom[(csrd->off - CSRROMOFF)/4]); + offset = csrd->off; + } + } + if((fc->ongoaddr - CSRROMOFF) > CSRROMSIZE){ + goto nextnode; + } + } +nextaddr: + fw_xfer_free( xfer); + fw_bus_explore(fc); + return; +errnode: + fc->retry_count++; + if (fc->ongodev != NULL) + fc->ongodev->status = FWDEVINVAL; +nextnode: + fw_xfer_free( xfer); + fc->ongonode++; +/* housekeeping work space */ + fc->ongoaddr = CSRROMOFF; + fc->ongodev = NULL; + fc->ongoeui.hi = 0xffffffff; fc->ongoeui.lo = 0xffffffff; + while((csrd = SLIST_FIRST(&fc->ongocsr)) != NULL){ + SLIST_REMOVE_HEAD(&fc->ongocsr, link); + SLIST_INSERT_HEAD(&fc->csrfree, csrd, link); + } + fw_bus_explore(fc); + return; +} + +/* + * To attach sub-devices layer onto IEEE1394 bus. + */ +static void +fw_attach_dev(struct firewire_comm *fc) +{ + struct fw_device *fwdev; + struct fw_xfer *xfer; + int i, err; + device_t *devlistp; + int devcnt; + struct firewire_dev_comm *fdc; + + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (fwdev->status == FWDEVINIT) + fwdev->status = FWDEVATTACHED; + + err = device_get_children(fc->bdev, &devlistp, &devcnt); + if( err != 0 ) + return; + for( i = 0 ; i < devcnt ; i++){ + if (device_get_state(devlistp[i]) >= DS_ATTACHED) { + fdc = device_get_softc(devlistp[i]); + if (fdc->post_explore != NULL) + fdc->post_explore(fdc); + } + } + free(devlistp, M_TEMP); + + /* call pending handlers */ + i = 0; + while ((xfer = STAILQ_FIRST(&fc->pending))) { + STAILQ_REMOVE_HEAD(&fc->pending, link); + i++; + if (xfer->act.hand) + xfer->act.hand(xfer); + } + if (i > 0) + printf("fw_attach_dev: %d pending handlers called\n", i); + if (fc->retry_count > 0) { + printf("probe failed for %d node\n", fc->retry_count); +#if 0 + callout_reset(&fc->retry_probe_callout, hz*2, + (void *)fc->ibr, (void *)fc); +#endif + } + return; +} + +/* + * To allocate uniq transaction label. + */ +static int +fw_get_tlabel(struct firewire_comm *fc, struct fw_xfer *xfer) +{ + u_int i; + struct tlabel *tl, *tmptl; + int s; + static u_int32_t label = 0; + + s = splfw(); + for( i = 0 ; i < 0x40 ; i ++){ + label = (label + 1) & 0x3f; + for(tmptl = STAILQ_FIRST(&fc->tlabels[label]); + tmptl != NULL; tmptl = STAILQ_NEXT(tmptl, link)){ + if(tmptl->xfer->dst == xfer->dst) break; + } + if(tmptl == NULL) { + tl = malloc(sizeof(struct tlabel),M_FW,M_NOWAIT); + if (tl == NULL) { + splx(s); + return (-1); + } + tl->xfer = xfer; + STAILQ_INSERT_TAIL(&fc->tlabels[label], tl, link); + splx(s); + if (firewire_debug > 1) + printf("fw_get_tlabel: dst=%d tl=%d\n", + xfer->dst, label); + return(label); + } + } + splx(s); + + printf("fw_get_tlabel: no free tlabel\n"); + return(-1); +} + +static void +fw_rcv_copy(struct fw_xfer *xfer, struct iovec *vec, int nvec) +{ + char *p; + int res, i, len; + + p = xfer->recv.buf; + res = xfer->recv.len; + for (i = 0; i < nvec; i++, vec++) { + len = vec->iov_len; + if (res < len) { + printf("rcv buffer(%d) is %d bytes short.\n", + xfer->recv.len, len - res); + len = res; + } + bcopy(vec->iov_base, p, len); + p += len; + res -= len; + if (res <= 0) + break; + } + xfer->recv.len -= res; +} + +/* + * Generic packet receving process. + */ +void +fw_rcv(struct firewire_comm *fc, struct iovec *vec, int nvec, u_int sub, u_int spd) +{ + struct fw_pkt *fp, *resfp; + struct fw_xfer *xfer; + struct fw_bind *bind; + struct firewire_softc *sc; + int tcode, s; + int i, len, oldstate; +#if 0 + { + u_int32_t *qld; + int i; + qld = (u_int32_t *)buf; + printf("spd %d len:%d\n", spd, len); + for( i = 0 ; i <= len && i < 32; i+= 4){ + printf("0x%08x ", ntohl(qld[i/4])); + if((i % 16) == 15) printf("\n"); + } + if((i % 16) != 15) printf("\n"); + } +#endif + fp = (struct fw_pkt *)vec[0].iov_base; + tcode = fp->mode.common.tcode; +#if 0 /* XXX this check is not valid for RRESQ and WREQQ */ + if (vec[0].iov_len < fc->tcode[tcode].hdr_len) { +#if __FreeBSD_version >= 500000 + printf("fw_rcv: iov_len(%zu) is less than" +#else + printf("fw_rcv: iov_len(%u) is less than" +#endif + " hdr_len(%d:tcode=%d)\n", vec[0].iov_len, + fc->tcode[tcode].hdr_len, tcode); + } +#endif + switch (tcode) { + case FWTCODE_WRES: + case FWTCODE_RRESQ: + case FWTCODE_RRESB: + case FWTCODE_LRES: + xfer = fw_tl2xfer(fc, fp->mode.hdr.src, + fp->mode.hdr.tlrt >> 2); + if(xfer == NULL) { + printf("fw_rcv: unknown response " + "tcode=%d src=0x%x tl=0x%x rt=%d data=0x%x\n", + tcode, + fp->mode.hdr.src, + fp->mode.hdr.tlrt >> 2, + fp->mode.hdr.tlrt & 3, + fp->mode.rresq.data); +#if 1 + printf("try ad-hoc work around!!\n"); + xfer = fw_tl2xfer(fc, fp->mode.hdr.src, + (fp->mode.hdr.tlrt >> 2)^3); + if (xfer == NULL) { + printf("no use...\n"); + goto err; + } +#else + goto err; +#endif + } + fw_rcv_copy(xfer, vec, nvec); + xfer->resp = 0; + /* make sure the packet is drained in AT queue */ + oldstate = xfer->state; + xfer->state = FWXF_RCVD; + switch (oldstate) { + case FWXF_SENT: + fw_xfer_done(xfer); + break; + case FWXF_START: + if (firewire_debug) + printf("not sent yet\n"); + break; + default: + printf("unexpected state %d\n", xfer->state); + } + return; + case FWTCODE_WREQQ: + case FWTCODE_WREQB: + case FWTCODE_RREQQ: + case FWTCODE_RREQB: + case FWTCODE_LREQ: + bind = fw_bindlookup(fc, fp->mode.rreqq.dest_hi, + fp->mode.rreqq.dest_lo); + if(bind == NULL){ +#if __FreeBSD_version >= 500000 + printf("Unknown service addr 0x%08x:0x%08x tcode=%x src=0x%x data=%x\n", +#else + printf("Unknown service addr 0x%08x:0x%08x tcode=%x src=0x%x data=%lx\n", +#endif + fp->mode.wreqq.dest_hi, + fp->mode.wreqq.dest_lo, + tcode, + fp->mode.hdr.src, + ntohl(fp->mode.wreqq.data)); + if (fc->status == FWBUSRESET) { + printf("fw_rcv: cannot respond(bus reset)!\n"); + goto err; + } + xfer = fw_xfer_alloc_buf(M_FWXFER, 16, 0); + if(xfer == NULL){ + return; + } + xfer->spd = spd; + resfp = (struct fw_pkt *)xfer->send.buf; + switch (tcode) { + case FWTCODE_WREQQ: + case FWTCODE_WREQB: + resfp->mode.hdr.tcode = FWTCODE_WRES; + xfer->send.len = 12; + break; + case FWTCODE_RREQQ: + resfp->mode.hdr.tcode = FWTCODE_RRESQ; + xfer->send.len = 16; + break; + case FWTCODE_RREQB: + resfp->mode.hdr.tcode = FWTCODE_RRESB; + xfer->send.len = 16; + break; + case FWTCODE_LREQ: + resfp->mode.hdr.tcode = FWTCODE_LRES; + xfer->send.len = 16; + break; + } + resfp->mode.hdr.dst = fp->mode.hdr.src; + resfp->mode.hdr.tlrt = fp->mode.hdr.tlrt; + resfp->mode.hdr.pri = fp->mode.hdr.pri; + resfp->mode.rresb.rtcode = 7; + resfp->mode.rresb.extcode = 0; + resfp->mode.rresb.len = 0; +/* + xfer->act.hand = fw_asy_callback; +*/ + xfer->act.hand = fw_xfer_free; + if(fw_asyreq(fc, -1, xfer)){ + fw_xfer_free( xfer); + return; + } + goto err; + } + len = 0; + for (i = 0; i < nvec; i ++) + len += vec[i].iov_len; + switch(bind->act_type){ + case FWACT_XFER: + /* splfw()?? */ + xfer = STAILQ_FIRST(&bind->xferlist); + if (xfer == NULL) { + printf("Discard a packet for this bind.\n"); + goto err; + } + STAILQ_REMOVE_HEAD(&bind->xferlist, link); + fw_rcv_copy(xfer, vec, nvec); + xfer->spd = spd; + if (fc->status != FWBUSRESET) + xfer->act.hand(xfer); + else + STAILQ_INSERT_TAIL(&fc->pending, xfer, link); + return; + break; + case FWACT_CH: + if(fc->ir[bind->sub]->queued >= + fc->ir[bind->sub]->maxq){ + device_printf(fc->bdev, + "Discard a packet %x %d\n", + bind->sub, + fc->ir[bind->sub]->queued); + goto err; + } + xfer = STAILQ_FIRST(&bind->xferlist); + if (xfer == NULL) { + printf("Discard packet for this bind\n"); + goto err; + } + STAILQ_REMOVE_HEAD(&bind->xferlist, link); + fw_rcv_copy(xfer, vec, nvec); + xfer->spd = spd; + s = splfw(); + fc->ir[bind->sub]->queued++; + STAILQ_INSERT_TAIL(&fc->ir[bind->sub]->q, xfer, link); + splx(s); + + wakeup((caddr_t)fc->ir[bind->sub]); + + return; + break; + default: + goto err; + break; + } + break; + case FWTCODE_STREAM: + { + struct fw_xferq *xferq; + + xferq = fc->ir[sub]; +#if 0 + printf("stream rcv dma %d len %d off %d spd %d\n", + sub, len, off, spd); +#endif + if(xferq->queued >= xferq->maxq) { + printf("receive queue is full\n"); + goto err; + } + /* XXX get xfer from xfer queue, we don't need copy for + per packet mode */ + xfer = fw_xfer_alloc_buf(M_FWXFER, 0, /* XXX */ + vec[0].iov_len); + if(xfer == NULL) goto err; + fw_rcv_copy(xfer, vec, nvec); + xfer->spd = spd; + s = splfw(); + xferq->queued++; + STAILQ_INSERT_TAIL(&xferq->q, xfer, link); + splx(s); + sc = device_get_softc(fc->bdev); +#if __FreeBSD_version >= 500000 + if (SEL_WAITING(&xferq->rsel)) +#else + if (&xferq->rsel.si_pid != 0) +#endif + selwakeup(&xferq->rsel); + if (xferq->flag & FWXFERQ_WAKEUP) { + xferq->flag &= ~FWXFERQ_WAKEUP; + wakeup((caddr_t)xferq); + } + if (xferq->flag & FWXFERQ_HANDLER) { + xferq->hand(xferq); + } + return; + break; + } + default: + printf("fw_rcv: unknow tcode %d\n", tcode); + break; + } +err: + return; +} + +/* + * Post process for Bus Manager election process. + */ +static void +fw_try_bmr_callback(struct fw_xfer *xfer) +{ + struct fw_pkt *rfp; + struct firewire_comm *fc; + int bmr; + + if (xfer == NULL) + return; + fc = xfer->fc; + if (xfer->resp != 0) + goto error; + if (xfer->send.buf == NULL) + goto error; + if (xfer->recv.buf == NULL) + goto error; + rfp = (struct fw_pkt *)xfer->recv.buf; + if (rfp->mode.lres.rtcode != FWRCODE_COMPLETE) + goto error; + + bmr = ntohl(rfp->mode.lres.payload[0]); + if (bmr == 0x3f) + bmr = fc->nodeid; + + CSRARC(fc, BUS_MGR_ID) = fc->set_bmr(fc, bmr & 0x3f); + fw_xfer_free(xfer); + fw_bmr(fc); + return; + +error: + device_printf(fc->bdev, "bus manager election failed\n"); + fw_xfer_free(xfer); +} + + +/* + * To candidate Bus Manager election process. + */ +static void +fw_try_bmr(void *arg) +{ + struct fw_xfer *xfer; + struct firewire_comm *fc = (struct firewire_comm *)arg; + struct fw_pkt *fp; + int err = 0; + + xfer = fw_xfer_alloc_buf(M_FWXFER, 24, 20); + if(xfer == NULL){ + return; + } + xfer->spd = 0; + fc->status = FWBUSMGRELECT; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.lreq.dest_hi = 0xffff; + fp->mode.lreq.tlrt = 0; + fp->mode.lreq.tcode = FWTCODE_LREQ; + fp->mode.lreq.pri = 0; + fp->mode.lreq.src = 0; + fp->mode.lreq.len = 8; + fp->mode.lreq.extcode = FW_LREQ_CMPSWAP; + xfer->dst = FWLOCALBUS | fc->irm; + fp->mode.lreq.dst = xfer->dst; + fp->mode.lreq.dest_lo = 0xf0000000 | BUS_MGR_ID; + fp->mode.lreq.payload[0] = htonl(0x3f); + fp->mode.lreq.payload[1] = htonl(fc->nodeid); + xfer->act.hand = fw_try_bmr_callback; + + err = fw_asyreq(fc, -1, xfer); + if(err){ + fw_xfer_free( xfer); + return; + } + return; +} + +#ifdef FW_VMACCESS +/* + * Software implementation for physical memory block access. + * XXX:Too slow, usef for debug purpose only. + */ +static void +fw_vmaccess(struct fw_xfer *xfer){ + struct fw_pkt *rfp, *sfp = NULL; + u_int32_t *ld = (u_int32_t *)xfer->recv.buf; + + printf("vmaccess spd:%2x len:%03x data:%08x %08x %08x %08x\n", + xfer->spd, xfer->recv.len, ntohl(ld[0]), ntohl(ld[1]), ntohl(ld[2]), ntohl(ld[3])); + printf("vmaccess data:%08x %08x %08x %08x\n", ntohl(ld[4]), ntohl(ld[5]), ntohl(ld[6]), ntohl(ld[7])); + if(xfer->resp != 0){ + fw_xfer_free( xfer); + return; + } + if(xfer->recv.buf == NULL){ + fw_xfer_free( xfer); + return; + } + rfp = (struct fw_pkt *)xfer->recv.buf; + switch(rfp->mode.hdr.tcode){ + /* XXX need fix for 64bit arch */ + case FWTCODE_WREQB: + xfer->send.buf = malloc(12, M_FW, M_NOWAIT); + xfer->send.len = 12; + sfp = (struct fw_pkt *)xfer->send.buf; + bcopy(rfp->mode.wreqb.payload, + (caddr_t)ntohl(rfp->mode.wreqb.dest_lo), ntohs(rfp->mode.wreqb.len)); + sfp->mode.wres.tcode = FWTCODE_WRES; + sfp->mode.wres.rtcode = 0; + break; + case FWTCODE_WREQQ: + xfer->send.buf = malloc(12, M_FW, M_NOWAIT); + xfer->send.len = 12; + sfp->mode.wres.tcode = FWTCODE_WRES; + *((u_int32_t *)(ntohl(rfp->mode.wreqb.dest_lo))) = rfp->mode.wreqq.data; + sfp->mode.wres.rtcode = 0; + break; + case FWTCODE_RREQB: + xfer->send.buf = malloc(16 + rfp->mode.rreqb.len, M_FW, M_NOWAIT); + xfer->send.len = 16 + ntohs(rfp->mode.rreqb.len); + sfp = (struct fw_pkt *)xfer->send.buf; + bcopy((caddr_t)ntohl(rfp->mode.rreqb.dest_lo), + sfp->mode.rresb.payload, (u_int16_t)ntohs(rfp->mode.rreqb.len)); + sfp->mode.rresb.tcode = FWTCODE_RRESB; + sfp->mode.rresb.len = rfp->mode.rreqb.len; + sfp->mode.rresb.rtcode = 0; + sfp->mode.rresb.extcode = 0; + break; + case FWTCODE_RREQQ: + xfer->send.buf = malloc(16, M_FW, M_NOWAIT); + xfer->send.len = 16; + sfp = (struct fw_pkt *)xfer->send.buf; + sfp->mode.rresq.data = *(u_int32_t *)(ntohl(rfp->mode.rreqq.dest_lo)); + sfp->mode.wres.tcode = FWTCODE_RRESQ; + sfp->mode.rresb.rtcode = 0; + break; + default: + fw_xfer_free( xfer); + return; + } + sfp->mode.hdr.dst = rfp->mode.hdr.src; + xfer->dst = ntohs(rfp->mode.hdr.src); + xfer->act.hand = fw_xfer_free; + xfer->retry_req = fw_asybusy; + + sfp->mode.hdr.tlrt = rfp->mode.hdr.tlrt; + sfp->mode.hdr.pri = 0; + + fw_asyreq(xfer->fc, -1, xfer); +/**/ + return; +} +#endif + +/* + * CRC16 check-sum for IEEE1394 register blocks. + */ +u_int16_t +fw_crc16(u_int32_t *ptr, u_int32_t len){ + u_int32_t i, sum, crc = 0; + int shift; + len = (len + 3) & ~3; + for(i = 0 ; i < len ; i+= 4){ + for( shift = 28 ; shift >= 0 ; shift -= 4){ + sum = ((crc >> 12) ^ (ptr[i/4] >> shift)) & 0xf; + crc = (crc << 4) ^ ( sum << 12 ) ^ ( sum << 5) ^ sum; + } + crc &= 0xffff; + } + return((u_int16_t) crc); +} + +static int +fw_bmr(struct firewire_comm *fc) +{ + struct fw_device fwdev; + union fw_self_id *self_id; + int cmstr; + + /* Check to see if the current root node is cycle master capable */ + self_id = &fc->topology_map->self_id[fc->max_node]; + if (fc->max_node > 0) { + /* XXX check cmc bit of businfo block rather than contender */ + if (self_id->p0.link_active && self_id->p0.contender) + cmstr = fc->max_node; + else { + device_printf(fc->bdev, + "root node is not cycle master capable\n"); + /* XXX shall we be the cycle master? */ + cmstr = fc->nodeid; + /* XXX need bus reset */ + } + } else + cmstr = -1; + + device_printf(fc->bdev, "bus manager %d ", CSRARC(fc, BUS_MGR_ID)); + if(CSRARC(fc, BUS_MGR_ID) != fc->nodeid) { + /* We are not the bus manager */ + printf("\n"); + return(0); + } + printf("(me)\n"); + + /* Optimize gapcount */ + if(fc->max_hop <= MAX_GAPHOP ) + fw_phy_config(fc, cmstr, gap_cnt[fc->max_hop]); + /* If we are the cycle master, nothing to do */ + if (cmstr == fc->nodeid || cmstr == -1) + return 0; + /* Bus probe has not finished, make dummy fwdev for cmstr */ + bzero(&fwdev, sizeof(fwdev)); + fwdev.fc = fc; + fwdev.dst = cmstr; + fwdev.speed = 0; + fwdev.maxrec = 8; /* 512 */ + fwdev.status = FWDEVINIT; + /* Set cmstr bit on the cycle master */ + fwmem_write_quad(&fwdev, NULL, 0/*spd*/, + 0xffff, 0xf0000000 | STATE_SET, htonl(1 << 8), + fw_asy_callback_free); + + return 0; +} + +DRIVER_MODULE(firewire,fwohci,firewire_driver,firewire_devclass,0,0); +MODULE_VERSION(firewire, 1); diff --git a/sys/dev/firewire/firewire.h b/sys/dev/firewire/firewire.h new file mode 100644 index 0000000..f90596f --- /dev/null +++ b/sys/dev/firewire/firewire.h @@ -0,0 +1,403 @@ +/* + * 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$ + * + */ + +#ifndef _FIREWIRE_H +#define _FIREWIRE_H 1 + +#define DEV_DEF 0 +#define DEV_DV 2 + +struct fw_isochreq { + unsigned char ch:6, + tag:2; +}; + +struct fw_isobufreq { + struct { + unsigned int nchunk; + unsigned int npacket; + unsigned int psize; + } tx, rx; +}; + +struct fw_addr { + unsigned long hi; + unsigned long lo; +}; + +struct fw_asybindreq { + struct fw_addr start; + unsigned long len; +}; + +struct fw_reg_req_t { + unsigned long addr; + unsigned long data; +}; + +#define MAXREC(x) (2 << (x)) +#define FWPMAX_S400 (2048 + 20) /* MAXREC plus space for control data */ +#define FWMAXQUEUE 64 + +#define FWLOCALBUS 0xffc0 + +#define FWTCODE_WREQQ 0 +#define FWTCODE_WREQB 1 +#define FWTCODE_WRES 2 +#define FWTCODE_RREQQ 4 +#define FWTCODE_RREQB 5 +#define FWTCODE_RRESQ 6 +#define FWTCODE_RRESB 7 +#define FWTCODE_CYCS 8 +#define FWTCODE_LREQ 9 +#define FWTCODE_STREAM 0xa +#define FWTCODE_LRES 0xb +#define FWTCODE_PHY 0xe + +#define FWRETRY_1 0 +#define FWRETRY_X 1 +#define FWRETRY_A 2 +#define FWRETRY_B 3 + +#define FWRCODE_COMPLETE 0 +#define FWRCODE_ER_CONFL 4 +#define FWRCODE_ER_DATA 5 +#define FWRCODE_ER_TYPE 6 +#define FWRCODE_ER_ADDR 7 + +#define FWSPD_S100 0 +#define FWSPD_S200 1 +#define FWSPD_S400 2 + +#define FWP_TL_VALID (1 << 7) + +struct fw_isohdr { + u_int32_t hdr[1]; +}; + +struct fw_asyhdr { + u_int32_t hdr[4]; +}; + +#if BYTE_ORDER == BIG_ENDIAN +#define BIT4x2(x,y) u_int8_t x:4, y:4 +#define BIT16x2(x,y) u_int32_t x:16, y:16 +#else +#define BIT4x2(x,y) u_int8_t y:4, x:4 +#define BIT16x2(x,y) u_int32_t y:16, x:16 +#endif + + +#if BYTE_ORDER == BIG_ENDIAN +#define COMMON_HDR(a,b,c,d) u_int32_t a:16,b:8,c:4,d:4 +#define COMMON_RES(a,b,c,d) u_int32_t a:16,b:4,c:4,d:8 +#else +#define COMMON_HDR(a,b,c,d) u_int32_t d:4,c:4,b:8,a:16 +#define COMMON_RES(a,b,c,d) u_int32_t d:8,c:4,b:4,a:16 +#endif + +struct fw_pkt { + union { + u_int32_t ld[0]; + struct { + COMMON_HDR(, , tcode, ); + } common; + struct { + COMMON_HDR(len, chtag, tcode, sy); + u_int32_t payload[0]; + } stream; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, ); + } hdr; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, dest_hi); + u_int32_t dest_lo; + } rreqq; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + COMMON_RES(src, rtcode, , ); + u_int32_t :32; + } wres; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, dest_hi); + u_int32_t dest_lo; + BIT16x2(len, extcode); + } rreqb; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, dest_hi); + u_int32_t dest_lo; + u_int32_t data; + } wreqq; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, dest_hi); + u_int32_t dest_lo; + u_int32_t data; + } cyc; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + COMMON_RES(src, rtcode, , ); + u_int32_t :32; + u_int32_t data; + } rresq; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, dest_hi); + u_int32_t dest_lo; + BIT16x2(len, extcode); + u_int32_t payload[0]; + } wreqb; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + BIT16x2(src, dest_hi); + u_int32_t dest_lo; + BIT16x2(len, extcode); +#define FW_LREQ_MSKSWAP 1 +#define FW_LREQ_CMPSWAP 2 +#define FW_LREQ_FTADD 3 +#define FW_LREQ_LTADD 4 +#define FW_LREQ_BDADD 5 +#define FW_LREQ_WRADD 6 + u_int32_t payload[0]; + } lreq; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + COMMON_RES(src, rtcode, , ); + u_int32_t :32; + BIT16x2(len, extcode); + u_int32_t payload[0]; + } rresb; + struct { + COMMON_HDR(dst, tlrt, tcode, pri); + COMMON_RES(src, rtcode, , ); + u_int32_t :32; + BIT16x2(len, extcode); + u_int32_t payload[0]; + } lres; + } mode; +}; + +struct fw_eui64 { + u_int32_t hi, lo; +}; +#define FW_EUI64_BYTE(eui, x) \ + ((((x)<4)? \ + ((eui)->hi >> (8*(3-(x)))): \ + ((eui)->lo >> (8*(7-(x)))) \ + ) & 0xff) +#define FW_EUI64_EQUAL(x, y) \ + ((x).hi == (y).hi && (x).lo == (y).lo) + +struct fw_asyreq { + struct fw_asyreq_t{ + unsigned char sped; + unsigned int type; +#define FWASREQNODE 0 +#define FWASREQEUI 1 +#define FWASRESTL 2 +#define FWASREQSTREAM 3 + unsigned short len; + union { + struct fw_eui64 eui; + }dst; + }req; + struct fw_pkt pkt; + u_int32_t data[512]; +}; + +struct fw_devinfo { + struct fw_eui64 eui; + u_int16_t dst; + u_int16_t status; +}; + +#define FW_MAX_DEVLST 70 +struct fw_devlstreq { + u_int16_t n; + u_int16_t info_len; + struct fw_devinfo dev[FW_MAX_DEVLST]; +}; + +#define FW_SELF_ID_PORT_CONNECTED_TO_CHILD 3 +#define FW_SELF_ID_PORT_CONNECTED_TO_PARENT 2 +#define FW_SELF_ID_PORT_NOT_CONNECTED 1 +#define FW_SELF_ID_PORT_NOT_EXISTS 0 +#if BYTE_ORDER == BIG_ENDIAN +union fw_self_id { + struct { + u_int32_t id:2, + phy_id:6, + sequel:1, + link_active:1, + gap_count:6, + phy_speed:2, + phy_delay:2, + contender:1, + power_class:3, + port0:2, + port1:2, + port2:2, + initiated_reset:1, + more_packets:1; + } p0; + struct { + u_int32_t + id:2, + phy_id:6, + sequel:1, + sequence_num:3, + :2, + porta:2, + portb:2, + portc:2, + portd:2, + porte:2, + portf:2, + portg:2, + porth:2, + :1, + more_packets:1; + } p1; +}; +#else +union fw_self_id { + struct { + u_int32_t more_packets:1, + initiated_reset:1, + port2:2, + port1:2, + port0:2, + power_class:3, + contender:1, + phy_delay:2, + phy_speed:2, + gap_count:6, + link_active:1, + sequel:1, + phy_id:6, + id:2; + } p0; + struct { + u_int32_t more_packets:1, + reserved1:1, + porth:2, + portg:2, + portf:2, + porte:2, + portd:2, + portc:2, + portb:2, + porta:2, + reserved2:2, + sequence_num:3, + sequel:1, + phy_id:6, + id:2; + } p1; +}; +#endif + + +struct fw_topology_map { + u_int32_t crc:16, + crc_len:16; + u_int32_t generation; + u_int32_t self_id_count:16, + node_count:16; + union fw_self_id self_id[4*64]; +}; + +struct fw_speed_map { + u_int32_t crc:16, + crc_len:16; + u_int32_t generation; + u_int8_t speed[64][64]; +}; + +struct fw_crom_buf { + struct fw_eui64 eui; + int len; + void *ptr; +}; + +/* + * FireWire specific system requests. + */ +#define FW_SSTBUF _IOWR('S', 86, struct fw_isobufreq) +#define FW_GSTBUF _IOWR('S', 87, struct fw_isobufreq) +#define FW_SRSTREAM _IOWR('S', 88, struct fw_isochreq) +#define FW_GRSTREAM _IOWR('S', 89, struct fw_isochreq) +#define FW_STSTREAM _IOWR('S', 90, struct fw_isochreq) +#define FW_GTSTREAM _IOWR('S', 91, struct fw_isochreq) + +#define FW_ASYREQ _IOWR('S', 92, struct fw_asyreq) +#define FW_IBUSRST _IOR('S', 1, unsigned int) +#define FW_GDEVLST _IOWR('S', 2, struct fw_devlstreq) +#define FW_SBINDADDR _IOWR('S', 3, struct fw_asybindreq) +#define FW_CBINDADDR _IOWR('S', 4, struct fw_asybindreq) +#define FW_GTPMAP _IOR('S', 5, struct fw_topology_map) +#define FW_GCROM _IOWR('S', 7, struct fw_crom_buf) + +#define FW_SDEUI64 _IOW('S', 20, struct fw_eui64) +#define FW_GDEUI64 _IOR('S', 21, struct fw_eui64) + +#define FWOHCI_RDREG _IOWR('S', 80, struct fw_reg_req_t) +#define FWOHCI_WRREG _IOWR('S', 81, struct fw_reg_req_t) + +#define DUMPDMA _IOWR('S', 82, u_int32_t) + +#ifdef _KERNEL + +#define FWMAXNDMA 0x100 /* 8 bits DMA channel id. in device No. */ + +#if __FreeBSD_version < 500000 +#define dev2unit(x) ((minor(x) & 0xff) | (minor(x) >> 8)) +#define unit2minor(x) (((x) & 0xff) | (((x) << 8) & ~0xffff)) +#endif + +#define UNIT2MIN(x) (((x) & 0xff) << 8) +#define DEV2UNIT(x) ((dev2unit(x) & 0xff00) >> 8) +#define DEV2DMACH(x) (dev2unit(x) & 0xff) + +#define FWMEM_FLAG 0x10000 +#define DEV_FWMEM(x) (dev2unit(x) & FWMEM_FLAG) +#endif +#endif diff --git a/sys/dev/firewire/firewire_phy.h b/sys/dev/firewire/firewire_phy.h new file mode 100644 index 0000000..02cda69 --- /dev/null +++ b/sys/dev/firewire/firewire_phy.h @@ -0,0 +1,85 @@ +/* + * 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 FW_PHY_PHYSID_REG 0x00 +#define FW_PHY_PHYSID (63<<2) +#define FW_PHY_ROOT_REG 0x00 +#define FW_PHY_ROOT (1<<1) +#define FW_PHY_CPS_REG 0x00 +#define FW_PHY_CPS (1<<0) + +#define FW_PHY_RHB_REG 0x01 +#define FW_PHY_RHB (1<<7) +#define FW_PHY_IBR_REG 0x01 +#define FW_PHY_IBR (1<<6) +#define FW_PHY_ISBR_REG 0x05 +#define FW_PHY_ISBR (1<<6) +#define FW_PHY_GC_REG 0x01 + +#define FW_PHY_SPD_REG 0x02 +#define FW_PHY_SPD (3<<6) +#define FW_PHY_REV_REG 0x02 +#define FW_PHY_REV (3<<4) +#define FW_PHY_NP_REG 0x02 +#define FW_PHY_NP (15<<0) + +#define FW_PHY_P1_REG 0x03 +#define FW_PHY_P2_REG 0x04 +#define FW_PHY_P3_REG 0x05 + +#define FW_PHY_P_ASTAT (3<<6) +#define FW_PHY_P_BSTAT (3<<4) +#define FW_PHY_P_CH (1<<3) +#define FW_PHY_P_CON (1<<2) + +#define FW_PHY_LOOPINT_REG 0x06 +#define FW_PHY_LOOPINT (1<<7) +#define FW_PHY_CPSINT_REG 0x06 +#define FW_PHY_CPSNT (1<<6) +/* +#define FW_PHY_CPS_REG 0x06 +#define FW_PHY_CPS (1<<5) +*/ +#define FW_PHY_IR_REG 0x06 +#define FW_PHY_IR (1<<4) +#define FW_PHY_C_REG 0x06 +#define FW_PHY_C (1<<0) + +#define FW_PHY_ESPD_REG 0x03 +#define FW_PHY_ESPD (7<<5) + +#define FW_PHY_EDEL_REG 0x03 +#define FW_PHY_EDEL 15<<0 diff --git a/sys/dev/firewire/firewirereg.h b/sys/dev/firewire/firewirereg.h new file mode 100644 index 0000000..f16fd32 --- /dev/null +++ b/sys/dev/firewire/firewirereg.h @@ -0,0 +1,306 @@ +/* + * 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$ + * + */ + +#if __FreeBSD_version >= 500000 +typedef struct thread fw_proc; +#include <sys/selinfo.h> +#else +typedef struct proc fw_proc; +#include <sys/select.h> +#endif + +#include <sys/uio.h> + +#define splfw splimp + +struct fw_device{ + u_int16_t dst; + struct fw_eui64 eui; + u_int8_t speed; + u_int8_t maxrec; + u_int8_t nport; + u_int8_t power; +#define CSRROMOFF 0x400 +#define CSRROMSIZE 0x400 + int rommax; /* offset from 0xffff f000 0000 */ + u_int32_t csrrom[CSRROMSIZE/4]; + int rcnt; + struct firewire_comm *fc; + u_int32_t status; +#define FWDEVINIT 1 +#define FWDEVATTACHED 2 +#define FWDEVINVAL 3 + STAILQ_ENTRY(fw_device) link; +}; + +struct firewire_softc { +#if __FreeBSD_version >= 500000 + dev_t dev; +#else + dev_t dev[FWMAXNDMA+1]; +#endif + struct firewire_comm *fc; +}; + +#define FW_MAX_DMACH 0x20 +#define FW_MAX_DEVCH FW_MAX_DMACH +#define FW_XFERTIMEOUT 1 + +struct firewire_dev_comm { + device_t dev; + struct firewire_comm *fc; + void (*post_busreset) __P((void *)); + void (*post_explore) __P((void *)); +}; + +struct tcode_info { + u_char hdr_len; /* IEEE1394 header length */ + u_char flag; +#define FWTI_REQ (1 << 0) +#define FWTI_RES (1 << 1) +#define FWTI_TLABEL (1 << 2) +#define FWTI_BLOCK_STR (1 << 3) +#define FWTI_BLOCK_ASY (1 << 4) +}; + +struct firewire_comm{ + device_t dev; + device_t bdev; + u_int16_t busid:10, + nodeid:6; + u_int mode; + u_int nport; + u_int speed; + u_int maxrec; + u_int irm; + u_int max_node; + u_int max_hop; + u_int max_asyretry; +#define FWPHYASYST (1 << 0) + u_int retry_count; + u_int32_t ongobus:10, + ongonode:6, + ongoaddr:16; + struct fw_device *ongodev; + struct fw_eui64 ongoeui; +#define FWMAXCSRDIR 16 + SLIST_HEAD(, csrdir) ongocsr; + SLIST_HEAD(, csrdir) csrfree; + u_int32_t status; +#define FWBUSRESET 0 +#define FWBUSINIT 1 +#define FWBUSCYMELECT 2 +#define FWBUSMGRELECT 3 +#define FWBUSMGRDONE 4 +#define FWBUSEXPLORE 5 +#define FWBUSPHYCONF 6 +#define FWBUSEXPDONE 7 +#define FWBUSCOMPLETION 10 + int nisodma; + struct fw_eui64 eui; + struct fw_xferq + *arq, *atq, *ars, *ats, *it[FW_MAX_DMACH],*ir[FW_MAX_DMACH]; + STAILQ_HEAD(, tlabel) tlabels[0x40]; + STAILQ_HEAD(, fw_bind) binds; + STAILQ_HEAD(, fw_device) devices; + STAILQ_HEAD(, fw_xfer) pending; + u_int sid_cnt; +#define CSRSIZE 0x4000 + u_int32_t csr_arc[CSRSIZE/4]; +#define CROMSIZE 0x400 + u_int32_t *config_rom; + struct crom_src_buf *crom_src_buf; + struct crom_src *crom_src; + struct crom_chunk *crom_root; + struct fw_topology_map *topology_map; + struct fw_speed_map *speed_map; + struct callout busprobe_callout; + struct callout bmr_callout; + struct callout timeout_callout; + struct callout retry_probe_callout; + u_int32_t (*cyctimer) __P((struct firewire_comm *)); + void (*ibr) __P((struct firewire_comm *)); + u_int32_t (*set_bmr) __P((struct firewire_comm *, u_int32_t)); + int (*ioctl) __P((dev_t, u_long, caddr_t, int, fw_proc *)); + int (*irx_enable) __P((struct firewire_comm *, int)); + int (*irx_disable) __P((struct firewire_comm *, int)); + int (*itx_enable) __P((struct firewire_comm *, int)); + int (*itx_disable) __P((struct firewire_comm *, int)); + void (*timeout) __P((void *)); + void (*poll) __P((struct firewire_comm *, int, int)); + void (*set_intr) __P((struct firewire_comm *, int)); + void (*irx_post) __P((struct firewire_comm *, u_int32_t *)); + void (*itx_post) __P((struct firewire_comm *, u_int32_t *)); + struct tcode_info *tcode; + bus_dma_tag_t dmat; +}; +#define CSRARC(sc, offset) ((sc)->csr_arc[(offset)/4]) + +struct csrdir{ + u_int32_t ongoaddr; + u_int32_t off; + SLIST_ENTRY(csrdir) link; +}; + +struct fw_xferq { + int flag; +#define FWXFERQ_CHTAGMASK 0xff +#define FWXFERQ_RUNNING (1 << 8) +#define FWXFERQ_STREAM (1 << 9) + +#define FWXFERQ_BULK (1 << 11) +#define FWXFERQ_MODEMASK (7 << 10) + +#define FWXFERQ_EXTBUF (1 << 13) +#define FWXFERQ_OPEN (1 << 14) + +#define FWXFERQ_HANDLER (1 << 16) +#define FWXFERQ_WAKEUP (1 << 17) + + void (*start) __P((struct firewire_comm*)); + STAILQ_HEAD(, fw_xfer) q; + u_int queued; + u_int maxq; + u_int psize; + STAILQ_HEAD(, fw_bind) binds; + struct fwdma_alloc_multi *buf; + u_int bnchunk; + u_int bnpacket; + struct fw_bulkxfer *bulkxfer; + STAILQ_HEAD(, fw_bulkxfer) stvalid; + STAILQ_HEAD(, fw_bulkxfer) stfree; + STAILQ_HEAD(, fw_bulkxfer) stdma; + struct fw_bulkxfer *stproc; + struct selinfo rsel; + caddr_t sc; + void (*hand) __P((struct fw_xferq *)); +}; + +struct fw_bulkxfer{ + int poffset; + struct mbuf *mbuf; + STAILQ_ENTRY(fw_bulkxfer) link; + caddr_t start; + caddr_t end; + int resp; +}; + +struct tlabel{ + struct fw_xfer *xfer; + STAILQ_ENTRY(tlabel) link; +}; + +struct fw_bind{ + u_int32_t start_hi, start_lo, addrlen; + STAILQ_HEAD(, fw_xfer) xferlist; + STAILQ_ENTRY(fw_bind) fclist; + STAILQ_ENTRY(fw_bind) chlist; +#define FWACT_NULL 0 +#define FWACT_XFER 2 +#define FWACT_CH 3 + u_int8_t act_type; + u_int8_t sub; +}; + +struct fw_xfer{ + caddr_t sc; + struct firewire_comm *fc; + struct fw_xferq *q; + struct timeval tv; + /* XXX should be removed */ + u_int32_t dst; /* XXX for if_fwe */ + u_int8_t spd; + int8_t resp; +#define FWXF_INIT 0 +#define FWXF_INQ 1 +#define FWXF_START 2 +#define FWXF_SENT 3 +#define FWXF_SENTERR 4 +#define FWXF_BUSY 8 +#define FWXF_RCVD 10 + u_int8_t state; + u_int8_t retry; + u_int8_t tl; + void (*retry_req) __P((struct fw_xfer *)); + union{ + void (*hand) __P((struct fw_xfer *)); + } act; + struct { + int len; + caddr_t buf; + } send, recv; + struct mbuf *mbuf; + STAILQ_ENTRY(fw_xfer) link; + struct malloc_type *malloc; +}; +void fw_sidrcv __P((struct firewire_comm *, u_int32_t *, u_int)); +void fw_rcv __P((struct firewire_comm *, struct iovec *, int, u_int, u_int)); +void fw_xfer_unload __P(( struct fw_xfer*)); +void fw_xfer_free __P(( struct fw_xfer*)); +struct fw_xfer *fw_xfer_alloc __P((struct malloc_type *)); +struct fw_xfer *fw_xfer_alloc_buf __P((struct malloc_type *, int, int)); +void fw_init __P((struct firewire_comm *)); +int fw_tbuf_update __P((struct firewire_comm *, int, int)); +int fw_rbuf_update __P((struct firewire_comm *, int, int)); +void fw_asybusy __P((struct fw_xfer *)); +int fw_bindadd __P((struct firewire_comm *, struct fw_bind *)); +int fw_bindremove __P((struct firewire_comm *, struct fw_bind *)); +int fw_asyreq __P((struct firewire_comm *, int, struct fw_xfer*)); +void fw_busreset __P((struct firewire_comm *)); +u_int16_t fw_crc16 __P((u_int32_t *, u_int32_t)); +void fw_xfer_timeout __P((void *)); +void fw_xfer_done __P((struct fw_xfer *)); +void fw_asy_callback __P((struct fw_xfer *)); +struct fw_device *fw_noderesolve_nodeid __P((struct firewire_comm *, int)); +struct fw_device *fw_noderesolve_eui64 __P((struct firewire_comm *, struct fw_eui64 *)); +struct fw_bind *fw_bindlookup __P((struct firewire_comm *, u_int32_t, u_int32_t)); +void fw_drain_txq __P((struct firewire_comm *)); + + +extern int firewire_debug; +extern devclass_t firewire_devclass; + +#define FWPRI ((PZERO+8)|PCATCH) + +#if __FreeBSD_version >= 500000 +#define CALLOUT_INIT(x) callout_init(x, 0 /* mpsafe */) +#else +#define CALLOUT_INIT(x) callout_init(x) +#endif + +MALLOC_DECLARE(M_FW); +MALLOC_DECLARE(M_FWXFER); diff --git a/sys/dev/firewire/fwcrom.c b/sys/dev/firewire/fwcrom.c new file mode 100644 index 0000000..24ab07c --- /dev/null +++ b/sys/dev/firewire/fwcrom.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2002-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 <sys/param.h> +#if defined(_KERNEL) || defined(TEST) +#include <sys/queue.h> +#endif +#ifdef _KERNEL +#include <sys/systm.h> +#include <sys/kernel.h> +#else +#include <netinet/in.h> +#include <fcntl.h> +#include <stdio.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> +#endif +#include <dev/firewire/firewire.h> +#include <dev/firewire/iec13213.h> + +#define MAX_ROM (1024 - sizeof(u_int32_t) * 5) +#define CROM_END(cc) ((vm_offset_t)(cc)->stack[0].dir + MAX_ROM - 1) + +void +crom_init_context(struct crom_context *cc, u_int32_t *p) +{ + struct csrhdr *hdr; + + hdr = (struct csrhdr *)p; + if (hdr->info_len == 1) { + /* minimum ROM */ + cc->depth = -1; + } + p += 1 + hdr->info_len; + + /* check size of root directory */ + if (((struct csrdirectory *)p)->crc_len == 0) { + cc->depth = -1; + return; + } + cc->depth = 0; + cc->stack[0].dir = (struct csrdirectory *)p; + cc->stack[0].index = 0; +} + +struct csrreg * +crom_get(struct crom_context *cc) +{ + struct crom_ptr *ptr; + + ptr = &cc->stack[cc->depth]; + return (&ptr->dir->entry[ptr->index]); +} + +void +crom_next(struct crom_context *cc) +{ + struct crom_ptr *ptr; + struct csrreg *reg; + + if (cc->depth < 0) + return; + reg = crom_get(cc); + if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) { + if (cc->depth >= CROM_MAX_DEPTH) { + printf("crom_next: too deep\n"); + goto again; + } + cc->depth ++; + + ptr = &cc->stack[cc->depth]; + ptr->dir = (struct csrdirectory *) (reg + reg->val); + ptr->index = 0; + goto check; + } +again: + ptr = &cc->stack[cc->depth]; + ptr->index ++; +check: + if (ptr->index < ptr->dir->crc_len && + (vm_offset_t)crom_get(cc) <= CROM_END(cc)) + return; + + if (ptr->index < ptr->dir->crc_len) + printf("crom_next: bound check failed\n"); + + if (cc->depth > 0) { + cc->depth--; + goto again; + } + /* no more data */ + cc->depth = -1; +} + + +struct csrreg * +crom_search_key(struct crom_context *cc, u_int8_t key) +{ + struct csrreg *reg; + + while(cc->depth >= 0) { + reg = crom_get(cc); + if (reg->key == key) + return reg; + crom_next(cc); + } + return NULL; +} + +int +crom_has_specver(u_int32_t *p, u_int32_t spec, u_int32_t ver) +{ + struct csrreg *reg; + struct crom_context c, *cc; + int state = 0; + + cc = &c; + crom_init_context(cc, p); + while(cc->depth >= 0) { + reg = crom_get(cc); + if (state == 0) { + if (reg->key == CSRKEY_SPEC && reg->val == spec) + state = 1; + else + state = 0; + } else { + if (reg->key == CSRKEY_VER && reg->val == ver) + return 1; + else + state = 0; + } + crom_next(cc); + } + return 0; +} + +void +crom_parse_text(struct crom_context *cc, char *buf, int len) +{ + struct csrreg *reg; + struct csrtext *textleaf; + u_int32_t *bp; + int i, qlen; + static char *nullstr = "(null)"; + + if (cc->depth < 0) + return; + + reg = crom_get(cc); + if (reg->key != CROM_TEXTLEAF || + (vm_offset_t)(reg + reg->val) > CROM_END(cc)) { + strncpy(buf, nullstr, len); + return; + } + textleaf = (struct csrtext *)(reg + reg->val); + + if ((vm_offset_t)textleaf + textleaf->crc_len > CROM_END(cc)) { + strncpy(buf, nullstr, len); + return; + } + + /* XXX should check spec and type */ + + bp = (u_int32_t *)&buf[0]; + qlen = textleaf->crc_len - 2; + if (len < qlen * 4) + qlen = len/4; + for (i = 0; i < qlen; i ++) + *bp++ = ntohl(textleaf->text[i]); + /* make sure to terminate the string */ + if (len <= qlen * 4) + buf[len - 1] = 0; + else + buf[qlen * 4] = 0; +} + +u_int16_t +crom_crc(u_int32_t *ptr, int len) +{ + int i, shift; + u_int32_t data, sum, crc = 0; + + for (i = 0; i < len; i++) { + data = ptr[i]; + for (shift = 28; shift >= 0; shift -= 4) { + sum = ((crc >> 12) ^ (data >> shift)) & 0xf; + crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum; + } + crc &= 0xffff; + } + return((u_int16_t) crc); +} + +#ifndef _KERNEL +static void +crom_desc_specver(u_int32_t spec, u_int32_t ver, char *buf, int len) +{ + char *s = NULL; + + if (spec == CSRVAL_ANSIT10 || spec == 0) { + switch (ver) { + case CSRVAL_T10SBP2: + s = "SBP-2"; + break; + default: + if (spec != 0) + s = "unknown ANSIT10"; + } + } + if (spec == CSRVAL_1394TA || spec == 0) { + switch (ver) { + case CSR_PROTAVC: + s = "AV/C"; + break; + case CSR_PROTCAL: + s = "CAL"; + break; + case CSR_PROTEHS: + s = "EHS"; + break; + case CSR_PROTHAVI: + s = "HAVi"; + break; + case CSR_PROTCAM104: + s = "1394 Cam 1.04"; + break; + case CSR_PROTCAM120: + s = "1394 Cam 1.20"; + break; + case CSR_PROTCAM130: + s = "1394 Cam 1.30"; + break; + case CSR_PROTDPP: + s = "1394 Direct print"; + break; + case CSR_PROTIICP: + s = "Industrial & Instrument"; + break; + default: + if (spec != 0) + s = "unknown 1394TA"; + } + } + if (s != NULL) + snprintf(buf, len, "%s", s); +} + +char * +crom_desc(struct crom_context *cc, char *buf, int len) +{ + struct csrreg *reg; + struct csrdirectory *dir; + char *desc, st; + u_int16_t crc; + + reg = crom_get(cc); + switch (reg->key & CSRTYPE_MASK) { + case CSRTYPE_I: +#if 0 + len -= snprintf(buf, len, "%d", reg->val); + buf += strlen(buf); +#else + *buf = '\0'; +#endif + break; + case CSRTYPE_C: + len -= snprintf(buf, len, "offset=0x%04x(%d)", + reg->val, reg->val); + buf += strlen(buf); + break; + case CSRTYPE_L: + /* XXX fall through */ + case CSRTYPE_D: + dir = (struct csrdirectory *) (reg + reg->val); + crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len); + len -= snprintf(buf, len, "len=%d crc=0x%04x(%s) ", + dir->crc_len, dir->crc, + (crc == dir->crc) ? "OK" : "NG"); + buf += strlen(buf); + } + switch (reg->key) { + case 0x03: + desc = "module_vendor_ID"; + break; + case 0x04: + desc = "hardware_version"; + break; + case 0x0c: + desc = "node_capabilities"; + break; + case 0x12: + desc = "unit_spec_ID"; + break; + case 0x13: + desc = "unit_sw_version"; + crom_desc_specver(0, reg->val, buf, len); + break; + case 0x14: + desc = "logical_unit_number"; + break; + case 0x17: + desc = "model_ID"; + break; + case 0x38: + desc = "command_set_spec_ID"; + break; + case 0x39: + desc = "command_set"; + break; + case 0x3a: + desc = "unit_characteristics"; + break; + case 0x3b: + desc = "command_set_revision"; + break; + case 0x3c: + desc = "firmware_revision"; + break; + case 0x3d: + desc = "reconnect_timeout"; + break; + case 0x54: + desc = "management_agent"; + break; + case 0x81: + desc = "text_leaf"; + crom_parse_text(cc, buf + strlen(buf), len); + break; + case 0xd1: + desc = "unit_directory"; + break; + case 0xd4: + desc = "logical_unit_directory"; + break; + default: + desc = "unknown"; + } + return desc; +} +#endif + +#if defined(_KERNEL) || defined(TEST) + +int +crom_add_quad(struct crom_chunk *chunk, u_int32_t entry) +{ + int index; + + index = chunk->data.crc_len; + if (index >= CROM_MAX_CHUNK_LEN - 1) { + printf("too large chunk %d\n", index); + return(-1); + } + chunk->data.buf[index] = entry; + chunk->data.crc_len++; + return(index); +} + +int +crom_add_entry(struct crom_chunk *chunk, int key, int val) +{ + struct csrreg *reg; + u_int32_t i; + + reg = (struct csrreg *)&i; + reg->key = key; + reg->val = val; + return(crom_add_quad(chunk, (u_int32_t) i)); +} + +int +crom_add_chunk(struct crom_src *src, struct crom_chunk *parent, + struct crom_chunk *child, int key) +{ + int index; + + if (parent == NULL) { + STAILQ_INSERT_TAIL(&src->chunk_list, child, link); + return(0); + } + + index = crom_add_entry(parent, key, 0); + if (index < 0) { + return(-1); + } + child->ref_chunk = parent; + child->ref_index = index; + STAILQ_INSERT_TAIL(&src->chunk_list, child, link); + return(index); +} + +int +crom_add_simple_text(struct crom_src *src, struct crom_chunk *parent, + struct crom_chunk *chunk, char *buf) +{ + struct csrtext *tl; + u_int32_t *p; + int len, i; + + len = strlen(buf); +#define MAX_TEXT ((CROM_MAX_CHUNK_LEN + 1) * 4 - sizeof(struct csrtext)) + if (len > MAX_TEXT) { +#if __FreeBSD_version < 500000 + printf("text(%d) trancated to %d.\n", len, MAX_TEXT); +#else + printf("text(%d) trancated to %td.\n", len, MAX_TEXT); +#endif + len = MAX_TEXT; + } + + tl = (struct csrtext *) &chunk->data; + tl->crc_len = howmany(sizeof(struct csrtext) + len, sizeof(u_int32_t)); + tl->spec_id = 0; + tl->spec_type = 0; + tl->lang_id = 0; + p = (u_int32_t *) buf; + for (i = 0; i < howmany(len, sizeof(u_int32_t)); i ++) + tl->text[i] = ntohl(*p++); + return (crom_add_chunk(src, parent, chunk, CROM_TEXTLEAF)); +} + +static int +crom_copy(u_int32_t *src, u_int32_t *dst, int *offset, int len, int maxlen) +{ + if (*offset + len > maxlen) { + printf("Config. ROM is too large for the buffer\n"); + return(-1); + } + bcopy(src, (char *)(dst + *offset), len * sizeof(u_int32_t)); + *offset += len; + return(0); +} + +int +crom_load(struct crom_src *src, u_int32_t *buf, int maxlen) +{ + struct crom_chunk *chunk, *parent; + struct csrhdr *hdr; +#ifdef _KERNEL + u_int32_t *ptr; + int i; +#endif + int count, offset; + int len; + + offset = 0; + /* Determine offset */ + STAILQ_FOREACH(chunk, &src->chunk_list, link) { + chunk->offset = offset; + /* Assume the offset of the parent is already known */ + parent = chunk->ref_chunk; + if (parent != NULL) { + struct csrreg *reg; + reg = (struct csrreg *) + &parent->data.buf[chunk->ref_index]; + reg->val = offset - + (parent->offset + 1 + chunk->ref_index); + } + offset += 1 + chunk->data.crc_len; + } + + /* Calculate CRC and dump to the buffer */ + len = 1 + src->hdr.info_len; + count = 0; + if (crom_copy((u_int32_t *)&src->hdr, buf, &count, len, maxlen) < 0) + return(-1); + STAILQ_FOREACH(chunk, &src->chunk_list, link) { + chunk->data.crc = + crom_crc(&chunk->data.buf[0], chunk->data.crc_len); + + len = 1 + chunk->data.crc_len; + if (crom_copy((u_int32_t *)&chunk->data, buf, + &count, len, maxlen) < 0) + return(-1); + } + hdr = (struct csrhdr *)buf; + hdr->crc_len = count - 1; + hdr->crc = crom_crc(&buf[1], hdr->crc_len); + +#ifdef _KERNEL + /* byte swap */ + ptr = buf; + for (i = 0; i < count; i ++) { + *ptr = htonl(*ptr); + ptr++; + } +#endif + + return(count); +} +#endif + +#ifdef TEST +int +main () { + struct crom_src src; + struct crom_chunk root,unit1,unit2,unit3; + struct crom_chunk text1,text2,text3,text4,text5,text6,text7; + u_int32_t buf[256], *p; + int i; + + bzero(&src, sizeof(src)); + bzero(&root, sizeof(root)); + bzero(&unit1, sizeof(unit1)); + bzero(&unit2, sizeof(unit2)); + bzero(&unit3, sizeof(unit3)); + bzero(&text1, sizeof(text1)); + bzero(&text2, sizeof(text2)); + bzero(&text3, sizeof(text3)); + bzero(&text3, sizeof(text4)); + bzero(&text3, sizeof(text5)); + bzero(&text3, sizeof(text6)); + bzero(&text3, sizeof(text7)); + bzero(buf, sizeof(buf)); + + /* BUS info sample */ + src.hdr.info_len = 4; + src.businfo.bus_name = CSR_BUS_NAME_IEEE1394; + src.businfo.eui64.hi = 0x11223344; + src.businfo.eui64.lo = 0x55667788; + src.businfo.link_spd = FWSPD_S400; + src.businfo.generation = 0; + src.businfo.max_rom = MAXROM_4; + src.businfo.max_rec = 10; + src.businfo.cyc_clk_acc = 100; + src.businfo.pmc = 0; + src.businfo.bmc = 1; + src.businfo.isc = 1; + src.businfo.cmc = 1; + src.businfo.irmc = 1; + STAILQ_INIT(&src.chunk_list); + + /* Root directory */ + crom_add_chunk(&src, NULL, &root, 0); + crom_add_entry(&root, CSRKEY_NCAP, 0x123456); + /* private company_id */ + crom_add_entry(&root, CSRKEY_VENDOR, 0xacde48); + + crom_add_simple_text(&src, &root, &text1, "FreeBSD"); + crom_add_entry(&root, CSRKEY_HW, __FreeBSD_version); + crom_add_simple_text(&src, &root, &text2, "FreeBSD-5"); + + /* SBP unit directory */ + crom_add_chunk(&src, &root, &unit1, CROM_UDIR); + crom_add_entry(&unit1, CSRKEY_SPEC, CSRVAL_ANSIT10); + crom_add_entry(&unit1, CSRKEY_VER, CSRVAL_T10SBP2); + crom_add_entry(&unit1, CSRKEY_COM_SPEC, CSRVAL_ANSIT10); + crom_add_entry(&unit1, CSRKEY_COM_SET, CSRVAL_SCSI); + /* management_agent */ + crom_add_entry(&unit1, CROM_MGM, 0x1000); + crom_add_entry(&unit1, CSRKEY_UNIT_CH, (10<<8) | 8); + /* Device type and LUN */ + crom_add_entry(&unit1, CROM_LUN, 0); + crom_add_entry(&unit1, CSRKEY_MODEL, 1); + crom_add_simple_text(&src, &unit1, &text3, "scsi_target"); + + /* RFC2734 IPv4 over IEEE1394 */ + crom_add_chunk(&src, &root, &unit2, CROM_UDIR); + crom_add_entry(&unit2, CSRKEY_SPEC, CSRVAL_IETF); + crom_add_simple_text(&src, &unit2, &text4, "IANA"); + crom_add_entry(&unit2, CSRKEY_VER, 1); + crom_add_simple_text(&src, &unit2, &text5, "IPv4"); + + /* RFC3146 IPv6 over IEEE1394 */ + crom_add_chunk(&src, &root, &unit3, CROM_UDIR); + crom_add_entry(&unit3, CSRKEY_SPEC, CSRVAL_IETF); + crom_add_simple_text(&src, &unit3, &text6, "IANA"); + crom_add_entry(&unit3, CSRKEY_VER, 2); + crom_add_simple_text(&src, &unit3, &text7, "IPv6"); + + crom_load(&src, buf, 256); + p = buf; +#define DUMP_FORMAT "%08x %08x %08x %08x %08x %08x %08x %08x\n" + for (i = 0; i < 256/8; i ++) { + printf(DUMP_FORMAT, + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + p += 8; + } + return(0); +} +#endif diff --git a/sys/dev/firewire/fwdev.c b/sys/dev/firewire/fwdev.c new file mode 100644 index 0000000..02b927e --- /dev/null +++ b/sys/dev/firewire/fwdev.c @@ -0,0 +1,703 @@ +/* + * 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$ + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/mbuf.h> + +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/conf.h> +#include <sys/poll.h> + +#include <sys/bus.h> +#include <machine/bus.h> + +#include <sys/ioccom.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/fwdma.h> +#include <dev/firewire/fwmem.h> +#include <dev/firewire/iec68113.h> + +#define CDEV_MAJOR 127 +#define FWNODE_INVAL 0xffff + +static d_open_t fw_open; +static d_close_t fw_close; +static d_ioctl_t fw_ioctl; +static d_poll_t fw_poll; +static d_read_t fw_read; /* for Isochronous packet */ +static d_write_t fw_write; +static d_mmap_t fw_mmap; + +struct cdevsw firewire_cdevsw = +{ +#if __FreeBSD_version >= 500104 + .d_open = fw_open, + .d_close = fw_close, + .d_read = fw_read, + .d_write = fw_write, + .d_ioctl = fw_ioctl, + .d_poll = fw_poll, + .d_mmap = fw_mmap, + .d_name = "fw", + .d_maj = CDEV_MAJOR, + .d_flags = D_MEM +#else + fw_open, fw_close, fw_read, fw_write, fw_ioctl, + fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM +#endif +}; + +static int +fw_open (dev_t dev, int flags, int fmt, fw_proc *td) +{ + struct firewire_softc *sc; + int unit = DEV2UNIT(dev); + int sub = DEV2DMACH(dev); + + int err = 0; + + if (DEV_FWMEM(dev)) + return fwmem_open(dev, flags, fmt, td); + + sc = devclass_get_softc(firewire_devclass, unit); + if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){ + err = EBUSY; + return err; + } + if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){ + err = EBUSY; + return err; + } + if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){ + err = EBUSY; + return err; + } +/* Default is per packet mode */ + sc->fc->ir[sub]->flag |= FWXFERQ_OPEN; + sc->fc->it[sub]->flag |= FWXFERQ_OPEN; + return err; +} + +static int +fw_close (dev_t dev, int flags, int fmt, fw_proc *td) +{ + struct firewire_softc *sc; + int unit = DEV2UNIT(dev); + int sub = DEV2DMACH(dev); + struct fw_xfer *xfer; + struct fw_bind *fwb; + int err = 0; + + if (DEV_FWMEM(dev)) + return fwmem_close(dev, flags, fmt, td); + + sc = devclass_get_softc(firewire_devclass, unit); + if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){ + err = EINVAL; + return err; + } + sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN; + if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){ + err = EINVAL; + return err; + } + sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN; + + if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){ + sc->fc->irx_disable(sc->fc, sub); + } + if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){ + sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING; + sc->fc->itx_disable(sc->fc, sub); + } + if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){ + if (sc->fc->ir[sub]->buf != NULL) + fwdma_free_multiseg(sc->fc->ir[sub]->buf); + sc->fc->ir[sub]->buf = NULL; + free(sc->fc->ir[sub]->bulkxfer, M_FW); + sc->fc->ir[sub]->bulkxfer = NULL; + sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF; + sc->fc->ir[sub]->psize = PAGE_SIZE; + sc->fc->ir[sub]->maxq = FWMAXQUEUE; + } + if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){ + if (sc->fc->it[sub]->buf != NULL) + fwdma_free_multiseg(sc->fc->it[sub]->buf); + sc->fc->it[sub]->buf = NULL; + free(sc->fc->it[sub]->bulkxfer, M_FW); + sc->fc->it[sub]->bulkxfer = NULL; + sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF; + sc->fc->it[sub]->psize = 0; + sc->fc->it[sub]->maxq = FWMAXQUEUE; + } + for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q); + xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){ + sc->fc->ir[sub]->queued--; + STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link); + + xfer->resp = 0; + fw_xfer_done(xfer); + } + for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL; + fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){ + STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); + STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist); + free(fwb, M_FW); + } + sc->fc->ir[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); + sc->fc->it[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); + return err; +} + +/* + * read request. + */ +static int +fw_read (dev_t dev, struct uio *uio, int ioflag) +{ + struct firewire_softc *sc; + struct fw_xferq *ir; + struct fw_xfer *xfer; + int err = 0, s, slept = 0; + int unit = DEV2UNIT(dev); + int sub = DEV2DMACH(dev); + struct fw_pkt *fp; + + if (DEV_FWMEM(dev)) + return fwmem_read(dev, uio, ioflag); + + sc = devclass_get_softc(firewire_devclass, unit); + + ir = sc->fc->ir[sub]; + +readloop: + xfer = STAILQ_FIRST(&ir->q); + if (ir->stproc == NULL) { + /* iso bulkxfer */ + ir->stproc = STAILQ_FIRST(&ir->stvalid); + if (ir->stproc != NULL) { + s = splfw(); + STAILQ_REMOVE_HEAD(&ir->stvalid, link); + splx(s); + ir->queued = 0; + } + } + if (xfer == NULL && ir->stproc == NULL) { + /* no data avaliable */ + if (slept == 0) { + slept = 1; + ir->flag |= FWXFERQ_WAKEUP; + err = tsleep(ir, FWPRI, "fw_read", hz); + ir->flag &= ~FWXFERQ_WAKEUP; + if (err == 0) + goto readloop; + } else if (slept == 1) + err = EIO; + return err; + } else if(xfer != NULL) { + /* per packet mode or FWACT_CH bind?*/ + s = splfw(); + ir->queued --; + STAILQ_REMOVE_HEAD(&ir->q, link); + splx(s); + fp = (struct fw_pkt *)xfer->recv.buf; + if(sc->fc->irx_post != NULL) + sc->fc->irx_post(sc->fc, fp->mode.ld); + err = uiomove(xfer->recv.buf, xfer->recv.len, uio); + /* XXX we should recycle this xfer */ + fw_xfer_free( xfer); + } else if(ir->stproc != NULL) { + /* iso bulkxfer */ + fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, + ir->stproc->poffset + ir->queued); + if(sc->fc->irx_post != NULL) + sc->fc->irx_post(sc->fc, fp->mode.ld); + if(fp->mode.stream.len == 0){ + err = EIO; + return err; + } + err = uiomove((caddr_t)fp, + fp->mode.stream.len + sizeof(u_int32_t), uio); + ir->queued ++; + if(ir->queued >= ir->bnpacket){ + s = splfw(); + STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); + splx(s); + sc->fc->irx_enable(sc->fc, sub); + ir->stproc = NULL; + } + if (uio->uio_resid >= ir->psize) { + slept = -1; + goto readloop; + } + } + return err; +} + +static int +fw_write (dev_t dev, struct uio *uio, int ioflag) +{ + int err = 0; + struct firewire_softc *sc; + int unit = DEV2UNIT(dev); + int sub = DEV2DMACH(dev); + int s, slept = 0; + struct fw_pkt *fp; + struct firewire_comm *fc; + struct fw_xferq *it; + + if (DEV_FWMEM(dev)) + return fwmem_write(dev, uio, ioflag); + + sc = devclass_get_softc(firewire_devclass, unit); + fc = sc->fc; + it = sc->fc->it[sub]; +isoloop: + if (it->stproc == NULL) { + it->stproc = STAILQ_FIRST(&it->stfree); + if (it->stproc != NULL) { + s = splfw(); + STAILQ_REMOVE_HEAD(&it->stfree, link); + splx(s); + it->queued = 0; + } else if (slept == 0) { + slept = 1; + err = sc->fc->itx_enable(sc->fc, sub); + if (err) + return err; + err = tsleep(it, FWPRI, "fw_write", hz); + if (err) + return err; + goto isoloop; + } else { + err = EIO; + return err; + } + } + fp = (struct fw_pkt *)fwdma_v_addr(it->buf, + it->stproc->poffset + it->queued); + err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); + err = uiomove((caddr_t)fp->mode.stream.payload, + fp->mode.stream.len, uio); + it->queued ++; + if (it->queued >= it->bnpacket) { + s = splfw(); + STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); + splx(s); + it->stproc = NULL; + err = sc->fc->itx_enable(sc->fc, sub); + } + if (uio->uio_resid >= sizeof(struct fw_isohdr)) { + slept = 0; + goto isoloop; + } + return err; +} + +/* + * ioctl support. + */ +int +fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) +{ + struct firewire_softc *sc; + int unit = DEV2UNIT(dev); + int sub = DEV2DMACH(dev); + int s, i, len, err = 0; + struct fw_device *fwdev; + struct fw_bind *fwb; + struct fw_xferq *ir, *it; + struct fw_xfer *xfer; + struct fw_pkt *fp; + struct fw_devinfo *devinfo; + + struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; + struct fw_asyreq *asyreq = (struct fw_asyreq *)data; + struct fw_isochreq *ichreq = (struct fw_isochreq *)data; + struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; + struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; + struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; + + if (DEV_FWMEM(dev)) + return fwmem_ioctl(dev, cmd, data, flag, td); + + sc = devclass_get_softc(firewire_devclass, unit); + if (!data) + return(EINVAL); + + switch (cmd) { + case FW_STSTREAM: + sc->fc->it[sub]->flag &= ~0xff; + sc->fc->it[sub]->flag |= (0x3f & ichreq->ch); + sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6); + err = 0; + break; + case FW_GTSTREAM: + ichreq->ch = sc->fc->it[sub]->flag & 0x3f; + ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3; + err = 0; + break; + case FW_SRSTREAM: + sc->fc->ir[sub]->flag &= ~0xff; + sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch); + sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6); + err = sc->fc->irx_enable(sc->fc, sub); + break; + case FW_GRSTREAM: + ichreq->ch = sc->fc->ir[sub]->flag & 0x3f; + ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3; + err = 0; + break; + case FW_SSTBUF: + ir = sc->fc->ir[sub]; + it = sc->fc->it[sub]; + + if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){ + return(EBUSY); + } + if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){ + return(EBUSY); + } + if((ibufreq->rx.nchunk * + ibufreq->rx.psize * ibufreq->rx.npacket) + + (ibufreq->tx.nchunk * + ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){ + return(EINVAL); + } + ir->bulkxfer + = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, M_WAITOK); + if(ir->bulkxfer == NULL){ + return(ENOMEM); + } + it->bulkxfer + = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, M_WAITOK); + if(it->bulkxfer == NULL){ + return(ENOMEM); + } + if (ibufreq->rx.psize > 0) { + ibufreq->rx.psize = roundup2(ibufreq->rx.psize, + sizeof(u_int32_t)); + ir->buf = fwdma_malloc_multiseg( + sc->fc, sizeof(u_int32_t), + ibufreq->rx.psize, + ibufreq->rx.nchunk * ibufreq->rx.npacket, + BUS_DMA_WAITOK); + + if(ir->buf == NULL){ + free(ir->bulkxfer, M_FW); + free(it->bulkxfer, M_FW); + ir->bulkxfer = NULL; + it->bulkxfer = NULL; + it->buf = NULL; + return(ENOMEM); + } + } + if (ibufreq->tx.psize > 0) { + ibufreq->tx.psize = roundup2(ibufreq->tx.psize, + sizeof(u_int32_t)); + it->buf = fwdma_malloc_multiseg( + sc->fc, sizeof(u_int32_t), + ibufreq->tx.psize, + ibufreq->tx.nchunk * ibufreq->tx.npacket, + BUS_DMA_WAITOK); + + if(it->buf == NULL){ + free(ir->bulkxfer, M_FW); + free(it->bulkxfer, M_FW); + fwdma_free_multiseg(ir->buf); + ir->bulkxfer = NULL; + it->bulkxfer = NULL; + it->buf = NULL; + return(ENOMEM); + } + } + + ir->bnchunk = ibufreq->rx.nchunk; + ir->bnpacket = ibufreq->rx.npacket; + ir->psize = (ibufreq->rx.psize + 3) & ~3; + ir->queued = 0; + + it->bnchunk = ibufreq->tx.nchunk; + it->bnpacket = ibufreq->tx.npacket; + it->psize = (ibufreq->tx.psize + 3) & ~3; + it->queued = 0; + + STAILQ_INIT(&ir->stvalid); + STAILQ_INIT(&ir->stfree); + STAILQ_INIT(&ir->stdma); + ir->stproc = NULL; + + STAILQ_INIT(&it->stvalid); + STAILQ_INIT(&it->stfree); + STAILQ_INIT(&it->stdma); + it->stproc = NULL; + + for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){ + ir->bulkxfer[i].poffset = i * ir->bnpacket; + ir->bulkxfer[i].mbuf = NULL; + STAILQ_INSERT_TAIL(&ir->stfree, + &ir->bulkxfer[i], link); + } + for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){ + it->bulkxfer[i].poffset = i * it->bnpacket; + it->bulkxfer[i].mbuf = NULL; + STAILQ_INSERT_TAIL(&it->stfree, + &it->bulkxfer[i], link); + } + ir->flag &= ~FWXFERQ_MODEMASK; + ir->flag |= FWXFERQ_STREAM; + ir->flag |= FWXFERQ_EXTBUF; + + it->flag &= ~FWXFERQ_MODEMASK; + it->flag |= FWXFERQ_STREAM; + it->flag |= FWXFERQ_EXTBUF; + err = 0; + break; + case FW_GSTBUF: + ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk; + ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket; + ibufreq->rx.psize = sc->fc->ir[sub]->psize; + + ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk; + ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket; + ibufreq->tx.psize = sc->fc->it[sub]->psize; + break; + case FW_ASYREQ: + xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len, + PAGE_SIZE /* XXX */); + if(xfer == NULL){ + err = ENOMEM; + return err; + } + fp = &asyreq->pkt; + switch (asyreq->req.type) { + case FWASREQNODE: + xfer->dst = fp->mode.hdr.dst; + break; + case FWASREQEUI: + fwdev = fw_noderesolve_eui64(sc->fc, + &asyreq->req.dst.eui); + if (fwdev == NULL) { + device_printf(sc->fc->bdev, + "cannot find node\n"); + err = EINVAL; + goto error; + } + xfer->dst = FWLOCALBUS | fwdev->dst; + fp->mode.hdr.dst = xfer->dst; + break; + case FWASRESTL: + /* XXX what's this? */ + break; + case FWASREQSTREAM: + /* nothing to do */ + break; + } + xfer->spd = asyreq->req.sped; + bcopy(fp, xfer->send.buf, xfer->send.len); + xfer->act.hand = fw_asy_callback; + err = fw_asyreq(sc->fc, sub, xfer); + if(err){ + fw_xfer_free( xfer); + return err; + } + err = tsleep(xfer, FWPRI, "asyreq", hz); + if(err == 0){ + if(asyreq->req.len >= xfer->recv.len){ + asyreq->req.len = xfer->recv.len; + }else{ + err = EINVAL; + } + bcopy(xfer->recv.buf, fp, asyreq->req.len); + } +error: + fw_xfer_free( xfer); + break; + case FW_IBUSRST: + sc->fc->ibr(sc->fc); + break; + case FW_CBINDADDR: + fwb = fw_bindlookup(sc->fc, + bindreq->start.hi, bindreq->start.lo); + if(fwb == NULL){ + err = EINVAL; + break; + } + STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); + STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist); + free(fwb, M_FW); + break; + case FW_SBINDADDR: + if(bindreq->len <= 0 ){ + err = EINVAL; + break; + } + if(bindreq->start.hi > 0xffff ){ + err = EINVAL; + break; + } + fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT); + if(fwb == NULL){ + err = ENOMEM; + break; + } + fwb->start_hi = bindreq->start.hi; + fwb->start_lo = bindreq->start.lo; + fwb->addrlen = bindreq->len; + fwb->sub = sub; + fwb->act_type = FWACT_CH; + + xfer = fw_xfer_alloc(M_FWXFER); + if(xfer == NULL){ + err = ENOMEM; + return err; + } + xfer->fc = sc->fc; + + s = splfw(); + /* XXX broken. need multiple xfer */ + STAILQ_INIT(&fwb->xferlist); + STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); + splx(s); + err = fw_bindadd(sc->fc, fwb); + break; + case FW_GDEVLST: + i = len = 1; + /* myself */ + devinfo = &fwdevlst->dev[0]; + devinfo->dst = sc->fc->nodeid; + devinfo->status = 0; /* XXX */ + devinfo->eui.hi = sc->fc->eui.hi; + devinfo->eui.lo = sc->fc->eui.lo; + STAILQ_FOREACH(fwdev, &sc->fc->devices, link) { + if(len < FW_MAX_DEVLST){ + devinfo = &fwdevlst->dev[len++]; + devinfo->dst = fwdev->dst; + devinfo->status = + (fwdev->status == FWDEVINVAL)?0:1; + devinfo->eui.hi = fwdev->eui.hi; + devinfo->eui.lo = fwdev->eui.lo; + } + i++; + } + fwdevlst->n = i; + fwdevlst->info_len = len; + break; + case FW_GTPMAP: + bcopy(sc->fc->topology_map, data, + (sc->fc->topology_map->crc_len + 1) * 4); + break; + case FW_GCROM: + STAILQ_FOREACH(fwdev, &sc->fc->devices, link) + if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) + break; + if (fwdev == NULL) { + err = FWNODE_INVAL; + break; + } + if (fwdev->rommax < CSRROMOFF) + len = 0; + else + len = fwdev->rommax - CSRROMOFF + 4; + if (crom_buf->len < len) + len = crom_buf->len; + else + crom_buf->len = len; + err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len); + break; + default: + sc->fc->ioctl (dev, cmd, data, flag, td); + break; + } + return err; +} +int +fw_poll(dev_t dev, int events, fw_proc *td) +{ + int revents; + int tmp; + int unit = DEV2UNIT(dev); + int sub = DEV2DMACH(dev); + struct firewire_softc *sc; + + if (DEV_FWMEM(dev)) + return fwmem_poll(dev, events, td); + + sc = devclass_get_softc(firewire_devclass, unit); + revents = 0; + tmp = POLLIN | POLLRDNORM; + if (events & tmp) { + if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL) + revents |= tmp; + else + selrecord(td, &sc->fc->ir[sub]->rsel); + } + tmp = POLLOUT | POLLWRNORM; + if (events & tmp) { + /* XXX should be fixed */ + revents |= tmp; + } + + return revents; +} + +static int +#if __FreeBSD_version < 500102 +fw_mmap (dev_t dev, vm_offset_t offset, int nproto) +#else +fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) +#endif +{ + struct firewire_softc *fc; + int unit = DEV2UNIT(dev); + + if (DEV_FWMEM(dev)) +#if __FreeBSD_version < 500102 + return fwmem_mmap(dev, offset, nproto); +#else + return fwmem_mmap(dev, offset, paddr, nproto); +#endif + + fc = devclass_get_softc(firewire_devclass, unit); + + return EINVAL; +} diff --git a/sys/dev/firewire/fwdma.c b/sys/dev/firewire/fwdma.c new file mode 100644 index 0000000..14b4903 --- /dev/null +++ b/sys/dev/firewire/fwdma.c @@ -0,0 +1,208 @@ +/* + * 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <sys/bus.h> +#include <machine/bus.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/fwdma.h> + +static void +fwdma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *baddr; + + if (error) + printf("fwdma_map_cb: error=%d\n", error); + baddr = (bus_addr_t *)arg; + *baddr = segs->ds_addr; +} + +void * +fwdma_malloc(struct firewire_comm *fc, int alignment, bus_size_t size, + struct fwdma_alloc *dma, int flag) +{ + int err; + + dma->v_addr = NULL; + err = bus_dma_tag_create( + /*parent*/ fc->dmat, + /*alignment*/ alignment, + /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ size, + /*nsegments*/ 1, + /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, + /*flags*/ BUS_DMA_ALLOCNOW, &dma->dma_tag); + if (err) { + printf("fwdma_malloc: failed(1)\n"); + return(NULL); + } + + err = bus_dmamem_alloc(dma->dma_tag, &dma->v_addr, + flag, &dma->dma_map); + if (err) { + printf("fwdma_malloc: failed(2)\n"); + /* XXX destory tag */ + return(NULL); + } + + bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->v_addr, + size, fwdma_map_cb, &dma->bus_addr, /*flags*/0); + + return(dma->v_addr); +} + +void +fwdma_free(struct firewire_comm *fc, struct fwdma_alloc *dma) +{ + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + bus_dmamem_free(dma->dma_tag, dma->v_addr, dma->dma_map); + bus_dma_tag_destroy(dma->dma_tag); +} + + +void * +fwdma_malloc_size(bus_dma_tag_t dmat, bus_dmamap_t *dmamap, + bus_size_t size, bus_addr_t *bus_addr, int flag) +{ + void *v_addr; + + if (bus_dmamem_alloc(dmat, &v_addr, flag, dmamap)) { + printf("fwdma_malloc_size: failed(1)\n"); + return(NULL); + } + bus_dmamap_load(dmat, *dmamap, v_addr, size, + fwdma_map_cb, bus_addr, /*flags*/0); + return(v_addr); +} + +void +fwdma_free_size(bus_dma_tag_t dmat, bus_dmamap_t dmamap, + void *vaddr, bus_size_t size) +{ + bus_dmamap_unload(dmat, dmamap); + bus_dmamem_free(dmat, vaddr, dmamap); +} + +/* + * Allocate multisegment dma buffers + * each segment size is eqaul to ssize except last segment. + */ +struct fwdma_alloc_multi * +fwdma_malloc_multiseg(struct firewire_comm *fc, int alignment, + int esize, int n, int flag) +{ + struct fwdma_alloc_multi *am; + struct fwdma_seg *seg; + bus_size_t ssize; + int nseg; + + if (esize > PAGE_SIZE) { + /* round up to PAGE_SIZE */ + esize = ssize = roundup2(esize, PAGE_SIZE); + nseg = n; + } else { + /* allocate PAGE_SIZE segment for small elements */ + ssize = rounddown(PAGE_SIZE, esize); + nseg = howmany(n, ssize / esize); + } + am = (struct fwdma_alloc_multi *)malloc(sizeof(struct fwdma_alloc_multi) + + sizeof(struct fwdma_seg)*nseg, M_FW, M_WAITOK); + if (am == NULL) { + printf("fwdma_malloc_multiseg: malloc failed\n"); + return(NULL); + } + am->ssize = ssize; + am->esize = esize; + am->nseg = 0; + if (bus_dma_tag_create( + /*parent*/ fc->dmat, + /*alignment*/ alignment, + /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ ssize, + /*nsegments*/ 1, + /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, + /*flags*/ BUS_DMA_ALLOCNOW, &am->dma_tag)) { + printf("fwdma_malloc_multiseg: tag_create failed\n"); + free(am, M_FW); + return(NULL); + } + +#if 0 +#if __FreeBSD_version < 500000 + printf("malloc_multi: ssize=%d nseg=%d\n", ssize, nseg); +#else + printf("malloc_multi: ssize=%td nseg=%d\n", ssize, nseg); +#endif +#endif + for (seg = &am->seg[0]; nseg --; seg ++) { + seg->v_addr = fwdma_malloc_size(am->dma_tag, &seg->dma_map, + ssize, &seg->bus_addr, flag); + if (seg->v_addr == NULL) { + printf("fwdma_malloc_multi: malloc_size failed %d\n", + am->nseg); + fwdma_free_multiseg(am); + return(NULL); + } + am->nseg++; + } + return(am); +} + +void +fwdma_free_multiseg(struct fwdma_alloc_multi *am) +{ + struct fwdma_seg *seg; + + for (seg = &am->seg[0]; am->nseg --; seg ++) { + fwdma_free_size(am->dma_tag, seg->dma_map, + seg->v_addr, am->ssize); + } + bus_dma_tag_destroy(am->dma_tag); + free(am, M_FW); +} diff --git a/sys/dev/firewire/fwdma.h b/sys/dev/firewire/fwdma.h new file mode 100644 index 0000000..6bf66c7 --- /dev/null +++ b/sys/dev/firewire/fwdma.h @@ -0,0 +1,112 @@ +/* + * 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$ + */ + +struct fwdma_alloc { + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + void * v_addr; + bus_addr_t bus_addr; +}; + +struct fwdma_seg { + bus_dmamap_t dma_map; + void * v_addr; + bus_addr_t bus_addr; +}; + +struct fwdma_alloc_multi { + bus_size_t ssize; + bus_size_t esize; + int nseg; + bus_dma_tag_t dma_tag; + struct fwdma_seg seg[0]; +}; + +static __inline void * +fwdma_v_addr(struct fwdma_alloc_multi *am, int index) +{ + bus_size_t ssize = am->ssize; + int offset = am->esize * index; + + return ((caddr_t)am->seg[offset / ssize].v_addr + (offset % ssize)); +} + +static __inline bus_addr_t +fwdma_bus_addr(struct fwdma_alloc_multi *am, int index) +{ + bus_size_t ssize = am->ssize; + int offset = am->esize * index; + + return (am->seg[offset / ssize].bus_addr + (offset % ssize)); +} + +static __inline void +fwdma_sync(struct fwdma_alloc *dma, bus_dmasync_op_t op) +{ + bus_dmamap_sync(dma->dma_tag, dma->dma_map, op); +} + +static __inline void +fwdma_sync_multiseg(struct fwdma_alloc_multi *am, + int start, int end, bus_dmasync_op_t op) +{ + struct fwdma_seg *seg, *eseg; + + seg = &am->seg[am->esize * start / am->ssize]; + eseg = &am->seg[am->esize * end / am->ssize]; + for (; seg <= eseg; seg ++) + bus_dmamap_sync(am->dma_tag, seg->dma_map, op); +} + +static __inline void +fwdma_sync_multiseg_all(struct fwdma_alloc_multi *am, bus_dmasync_op_t op) +{ + struct fwdma_seg *seg; + int i; + + seg = &am->seg[0]; + for (i = 0; i < am->nseg; i++, seg++) + bus_dmamap_sync(am->dma_tag, seg->dma_map, op); +} + +void *fwdma_malloc(struct firewire_comm *, int, bus_size_t, struct fwdma_alloc *, int); +void fwdma_free(struct firewire_comm *, struct fwdma_alloc *); +void *fwdma_malloc_size(bus_dma_tag_t, bus_dmamap_t *, bus_size_t, bus_addr_t *, int); +void fwdma_free_size(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t); +struct fwdma_alloc_multi *fwdma_malloc_multiseg(struct firewire_comm *, + int, int, int, int); +void fwdma_free_multiseg(struct fwdma_alloc_multi *); + diff --git a/sys/dev/firewire/fwmem.c b/sys/dev/firewire/fwmem.c new file mode 100644 index 0000000..7a0fda7 --- /dev/null +++ b/sys/dev/firewire/fwmem.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2002-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 <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> + +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/conf.h> +#include <sys/sysctl.h> + +#include <sys/bus.h> +#include <machine/bus.h> + +#include <sys/signal.h> +#include <sys/mman.h> +#include <sys/ioccom.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/fwmem.h> + +static int fwmem_speed=2, fwmem_debug=0; +static struct fw_eui64 fwmem_eui64; +SYSCTL_DECL(_hw_firewire); +SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0, + "FireWire Memory Access"); +SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW, + &fwmem_eui64.hi, 0, "Fwmem target EUI64 high"); +SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW, + &fwmem_eui64.lo, 0, "Fwmem target EUI64 low"); +SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0, + "Fwmem link speed"); +SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0, + "Fwmem driver debug flag"); + +static struct fw_xfer * +fwmem_xfer_req( + struct fw_device *fwdev, + caddr_t sc, + int spd, + int slen, + int rlen, + void *hand) +{ + struct fw_xfer *xfer; + + xfer = fw_xfer_alloc_buf(M_FWXFER, slen, rlen); + if (xfer == NULL) + return NULL; + + xfer->fc = fwdev->fc; + xfer->dst = FWLOCALBUS | fwdev->dst; + if (spd < 0) + xfer->spd = fwdev->speed; + else + xfer->spd = min(spd, fwdev->speed); + xfer->act.hand = hand; + xfer->retry_req = fw_asybusy; + xfer->sc = sc; + + return xfer; +} + +struct fw_xfer * +fwmem_read_quad( + struct fw_device *fwdev, + caddr_t sc, + u_int8_t spd, + u_int16_t dst_hi, + u_int32_t dst_lo, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, 12, 16, hand); + if (xfer == NULL) + return NULL; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.rreqq.tcode = FWTCODE_RREQQ; + fp->mode.rreqq.dst = xfer->dst; + fp->mode.rreqq.dest_hi = dst_hi; + fp->mode.rreqq.dest_lo = dst_lo; + + if (fwmem_debug) + printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, + dst_hi, dst_lo); + + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + +struct fw_xfer * +fwmem_write_quad( + struct fw_device *fwdev, + caddr_t sc, + u_int8_t spd, + u_int16_t dst_hi, + u_int32_t dst_lo, + u_int32_t data, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, 16, 12, hand); + if (xfer == NULL) + return NULL; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqq.tcode = FWTCODE_WREQQ; + fp->mode.wreqq.dst = xfer->dst; + fp->mode.wreqq.dest_hi = dst_hi; + fp->mode.wreqq.dest_lo = dst_lo; + + fp->mode.wreqq.data = data; + + if (fwmem_debug) + printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, + dst_hi, dst_lo, data); + + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + +struct fw_xfer * +fwmem_read_block( + struct fw_device *fwdev, + caddr_t sc, + u_int8_t spd, + u_int16_t dst_hi, + u_int32_t dst_lo, + int len, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, 16, roundup2(16+len,4), hand); + if (xfer == NULL) + return NULL; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.rreqb.tcode = FWTCODE_RREQB; + fp->mode.rreqb.dst = xfer->dst; + fp->mode.rreqb.dest_hi = dst_hi; + fp->mode.rreqb.dest_lo = dst_lo; + fp->mode.rreqb.len = len; + + if (fwmem_debug) + printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, + dst_hi, dst_lo, len); + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + +struct fw_xfer * +fwmem_write_block( + struct fw_device *fwdev, + caddr_t sc, + u_int8_t spd, + u_int16_t dst_hi, + u_int32_t dst_lo, + int len, + char *data, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, roundup(16+len, 4), 12, hand); + if (xfer == NULL) + return NULL; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqb.tcode = FWTCODE_WREQB; + fp->mode.wreqb.dst = xfer->dst; + fp->mode.wreqb.dest_hi = dst_hi; + fp->mode.wreqb.dest_lo = dst_lo; + fp->mode.wreqb.len = len; + bcopy(data, &fp->mode.wreqb.payload[0], len); + + if (fwmem_debug) + printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, + dst_hi, dst_lo, len); + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + + +int +fwmem_open (dev_t dev, int flags, int fmt, fw_proc *td) +{ + struct fw_eui64 *eui; + + if (dev->si_drv1 != NULL) + return (EBUSY); + + eui = (struct fw_eui64 *)malloc(sizeof(struct fw_eui64), + M_FW, M_WAITOK); + if (eui == NULL) + return ENOMEM; + bcopy(&fwmem_eui64, eui, sizeof(struct fw_eui64)); + dev->si_drv1 = (void *)eui; + + return (0); +} + +int +fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td) +{ + free(dev->si_drv1, M_FW); + dev->si_drv1 = NULL; + + return (0); +} + +#define MAXLEN 2048 +#define USE_QUAD 0 +int +fwmem_read (dev_t dev, struct uio *uio, int ioflag) +{ + struct firewire_softc *sc; + struct fw_device *fwdev; + struct fw_xfer *xfer; + int err = 0; + int unit = DEV2UNIT(dev); + u_int16_t dst_hi; + u_int32_t dst_lo; + off_t offset; + int len; + + sc = devclass_get_softc(firewire_devclass, unit); + fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1); + if (fwdev == NULL) { + if (fwmem_debug) + printf("fwmem: no such device ID:%08x%08x\n", + fwmem_eui64.hi, fwmem_eui64.lo); + return EINVAL; + } + + while(uio->uio_resid > 0 && !err) { + offset = uio->uio_offset; + dst_hi = (offset >> 32) & 0xffff; + dst_lo = offset & 0xffffffff; + len = uio->uio_resid; + if (len == 4 && (dst_lo & 3) == 0) { + xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed, + dst_hi, dst_lo, fw_asy_callback); + if (xfer == NULL) { + err = EINVAL; + break; + } + err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", 0); + if (xfer->recv.buf == NULL) + err = EIO; + else if (xfer->resp != 0) + err = xfer->resp; + else if (err == 0) + err = uiomove(xfer->recv.buf + 4*3, 4, uio); + } else { + if (len > MAXLEN) + len = MAXLEN; + xfer = fwmem_read_block(fwdev, NULL, fwmem_speed, + dst_hi, dst_lo, len, fw_asy_callback); + if (xfer == NULL) { + err = EINVAL; + break; + } + err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", 0); + if (xfer->recv.buf == NULL) + err = EIO; + else if (xfer->resp != 0) + err = xfer->resp; + else if (err == 0) + err = uiomove(xfer->recv.buf + 4*4, len, uio); + } + fw_xfer_free(xfer); + } + return err; +} +int +fwmem_write (dev_t dev, struct uio *uio, int ioflag) +{ + struct firewire_softc *sc; + struct fw_device *fwdev; + struct fw_xfer *xfer; + int err = 0; + int unit = DEV2UNIT(dev); + u_int16_t dst_hi; + u_int32_t dst_lo, quad; + char *data; + off_t offset; + int len; + + sc = devclass_get_softc(firewire_devclass, unit); + fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1); + if (fwdev == NULL) { + if (fwmem_debug) + printf("fwmem: no such device ID:%08x%08x\n", + fwmem_eui64.hi, fwmem_eui64.lo); + return EINVAL; + } + + data = malloc(MAXLEN, M_FW, M_WAITOK); + if (data == NULL) + return ENOMEM; + + while(uio->uio_resid > 0 && !err) { + offset = uio->uio_offset; + dst_hi = (offset >> 32) & 0xffff; + dst_lo = offset & 0xffffffff; + len = uio->uio_resid; + if (len == 4 && (dst_lo & 3) == 0) { + err = uiomove((char *)&quad, sizeof(quad), uio); + xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed, + dst_hi, dst_lo, quad, fw_asy_callback); + if (xfer == NULL) { + err = EINVAL; + break; + } + err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", 0); + if (xfer->resp != 0) + err = xfer->resp; + } else { + if (len > MAXLEN) + len = MAXLEN; + err = uiomove(data, len, uio); + if (err) + break; + xfer = fwmem_write_block(fwdev, NULL, fwmem_speed, + dst_hi, dst_lo, len, data, fw_asy_callback); + if (xfer == NULL) { + err = EINVAL; + break; + } + err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", 0); + if (xfer->resp != 0) + err = xfer->resp; + } + fw_xfer_free(xfer); + } + free(data, M_FW); + return err; +} + +int +fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) +{ + int err = 0; + switch (cmd) { + case FW_SDEUI64: + bcopy(data, dev->si_drv1, sizeof(struct fw_eui64)); + break; + case FW_GDEUI64: + bcopy(dev->si_drv1, data, sizeof(struct fw_eui64)); + break; + default: + err = EINVAL; + } + return(err); +} +int +fwmem_poll (dev_t dev, int events, fw_proc *td) +{ + return EINVAL; +} +int +#if __FreeBSD_version < 500102 +fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto) +#else +fwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) +#endif +{ + return EINVAL; +} diff --git a/sys/dev/firewire/fwmem.h b/sys/dev/firewire/fwmem.h new file mode 100644 index 0000000..0567f81 --- /dev/null +++ b/sys/dev/firewire/fwmem.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2002-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$ + */ + +struct fw_xfer *fwmem_read_quad(struct fw_device *, caddr_t, u_int8_t, + u_int16_t, u_int32_t, void (*)(struct fw_xfer *)); +struct fw_xfer *fwmem_write_quad(struct fw_device *, caddr_t, u_int8_t, + u_int16_t, u_int32_t, u_int32_t, void (*)(struct fw_xfer *)); +struct fw_xfer *fwmem_read_block(struct fw_device *, caddr_t, u_int8_t, + u_int16_t, u_int32_t, int, void (*)(struct fw_xfer *)); +struct fw_xfer *fwmem_write_block(struct fw_device *, caddr_t, u_int8_t, + u_int16_t, u_int32_t, int, char *, void (*)(struct fw_xfer *)); + +d_open_t fwmem_open; +d_close_t fwmem_close; +d_ioctl_t fwmem_ioctl; +d_read_t fwmem_read; +d_write_t fwmem_write; +d_poll_t fwmem_poll; +d_mmap_t fwmem_mmap; diff --git a/sys/dev/firewire/fwohci.c b/sys/dev/firewire/fwohci.c new file mode 100644 index 0000000..d7ca246 --- /dev/null +++ b/sys/dev/firewire/fwohci.c @@ -0,0 +1,2853 @@ +/* + * 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 ATRQ_CH 0 +#define ATRS_CH 1 +#define ARRQ_CH 2 +#define ARRS_CH 3 +#define ITX_CH 4 +#define IRX_CH 0x24 + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/mbuf.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/signalvar.h> +#include <sys/malloc.h> +#include <sys/sockio.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/conf.h> +#include <sys/endian.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <machine/cpufunc.h> /* for rdtsc proto for clock.h below */ +#include <machine/clock.h> +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/fwdma.h> +#include <dev/firewire/fwohcireg.h> +#include <dev/firewire/fwohcivar.h> +#include <dev/firewire/firewire_phy.h> + +#include <dev/firewire/iec68113.h> + +#undef OHCI_DEBUG + +static char dbcode[16][0x10]={"OUTM", "OUTL","INPM","INPL", + "STOR","LOAD","NOP ","STOP",}; + +static char dbkey[8][0x10]={"ST0", "ST1","ST2","ST3", + "UNDEF","REG","SYS","DEV"}; +static char dbcond[4][0x10]={"NEV","C=1", "C=0", "ALL"}; +char fwohcicode[32][0x20]={ + "No stat","Undef","long","miss Ack err", + "underrun","overrun","desc err", "data read err", + "data write err","bus reset","timeout","tcode err", + "Undef","Undef","unknown event","flushed", + "Undef","ack complete","ack pend","Undef", + "ack busy_X","ack busy_A","ack busy_B","Undef", + "Undef","Undef","Undef","ack tardy", + "Undef","ack data_err","ack type_err",""}; + +#define MAX_SPEED 3 +extern char linkspeed[][0x10]; +u_int32_t tagbit[4] = { 1 << 28, 1 << 29, 1 << 30, 1 << 31}; + +static struct tcode_info tinfo[] = { +/* hdr_len block flag*/ +/* 0 WREQQ */ {16, FWTI_REQ | FWTI_TLABEL}, +/* 1 WREQB */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY}, +/* 2 WRES */ {12, FWTI_RES}, +/* 3 XXX */ { 0, 0}, +/* 4 RREQQ */ {12, FWTI_REQ | FWTI_TLABEL}, +/* 5 RREQB */ {16, FWTI_REQ | FWTI_TLABEL}, +/* 6 RRESQ */ {16, FWTI_RES}, +/* 7 RRESB */ {16, FWTI_RES | FWTI_BLOCK_ASY}, +/* 8 CYCS */ { 0, 0}, +/* 9 LREQ */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY}, +/* a STREAM */ { 4, FWTI_REQ | FWTI_BLOCK_STR}, +/* b LRES */ {16, FWTI_RES | FWTI_BLOCK_ASY}, +/* c XXX */ { 0, 0}, +/* d XXX */ { 0, 0}, +/* e PHY */ {12, FWTI_REQ}, +/* f XXX */ { 0, 0} +}; + +#define OHCI_WRITE_SIGMASK 0xffff0000 +#define OHCI_READ_SIGMASK 0xffff0000 + +#define OWRITE(sc, r, x) bus_space_write_4((sc)->bst, (sc)->bsh, (r), (x)) +#define OREAD(sc, r) bus_space_read_4((sc)->bst, (sc)->bsh, (r)) + +static void fwohci_ibr __P((struct firewire_comm *)); +static void fwohci_db_init __P((struct fwohci_softc *, struct fwohci_dbch *)); +static void fwohci_db_free __P((struct fwohci_dbch *)); +static void fwohci_arcv __P((struct fwohci_softc *, struct fwohci_dbch *, int)); +static void fwohci_txd __P((struct fwohci_softc *, struct fwohci_dbch *)); +static void fwohci_start_atq __P((struct firewire_comm *)); +static void fwohci_start_ats __P((struct firewire_comm *)); +static void fwohci_start __P((struct fwohci_softc *, struct fwohci_dbch *)); +static u_int32_t fwphy_wrdata __P(( struct fwohci_softc *, u_int32_t, u_int32_t)); +static u_int32_t fwphy_rddata __P(( struct fwohci_softc *, u_int32_t)); +static int fwohci_rx_enable __P((struct fwohci_softc *, struct fwohci_dbch *)); +static int fwohci_tx_enable __P((struct fwohci_softc *, struct fwohci_dbch *)); +static int fwohci_irx_enable __P((struct firewire_comm *, int)); +static int fwohci_irx_disable __P((struct firewire_comm *, int)); +#if BYTE_ORDER == BIG_ENDIAN +static void fwohci_irx_post __P((struct firewire_comm *, u_int32_t *)); +#endif +static int fwohci_itxbuf_enable __P((struct firewire_comm *, int)); +static int fwohci_itx_disable __P((struct firewire_comm *, int)); +static void fwohci_timeout __P((void *)); +static void fwohci_poll __P((struct firewire_comm *, int, int)); +static void fwohci_set_intr __P((struct firewire_comm *, int)); + +static int fwohci_add_rx_buf __P((struct fwohci_dbch *, struct fwohcidb_tr *, int, struct fwdma_alloc *)); +static int fwohci_add_tx_buf __P((struct fwohci_dbch *, struct fwohcidb_tr *, int)); +static void dump_db __P((struct fwohci_softc *, u_int32_t)); +static void print_db __P((struct fwohcidb_tr *, volatile struct fwohcidb *, u_int32_t , u_int32_t)); +static void dump_dma __P((struct fwohci_softc *, u_int32_t)); +static u_int32_t fwohci_cyctimer __P((struct firewire_comm *)); +static void fwohci_rbuf_update __P((struct fwohci_softc *, int)); +static void fwohci_tbuf_update __P((struct fwohci_softc *, int)); +void fwohci_txbufdb __P((struct fwohci_softc *, int , struct fw_bulkxfer *)); +#if FWOHCI_TASKQUEUE +static void fwohci_complete(void *, int); +#endif + +/* + * memory allocated for DMA programs + */ +#define DMA_PROG_ALLOC (8 * PAGE_SIZE) + +/* #define NDB 1024 */ +#define NDB FWMAXQUEUE +#define NDVDB (DVBUF * NDB) + +#define OHCI_VERSION 0x00 +#define OHCI_ATRETRY 0x08 +#define OHCI_CROMHDR 0x18 +#define OHCI_BUS_OPT 0x20 +#define OHCI_BUSIRMC (1 << 31) +#define OHCI_BUSCMC (1 << 30) +#define OHCI_BUSISC (1 << 29) +#define OHCI_BUSBMC (1 << 28) +#define OHCI_BUSPMC (1 << 27) +#define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\ + OHCI_BUSBMC | OHCI_BUSPMC + +#define OHCI_EUID_HI 0x24 +#define OHCI_EUID_LO 0x28 + +#define OHCI_CROMPTR 0x34 +#define OHCI_HCCCTL 0x50 +#define OHCI_HCCCTLCLR 0x54 +#define OHCI_AREQHI 0x100 +#define OHCI_AREQHICLR 0x104 +#define OHCI_AREQLO 0x108 +#define OHCI_AREQLOCLR 0x10c +#define OHCI_PREQHI 0x110 +#define OHCI_PREQHICLR 0x114 +#define OHCI_PREQLO 0x118 +#define OHCI_PREQLOCLR 0x11c +#define OHCI_PREQUPPER 0x120 + +#define OHCI_SID_BUF 0x64 +#define OHCI_SID_CNT 0x68 +#define OHCI_SID_ERR (1 << 31) +#define OHCI_SID_CNT_MASK 0xffc + +#define OHCI_IT_STAT 0x90 +#define OHCI_IT_STATCLR 0x94 +#define OHCI_IT_MASK 0x98 +#define OHCI_IT_MASKCLR 0x9c + +#define OHCI_IR_STAT 0xa0 +#define OHCI_IR_STATCLR 0xa4 +#define OHCI_IR_MASK 0xa8 +#define OHCI_IR_MASKCLR 0xac + +#define OHCI_LNKCTL 0xe0 +#define OHCI_LNKCTLCLR 0xe4 + +#define OHCI_PHYACCESS 0xec +#define OHCI_CYCLETIMER 0xf0 + +#define OHCI_DMACTL(off) (off) +#define OHCI_DMACTLCLR(off) (off + 4) +#define OHCI_DMACMD(off) (off + 0xc) +#define OHCI_DMAMATCH(off) (off + 0x10) + +#define OHCI_ATQOFF 0x180 +#define OHCI_ATQCTL OHCI_ATQOFF +#define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4) +#define OHCI_ATQCMD (OHCI_ATQOFF + 0xc) +#define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10) + +#define OHCI_ATSOFF 0x1a0 +#define OHCI_ATSCTL OHCI_ATSOFF +#define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4) +#define OHCI_ATSCMD (OHCI_ATSOFF + 0xc) +#define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10) + +#define OHCI_ARQOFF 0x1c0 +#define OHCI_ARQCTL OHCI_ARQOFF +#define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4) +#define OHCI_ARQCMD (OHCI_ARQOFF + 0xc) +#define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10) + +#define OHCI_ARSOFF 0x1e0 +#define OHCI_ARSCTL OHCI_ARSOFF +#define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4) +#define OHCI_ARSCMD (OHCI_ARSOFF + 0xc) +#define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10) + +#define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH)) +#define OHCI_ITCTL(CH) (OHCI_ITOFF(CH)) +#define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4) +#define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc) + +#define OHCI_IROFF(CH) (0x400 + 0x20 * (CH)) +#define OHCI_IRCTL(CH) (OHCI_IROFF(CH)) +#define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4) +#define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc) +#define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10) + +d_ioctl_t fwohci_ioctl; + +/* + * Communication with PHY device + */ +static u_int32_t +fwphy_wrdata( struct fwohci_softc *sc, u_int32_t addr, u_int32_t data) +{ + u_int32_t fun; + + addr &= 0xf; + data &= 0xff; + + fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA)); + OWRITE(sc, OHCI_PHYACCESS, fun); + DELAY(100); + + return(fwphy_rddata( sc, addr)); +} + +static u_int32_t +fwohci_set_bus_manager(struct firewire_comm *fc, u_int node) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int i; + u_int32_t bm; + +#define OHCI_CSR_DATA 0x0c +#define OHCI_CSR_COMP 0x10 +#define OHCI_CSR_CONT 0x14 +#define OHCI_BUS_MANAGER_ID 0 + + OWRITE(sc, OHCI_CSR_DATA, node); + OWRITE(sc, OHCI_CSR_COMP, 0x3f); + OWRITE(sc, OHCI_CSR_CONT, OHCI_BUS_MANAGER_ID); + for (i = 0; !(OREAD(sc, OHCI_CSR_CONT) & (1<<31)) && (i < 1000); i++) + DELAY(10); + bm = OREAD(sc, OHCI_CSR_DATA); + if((bm & 0x3f) == 0x3f) + bm = node; + if (bootverbose) + device_printf(sc->fc.dev, + "fw_set_bus_manager: %d->%d (loop=%d)\n", bm, node, i); + + return(bm); +} + +static u_int32_t +fwphy_rddata(struct fwohci_softc *sc, u_int addr) +{ + u_int32_t fun, stat; + u_int i, retry = 0; + + addr &= 0xf; +#define MAX_RETRY 100 +again: + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL); + fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR); + OWRITE(sc, OHCI_PHYACCESS, fun); + for ( i = 0 ; i < MAX_RETRY ; i ++ ){ + fun = OREAD(sc, OHCI_PHYACCESS); + if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0) + break; + DELAY(100); + } + if(i >= MAX_RETRY) { + if (bootverbose) + device_printf(sc->fc.dev, "phy read failed(1).\n"); + if (++retry < MAX_RETRY) { + DELAY(100); + goto again; + } + } + /* Make sure that SCLK is started */ + stat = OREAD(sc, FWOHCI_INTSTAT); + if ((stat & OHCI_INT_REG_FAIL) != 0 || + ((fun >> PHYDEV_REGADDR) & 0xf) != addr) { + if (bootverbose) + device_printf(sc->fc.dev, "phy read failed(2).\n"); + if (++retry < MAX_RETRY) { + DELAY(100); + goto again; + } + } + if (bootverbose || retry >= MAX_RETRY) + device_printf(sc->fc.dev, + "fwphy_rddata: loop=%d, retry=%d\n", i, retry); +#undef MAX_RETRY + return((fun >> PHYDEV_RDDATA )& 0xff); +} +/* Device specific ioctl. */ +int +fwohci_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) +{ + struct firewire_softc *sc; + struct fwohci_softc *fc; + int unit = DEV2UNIT(dev); + int err = 0; + struct fw_reg_req_t *reg = (struct fw_reg_req_t *) data; + u_int32_t *dmach = (u_int32_t *) data; + + sc = devclass_get_softc(firewire_devclass, unit); + if(sc == NULL){ + return(EINVAL); + } + fc = (struct fwohci_softc *)sc->fc; + + if (!data) + return(EINVAL); + + switch (cmd) { + case FWOHCI_WRREG: +#define OHCI_MAX_REG 0x800 + if(reg->addr <= OHCI_MAX_REG){ + OWRITE(fc, reg->addr, reg->data); + reg->data = OREAD(fc, reg->addr); + }else{ + err = EINVAL; + } + break; + case FWOHCI_RDREG: + if(reg->addr <= OHCI_MAX_REG){ + reg->data = OREAD(fc, reg->addr); + }else{ + err = EINVAL; + } + break; +/* Read DMA descriptors for debug */ + case DUMPDMA: + if(*dmach <= OHCI_MAX_DMA_CH ){ + dump_dma(fc, *dmach); + dump_db(fc, *dmach); + }else{ + err = EINVAL; + } + break; + default: + break; + } + return err; +} + +static int +fwohci_probe_phy(struct fwohci_softc *sc, device_t dev) +{ + u_int32_t reg, reg2; + int e1394a = 1; +/* + * probe PHY parameters + * 0. to prove PHY version, whether compliance of 1394a. + * 1. to probe maximum speed supported by the PHY and + * number of port supported by core-logic. + * It is not actually available port on your PC . + */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS); + reg = fwphy_rddata(sc, FW_PHY_SPD_REG); + + if((reg >> 5) != 7 ){ + sc->fc.mode &= ~FWPHYASYST; + sc->fc.nport = reg & FW_PHY_NP; + sc->fc.speed = reg & FW_PHY_SPD >> 6; + if (sc->fc.speed > MAX_SPEED) { + device_printf(dev, "invalid speed %d (fixed to %d).\n", + sc->fc.speed, MAX_SPEED); + sc->fc.speed = MAX_SPEED; + } + device_printf(dev, + "Phy 1394 only %s, %d ports.\n", + linkspeed[sc->fc.speed], sc->fc.nport); + }else{ + reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG); + sc->fc.mode |= FWPHYASYST; + sc->fc.nport = reg & FW_PHY_NP; + sc->fc.speed = (reg2 & FW_PHY_ESPD) >> 5; + if (sc->fc.speed > MAX_SPEED) { + device_printf(dev, "invalid speed %d (fixed to %d).\n", + sc->fc.speed, MAX_SPEED); + sc->fc.speed = MAX_SPEED; + } + device_printf(dev, + "Phy 1394a available %s, %d ports.\n", + linkspeed[sc->fc.speed], sc->fc.nport); + + /* check programPhyEnable */ + reg2 = fwphy_rddata(sc, 5); +#if 0 + if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) { +#else /* XXX force to enable 1394a */ + if (e1394a) { +#endif + if (bootverbose) + device_printf(dev, + "Enable 1394a Enhancements\n"); + /* enable EAA EMC */ + reg2 |= 0x03; + /* set aPhyEnhanceEnable */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY); + } else { + /* for safe */ + reg2 &= ~0x83; + } + reg2 = fwphy_wrdata(sc, 5, reg2); + } + + reg = fwphy_rddata(sc, FW_PHY_SPD_REG); + if((reg >> 5) == 7 ){ + reg = fwphy_rddata(sc, 4); + reg |= 1 << 6; + fwphy_wrdata(sc, 4, reg); + reg = fwphy_rddata(sc, 4); + } + return 0; +} + + +void +fwohci_reset(struct fwohci_softc *sc, device_t dev) +{ + int i, max_rec, speed; + u_int32_t reg, reg2; + struct fwohcidb_tr *db_tr; + + /* Disable interrupt */ + OWRITE(sc, FWOHCI_INTMASKCLR, ~0); + + /* Now stopping all DMA channel */ + OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); + + OWRITE(sc, OHCI_IR_MASKCLR, ~0); + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN); + } + + /* FLUSH FIFO and reset Transmitter/Reciever */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); + if (bootverbose) + device_printf(dev, "resetting OHCI..."); + i = 0; + while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) { + if (i++ > 100) break; + DELAY(1000); + } + if (bootverbose) + printf("done (loop=%d)\n", i); + + /* Probe phy */ + fwohci_probe_phy(sc, dev); + + /* Probe link */ + reg = OREAD(sc, OHCI_BUS_OPT); + reg2 = reg | OHCI_BUSFNC; + max_rec = (reg & 0x0000f000) >> 12; + speed = (reg & 0x00000007); + device_printf(dev, "Link %s, max_rec %d bytes.\n", + linkspeed[speed], MAXREC(max_rec)); + /* XXX fix max_rec */ + sc->fc.maxrec = sc->fc.speed + 8; + if (max_rec != sc->fc.maxrec) { + reg2 = (reg2 & 0xffff0fff) | (sc->fc.maxrec << 12); + device_printf(dev, "max_rec %d -> %d\n", + MAXREC(max_rec), MAXREC(sc->fc.maxrec)); + } + if (bootverbose) + device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2); + OWRITE(sc, OHCI_BUS_OPT, reg2); + + /* Initialize registers */ + OWRITE(sc, OHCI_CROMHDR, sc->fc.config_rom[0]); + OWRITE(sc, OHCI_CROMPTR, sc->crom_dma.bus_addr); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND); + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR); + OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr); + OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID); + fw_busreset(&sc->fc); + + /* Enable link */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN); + + /* Force to start async RX DMA */ + sc->arrq.xferq.flag &= ~FWXFERQ_RUNNING; + sc->arrs.xferq.flag &= ~FWXFERQ_RUNNING; + fwohci_rx_enable(sc, &sc->arrq); + fwohci_rx_enable(sc, &sc->arrs); + + /* Initialize async TX */ + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD); + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD); + /* AT Retries */ + OWRITE(sc, FWOHCI_RETRY, + /* CycleLimit PhyRespRetries ATRespRetries ATReqRetries */ + (0xffff << 16 ) | (0x0f << 8) | (0x0f << 4) | 0x0f) ; + for( i = 0, db_tr = sc->atrq.top; i < sc->atrq.ndb ; + i ++, db_tr = STAILQ_NEXT(db_tr, link)){ + db_tr->xfer = NULL; + } + for( i = 0, db_tr = sc->atrs.top; i < sc->atrs.ndb ; + i ++, db_tr = STAILQ_NEXT(db_tr, link)){ + db_tr->xfer = NULL; + } + + + /* Enable interrupt */ + OWRITE(sc, FWOHCI_INTMASK, + OHCI_INT_ERR | OHCI_INT_PHY_SID + | OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS + | OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS + | OHCI_INT_PHY_BUS_R | OHCI_INT_PW_ERR); + fwohci_set_intr(&sc->fc, 1); + +} + +int +fwohci_init(struct fwohci_softc *sc, device_t dev) +{ + int i; + u_int32_t reg; + u_int8_t ui[8]; + +#if FWOHCI_TASKQUEUE + TASK_INIT(&sc->fwohci_task_complete, 0, fwohci_complete, sc); +#endif + + reg = OREAD(sc, OHCI_VERSION); + device_printf(dev, "OHCI version %x.%x (ROM=%d)\n", + (reg>>16) & 0xff, reg & 0xff, (reg>>24) & 1); + +/* Available Isochrounous DMA channel probe */ + OWRITE(sc, OHCI_IT_MASK, 0xffffffff); + OWRITE(sc, OHCI_IR_MASK, 0xffffffff); + reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK); + OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff); + OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff); + for (i = 0; i < 0x20; i++) + if ((reg & (1 << i)) == 0) + break; + sc->fc.nisodma = i; + device_printf(dev, "No. of Isochronous channel is %d.\n", i); + + sc->fc.arq = &sc->arrq.xferq; + sc->fc.ars = &sc->arrs.xferq; + sc->fc.atq = &sc->atrq.xferq; + sc->fc.ats = &sc->atrs.xferq; + + sc->arrq.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); + sc->arrs.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); + sc->atrq.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); + sc->atrs.xferq.psize = roundup2(FWPMAX_S400, PAGE_SIZE); + + sc->arrq.xferq.start = NULL; + sc->arrs.xferq.start = NULL; + sc->atrq.xferq.start = fwohci_start_atq; + sc->atrs.xferq.start = fwohci_start_ats; + + sc->arrq.xferq.buf = NULL; + sc->arrs.xferq.buf = NULL; + sc->atrq.xferq.buf = NULL; + sc->atrs.xferq.buf = NULL; + + sc->arrq.ndesc = 1; + sc->arrs.ndesc = 1; + sc->atrq.ndesc = 8; /* equal to maximum of mbuf chains */ + sc->atrs.ndesc = 2; + + sc->arrq.ndb = NDB; + sc->arrs.ndb = NDB / 2; + sc->atrq.ndb = NDB; + sc->atrs.ndb = NDB / 2; + + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + sc->fc.it[i] = &sc->it[i].xferq; + sc->fc.ir[i] = &sc->ir[i].xferq; + sc->it[i].ndb = 0; + sc->ir[i].ndb = 0; + } + + sc->fc.tcode = tinfo; + sc->fc.dev = dev; + + sc->fc.config_rom = fwdma_malloc(&sc->fc, CROMSIZE, CROMSIZE, + &sc->crom_dma, BUS_DMA_WAITOK); + if(sc->fc.config_rom == NULL){ + device_printf(dev, "config_rom alloc failed."); + return ENOMEM; + } + +#if 0 + bzero(&sc->fc.config_rom[0], CROMSIZE); + sc->fc.config_rom[1] = 0x31333934; + sc->fc.config_rom[2] = 0xf000a002; + sc->fc.config_rom[3] = OREAD(sc, OHCI_EUID_HI); + sc->fc.config_rom[4] = OREAD(sc, OHCI_EUID_LO); + sc->fc.config_rom[5] = 0; + sc->fc.config_rom[0] = (4 << 24) | (5 << 16); + + sc->fc.config_rom[0] |= fw_crc16(&sc->fc.config_rom[1], 5*4); +#endif + + +/* SID recieve buffer must allign 2^11 */ +#define OHCI_SIDSIZE (1 << 11) + sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE, + &sc->sid_dma, BUS_DMA_WAITOK); + if (sc->sid_buf == NULL) { + device_printf(dev, "sid_buf alloc failed."); + return ENOMEM; + } + + fwdma_malloc(&sc->fc, sizeof(u_int32_t), sizeof(u_int32_t), + &sc->dummy_dma, BUS_DMA_WAITOK); + + if (sc->dummy_dma.v_addr == NULL) { + device_printf(dev, "dummy_dma alloc failed."); + return ENOMEM; + } + + fwohci_db_init(sc, &sc->arrq); + if ((sc->arrq.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + fwohci_db_init(sc, &sc->arrs); + if ((sc->arrs.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + fwohci_db_init(sc, &sc->atrq); + if ((sc->atrq.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + fwohci_db_init(sc, &sc->atrs); + if ((sc->atrs.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + sc->fc.eui.hi = OREAD(sc, FWOHCIGUID_H); + sc->fc.eui.lo = OREAD(sc, FWOHCIGUID_L); + for( i = 0 ; i < 8 ; i ++) + ui[i] = FW_EUI64_BYTE(&sc->fc.eui,i); + device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]); + + sc->fc.ioctl = fwohci_ioctl; + sc->fc.cyctimer = fwohci_cyctimer; + sc->fc.set_bmr = fwohci_set_bus_manager; + sc->fc.ibr = fwohci_ibr; + sc->fc.irx_enable = fwohci_irx_enable; + sc->fc.irx_disable = fwohci_irx_disable; + + sc->fc.itx_enable = fwohci_itxbuf_enable; + sc->fc.itx_disable = fwohci_itx_disable; +#if BYTE_ORDER == BIG_ENDIAN + sc->fc.irx_post = fwohci_irx_post; +#else + sc->fc.irx_post = NULL; +#endif + sc->fc.itx_post = NULL; + sc->fc.timeout = fwohci_timeout; + sc->fc.poll = fwohci_poll; + sc->fc.set_intr = fwohci_set_intr; + + sc->intmask = sc->irstat = sc->itstat = 0; + + fw_init(&sc->fc); + fwohci_reset(sc, dev); + + return 0; +} + +void +fwohci_timeout(void *arg) +{ + struct fwohci_softc *sc; + + sc = (struct fwohci_softc *)arg; +} + +u_int32_t +fwohci_cyctimer(struct firewire_comm *fc) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + return(OREAD(sc, OHCI_CYCLETIMER)); +} + +int +fwohci_detach(struct fwohci_softc *sc, device_t dev) +{ + int i; + + if (sc->sid_buf != NULL) + fwdma_free(&sc->fc, &sc->sid_dma); + if (sc->fc.config_rom != NULL) + fwdma_free(&sc->fc, &sc->crom_dma); + + fwohci_db_free(&sc->arrq); + fwohci_db_free(&sc->arrs); + + fwohci_db_free(&sc->atrq); + fwohci_db_free(&sc->atrs); + + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + fwohci_db_free(&sc->it[i]); + fwohci_db_free(&sc->ir[i]); + } + + return 0; +} + +#define LAST_DB(dbtr, db) do { \ + struct fwohcidb_tr *_dbtr = (dbtr); \ + int _cnt = _dbtr->dbcnt; \ + db = &_dbtr->db[ (_cnt > 2) ? (_cnt -1) : 0]; \ +} while (0) + +static void +fwohci_execute_db(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct fwohcidb_tr *db_tr; + volatile struct fwohcidb *db; + bus_dma_segment_t *s; + int i; + + db_tr = (struct fwohcidb_tr *)arg; + db = &db_tr->db[db_tr->dbcnt]; + if (error) { + if (firewire_debug || error != EFBIG) + printf("fwohci_execute_db: error=%d\n", error); + return; + } + for (i = 0; i < nseg; i++) { + s = &segs[i]; + FWOHCI_DMA_WRITE(db->db.desc.addr, s->ds_addr); + FWOHCI_DMA_WRITE(db->db.desc.cmd, s->ds_len); + FWOHCI_DMA_WRITE(db->db.desc.res, 0); + db++; + db_tr->dbcnt++; + } +} + +static void +fwohci_execute_db2(void *arg, bus_dma_segment_t *segs, int nseg, + bus_size_t size, int error) +{ + fwohci_execute_db(arg, segs, nseg, error); +} + +static void +fwohci_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int i, s; + int tcode, hdr_len, pl_off, pl_len; + int fsegment = -1; + u_int32_t off; + struct fw_xfer *xfer; + struct fw_pkt *fp; + volatile struct fwohci_txpkthdr *ohcifp; + struct fwohcidb_tr *db_tr; + volatile struct fwohcidb *db; + struct tcode_info *info; + static int maxdesc=0; + + if(&sc->atrq == dbch){ + off = OHCI_ATQOFF; + }else if(&sc->atrs == dbch){ + off = OHCI_ATSOFF; + }else{ + return; + } + + if (dbch->flags & FWOHCI_DBCH_FULL) + return; + + s = splfw(); + db_tr = dbch->top; +txloop: + xfer = STAILQ_FIRST(&dbch->xferq.q); + if(xfer == NULL){ + goto kick; + } + if(dbch->xferq.queued == 0 ){ + device_printf(sc->fc.dev, "TX queue empty\n"); + } + STAILQ_REMOVE_HEAD(&dbch->xferq.q, link); + db_tr->xfer = xfer; + xfer->state = FWXF_START; + + fp = (struct fw_pkt *)xfer->send.buf; + tcode = fp->mode.common.tcode; + + ohcifp = (volatile struct fwohci_txpkthdr *) db_tr->db[1].db.immed; + info = &tinfo[tcode]; + hdr_len = pl_off = info->hdr_len; + for( i = 0 ; i < pl_off ; i+= 4){ + ohcifp->mode.ld[i/4] = fp->mode.ld[i/4]; + } + ohcifp->mode.common.spd = xfer->spd; + if (tcode == FWTCODE_STREAM ){ + hdr_len = 8; + ohcifp->mode.stream.len = fp->mode.stream.len; + } else if (tcode == FWTCODE_PHY) { + hdr_len = 12; + ohcifp->mode.ld[1] = fp->mode.ld[1]; + ohcifp->mode.ld[2] = fp->mode.ld[2]; + ohcifp->mode.common.spd = 0; + ohcifp->mode.common.tcode = FWOHCITCODE_PHY; + } else { + ohcifp->mode.asycomm.dst = fp->mode.hdr.dst; + ohcifp->mode.asycomm.srcbus = OHCI_ASYSRCBUS; + ohcifp->mode.asycomm.tlrt |= FWRETRY_X; + } + db = &db_tr->db[0]; + FWOHCI_DMA_WRITE(db->db.desc.cmd, + OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | hdr_len); + FWOHCI_DMA_WRITE(db->db.desc.res, 0); +/* Specify bound timer of asy. responce */ + if(&sc->atrs == dbch){ + FWOHCI_DMA_WRITE(db->db.desc.res, + (OREAD(sc, OHCI_CYCLETIMER) >> 12) + (1 << 13)); + } +#if BYTE_ORDER == BIG_ENDIAN + if (tcode == FWTCODE_WREQQ || tcode == FWTCODE_RRESQ) + hdr_len = 12; + for (i = 0; i < hdr_len/4; i ++) + FWOHCI_DMA_WRITE(ohcifp->mode.ld[i], ohcifp->mode.ld[i]); +#endif + +again: + db_tr->dbcnt = 2; + db = &db_tr->db[db_tr->dbcnt]; + pl_len = xfer->send.len - pl_off; + if (pl_len > 0) { + int err; + /* handle payload */ + if (xfer->mbuf == NULL) { + caddr_t pl_addr; + + pl_addr = xfer->send.buf + pl_off; + err = bus_dmamap_load(dbch->dmat, db_tr->dma_map, + pl_addr, pl_len, + fwohci_execute_db, db_tr, + /*flags*/0); + } else { + /* XXX we can handle only 6 (=8-2) mbuf chains */ + err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map, + xfer->mbuf, + fwohci_execute_db2, db_tr, + /* flags */0); + if (err == EFBIG) { + struct mbuf *m0; + + if (firewire_debug) + device_printf(sc->fc.dev, "EFBIG.\n"); + m0 = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m0 != NULL) { + m_copydata(xfer->mbuf, 0, + xfer->mbuf->m_pkthdr.len, + mtod(m0, caddr_t)); + m0->m_len = m0->m_pkthdr.len = + xfer->mbuf->m_pkthdr.len; + m_freem(xfer->mbuf); + xfer->mbuf = m0; + goto again; + } + device_printf(sc->fc.dev, "m_getcl failed.\n"); + } + } + if (err) + printf("dmamap_load: err=%d\n", err); + bus_dmamap_sync(dbch->dmat, db_tr->dma_map, + BUS_DMASYNC_PREWRITE); +#if 0 /* OHCI_OUTPUT_MODE == 0 */ + for (i = 2; i < db_tr->dbcnt; i++) + FWOHCI_DMA_SET(db_tr->db[i].db.desc.cmd, + OHCI_OUTPUT_MORE); +#endif + } + if (maxdesc < db_tr->dbcnt) { + maxdesc = db_tr->dbcnt; + if (bootverbose) + device_printf(sc->fc.dev, "maxdesc: %d\n", maxdesc); + } + /* last db */ + LAST_DB(db_tr, db); + FWOHCI_DMA_SET(db->db.desc.cmd, + OHCI_OUTPUT_LAST | OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS); + FWOHCI_DMA_WRITE(db->db.desc.depend, + STAILQ_NEXT(db_tr, link)->bus_addr); + + if(fsegment == -1 ) + fsegment = db_tr->dbcnt; + if (dbch->pdb_tr != NULL) { + LAST_DB(dbch->pdb_tr, db); + FWOHCI_DMA_SET(db->db.desc.depend, db_tr->dbcnt); + } + dbch->pdb_tr = db_tr; + db_tr = STAILQ_NEXT(db_tr, link); + if(db_tr != dbch->bottom){ + goto txloop; + } else { + device_printf(sc->fc.dev, "fwohci_start: lack of db_trq\n"); + dbch->flags |= FWOHCI_DBCH_FULL; + } +kick: + /* kick asy q */ + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + + if(dbch->xferq.flag & FWXFERQ_RUNNING) { + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); + } else { + if (bootverbose) + device_printf(sc->fc.dev, "start AT DMA status=%x\n", + OREAD(sc, OHCI_DMACTL(off))); + OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | fsegment); + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN); + dbch->xferq.flag |= FWXFERQ_RUNNING; + } + + dbch->top = db_tr; + splx(s); + return; +} + +static void +fwohci_start_atq(struct firewire_comm *fc) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + fwohci_start( sc, &(sc->atrq)); + return; +} + +static void +fwohci_start_ats(struct firewire_comm *fc) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + fwohci_start( sc, &(sc->atrs)); + return; +} + +void +fwohci_txd(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int s, ch, err = 0; + struct fwohcidb_tr *tr; + volatile struct fwohcidb *db; + struct fw_xfer *xfer; + u_int32_t off; + u_int stat, status; + int packets; + struct firewire_comm *fc = (struct firewire_comm *)sc; + + if(&sc->atrq == dbch){ + off = OHCI_ATQOFF; + ch = ATRQ_CH; + }else if(&sc->atrs == dbch){ + off = OHCI_ATSOFF; + ch = ATRS_CH; + }else{ + return; + } + s = splfw(); + tr = dbch->bottom; + packets = 0; + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE); + while(dbch->xferq.queued > 0){ + LAST_DB(tr, db); + status = FWOHCI_DMA_READ(db->db.desc.res) >> OHCI_STATUS_SHIFT; + if(!(status & OHCI_CNTL_DMA_ACTIVE)){ + if (fc->status != FWBUSRESET) + /* maybe out of order?? */ + goto out; + } + bus_dmamap_sync(dbch->dmat, tr->dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dbch->dmat, tr->dma_map); +#if 0 + dump_db(sc, ch); +#endif + if(status & OHCI_CNTL_DMA_DEAD) { + /* Stop DMA */ + OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN); + device_printf(sc->fc.dev, "force reset AT FIFO\n"); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_LINKEN); + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS | OHCI_HCC_LINKEN); + OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN); + } + stat = status & FWOHCIEV_MASK; + switch(stat){ + case FWOHCIEV_ACKPEND: + case FWOHCIEV_ACKCOMPL: + err = 0; + break; + case FWOHCIEV_ACKBSA: + case FWOHCIEV_ACKBSB: + case FWOHCIEV_ACKBSX: + device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]); + err = EBUSY; + break; + case FWOHCIEV_FLUSHED: + case FWOHCIEV_ACKTARD: + device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]); + err = EAGAIN; + break; + case FWOHCIEV_MISSACK: + case FWOHCIEV_UNDRRUN: + case FWOHCIEV_OVRRUN: + case FWOHCIEV_DESCERR: + case FWOHCIEV_DTRDERR: + case FWOHCIEV_TIMEOUT: + case FWOHCIEV_TCODERR: + case FWOHCIEV_UNKNOWN: + case FWOHCIEV_ACKDERR: + case FWOHCIEV_ACKTERR: + default: + device_printf(sc->fc.dev, "txd err=%2x %s\n", + stat, fwohcicode[stat]); + err = EINVAL; + break; + } + if (tr->xfer != NULL) { + xfer = tr->xfer; + if (xfer->state == FWXF_RCVD) { + if (firewire_debug) + printf("already rcvd\n"); + fw_xfer_done(xfer); + } else { + xfer->state = FWXF_SENT; + if (err == EBUSY && fc->status != FWBUSRESET) { + xfer->state = FWXF_BUSY; + xfer->resp = err; + if (xfer->retry_req != NULL) + xfer->retry_req(xfer); + else { + xfer->recv.len = 0; + fw_xfer_done(xfer); + } + } else if (stat != FWOHCIEV_ACKPEND) { + if (stat != FWOHCIEV_ACKCOMPL) + xfer->state = FWXF_SENTERR; + xfer->resp = err; + xfer->recv.len = 0; + fw_xfer_done(xfer); + } + } + /* + * The watchdog timer takes care of split + * transcation timeout for ACKPEND case. + */ + } else { + printf("this shouldn't happen\n"); + } + dbch->xferq.queued --; + tr->xfer = NULL; + + packets ++; + tr = STAILQ_NEXT(tr, link); + dbch->bottom = tr; + if (dbch->bottom == dbch->top) { + /* we reaches the end of context program */ + if (firewire_debug && dbch->xferq.queued > 0) + printf("queued > 0\n"); + break; + } + } +out: + if ((dbch->flags & FWOHCI_DBCH_FULL) && packets > 0) { + printf("make free slot\n"); + dbch->flags &= ~FWOHCI_DBCH_FULL; + fwohci_start(sc, dbch); + } + splx(s); +} + +static void +fwohci_db_free(struct fwohci_dbch *dbch) +{ + struct fwohcidb_tr *db_tr; + int idb; + + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) + return; + + for(db_tr = STAILQ_FIRST(&dbch->db_trq), idb = 0; idb < dbch->ndb; + db_tr = STAILQ_NEXT(db_tr, link), idb++){ + if ((dbch->xferq.flag & FWXFERQ_EXTBUF) == 0 && + db_tr->buf != NULL) { + fwdma_free_size(dbch->dmat, db_tr->dma_map, + db_tr->buf, dbch->xferq.psize); + db_tr->buf = NULL; + } else if (db_tr->dma_map != NULL) + bus_dmamap_destroy(dbch->dmat, db_tr->dma_map); + } + dbch->ndb = 0; + db_tr = STAILQ_FIRST(&dbch->db_trq); + fwdma_free_multiseg(dbch->am); + free(db_tr, M_FW); + STAILQ_INIT(&dbch->db_trq); + dbch->flags &= ~FWOHCI_DBCH_INIT; +} + +static void +fwohci_db_init(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int idb; + struct fwohcidb_tr *db_tr; + + if ((dbch->flags & FWOHCI_DBCH_INIT) != 0) + goto out; + + /* create dma_tag for buffers */ +#define MAX_REQCOUNT 0xffff + if (bus_dma_tag_create(/*parent*/ sc->fc.dmat, + /*alignment*/ 1, /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ dbch->xferq.psize, + /*nsegments*/ dbch->ndesc > 3 ? dbch->ndesc - 2 : 1, + /*maxsegsz*/ MAX_REQCOUNT, + /*flags*/ 0, &dbch->dmat)) + return; + + /* allocate DB entries and attach one to each DMA channels */ + /* DB entry must start at 16 bytes bounary. */ + STAILQ_INIT(&dbch->db_trq); + db_tr = (struct fwohcidb_tr *) + malloc(sizeof(struct fwohcidb_tr) * dbch->ndb, + M_FW, M_WAITOK | M_ZERO); + if(db_tr == NULL){ + printf("fwohci_db_init: malloc(1) failed\n"); + return; + } + +#define DB_SIZE(x) (sizeof(struct fwohcidb) * (x)->ndesc) + dbch->am = fwdma_malloc_multiseg(&sc->fc, DB_SIZE(dbch), + DB_SIZE(dbch), dbch->ndb, BUS_DMA_WAITOK); + if (dbch->am == NULL) { + printf("fwohci_db_init: fwdma_malloc_multiseg failed\n"); + return; + } + /* Attach DB to DMA ch. */ + for(idb = 0 ; idb < dbch->ndb ; idb++){ + db_tr->dbcnt = 0; + db_tr->db = (struct fwohcidb *)fwdma_v_addr(dbch->am, idb); + db_tr->bus_addr = fwdma_bus_addr(dbch->am, idb); + /* create dmamap for buffers */ + /* XXX do we need 4bytes alignment tag? */ + /* XXX don't alloc dma_map for AR */ + if (bus_dmamap_create(dbch->dmat, 0, &db_tr->dma_map) != 0) { + printf("bus_dmamap_create failed\n"); + dbch->flags = FWOHCI_DBCH_INIT; /* XXX fake */ + fwohci_db_free(dbch); + return; + } + STAILQ_INSERT_TAIL(&dbch->db_trq, db_tr, link); + if (dbch->xferq.flag & FWXFERQ_EXTBUF) { + if (idb % dbch->xferq.bnpacket == 0) + dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket + ].start = (caddr_t)db_tr; + if ((idb + 1) % dbch->xferq.bnpacket == 0) + dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket + ].end = (caddr_t)db_tr; + } + db_tr++; + } + STAILQ_LAST(&dbch->db_trq, fwohcidb_tr,link)->link.stqe_next + = STAILQ_FIRST(&dbch->db_trq); +out: + dbch->xferq.queued = 0; + dbch->pdb_tr = NULL; + dbch->top = STAILQ_FIRST(&dbch->db_trq); + dbch->bottom = dbch->top; + dbch->flags = FWOHCI_DBCH_INIT; +} + +static int +fwohci_itx_disable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int sleepch; + + OWRITE(sc, OHCI_ITCTLCLR(dmach), + OHCI_CNTL_DMA_RUN | OHCI_CNTL_CYCMATCH_S); + OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); + /* XXX we cannot free buffers until the DMA really stops */ + tsleep((void *)&sleepch, FWPRI, "fwitxd", hz); + fwohci_db_free(&sc->it[dmach]); + sc->it[dmach].xferq.flag &= ~FWXFERQ_RUNNING; + return 0; +} + +static int +fwohci_irx_disable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int sleepch; + + OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach); + /* XXX we cannot free buffers until the DMA really stops */ + tsleep((void *)&sleepch, FWPRI, "fwirxd", hz); + fwohci_db_free(&sc->ir[dmach]); + sc->ir[dmach].xferq.flag &= ~FWXFERQ_RUNNING; + return 0; +} + +#if BYTE_ORDER == BIG_ENDIAN +static void +fwohci_irx_post (struct firewire_comm *fc , u_int32_t *qld) +{ + qld[0] = FWOHCI_DMA_READ(qld[0]); + return; +} +#endif + +static int +fwohci_tx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int err = 0; + int idb, z, i, dmach = 0, ldesc; + u_int32_t off = NULL; + struct fwohcidb_tr *db_tr; + volatile struct fwohcidb *db; + + if(!(dbch->xferq.flag & FWXFERQ_EXTBUF)){ + err = EINVAL; + return err; + } + z = dbch->ndesc; + for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){ + if( &sc->it[dmach] == dbch){ + off = OHCI_ITOFF(dmach); + break; + } + } + if(off == NULL){ + err = EINVAL; + return err; + } + if(dbch->xferq.flag & FWXFERQ_RUNNING) + return err; + dbch->xferq.flag |= FWXFERQ_RUNNING; + for( i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++){ + dbch->bottom = STAILQ_NEXT(dbch->bottom, link); + } + db_tr = dbch->top; + for (idb = 0; idb < dbch->ndb; idb ++) { + fwohci_add_tx_buf(dbch, db_tr, idb); + if(STAILQ_NEXT(db_tr, link) == NULL){ + break; + } + db = db_tr->db; + ldesc = db_tr->dbcnt - 1; + FWOHCI_DMA_WRITE(db[0].db.desc.depend, + STAILQ_NEXT(db_tr, link)->bus_addr | z); + db[ldesc].db.desc.depend = db[0].db.desc.depend; + if(dbch->xferq.flag & FWXFERQ_EXTBUF){ + if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){ + FWOHCI_DMA_SET( + db[ldesc].db.desc.cmd, + OHCI_INTERRUPT_ALWAYS); + /* OHCI 1.1 and above */ + FWOHCI_DMA_SET( + db[0].db.desc.cmd, + OHCI_INTERRUPT_ALWAYS); + } + } + db_tr = STAILQ_NEXT(db_tr, link); + } + FWOHCI_DMA_CLEAR( + dbch->bottom->db[dbch->bottom->dbcnt - 1].db.desc.depend, 0xf); + return err; +} + +static int +fwohci_rx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int err = 0; + int idb, z, i, dmach = 0, ldesc; + u_int32_t off = NULL; + struct fwohcidb_tr *db_tr; + volatile struct fwohcidb *db; + + z = dbch->ndesc; + if(&sc->arrq == dbch){ + off = OHCI_ARQOFF; + }else if(&sc->arrs == dbch){ + off = OHCI_ARSOFF; + }else{ + for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){ + if( &sc->ir[dmach] == dbch){ + off = OHCI_IROFF(dmach); + break; + } + } + } + if(off == NULL){ + err = EINVAL; + return err; + } + if(dbch->xferq.flag & FWXFERQ_STREAM){ + if(dbch->xferq.flag & FWXFERQ_RUNNING) + return err; + }else{ + if(dbch->xferq.flag & FWXFERQ_RUNNING){ + err = EBUSY; + return err; + } + } + dbch->xferq.flag |= FWXFERQ_RUNNING; + dbch->top = STAILQ_FIRST(&dbch->db_trq); + for( i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++){ + dbch->bottom = STAILQ_NEXT(dbch->bottom, link); + } + db_tr = dbch->top; + for (idb = 0; idb < dbch->ndb; idb ++) { + fwohci_add_rx_buf(dbch, db_tr, idb, &sc->dummy_dma); + if (STAILQ_NEXT(db_tr, link) == NULL) + break; + db = db_tr->db; + ldesc = db_tr->dbcnt - 1; + FWOHCI_DMA_WRITE(db[ldesc].db.desc.depend, + STAILQ_NEXT(db_tr, link)->bus_addr | z); + if(dbch->xferq.flag & FWXFERQ_EXTBUF){ + if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){ + FWOHCI_DMA_SET( + db[ldesc].db.desc.cmd, + OHCI_INTERRUPT_ALWAYS); + FWOHCI_DMA_CLEAR( + db[ldesc].db.desc.depend, + 0xf); + } + } + db_tr = STAILQ_NEXT(db_tr, link); + } + FWOHCI_DMA_CLEAR( + dbch->bottom->db[db_tr->dbcnt - 1].db.desc.depend, 0xf); + dbch->buf_offset = 0; + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + if(dbch->xferq.flag & FWXFERQ_STREAM){ + return err; + }else{ + OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | z); + } + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN); + return err; +} + +static int +fwohci_next_cycle(struct firewire_comm *fc, int cycle_now) +{ + int sec, cycle, cycle_match; + + cycle = cycle_now & 0x1fff; + sec = cycle_now >> 13; +#define CYCLE_MOD 0x10 +#if 1 +#define CYCLE_DELAY 8 /* min delay to start DMA */ +#else +#define CYCLE_DELAY 7000 /* min delay to start DMA */ +#endif + cycle = cycle + CYCLE_DELAY; + if (cycle >= 8000) { + sec ++; + cycle -= 8000; + } + cycle = roundup2(cycle, CYCLE_MOD); + if (cycle >= 8000) { + sec ++; + if (cycle == 8000) + cycle = 0; + else + cycle = CYCLE_MOD; + } + cycle_match = ((sec << 13) | cycle) & 0x7ffff; + + return(cycle_match); +} + +static int +fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int err = 0; + unsigned short tag, ich; + struct fwohci_dbch *dbch; + int cycle_match, cycle_now, s, ldesc; + u_int32_t stat; + struct fw_bulkxfer *first, *chunk, *prev; + struct fw_xferq *it; + + dbch = &sc->it[dmach]; + it = &dbch->xferq; + + tag = (it->flag >> 6) & 3; + ich = it->flag & 0x3f; + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) { + dbch->ndb = it->bnpacket * it->bnchunk; + dbch->ndesc = 3; + fwohci_db_init(sc, dbch); + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + err = fwohci_tx_enable(sc, dbch); + } + if(err) + return err; + + ldesc = dbch->ndesc - 1; + s = splfw(); + prev = STAILQ_LAST(&it->stdma, fw_bulkxfer, link); + while ((chunk = STAILQ_FIRST(&it->stvalid)) != NULL) { + volatile struct fwohcidb *db; + + fwdma_sync_multiseg(it->buf, chunk->poffset, it->bnpacket, + BUS_DMASYNC_PREWRITE); + fwohci_txbufdb(sc, dmach, chunk); + if (prev != NULL) { + db = ((struct fwohcidb_tr *)(prev->end))->db; +#if 0 /* XXX necessary? */ + FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, + OHCI_BRANCH_ALWAYS); +#endif +#if 0 /* if bulkxfer->npacket changes */ + db[ldesc].db.desc.depend = db[0].db.desc.depend = + ((struct fwohcidb_tr *) + (chunk->start))->bus_addr | dbch->ndesc; +#else + FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc); + FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc); +#endif + } + STAILQ_REMOVE_HEAD(&it->stvalid, link); + STAILQ_INSERT_TAIL(&it->stdma, chunk, link); + prev = chunk; + } + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); + splx(s); + stat = OREAD(sc, OHCI_ITCTL(dmach)); + if (firewire_debug && (stat & OHCI_CNTL_CYCMATCH_S)) + printf("stat 0x%x\n", stat); + + if (stat & (OHCI_CNTL_DMA_ACTIVE | OHCI_CNTL_CYCMATCH_S)) + return 0; + +#if 0 + OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN); +#endif + OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); + OWRITE(sc, OHCI_IT_MASK, 1 << dmach); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT); + + first = STAILQ_FIRST(&it->stdma); + OWRITE(sc, OHCI_ITCMD(dmach), + ((struct fwohcidb_tr *)(first->start))->bus_addr | dbch->ndesc); + if (firewire_debug) { + printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat); +#if 1 + dump_dma(sc, ITX_CH + dmach); +#endif + } + if ((stat & OHCI_CNTL_DMA_RUN) == 0) { +#if 1 + /* Don't start until all chunks are buffered */ + if (STAILQ_FIRST(&it->stfree) != NULL) + goto out; +#endif +#if 1 + /* Clear cycle match counter bits */ + OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xffff0000); + + /* 2bit second + 13bit cycle */ + cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff; + cycle_match = fwohci_next_cycle(fc, cycle_now); + + OWRITE(sc, OHCI_ITCTL(dmach), + OHCI_CNTL_CYCMATCH_S | (cycle_match << 16) + | OHCI_CNTL_DMA_RUN); +#else + OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN); +#endif + if (firewire_debug) { + printf("cycle_match: 0x%04x->0x%04x\n", + cycle_now, cycle_match); + dump_dma(sc, ITX_CH + dmach); + dump_db(sc, ITX_CH + dmach); + } + } else if ((stat & OHCI_CNTL_CYCMATCH_S) == 0) { + device_printf(sc->fc.dev, + "IT DMA underrun (0x%08x)\n", stat); + OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_WAKE); + } +out: + return err; +} + +static int +fwohci_irx_enable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int err = 0, s, ldesc; + unsigned short tag, ich; + u_int32_t stat; + struct fwohci_dbch *dbch; + struct fwohcidb_tr *db_tr; + struct fw_bulkxfer *first, *prev, *chunk; + struct fw_xferq *ir; + + dbch = &sc->ir[dmach]; + ir = &dbch->xferq; + + if ((ir->flag & FWXFERQ_RUNNING) == 0) { + tag = (ir->flag >> 6) & 3; + ich = ir->flag & 0x3f; + OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich); + + ir->queued = 0; + dbch->ndb = ir->bnpacket * ir->bnchunk; + dbch->ndesc = 2; + fwohci_db_init(sc, dbch); + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + err = fwohci_rx_enable(sc, dbch); + } + if(err) + return err; + + first = STAILQ_FIRST(&ir->stfree); + if (first == NULL) { + device_printf(fc->dev, "IR DMA no free chunk\n"); + return 0; + } + + ldesc = dbch->ndesc - 1; + s = splfw(); + prev = STAILQ_LAST(&ir->stdma, fw_bulkxfer, link); + while ((chunk = STAILQ_FIRST(&ir->stfree)) != NULL) { + volatile struct fwohcidb *db; + +#if 1 /* XXX for if_fwe */ + if (chunk->mbuf != NULL) { + db_tr = (struct fwohcidb_tr *)(chunk->start); + db_tr->dbcnt = 1; + err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map, + chunk->mbuf, fwohci_execute_db2, db_tr, + /* flags */0); + FWOHCI_DMA_SET(db_tr->db[1].db.desc.cmd, + OHCI_UPDATE | OHCI_INPUT_LAST | + OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS); + } +#endif + db = ((struct fwohcidb_tr *)(chunk->end))->db; + FWOHCI_DMA_WRITE(db[ldesc].db.desc.res, 0); + FWOHCI_DMA_CLEAR(db[ldesc].db.desc.depend, 0xf); + if (prev != NULL) { + db = ((struct fwohcidb_tr *)(prev->end))->db; + FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc); + } + STAILQ_REMOVE_HEAD(&ir->stfree, link); + STAILQ_INSERT_TAIL(&ir->stdma, chunk, link); + prev = chunk; + } + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); + splx(s); + stat = OREAD(sc, OHCI_IRCTL(dmach)); + if (stat & OHCI_CNTL_DMA_ACTIVE) + return 0; + if (stat & OHCI_CNTL_DMA_RUN) { + OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); + device_printf(sc->fc.dev, "IR DMA overrun (0x%08x)\n", stat); + } + + if (firewire_debug) + printf("start IR DMA 0x%x\n", stat); + OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach); + OWRITE(sc, OHCI_IR_MASK, 1 << dmach); + OWRITE(sc, OHCI_IRCTLCLR(dmach), 0xf0000000); + OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_ISOHDR); + OWRITE(sc, OHCI_IRCMD(dmach), + ((struct fwohcidb_tr *)(first->start))->bus_addr + | dbch->ndesc); + OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR); +#if 0 + dump_db(sc, IRX_CH + dmach); +#endif + return err; +} + +int +fwohci_stop(struct fwohci_softc *sc, device_t dev) +{ + u_int i; + +/* Now stopping all DMA channel */ + OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); + + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN); + } + +/* FLUSH FIFO and reset Transmitter/Reciever */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); + +/* Stop interrupt */ + OWRITE(sc, FWOHCI_INTMASKCLR, + OHCI_INT_EN | OHCI_INT_ERR | OHCI_INT_PHY_SID + | OHCI_INT_PHY_INT + | OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS + | OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS + | OHCI_INT_DMA_ARRQ | OHCI_INT_DMA_ARRS + | OHCI_INT_PHY_BUS_R); +/* XXX Link down? Bus reset? */ + return 0; +} + +int +fwohci_resume(struct fwohci_softc *sc, device_t dev) +{ + int i; + + fwohci_reset(sc, dev); + /* XXX resume isochronus receive automatically. (how about TX?) */ + for(i = 0; i < sc->fc.nisodma; i ++) { + if((sc->ir[i].xferq.flag & FWXFERQ_RUNNING) != 0) { + device_printf(sc->fc.dev, + "resume iso receive ch: %d\n", i); + sc->ir[i].xferq.flag &= ~FWXFERQ_RUNNING; + sc->fc.irx_enable(&sc->fc, i); + } + } + + bus_generic_resume(dev); + sc->fc.ibr(&sc->fc); + return 0; +} + +#define ACK_ALL +static void +fwohci_intr_body(struct fwohci_softc *sc, u_int32_t stat, int count) +{ + u_int32_t irstat, itstat; + u_int i; + struct firewire_comm *fc = (struct firewire_comm *)sc; + +#ifdef OHCI_DEBUG + if(stat & OREAD(sc, FWOHCI_INTMASK)) + device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n", + stat & OHCI_INT_EN ? "DMA_EN ":"", + stat & OHCI_INT_PHY_REG ? "PHY_REG ":"", + stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"", + stat & OHCI_INT_ERR ? "INT_ERR ":"", + stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"", + stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"", + stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"", + stat & OHCI_INT_CYC_START ? "CYC_START ":"", + stat & OHCI_INT_PHY_INT ? "PHY_INT ":"", + stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"", + stat & OHCI_INT_PHY_SID ? "SID ":"", + stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"", + stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"", + stat & OHCI_INT_DMA_IR ? "DMA_IR ":"", + stat & OHCI_INT_DMA_IT ? "DMA_IT " :"", + stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"", + stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"", + stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"", + stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"", + stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"", + stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"", + stat, OREAD(sc, FWOHCI_INTMASK) + ); +#endif +/* Bus reset */ + if(stat & OHCI_INT_PHY_BUS_R ){ + if (fc->status == FWBUSRESET) + goto busresetout; + /* Disable bus reset interrupt until sid recv. */ + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R); + + device_printf(fc->dev, "BUS reset\n"); + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST); + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC); + + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); + sc->atrq.xferq.flag &= ~FWXFERQ_RUNNING; + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); + sc->atrs.xferq.flag &= ~FWXFERQ_RUNNING; + +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_BUS_R); +#endif + fw_busreset(fc); + OWRITE(sc, OHCI_CROMHDR, ntohl(sc->fc.config_rom[0])); + OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->fc.config_rom[2])); + } +busresetout: + if((stat & OHCI_INT_DMA_IR )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_IR); +#endif +#if __FreeBSD_version >= 500000 + irstat = atomic_readandclear_int(&sc->irstat); +#else + irstat = sc->irstat; + sc->irstat = 0; +#endif + for(i = 0; i < fc->nisodma ; i++){ + struct fwohci_dbch *dbch; + + if((irstat & (1 << i)) != 0){ + dbch = &sc->ir[i]; + if ((dbch->xferq.flag & FWXFERQ_OPEN) == 0) { + device_printf(sc->fc.dev, + "dma(%d) not active\n", i); + continue; + } + fwohci_rbuf_update(sc, i); + } + } + } + if((stat & OHCI_INT_DMA_IT )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_IT); +#endif +#if __FreeBSD_version >= 500000 + itstat = atomic_readandclear_int(&sc->itstat); +#else + itstat = sc->itstat; + sc->itstat = 0; +#endif + for(i = 0; i < fc->nisodma ; i++){ + if((itstat & (1 << i)) != 0){ + fwohci_tbuf_update(sc, i); + } + } + } + if((stat & OHCI_INT_DMA_PRRS )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_PRRS); +#endif +#if 0 + dump_dma(sc, ARRS_CH); + dump_db(sc, ARRS_CH); +#endif + fwohci_arcv(sc, &sc->arrs, count); + } + if((stat & OHCI_INT_DMA_PRRQ )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_PRRQ); +#endif +#if 0 + dump_dma(sc, ARRQ_CH); + dump_db(sc, ARRQ_CH); +#endif + fwohci_arcv(sc, &sc->arrq, count); + } + if(stat & OHCI_INT_PHY_SID){ + u_int32_t *buf, node_id; + int plen; + +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_SID); +#endif + /* Enable bus reset interrupt */ + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R); + /* Allow async. request to us */ + OWRITE(sc, OHCI_AREQHI, 1 << 31); + /* XXX insecure ?? */ + OWRITE(sc, OHCI_PREQHI, 0x7fffffff); + OWRITE(sc, OHCI_PREQLO, 0xffffffff); + OWRITE(sc, OHCI_PREQUPPER, 0x10000); + /* Set ATRetries register */ + OWRITE(sc, OHCI_ATRETRY, 1<<(13+16) | 0xfff); +/* +** Checking whether the node is root or not. If root, turn on +** cycle master. +*/ + node_id = OREAD(sc, FWOHCI_NODEID); + plen = OREAD(sc, OHCI_SID_CNT); + + device_printf(fc->dev, "node_id=0x%08x, gen=%d, ", + node_id, (plen >> 16) & 0xff); + if (!(node_id & OHCI_NODE_VALID)) { + printf("Bus reset failure\n"); + goto sidout; + } + if (node_id & OHCI_NODE_ROOT) { + printf("CYCLEMASTER mode\n"); + OWRITE(sc, OHCI_LNKCTL, + OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER); + } else { + printf("non CYCLEMASTER mode\n"); + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR); + OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER); + } + fc->nodeid = node_id & 0x3f; + + if (plen & OHCI_SID_ERR) { + device_printf(fc->dev, "SID Error\n"); + goto sidout; + } + plen &= OHCI_SID_CNT_MASK; + if (plen < 4 || plen > OHCI_SIDSIZE) { + device_printf(fc->dev, "invalid SID len = %d\n", plen); + goto sidout; + } + plen -= 4; /* chop control info */ + buf = (u_int32_t *)malloc(OHCI_SIDSIZE, M_FW, M_NOWAIT); + if (buf == NULL) { + device_printf(fc->dev, "malloc failed\n"); + goto sidout; + } + for (i = 0; i < plen / 4; i ++) + buf[i] = FWOHCI_DMA_READ(sc->sid_buf[i+1]); +#if 1 + /* pending all pre-bus_reset packets */ + fwohci_txd(sc, &sc->atrq); + fwohci_txd(sc, &sc->atrs); + fwohci_arcv(sc, &sc->arrs, -1); + fwohci_arcv(sc, &sc->arrq, -1); + fw_drain_txq(fc); +#endif + fw_sidrcv(fc, buf, plen); + free(buf, M_FW); + } +sidout: + if((stat & OHCI_INT_DMA_ATRQ )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_ATRQ); +#endif + fwohci_txd(sc, &(sc->atrq)); + } + if((stat & OHCI_INT_DMA_ATRS )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_ATRS); +#endif + fwohci_txd(sc, &(sc->atrs)); + } + if((stat & OHCI_INT_PW_ERR )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PW_ERR); +#endif + device_printf(fc->dev, "posted write error\n"); + } + if((stat & OHCI_INT_ERR )){ +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_ERR); +#endif + device_printf(fc->dev, "unrecoverable error\n"); + } + if((stat & OHCI_INT_PHY_INT)) { +#ifndef ACK_ALL + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_INT); +#endif + device_printf(fc->dev, "phy int\n"); + } + + return; +} + +#if FWOHCI_TASKQUEUE +static void +fwohci_complete(void *arg, int pending) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)arg; + u_int32_t stat; + +again: + stat = atomic_readandclear_int(&sc->intstat); + if (stat) + fwohci_intr_body(sc, stat, -1); + else + return; + goto again; +} +#endif + +static u_int32_t +fwochi_check_stat(struct fwohci_softc *sc) +{ + u_int32_t stat, irstat, itstat; + + stat = OREAD(sc, FWOHCI_INTSTAT); + if (stat == 0xffffffff) { + device_printf(sc->fc.dev, + "device physically ejected?\n"); + return(stat); + } +#ifdef ACK_ALL + if (stat) + OWRITE(sc, FWOHCI_INTSTATCLR, stat); +#endif + if (stat & OHCI_INT_DMA_IR) { + irstat = OREAD(sc, OHCI_IR_STAT); + OWRITE(sc, OHCI_IR_STATCLR, irstat); + atomic_set_int(&sc->irstat, irstat); + } + if (stat & OHCI_INT_DMA_IT) { + itstat = OREAD(sc, OHCI_IT_STAT); + OWRITE(sc, OHCI_IT_STATCLR, itstat); + atomic_set_int(&sc->itstat, itstat); + } + return(stat); +} + +void +fwohci_intr(void *arg) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)arg; + u_int32_t stat; +#if !FWOHCI_TASKQUEUE + u_int32_t bus_reset = 0; +#endif + + if (!(sc->intmask & OHCI_INT_EN)) { + /* polling mode */ + return; + } + +#if !FWOHCI_TASKQUEUE +again: +#endif + stat = fwochi_check_stat(sc); + if (stat == 0 || stat == 0xffffffff) + return; +#if FWOHCI_TASKQUEUE + atomic_set_int(&sc->intstat, stat); + /* XXX mask bus reset intr. during bus reset phase */ + if (stat) + taskqueue_enqueue(taskqueue_swi_giant, &sc->fwohci_task_complete); +#else + /* We cannot clear bus reset event during bus reset phase */ + if ((stat & ~bus_reset) == 0) + return; + bus_reset = stat & OHCI_INT_PHY_BUS_R; + fwohci_intr_body(sc, stat, -1); + goto again; +#endif +} + +static void +fwohci_poll(struct firewire_comm *fc, int quick, int count) +{ + int s; + u_int32_t stat; + struct fwohci_softc *sc; + + + sc = (struct fwohci_softc *)fc; + stat = OHCI_INT_DMA_IR | OHCI_INT_DMA_IT | + OHCI_INT_DMA_PRRS | OHCI_INT_DMA_PRRQ | + OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS; +#if 0 + if (!quick) { +#else + if (1) { +#endif + stat = fwochi_check_stat(sc); + if (stat == 0 || stat == 0xffffffff) + return; + } + s = splfw(); + fwohci_intr_body(sc, stat, count); + splx(s); +} + +static void +fwohci_set_intr(struct firewire_comm *fc, int enable) +{ + struct fwohci_softc *sc; + + sc = (struct fwohci_softc *)fc; + if (bootverbose) + device_printf(sc->fc.dev, "fwohci_set_intr: %d\n", enable); + if (enable) { + sc->intmask |= OHCI_INT_EN; + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_EN); + } else { + sc->intmask &= ~OHCI_INT_EN; + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_EN); + } +} + +static void +fwohci_tbuf_update(struct fwohci_softc *sc, int dmach) +{ + struct firewire_comm *fc = &sc->fc; + volatile struct fwohcidb *db; + struct fw_bulkxfer *chunk; + struct fw_xferq *it; + u_int32_t stat, count; + int s, w=0, ldesc; + + it = fc->it[dmach]; + ldesc = sc->it[dmach].ndesc - 1; + s = splfw(); /* unnecessary ? */ + fwdma_sync_multiseg_all(sc->it[dmach].am, BUS_DMASYNC_POSTREAD); + while ((chunk = STAILQ_FIRST(&it->stdma)) != NULL) { + db = ((struct fwohcidb_tr *)(chunk->end))->db; + stat = FWOHCI_DMA_READ(db[ldesc].db.desc.res) + >> OHCI_STATUS_SHIFT; + db = ((struct fwohcidb_tr *)(chunk->start))->db; + count = FWOHCI_DMA_READ(db[ldesc].db.desc.res) + & OHCI_COUNT_MASK; + if (stat == 0) + break; + STAILQ_REMOVE_HEAD(&it->stdma, link); + switch (stat & FWOHCIEV_MASK){ + case FWOHCIEV_ACKCOMPL: +#if 0 + device_printf(fc->dev, "0x%08x\n", count); +#endif + break; + default: + device_printf(fc->dev, + "Isochronous transmit err %02x(%s)\n", + stat, fwohcicode[stat & 0x1f]); + } + STAILQ_INSERT_TAIL(&it->stfree, chunk, link); + w++; + } + splx(s); + if (w) + wakeup(it); +} + +static void +fwohci_rbuf_update(struct fwohci_softc *sc, int dmach) +{ + struct firewire_comm *fc = &sc->fc; + volatile struct fwohcidb_tr *db_tr; + struct fw_bulkxfer *chunk; + struct fw_xferq *ir; + u_int32_t stat; + int s, w=0, ldesc; + + ir = fc->ir[dmach]; + ldesc = sc->ir[dmach].ndesc - 1; +#if 0 + dump_db(sc, dmach); +#endif + s = splfw(); + fwdma_sync_multiseg_all(sc->ir[dmach].am, BUS_DMASYNC_POSTREAD); + while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) { + db_tr = (struct fwohcidb_tr *)chunk->end; + stat = FWOHCI_DMA_READ(db_tr->db[ldesc].db.desc.res) + >> OHCI_STATUS_SHIFT; + if (stat == 0) + break; + + if (chunk->mbuf != NULL) { + bus_dmamap_sync(sc->ir[dmach].dmat, db_tr->dma_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->ir[dmach].dmat, db_tr->dma_map); + } else if (ir->buf != NULL) { + fwdma_sync_multiseg(ir->buf, chunk->poffset, + ir->bnpacket, BUS_DMASYNC_POSTREAD); + } else { + /* XXX */ + printf("fwohci_rbuf_update: this shouldn't happend\n"); + } + + STAILQ_REMOVE_HEAD(&ir->stdma, link); + STAILQ_INSERT_TAIL(&ir->stvalid, chunk, link); + switch (stat & FWOHCIEV_MASK) { + case FWOHCIEV_ACKCOMPL: + chunk->resp = 0; + break; + default: + chunk->resp = EINVAL; + device_printf(fc->dev, + "Isochronous receive err %02x(%s)\n", + stat, fwohcicode[stat & 0x1f]); + } + w++; + } + splx(s); + if (w) { + if (ir->flag & FWXFERQ_HANDLER) + ir->hand(ir); + else + wakeup(ir); + } +} + +void +dump_dma(struct fwohci_softc *sc, u_int32_t ch) +{ + u_int32_t off, cntl, stat, cmd, match; + + if(ch == 0){ + off = OHCI_ATQOFF; + }else if(ch == 1){ + off = OHCI_ATSOFF; + }else if(ch == 2){ + off = OHCI_ARQOFF; + }else if(ch == 3){ + off = OHCI_ARSOFF; + }else if(ch < IRX_CH){ + off = OHCI_ITCTL(ch - ITX_CH); + }else{ + off = OHCI_IRCTL(ch - IRX_CH); + } + cntl = stat = OREAD(sc, off); + cmd = OREAD(sc, off + 0xc); + match = OREAD(sc, off + 0x10); + + device_printf(sc->fc.dev, "ch %1x cntl:0x%08x cmd:0x%08x match:0x%08x\n", + ch, + cntl, + cmd, + match); + stat &= 0xffff ; + if (stat) { + device_printf(sc->fc.dev, "dma %d ch:%s%s%s%s%s%s %s(%x)\n", + ch, + stat & OHCI_CNTL_DMA_RUN ? "RUN," : "", + stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "", + stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "", + stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "", + stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "", + stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "", + fwohcicode[stat & 0x1f], + stat & 0x1f + ); + }else{ + device_printf(sc->fc.dev, "dma %d ch: Nostat\n", ch); + } +} + +void +dump_db(struct fwohci_softc *sc, u_int32_t ch) +{ + struct fwohci_dbch *dbch; + struct fwohcidb_tr *cp = NULL, *pp, *np = NULL; + volatile struct fwohcidb *curr = NULL, *prev, *next = NULL; + int idb, jdb; + u_int32_t cmd, off; + if(ch == 0){ + off = OHCI_ATQOFF; + dbch = &sc->atrq; + }else if(ch == 1){ + off = OHCI_ATSOFF; + dbch = &sc->atrs; + }else if(ch == 2){ + off = OHCI_ARQOFF; + dbch = &sc->arrq; + }else if(ch == 3){ + off = OHCI_ARSOFF; + dbch = &sc->arrs; + }else if(ch < IRX_CH){ + off = OHCI_ITCTL(ch - ITX_CH); + dbch = &sc->it[ch - ITX_CH]; + }else { + off = OHCI_IRCTL(ch - IRX_CH); + dbch = &sc->ir[ch - IRX_CH]; + } + cmd = OREAD(sc, off + 0xc); + + if( dbch->ndb == 0 ){ + device_printf(sc->fc.dev, "No DB is attached ch=%d\n", ch); + return; + } + pp = dbch->top; + prev = pp->db; + for(idb = 0 ; idb < dbch->ndb ; idb ++ ){ + if(pp == NULL){ + curr = NULL; + goto outdb; + } + cp = STAILQ_NEXT(pp, link); + if(cp == NULL){ + curr = NULL; + goto outdb; + } + np = STAILQ_NEXT(cp, link); + for(jdb = 0 ; jdb < dbch->ndesc ; jdb ++ ){ + if ((cmd & 0xfffffff0) == cp->bus_addr) { + curr = cp->db; + if(np != NULL){ + next = np->db; + }else{ + next = NULL; + } + goto outdb; + } + } + pp = STAILQ_NEXT(pp, link); + prev = pp->db; + } +outdb: + if( curr != NULL){ +#if 0 + printf("Prev DB %d\n", ch); + print_db(pp, prev, ch, dbch->ndesc); +#endif + printf("Current DB %d\n", ch); + print_db(cp, curr, ch, dbch->ndesc); +#if 0 + printf("Next DB %d\n", ch); + print_db(np, next, ch, dbch->ndesc); +#endif + }else{ + printf("dbdump err ch = %d cmd = 0x%08x\n", ch, cmd); + } + return; +} + +void +print_db(struct fwohcidb_tr *db_tr, volatile struct fwohcidb *db, + u_int32_t ch, u_int32_t max) +{ + fwohcireg_t stat; + int i, key; + u_int32_t cmd, res; + + if(db == NULL){ + printf("No Descriptor is found\n"); + return; + } + + printf("ch = %d\n%8s %s %s %s %s %4s %8s %8s %4s:%4s\n", + ch, + "Current", + "OP ", + "KEY", + "INT", + "BR ", + "len", + "Addr", + "Depend", + "Stat", + "Cnt"); + for( i = 0 ; i <= max ; i ++){ + cmd = FWOHCI_DMA_READ(db[i].db.desc.cmd); + res = FWOHCI_DMA_READ(db[i].db.desc.res); + key = cmd & OHCI_KEY_MASK; + stat = res >> OHCI_STATUS_SHIFT; +#if __FreeBSD_version >= 500000 + printf("%08jx %s %s %s %s %5d %08x %08x %04x:%04x", + (uintmax_t)db_tr->bus_addr, +#else + printf("%08x %s %s %s %s %5d %08x %08x %04x:%04x", + db_tr->bus_addr, +#endif + dbcode[(cmd >> 28) & 0xf], + dbkey[(cmd >> 24) & 0x7], + dbcond[(cmd >> 20) & 0x3], + dbcond[(cmd >> 18) & 0x3], + cmd & OHCI_COUNT_MASK, + FWOHCI_DMA_READ(db[i].db.desc.addr), + FWOHCI_DMA_READ(db[i].db.desc.depend), + stat, + res & OHCI_COUNT_MASK); + if(stat & 0xff00){ + printf(" %s%s%s%s%s%s %s(%x)\n", + stat & OHCI_CNTL_DMA_RUN ? "RUN," : "", + stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "", + stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "", + stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "", + stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "", + stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "", + fwohcicode[stat & 0x1f], + stat & 0x1f + ); + }else{ + printf(" Nostat\n"); + } + if(key == OHCI_KEY_ST2 ){ + printf("0x%08x 0x%08x 0x%08x 0x%08x\n", + FWOHCI_DMA_READ(db[i+1].db.immed[0]), + FWOHCI_DMA_READ(db[i+1].db.immed[1]), + FWOHCI_DMA_READ(db[i+1].db.immed[2]), + FWOHCI_DMA_READ(db[i+1].db.immed[3])); + } + if(key == OHCI_KEY_DEVICE){ + return; + } + if((cmd & OHCI_BRANCH_MASK) + == OHCI_BRANCH_ALWAYS){ + return; + } + if((cmd & OHCI_CMD_MASK) + == OHCI_OUTPUT_LAST){ + return; + } + if((cmd & OHCI_CMD_MASK) + == OHCI_INPUT_LAST){ + return; + } + if(key == OHCI_KEY_ST2 ){ + i++; + } + } + return; +} + +void +fwohci_ibr(struct firewire_comm *fc) +{ + struct fwohci_softc *sc; + u_int32_t fun; + + device_printf(fc->dev, "Initiate bus reset\n"); + sc = (struct fwohci_softc *)fc; + + /* + * Set root hold-off bit so that non cyclemaster capable node + * shouldn't became the root node. + */ +#if 1 + fun = fwphy_rddata(sc, FW_PHY_IBR_REG); + fun |= FW_PHY_IBR | FW_PHY_RHB; + fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun); +#else /* Short bus reset */ + fun = fwphy_rddata(sc, FW_PHY_ISBR_REG); + fun |= FW_PHY_ISBR | FW_PHY_RHB; + fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun); +#endif +} + +void +fwohci_txbufdb(struct fwohci_softc *sc, int dmach, struct fw_bulkxfer *bulkxfer) +{ + struct fwohcidb_tr *db_tr, *fdb_tr; + struct fwohci_dbch *dbch; + volatile struct fwohcidb *db; + struct fw_pkt *fp; + volatile struct fwohci_txpkthdr *ohcifp; + unsigned short chtag; + int idb; + + dbch = &sc->it[dmach]; + chtag = sc->it[dmach].xferq.flag & 0xff; + + db_tr = (struct fwohcidb_tr *)(bulkxfer->start); + fdb_tr = (struct fwohcidb_tr *)(bulkxfer->end); +/* +device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, db_tr->bus_addr, fdb_tr->bus_addr); +*/ + for (idb = 0; idb < dbch->xferq.bnpacket; idb ++) { + db = db_tr->db; + fp = (struct fw_pkt *)db_tr->buf; + ohcifp = (volatile struct fwohci_txpkthdr *) db[1].db.immed; + ohcifp->mode.ld[0] = fp->mode.ld[0]; + ohcifp->mode.stream.len = fp->mode.stream.len; + ohcifp->mode.stream.chtag = chtag; + ohcifp->mode.stream.tcode = 0xa; + ohcifp->mode.stream.spd = 0; +#if BYTE_ORDER == BIG_ENDIAN + FWOHCI_DMA_WRITE(db[1].db.immed[0], db[1].db.immed[0]); + FWOHCI_DMA_WRITE(db[1].db.immed[1], db[1].db.immed[1]); +#endif + + FWOHCI_DMA_CLEAR(db[2].db.desc.cmd, OHCI_COUNT_MASK); + FWOHCI_DMA_SET(db[2].db.desc.cmd, fp->mode.stream.len); + FWOHCI_DMA_WRITE(db[2].db.desc.res, 0); +#if 0 /* if bulkxfer->npackets changes */ + db[2].db.desc.cmd = OHCI_OUTPUT_LAST + | OHCI_UPDATE + | OHCI_BRANCH_ALWAYS; + db[0].db.desc.depend = + = db[dbch->ndesc - 1].db.desc.depend + = STAILQ_NEXT(db_tr, link)->bus_addr | dbch->ndesc; +#else + FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc); + FWOHCI_DMA_SET(db[dbch->ndesc - 1].db.desc.depend, dbch->ndesc); +#endif + bulkxfer->end = (caddr_t)db_tr; + db_tr = STAILQ_NEXT(db_tr, link); + } + db = ((struct fwohcidb_tr *)bulkxfer->end)->db; + FWOHCI_DMA_CLEAR(db[0].db.desc.depend, 0xf); + FWOHCI_DMA_CLEAR(db[dbch->ndesc - 1].db.desc.depend, 0xf); +#if 0 /* if bulkxfer->npackets changes */ + db[dbch->ndesc - 1].db.desc.control |= OHCI_INTERRUPT_ALWAYS; + /* OHCI 1.1 and above */ + db[0].db.desc.control |= OHCI_INTERRUPT_ALWAYS; +#endif +/* + db_tr = (struct fwohcidb_tr *)bulkxfer->start; + fdb_tr = (struct fwohcidb_tr *)bulkxfer->end; +device_printf(sc->fc.dev, "DB %08x %3d %08x %08x\n", bulkxfer, bulkxfer->npacket, db_tr->bus_addr, fdb_tr->bus_addr); +*/ + return; +} + +static int +fwohci_add_tx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, + int poffset) +{ + volatile struct fwohcidb *db = db_tr->db; + struct fw_xferq *it; + int err = 0; + + it = &dbch->xferq; + if(it->buf == 0){ + err = EINVAL; + return err; + } + db_tr->buf = fwdma_v_addr(it->buf, poffset); + db_tr->dbcnt = 3; + + FWOHCI_DMA_WRITE(db[0].db.desc.cmd, + OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8); + FWOHCI_DMA_WRITE(db[2].db.desc.addr, + fwdma_bus_addr(it->buf, poffset) + sizeof(u_int32_t)); + + FWOHCI_DMA_WRITE(db[2].db.desc.cmd, + OHCI_OUTPUT_LAST | OHCI_UPDATE | OHCI_BRANCH_ALWAYS); +#if 1 + FWOHCI_DMA_WRITE(db[0].db.desc.res, 0); + FWOHCI_DMA_WRITE(db[2].db.desc.res, 0); +#endif + return 0; +} + +int +fwohci_add_rx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, + int poffset, struct fwdma_alloc *dummy_dma) +{ + volatile struct fwohcidb *db = db_tr->db; + struct fw_xferq *ir; + int i, ldesc; + bus_addr_t dbuf[2]; + int dsiz[2]; + + ir = &dbch->xferq; + if (ir->buf == NULL && (dbch->xferq.flag & FWXFERQ_EXTBUF) == 0) { + db_tr->buf = fwdma_malloc_size(dbch->dmat, &db_tr->dma_map, + ir->psize, &dbuf[0], BUS_DMA_NOWAIT); + if (db_tr->buf == NULL) + return(ENOMEM); + db_tr->dbcnt = 1; + dsiz[0] = ir->psize; + bus_dmamap_sync(dbch->dmat, db_tr->dma_map, + BUS_DMASYNC_PREREAD); + } else { + db_tr->dbcnt = 0; + if (dummy_dma != NULL) { + dsiz[db_tr->dbcnt] = sizeof(u_int32_t); + dbuf[db_tr->dbcnt++] = dummy_dma->bus_addr; + } + dsiz[db_tr->dbcnt] = ir->psize; + if (ir->buf != NULL) { + db_tr->buf = fwdma_v_addr(ir->buf, poffset); + dbuf[db_tr->dbcnt] = fwdma_bus_addr( ir->buf, poffset); + } + db_tr->dbcnt++; + } + for(i = 0 ; i < db_tr->dbcnt ; i++){ + FWOHCI_DMA_WRITE(db[i].db.desc.addr, dbuf[i]); + FWOHCI_DMA_WRITE(db[i].db.desc.cmd, OHCI_INPUT_MORE | dsiz[i]); + if (ir->flag & FWXFERQ_STREAM) { + FWOHCI_DMA_SET(db[i].db.desc.cmd, OHCI_UPDATE); + } + FWOHCI_DMA_WRITE(db[i].db.desc.res, dsiz[i]); + } + ldesc = db_tr->dbcnt - 1; + if (ir->flag & FWXFERQ_STREAM) { + FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_INPUT_LAST); + } + FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_BRANCH_ALWAYS); + return 0; +} + + +static int +fwohci_arcv_swap(struct fw_pkt *fp, int len) +{ + struct fw_pkt *fp0; + u_int32_t ld0; + int slen; +#if BYTE_ORDER == BIG_ENDIAN + int i; +#endif + + ld0 = FWOHCI_DMA_READ(fp->mode.ld[0]); +#if 0 + printf("ld0: x%08x\n", ld0); +#endif + fp0 = (struct fw_pkt *)&ld0; + switch (fp0->mode.common.tcode) { + case FWTCODE_RREQQ: + case FWTCODE_WRES: + case FWTCODE_WREQQ: + case FWTCODE_RRESQ: + case FWOHCITCODE_PHY: + slen = 12; + break; + case FWTCODE_RREQB: + case FWTCODE_WREQB: + case FWTCODE_LREQ: + case FWTCODE_RRESB: + case FWTCODE_LRES: + slen = 16; + break; + default: + printf("Unknown tcode %d\n", fp0->mode.common.tcode); + return(0); + } + if (slen > len) { + if (firewire_debug) + printf("splitted header\n"); + return(-slen); + } +#if BYTE_ORDER == BIG_ENDIAN + for(i = 0; i < slen/4; i ++) + fp->mode.ld[i] = FWOHCI_DMA_READ(fp->mode.ld[i]); +#endif + return(slen); +} + +#define PLEN(x) roundup2(x, sizeof(u_int32_t)) +static int +fwohci_get_plen(struct fwohci_softc *sc, struct fwohci_dbch *dbch, struct fw_pkt *fp) +{ + int r; + + switch(fp->mode.common.tcode){ + case FWTCODE_RREQQ: + r = sizeof(fp->mode.rreqq) + sizeof(u_int32_t); + break; + case FWTCODE_WRES: + r = sizeof(fp->mode.wres) + sizeof(u_int32_t); + break; + case FWTCODE_WREQQ: + r = sizeof(fp->mode.wreqq) + sizeof(u_int32_t); + break; + case FWTCODE_RREQB: + r = sizeof(fp->mode.rreqb) + sizeof(u_int32_t); + break; + case FWTCODE_RRESQ: + r = sizeof(fp->mode.rresq) + sizeof(u_int32_t); + break; + case FWTCODE_WREQB: + r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.wreqb.len) + + sizeof(u_int32_t); + break; + case FWTCODE_LREQ: + r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.lreq.len) + + sizeof(u_int32_t); + break; + case FWTCODE_RRESB: + r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.rresb.len) + + sizeof(u_int32_t); + break; + case FWTCODE_LRES: + r = sizeof(struct fw_asyhdr) + PLEN(fp->mode.lres.len) + + sizeof(u_int32_t); + break; + case FWOHCITCODE_PHY: + r = 16; + break; + default: + device_printf(sc->fc.dev, "Unknown tcode %d\n", + fp->mode.common.tcode); + r = 0; + } + if (r > dbch->xferq.psize) { + device_printf(sc->fc.dev, "Invalid packet length %d\n", r); + /* panic ? */ + } + return r; +} + +static void +fwohci_arcv_free_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr) +{ + volatile struct fwohcidb *db = &db_tr->db[0]; + + FWOHCI_DMA_CLEAR(db->db.desc.depend, 0xf); + FWOHCI_DMA_WRITE(db->db.desc.res, dbch->xferq.psize); + FWOHCI_DMA_SET(dbch->bottom->db[0].db.desc.depend, 1); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + dbch->bottom = db_tr; +} + +static void +fwohci_arcv(struct fwohci_softc *sc, struct fwohci_dbch *dbch, int count) +{ + struct fwohcidb_tr *db_tr; + struct iovec vec[2]; + struct fw_pkt pktbuf; + int nvec; + struct fw_pkt *fp; + u_int8_t *ld; + u_int32_t stat, off, status; + u_int spd; + int len, plen, hlen, pcnt, offset; + int s; + caddr_t buf; + int resCount; + + if(&sc->arrq == dbch){ + off = OHCI_ARQOFF; + }else if(&sc->arrs == dbch){ + off = OHCI_ARSOFF; + }else{ + return; + } + + s = splfw(); + db_tr = dbch->top; + pcnt = 0; + /* XXX we cannot handle a packet which lies in more than two buf */ + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD); + fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE); + status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) >> OHCI_STATUS_SHIFT; + resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) & OHCI_COUNT_MASK; +#if 0 + printf("status 0x%04x, resCount 0x%04x\n", status, resCount); +#endif + while (status & OHCI_CNTL_DMA_ACTIVE) { + len = dbch->xferq.psize - resCount; + ld = (u_int8_t *)db_tr->buf; + if (dbch->pdb_tr == NULL) { + len -= dbch->buf_offset; + ld += dbch->buf_offset; + } + if (len > 0) + bus_dmamap_sync(dbch->dmat, db_tr->dma_map, + BUS_DMASYNC_POSTREAD); + while (len > 0 ) { + if (count >= 0 && count-- == 0) + goto out; + if(dbch->pdb_tr != NULL){ + /* we have a fragment in previous buffer */ + int rlen; + + offset = dbch->buf_offset; + if (offset < 0) + offset = - offset; + buf = dbch->pdb_tr->buf + offset; + rlen = dbch->xferq.psize - offset; + if (firewire_debug) + printf("rlen=%d, offset=%d\n", + rlen, dbch->buf_offset); + if (dbch->buf_offset < 0) { + /* splitted in header, pull up */ + char *p; + + p = (char *)&pktbuf; + bcopy(buf, p, rlen); + p += rlen; + /* this must be too long but harmless */ + rlen = sizeof(pktbuf) - rlen; + if (rlen < 0) + printf("why rlen < 0\n"); + bcopy(db_tr->buf, p, rlen); + ld += rlen; + len -= rlen; + hlen = fwohci_arcv_swap(&pktbuf, sizeof(pktbuf)); + if (hlen < 0) { + printf("hlen < 0 shouldn't happen"); + } + offset = sizeof(pktbuf); + vec[0].iov_base = (char *)&pktbuf; + vec[0].iov_len = offset; + } else { + /* splitted in payload */ + offset = rlen; + vec[0].iov_base = buf; + vec[0].iov_len = rlen; + } + fp=(struct fw_pkt *)vec[0].iov_base; + nvec = 1; + } else { + /* no fragment in previous buffer */ + fp=(struct fw_pkt *)ld; + hlen = fwohci_arcv_swap(fp, len); + if (hlen == 0) + /* XXX need reset */ + goto out; + if (hlen < 0) { + dbch->pdb_tr = db_tr; + dbch->buf_offset = - dbch->buf_offset; + /* sanity check */ + if (resCount != 0) + printf("resCount != 0 !?\n"); + goto out; + } + offset = 0; + nvec = 0; + } + plen = fwohci_get_plen(sc, dbch, fp) - offset; + if (plen < 0) { + /* minimum header size + trailer + = sizeof(fw_pkt) so this shouldn't happens */ + printf("plen is negative! offset=%d\n", offset); + goto out; + } + if (plen > 0) { + len -= plen; + if (len < 0) { + dbch->pdb_tr = db_tr; + if (firewire_debug) + printf("splitted payload\n"); + /* sanity check */ + if (resCount != 0) + printf("resCount != 0 !?\n"); + goto out; + } + vec[nvec].iov_base = ld; + vec[nvec].iov_len = plen; + nvec ++; + ld += plen; + } + dbch->buf_offset = ld - (u_int8_t *)db_tr->buf; + if (nvec == 0) + printf("nvec == 0\n"); + +/* DMA result-code will be written at the tail of packet */ +#if BYTE_ORDER == BIG_ENDIAN + stat = FWOHCI_DMA_READ(((struct fwohci_trailer *)(ld - sizeof(struct fwohci_trailer)))->stat) >> 16; +#else + stat = ((struct fwohci_trailer *)(ld - sizeof(struct fwohci_trailer)))->stat; +#endif +#if 0 + printf("plen: %d, stat %x\n", plen ,stat); +#endif + spd = (stat >> 5) & 0x3; + stat &= 0x1f; + switch(stat){ + case FWOHCIEV_ACKPEND: +#if 0 + printf("fwohci_arcv: ack pending tcode=0x%x..\n", fp->mode.common.tcode); +#endif + /* fall through */ + case FWOHCIEV_ACKCOMPL: + if ((vec[nvec-1].iov_len -= + sizeof(struct fwohci_trailer)) == 0) + nvec--; + fw_rcv(&sc->fc, vec, nvec, 0, spd); + break; + case FWOHCIEV_BUSRST: + if (sc->fc.status != FWBUSRESET) + printf("got BUSRST packet!?\n"); + break; + default: + device_printf(sc->fc.dev, "Async DMA Receive error err = %02x %s\n", stat, fwohcicode[stat]); +#if 0 /* XXX */ + goto out; +#endif + break; + } + pcnt ++; + if (dbch->pdb_tr != NULL) { + fwohci_arcv_free_buf(dbch, dbch->pdb_tr); + dbch->pdb_tr = NULL; + } + + } +out: + if (resCount == 0) { + /* done on this buffer */ + if (dbch->pdb_tr == NULL) { + fwohci_arcv_free_buf(dbch, db_tr); + dbch->buf_offset = 0; + } else + if (dbch->pdb_tr != db_tr) + printf("pdb_tr != db_tr\n"); + db_tr = STAILQ_NEXT(db_tr, link); + status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) + >> OHCI_STATUS_SHIFT; + resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) + & OHCI_COUNT_MASK; + /* XXX check buffer overrun */ + dbch->top = db_tr; + } else { + dbch->buf_offset = dbch->xferq.psize - resCount; + break; + } + /* XXX make sure DMA is not dead */ + } +#if 0 + if (pcnt < 1) + printf("fwohci_arcv: no packets\n"); +#endif + splx(s); +} diff --git a/sys/dev/firewire/fwohci_pci.c b/sys/dev/firewire/fwohci_pci.c new file mode 100644 index 0000000..9898aba --- /dev/null +++ b/sys/dev/firewire/fwohci_pci.c @@ -0,0 +1,438 @@ +/* + * 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 BOUNCE_BUFFER_TEST 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/queue.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> +#include <machine/resource.h> + +#include <pci/pcivar.h> +#include <pci/pcireg.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> + +#include <dev/firewire/fwdma.h> +#include <dev/firewire/fwohcireg.h> +#include <dev/firewire/fwohcivar.h> + +static int fwohci_pci_attach(device_t self); +static int fwohci_pci_detach(device_t self); + +/* + * The probe routine. + */ +static int +fwohci_pci_probe( device_t dev ) +{ +#if 1 + u_int32_t id; + + id = pci_get_devid(dev); + if (id == (FW_VENDORID_NEC | FW_DEVICE_UPD861)) { + device_set_desc(dev, "NEC uPD72861"); + return 0; + } + if (id == (FW_VENDORID_NEC | FW_DEVICE_UPD871)) { + device_set_desc(dev, "NEC uPD72871/2"); + return 0; + } + if (id == (FW_VENDORID_NEC | FW_DEVICE_UPD72870)) { + device_set_desc(dev, "NEC uPD72870"); + return 0; + } + if (id == (FW_VENDORID_NEC | FW_DEVICE_UPD72874)) { + device_set_desc(dev, "NEC uPD72874"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB22)) { + device_set_desc(dev, "Texas Instruments TSB12LV22"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB23)) { + device_set_desc(dev, "Texas Instruments TSB12LV23"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB26)) { + device_set_desc(dev, "Texas Instruments TSB12LV26"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB43)) { + device_set_desc(dev, "Texas Instruments TSB43AA22"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB43A)) { + device_set_desc(dev, "Texas Instruments TSB43AB22/A"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB43AB23)) { + device_set_desc(dev, "Texas Instruments TSB43AB23"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TITSB82AA2)) { + device_set_desc(dev, "Texas Instruments TSB82AA2"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TIPCI4450)) { + device_set_desc(dev, "Texas Instruments PCI4450"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TIPCI4410A)) { + device_set_desc(dev, "Texas Instruments PCI4410A"); + return 0; + } + if (id == (FW_VENDORID_TI | FW_DEVICE_TIPCI4451)) { + device_set_desc(dev, "Texas Instruments PCI4451"); + return 0; + } + if (id == (FW_VENDORID_SONY | FW_DEVICE_CX3022)) { + device_set_desc(dev, "Sony CX3022"); + return 0; + } + if (id == (FW_VENDORID_VIA | FW_DEVICE_VT6306)) { + device_set_desc(dev, "VIA VT6306"); + return 0; + } + if (id == (FW_VENDORID_RICOH | FW_DEVICE_R5C551)) { + device_set_desc(dev, "Ricoh R5C551"); + return 0; + } + if (id == (FW_VENDORID_RICOH | FW_DEVICE_R5C552)) { + device_set_desc(dev, "Ricoh R5C552"); + return 0; + } + if (id == (FW_VENDORID_APPLE | FW_DEVICE_PANGEA)) { + device_set_desc(dev, "Apple Pangea"); + return 0; + } + if (id == (FW_VENDORID_APPLE | FW_DEVICE_UNINORTH)) { + device_set_desc(dev, "Apple UniNorth"); + return 0; + } + if (id == (FW_VENDORID_LUCENT | FW_DEVICE_FW322)) { + device_set_desc(dev, "Lucent FW322/323"); + return 0; + } +#endif + if (pci_get_class(dev) == PCIC_SERIALBUS + && pci_get_subclass(dev) == PCIS_SERIALBUS_FW + && pci_get_progif(dev) == PCI_INTERFACE_OHCI) { + device_printf(dev, "vendor=%x, dev=%x\n", pci_get_vendor(dev), + pci_get_device(dev)); + device_set_desc(dev, "1394 Open Host Controller Interface"); + return 0; + } + + return ENXIO; +} + +#if __FreeBSD_version < 500000 +static void +fwohci_dummy_intr(void *arg) +{ + /* XXX do nothing */ +} +#endif + +static int +fwohci_pci_init(device_t self) +{ + int olatency, latency, ocache_line, cache_line; + u_int16_t cmd; + + cmd = pci_read_config(self, PCIR_COMMAND, 2); + cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN | + PCIM_CMD_SERRESPEN | PCIM_CMD_PERRESPEN; +#if 1 + cmd &= ~PCIM_CMD_MWRICEN; +#endif + pci_write_config(self, PCIR_COMMAND, cmd, 2); + + latency = olatency = pci_read_config(self, PCIR_LATTIMER, 1); +#define DEF_LATENCY 0x20 + if (olatency < DEF_LATENCY) { + latency = DEF_LATENCY; + pci_write_config(self, PCIR_LATTIMER, latency, 1); + } + + cache_line = ocache_line = pci_read_config(self, PCIR_CACHELNSZ, 1); +#define DEF_CACHE_LINE 8 + if (ocache_line < DEF_CACHE_LINE) { + cache_line = DEF_CACHE_LINE; + pci_write_config(self, PCIR_CACHELNSZ, cache_line, 1); + } + + if (firewire_debug) { + device_printf(self, "latency timer %d -> %d.\n", + olatency, latency); + device_printf(self, "cache size %d -> %d.\n", + ocache_line, cache_line); + } + + return 0; +} + +static int +fwohci_pci_attach(device_t self) +{ + fwohci_softc_t *sc = device_get_softc(self); + int err; + int rid, s; +#if __FreeBSD_version < 500000 + int intr; + /* For the moment, put in a message stating what is wrong */ + intr = pci_read_config(self, PCIR_INTLINE, 1); + if (intr == 0 || intr == 255) { + device_printf(self, "Invalid irq %d\n", intr); +#ifdef __i386__ + device_printf(self, "Please switch PNP-OS to 'No' in BIOS\n"); +#endif + } +#endif + + if (bootverbose) + firewire_debug = bootverbose; + + fwohci_pci_init(self); + + rid = PCI_CBMEM; + sc->bsr = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->bsr) { + device_printf(self, "Could not map memory\n"); + return ENXIO; + } + + sc->bst = rman_get_bustag(sc->bsr); + sc->bsh = rman_get_bushandle(sc->bsr); + + rid = 0; + sc->irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + fwohci_pci_detach(self); + return ENXIO; + } + + sc->fc.bdev = device_add_child(self, "firewire", -1); + if (!sc->fc.bdev) { + device_printf(self, "Could not add firewire device\n"); + fwohci_pci_detach(self); + return ENOMEM; + } + device_set_ivars(sc->fc.bdev, sc); + + err = bus_setup_intr(self, sc->irq_res, +#if FWOHCI_TASKQUEUE + INTR_TYPE_NET | INTR_MPSAFE, +#else + INTR_TYPE_NET, +#endif + (driver_intr_t *) fwohci_intr, sc, &sc->ih); +#if __FreeBSD_version < 500000 + /* XXX splcam() should mask this irq for sbp.c*/ + err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_CAM, + (driver_intr_t *) fwohci_dummy_intr, sc, &sc->ih_cam); +#endif + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + fwohci_pci_detach(self); + return ENXIO; + } + + err = bus_dma_tag_create(/*parent*/NULL, /*alignment*/1, + /*boundary*/0, +#if BOUNCE_BUFFER_TEST + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, +#else + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, +#endif + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/0x100000, + /*nsegments*/0x20, + /*maxsegsz*/0x8000, + /*flags*/BUS_DMA_ALLOCNOW, + &sc->fc.dmat); + if (err != 0) { + printf("fwohci_pci_attach: Could not allocate DMA tag " + "- error %d\n", err); + return (ENOMEM); + } + + err = fwohci_init(sc, self); + + if (!err) + err = device_probe_and_attach(sc->fc.bdev); + + if (err) { + device_printf(self, "FireWire init failed\n"); + fwohci_pci_detach(self); + return EIO; + } + + /* XXX + * Clear the bus reset event flag to start transactions even when + * interrupt is disabled during the boot process. + */ + s = splfw(); + fwohci_intr((void *)sc); + splx(s); + + return 0; +} + +static int +fwohci_pci_detach(device_t self) +{ + fwohci_softc_t *sc = device_get_softc(self); + int s; + + + s = splfw(); + + fwohci_stop(sc, self); + bus_generic_detach(self); + + /* disable interrupts that might have been switched on */ + if (sc->bst && sc->bsh) + bus_space_write_4(sc->bst, sc->bsh, + FWOHCI_INTMASKCLR, OHCI_INT_EN); + + if (sc->irq_res) { + int err = bus_teardown_intr(self, sc->irq_res, sc->ih); + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); +#if __FreeBSD_version < 500000 + err = bus_teardown_intr(self, sc->irq_res, sc->ih_cam); +#endif + sc->ih = NULL; + } + + if (sc->fc.bdev) { + device_delete_child(self, sc->fc.bdev); + sc->fc.bdev = NULL; + } + + if (sc->irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); + sc->irq_res = NULL; + } + + if (sc->bsr) { + bus_release_resource(self, SYS_RES_MEMORY,PCI_CBMEM,sc->bsr); + sc->bsr = NULL; + sc->bst = 0; + sc->bsh = 0; + } + + fwohci_detach(sc, self); + splx(s); + + return 0; +} + +static int +fwohci_pci_suspend(device_t dev) +{ + int err; + + device_printf(dev, "fwohci_pci_suspend\n"); + err = bus_generic_suspend(dev); + if (err) + return err; + /* fwohci_stop(dev); */ + return 0; +} + +static int +fwohci_pci_resume(device_t dev) +{ + fwohci_softc_t *sc = device_get_softc(dev); + + device_printf(dev, "fwohci_pci_resume: power_state = 0x%08x\n", + pci_get_powerstate(dev)); + fwohci_pci_init(dev); + fwohci_resume(sc, dev); + return 0; +} + +static int +fwohci_pci_shutdown(device_t dev) +{ + fwohci_softc_t *sc = device_get_softc(dev); + + bus_generic_shutdown(dev); + fwohci_stop(sc, dev); + return 0; +} + +static device_method_t fwohci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fwohci_pci_probe), + DEVMETHOD(device_attach, fwohci_pci_attach), + DEVMETHOD(device_detach, fwohci_pci_detach), + DEVMETHOD(device_suspend, fwohci_pci_suspend), + DEVMETHOD(device_resume, fwohci_pci_resume), + DEVMETHOD(device_shutdown, fwohci_pci_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + { 0, 0 } +}; + +static driver_t fwohci_driver = { + "fwohci", + fwohci_methods, + sizeof(fwohci_softc_t), +}; + +static devclass_t fwohci_devclass; + +DRIVER_MODULE(fwohci, pci, fwohci_driver, fwohci_devclass, 0, 0); +DRIVER_MODULE(fwohci, cardbus, fwohci_driver, fwohci_devclass, 0, 0); diff --git a/sys/dev/firewire/fwohcireg.h b/sys/dev/firewire/fwohcireg.h new file mode 100644 index 0000000..7ce2087 --- /dev/null +++ b/sys/dev/firewire/fwohcireg.h @@ -0,0 +1,431 @@ +/* + * 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 PCI_CBMEM 0x10 + +#define FW_VENDORID_NEC 0x1033 +#define FW_VENDORID_TI 0x104c +#define FW_VENDORID_SONY 0x104d +#define FW_VENDORID_VIA 0x1106 +#define FW_VENDORID_RICOH 0x1180 +#define FW_VENDORID_APPLE 0x106b +#define FW_VENDORID_LUCENT 0x11c1 + +#define FW_DEVICE_UPD861 (0x0063 << 16) +#define FW_DEVICE_UPD871 (0x00ce << 16) +#define FW_DEVICE_UPD72870 (0x00cd << 16) +#define FW_DEVICE_UPD72874 (0x00f2 << 16) +#define FW_DEVICE_TITSB22 (0x8009 << 16) +#define FW_DEVICE_TITSB23 (0x8019 << 16) +#define FW_DEVICE_TITSB26 (0x8020 << 16) +#define FW_DEVICE_TITSB43 (0x8021 << 16) +#define FW_DEVICE_TITSB43A (0x8023 << 16) +#define FW_DEVICE_TITSB43AB23 (0x8024 << 16) +#define FW_DEVICE_TITSB82AA2 (0x8025 << 16) +#define FW_DEVICE_TIPCI4410A (0x8017 << 16) +#define FW_DEVICE_TIPCI4450 (0x8011 << 16) +#define FW_DEVICE_TIPCI4451 (0x8027 << 16) +#define FW_DEVICE_CX3022 (0x8039 << 16) +#define FW_DEVICE_VT6306 (0x3044 << 16) +#define FW_DEVICE_R5C551 (0x0551 << 16) +#define FW_DEVICE_R5C552 (0x0552 << 16) +#define FW_DEVICE_PANGEA (0x0030 << 16) +#define FW_DEVICE_UNINORTH (0x0031 << 16) +#define FW_DEVICE_FW322 (0x5811 << 16) + +#define PCI_INTERFACE_OHCI 0x10 + +#define FW_OHCI_BASE_REG 0x10 + +#define OHCI_DMA_ITCH 0x20 +#define OHCI_DMA_IRCH 0x20 + +#define OHCI_MAX_DMA_CH (0x4 + OHCI_DMA_ITCH + OHCI_DMA_IRCH) + + +typedef volatile u_int32_t fwohcireg_t; + +/* for PCI */ +#if BYTE_ORDER == BIG_ENDIAN +#define FWOHCI_DMA_WRITE(x, y) ((x) = htole32(y)) +#define FWOHCI_DMA_READ(x) le32toh(x) +#define FWOHCI_DMA_SET(x, y) ((x) |= htole32(y)) +#define FWOHCI_DMA_CLEAR(x, y) ((x) &= htole32(~(y))) +#else +#define FWOHCI_DMA_WRITE(x, y) ((x) = (y)) +#define FWOHCI_DMA_READ(x) (x) +#define FWOHCI_DMA_SET(x, y) ((x) |= (y)) +#define FWOHCI_DMA_CLEAR(x, y) ((x) &= ~(y)) +#endif + +struct fwohcidb { + union { + struct { + volatile u_int32_t cmd; + volatile u_int32_t addr; + volatile u_int32_t depend; + volatile u_int32_t res; + } desc; + volatile u_int32_t immed[4]; + } db; +#define OHCI_STATUS_SHIFT 16 +#define OHCI_COUNT_MASK 0xffff +#define OHCI_OUTPUT_MORE (0 << 28) +#define OHCI_OUTPUT_LAST (1 << 28) +#define OHCI_INPUT_MORE (2 << 28) +#define OHCI_INPUT_LAST (3 << 28) +#define OHCI_STORE_QUAD (4 << 28) +#define OHCI_LOAD_QUAD (5 << 28) +#define OHCI_NOP (6 << 28) +#define OHCI_STOP (7 << 28) +#define OHCI_STORE (8 << 28) +#define OHCI_CMD_MASK (0xf << 28) + +#define OHCI_UPDATE (1 << 27) + +#define OHCI_KEY_ST0 (0 << 24) +#define OHCI_KEY_ST1 (1 << 24) +#define OHCI_KEY_ST2 (2 << 24) +#define OHCI_KEY_ST3 (3 << 24) +#define OHCI_KEY_REGS (5 << 24) +#define OHCI_KEY_SYS (6 << 24) +#define OHCI_KEY_DEVICE (7 << 24) +#define OHCI_KEY_MASK (7 << 24) + +#define OHCI_INTERRUPT_NEVER (0 << 20) +#define OHCI_INTERRUPT_TRUE (1 << 20) +#define OHCI_INTERRUPT_FALSE (2 << 20) +#define OHCI_INTERRUPT_ALWAYS (3 << 20) + +#define OHCI_BRANCH_NEVER (0 << 18) +#define OHCI_BRANCH_TRUE (1 << 18) +#define OHCI_BRANCH_FALSE (2 << 18) +#define OHCI_BRANCH_ALWAYS (3 << 18) +#define OHCI_BRANCH_MASK (3 << 18) + +#define OHCI_WAIT_NEVER (0 << 16) +#define OHCI_WAIT_TRUE (1 << 16) +#define OHCI_WAIT_FALSE (2 << 16) +#define OHCI_WAIT_ALWAYS (3 << 16) +}; + +#define OHCI_SPD_S100 0x4 +#define OHCI_SPD_S200 0x1 +#define OHCI_SPD_S400 0x2 + + +#define FWOHCIEV_NOSTAT 0 +#define FWOHCIEV_LONGP 2 +#define FWOHCIEV_MISSACK 3 +#define FWOHCIEV_UNDRRUN 4 +#define FWOHCIEV_OVRRUN 5 +#define FWOHCIEV_DESCERR 6 +#define FWOHCIEV_DTRDERR 7 +#define FWOHCIEV_DTWRERR 8 +#define FWOHCIEV_BUSRST 9 +#define FWOHCIEV_TIMEOUT 0xa +#define FWOHCIEV_TCODERR 0xb +#define FWOHCIEV_UNKNOWN 0xe +#define FWOHCIEV_FLUSHED 0xf +#define FWOHCIEV_ACKCOMPL 0x11 +#define FWOHCIEV_ACKPEND 0x12 +#define FWOHCIEV_ACKBSX 0x14 +#define FWOHCIEV_ACKBSA 0x15 +#define FWOHCIEV_ACKBSB 0x16 +#define FWOHCIEV_ACKTARD 0x1b +#define FWOHCIEV_ACKDERR 0x1d +#define FWOHCIEV_ACKTERR 0x1e + +#define FWOHCIEV_MASK 0x1f + +struct ohci_registers { + fwohcireg_t ver; /* Version No. 0x0 */ + fwohcireg_t guid; /* GUID_ROM No. 0x4 */ + fwohcireg_t retry; /* AT retries 0x8 */ +#define FWOHCI_RETRY 0x8 + fwohcireg_t csr_data; /* CSR data 0xc */ + fwohcireg_t csr_cmp; /* CSR compare 0x10 */ + fwohcireg_t csr_cntl; /* CSR compare 0x14 */ + fwohcireg_t rom_hdr; /* config ROM ptr. 0x18 */ + fwohcireg_t bus_id; /* BUS_ID 0x1c */ + fwohcireg_t bus_opt; /* BUS option 0x20 */ +#define FWOHCIGUID_H 0x24 +#define FWOHCIGUID_L 0x28 + fwohcireg_t guid_hi; /* GUID hi 0x24 */ + fwohcireg_t guid_lo; /* GUID lo 0x28 */ + fwohcireg_t dummy0[2]; /* dummy 0x2c-0x30 */ + fwohcireg_t config_rom; /* config ROM map 0x34 */ + fwohcireg_t post_wr_lo; /* post write addr lo 0x38 */ + fwohcireg_t post_wr_hi; /* post write addr hi 0x3c */ + fwohcireg_t vender; /* vender ID 0x40 */ + fwohcireg_t dummy1[3]; /* dummy 0x44-0x4c */ + fwohcireg_t hcc_cntl_set; /* HCC control set 0x50 */ + fwohcireg_t hcc_cntl_clr; /* HCC control clr 0x54 */ +#define OHCI_HCC_BIBIV (1 << 31) /* BIBimage Valid */ +#define OHCI_HCC_BIGEND (1 << 30) /* noByteSwapData */ +#define OHCI_HCC_PRPHY (1 << 23) /* programPhyEnable */ +#define OHCI_HCC_PHYEN (1 << 22) /* aPhyEnhanceEnable */ +#define OHCI_HCC_LPS (1 << 19) /* LPS */ +#define OHCI_HCC_POSTWR (1 << 18) /* postedWriteEnable */ +#define OHCI_HCC_LINKEN (1 << 17) /* linkEnable */ +#define OHCI_HCC_RESET (1 << 16) /* softReset */ + fwohcireg_t dummy2[2]; /* dummy 0x58-0x5c */ + fwohcireg_t dummy3[1]; /* dummy 0x60 */ + fwohcireg_t sid_buf; /* self id buffer 0x64 */ + fwohcireg_t sid_cnt; /* self id count 0x68 */ + fwohcireg_t dummy4[1]; /* dummy 0x6c */ + fwohcireg_t ir_mask_hi_set; /* ir mask hi set 0x70 */ + fwohcireg_t ir_mask_hi_clr; /* ir mask hi set 0x74 */ + fwohcireg_t ir_mask_lo_set; /* ir mask hi set 0x78 */ + fwohcireg_t ir_mask_lo_clr; /* ir mask hi set 0x7c */ +#define FWOHCI_INTSTAT 0x80 +#define FWOHCI_INTSTATCLR 0x84 +#define FWOHCI_INTMASK 0x88 +#define FWOHCI_INTMASKCLR 0x8c + fwohcireg_t int_stat; /* 0x80 */ + fwohcireg_t int_clear; /* 0x84 */ + fwohcireg_t int_mask; /* 0x88 */ + fwohcireg_t int_mask_clear; /* 0x8c */ + fwohcireg_t it_int_stat; /* 0x90 */ + fwohcireg_t it_int_clear; /* 0x94 */ + fwohcireg_t it_int_mask; /* 0x98 */ + fwohcireg_t it_mask_clear; /* 0x9c */ + fwohcireg_t ir_int_stat; /* 0xa0 */ + fwohcireg_t ir_int_clear; /* 0xa4 */ + fwohcireg_t ir_int_mask; /* 0xa8 */ + fwohcireg_t ir_mask_clear; /* 0xac */ + fwohcireg_t dummy5[11]; /* dummy 0xb0-d8 */ + fwohcireg_t fairness; /* fairness control 0xdc */ + fwohcireg_t link_cntl; /* Chip control 0xe0*/ + fwohcireg_t link_cntl_clr; /* Chip control clear 0xe4*/ +#define FWOHCI_NODEID 0xe8 + fwohcireg_t node; /* Node ID 0xe8 */ +#define OHCI_NODE_VALID (1 << 31) +#define OHCI_NODE_ROOT (1 << 30) + +#define OHCI_ASYSRCBUS 1 + + fwohcireg_t phy_access; /* PHY cntl 0xec */ +#define PHYDEV_RDDONE (1<<31) +#define PHYDEV_RDCMD (1<<15) +#define PHYDEV_WRCMD (1<<14) +#define PHYDEV_REGADDR 8 +#define PHYDEV_WRDATA 0 +#define PHYDEV_RDADDR 24 +#define PHYDEV_RDDATA 16 + + fwohcireg_t cycle_timer; /* Cycle Timer 0xf0 */ + fwohcireg_t dummy6[3]; /* dummy 0xf4-fc */ + fwohcireg_t areq_hi; /* Async req. filter hi 0x100 */ + fwohcireg_t areq_hi_clr; /* Async req. filter hi 0x104 */ + fwohcireg_t areq_lo; /* Async req. filter lo 0x108 */ + fwohcireg_t areq_lo_clr; /* Async req. filter lo 0x10c */ + fwohcireg_t preq_hi; /* Async req. filter hi 0x110 */ + fwohcireg_t preq_hi_clr; /* Async req. filter hi 0x114 */ + fwohcireg_t preq_lo; /* Async req. filter lo 0x118 */ + fwohcireg_t preq_lo_clr; /* Async req. filter lo 0x11c */ + + fwohcireg_t pys_upper; /* Physical Upper bound 0x120 */ + + fwohcireg_t dummy7[23]; /* dummy 0x124-0x17c */ + + struct ohci_dma{ + fwohcireg_t cntl; + +#define OHCI_CNTL_CYCMATCH_S (0x1 << 31) + +#define OHCI_CNTL_BUFFIL (0x1 << 31) +#define OHCI_CNTL_ISOHDR (0x1 << 30) +#define OHCI_CNTL_CYCMATCH_R (0x1 << 29) +#define OHCI_CNTL_MULTICH (0x1 << 28) + +#define OHCI_CNTL_DMA_RUN (0x1 << 15) +#define OHCI_CNTL_DMA_WAKE (0x1 << 12) +#define OHCI_CNTL_DMA_DEAD (0x1 << 11) +#define OHCI_CNTL_DMA_ACTIVE (0x1 << 10) +#define OHCI_CNTL_DMA_BT (0x1 << 8) +#define OHCI_CNTL_DMA_BAD (0x1 << 7) +#define OHCI_CNTL_DMA_STAT (0xff) + + fwohcireg_t cntl_clr; + fwohcireg_t dummy0; + fwohcireg_t cmd; + fwohcireg_t match; + fwohcireg_t dummy1; + fwohcireg_t dummy2; + fwohcireg_t dummy3; + }; + /* 0x180, 0x184, 0x188, 0x18c */ + /* 0x190, 0x194, 0x198, 0x19c */ + /* 0x1a0, 0x1a4, 0x1a8, 0x1ac */ + /* 0x1b0, 0x1b4, 0x1b8, 0x1bc */ + /* 0x1c0, 0x1c4, 0x1c8, 0x1cc */ + /* 0x1d0, 0x1d4, 0x1d8, 0x1dc */ + /* 0x1e0, 0x1e4, 0x1e8, 0x1ec */ + /* 0x1f0, 0x1f4, 0x1f8, 0x1fc */ + struct ohci_dma dma_ch[0x4]; + + /* 0x200, 0x204, 0x208, 0x20c */ + /* 0x210, 0x204, 0x208, 0x20c */ + struct ohci_itdma{ + fwohcireg_t cntl; + fwohcireg_t cntl_clr; + fwohcireg_t dummy0; + fwohcireg_t cmd; + }; + struct ohci_itdma dma_itch[0x20]; + + /* 0x400, 0x404, 0x408, 0x40c */ + /* 0x410, 0x404, 0x408, 0x40c */ + + struct ohci_dma dma_irch[0x20]; +}; + +struct fwohcidb_tr{ + STAILQ_ENTRY(fwohcidb_tr) link; + struct fw_xfer *xfer; + volatile struct fwohcidb *db; + bus_dmamap_t dma_map; + caddr_t buf; + bus_addr_t bus_addr; + int dbcnt; +}; + +/* + * OHCI info structure. + */ +struct fwohci_txpkthdr{ + union{ + u_int32_t ld[4]; + struct { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t :13, + spd:3, + :8, + tcode:4, + :4; +#else + u_int32_t :4, + tcode:4, + :8, + spd:3, + :13; +#endif + }common; + struct { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t :8, + srcbus:1, + :4, + spd:3, + tlrt:8, + tcode:4, + :4; +#else + u_int32_t :4, + tcode:4, + tlrt:8, + spd:3, + :4, + srcbus:1, + :8; +#endif + BIT16x2(dst, ); + }asycomm; + struct { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t :13, + spd:3, + chtag:8, + tcode:4, + sy:4; +#else + u_int32_t sy:4, + tcode:4, + chtag:8, + spd:3, + :13; +#endif + BIT16x2(len, ); + }stream; + }mode; +}; +struct fwohci_trailer{ + u_int32_t time:16, + stat:16; +}; + +#define OHCI_CNTL_CYCSRC (0x1 << 22) +#define OHCI_CNTL_CYCMTR (0x1 << 21) +#define OHCI_CNTL_CYCTIMER (0x1 << 20) +#define OHCI_CNTL_PHYPKT (0x1 << 10) +#define OHCI_CNTL_SID (0x1 << 9) + +#define OHCI_INT_DMA_ATRQ (0x1 << 0) +#define OHCI_INT_DMA_ATRS (0x1 << 1) +#define OHCI_INT_DMA_ARRQ (0x1 << 2) +#define OHCI_INT_DMA_ARRS (0x1 << 3) +#define OHCI_INT_DMA_PRRQ (0x1 << 4) +#define OHCI_INT_DMA_PRRS (0x1 << 5) +#define OHCI_INT_DMA_IT (0x1 << 6) +#define OHCI_INT_DMA_IR (0x1 << 7) +#define OHCI_INT_PW_ERR (0x1 << 8) +#define OHCI_INT_LR_ERR (0x1 << 9) + +#define OHCI_INT_PHY_SID (0x1 << 16) +#define OHCI_INT_PHY_BUS_R (0x1 << 17) + +#define OHCI_INT_REG_FAIL (0x1 << 18) + +#define OHCI_INT_PHY_INT (0x1 << 19) +#define OHCI_INT_CYC_START (0x1 << 20) +#define OHCI_INT_CYC_64SECOND (0x1 << 21) +#define OHCI_INT_CYC_LOST (0x1 << 22) +#define OHCI_INT_CYC_ERR (0x1 << 23) + +#define OHCI_INT_ERR (0x1 << 24) +#define OHCI_INT_CYC_LONG (0x1 << 25) +#define OHCI_INT_PHY_REG (0x1 << 26) + +#define OHCI_INT_EN (0x1 << 31) + +#define IP_CHANNELS 0x0234 +#define FWOHCI_MAXREC 2048 + +#define OHCI_ISORA 0x02 +#define OHCI_ISORB 0x04 + +#define FWOHCITCODE_PHY 0xe diff --git a/sys/dev/firewire/fwohcivar.h b/sys/dev/firewire/fwohcivar.h new file mode 100644 index 0000000..b68e9e2 --- /dev/null +++ b/sys/dev/firewire/fwohcivar.h @@ -0,0 +1,90 @@ +/* + * 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$ + * + */ + +#if __FreeBSD_version >= 500000 +#define FWOHCI_TASKQUEUE 1 +#else +#define FWOHCI_TASKQUEUE 0 +#endif +#if FWOHCI_TASKQUEUE +#include <sys/taskqueue.h> +#endif + +typedef struct fwohci_softc { + struct firewire_comm fc; + bus_space_tag_t bst; + bus_space_handle_t bsh; + void *ih; +#if __FreeBSD_version < 500000 + void *ih_cam; +#endif + struct resource *bsr; + struct resource *irq_res; + struct fwohci_dbch{ + u_int ndb; + u_int ndesc; + STAILQ_HEAD(, fwohcidb_tr) db_trq; + struct fwohcidb_tr *top, *bottom, *pdb_tr; + struct fw_xferq xferq; + int flags; +#define FWOHCI_DBCH_INIT (1<<0) +#define FWOHCI_DBCH_FULL (1<<1) + /* used only in receive context */ + int buf_offset; /* signed */ +#define FWOHCI_DBCH_MAX_PAGES 32 + /* Context programs buffer */ + struct fwdma_alloc_multi *am; + bus_dma_tag_t dmat; + } arrq, arrs, atrq, atrs, it[OHCI_DMA_ITCH], ir[OHCI_DMA_IRCH]; + u_int maxrec; + u_int32_t *sid_buf; + struct fwdma_alloc sid_dma; + struct fwdma_alloc crom_dma; + struct fwdma_alloc dummy_dma; + u_int32_t intmask, irstat, itstat; +#if FWOHCI_TASKQUEUE + u_int32_t intstat; + struct task fwohci_task_complete; +#endif +} fwohci_softc_t; + +void fwohci_intr __P((void *arg)); +int fwohci_init __P((struct fwohci_softc *, device_t)); +void fwohci_reset __P((struct fwohci_softc *, device_t)); +int fwohci_detach __P((struct fwohci_softc *, device_t)); +int fwohci_resume __P((struct fwohci_softc *, device_t)); +int fwohci_stop __P((struct fwohci_softc *, device_t dev)); diff --git a/sys/dev/firewire/iec13213.h b/sys/dev/firewire/iec13213.h new file mode 100644 index 0000000..56d02a6 --- /dev/null +++ b/sys/dev/firewire/iec13213.h @@ -0,0 +1,251 @@ +/* + * 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 STATE_CLEAR 0x0000 +#define STATE_SET 0x0004 +#define NODE_IDS 0x0008 +#define RESET_START 0x000c +#define SPLIT_TIMEOUT_HI 0x0018 +#define SPLIT_TIMEOUT_LO 0x001c +#define CYCLE_TIME 0x0200 +#define BUS_TIME 0x0204 +#define BUSY_TIMEOUT 0x0210 +#define PRIORITY_BUDGET 0x0218 +#define BUS_MGR_ID 0x021c +#define BANDWIDTH_AV 0x0220 +#define CHANNELS_AV_HI 0x0224 +#define CHANNELS_AV_LO 0x0228 +#define IP_CHANNELS 0x0234 + +#define CONF_ROM 0x0400 + +#define TOPO_MAP 0x1000 +#define SPED_MAP 0x2000 + +#define CSRTYPE_SHIFT 6 +#define CSRTYPE_MASK (3 << CSRTYPE_SHIFT) +#define CSRTYPE_I (0 << CSRTYPE_SHIFT) /* Immediate */ +#define CSRTYPE_C (1 << CSRTYPE_SHIFT) /* CSR offset */ +#define CSRTYPE_L (2 << CSRTYPE_SHIFT) /* Leaf */ +#define CSRTYPE_D (3 << CSRTYPE_SHIFT) /* Directory */ + +/* + * CSR keys + * 00 - 2F: defined by CSR architecture standards. + * 30 - 37: defined by BUS starndards + * 38 - 3F: defined by Vendor/Specifier + */ +#define CSRKEY_MASK 0x3f +#define CSRKEY_DESC 0x01 /* Descriptor */ +#define CSRKEY_BDINFO 0x02 /* Bus_Dependent_Info */ +#define CSRKEY_VENDOR 0x03 /* Vendor */ +#define CSRKEY_HW 0x04 /* Hardware_Version */ +#define CSRKEY_MODULE 0x07 /* Module */ +#define CSRKEY_NCAP 0x0c /* Node_Capabilities */ +#define CSRKEY_EUI64 0x0d /* EUI_64 */ +#define CSRKEY_UNIT 0x11 /* Unit */ +#define CSRKEY_SPEC 0x12 /* Specifier_ID */ +#define CSRKEY_VER 0x13 /* Version */ +#define CSRKEY_DINFO 0x14 /* Dependent_Info */ +#define CSRKEY_ULOC 0x15 /* Unit_Location */ +#define CSRKEY_MODEL 0x17 /* Model */ +#define CSRKEY_INST 0x18 /* Instance */ +#define CSRKEY_KEYW 0x19 /* Keyword */ +#define CSRKEY_FEAT 0x1a /* Feature */ +#define CSRKEY_EROM 0x1b /* Extended_ROM */ +#define CSRKEY_EKSID 0x1c /* Extended_Key_Specifier_ID */ +#define CSRKEY_EKEY 0x1d /* Extended_Key */ +#define CSRKEY_EDATA 0x1e /* Extended_Data */ +#define CSRKEY_MDESC 0x1f /* Modifiable_Descriptor */ +#define CSRKEY_DID 0x20 /* Directory_ID */ +#define CSRKEY_REV 0x21 /* Revision */ + +#define CSRKEY_FIRM_VER 0x3c /* Firemware version */ +#define CSRKEY_UNIT_CH 0x3a /* Unit characteristics */ +#define CSRKEY_COM_SPEC 0x38 /* Command set revision */ +#define CSRKEY_COM_SET 0x39 /* Command set */ + +#define CROM_UDIR (CSRTYPE_D | CSRKEY_UNIT) /* 0x81 Unit directory */ +#define CROM_TEXTLEAF (CSRTYPE_L | CSRKEY_DESC) /* 0x81 Text leaf */ +#define CROM_LUN (CSRTYPE_I | CSRKEY_DINFO) /* 0x14 Logical unit num. */ +#define CROM_MGM (CSRTYPE_C | CSRKEY_DINFO) /* 0x54 Management agent */ + +#define CSRVAL_VENDOR_PRIVATE 0xacde48 +#define CSRVAL_1394TA 0x00a02d +#define CSRVAL_ANSIT10 0x00609e +#define CSRVAL_IETF 0x00005e + +#define CSR_PROTAVC 0x010001 +#define CSR_PROTCAL 0x010002 +#define CSR_PROTEHS 0x010004 +#define CSR_PROTHAVI 0x010008 +#define CSR_PROTCAM104 0x000100 +#define CSR_PROTCAM120 0x000101 +#define CSR_PROTCAM130 0x000102 +#define CSR_PROTDPP 0x0a6be2 +#define CSR_PROTIICP 0x4b661f + +#define CSRVAL_T10SBP2 0x010483 +#define CSRVAL_SCSI 0x0104d8 + +struct csrreg { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t key:8, + val:24; +#else + u_int32_t val:24, + key:8; +#endif +}; +struct csrhdr { +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t info_len:8, + crc_len:8, + crc:16; +#else + u_int32_t crc:16, + crc_len:8, + info_len:8; +#endif +}; +struct csrdirectory { + BIT16x2(crc_len, crc); + struct csrreg entry[0]; +}; +struct csrtext { + BIT16x2(crc_len, crc); +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t spec_type:8, + spec_id:24; +#else + u_int32_t spec_id:24, + spec_type:8; +#endif + u_int32_t lang_id; + u_int32_t text[0]; +}; + +struct bus_info { +#define CSR_BUS_NAME_IEEE1394 0x31333934 + u_int32_t bus_name; +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t irmc:1, /* iso. resource manager capable */ + cmc:1, /* cycle master capable */ + isc:1, /* iso. operation support */ + bmc:1, /* bus manager capable */ + pmc:1, /* power manager capable */ + :3, + cyc_clk_acc:8, /* 0 <= ppm <= 100 */ + max_rec:4, /* (2 << max_rec) bytes */ + :2, + max_rom:2, + generation:4, + :1, + link_spd:3; +#else + u_int32_t link_spd:3, + :1, + generation:4, + max_rom:2, + :2, + max_rec:4, /* (2 << max_rec) bytes */ + cyc_clk_acc:8, /* 0 <= ppm <= 100 */ + :3, + pmc:1, /* power manager capable */ + bmc:1, /* bus manager capable */ + isc:1, /* iso. operation support */ + cmc:1, /* cycle master capable */ + irmc:1; /* iso. resource manager capable */ +#endif + struct fw_eui64 eui64; +}; +/* max_rom */ +#define MAXROM_4 0 +#define MAXROM_64 1 +#define MAXROM_1024 2 + +#define CROM_MAX_DEPTH 10 +struct crom_ptr { + struct csrdirectory *dir; + int index; +}; + +struct crom_context { + int depth; + struct crom_ptr stack[CROM_MAX_DEPTH]; +}; + +void crom_init_context(struct crom_context *, u_int32_t *); +struct csrreg *crom_get(struct crom_context *); +void crom_next(struct crom_context *); +void crom_parse_text(struct crom_context *, char *, int); +u_int16_t crom_crc(u_int32_t *r, int); +struct csrreg *crom_search_key(struct crom_context *, u_int8_t); +int crom_has_specver(u_int32_t *, u_int32_t, u_int32_t); + +#ifndef _KERNEL +char *crom_desc(struct crom_context *, char *, int); +#endif + +/* For CROM build */ +#if defined(_KERNEL) || defined(TEST) +#define CROM_MAX_CHUNK_LEN 20 +struct crom_src { + struct csrhdr hdr; + struct bus_info businfo; + STAILQ_HEAD(, crom_chunk) chunk_list; +}; + +struct crom_chunk { + STAILQ_ENTRY(crom_chunk) link; + struct crom_chunk *ref_chunk; + int ref_index; + int offset; + struct { + BIT16x2(crc_len, crc); + u_int32_t buf[CROM_MAX_CHUNK_LEN]; + } data; +}; + +extern int crom_add_quad(struct crom_chunk *, u_int32_t); +extern int crom_add_entry(struct crom_chunk *, int, int); +extern int crom_add_chunk(struct crom_src *src, struct crom_chunk *, + struct crom_chunk *, int); +extern int crom_add_simple_text(struct crom_src *src, struct crom_chunk *, + struct crom_chunk *, char *); +extern int crom_load(struct crom_src *, u_int32_t *, int); +#endif diff --git a/sys/dev/firewire/iec68113.h b/sys/dev/firewire/iec68113.h new file mode 100644 index 0000000..7b7c188 --- /dev/null +++ b/sys/dev/firewire/iec68113.h @@ -0,0 +1,127 @@ +/* + * 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 DV_BROADCAST_ON (1<<30) +#define oMPR 0x900 +#define oPCR 0x904 +#define iMPR 0x980 +#define iPCR 0x984 + +struct ciphdr { +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t eoh0:1, /* 0 */ + form0:1, /* 0 */ + src:6; +#else + u_int8_t src:6, + form0:1, /* 0 */ + eoh0:1; /* 0 */ +#endif + u_int8_t len; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t fn:2, + qpc:3, + sph:1, + :2; +#else + u_int8_t :2, + sph:1, + qpc:3, + fn:2; +#endif + u_int8_t dbc; +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t eoh1:1, /* 1 */ + form1:1, /* 0 */ + fmt:6; +#else + u_int8_t fmt:6, + form1:1, /* 0 */ + eoh1:1; /* 1 */ +#endif +#define CIP_FMT_DVCR 0 +#define CIP_FMT_MPEG (1<<5) + union { + struct { +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t fs:1, /* 50/60 field system + NTSC/PAL */ + stype:5, + :2; +#else + u_int8_t :2, + stype:5, + fs:1; /* 50/60 field system + NTSC/PAL */ +#endif +#define CIP_STYPE_SD 0 +#define CIP_STYPE_SDL 1 +#define CIP_STYPE_HD 2 + u_int16_t cyc:16; /* take care of byte order! */ + } __attribute__ ((packed)) dv; + u_int8_t bytes[3]; + } fdf; + +}; +struct dvdbc{ +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t sct:3, /* Section type */ + :1, /* Reserved */ + arb:4; /* Arbitrary bit */ +#else + u_int8_t arb:4, /* Arbitrary bit */ + :1, /* Reserved */ + sct:3; /* Section type */ +#endif +#define DV_SCT_HEADER 0 +#define DV_SCT_SUBCODE 1 +#define DV_SCT_VAUX 2 +#define DV_SCT_AUDIO 3 +#define DV_SCT_VIDEO 4 +#if BYTE_ORDER == BIG_ENDIAN + u_int8_t dseq:4, /* DIF sequence number */ + fsc:1, /* ID of a DIF block in each channel */ + :3; +#else + u_int8_t :3, + fsc:1, /* ID of a DIF block in each channel */ + dseq:4; /* DIF sequence number */ +#endif + u_int8_t dbn; /* DIF block number */ + u_int8_t payload[77]; +#define DV_DSF_12 0x80 /* PAL: payload[0] in Header DIF */ +}; diff --git a/sys/dev/firewire/if_fwe.c b/sys/dev/firewire/if_fwe.c new file mode 100644 index 0000000..d029533 --- /dev/null +++ b/sys/dev/firewire/if_fwe.c @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2002-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 "opt_inet.h" + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <machine/bus.h> + +#include <net/bpf.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_vlan_var.h> +#include <net/route.h> + +#include <netinet/in.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/if_fwevar.h> + +#define FWEDEBUG if (fwedebug) printf +#define TX_MAX_QUEUE (FWMAXQUEUE - 1) +#define RX_MAX_QUEUE FWMAXQUEUE + +/* network interface */ +static void fwe_start __P((struct ifnet *)); +static int fwe_ioctl __P((struct ifnet *, u_long, caddr_t)); +static void fwe_init __P((void *)); + +static void fwe_output_callback __P((struct fw_xfer *)); +static void fwe_as_output __P((struct fwe_softc *, struct ifnet *)); +static void fwe_as_input __P((struct fw_xferq *)); + +static int fwedebug = 0; +static int stream_ch = 1; +static int tx_speed = 2; + +MALLOC_DEFINE(M_FWE, "if_fwe", "Ethernet over FireWire interface"); +SYSCTL_INT(_debug, OID_AUTO, if_fwe_debug, CTLFLAG_RW, &fwedebug, 0, ""); +SYSCTL_DECL(_hw_firewire); +SYSCTL_NODE(_hw_firewire, OID_AUTO, fwe, CTLFLAG_RD, 0, + "Ethernet Emulation Subsystem"); +SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, stream_ch, CTLFLAG_RW, &stream_ch, 0, + "Stream channel to use"); +SYSCTL_INT(_hw_firewire_fwe, OID_AUTO, tx_speed, CTLFLAG_RW, &tx_speed, 0, + "Transmission Speed"); + +#ifdef DEVICE_POLLING +#define FWE_POLL_REGISTER(func, fwe, ifp) \ + if (ether_poll_register(func, ifp)) { \ + struct firewire_comm *fc = (fwe)->fd.fc; \ + fc->set_intr(fc, 0); \ + } + +#define FWE_POLL_DEREGISTER(fwe, ifp) \ + do { \ + struct firewire_comm *fc = (fwe)->fd.fc; \ + ether_poll_deregister(ifp); \ + fc->set_intr(fc, 1); \ + } while(0) \ + +static poll_handler_t fwe_poll; + +static void +fwe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct fwe_softc *fwe; + struct firewire_comm *fc; + + fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; + fc = fwe->fd.fc; + if (cmd == POLL_DEREGISTER) { + /* enable interrupts */ + fc->set_intr(fc, 1); + return; + } + fc->poll(fc, (cmd == POLL_AND_CHECK_STATUS)?0:1, count); +} +#else +#define FWE_POLL_REGISTER(func, fwe, ifp) +#define FWE_POLL_DEREGISTER(fwe, ifp) +#endif +static void +fwe_identify(driver_t *driver, device_t parent) +{ + BUS_ADD_CHILD(parent, 0, "if_fwe", device_get_unit(parent)); +} + +static int +fwe_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, "Ethernet over FireWire"); + return (0); +} + +static int +fwe_attach(device_t dev) +{ + struct fwe_softc *fwe; + struct ifnet *ifp; + int unit, s; + u_char *eaddr; + struct fw_eui64 *eui; + + fwe = ((struct fwe_softc *)device_get_softc(dev)); + unit = device_get_unit(dev); + + bzero(fwe, sizeof(struct fwe_softc)); + /* XXX */ + fwe->stream_ch = stream_ch; + fwe->dma_ch = -1; + + fwe->fd.fc = device_get_ivars(dev); + fwe->fd.dev = dev; + fwe->fd.post_explore = NULL; + fwe->eth_softc.fwe = fwe; + + fwe->pkt_hdr.mode.stream.tcode = FWTCODE_STREAM; + fwe->pkt_hdr.mode.stream.sy = 0; + fwe->pkt_hdr.mode.stream.chtag = fwe->stream_ch; + + /* generate fake MAC address: first and last 3bytes from eui64 */ +#define LOCAL (0x02) +#define GROUP (0x01) + eaddr = &fwe->eth_softc.arpcom.ac_enaddr[0]; + + eui = &fwe->fd.fc->eui; + eaddr[0] = (FW_EUI64_BYTE(eui, 0) | LOCAL) & ~GROUP; + eaddr[1] = FW_EUI64_BYTE(eui, 1); + eaddr[2] = FW_EUI64_BYTE(eui, 2); + eaddr[3] = FW_EUI64_BYTE(eui, 5); + eaddr[4] = FW_EUI64_BYTE(eui, 6); + eaddr[5] = FW_EUI64_BYTE(eui, 7); + printf("if_fwe%d: Fake Ethernet address: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", unit, + eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]); + + /* fill the rest and attach interface */ + ifp = &fwe->fwe_if; + ifp->if_softc = &fwe->eth_softc; + + ifp->if_unit = unit; + ifp->if_name = "fwe"; + ifp->if_init = fwe_init; + ifp->if_output = ether_output; + ifp->if_start = fwe_start; + ifp->if_ioctl = fwe_ioctl; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); + ifp->if_snd.ifq_maxlen = TX_MAX_QUEUE; + + s = splimp(); +#if __FreeBSD_version >= 500000 + ether_ifattach(ifp, eaddr); +#else + ether_ifattach(ifp, 1); +#endif + splx(s); + + /* Tell the upper layer(s) we support long frames. */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); +#if __FreeBSD_version >= 500000 + ifp->if_capabilities |= IFCAP_VLAN_MTU; +#endif + + + FWEDEBUG("interface %s%d created.\n", ifp->if_name, ifp->if_unit); + return 0; +} + +static void +fwe_stop(struct fwe_softc *fwe) +{ + struct firewire_comm *fc; + struct fw_xferq *xferq; + struct ifnet *ifp = &fwe->fwe_if; + struct fw_xfer *xfer, *next; + int i; + + fc = fwe->fd.fc; + + FWE_POLL_DEREGISTER(fwe, ifp); + + if (fwe->dma_ch >= 0) { + xferq = fc->ir[fwe->dma_ch]; + + if (xferq->flag & FWXFERQ_RUNNING) + fc->irx_disable(fc, fwe->dma_ch); + xferq->flag &= + ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM | + FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); + xferq->hand = NULL; + + for (i = 0; i < xferq->bnchunk; i ++) + m_freem(xferq->bulkxfer[i].mbuf); + free(xferq->bulkxfer, M_FWE); + + for (xfer = STAILQ_FIRST(&fwe->xferlist); xfer != NULL; + xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free(xfer); + } + STAILQ_INIT(&fwe->xferlist); + + xferq->bulkxfer = NULL; + fwe->dma_ch = -1; + } + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); +} + +static int +fwe_detach(device_t dev) +{ + struct fwe_softc *fwe; + int s; + + fwe = (struct fwe_softc *)device_get_softc(dev); + s = splimp(); + + fwe_stop(fwe); +#if __FreeBSD_version >= 500000 + ether_ifdetach(&fwe->fwe_if); +#else + ether_ifdetach(&fwe->fwe_if, 1); +#endif + + splx(s); + return 0; +} + +static void +fwe_init(void *arg) +{ + struct fwe_softc *fwe = ((struct fwe_eth_softc *)arg)->fwe; + struct firewire_comm *fc; + struct ifnet *ifp = &fwe->fwe_if; + struct fw_xferq *xferq; + struct fw_xfer *xfer; + struct mbuf *m; + int i; + + FWEDEBUG("initializing %s%d\n", ifp->if_name, ifp->if_unit); + + /* XXX keep promiscoud mode */ + ifp->if_flags |= IFF_PROMISC; + + fc = fwe->fd.fc; +#define START 0 + if (fwe->dma_ch < 0) { + xferq = NULL; + for (i = START; i < fc->nisodma; i ++) { + xferq = fc->ir[i]; + if ((xferq->flag & FWXFERQ_OPEN) == 0) + break; + } + + if (xferq == NULL) { + printf("no free dma channel\n"); + return; + } + fwe->dma_ch = i; + fwe->stream_ch = stream_ch; + fwe->pkt_hdr.mode.stream.chtag = fwe->stream_ch; + /* allocate DMA channel and init packet mode */ + xferq->flag |= FWXFERQ_OPEN | FWXFERQ_EXTBUF | + FWXFERQ_HANDLER | FWXFERQ_STREAM; + xferq->flag &= ~0xff; + xferq->flag |= fwe->stream_ch & 0xff; + /* register fwe_input handler */ + xferq->sc = (caddr_t) fwe; + xferq->hand = fwe_as_input; + xferq->bnchunk = RX_MAX_QUEUE; + xferq->bnpacket = 1; + xferq->psize = MCLBYTES; + xferq->queued = 0; + xferq->buf = NULL; + xferq->bulkxfer = (struct fw_bulkxfer *) malloc( + sizeof(struct fw_bulkxfer) * xferq->bnchunk, + M_FWE, M_WAITOK); + if (xferq->bulkxfer == NULL) { + printf("if_fwe: malloc failed\n"); + return; + } + STAILQ_INIT(&xferq->stvalid); + STAILQ_INIT(&xferq->stfree); + STAILQ_INIT(&xferq->stdma); + xferq->stproc = NULL; + for (i = 0; i < xferq->bnchunk; i ++) { + m = +#if __FreeBSD_version >= 500000 + m_getcl(M_TRYWAIT, MT_DATA, M_PKTHDR); +#else + m_getcl(M_WAIT, MT_DATA, M_PKTHDR); +#endif + xferq->bulkxfer[i].mbuf = m; + if (m != NULL) { + m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; + STAILQ_INSERT_TAIL(&xferq->stfree, + &xferq->bulkxfer[i], link); + } else + printf("fwe_as_input: m_getcl failed\n"); + } + STAILQ_INIT(&fwe->xferlist); + for (i = 0; i < TX_MAX_QUEUE; i++) { + xfer = fw_xfer_alloc(M_FWE); + if (xfer == NULL) + break; + xfer->spd = tx_speed; + xfer->fc = fwe->fd.fc; + xfer->retry_req = fw_asybusy; + xfer->sc = (caddr_t)fwe; + xfer->act.hand = fwe_output_callback; + STAILQ_INSERT_TAIL(&fwe->xferlist, xfer, link); + } + } else + xferq = fc->ir[fwe->dma_ch]; + + + /* start dma */ + if ((xferq->flag & FWXFERQ_RUNNING) == 0) + fc->irx_enable(fc, fwe->dma_ch); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + FWE_POLL_REGISTER(fwe_poll, fwe, ifp); +#if 0 + /* attempt to start output */ + fwe_start(ifp); +#endif +} + + +static int +fwe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; + struct ifstat *ifs = NULL; + int s, error, len; + + switch (cmd) { + case SIOCSIFFLAGS: + s = splimp(); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + fwe_init(&fwe->eth_softc); + } else { + if (ifp->if_flags & IFF_RUNNING) + fwe_stop(fwe); + } + /* XXX keep promiscoud mode */ + ifp->if_flags |= IFF_PROMISC; + splx(s); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCGIFSTATUS: + s = splimp(); + ifs = (struct ifstat *)data; + len = strlen(ifs->ascii); + if (len < sizeof(ifs->ascii)) + snprintf(ifs->ascii + len, + sizeof(ifs->ascii) - len, + "\tch %d dma %d\n", + fwe->stream_ch, fwe->dma_ch); + splx(s); + break; +#if __FreeBSD_version >= 500000 + default: +#else + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: +#endif + s = splimp(); + error = ether_ioctl(ifp, cmd, data); + splx(s); + return (error); +#if __FreeBSD_version < 500000 + default: + return (EINVAL); +#endif + } + + return (0); +} + +static void +fwe_output_callback(struct fw_xfer *xfer) +{ + struct fwe_softc *fwe; + struct ifnet *ifp; + int s; + + fwe = (struct fwe_softc *)xfer->sc; + ifp = &fwe->fwe_if; + /* XXX error check */ + FWEDEBUG("resp = %d\n", xfer->resp); + if (xfer->resp != 0) + ifp->if_oerrors ++; + + m_freem(xfer->mbuf); + xfer->send.buf = NULL; + fw_xfer_unload(xfer); + + s = splimp(); + STAILQ_INSERT_TAIL(&fwe->xferlist, xfer, link); + splx(s); + + /* for queue full */ + if (ifp->if_snd.ifq_head != NULL) + fwe_start(ifp); +} + +static void +fwe_start(struct ifnet *ifp) +{ + struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe; + int s; + + FWEDEBUG("%s%d starting\n", ifp->if_name, ifp->if_unit); + + if (fwe->dma_ch < 0) { + struct mbuf *m = NULL; + + FWEDEBUG("%s%d not ready.\n", ifp->if_name, ifp->if_unit); + + s = splimp(); + do { + IF_DEQUEUE(&ifp->if_snd, m); + if (m != NULL) + m_freem(m); + ifp->if_oerrors ++; + } while (m != NULL); + splx(s); + + return; + } + + s = splimp(); + ifp->if_flags |= IFF_OACTIVE; + + if (ifp->if_snd.ifq_len != 0) + fwe_as_output(fwe, ifp); + + ifp->if_flags &= ~IFF_OACTIVE; + splx(s); +} + +#define HDR_LEN 4 +#ifndef ETHER_ALIGN +#define ETHER_ALIGN 2 +#endif +/* Async. stream output */ +static void +fwe_as_output(struct fwe_softc *fwe, struct ifnet *ifp) +{ + struct mbuf *m; + struct fw_xfer *xfer; + struct fw_xferq *xferq; + struct fw_pkt *fp; + int i = 0; + + xfer = NULL; + xferq = fwe->fd.fc->atq; + while (xferq->queued < xferq->maxq - 1) { + xfer = STAILQ_FIRST(&fwe->xferlist); + if (xfer == NULL) { + printf("if_fwe: lack of xfer\n"); + return; + } + IF_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + STAILQ_REMOVE_HEAD(&fwe->xferlist, link); +#if __FreeBSD_version >= 500000 + BPF_MTAP(ifp, m); +#else + if (ifp->if_bpf != NULL) + bpf_mtap(ifp, m); +#endif + + /* keep ip packet alignment for alpha */ + M_PREPEND(m, ETHER_ALIGN, M_DONTWAIT); + fp = (struct fw_pkt *)&xfer->dst; /* XXX */ + xfer->dst = *((int32_t *)&fwe->pkt_hdr); + fp->mode.stream.len = m->m_pkthdr.len; + xfer->send.buf = (caddr_t) fp; + xfer->mbuf = m; + xfer->send.len = m->m_pkthdr.len + HDR_LEN; + + if (fw_asyreq(fwe->fd.fc, -1, xfer) != 0) { + /* error */ + ifp->if_oerrors ++; + /* XXX set error code */ + fwe_output_callback(xfer); + } else { + ifp->if_opackets ++; + i++; + } + } +#if 0 + if (i > 1) + printf("%d queued\n", i); +#endif + if (i > 0) + xferq->start(fwe->fd.fc); +} + +/* Async. stream output */ +static void +fwe_as_input(struct fw_xferq *xferq) +{ + struct mbuf *m, *m0; + struct ifnet *ifp; + struct fwe_softc *fwe; + struct fw_bulkxfer *sxfer; + struct fw_pkt *fp; + u_char *c; +#if __FreeBSD_version < 500000 + struct ether_header *eh; +#endif + + fwe = (struct fwe_softc *)xferq->sc; + ifp = &fwe->fwe_if; +#if 0 + FWE_POLL_REGISTER(fwe_poll, fwe, ifp); +#endif + while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) { + STAILQ_REMOVE_HEAD(&xferq->stvalid, link); + if (sxfer->resp != 0) + ifp->if_ierrors ++; + fp = mtod(sxfer->mbuf, struct fw_pkt *); + /* XXX */ + if (fwe->fd.fc->irx_post != NULL) + fwe->fd.fc->irx_post(fwe->fd.fc, fp->mode.ld); + m = sxfer->mbuf; + + /* insert rbuf */ + sxfer->mbuf = m0 = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m0 != NULL) { + m0->m_len = m0->m_pkthdr.len = m0->m_ext.ext_size; + STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link); + } else + printf("fwe_as_input: m_getcl failed\n"); + + m->m_data += HDR_LEN + ETHER_ALIGN; + c = mtod(m, char *); +#if __FreeBSD_version < 500000 + eh = (struct ether_header *)c; + m->m_data += sizeof(struct ether_header); +#endif + m->m_len = m->m_pkthdr.len = + fp->mode.stream.len - ETHER_ALIGN; + m->m_pkthdr.rcvif = ifp; +#if 0 + FWEDEBUG("%02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x %02x %02x\n" + "%02x %02x %02x %02x\n" + "%02x %02x %02x %02x\n" + "%02x %02x %02x %02x\n" + "%02x %02x %02x %02x\n", + c[0], c[1], c[2], c[3], c[4], c[5], + c[6], c[7], c[8], c[9], c[10], c[11], + c[12], c[13], c[14], c[15], + c[16], c[17], c[18], c[19], + c[20], c[21], c[22], c[23], + c[20], c[21], c[22], c[23] + ); +#endif +#if __FreeBSD_version >= 500000 + (*ifp->if_input)(ifp, m); +#else + ether_input(ifp, eh, m); +#endif + ifp->if_ipackets ++; + } + if (STAILQ_FIRST(&xferq->stfree) != NULL) + fwe->fd.fc->irx_enable(fwe->fd.fc, fwe->dma_ch); +} + + +static devclass_t fwe_devclass; + +static device_method_t fwe_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, fwe_identify), + DEVMETHOD(device_probe, fwe_probe), + DEVMETHOD(device_attach, fwe_attach), + DEVMETHOD(device_detach, fwe_detach), + { 0, 0 } +}; + +static driver_t fwe_driver = { + "if_fwe", + fwe_methods, + sizeof(struct fwe_softc), +}; + + +DRIVER_MODULE(fwe, firewire, fwe_driver, fwe_devclass, 0, 0); +MODULE_VERSION(fwe, 1); +MODULE_DEPEND(fwe, firewire, 1, 1, 1); diff --git a/sys/dev/firewire/if_fwevar.h b/sys/dev/firewire/if_fwevar.h new file mode 100644 index 0000000..9742216 --- /dev/null +++ b/sys/dev/firewire/if_fwevar.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-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$ + */ + +#ifndef _NET_IF_FWEVAR_H_ +#define _NET_IF_FWEVAR_H_ + +struct fwe_softc { + /* XXX this must be first for fd.post_explore() */ + struct firewire_dev_comm fd; + short stream_ch; + short dma_ch; + struct fw_pkt pkt_hdr; + STAILQ_HEAD(, fw_xfer) xferlist; + struct fwe_eth_softc { + /* XXX this must be the first for if_ethersub.c */ + struct arpcom arpcom; /* ethernet common data */ + #define fwe_if eth_softc.arpcom.ac_if + struct fwe_softc *fwe; + } eth_softc; +}; +#endif /* !_NET_IF_FWEVAR_H_ */ diff --git a/sys/dev/firewire/sbp.c b/sys/dev/firewire/sbp.c new file mode 100644 index 0000000..8e296dd --- /dev/null +++ b/sys/dev/firewire/sbp.c @@ -0,0 +1,2683 @@ +/* + * Copyright (c) 2003 Hidetosh Shimokawa + * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetosh 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$ + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/mbuf.h> +#include <sys/sysctl.h> +#include <machine/bus.h> +#include <sys/malloc.h> + +#if __FreeBSD_version < 500106 +#include <sys/devicestat.h> /* for struct devstat */ +#endif + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/cam_debug.h> +#include <cam/cam_periph.h> + +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> +#include <cam/scsi/scsi_da.h> + +#include <sys/kernel.h> + +#include <dev/firewire/firewire.h> +#include <dev/firewire/firewirereg.h> +#include <dev/firewire/fwdma.h> +#include <dev/firewire/iec13213.h> + +#define ccb_sdev_ptr spriv_ptr0 +#define ccb_sbp_ptr spriv_ptr1 + +#define SBP_NUM_TARGETS 8 /* MAX 64 */ +#define SBP_NUM_LUNS 8 /* limited by CAM_SCSI2_MAXLUN in cam_xpt.c */ +#define SBP_DMA_SIZE PAGE_SIZE +#define SBP_LOGIN_SIZE sizeof(struct sbp_login_res) +#define SBP_QUEUE_LEN ((SBP_DMA_SIZE - SBP_LOGIN_SIZE) / sizeof(struct sbp_ocb)) +#define SBP_NUM_OCB (SBP_QUEUE_LEN * SBP_NUM_TARGETS) + +#define SBP_INITIATOR 7 + +#define LOGIN_DELAY 2 + +/* + * STATUS FIFO addressing + * bit + * ----------------------- + * 0- 1( 2): 0 (alingment) + * 2- 7( 6): target + * 8-15( 8): lun + * 16-23( 8): unit + * 24-31( 8): reserved + * 32-47(16): SBP_BIND_HI + * 48-64(16): bus_id, node_id + */ +#define SBP_BIND_HI 0x1 +#define SBP_DEV2ADDR(u, t, l) \ + ((((u) & 0xff) << 16) | (((l) & 0xff) << 8) | (((t) & 0x3f) << 2)) +#define SBP_ADDR2TRG(a) (((a) >> 2) & 0x3f) +#define SBP_ADDR2LUN(a) (((a) >> 8) & 0xff) + +#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 + +static char *orb_fun_name[] = { + /* 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" +}; + +#define ORB_RES_CMPL 0 +#define ORB_RES_FAIL 1 +#define ORB_RES_ILLE 2 +#define ORB_RES_VEND 3 + +static int debug = 0; +static int auto_login = 1; +static int max_speed = 2; +static int sbp_cold = 1; + +SYSCTL_DECL(_hw_firewire); +SYSCTL_NODE(_hw_firewire, OID_AUTO, sbp, CTLFLAG_RD, 0, "SBP-II Subsystem"); +SYSCTL_INT(_debug, OID_AUTO, sbp_debug, CTLFLAG_RW, &debug, 0, + "SBP debug flag"); +SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, auto_login, CTLFLAG_RW, &auto_login, 0, + "SBP perform login automatically"); +SYSCTL_INT(_hw_firewire_sbp, OID_AUTO, max_speed, CTLFLAG_RW, &max_speed, 0, + "SBP transfer max speed"); + +#define SBP_DEBUG(x) if (debug > x) { +#define END_DEBUG } + +#define NEED_RESPONSE 0 + +struct ind_ptr { + u_int32_t hi,lo; +}; +#define SBP_SEG_MAX rounddown(0xffff, PAGE_SIZE) +#ifdef __sparc64__ /* iommu */ +#define SBP_IND_MAX howmany(MAXPHYS, SBP_SEG_MAX) +#else +#define SBP_IND_MAX howmany(MAXPHYS, PAGE_SIZE) +#endif +struct sbp_ocb { + STAILQ_ENTRY(sbp_ocb) ocb; + union ccb *ccb; + bus_addr_t bus_addr; + volatile u_int32_t orb[8]; +#define IND_PTR_OFFSET (8*sizeof(u_int32_t)) + volatile struct ind_ptr ind_ptr[SBP_IND_MAX]; + struct sbp_dev *sdev; + int flags; /* XXX should be removed */ + bus_dmamap_t dmamap; +}; + +#define OCB_ACT_MGM 0 +#define OCB_ACT_CMD 1 +#define OCB_MATCH(o,s) ((o)->bus_addr == ntohl((s)->orb_lo)) + +#define SBP_RECV_LEN (16 + 32) /* header + payload */ + +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]; +}; +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; + +#if BYTE_ORDER == BIG_ENDIAN + u_int32_t s_keydep:24, + fru:8; +#else + u_int32_t fru:8, + s_keydep:24; +#endif + u_int32_t vend[2]; + +}; + +struct sbp_dev{ +#define SBP_DEV_RESET 0 /* accept login */ +#define SBP_DEV_LOGIN 1 /* to login */ +#if 0 +#define SBP_DEV_RECONN 2 /* to reconnect */ +#endif +#define SBP_DEV_TOATTACH 3 /* to attach */ +#define SBP_DEV_PROBE 4 /* scan lun */ +#define SBP_DEV_ATTACHED 5 /* in operation */ +#define SBP_DEV_DEAD 6 /* unavailable unit */ +#define SBP_DEV_RETRY 7 /* unavailable unit */ + u_int8_t status:4, + timeout:4; + u_int8_t type; + u_int16_t lun_id; + int freeze; + struct cam_path *path; + struct sbp_target *target; + struct fwdma_alloc dma; + struct sbp_login_res *login; + struct callout login_callout; + struct sbp_ocb *ocb; + STAILQ_HEAD(, sbp_ocb) ocbs; + STAILQ_HEAD(, sbp_ocb) free_ocbs; + char vendor[32]; + char product[32]; + char revision[10]; +}; + +struct sbp_target { + int target_id; + int num_lun; + struct sbp_dev *luns; + struct sbp_softc *sbp; + struct fw_device *fwdev; + u_int32_t mgm_hi, mgm_lo; + struct sbp_ocb *mgm_ocb_cur; + STAILQ_HEAD(, sbp_ocb) mgm_ocb_queue; + struct callout mgm_ocb_timeout; +#define SCAN_DELAY 2 + struct callout scan_callout; + STAILQ_HEAD(, fw_xfer) xferlist; + int n_xfer; +}; + +struct sbp_softc { + struct firewire_dev_comm fd; + struct cam_sim *sim; + struct cam_path *path; + struct sbp_target targets[SBP_NUM_TARGETS]; + struct fw_bind fwb; + bus_dma_tag_t dmat; +#define SBP_RESOURCE_SHORTAGE 0x10 + unsigned char flags; +}; +static void sbp_post_explore __P((void *)); +static void sbp_recv __P((struct fw_xfer *)); +static void sbp_mgm_callback __P((struct fw_xfer *)); +static void sbp_cmd_callback __P((struct fw_xfer *)); +static void sbp_orb_pointer __P((struct sbp_dev *, struct sbp_ocb *)); +static void sbp_execute_ocb __P((void *, bus_dma_segment_t *, int, int)); +static void sbp_free_ocb __P((struct sbp_dev *, struct sbp_ocb *)); +static void sbp_abort_ocb __P((struct sbp_ocb *, int)); +static void sbp_abort_all_ocbs __P((struct sbp_dev *, int)); +static struct fw_xfer * sbp_write_cmd __P((struct sbp_dev *, int, int)); +static struct sbp_ocb * sbp_get_ocb __P((struct sbp_dev *)); +static struct sbp_ocb * sbp_enqueue_ocb __P((struct sbp_dev *, struct sbp_ocb *)); +static struct sbp_ocb * sbp_dequeue_ocb __P((struct sbp_dev *, struct sbp_status *)); +static void sbp_cam_detach_target __P((struct sbp_target *)); +static void sbp_mgm_timeout __P((void *arg)); +static void sbp_timeout __P((void *arg)); +static void sbp_mgm_orb __P((struct sbp_dev *, int, struct sbp_ocb *)); +#define sbp_login(sdev) \ + callout_reset(&(sdev)->login_callout, LOGIN_DELAY * hz, \ + sbp_login_callout, (void *)(sdev)); + +MALLOC_DEFINE(M_SBP, "sbp", "SBP-II/FireWire"); + +/* cam related functions */ +static void sbp_action(struct cam_sim *sim, union ccb *ccb); +static void sbp_poll(struct cam_sim *sim); +static void sbp_cam_scan_lun(struct cam_periph *, union ccb *); +static void sbp_cam_scan_target(void *arg); + +static char *orb_status0[] = { + /* 0 */ "No additional information to report", + /* 1 */ "Request type not supported", + /* 2 */ "Speed not supported", + /* 3 */ "Page size not supported", + /* 4 */ "Access denied", + /* 5 */ "Logical unit not supported", + /* 6 */ "Maximum payload too small", + /* 7 */ "Reserved for future standardization", + /* 8 */ "Resources unavailable", + /* 9 */ "Function rejected", + /* A */ "Login ID not recognized", + /* B */ "Dummy ORB completed", + /* C */ "Request aborted", + /* FF */ "Unspecified error" +#define MAX_ORB_STATUS0 0xd +}; + +static char *orb_status1_object[] = { + /* 0 */ "Operation request block (ORB)", + /* 1 */ "Data buffer", + /* 2 */ "Page table", + /* 3 */ "Unable to specify" +}; + +static char *orb_status1_serial_bus_error[] = { + /* 0 */ "Missing acknowledge", + /* 1 */ "Reserved; not to be used", + /* 2 */ "Time-out error", + /* 3 */ "Reserved; not to be used", + /* 4 */ "Busy retry limit exceeded(X)", + /* 5 */ "Busy retry limit exceeded(A)", + /* 6 */ "Busy retry limit exceeded(B)", + /* 7 */ "Reserved for future standardization", + /* 8 */ "Reserved for future standardization", + /* 9 */ "Reserved for future standardization", + /* A */ "Reserved for future standardization", + /* B */ "Tardy retry limit exceeded", + /* C */ "Conflict error", + /* D */ "Data error", + /* E */ "Type error", + /* F */ "Address error" +}; + +static void +sbp_identify(driver_t *driver, device_t parent) +{ + device_t child; +SBP_DEBUG(0) + printf("sbp_identify\n"); +END_DEBUG + + child = BUS_ADD_CHILD(parent, 0, "sbp", device_get_unit(parent)); +} + +/* + * sbp_probe() + */ +static int +sbp_probe(device_t dev) +{ + device_t pa; + +SBP_DEBUG(0) + printf("sbp_probe\n"); +END_DEBUG + + pa = device_get_parent(dev); + if(device_get_unit(dev) != device_get_unit(pa)){ + return(ENXIO); + } + + device_set_desc(dev, "SBP2/SCSI over firewire"); + + if (bootverbose) + debug = bootverbose; + return (0); +} + +static void +sbp_show_sdev_info(struct sbp_dev *sdev, int new) +{ + struct fw_device *fwdev; + + printf("%s:%d:%d ", + device_get_nameunit(sdev->target->sbp->fd.dev), + sdev->target->target_id, + sdev->lun_id + ); + if (new == 2) { + return; + } + fwdev = sdev->target->fwdev; + printf("ordered:%d type:%d EUI:%08x%08x node:%d " + "speed:%d maxrec:%d", + (sdev->type & 0x40) >> 6, + (sdev->type & 0x1f), + fwdev->eui.hi, + fwdev->eui.lo, + fwdev->dst, + fwdev->speed, + fwdev->maxrec + ); + if (new) + printf(" new!\n"); + else + printf("\n"); + sbp_show_sdev_info(sdev, 2); + printf("'%s' '%s' '%s'\n", sdev->vendor, sdev->product, sdev->revision); +} + +static struct { + int bus; + int target; + struct fw_eui64 eui; +} wired[] = { + /* Bus Target EUI64 */ +#if 0 + {0, 2, {0x00018ea0, 0x01fd0154}}, /* Logitec HDD */ + {0, 0, {0x00018ea6, 0x00100682}}, /* Logitec DVD */ + {0, 1, {0x00d03200, 0xa412006a}}, /* Yano HDD */ +#endif + {-1, -1, {0,0}} +}; + +static int +sbp_new_target(struct sbp_softc *sbp, struct fw_device *fwdev) +{ + int bus, i, target=-1; + char w[SBP_NUM_TARGETS]; + + bzero(w, sizeof(w)); + bus = device_get_unit(sbp->fd.dev); + + /* XXX wired-down configuration should be gotten from + tunable or device hint */ + for (i = 0; wired[i].bus >= 0; i ++) { + if (wired[i].bus == bus) { + w[wired[i].target] = 1; + if (wired[i].eui.hi == fwdev->eui.hi && + wired[i].eui.lo == fwdev->eui.lo) + target = wired[i].target; + } + } + if (target >= 0) { + if(target < SBP_NUM_TARGETS && + sbp->targets[target].fwdev == NULL) + return(target); + device_printf(sbp->fd.dev, + "target %d is not free for %08x:%08x\n", + target, fwdev->eui.hi, fwdev->eui.lo); + target = -1; + } + /* non-wired target */ + for (i = 0; i < SBP_NUM_TARGETS; i ++) + if (sbp->targets[i].fwdev == NULL && w[i] == 0) { + target = i; + break; + } + + return target; +} + +static struct sbp_target * +sbp_alloc_target(struct sbp_softc *sbp, struct fw_device *fwdev) +{ + int i, maxlun, lun; + struct sbp_target *target; + struct sbp_dev *sdev; + struct crom_context cc; + struct csrreg *reg; + +SBP_DEBUG(1) + printf("sbp_alloc_target\n"); +END_DEBUG + i = sbp_new_target(sbp, fwdev); + if (i < 0) { + device_printf(sbp->fd.dev, "increase SBP_NUM_TARGETS!\n"); + return NULL; + } + /* new target */ + target = &sbp->targets[i]; + target->sbp = sbp; + target->fwdev = fwdev; + target->target_id = i; + /* XXX we may want to reload mgm port after each bus reset */ + /* XXX there might be multiple management agents */ + crom_init_context(&cc, target->fwdev->csrrom); + reg = crom_search_key(&cc, CROM_MGM); + if (reg == NULL || reg->val == 0) { + printf("NULL management address\n"); + target->fwdev = NULL; + return NULL; + } + target->mgm_hi = 0xffff; + target->mgm_lo = 0xf0000000 | (reg->val << 2); + target->mgm_ocb_cur = NULL; +SBP_DEBUG(1) + printf("target:%d mgm_port: %x\n", i, target->mgm_lo); +END_DEBUG + STAILQ_INIT(&target->xferlist); + target->n_xfer = 0; + STAILQ_INIT(&target->mgm_ocb_queue); + CALLOUT_INIT(&target->mgm_ocb_timeout); + CALLOUT_INIT(&target->scan_callout); + + /* XXX num_lun may be changed. realloc luns? */ + crom_init_context(&cc, target->fwdev->csrrom); + /* XXX shoud parse appropriate unit directories only */ + maxlun = -1; + while (cc.depth >= 0) { + reg = crom_search_key(&cc, CROM_LUN); + if (reg == NULL) + break; + lun = reg->val & 0xffff; +SBP_DEBUG(0) + printf("target %d lun %d found\n", target->target_id, lun); +END_DEBUG + if (maxlun < lun) + maxlun = lun; + crom_next(&cc); + } + if (maxlun < 0) + printf("no lun found!\n"); + if (maxlun >= SBP_NUM_LUNS) + maxlun = SBP_NUM_LUNS; + target->num_lun = maxlun + 1; + target->luns = (struct sbp_dev *) malloc( + sizeof(struct sbp_dev) * target->num_lun, + M_SBP, M_NOWAIT | M_ZERO); + for (i = 0; i < target->num_lun; i++) { + sdev = &target->luns[i]; + sdev->lun_id = i; + sdev->target = target; + STAILQ_INIT(&sdev->ocbs); + CALLOUT_INIT(&sdev->login_callout); + sdev->status = SBP_DEV_DEAD; + } + crom_init_context(&cc, target->fwdev->csrrom); + while (cc.depth >= 0) { + reg = crom_search_key(&cc, CROM_LUN); + if (reg == NULL) + break; + lun = reg->val & 0xffff; + if (lun >= SBP_NUM_LUNS) { + printf("too large lun %d\n", lun); + continue; + } + sdev = &target->luns[lun]; + sdev->status = SBP_DEV_RESET; + sdev->type = (reg->val & 0xf0000) >> 16; + + fwdma_malloc(sbp->fd.fc, + /* alignment */ sizeof(u_int32_t), + SBP_DMA_SIZE, &sdev->dma, BUS_DMA_NOWAIT); + if (sdev->dma.v_addr == NULL) { + printf("%s: dma space allocation failed\n", + __FUNCTION__); + return (NULL); + } + sdev->login = (struct sbp_login_res *) sdev->dma.v_addr; + sdev->ocb = (struct sbp_ocb *) + ((char *)sdev->dma.v_addr + SBP_LOGIN_SIZE); + bzero((char *)sdev->ocb, + sizeof (struct sbp_ocb) * SBP_QUEUE_LEN); + + STAILQ_INIT(&sdev->free_ocbs); + for (i = 0; i < SBP_QUEUE_LEN; i++) { + struct sbp_ocb *ocb; + ocb = &sdev->ocb[i]; + ocb->bus_addr = sdev->dma.bus_addr + + SBP_LOGIN_SIZE + + sizeof(struct sbp_ocb) * i + + offsetof(struct sbp_ocb, orb[0]); + if (bus_dmamap_create(sbp->dmat, 0, &ocb->dmamap)) { + printf("sbp_attach: cannot create dmamap\n"); + return (NULL); + } + sbp_free_ocb(sdev, ocb); + } + crom_next(&cc); + } + return target; +} + +static void +sbp_probe_lun(struct sbp_dev *sdev) +{ + struct fw_device *fwdev; + struct crom_context c, *cc = &c; + struct csrreg *reg; + + bzero(sdev->vendor, sizeof(sdev->vendor)); + bzero(sdev->product, sizeof(sdev->product)); + + fwdev = sdev->target->fwdev; + crom_init_context(cc, fwdev->csrrom); + /* get vendor string */ + crom_search_key(cc, CSRKEY_VENDOR); + crom_next(cc); + crom_parse_text(cc, sdev->vendor, sizeof(sdev->vendor)); + /* skip to the unit directory for SBP-2 */ + while ((reg = crom_search_key(cc, CSRKEY_VER)) != NULL) { + if (reg->val == CSRVAL_T10SBP2) + break; + crom_next(cc); + } + /* get firmware revision */ + reg = crom_search_key(cc, CSRKEY_FIRM_VER); + if (reg != NULL) + snprintf(sdev->revision, sizeof(sdev->revision), + "%06x", reg->val); + /* get product string */ + crom_search_key(cc, CSRKEY_MODEL); + crom_next(cc); + crom_parse_text(cc, sdev->product, sizeof(sdev->product)); +} + +static void +sbp_login_callout(void *arg) +{ + struct sbp_dev *sdev = (struct sbp_dev *)arg; + sbp_mgm_orb(sdev, ORB_FUN_LGI, NULL); +} + +#define SBP_FWDEV_ALIVE(fwdev) (((fwdev)->status == FWDEVATTACHED) \ + && crom_has_specver((fwdev)->csrrom, CSRVAL_ANSIT10, CSRVAL_T10SBP2)) + +static void +sbp_probe_target(void *arg) +{ + struct sbp_target *target = (struct sbp_target *)arg; + struct sbp_softc *sbp; + struct sbp_dev *sdev; + struct firewire_comm *fc; + int i, alive; + + alive = SBP_FWDEV_ALIVE(target->fwdev); +SBP_DEBUG(1) + printf("sbp_probe_target %d\n", target->target_id); + if (!alive) + printf("not alive\n"); +END_DEBUG + + sbp = target->sbp; + fc = target->sbp->fd.fc; + /* XXX untimeout mgm_ocb and dequeue */ + for (i=0; i < target->num_lun; i++) { + sdev = &target->luns[i]; + if (alive && (sdev->status != SBP_DEV_DEAD)) { + if (sdev->path != NULL) { + xpt_freeze_devq(sdev->path, 1); + sdev->freeze ++; + } + sbp_probe_lun(sdev); +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, + (sdev->status == SBP_DEV_RESET)); +END_DEBUG + + sbp_abort_all_ocbs(sdev, CAM_SCSI_BUS_RESET); + switch (sdev->status) { + case SBP_DEV_RESET: + /* new or revived target */ + if (auto_login) + sbp_login(sdev); + break; + case SBP_DEV_TOATTACH: + case SBP_DEV_PROBE: + case SBP_DEV_ATTACHED: + case SBP_DEV_RETRY: + default: + sbp_mgm_orb(sdev, ORB_FUN_RCN, NULL); + break; + } + } else { + switch (sdev->status) { + case SBP_DEV_ATTACHED: +SBP_DEBUG(0) + /* the device has gone */ + sbp_show_sdev_info(sdev, 2); + printf("lost target\n"); +END_DEBUG + if (sdev->path) { + xpt_freeze_devq(sdev->path, 1); + sdev->freeze ++; + } + sdev->status = SBP_DEV_RETRY; + sbp_abort_all_ocbs(sdev, CAM_SCSI_BUS_RESET); + break; + case SBP_DEV_PROBE: + case SBP_DEV_TOATTACH: + sdev->status = SBP_DEV_RESET; + break; + case SBP_DEV_RETRY: + case SBP_DEV_RESET: + case SBP_DEV_DEAD: + break; + } + } + } +} + +static void +sbp_post_busreset(void *arg) +{ + struct sbp_softc *sbp; + + sbp = (struct sbp_softc *)arg; +SBP_DEBUG(0) + printf("sbp_post_busreset\n"); +END_DEBUG +} + +static void +sbp_post_explore(void *arg) +{ + struct sbp_softc *sbp = (struct sbp_softc *)arg; + struct sbp_target *target; + struct fw_device *fwdev; + int i, alive; + +SBP_DEBUG(0) + printf("sbp_post_explore (sbp_cold=%d)\n", sbp_cold); +END_DEBUG +#if 0 /* + * XXX don't let CAM the bus rest. CAM tries to do something with + * freezed (DEV_RETRY) devices + */ + xpt_async(AC_BUS_RESET, sbp->path, /*arg*/ NULL); +#endif + if (sbp_cold > 0) + sbp_cold --; + + /* Gabage Collection */ + for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){ + target = &sbp->targets[i]; + STAILQ_FOREACH(fwdev, &sbp->fd.fc->devices, link) + if (target->fwdev == NULL || target->fwdev == fwdev) + break; + if(fwdev == NULL){ + /* device has removed in lower driver */ + sbp_cam_detach_target(target); + if (target->luns != NULL) + free(target->luns, M_SBP); + target->num_lun = 0;; + target->luns = NULL; + target->fwdev = NULL; + } + } + /* traverse device list */ + STAILQ_FOREACH(fwdev, &sbp->fd.fc->devices, link) { +SBP_DEBUG(0) + printf("sbp_post_explore: EUI:%08x%08x ", + fwdev->eui.hi, fwdev->eui.lo); + if (fwdev->status != FWDEVATTACHED) + printf("not attached, state=%d.\n", fwdev->status); + else + printf("attached\n"); +END_DEBUG + alive = SBP_FWDEV_ALIVE(fwdev); + for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){ + target = &sbp->targets[i]; + if(target->fwdev == fwdev ) { + /* known target */ + break; + } + } + if(i == SBP_NUM_TARGETS){ + if (alive) { + /* new target */ + target = sbp_alloc_target(sbp, fwdev); + if (target == NULL) + continue; + } else { + continue; + } + } + sbp_probe_target((void *)target); + } +} + +#if NEED_RESPONSE +static void +sbp_loginres_callback(struct fw_xfer *xfer){ + int s; + struct sbp_dev *sdev; + sdev = (struct sbp_dev *)xfer->sc; +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); + printf("sbp_loginres_callback\n"); +END_DEBUG + /* recycle */ + s = splfw(); + STAILQ_INSERT_TAIL(&sdev->target->sbp->fwb.xferlist, xfer, link); + splx(s); + return; +} +#endif + +static __inline void +sbp_xfer_free(struct fw_xfer *xfer) +{ + struct sbp_dev *sdev; + int s; + + sdev = (struct sbp_dev *)xfer->sc; + fw_xfer_unload(xfer); + s = splfw(); + STAILQ_INSERT_TAIL(&sdev->target->xferlist, xfer, link); + splx(s); +} + +static void +sbp_reset_start_callback(struct fw_xfer *xfer) +{ + struct sbp_dev *tsdev, *sdev = (struct sbp_dev *)xfer->sc; + struct sbp_target *target = sdev->target; + int i; + + if (xfer->resp != 0) { + sbp_show_sdev_info(sdev, 2); + printf("sbp_reset_start failed: resp=%d\n", xfer->resp); + } + + for (i = 0; i < target->num_lun; i++) { + tsdev = &target->luns[i]; + if (tsdev->status == SBP_DEV_LOGIN) + sbp_login(sdev); + } +} + +static void +sbp_reset_start(struct sbp_dev *sdev) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("sbp_reset_start\n"); +END_DEBUG + + xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0); + xfer->act.hand = sbp_reset_start_callback; + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqq.dest_hi = 0xffff; + fp->mode.wreqq.dest_lo = 0xf0000000 | RESET_START; + fp->mode.wreqq.data = htonl(0xf); + fw_asyreq(xfer->fc, -1, xfer); +} + +static void +sbp_mgm_callback(struct fw_xfer *xfer) +{ + struct sbp_dev *sdev; + int resp; + + sdev = (struct sbp_dev *)xfer->sc; + +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); + printf("sbp_mgm_callback\n"); +END_DEBUG + resp = xfer->resp; + sbp_xfer_free(xfer); +#if 0 + if (resp != 0) { + sbp_show_sdev_info(sdev, 2); + printf("management ORB failed(%d) ... RESET_START\n", resp); + sbp_reset_start(sdev); + } +#endif + return; +} + +static void +sbp_cmd_callback(struct fw_xfer *xfer) +{ +SBP_DEBUG(2) + struct sbp_dev *sdev; + sdev = (struct sbp_dev *)xfer->sc; + sbp_show_sdev_info(sdev, 2); + printf("sbp_cmd_callback\n"); +END_DEBUG + sbp_xfer_free(xfer); + return; +} + +static struct sbp_dev * +sbp_next_dev(struct sbp_target *target, int lun) +{ + struct sbp_dev *sdev; + int i; + + for (i = lun, sdev = &target->luns[lun]; + i < target->num_lun; i++, sdev++) { + if (sdev->status == SBP_DEV_PROBE) + break; + } + if (i >= target->num_lun) + return(NULL); + return(sdev); +} + +#define SCAN_PRI 1 +static void +sbp_cam_scan_lun(struct cam_periph *periph, union ccb *ccb) +{ + struct sbp_target *target; + struct sbp_dev *sdev; + + sdev = (struct sbp_dev *) ccb->ccb_h.ccb_sdev_ptr; + target = sdev->target; +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("sbp_cam_scan_lun\n"); +END_DEBUG + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { + sdev->status = SBP_DEV_ATTACHED; + } else { + sbp_show_sdev_info(sdev, 2); + printf("scan failed\n"); + } + sdev = sbp_next_dev(target, sdev->lun_id + 1); + if (sdev == NULL) { + free(ccb, M_SBP); + return; + } + /* reuse ccb */ + xpt_setup_ccb(&ccb->ccb_h, sdev->path, SCAN_PRI); + ccb->ccb_h.ccb_sdev_ptr = sdev; + xpt_action(ccb); + xpt_release_devq(sdev->path, sdev->freeze, TRUE); + sdev->freeze = 1; +} + +static void +sbp_cam_scan_target(void *arg) +{ + struct sbp_target *target = (struct sbp_target *)arg; + struct sbp_dev *sdev; + union ccb *ccb; + + sdev = sbp_next_dev(target, 0); + if (sdev == NULL) { + printf("sbp_cam_scan_target: nothing to do for target%d\n", + target->target_id); + return; + } +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("sbp_cam_scan_target\n"); +END_DEBUG + ccb = malloc(sizeof(union ccb), M_SBP, M_NOWAIT | M_ZERO); + if (ccb == NULL) { + printf("sbp_cam_scan_target: malloc failed\n"); + return; + } + xpt_setup_ccb(&ccb->ccb_h, sdev->path, SCAN_PRI); + ccb->ccb_h.func_code = XPT_SCAN_LUN; + ccb->ccb_h.cbfcnp = sbp_cam_scan_lun; + ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + ccb->crcn.flags = CAM_FLAG_NONE; + ccb->ccb_h.ccb_sdev_ptr = sdev; + + /* The scan is in progress now. */ + xpt_action(ccb); + xpt_release_devq(sdev->path, sdev->freeze, TRUE); + sdev->freeze = 1; +} + +static __inline void +sbp_scan_dev(struct sbp_dev *sdev) +{ + sdev->status = SBP_DEV_PROBE; + callout_reset(&sdev->target->scan_callout, SCAN_DELAY * hz, + sbp_cam_scan_target, (void *)sdev->target); +} + +static void +sbp_do_attach(struct fw_xfer *xfer) +{ + struct sbp_dev *sdev; + struct sbp_target *target; + struct sbp_softc *sbp; + + sdev = (struct sbp_dev *)xfer->sc; + target = sdev->target; + sbp = target->sbp; +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("sbp_do_attach\n"); +END_DEBUG + sbp_xfer_free(xfer); + + if (sdev->path == NULL) + xpt_create_path(&sdev->path, xpt_periph, + cam_sim_path(target->sbp->sim), + target->target_id, sdev->lun_id); + + /* + * Let CAM scan the bus if we are in the boot process. + * XXX xpt_scan_bus cannot detect LUN larger than 0 + * if LUN 0 doesn't exists. + */ + if (sbp_cold > 0) { + sdev->status = SBP_DEV_ATTACHED; + return; + } + + sbp_scan_dev(sdev); + return; +} + +static void +sbp_agent_reset_callback(struct fw_xfer *xfer) +{ + struct sbp_dev *sdev; + + sdev = (struct sbp_dev *)xfer->sc; +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); + printf("sbp_cmd_callback\n"); +END_DEBUG + if (xfer->resp != 0) { + sbp_show_sdev_info(sdev, 2); + printf("sbp_cmd_callback resp=%d\n", xfer->resp); + } + + sbp_xfer_free(xfer); + if (sdev->path) { + xpt_release_devq(sdev->path, sdev->freeze, TRUE); + sdev->freeze = 0; + } +} + +static void +sbp_agent_reset(struct sbp_dev *sdev) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("sbp_agent_reset\n"); +END_DEBUG + xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x04); + if (xfer == NULL) + return; + if (sdev->status == SBP_DEV_ATTACHED || sdev->status == SBP_DEV_PROBE) + xfer->act.hand = sbp_agent_reset_callback; + else + xfer->act.hand = sbp_do_attach; + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqq.data = htonl(0xf); + fw_asyreq(xfer->fc, -1, xfer); + sbp_abort_all_ocbs(sdev, CAM_BDR_SENT); +} + +static void +sbp_busy_timeout_callback(struct fw_xfer *xfer) +{ + struct sbp_dev *sdev; + + sdev = (struct sbp_dev *)xfer->sc; +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); + printf("sbp_busy_timeout_callback\n"); +END_DEBUG + sbp_xfer_free(xfer); + sbp_agent_reset(sdev); +} + +static void +sbp_busy_timeout(struct sbp_dev *sdev) +{ + struct fw_pkt *fp; + struct fw_xfer *xfer; +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("sbp_busy_timeout\n"); +END_DEBUG + xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0); + + xfer->act.hand = sbp_busy_timeout_callback; + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqq.dest_hi = 0xffff; + fp->mode.wreqq.dest_lo = 0xf0000000 | BUSY_TIMEOUT; + fp->mode.wreqq.data = htonl((1 << (13+12)) | 0xf); + fw_asyreq(xfer->fc, -1, xfer); +} + +static void +sbp_orb_pointer(struct sbp_dev *sdev, struct sbp_ocb *ocb) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; +SBP_DEBUG(2) + sbp_show_sdev_info(sdev, 2); + printf("sbp_orb_pointer\n"); +END_DEBUG + + xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0x08); + if (xfer == NULL) + return; + xfer->act.hand = sbp_cmd_callback; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqb.len = 8; + fp->mode.wreqb.extcode = 0; + fp->mode.wreqb.payload[0] = + htonl(((sdev->target->sbp->fd.fc->nodeid | FWLOCALBUS )<< 16)); + fp->mode.wreqb.payload[1] = htonl(ocb->bus_addr); + + if(fw_asyreq(xfer->fc, -1, xfer) != 0){ + sbp_xfer_free(xfer); + ocb->ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ocb->ccb); + } +} + +#if 0 +static void +sbp_doorbell(struct sbp_dev *sdev) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); + printf("sbp_doorbell\n"); +END_DEBUG + + xfer = sbp_write_cmd(sdev, FWTCODE_WREQQ, 0x10); + if (xfer == NULL) + return; + xfer->act.hand = sbp_cmd_callback; + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqq.data = htonl(0xf); + fw_asyreq(xfer->fc, -1, xfer); +} +#endif + +static struct fw_xfer * +sbp_write_cmd(struct sbp_dev *sdev, int tcode, int offset) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + struct sbp_target *target; + int s, new = 0; + + target = sdev->target; + s = splfw(); + xfer = STAILQ_FIRST(&target->xferlist); + if (xfer == NULL) { + if (target->n_xfer > 5 /* XXX */) { + printf("sbp: no more xfer for this target\n"); + splx(s); + return(NULL); + } + xfer = fw_xfer_alloc_buf(M_SBP, 24, 12); + if(xfer == NULL){ + printf("sbp: fw_xfer_alloc_buf failed\n"); + splx(s); + return NULL; + } + target->n_xfer ++; + if (debug) + printf("sbp: alloc %d xfer\n", target->n_xfer); + new = 1; + } else { + STAILQ_REMOVE_HEAD(&target->xferlist, link); + } + splx(s); + + microtime(&xfer->tv); + + if (tcode == FWTCODE_WREQQ) + xfer->send.len = 16; + else + xfer->send.len = 24; + xfer->recv.len = 12; + + if (new) { + xfer->spd = min(sdev->target->fwdev->speed, max_speed); + xfer->fc = sdev->target->sbp->fd.fc; + xfer->retry_req = fw_asybusy; + } + xfer->sc = (caddr_t)sdev; + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqq.dest_hi = sdev->login->cmd_hi; + fp->mode.wreqq.dest_lo = sdev->login->cmd_lo + offset; + fp->mode.wreqq.tlrt = 0; + fp->mode.wreqq.tcode = tcode; + fp->mode.wreqq.pri = 0; + xfer->dst = FWLOCALBUS | sdev->target->fwdev->dst; + fp->mode.wreqq.dst = xfer->dst; + + return xfer; + +} + +static void +sbp_mgm_orb(struct sbp_dev *sdev, int func, struct sbp_ocb *aocb) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + struct sbp_ocb *ocb; + struct sbp_target *target; + int s, nid; + + target = sdev->target; + nid = target->sbp->fd.fc->nodeid | FWLOCALBUS; + + s = splfw(); + if (func == ORB_FUN_RUNQUEUE) { + ocb = STAILQ_FIRST(&target->mgm_ocb_queue); + if (target->mgm_ocb_cur != NULL || ocb == NULL) { + splx(s); + return; + } + STAILQ_REMOVE_HEAD(&target->mgm_ocb_queue, ocb); + goto start; + } + if ((ocb = sbp_get_ocb(sdev)) == NULL) { + splx(s); + return; + } + ocb->flags = OCB_ACT_MGM; + ocb->sdev = sdev; + + bzero((void *)(uintptr_t)(volatile void *)ocb->orb, sizeof(ocb->orb)); + ocb->orb[6] = htonl((nid << 16) | SBP_BIND_HI); + ocb->orb[7] = htonl(SBP_DEV2ADDR( + device_get_unit(target->sbp->fd.dev), + target->target_id, + sdev->lun_id)); + +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("%s\n", orb_fun_name[(func>>16)&0xf]); +END_DEBUG + switch (func) { + case ORB_FUN_LGI: + ocb->orb[2] = htonl(nid << 16); + ocb->orb[3] = htonl(sdev->dma.bus_addr); + ocb->orb[4] = htonl(ORB_NOTIFY | ORB_EXV | sdev->lun_id); + ocb->orb[5] = htonl(SBP_LOGIN_SIZE); + fwdma_sync(&sdev->dma, BUS_DMASYNC_PREREAD); + break; + case ORB_FUN_ATA: + ocb->orb[0] = htonl((0 << 16) | 0); + ocb->orb[1] = htonl(aocb->bus_addr & 0xffffffff); + /* fall through */ + case ORB_FUN_RCN: + case ORB_FUN_LGO: + case ORB_FUN_LUR: + case ORB_FUN_RST: + case ORB_FUN_ATS: + ocb->orb[4] = htonl(ORB_NOTIFY | func | sdev->login->id); + break; + } + + if (target->mgm_ocb_cur != NULL) { + /* there is a standing ORB */ + STAILQ_INSERT_TAIL(&sdev->target->mgm_ocb_queue, ocb, ocb); + splx(s); + return; + } +start: + target->mgm_ocb_cur = ocb; + splx(s); + + callout_reset(&target->mgm_ocb_timeout, 5*hz, + sbp_mgm_timeout, (caddr_t)ocb); + xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0); + if(xfer == NULL){ + return; + } + xfer->act.hand = sbp_mgm_callback; + + fp = (struct fw_pkt *)xfer->send.buf; + fp->mode.wreqb.dest_hi = sdev->target->mgm_hi; + fp->mode.wreqb.dest_lo = sdev->target->mgm_lo; + fp->mode.wreqb.len = 8; + fp->mode.wreqb.extcode = 0; + fp->mode.wreqb.payload[0] = htonl(nid << 16); + fp->mode.wreqb.payload[1] = htonl(ocb->bus_addr); + + fw_asyreq(xfer->fc, -1, xfer); +} + +static void +sbp_print_scsi_cmd(struct sbp_ocb *ocb) +{ + struct ccb_scsiio *csio; + + csio = &ocb->ccb->csio; + printf("%s:%d:%d XPT_SCSI_IO: " + "cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x" + ", flags: 0x%02x, " + "%db cmd/%db data/%db sense\n", + device_get_nameunit(ocb->sdev->target->sbp->fd.dev), + ocb->ccb->ccb_h.target_id, ocb->ccb->ccb_h.target_lun, + csio->cdb_io.cdb_bytes[0], + csio->cdb_io.cdb_bytes[1], + csio->cdb_io.cdb_bytes[2], + csio->cdb_io.cdb_bytes[3], + csio->cdb_io.cdb_bytes[4], + csio->cdb_io.cdb_bytes[5], + csio->cdb_io.cdb_bytes[6], + csio->cdb_io.cdb_bytes[7], + csio->cdb_io.cdb_bytes[8], + csio->cdb_io.cdb_bytes[9], + ocb->ccb->ccb_h.flags & CAM_DIR_MASK, + csio->cdb_len, csio->dxfer_len, + csio->sense_len); +} + +static void +sbp_scsi_status(struct sbp_status *sbp_status, struct sbp_ocb *ocb) +{ + struct sbp_cmd_status *sbp_cmd_status; + struct scsi_sense_data *sense; + + sbp_cmd_status = (struct sbp_cmd_status *)sbp_status->data; + sense = &ocb->ccb->csio.sense_data; + +SBP_DEBUG(0) + sbp_print_scsi_cmd(ocb); + /* XXX need decode status */ + sbp_show_sdev_info(ocb->sdev, 2); + printf("SCSI status %x sfmt %x valid %x key %x code %x qlfr %x len %d\n", + sbp_cmd_status->status, + sbp_cmd_status->sfmt, + sbp_cmd_status->valid, + sbp_cmd_status->s_key, + sbp_cmd_status->s_code, + sbp_cmd_status->s_qlfr, + sbp_status->len + ); +END_DEBUG + + switch (sbp_cmd_status->status) { + case SCSI_STATUS_CHECK_COND: + case SCSI_STATUS_BUSY: + case SCSI_STATUS_CMD_TERMINATED: + if(sbp_cmd_status->sfmt == SBP_SFMT_CURR){ + sense->error_code = SSD_CURRENT_ERROR; + }else{ + sense->error_code = SSD_DEFERRED_ERROR; + } + if(sbp_cmd_status->valid) + sense->error_code |= SSD_ERRCODE_VALID; + sense->flags = sbp_cmd_status->s_key; + if(sbp_cmd_status->mark) + sense->flags |= SSD_FILEMARK; + if(sbp_cmd_status->eom) + sense->flags |= SSD_EOM; + if(sbp_cmd_status->ill_len) + sense->flags |= SSD_ILI; + sense->info[0] = ntohl(sbp_cmd_status->info) & 0xff; + sense->info[1] =(ntohl(sbp_cmd_status->info) >> 8) & 0xff; + sense->info[2] =(ntohl(sbp_cmd_status->info) >> 16) & 0xff; + sense->info[3] =(ntohl(sbp_cmd_status->info) >> 24) & 0xff; + if (sbp_status->len <= 1) + /* XXX not scsi status. shouldn't be happened */ + sense->extra_len = 0; + else if (sbp_status->len <= 4) + /* add_sense_code(_qual), info, cmd_spec_info */ + sense->extra_len = 6; + else + /* fru, sense_key_spec */ + sense->extra_len = 10; + sense->cmd_spec_info[0] = ntohl(sbp_cmd_status->cdb) & 0xff; + sense->cmd_spec_info[1] = (ntohl(sbp_cmd_status->cdb) >> 8) & 0xff; + sense->cmd_spec_info[2] = (ntohl(sbp_cmd_status->cdb) >> 16) & 0xff; + sense->cmd_spec_info[3] = (ntohl(sbp_cmd_status->cdb) >> 24) & 0xff; + sense->add_sense_code = sbp_cmd_status->s_code; + sense->add_sense_code_qual = sbp_cmd_status->s_qlfr; + sense->fru = sbp_cmd_status->fru; + sense->sense_key_spec[0] = ntohl(sbp_cmd_status->s_keydep) & 0xff; + sense->sense_key_spec[1] = (ntohl(sbp_cmd_status->s_keydep) >>8) & 0xff; + sense->sense_key_spec[2] = (ntohl(sbp_cmd_status->s_keydep) >>16) & 0xff; + + ocb->ccb->csio.scsi_status = sbp_cmd_status->status;; + ocb->ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR + | CAM_AUTOSNS_VALID; +/* +{ + u_int8_t j, *tmp; + tmp = sense; + for( j = 0 ; j < 32 ; j+=8){ + printf("sense %02x%02x %02x%02x %02x%02x %02x%02x\n", + tmp[j], tmp[j+1], tmp[j+2], tmp[j+3], + tmp[j+4], tmp[j+5], tmp[j+6], tmp[j+7]); + } + +} +*/ + break; + default: + sbp_show_sdev_info(ocb->sdev, 2); + printf("sbp_scsi_status: unknown scsi status 0x%x\n", + sbp_cmd_status->status); + } +} + +static void +sbp_fix_inq_data(struct sbp_ocb *ocb) +{ + union ccb *ccb; + struct sbp_dev *sdev; + struct scsi_inquiry_data *inq; + + ccb = ocb->ccb; + sdev = ocb->sdev; + + if (ccb->csio.cdb_io.cdb_bytes[1] & SI_EVPD) + return; +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); + printf("sbp_fix_inq_data\n"); +END_DEBUG + inq = (struct scsi_inquiry_data *) ccb->csio.data_ptr; + switch (SID_TYPE(inq)) { + case T_DIRECT: + /* + * XXX Convert Direct Access device to RBC. + * I've never seen FireWire DA devices which support READ_6. + */ +#if 1 + if (SID_TYPE(inq) == T_DIRECT) + inq->device |= T_RBC; /* T_DIRECT == 0 */ +#endif + /* fall through */ + case T_RBC: + /* enable tag queuing */ +#if 1 + inq->flags |= SID_CmdQue; +#endif + /* + * Override vendor/product/revision information. + * Some devices sometimes return strange strings. + */ +#if 1 + bcopy(sdev->vendor, inq->vendor, sizeof(inq->vendor)); + bcopy(sdev->product, inq->product, sizeof(inq->product)); + bcopy(sdev->revision+2, inq->revision, sizeof(inq->revision)); +#endif + break; + } +} + +static void +sbp_recv1(struct fw_xfer *xfer) +{ + struct fw_pkt *rfp; +#if NEED_RESPONSE + struct fw_pkt *sfp; +#endif + struct sbp_softc *sbp; + struct sbp_dev *sdev; + struct sbp_ocb *ocb; + struct sbp_login_res *login_res = NULL; + struct sbp_status *sbp_status; + struct sbp_target *target; + int orb_fun, status_valid0, status_valid, t, l, reset_agent = 0; + u_int32_t addr; +/* + u_int32_t *ld; + ld = xfer->recv.buf; +printf("sbp %x %d %d %08x %08x %08x %08x\n", + xfer->resp, xfer->recv.len, xfer->recv.off, ntohl(ld[0]), ntohl(ld[1]), ntohl(ld[2]), ntohl(ld[3])); +printf("sbp %08x %08x %08x %08x\n", ntohl(ld[4]), ntohl(ld[5]), ntohl(ld[6]), ntohl(ld[7])); +printf("sbp %08x %08x %08x %08x\n", ntohl(ld[8]), ntohl(ld[9]), ntohl(ld[10]), ntohl(ld[11])); +*/ + + sbp = (struct sbp_softc *)xfer->sc; + if(xfer->resp != 0){ + printf("sbp_recv: xfer->resp != 0\n"); + goto done0; + } + if(xfer->recv.buf == NULL){ + printf("sbp_recv: xfer->recv.buf == NULL\n"); + goto done0; + } + sbp = (struct sbp_softc *)xfer->sc; + rfp = (struct fw_pkt *)xfer->recv.buf; + if(rfp->mode.wreqb.tcode != FWTCODE_WREQB){ + printf("sbp_recv: tcode = %d\n", rfp->mode.wreqb.tcode); + goto done0; + } + sbp_status = (struct sbp_status *)rfp->mode.wreqb.payload; + addr = rfp->mode.wreqb.dest_lo; +SBP_DEBUG(2) + printf("received address 0x%x\n", addr); +END_DEBUG + t = SBP_ADDR2TRG(addr); + if (t >= SBP_NUM_TARGETS) { + device_printf(sbp->fd.dev, + "sbp_recv1: invalid target %d\n", t); + goto done0; + } + target = &sbp->targets[t]; + l = SBP_ADDR2LUN(addr); + if (l >= target->num_lun) { + device_printf(sbp->fd.dev, + "sbp_recv1: invalid lun %d (target=%d)\n", l, t); + goto done0; + } + sdev = &target->luns[l]; + + ocb = NULL; + switch (sbp_status->src) { + case 0: + case 1: + /* check mgm_ocb_cur first */ + ocb = target->mgm_ocb_cur; + if (ocb != NULL) { + if (OCB_MATCH(ocb, sbp_status)) { + callout_stop(&target->mgm_ocb_timeout); + target->mgm_ocb_cur = NULL; + break; + } + } + ocb = sbp_dequeue_ocb(sdev, sbp_status); + if (ocb == NULL) { + sbp_show_sdev_info(sdev, 2); +#if __FreeBSD_version >= 500000 + printf("No ocb(%x) on the queue\n", +#else + printf("No ocb(%lx) on the queue\n", +#endif + ntohl(sbp_status->orb_lo)); + } + break; + case 2: + /* unsolicit */ + sbp_show_sdev_info(sdev, 2); + printf("unsolicit status received\n"); + break; + default: + sbp_show_sdev_info(sdev, 2); + printf("unknown sbp_status->src\n"); + } + + status_valid0 = (sbp_status->src < 2 + && sbp_status->resp == ORB_RES_CMPL + && sbp_status->dead == 0); + status_valid = (status_valid0 && sbp_status->status == 0); + + if (!status_valid0 || debug > 1){ + int status; +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("ORB status src:%x resp:%x dead:%x" +#if __FreeBSD_version >= 500000 + " len:%x stat:%x orb:%x%08x\n", +#else + " len:%x stat:%x orb:%x%08lx\n", +#endif + sbp_status->src, sbp_status->resp, sbp_status->dead, + sbp_status->len, sbp_status->status, + ntohs(sbp_status->orb_hi), ntohl(sbp_status->orb_lo)); +END_DEBUG + sbp_show_sdev_info(sdev, 2); + status = sbp_status->status; + switch(sbp_status->resp) { + case 0: + if (status > MAX_ORB_STATUS0) + printf("%s\n", orb_status0[MAX_ORB_STATUS0]); + else + printf("%s\n", orb_status0[status]); + break; + case 1: + printf("Obj: %s, Error: %s\n", + orb_status1_object[(status>>6) & 3], + orb_status1_serial_bus_error[status & 0xf]); + break; + case 2: + printf("Illegal request\n"); + break; + case 3: + printf("Vendor dependent\n"); + break; + default: + printf("unknown respose code %d\n", sbp_status->resp); + } + } + + /* we have to reset the fetch agent if it's dead */ + if (sbp_status->dead) { + if (sdev->path) { + xpt_freeze_devq(sdev->path, 1); + sdev->freeze ++; + } + reset_agent = 1; + } + + if (ocb == NULL) + goto done; + + switch(ntohl(ocb->orb[4]) & ORB_FMT_MSK){ + case ORB_FMT_NOP: + break; + case ORB_FMT_VED: + break; + case ORB_FMT_STD: + switch(ocb->flags) { + case OCB_ACT_MGM: + orb_fun = ntohl(ocb->orb[4]) & ORB_FUN_MSK; + switch(orb_fun) { + case ORB_FUN_LGI: + fwdma_sync(&sdev->dma, BUS_DMASYNC_POSTREAD); + login_res = sdev->login; + login_res->len = ntohs(login_res->len); + login_res->id = ntohs(login_res->id); + login_res->cmd_hi = ntohs(login_res->cmd_hi); + login_res->cmd_lo = ntohl(login_res->cmd_lo); + if (status_valid) { +SBP_DEBUG(0) +sbp_show_sdev_info(sdev, 2); +printf("login: len %d, ID %d, cmd %08x%08x, recon_hold %d\n", login_res->len, login_res->id, login_res->cmd_hi, login_res->cmd_lo, ntohs(login_res->recon_hold)); +END_DEBUG + sbp_busy_timeout(sdev); + } else { + /* forgot logout? */ + sbp_show_sdev_info(sdev, 2); + printf("login failed\n"); + sdev->status = SBP_DEV_RESET; + } + break; + case ORB_FUN_RCN: + login_res = sdev->login; + if (status_valid) { +SBP_DEBUG(0) +sbp_show_sdev_info(sdev, 2); +printf("reconnect: len %d, ID %d, cmd %08x%08x\n", login_res->len, login_res->id, login_res->cmd_hi, login_res->cmd_lo); +END_DEBUG +#if 1 + if (sdev->status == SBP_DEV_ATTACHED) + sbp_scan_dev(sdev); + else + sbp_agent_reset(sdev); +#else + sdev->status = SBP_DEV_ATTACHED; + sbp_mgm_orb(sdev, ORB_FUN_ATS, NULL); +#endif + } else { + /* reconnection hold time exceed? */ +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); + printf("reconnect failed\n"); +END_DEBUG + sbp_login(sdev); + } + break; + case ORB_FUN_LGO: + sdev->status = SBP_DEV_RESET; + break; + case ORB_FUN_RST: + sbp_busy_timeout(sdev); + break; + case ORB_FUN_LUR: + case ORB_FUN_ATA: + case ORB_FUN_ATS: + sbp_agent_reset(sdev); + break; + default: + sbp_show_sdev_info(sdev, 2); + printf("unknown function %d\n", orb_fun); + break; + } + sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL); + break; + case OCB_ACT_CMD: + sdev->timeout = 0; + if(ocb->ccb != NULL){ + union ccb *ccb; +/* + u_int32_t *ld; + ld = ocb->ccb->csio.data_ptr; + if(ld != NULL && ocb->ccb->csio.dxfer_len != 0) + printf("ptr %08x %08x %08x %08x\n", ld[0], ld[1], ld[2], ld[3]); + else + printf("ptr NULL\n"); +printf("len %d\n", sbp_status->len); +*/ + ccb = ocb->ccb; + if(sbp_status->len > 1){ + sbp_scsi_status(sbp_status, ocb); + }else{ + if(sbp_status->resp != ORB_RES_CMPL){ + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + }else{ + ccb->ccb_h.status = CAM_REQ_CMP; + } + } + /* fix up inq data */ + if (ccb->csio.cdb_io.cdb_bytes[0] == INQUIRY) + sbp_fix_inq_data(ocb); + xpt_done(ccb); + } + break; + default: + break; + } + } + + sbp_free_ocb(sdev, ocb); +done: + if (reset_agent) + sbp_agent_reset(sdev); + +done0: +/* The received packet is usually small enough to be stored within + * the buffer. In that case, the controller return ack_complete and + * no respose is necessary. + * + * XXX fwohci.c and firewire.c should inform event_code such as + * ack_complete or ack_pending to upper driver. + */ +#if NEED_RESPONSE + xfer->send.off = 0; + sfp = (struct fw_pkt *)xfer->send.buf; + sfp->mode.wres.dst = rfp->mode.wreqb.src; + xfer->dst = sfp->mode.wres.dst; + xfer->spd = min(sdev->target->fwdev->speed, max_speed); + xfer->act.hand = sbp_loginres_callback; + xfer->retry_req = fw_asybusy; + + sfp->mode.wres.tlrt = rfp->mode.wreqb.tlrt; + sfp->mode.wres.tcode = FWTCODE_WRES; + sfp->mode.wres.rtcode = 0; + sfp->mode.wres.pri = 0; + + fw_asyreq(xfer->fc, -1, xfer); +#else + /* recycle */ + xfer->recv.len = SBP_RECV_LEN; + STAILQ_INSERT_TAIL(&sbp->fwb.xferlist, xfer, link); +#endif + + return; + +} + +static void +sbp_recv(struct fw_xfer *xfer) +{ + int s; + + s = splcam(); + sbp_recv1(xfer); + splx(s); +} +/* + * sbp_attach() + */ +static int +sbp_attach(device_t dev) +{ + struct sbp_softc *sbp; + struct cam_devq *devq; + struct fw_xfer *xfer; + int i, s, error; + +SBP_DEBUG(0) + printf("sbp_attach (cold=%d)\n", cold); +END_DEBUG + + if (cold) + sbp_cold ++; + sbp = ((struct sbp_softc *)device_get_softc(dev)); + bzero(sbp, sizeof(struct sbp_softc)); + sbp->fd.dev = dev; + sbp->fd.fc = device_get_ivars(dev); + error = bus_dma_tag_create(/*parent*/sbp->fd.fc->dmat, + /* XXX shoud be 4 for sane backend? */ + /*alignment*/1, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/0x100000, /*nsegments*/SBP_IND_MAX, + /*maxsegsz*/SBP_SEG_MAX, + /*flags*/BUS_DMA_ALLOCNOW, + &sbp->dmat); + if (error != 0) { + printf("sbp_attach: Could not allocate DMA tag " + "- error %d\n", error); + return (ENOMEM); + } + + devq = cam_simq_alloc(/*maxopenings*/SBP_NUM_OCB); + if (devq == NULL) + return (ENXIO); + + for( i = 0 ; i < SBP_NUM_TARGETS ; i++){ + sbp->targets[i].fwdev = NULL; + sbp->targets[i].luns = NULL; + } + + sbp->sim = cam_sim_alloc(sbp_action, sbp_poll, "sbp", sbp, + device_get_unit(dev), + /*untagged*/ 1, + /*tagged*/ SBP_QUEUE_LEN, + devq); + + if (sbp->sim == NULL) { + cam_simq_free(devq); + return (ENXIO); + } + + + if (xpt_bus_register(sbp->sim, /*bus*/0) != CAM_SUCCESS) + goto fail; + + if (xpt_create_path(&sbp->path, xpt_periph, cam_sim_path(sbp->sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) + goto fail; + + sbp->fwb.start_hi = SBP_BIND_HI; + sbp->fwb.start_lo = SBP_DEV2ADDR(device_get_unit(sbp->fd.dev), 0, 0); + /* We reserve 16 bit space (4 bytes X 64 targets X 256 luns) */ + sbp->fwb.addrlen = 0xffff; + sbp->fwb.act_type = FWACT_XFER; + /* pre-allocate xfer */ + STAILQ_INIT(&sbp->fwb.xferlist); + for (i = 0; i < SBP_NUM_OCB/2; i ++) { + xfer = fw_xfer_alloc_buf(M_SBP, +#if NEED_RESPONSE + /* send */12, +#else + /* send */0, +#endif + /* recv */SBP_RECV_LEN); + xfer->act.hand = sbp_recv; +#if NEED_RESPONSE + xfer->fc = sbp->fd.fc; +#endif + xfer->sc = (caddr_t)sbp; + STAILQ_INSERT_TAIL(&sbp->fwb.xferlist, xfer, link); + } + fw_bindadd(sbp->fd.fc, &sbp->fwb); + + sbp->fd.post_busreset = sbp_post_busreset; + sbp->fd.post_explore = sbp_post_explore; + + if (sbp->fd.fc->status != -1) { + s = splfw(); + sbp_post_explore((void *)sbp); + splx(s); + } + + return (0); +fail: + cam_sim_free(sbp->sim, /*free_devq*/TRUE); + return (ENXIO); +} + +static int +sbp_logout_all(struct sbp_softc *sbp) +{ + struct sbp_target *target; + struct sbp_dev *sdev; + int i, j; + +SBP_DEBUG(0) + printf("sbp_logout_all\n"); +END_DEBUG + for (i = 0 ; i < SBP_NUM_TARGETS ; i ++) { + target = &sbp->targets[i]; + if (target->luns == NULL) + continue; + for (j = 0; j < target->num_lun; j++) { + sdev = &target->luns[j]; + callout_stop(&sdev->login_callout); + if (sdev->status >= SBP_DEV_TOATTACH && + sdev->status <= SBP_DEV_ATTACHED) + sbp_mgm_orb(sdev, ORB_FUN_LGO, NULL); + } + } + + return 0; +} + +static int +sbp_shutdown(device_t dev) +{ + struct sbp_softc *sbp = ((struct sbp_softc *)device_get_softc(dev)); + + sbp_logout_all(sbp); + return (0); +} + +static int +sbp_detach(device_t dev) +{ + struct sbp_softc *sbp = ((struct sbp_softc *)device_get_softc(dev)); + struct firewire_comm *fc = sbp->fd.fc; + struct sbp_target *target; + struct sbp_dev *sdev; + struct fw_xfer *xfer, *next; + int i, j; + +SBP_DEBUG(0) + printf("sbp_detach\n"); +END_DEBUG + + for (i = 0; i < SBP_NUM_TARGETS; i ++) + sbp_cam_detach_target(&sbp->targets[i]); + xpt_free_path(sbp->path); + xpt_bus_deregister(cam_sim_path(sbp->sim)); + + sbp_logout_all(sbp); + + /* XXX wait for logout completion */ + tsleep(&i, FWPRI, "sbpdtc", hz/2); + + for (i = 0 ; i < SBP_NUM_TARGETS ; i ++) { + target = &sbp->targets[i]; + if (target->luns == NULL) + continue; + callout_stop(&target->mgm_ocb_timeout); + for (j = 0; j < target->num_lun; j++) { + sdev = &target->luns[j]; + if (sdev->status != SBP_DEV_DEAD) { + for (i = 0; i < SBP_QUEUE_LEN; i++) + bus_dmamap_destroy(sbp->dmat, + sdev->ocb[i].dmamap); + fwdma_free(sbp->fd.fc, &sdev->dma); + } + } + for (xfer = STAILQ_FIRST(&target->xferlist); + xfer != NULL; xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free(xfer); + } + free(target->luns, M_SBP); + } + + for (xfer = STAILQ_FIRST(&sbp->fwb.xferlist); + xfer != NULL; xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free(xfer); + } + STAILQ_INIT(&sbp->fwb.xferlist); + fw_bindremove(fc, &sbp->fwb); + + bus_dma_tag_destroy(sbp->dmat); + + return (0); +} + +static void +sbp_cam_detach_target(struct sbp_target *target) +{ + struct sbp_dev *sdev; + int i; + + if (target->luns != NULL) { +SBP_DEBUG(0) + printf("sbp_detach_target %d\n", target->target_id); +END_DEBUG + callout_stop(&target->scan_callout); + for (i = 0; i < target->num_lun; i++) { + sdev = &target->luns[i]; + if (sdev->status == SBP_DEV_DEAD) + continue; + if (sdev->status == SBP_DEV_RESET) + continue; + if (sdev->path) { + xpt_release_devq(sdev->path, + sdev->freeze, TRUE); + sdev->freeze = 0; + xpt_async(AC_LOST_DEVICE, sdev->path, NULL); + xpt_free_path(sdev->path); + sdev->path = NULL; + } + sbp_abort_all_ocbs(sdev, CAM_DEV_NOT_THERE); + } + } +} + +static void +sbp_target_reset(struct sbp_dev *sdev, int method) +{ + int i; + struct sbp_target *target = sdev->target; + struct sbp_dev *tsdev; + + for (i = 0; i < target->num_lun; i++) { + tsdev = &target->luns[i]; + if (tsdev->status == SBP_DEV_DEAD) + continue; + if (tsdev->status == SBP_DEV_RESET) + continue; + xpt_freeze_devq(tsdev->path, 1); + tsdev->freeze ++; + sbp_abort_all_ocbs(tsdev, CAM_CMD_TIMEOUT); + if (method == 2) + tsdev->status = SBP_DEV_LOGIN; + } + switch(method) { + case 1: + printf("target reset\n"); + sbp_mgm_orb(sdev, ORB_FUN_RST, NULL); + break; + case 2: + printf("reset start\n"); + sbp_reset_start(sdev); + break; + } + +} + +static void +sbp_mgm_timeout(void *arg) +{ + struct sbp_ocb *ocb = (struct sbp_ocb *)arg; + struct sbp_dev *sdev = ocb->sdev; + struct sbp_target *target = sdev->target; + + sbp_show_sdev_info(sdev, 2); + printf("management ORB timeout\n"); + target->mgm_ocb_cur = NULL; + sbp_free_ocb(sdev, ocb); +#if 0 + /* XXX */ + sbp_mgm_orb(sdev, ORB_FUN_RUNQUEUE, NULL); +#endif +#if 0 + sbp_reset_start(sdev); +#endif +} + +static void +sbp_timeout(void *arg) +{ + struct sbp_ocb *ocb = (struct sbp_ocb *)arg; + struct sbp_dev *sdev = ocb->sdev; + + sbp_show_sdev_info(sdev, 2); + printf("request timeout ... "); + + sdev->timeout ++; + switch(sdev->timeout) { + case 1: + printf("agent reset\n"); + xpt_freeze_devq(sdev->path, 1); + sdev->freeze ++; + sbp_abort_all_ocbs(sdev, CAM_CMD_TIMEOUT); + sbp_agent_reset(sdev); + break; + case 2: + case 3: + sbp_target_reset(sdev, sdev->timeout - 1); + break; +#if 0 + default: + /* XXX give up */ + sbp_cam_detach_target(target); + if (target->luns != NULL) + free(target->luns, M_SBP); + target->num_lun = 0;; + target->luns = NULL; + target->fwdev = NULL; +#endif + } +} + +static void +sbp_action1(struct cam_sim *sim, union ccb *ccb) +{ + + struct sbp_softc *sbp = (struct sbp_softc *)sim->softc; + struct sbp_target *target = NULL; + struct sbp_dev *sdev = NULL; + + /* target:lun -> sdev mapping */ + if (sbp != NULL + && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD + && ccb->ccb_h.target_id < SBP_NUM_TARGETS) { + target = &sbp->targets[ccb->ccb_h.target_id]; + if (target->fwdev != NULL + && ccb->ccb_h.target_lun != CAM_LUN_WILDCARD + && ccb->ccb_h.target_lun < target->num_lun) { + sdev = &target->luns[ccb->ccb_h.target_lun]; + if (sdev->status != SBP_DEV_ATTACHED && + sdev->status != SBP_DEV_PROBE) + sdev = NULL; + } + } + +SBP_DEBUG(1) + if (sdev == NULL) + printf("invalid target %d lun %d\n", + ccb->ccb_h.target_id, ccb->ccb_h.target_lun); +END_DEBUG + + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + case XPT_RESET_DEV: + case XPT_GET_TRAN_SETTINGS: + case XPT_SET_TRAN_SETTINGS: + case XPT_CALC_GEOMETRY: + if (sdev == NULL) { +SBP_DEBUG(1) + printf("%s:%d:%d:func_code 0x%04x: " + "Invalid target (target needed)\n", + device_get_nameunit(sbp->fd.dev), + ccb->ccb_h.target_id, ccb->ccb_h.target_lun, + ccb->ccb_h.func_code); +END_DEBUG + + ccb->ccb_h.status = CAM_DEV_NOT_THERE; + xpt_done(ccb); + return; + } + break; + case XPT_PATH_INQ: + case XPT_NOOP: + /* The opcodes sometimes aimed at a target (sc is valid), + * sometimes aimed at the SIM (sc is invalid and target is + * CAM_TARGET_WILDCARD) + */ + if (sbp == NULL && + ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) { +SBP_DEBUG(0) + printf("%s:%d:%d func_code 0x%04x: " + "Invalid target (no wildcard)\n", + device_get_nameunit(sbp->fd.dev), + ccb->ccb_h.target_id, ccb->ccb_h.target_lun, + ccb->ccb_h.func_code); +END_DEBUG + ccb->ccb_h.status = CAM_DEV_NOT_THERE; + xpt_done(ccb); + return; + } + break; + default: + /* XXX Hm, we should check the input parameters */ + break; + } + + switch (ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + { + struct ccb_scsiio *csio; + struct sbp_ocb *ocb; + int speed; + void *cdb; + + csio = &ccb->csio; + +SBP_DEBUG(1) + printf("%s:%d:%d XPT_SCSI_IO: " + "cmd: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x" + ", flags: 0x%02x, " + "%db cmd/%db data/%db sense\n", + device_get_nameunit(sbp->fd.dev), + ccb->ccb_h.target_id, ccb->ccb_h.target_lun, + csio->cdb_io.cdb_bytes[0], + csio->cdb_io.cdb_bytes[1], + csio->cdb_io.cdb_bytes[2], + csio->cdb_io.cdb_bytes[3], + csio->cdb_io.cdb_bytes[4], + csio->cdb_io.cdb_bytes[5], + csio->cdb_io.cdb_bytes[6], + csio->cdb_io.cdb_bytes[7], + csio->cdb_io.cdb_bytes[8], + csio->cdb_io.cdb_bytes[9], + ccb->ccb_h.flags & CAM_DIR_MASK, + csio->cdb_len, csio->dxfer_len, + csio->sense_len); +END_DEBUG + if(sdev == NULL){ + ccb->ccb_h.status = CAM_DEV_NOT_THERE; + xpt_done(ccb); + return; + } +#if 0 + /* if we are in probe stage, pass only probe commands */ + if (sdev->status == SBP_DEV_PROBE) { + char *name; + name = xpt_path_periph(ccb->ccb_h.path)->periph_name; + printf("probe stage, periph name: %s\n", name); + if (strcmp(name, "probe") != 0) { + ccb->ccb_h.status = CAM_REQUEUE_REQ; + xpt_done(ccb); + return; + } + } +#endif + if ((ocb = sbp_get_ocb(sdev)) == NULL) + return; + + ocb->flags = OCB_ACT_CMD; + ocb->sdev = sdev; + ocb->ccb = ccb; + ccb->ccb_h.ccb_sdev_ptr = sdev; + ocb->orb[0] = htonl(1 << 31); + ocb->orb[1] = 0; + ocb->orb[2] = htonl(((sbp->fd.fc->nodeid | FWLOCALBUS )<< 16) ); + ocb->orb[3] = htonl(ocb->bus_addr + IND_PTR_OFFSET); + speed = min(target->fwdev->speed, max_speed); + ocb->orb[4] = htonl(ORB_NOTIFY | ORB_CMD_SPD(speed) + | ORB_CMD_MAXP(speed + 7)); + if((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN){ + ocb->orb[4] |= htonl(ORB_CMD_IN); + } + + if (csio->ccb_h.flags & CAM_SCATTER_VALID) + printf("sbp: CAM_SCATTER_VALID\n"); + if (csio->ccb_h.flags & CAM_DATA_PHYS) + printf("sbp: CAM_DATA_PHYS\n"); + + if (csio->ccb_h.flags & CAM_CDB_POINTER) + cdb = (void *)csio->cdb_io.cdb_ptr; + else + cdb = (void *)&csio->cdb_io.cdb_bytes; + bcopy(cdb, + (void *)(uintptr_t)(volatile void *)&ocb->orb[5], + csio->cdb_len); +/* +printf("ORB %08x %08x %08x %08x\n", ntohl(ocb->orb[0]), ntohl(ocb->orb[1]), ntohl(ocb->orb[2]), ntohl(ocb->orb[3])); +printf("ORB %08x %08x %08x %08x\n", ntohl(ocb->orb[4]), ntohl(ocb->orb[5]), ntohl(ocb->orb[6]), ntohl(ocb->orb[7])); +*/ + if (ccb->csio.dxfer_len > 0) { + int s, error; + + s = splsoftvm(); + error = bus_dmamap_load(/*dma tag*/sbp->dmat, + /*dma map*/ocb->dmamap, + ccb->csio.data_ptr, + ccb->csio.dxfer_len, + sbp_execute_ocb, + ocb, + /*flags*/0); + splx(s); + if (error) + printf("sbp: bus_dmamap_load error %d\n", error); + } else + sbp_execute_ocb(ocb, NULL, 0, 0); + break; + } + case XPT_CALC_GEOMETRY: + { + struct ccb_calc_geometry *ccg; +#if __FreeBSD_version < 501100 + u_int32_t size_mb; + u_int32_t secs_per_cylinder; + int extended = 1; +#endif + + ccg = &ccb->ccg; + if (ccg->block_size == 0) { + printf("sbp_action1: block_size is 0.\n"); + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +SBP_DEBUG(1) + printf("%s:%d:%d:%d:XPT_CALC_GEOMETRY: " +#if __FreeBSD_version >= 500000 + "Volume size = %jd\n", +#else + "Volume size = %d\n", +#endif + device_get_nameunit(sbp->fd.dev), + cam_sim_path(sbp->sim), + ccb->ccb_h.target_id, ccb->ccb_h.target_lun, +#if __FreeBSD_version >= 500000 + (uintmax_t) +#endif + ccg->volume_size); +END_DEBUG + +#if __FreeBSD_version < 501100 + size_mb = ccg->volume_size + / ((1024L * 1024L) / ccg->block_size); + + if (size_mb > 1024 && extended) { + ccg->heads = 255; + ccg->secs_per_track = 63; + } else { + ccg->heads = 64; + ccg->secs_per_track = 32; + } + secs_per_cylinder = ccg->heads * ccg->secs_per_track; + ccg->cylinders = ccg->volume_size / secs_per_cylinder; + ccb->ccb_h.status = CAM_REQ_CMP; +#else + cam_calc_geometry(ccg, /*extended*/1); +#endif + xpt_done(ccb); + break; + } + case XPT_RESET_BUS: /* Reset the specified SCSI bus */ + { + +SBP_DEBUG(1) + printf("%s:%d:XPT_RESET_BUS: \n", + device_get_nameunit(sbp->fd.dev), cam_sim_path(sbp->sim)); +END_DEBUG + + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + case XPT_PATH_INQ: /* Path routing inquiry */ + { + struct ccb_pathinq *cpi = &ccb->cpi; + +SBP_DEBUG(1) + printf("%s:%d:%d XPT_PATH_INQ:.\n", + device_get_nameunit(sbp->fd.dev), + ccb->ccb_h.target_id, ccb->ccb_h.target_lun); +END_DEBUG + cpi->version_num = 1; /* XXX??? */ + cpi->hba_inquiry = PI_TAG_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = PIM_NOBUSRESET; + cpi->hba_eng_cnt = 0; + cpi->max_target = SBP_NUM_TARGETS - 1; + cpi->max_lun = SBP_NUM_LUNS - 1; + cpi->initiator_id = SBP_INITIATOR; + 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", 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_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts = &ccb->cts; +SBP_DEBUG(1) + printf("%s:%d:%d XPT_GET_TRAN_SETTINGS:.\n", + device_get_nameunit(sbp->fd.dev), + ccb->ccb_h.target_id, ccb->ccb_h.target_lun); +END_DEBUG + /* Enable disconnect and tagged queuing */ + cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; + cts->flags = CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB; + + cts->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + break; + } + case XPT_ABORT: + ccb->ccb_h.status = CAM_UA_ABORT; + xpt_done(ccb); + break; + case XPT_SET_TRAN_SETTINGS: + /* XXX */ + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } + return; +} + +static void +sbp_action(struct cam_sim *sim, union ccb *ccb) +{ + int s; + + s = splfw(); + sbp_action1(sim, ccb); + splx(s); +} + +static void +sbp_execute_ocb(void *arg, bus_dma_segment_t *segments, int seg, int error) +{ + int i; + struct sbp_ocb *ocb; + struct sbp_ocb *prev; + bus_dma_segment_t *s; + + if (error) + printf("sbp_execute_ocb: error=%d\n", error); + + ocb = (struct sbp_ocb *)arg; + +SBP_DEBUG(1) + printf("sbp_execute_ocb: seg %d", seg); + for (i = 0; i < seg; i++) +#if __FreeBSD_version >= 500000 + printf(", %jx:%jd", (uintmax_t)segments[i].ds_addr, + (uintmax_t)segments[i].ds_len); +#else + printf(", %x:%d", segments[i].ds_addr, segments[i].ds_len); +#endif + printf("\n"); +END_DEBUG + + if (seg == 1) { + /* direct pointer */ + s = &segments[0]; + if (s->ds_len > SBP_SEG_MAX) + panic("ds_len > SBP_SEG_MAX, fix busdma code"); + ocb->orb[3] = htonl(s->ds_addr); + ocb->orb[4] |= htonl(s->ds_len); + } else if(seg > 1) { + /* page table */ + for (i = 0; i < seg; i++) { + s = &segments[i]; +SBP_DEBUG(0) + /* XXX LSI Logic "< 16 byte" bug might be hit */ + if (s->ds_len < 16) + printf("sbp_execute_ocb: warning, " +#if __FreeBSD_version >= 500000 + "segment length(%zd) is less than 16." +#else + "segment length(%d) is less than 16." +#endif + "(seg=%d/%d)\n", s->ds_len, i+1, seg); +END_DEBUG + if (s->ds_len > SBP_SEG_MAX) + panic("ds_len > SBP_SEG_MAX, fix busdma code"); + ocb->ind_ptr[i].hi = htonl(s->ds_len << 16); + ocb->ind_ptr[i].lo = htonl(s->ds_addr); + } + ocb->orb[4] |= htonl(ORB_CMD_PTBL | seg); + } + + if (seg > 0) + bus_dmamap_sync(ocb->sdev->target->sbp->dmat, ocb->dmamap, + (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + prev = sbp_enqueue_ocb(ocb->sdev, ocb); + fwdma_sync(&ocb->sdev->dma, BUS_DMASYNC_PREWRITE); + if (prev == NULL) + sbp_orb_pointer(ocb->sdev, ocb); +} + +static void +sbp_poll(struct cam_sim *sim) +{ + /* should call fwohci_intr? */ + return; +} +static struct sbp_ocb * +sbp_dequeue_ocb(struct sbp_dev *sdev, struct sbp_status *sbp_status) +{ + struct sbp_ocb *ocb; + struct sbp_ocb *next; + int s = splfw(), order = 0; + int flags; + + for (ocb = STAILQ_FIRST(&sdev->ocbs); ocb != NULL; ocb = next) { + next = STAILQ_NEXT(ocb, ocb); + flags = ocb->flags; +SBP_DEBUG(1) + sbp_show_sdev_info(sdev, 2); +#if __FreeBSD_version >= 500000 + printf("orb: 0x%jx next: 0x%x, flags %x\n", + (uintmax_t)ocb->bus_addr, +#else + printf("orb: 0x%x next: 0x%lx, flags %x\n", + ocb->bus_addr, +#endif + ntohl(ocb->orb[1]), flags); +END_DEBUG + if (OCB_MATCH(ocb, sbp_status)) { + /* found */ + STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb); + if (ocb->ccb != NULL) + untimeout(sbp_timeout, (caddr_t)ocb, + ocb->ccb->ccb_h.timeout_ch); + if (ntohl(ocb->orb[4]) & 0xffff) { + bus_dmamap_sync(sdev->target->sbp->dmat, + ocb->dmamap, + (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? + BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sdev->target->sbp->dmat, + ocb->dmamap); + } + if (next != NULL && sbp_status->src == 1) + sbp_orb_pointer(sdev, next); + break; + } else + order ++; + } + splx(s); +SBP_DEBUG(0) + if (ocb && order > 0) { + sbp_show_sdev_info(sdev, 2); + printf("unordered execution order:%d\n", order); + } +END_DEBUG + return (ocb); +} + +static struct sbp_ocb * +sbp_enqueue_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb) +{ + int s = splfw(); + struct sbp_ocb *prev; + +SBP_DEBUG(2) + sbp_show_sdev_info(sdev, 2); +#if __FreeBSD_version >= 500000 + printf("sbp_enqueue_ocb orb=0x%jx in physical memory\n", + (uintmax_t)ocb->bus_addr); +#else + printf("sbp_enqueue_ocb orb=0x%x in physical memory\n", ocb->bus_addr); +#endif +END_DEBUG + prev = STAILQ_LAST(&sdev->ocbs, sbp_ocb, ocb); + STAILQ_INSERT_TAIL(&sdev->ocbs, ocb, ocb); + + if (ocb->ccb != NULL) + ocb->ccb->ccb_h.timeout_ch = timeout(sbp_timeout, (caddr_t)ocb, + (ocb->ccb->ccb_h.timeout * hz) / 1000); + + if (prev != NULL ) { +SBP_DEBUG(1) +#if __FreeBSD_version >= 500000 + printf("linking chain 0x%jx -> 0x%jx\n", + (uintmax_t)prev->bus_addr, (uintmax_t)ocb->bus_addr); +#else + printf("linking chain 0x%x -> 0x%x\n", prev->bus_addr, ocb->bus_addr); +#endif +END_DEBUG + prev->orb[1] = htonl(ocb->bus_addr); + prev->orb[0] = 0; + } + splx(s); + + return prev; +} + +static struct sbp_ocb * +sbp_get_ocb(struct sbp_dev *sdev) +{ + struct sbp_ocb *ocb; + int s = splfw(); + ocb = STAILQ_FIRST(&sdev->free_ocbs); + if (ocb == NULL) { + printf("ocb shortage!!!\n"); + return NULL; + } + STAILQ_REMOVE_HEAD(&sdev->free_ocbs, ocb); + splx(s); + ocb->ccb = NULL; + return (ocb); +} + +static void +sbp_free_ocb(struct sbp_dev *sdev, struct sbp_ocb *ocb) +{ + ocb->flags = 0; + ocb->ccb = NULL; + STAILQ_INSERT_TAIL(&sdev->free_ocbs, ocb, ocb); +} + +static void +sbp_abort_ocb(struct sbp_ocb *ocb, int status) +{ + struct sbp_dev *sdev; + + sdev = ocb->sdev; +SBP_DEBUG(0) + sbp_show_sdev_info(sdev, 2); +#if __FreeBSD_version >= 500000 + printf("sbp_abort_ocb 0x%jx\n", (uintmax_t)ocb->bus_addr); +#else + printf("sbp_abort_ocb 0x%x\n", ocb->bus_addr); +#endif +END_DEBUG +SBP_DEBUG(1) + if (ocb->ccb != NULL) + sbp_print_scsi_cmd(ocb); +END_DEBUG + if (ntohl(ocb->orb[4]) & 0xffff) { + bus_dmamap_sync(sdev->target->sbp->dmat, ocb->dmamap, + (ntohl(ocb->orb[4]) & ORB_CMD_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sdev->target->sbp->dmat, ocb->dmamap); + } + if (ocb->ccb != NULL) { + untimeout(sbp_timeout, (caddr_t)ocb, + ocb->ccb->ccb_h.timeout_ch); + ocb->ccb->ccb_h.status = status; + xpt_done(ocb->ccb); + } + sbp_free_ocb(sdev, ocb); +} + +static void +sbp_abort_all_ocbs(struct sbp_dev *sdev, int status) +{ + int s; + struct sbp_ocb *ocb, *next; + STAILQ_HEAD(, sbp_ocb) temp; + + s = splfw(); + + bcopy(&sdev->ocbs, &temp, sizeof(temp)); + STAILQ_INIT(&sdev->ocbs); + for (ocb = STAILQ_FIRST(&temp); ocb != NULL; ocb = next) { + next = STAILQ_NEXT(ocb, ocb); + sbp_abort_ocb(ocb, status); + } + + splx(s); +} + +static devclass_t sbp_devclass; + +static device_method_t sbp_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, sbp_identify), + DEVMETHOD(device_probe, sbp_probe), + DEVMETHOD(device_attach, sbp_attach), + DEVMETHOD(device_detach, sbp_detach), + DEVMETHOD(device_shutdown, sbp_shutdown), + + { 0, 0 } +}; + +static driver_t sbp_driver = { + "sbp", + sbp_methods, + sizeof(struct sbp_softc), +}; +DRIVER_MODULE(sbp, firewire, sbp_driver, sbp_devclass, 0, 0); +MODULE_VERSION(sbp, 1); +MODULE_DEPEND(sbp, firewire, 1, 1, 1); +MODULE_DEPEND(sbp, cam, 1, 1, 1); |