summaryrefslogtreecommitdiffstats
path: root/sys/dev/firewire
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/firewire')
-rw-r--r--sys/dev/firewire/00README155
-rw-r--r--sys/dev/firewire/firewire.c2169
-rw-r--r--sys/dev/firewire/firewire.h403
-rw-r--r--sys/dev/firewire/firewire_phy.h85
-rw-r--r--sys/dev/firewire/firewirereg.h306
-rw-r--r--sys/dev/firewire/fwcrom.c615
-rw-r--r--sys/dev/firewire/fwdev.c703
-rw-r--r--sys/dev/firewire/fwdma.c208
-rw-r--r--sys/dev/firewire/fwdma.h112
-rw-r--r--sys/dev/firewire/fwmem.c424
-rw-r--r--sys/dev/firewire/fwmem.h52
-rw-r--r--sys/dev/firewire/fwohci.c2853
-rw-r--r--sys/dev/firewire/fwohci_pci.c438
-rw-r--r--sys/dev/firewire/fwohcireg.h431
-rw-r--r--sys/dev/firewire/fwohcivar.h90
-rw-r--r--sys/dev/firewire/iec13213.h251
-rw-r--r--sys/dev/firewire/iec68113.h127
-rw-r--r--sys/dev/firewire/if_fwe.c657
-rw-r--r--sys/dev/firewire/if_fwevar.h54
-rw-r--r--sys/dev/firewire/sbp.c2683
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);
OpenPOWER on IntegriCloud