summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorikob <ikob@FreeBSD.org>2002-09-13 12:31:56 +0000
committerikob <ikob@FreeBSD.org>2002-09-13 12:31:56 +0000
commit594125652993c3f7e88268f9b36d9730c5c2961d (patch)
tree9da4eff2bec066848b2818f3ae10a47cf62315ad
parent238e3d325507fc228dd938ae173eb4d9d1a8f9c7 (diff)
downloadFreeBSD-src-594125652993c3f7e88268f9b36d9730c5c2961d.zip
FreeBSD-src-594125652993c3f7e88268f9b36d9730c5c2961d.tar.gz
Initial import for IEEE1394 OHCI chipdet device driver and SBP-2 (Serial
Bus Protocol 2:SCSI over IEEE1394) support for CAM.
-rw-r--r--share/man/man4/firewire.489
-rw-r--r--share/man/man4/fwohci.473
-rw-r--r--share/man/man4/sbp.486
-rw-r--r--sys/dev/firewire/00README155
-rw-r--r--sys/dev/firewire/bus_mgm.c456
-rw-r--r--sys/dev/firewire/firewire.c3245
-rw-r--r--sys/dev/firewire/firewire.h565
-rw-r--r--sys/dev/firewire/firewire_phy.h87
-rw-r--r--sys/dev/firewire/firewirebusreg.h38
-rw-r--r--sys/dev/firewire/firewirereg.h349
-rw-r--r--sys/dev/firewire/fw_tap.c219
-rw-r--r--sys/dev/firewire/fwmem.c254
-rw-r--r--sys/dev/firewire/fwmem.h47
-rw-r--r--sys/dev/firewire/fwohci.c2649
-rw-r--r--sys/dev/firewire/fwohci_pci.c294
-rw-r--r--sys/dev/firewire/fwohcireg.h394
-rw-r--r--sys/dev/firewire/fwohcivar.h70
-rw-r--r--sys/dev/firewire/iec13213.h92
-rw-r--r--sys/dev/firewire/iec68113.h74
-rw-r--r--sys/dev/firewire/if_fwe.c597
-rw-r--r--sys/dev/firewire/if_fwevar.h53
-rw-r--r--sys/dev/firewire/sbp.c2214
-rw-r--r--sys/modules/firewire/Makefile8
-rw-r--r--sys/modules/firewire/firewire/Makefile29
-rw-r--r--sys/modules/firewire/fwe/Makefile19
-rw-r--r--sys/modules/firewire/sbp/Makefile20
26 files changed, 12176 insertions, 0 deletions
diff --git a/share/man/man4/firewire.4 b/share/man/man4/firewire.4
new file mode 100644
index 0000000..5023f59
--- /dev/null
+++ b/share/man/man4/firewire.4
@@ -0,0 +1,89 @@
+.\" 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$
+.\"
+.Dd May 23, 2002
+.Dt FIREWIRE 4
+.Os
+.Sh NAME
+.Nm firewire
+.Nd IEEE1394 High-performance Serial Bus
+.Sh SYNOPSIS
+.Cd "device firewire"
+.Pp
+.In dev/firewire/firewire.h
+.Sh DESCRIPTION
+.Fx
+provides machine-independent bus support and row drivers for
+.Tn firewire
+interfaces.
+.Pp
+The
+.Nm
+driver consists of two layers: the controller and the
+bus layer.
+The controller attaches to a physical bus
+(like
+.Xr pci 4 ) .
+The
+.Tn firewire
+bus attaches to the controller. And the additional driver can be
+attached to the bus.
+.Pp
+Up to 63 devices, including the host itself, can be attached to
+a firewire bus. The root node is dynamically assigned with PHY
+device function. Also, the other firewire bus specific parameters
+e.g. node id, cycle master, isochronous resource manager and bus
+manager, are dynamically assigned, after bus rest is initiated.
+On firewire bus, every device is identified with EUI 64 address.
+
+.Pp
+.El
+.Sh SEE ALSO
+.Xr fwohci 4 ,
+.Xr pci 4 ,
+.Xr sbp 4 ,
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Katsushi Kobayashi
+and
+.An Hidetoshi Shimokawa
+for the
+.Nx
+project.
diff --git a/share/man/man4/fwohci.4 b/share/man/man4/fwohci.4
new file mode 100644
index 0000000..4b1e54b
--- /dev/null
+++ b/share/man/man4/fwohci.4
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1998,1999,2000 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$
+.\"
+.\"
+.Dd April 10, 2000
+.Dt FWOHCI 4
+.Os FreeBSD
+.Sh NAME
+.Nm fwohci
+.Nd
+OHCI firewire chipset device driver
+.Sh SYNOPSIS
+.Cd "device fwohci"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for PCI firewire interface cards.
+The driver supports following OHCI chipsets.
+.Pp
+.Bl -tag -width xxxxxxxxxxxxxxxxxxxx
+.It uPD72861
+.It TI TSB12LV22,LV23,26 and TSB43AA22
+.It Sony CX3022
+.It VIA VT6306
+.It Ricoh R5C552
+.Pp
+.El
+.Sh SEE ALSO
+.Xr firewire 8 ,
+.Xr sbp 4
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+The
+.Nm
+device driver was written by
+.An Katsushi Kobayashi .
+.Pp
+This manual page was written by
+.An Katsushi Kobayashi .
diff --git a/share/man/man4/sbp.4 b/share/man/man4/sbp.4
new file mode 100644
index 0000000..e779e11
--- /dev/null
+++ b/share/man/man4/sbp.4
@@ -0,0 +1,86 @@
+.\" 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
+.\"
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd May 20, 2002
+.Dt SBP 4
+.Os
+.Sh NAME
+.Nm sbp
+.Nd Serial Bus Protocol 2 (SBP-2) Mass Storage Devices driver
+.Sh SYNOPSIS
+.Cd "device sbp"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for SBP-2 devices that attach to the firewire (IEEE1394)
+port.
+Verified are
+.Pp
+.Bl -tag -compact -width xxxxxx
+.It Apple Macintosh G4 (target mode)
+.It Apple iPod
+.El
+.Pp
+.Nm firewire
+and
+.Nm fwohci
+must be configured in the kernel as well.
+Last but not least, support for
+SCSI drives,
+.Nm da
+.Sh EXAMPLES
+.Dl device sbp
+.Dl device scbus
+.Dl device da
+.Dl device pass
+.Pp
+Add the
+.Nm
+driver to the kernel.
+.Pp
+.Dl camcontrol rescan 0
+.Pp
+Rescan a SCSI drive that was added after boot.
+.\".Sh HISTORY
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Katsushi Kobayashi
+and
+.An Hidetoshi Shimokawa .
+.Pp
+This manual page was written by
+.An Katsushi Kobayashi .
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/bus_mgm.c b/sys/dev/firewire/bus_mgm.c
new file mode 100644
index 0000000..87742ad
--- /dev/null
+++ b/sys/dev/firewire/bus_mgm.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec13213.h>
+
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+usage(void)
+{
+ printf("bus_mgm [-g gap_count] [-b pri_req] [-c node]"
+ " [-r] [-t] [-s]\n");
+ printf("\t-g: broadcast gap_count by phy_config packet\n");
+ printf("\t-b: set PRIORITY_BUDGET register on all supported nodes\n");
+ printf("\t-c: read configuration ROM\n");
+ printf("\t-r: bus reset\n");
+ printf("\t-t: read topology map\n");
+ printf("\t-s: read speed map\n");
+ exit(0);
+}
+
+void
+get_num_of_dev(int fd, struct fw_devlstreq *data)
+{
+ int i;
+ data->n = 64;
+ if( ioctl(fd, FW_GDEVLST, data) < 0) {
+ err(1, "ioctl");
+ }
+#if 1
+ printf("%d devices\n", data->n);
+ for (i = 0; i < data->n; i++) {
+ printf("%d node %d eui:%08x%08x status:%d\n",
+ i,
+ data->dst[i],
+ data->eui[i].hi,
+ data->eui[i].lo,
+ data->status[i]
+ );
+ }
+#endif
+}
+
+u_int32_t
+read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int read, u_int32_t data)
+{
+ struct fw_asyreq *asyreq;
+ u_int32_t *qld, res;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
+ asyreq->req.len = 16;
+ asyreq->req.type = FWASREQEUI;
+ asyreq->req.dst.eui = eui;
+#if 0
+ asyreq->pkt.mode.rreqq.dst = htons(FWLOCALBUS | node);
+#endif
+ asyreq->pkt.mode.rreqq.tlrt = 0;
+ if (read)
+ asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ;
+ else
+ asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ;
+
+ asyreq->pkt.mode.rreqq.dest_hi = htons(0xffff);
+ asyreq->pkt.mode.rreqq.dest_lo = htonl(addr_lo);
+
+ qld = (u_int32_t *)&asyreq->pkt;
+ if (!read)
+ asyreq->pkt.mode.wreqq.data = data;
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
+ err(1, "ioctl");
+ }
+ res = qld[3];
+ free(asyreq);
+ if (read)
+ return ntohl(res);
+ else
+ return 0;
+}
+void
+send_phy_config(int fd, int root_node, int gap_count)
+{
+ struct fw_asyreq *asyreq;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
+ asyreq->req.len = 12;
+ asyreq->req.type = FWASREQNODE;
+ asyreq->pkt.mode.ld[0] = 0;
+ asyreq->pkt.mode.ld[1] = 0;
+ asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
+ if (root_node >= 0)
+ asyreq->pkt.mode.ld[1] |= htonl((root_node & 0x3f) << 24 | 1 << 23);
+ if (gap_count >= 0)
+ asyreq->pkt.mode.ld[1] |= htonl(1 << 22 | (gap_count & 0x3f) << 16);
+ asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
+
+ printf("send phy_config root_node=%d gap_count=%d\n",
+ root_node, gap_count);
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
+ err(1, "ioctl");
+ }
+}
+
+void
+set_pri_req(int fd, int pri_req)
+{
+ struct fw_devlstreq data;
+ u_int32_t max, reg, old;
+ int i;
+
+ get_num_of_dev(fd, &data);
+#define BUGET_REG 0xf0000218
+ for (i = 0; i < data.n; i++) {
+ if (!data.status[i])
+ continue;
+ reg = read_write_quad(fd, data.eui[i], BUGET_REG, 1, 0);
+ printf("%d %08x:%08x, %08x",
+ data.dst[i], data.eui[i].hi, data.eui[i].lo, reg);
+ if (reg > 0 && pri_req >= 0) {
+ old = (reg & 0x3f);
+ max = (reg & 0x3f00) >> 8;
+ if (pri_req > max)
+ pri_req = max;
+ printf(" 0x%x -> 0x%x\n", old, pri_req);
+ read_write_quad(fd, data.eui[i], BUGET_REG, 0, pri_req);
+ } else {
+ printf("\n");
+ }
+ }
+}
+
+void
+parse_text(struct csrtext *textleaf)
+{
+ char buf[256];
+ u_int32_t *bp;
+ int i, len;
+
+ bp = (u_int32_t *)&buf[0];
+ len = textleaf->crc_len - 2;
+ for (i = 0; i < len; i ++) {
+ *bp++ = ntohl(textleaf->text[i]);
+ }
+ *bp = 0;
+ printf("'%s'", buf);
+}
+
+void
+parse_crom(struct csrdirectory *dir, int depth)
+{
+ int i, j;
+ struct csrreg *reg;
+
+ printf(" len=0x%04x(%d) crc=0x%04x\n",
+ dir->crc_len, dir->crc_len, dir->crc);
+ if (dir->crc_len > 0xff) {
+ printf(" too long!\n");
+ return;
+ }
+ for (i = 0; i < dir->crc_len; i ++) {
+ for (j = 0; j < depth; j++)
+ printf("\t");
+ reg = &dir->entry[i];
+ printf("0x%02x 0x%06x ", reg->key, reg->val);
+ switch (reg->key) {
+ case 0x03:
+ printf("module_vendor_ID");
+ break;
+ case 0x04:
+ printf("XXX");
+ break;
+ case 0x0c:
+ printf("node_capabilities");
+ break;
+ case 0x12:
+ printf("unit_spec_ID");
+ break;
+ case 0x13:
+ printf("unit_sw_version");
+ break;
+ case 0x14:
+ printf("logical_unit_number");
+ break;
+ case 0x17:
+ printf("model_ID");
+ break;
+ case 0x38:
+ printf("command_set_spec_ID");
+ break;
+ case 0x39:
+ printf("command_set");
+ break;
+ case 0x3a:
+ printf("unit_characteristics");
+ break;
+ case 0x3b:
+ printf("command_set_revision");
+ break;
+ case 0x3c:
+ printf("firmware_revision");
+ break;
+ case 0x3d:
+ printf("reconnect_timeout");
+ break;
+ case 0x54:
+ printf("management_agent");
+ break;
+ case 0x81:
+ printf("text_leaf: ");
+ parse_text((struct csrtext *)(reg + reg->val));
+ break;
+ case 0xd1:
+ printf("unit_directory");
+ parse_crom((struct csrdirectory *)(reg + reg->val),
+ depth + 1);
+ break;
+ default:
+ printf("uknown");
+ break;
+ }
+ printf("\n");
+ }
+}
+
+void parse_bus_info_block(u_int32_t *p, int info_len)
+{
+ int i;
+
+ for (i = 0; i < info_len; i++) {
+ printf("bus_info: 0x%08x\n", *p++);
+ }
+}
+void
+show_crom(int fd, int node)
+{
+ struct fw_devlstreq data;
+ struct fw_crom_buf buf;
+ struct csrhdr *hdr;
+ u_int32_t *p;
+ int i;
+
+ get_num_of_dev(fd, &data);
+ for (i = 0; i < data.n; i++) {
+ if (data.dst[i] == node && data.eui[i].lo != 0)
+ break;
+ }
+ if (i != data.n) {
+ printf("node: %d\n", node);
+ buf.eui = data.eui[i];
+ } else {
+ printf("no such node: %d\n", node);
+ return;
+ }
+
+ buf.len = 256 * 4;
+ buf.ptr = malloc(buf.len);
+ if (ioctl(fd, FW_GCROM, &buf) < 0) {
+ err(1, "ioctl");
+ }
+ p = (u_int32_t *)buf.ptr;
+ printf("first quad: 0x%08x\n", *p);
+ hdr = (struct csrhdr *)p;
+ if (hdr->info_len == 1) {
+ /* minimum ROM */
+ struct csrreg *reg;
+ reg = (struct csrreg *)p;
+ printf("verndor ID: 0x%06x\n", reg->val);
+ return;
+ }
+ printf("len: %d\n", hdr->crc_len);
+ parse_bus_info_block(p+1, hdr->info_len);
+ p += 1 + hdr->info_len;
+ printf("root_directory");
+ parse_crom((struct csrdirectory *)p, 0);
+}
+static void
+show_topology_map(int fd)
+{
+ struct fw_topology_map *tmap;
+ union fw_self_id sid;
+ int i;
+ static char *port_status[] = {" ", "-", "P", "C"};
+ static char *pwr_class[] = {" 0W", "15W", "30W", "45W",
+ "-1W", "-2W", "-5W", "-9W"};
+ static char *speed[] = {"S100", "S200", "S400", "S800"};
+ tmap = malloc(sizeof(struct fw_topology_map));
+ if (tmap == NULL)
+ return;
+ if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
+ err(1, "ioctl");
+ }
+ printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n",
+ tmap->crc_len, tmap->generation,
+ tmap->node_count, tmap->self_id_count);
+ printf("id link gap_cnt speed delay cIRM power port0 port1 port2"
+ " ini more\n");
+ for (i = 0; i < tmap->crc_len - 2; i++) {
+ sid = tmap->self_id[i];
+ if (sid.p0.sequel) {
+ printf("%02d sequel packet\n", sid.p0.phy_id);
+ continue;
+ }
+ printf("%02d %2d %d %4s %d %d %3s"
+ " %s %s %s %d %d\n",
+ sid.p0.phy_id,
+ sid.p0.link_active,
+ sid.p0.gap_count,
+ speed[sid.p0.phy_speed],
+ sid.p0.phy_delay,
+ sid.p0.contender,
+ pwr_class[sid.p0.power_class],
+ port_status[sid.p0.port0],
+ port_status[sid.p0.port1],
+ port_status[sid.p0.port2],
+ sid.p0.initiated_reset,
+ sid.p0.more_packets
+ );
+ }
+}
+
+static void
+show_speed_map(int fd)
+{
+ struct fw_speed_map *smap;
+ int i,j;
+
+ smap = malloc(sizeof(struct fw_speed_map));
+ if (smap == NULL)
+ return;
+ if (ioctl(fd, FW_GSPMAP, &smap) < 0) {
+ err(1, "ioctl");
+ }
+ printf("crc_len: %d generation:%d\n", smap->crc_len, smap->generation);
+ for (i = 0; i < 64; i ++) {
+ for (j = 0; j < 64; j ++)
+ printf("%d", smap->speed[i][j]);
+ printf("\n");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ char devname[] = "/dev/fw1";
+ int fd, tmp;
+
+ if ((fd = open(devname, O_RDWR)) < 0)
+ err(1, "open");
+
+ if (argc < 2) {
+ usage();
+ }
+
+ argv++;
+ argc--;
+ while (argc > 0) {
+ if (strcmp(*argv, "-g") == 0) {
+ /* gap count */
+ argv++;
+ argc--;
+ if (argc > 0) {
+ tmp = strtoul(*argv, (char **)NULL, 0);
+ argv++;
+ argc--;
+ send_phy_config(fd, -1, tmp);
+ } else {
+ usage();
+ }
+ } else if (strcmp(*argv, "-b") == 0) {
+ argv++;
+ argc--;
+ tmp = -1;
+ if (argc > 0) {
+ tmp = strtoul(*argv, (char **)NULL, 0);
+ argv++;
+ argc--;
+ } else {
+ usage();
+ }
+ set_pri_req(fd, tmp);
+ } else if (strcmp(*argv, "-r") == 0) {
+ argv++;
+ argc--;
+ /* bus reset */
+ if(ioctl(fd, FW_IBUSRST, &tmp) < 0) {
+ err(1, "ioctl");
+ }
+ } else if (strcmp(*argv, "-t") == 0) {
+ argv++;
+ argc--;
+ show_topology_map(fd);
+ } else if (strcmp(*argv, "-s") == 0) {
+ argv++;
+ argc--;
+ show_speed_map(fd);
+ } else if (strcmp(*argv, "-c") == 0) {
+ argv++;
+ argc--;
+ if (argc > 0) {
+ tmp = strtoul(*argv, (char **)NULL, 0);
+ argv++;
+ argc--;
+ } else {
+ usage();
+ }
+ show_crom(fd, tmp);
+ } else
+ usage();
+ }
+ return 0;
+}
diff --git a/sys/dev/firewire/firewire.c b/sys/dev/firewire/firewire.c
new file mode 100644
index 0000000..a234d95
--- /dev/null
+++ b/sys/dev/firewire/firewire.c
@@ -0,0 +1,3245 @@
+/*
+ * 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/uio.h>
+#include <sys/sysctl.h>
+#include <sys/poll.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 <vm/vm.h>
+#include <vm/pmap.h> /* for vtophys proto */
+#include <vm/vm_extern.h>
+
+#include <sys/bus.h> /* used by smbus and newbus */
+
+#include <machine/bus.h> /* used by newbus */
+#include <sys/rman.h> /* used by newbus */
+#include <machine/resource.h> /* used by newbus */
+
+#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>
+#include <dev/firewire/iec13213.h>
+#include <dev/firewire/iec68113.h>
+
+int firewire_debug=0;
+SYSCTL_NODE(_hw, OID_AUTO, firewire, CTLFLAG_RD, 0, "Firewire Subsystem");
+SYSCTL_INT(_debug, OID_AUTO, firewire_debug, CTLFLAG_RW, &firewire_debug, 0,
+ "Firewire driver debug flag");
+
+#define CDEV_MAJOR 127
+#define FW_MAXASYRTY 4
+#define FW_MAXDEVRCNT 4
+#define FWNODE_INVAL 0xffff
+
+#define XFER_TIMEOUT 0
+
+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;
+
+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 struct fw_bind *fw_bindlookup __P((struct firewire_comm *, u_int32_t, u_int32_t));
+static void fw_try_bmr __P((void *));
+static void fw_try_bmr_callback __P((struct fw_xfer *));
+static u_int16_t fw_noderesolve __P((struct firewire_comm *, struct fw_eui64));
+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 *));
+static void fw_vmaccess __P((struct fw_xfer *));
+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(*)));
+
+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_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"};
+u_int maxrec[6]={512,1024,2048,4096,8192,0};
+
+#define MAX_GAPHOP 16
+u_int gap_cnt[] = {1, 1, 4, 6, 9, 12, 14, 17,
+ 20, 23, 25, 28, 31, 33, 36, 39, 42};
+/*
+ * The probe routine.
+ */
+struct cdevsw firewire_cdevsw =
+{
+ fw_open, fw_close, fw_read, fw_write, fw_ioctl,
+ fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM
+};
+static driver_t firewire_driver = {
+ "firewire",
+ firewire_methods,
+ sizeof(struct firewire_softc),
+};
+
+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;
+ sc->fc->ir[sub]->flag |= FWXFERQ_PACKET;
+ 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_dvbuf *dvbuf;
+ 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->it[sub]->flag & FWXFERQ_DV){
+ if((dvbuf = sc->fc->it[sub]->dvproc) != NULL){
+ free(dvbuf->buf, M_DEVBUF);
+ sc->fc->it[sub]->dvproc = NULL;
+ }
+ if((dvbuf = sc->fc->it[sub]->dvdma) != NULL){
+ free(dvbuf->buf, M_DEVBUF);
+ sc->fc->it[sub]->dvdma = NULL;
+ }
+ while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvvalid)) != NULL){
+ STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvvalid, link);
+ free(dvbuf->buf, M_DEVBUF);
+ }
+ while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvfree)) != NULL){
+ STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvfree, link);
+ free(dvbuf->buf, M_DEVBUF);
+ }
+ free(sc->fc->it[sub]->dvbuf, M_DEVBUF);
+ sc->fc->it[sub]->dvbuf = NULL;
+ }
+ if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
+ free(sc->fc->ir[sub]->buf, M_DEVBUF);
+ sc->fc->ir[sub]->buf = NULL;
+ free(sc->fc->ir[sub]->bulkxfer, M_DEVBUF);
+ sc->fc->ir[sub]->bulkxfer = NULL;
+ sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
+ sc->fc->ir[sub]->psize = FWPMAX_S400;
+ sc->fc->ir[sub]->maxq = FWMAXQUEUE;
+ }
+ if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
+ free(sc->fc->it[sub]->buf, M_DEVBUF);
+ sc->fc->it[sub]->buf = NULL;
+ free(sc->fc->it[sub]->bulkxfer, M_DEVBUF);
+ sc->fc->it[sub]->bulkxfer = NULL;
+ sc->fc->it[sub]->dvbuf = NULL;
+ sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
+ sc->fc->it[sub]->psize = FWPMAX_S400;
+ 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;
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ fw_xfer_done(xfer);
+ break;
+ default:
+ break;
+ }
+ fw_xfer_free(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_DEVBUF);
+ }
+ sc->fc->ir[sub]->flag &= ~FWXFERQ_MODEMASK;
+ sc->fc->it[sub]->flag &= ~FWXFERQ_MODEMASK;
+ 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];
+
+ if(ir->flag & FWXFERQ_PACKET){
+ ir->stproc = NULL;
+ }
+readloop:
+ xfer = STAILQ_FIRST(&ir->q);
+ if(!(ir->flag & FWXFERQ_PACKET) && ir->stproc == NULL){
+ 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){
+ if(slept == 0){
+ slept = 1;
+ if(!(ir->flag & FWXFERQ_RUNNING)
+ && (ir->flag & FWXFERQ_PACKET)){
+ err = sc->fc->irx_enable(sc->fc, sub);
+ }
+ if(err){
+ return err;
+ }
+ ir->flag |= FWXFERQ_WAKEUP;
+ err = tsleep((caddr_t)ir, FWPRI, "fw_read", hz);
+ if(err){
+ ir->flag &= ~FWXFERQ_WAKEUP;
+ return err;
+ }
+ goto readloop;
+ }else{
+ err = EIO;
+ return err;
+ }
+ }else if(xfer != NULL){
+ s = splfw();
+ ir->queued --;
+ STAILQ_REMOVE_HEAD(&ir->q, link);
+ splx(s);
+ fp = (struct fw_pkt *)(xfer->recv.buf + xfer->recv.off);
+ if(sc->fc->irx_post != NULL)
+ sc->fc->irx_post(sc->fc, fp->mode.ld);
+ err = uiomove(xfer->recv.buf + xfer->recv.off, xfer->recv.len, uio);
+ fw_xfer_free( xfer);
+ }else if(ir->stproc != NULL){
+ fp = (struct fw_pkt *)(ir->stproc->buf + ir->queued * ir->psize);
+ if(sc->fc->irx_post != NULL)
+ sc->fc->irx_post(sc->fc, fp->mode.ld);
+ if(ntohs(fp->mode.stream.len) == 0){
+ err = EIO;
+ return err;
+ }
+ err = uiomove((caddr_t)fp, ntohs(fp->mode.stream.len) + sizeof(u_int32_t), uio);
+ fp->mode.stream.len = 0;
+ ir->queued ++;
+ if(ir->queued >= ir->bnpacket){
+ s = splfw();
+ ir->stproc->flag = 0;
+ STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
+ splx(s);
+ ir->stproc = NULL;
+ }
+ }
+#if 0
+ if(STAILQ_FIRST(&ir->q) == NULL &&
+ (ir->flag & FWXFERQ_RUNNING) && (ir->flag & FWXFERQ_PACKET)){
+ err = sc->fc->irx_enable(sc->fc, sub);
+ }
+#endif
+#if 0
+ if(STAILQ_FIRST(&ir->stvalid) == NULL &&
+ (ir->flag & FWXFERQ_RUNNING) && !(ir->flag & FWXFERQ_PACKET)){
+ err = sc->fc->irx_enable(sc->fc, sub);
+ }
+#endif
+ 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 tl, s, slept = 0;
+ struct fw_pkt *fp;
+ struct fw_xfer *xfer;
+ struct fw_xferq *xferq;
+ 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];
+
+ fp = (struct fw_pkt *)uio->uio_iov->iov_base;
+ switch(fp->mode.common.tcode){
+ case FWTCODE_RREQQ:
+ case FWTCODE_RREQB:
+ case FWTCODE_LREQ:
+ err = EINVAL;
+ return err;
+ case FWTCODE_WREQQ:
+ case FWTCODE_WREQB:
+ xferq = fc->atq;
+ break;
+ case FWTCODE_STREAM:
+ if(it->flag & FWXFERQ_PACKET){
+ xferq = fc->atq;
+ }else{
+ xferq = NULL;
+ }
+ break;
+ case FWTCODE_WRES:
+ case FWTCODE_RRESQ:
+ case FWTCODE_RRESB:
+ case FWTCODE_LRES:
+ xferq = fc->ats;
+ break;
+ default:
+ err = EINVAL;
+ return err;
+ }
+ /* Discard unsent buffered stream packet, when sending Asyrequrst */
+ if(xferq != NULL && it->stproc != NULL){
+ s = splfw();
+ it->stproc->flag = 0;
+ STAILQ_INSERT_TAIL(&it->stfree, it->stproc, link);
+ splx(s);
+ it->stproc = NULL;
+ }
+ if(xferq == NULL && !(it->flag & FWXFERQ_DV)){
+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((caddr_t)it, FWPRI, "fw_write", hz);
+ if(err){
+ return err;
+ }
+ goto isoloop;
+ }else{
+ err = EIO;
+ return err;
+ }
+ }
+ fp = (struct fw_pkt *)(it->stproc->buf + it->queued * it->psize);
+ fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t));
+ err = uiomove(it->stproc->buf + it->queued * it->psize,
+ uio->uio_resid, uio);
+ it->queued ++;
+ if(it->queued >= it->btpacket){
+ s = splfw();
+ STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
+ splx(s);
+ it->stproc = NULL;
+ fw_tbuf_update(sc->fc, sub, 0);
+ err = sc->fc->itx_enable(sc->fc, sub);
+ }
+ return err;
+ } if(xferq == NULL && it->flag & FWXFERQ_DV){
+dvloop:
+ if(it->dvproc == NULL){
+ it->dvproc = STAILQ_FIRST(&it->dvfree);
+ if(it->dvproc != NULL){
+ s = splfw();
+ STAILQ_REMOVE_HEAD(&it->dvfree, link);
+ splx(s);
+ it->dvptr = 0;
+ }else if(slept == 0){
+ slept = 1;
+ err = sc->fc->itx_enable(sc->fc, sub);
+ if(err){
+ return err;
+ }
+ err = tsleep((caddr_t)it, FWPRI, "fw_write", hz);
+ if(err){
+ return err;
+ }
+ goto dvloop;
+ }else{
+ err = EIO;
+ return err;
+ }
+ }
+ fp = (struct fw_pkt *)(it->dvproc->buf + it->queued * it->psize);
+ fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t));
+ err = uiomove(it->dvproc->buf + it->dvptr,
+ uio->uio_resid, uio);
+ it->dvptr += it->psize;
+ if(err){
+ return err;
+ }
+ if(it->dvptr >= it->psize * it->dvpacket){
+ s = splfw();
+ STAILQ_INSERT_TAIL(&it->dvvalid, it->dvproc, link);
+ splx(s);
+ it->dvproc = NULL;
+ err = fw_tbuf_update(sc->fc, sub, 0);
+ if(err){
+ return err;
+ }
+ err = sc->fc->itx_enable(sc->fc, sub);
+ }
+ return err;
+ }
+ if(xferq != NULL){
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ xfer->send.buf = malloc(uio->uio_resid, M_DEVBUF, M_NOWAIT);
+ if(xfer->send.buf == NULL){
+ fw_xfer_free( xfer);
+ err = ENOBUFS;
+ return err;
+ }
+ xfer->dst = ntohs(fp->mode.hdr.dst);
+
+ switch(fp->mode.common.tcode){
+ case FWTCODE_WREQQ:
+ case FWTCODE_WREQB:
+ if((tl = fw_get_tlabel(fc, xfer)) == -1 ){
+ fw_xfer_free( xfer);
+ err = EAGAIN;
+ return err;
+ }
+ fp->mode.hdr.tlrt = tl << 2;
+ default:
+ break;
+ }
+
+ xfer->tl = fp->mode.hdr.tlrt >> 2;
+ xfer->send.len = uio->uio_resid;
+ xfer->send.off = 0;
+ xfer->tcode = fp->mode.common.tcode;
+ xfer->spd = 0;/* XXX: how to setup it */
+ xfer->fc = fc;
+ xfer->q = xferq;
+ xfer->act_type = FWACT_XFER;
+ xfer->act.hand = fw_asy_callback;
+ xfer->retry_req = fw_asybusy;
+
+ err = uiomove(xfer->send.buf, uio->uio_resid, uio);
+ if(err){
+ return err;
+ }
+ fw_asystart(xfer);
+ err = tsleep((caddr_t)xfer, FWPRI, "fw_write", hz);
+ if(xfer->resp == EBUSY)
+ return EBUSY;
+ fw_xfer_free( xfer);
+ return err;
+ }
+ return EINVAL;
+}
+/*
+ * transmitter buffer update.
+ */
+int
+fw_tbuf_update(struct firewire_comm *fc, int sub, int flag){
+ struct fw_bulkxfer *bulkxfer, *bulkxfer2 = NULL;
+ struct fw_dvbuf *dvbuf = NULL;
+ struct fw_xferq *it;
+ int s, err = 0, i, j, chtag;
+ struct fw_pkt *fp;
+ u_int64_t tmpsync, dvsync;
+
+ it = fc->it[sub];
+
+ s = splfw();
+ if(it->stdma == NULL){
+ bulkxfer = STAILQ_FIRST(&it->stvalid);
+ }else if(flag != 0){
+ bulkxfer = STAILQ_FIRST(&it->stvalid);
+ if(bulkxfer == it->stdma){
+ STAILQ_REMOVE_HEAD(&it->stvalid, link);
+ it->stdma->flag = 0;
+ STAILQ_INSERT_TAIL(&it->stfree, it->stdma, link);
+ if(!(it->flag & FWXFERQ_DV))
+ wakeup(it);
+ }
+ bulkxfer = STAILQ_FIRST(&it->stvalid);
+ }else{
+ bulkxfer = it->stdma;
+ }
+ splx(s);
+ if(bulkxfer != NULL){
+ s = splfw();
+ bulkxfer2 = STAILQ_NEXT(bulkxfer, link);
+#if 0
+ if(it->flag & FWXFERQ_DV && bulkxfer2 == NULL){
+ bulkxfer2 = STAILQ_FIRST(&it->stfree);
+ STAILQ_REMOVE_HEAD(&it->stfree, link);
+ splx(s);
+ bcopy(bulkxfer->buf, bulkxfer2->buf,
+ it->psize * it->btpacket);
+ s = splfw();
+ STAILQ_INSERT_TAIL(&it->stvalid, bulkxfer2, link);
+ }
+#endif
+ splx(s);
+ }
+ it->stdma = bulkxfer;
+ it->stdma2 = bulkxfer2;
+
+ if(it->flag & FWXFERQ_DV){
+ chtag = it->flag & 0xff;
+dvloop:
+ if(it->dvdma == NULL){
+ dvbuf = STAILQ_FIRST(&it->dvvalid);
+ if(dvbuf != NULL){
+ s = splfw();
+ STAILQ_REMOVE_HEAD(&it->dvvalid, link);
+ it->dvdma = dvbuf;
+ splx(s);
+ it->queued = 0;
+ }
+ }
+ if(it->dvdma == NULL)
+ return err;
+
+ it->stproc = STAILQ_FIRST(&it->stfree);
+ if(it->stproc != NULL){
+ s = splfw();
+ STAILQ_REMOVE_HEAD(&it->stfree, link);
+ splx(s);
+ }else{
+ return err;
+ }
+/*
+ * Insert least significant 12 bits timestamp value by computation.
+ * Highest significant 4 bits is insert at just before packet sending.
+ */
+ fp = (struct fw_pkt *)(it->stproc->buf);
+/* XXX: Parameter relies on NTSC type DV video */
+ tmpsync = 3072 * 8000 * 100 / 2997;
+ tmpsync *= it->dvsync;
+ dvsync = tmpsync;
+ dvsync %= 0xc00;
+ fp->mode.ld[2] = htonl(0x80000000 | (dvsync % 0xc00));
+ it->dvsync ++;
+ it->dvsync %= 2997;
+
+ for( i = 0, j = 0 ; i < it->dvpacket ; i++){
+ bcopy(it->dvdma->buf + it->queued * it->psize,
+ it->stproc->buf + j * it->psize, it->psize);
+ fp = (struct fw_pkt *)(it->stproc->buf + j * it->psize);
+ fp->mode.stream.len = htons(488);
+ fp->mode.stream.chtag = chtag;
+ fp->mode.stream.tcode = FWTCODE_STREAM;
+ fp->mode.ld[1] = htonl((fc->nodeid << 24) | 0x00780000 | it->dvdbc);
+ it->dvdbc++;
+ it->dvdbc %= 256;
+ it->queued ++;
+ j++;
+/* XXX: Parameter relies on NTSC type DV video */
+#if 1
+#define DVDIFF 203
+#define DVFRAC 2997
+#else
+#define DVDIFF 127
+#define DVFRAC 1875
+#endif
+ it->dvdiff += DVDIFF;
+ if(it->dvdiff >= DVFRAC){
+ it->dvdiff %= DVFRAC;
+ fp = (struct fw_pkt *)(it->stproc->buf + j * it->psize);
+
+ fp->mode.stream.len = htons(0x8);
+ fp->mode.stream.chtag = chtag;
+ fp->mode.stream.tcode = FWTCODE_STREAM;
+ fp->mode.ld[1] = htonl((fc->nodeid << 24) |
+ 0x00780000 | it->dvdbc);
+ j++;
+ }
+ }
+ it->stproc->npacket = j;
+ s = splfw();
+ STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
+ splx(s);
+ if(it->queued >= it->dvpacket){
+ s = splfw();
+ STAILQ_INSERT_TAIL(&it->dvfree, it->dvdma, link);
+ it->dvdma = NULL;
+ splx(s);
+ wakeup(it);
+ goto dvloop;
+ }
+ }
+ return err;
+}
+/*
+ * receving buffer update.
+ */
+int
+fw_rbuf_update(struct firewire_comm *fc, int sub, int flag){
+ struct fw_bulkxfer *bulkxfer, *bulkxfer2 = NULL;
+ struct fw_xferq *ir;
+ int s, err = 0;
+
+ ir = fc->ir[sub];
+ s = splfw();
+ if(ir->stdma != NULL){
+ if(flag != 0){
+ STAILQ_INSERT_TAIL(&ir->stvalid, ir->stdma, link);
+ }else{
+ ir->stdma->flag = 0;
+ STAILQ_INSERT_TAIL(&ir->stfree, ir->stdma, link);
+ }
+ }
+ if(ir->stdma2 != NULL){
+ bulkxfer = ir->stdma2;
+ bulkxfer2 = STAILQ_FIRST(&ir->stfree);
+ if(bulkxfer2 != NULL){
+ STAILQ_REMOVE_HEAD(&ir->stfree, link);
+ }
+ }else{
+ bulkxfer = STAILQ_FIRST(&ir->stfree);
+ if(bulkxfer != NULL){
+ STAILQ_REMOVE_HEAD(&ir->stfree, link);
+ bulkxfer2 = STAILQ_FIRST(&ir->stfree);
+ if(bulkxfer2 != NULL){
+ STAILQ_REMOVE_HEAD(&ir->stfree, link);
+ }
+ }else{
+ bulkxfer = STAILQ_FIRST(&ir->stvalid);
+ STAILQ_REMOVE_HEAD(&ir->stvalid, link);
+ }
+ }
+ splx(s);
+ ir->stdma = bulkxfer;
+ ir->stdma2 = bulkxfer2;
+ 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 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_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;
+#if 0
+ struct fw_map_buf *map_buf = (struct fw_map_buf *)data;
+#endif
+ 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_SSTDV:
+ ibufreq = (struct fw_isobufreq *)
+ malloc(sizeof(struct fw_isobufreq), M_DEVBUF, M_NOWAIT);
+ if(ibufreq == NULL){
+ err = ENOMEM;
+ break;
+ }
+#define FWDVPACKET 250
+#define FWDVPMAX 512
+ ibufreq->rx.nchunk = 8;
+ ibufreq->rx.npacket = 50;
+ ibufreq->rx.psize = FWDVPMAX;
+
+ ibufreq->tx.nchunk = 5;
+ ibufreq->tx.npacket = 300;
+ ibufreq->tx.psize = FWDVPMAX;
+
+ err = fw_ioctl(dev, FW_SSTBUF, (caddr_t)ibufreq, flag, td);
+ sc->fc->it[sub]->dvpacket = FWDVPACKET;
+ free(ibufreq, M_DEVBUF);
+/* reserve a buffer space */
+#define NDVCHUNK 8
+ sc->fc->it[sub]->dvproc = NULL;
+ sc->fc->it[sub]->dvdma = NULL;
+ sc->fc->it[sub]->flag |= FWXFERQ_DV;
+ sc->fc->it[sub]->dvbuf
+ = (struct fw_dvbuf *)malloc(sizeof(struct fw_dvbuf) * NDVCHUNK, M_DEVBUF, M_DONTWAIT);
+ STAILQ_INIT(&sc->fc->it[sub]->dvvalid);
+ STAILQ_INIT(&sc->fc->it[sub]->dvfree);
+ for( i = 0 ; i < NDVCHUNK ; i++){
+ sc->fc->it[sub]->dvbuf[i].buf
+ = malloc(FWDVPMAX * sc->fc->it[sub]->dvpacket, M_DEVBUF, M_DONTWAIT);
+ STAILQ_INSERT_TAIL(&sc->fc->it[sub]->dvfree,
+ &sc->fc->it[sub]->dvbuf[i], link);
+ }
+ 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);
+ }
+ if(ibufreq->rx.nchunk > FWSTMAXCHUNK ||
+ ibufreq->tx.nchunk > FWSTMAXCHUNK){
+ return(EINVAL);
+ }
+ ir->bulkxfer
+ = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_DEVBUF, M_DONTWAIT);
+ if(ir->bulkxfer == NULL){
+ return(ENOMEM);
+ }
+ it->bulkxfer
+ = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_DEVBUF, M_DONTWAIT);
+ if(it->bulkxfer == NULL){
+ return(ENOMEM);
+ }
+ ir->buf = malloc(
+ ibufreq->rx.nchunk * ibufreq->rx.npacket
+ * ((ibufreq->rx.psize + 3) &~3),
+ M_DEVBUF, M_DONTWAIT);
+ if(ir->buf == NULL){
+ free(ir->bulkxfer, M_DEVBUF);
+ free(it->bulkxfer, M_DEVBUF);
+ ir->bulkxfer = NULL;
+ it->bulkxfer = NULL;
+ it->buf = NULL;
+ return(ENOMEM);
+ }
+ it->buf = malloc(
+ ibufreq->tx.nchunk * ibufreq->tx.npacket
+ * ((ibufreq->tx.psize + 3) &~3),
+ M_DEVBUF, M_DONTWAIT);
+ if(it->buf == NULL){
+ free(ir->bulkxfer, M_DEVBUF);
+ free(it->bulkxfer, M_DEVBUF);
+ free(ir->buf, M_DEVBUF);
+ ir->bulkxfer = NULL;
+ it->bulkxfer = NULL;
+ it->buf = NULL;
+ return(ENOMEM);
+ }
+
+ ir->bnchunk = ibufreq->rx.nchunk;
+ ir->bnpacket = ibufreq->rx.npacket;
+ ir->btpacket = ibufreq->rx.npacket;
+ ir->psize = (ibufreq->rx.psize + 3) & ~3;
+ ir->queued = 0;
+
+ it->bnchunk = ibufreq->tx.nchunk;
+ it->bnpacket = ibufreq->tx.npacket;
+ it->btpacket = ibufreq->tx.npacket;
+ it->psize = (ibufreq->tx.psize + 3) & ~3;
+ ir->queued = 0;
+ it->dvdbc = 0;
+ it->dvdiff = 0;
+ it->dvsync = 0;
+
+ STAILQ_INIT(&ir->stvalid);
+ STAILQ_INIT(&ir->stfree);
+ ir->stdma = NULL;
+ ir->stdma2 = NULL;
+ ir->stproc = NULL;
+
+ STAILQ_INIT(&it->stvalid);
+ STAILQ_INIT(&it->stfree);
+ it->stdma = NULL;
+ it->stdma2 = NULL;
+ it->stproc = NULL;
+
+ for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
+ ir->bulkxfer[i].buf =
+ ir->buf +
+ i * sc->fc->ir[sub]->bnpacket *
+ sc->fc->ir[sub]->psize;
+ ir->bulkxfer[i].flag = 0;
+ STAILQ_INSERT_TAIL(&ir->stfree,
+ &ir->bulkxfer[i], link);
+ ir->bulkxfer[i].npacket = ir->bnpacket;
+ }
+ for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
+ it->bulkxfer[i].buf =
+ it->buf +
+ i * sc->fc->it[sub]->bnpacket *
+ sc->fc->it[sub]->psize;
+ it->bulkxfer[i].flag = 0;
+ STAILQ_INSERT_TAIL(&it->stfree,
+ &it->bulkxfer[i], link);
+ it->bulkxfer[i].npacket = it->bnpacket;
+ }
+ 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();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ fp = &asyreq->pkt;
+ switch (asyreq->req.type) {
+ case FWASREQNODE:
+ xfer->dst = ntohs(fp->mode.hdr.dst);
+ break;
+ case FWASREQEUI:
+ xfer->dst = fw_noderesolve(sc->fc, asyreq->req.dst.eui);
+ if(xfer->dst == FWNODE_INVAL ){
+ printf("%s:cannot found node\n",
+ device_get_nameunit(sc->fc->dev));
+ err = EINVAL;
+ goto error;
+ }
+ fp->mode.hdr.dst = htons(FWLOCALBUS | xfer->dst);
+ break;
+ case FWASRESTL:
+ /* XXX what's this? */
+ break;
+ case FWASREQSTREAM:
+ /* nothing to do */
+ break;
+ }
+ xfer->spd = asyreq->req.sped;
+ xfer->send.len = asyreq->req.len;
+ xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT);
+ if(xfer->send.buf == NULL){
+ return ENOMEM;
+ }
+ xfer->send.off = 0;
+ 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((caddr_t)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 + xfer->recv.off, 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_DEVBUF);
+ 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_DEVBUF, M_DONTWAIT);
+ if(fwb == NULL){
+ err = ENOMEM;
+ break;
+ }
+ fwb->start_hi = bindreq->start.hi;
+ fwb->start_lo = bindreq->start.lo;
+ fwb->addrlen = bindreq->len;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ xfer->act_type = FWACT_CH;
+ xfer->sub = sub;
+ xfer->fc = sc->fc;
+
+ fwb->xfer = xfer;
+ err = fw_bindadd(sc->fc, fwb);
+ break;
+ case FW_GDEVLST:
+ i = 0;
+ for(fwdev = TAILQ_FIRST(&sc->fc->devices); fwdev != NULL;
+ fwdev = TAILQ_NEXT(fwdev, link)){
+ if(i < fwdevlst->n){
+ fwdevlst->dst[i] = fwdev->dst;
+ fwdevlst->status[i] =
+ (fwdev->status == FWDEVATTACHED)?1:0;
+ fwdevlst->eui[i].hi = fwdev->eui.hi;
+ fwdevlst->eui[i].lo = fwdev->eui.lo;
+ }
+ i++;
+ }
+ fwdevlst->n = i;
+ break;
+ case FW_GTPMAP:
+ bcopy(sc->fc->topology_map, data,
+ (sc->fc->topology_map->crc_len + 1) * 4);
+ break;
+ case FW_GSPMAP:
+ /* speed_map is larger than a page */
+ err = copyout(sc->fc->speed_map, *(void **)data,
+ (sc->fc->speed_map->crc_len + 1) * 4);
+ break;
+ case FW_GCROM:
+ for (fwdev = TAILQ_FIRST(&sc->fc->devices); fwdev != NULL;
+ fwdev = TAILQ_NEXT(fwdev, link)) {
+ if (fwdev->eui.hi == crom_buf->eui.hi &&
+ fwdev->eui.lo == crom_buf->eui.lo)
+ break;
+ }
+ if (fwdev == NULL) {
+ err = FWNODE_INVAL;
+ break;
+ }
+#if 0
+ if (fwdev->csrrom[0] >> 24 == 1)
+ len = 4;
+ else
+ len = (1 + ((fwdev->csrrom[0] >> 16) & 0xff)) * 4;
+#else
+ if (fwdev->rommax < CSRROMOFF)
+ len = 0;
+ else
+ len = fwdev->rommax - CSRROMOFF + 4;
+#endif
+ 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;
+}
+
+/*
+ * To lookup node id. from EUI64.
+ */
+u_int16_t fw_noderesolve(struct firewire_comm *fc, struct fw_eui64 eui)
+{
+ struct fw_device *fwdev;
+ for(fwdev = TAILQ_FIRST(&fc->devices); fwdev != NULL;
+ fwdev = TAILQ_NEXT(fwdev, link)){
+ if(fwdev->eui.hi == eui.hi && fwdev->eui.lo == eui.lo){
+ break;
+ }
+ }
+ if(fwdev == NULL) return FWNODE_INVAL;
+ if(fwdev->status != FWDEVATTACHED) return FWNODE_INVAL;
+ return fwdev->dst;
+}
+/*
+ * 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 > 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;
+
+#if 0
+ switch(fp->mode.common.tcode){
+ case FWTCODE_STREAM:
+ len = ntohs(fp->mode.stream.len) + 4;
+ break;
+ case FWTCODE_RREQQ:
+ case FWTCODE_WRES:
+ case FWTCODE_PHY:
+ len = 12;
+ break;
+ case FWTCODE_WREQQ:
+ case FWTCODE_RRESQ:
+ len = 16;
+ break;
+ default:
+ len = ntohs(fp->mode.rresb.len) + 16;
+ break;
+ }
+ if( len > xfer->send.len ){
+ printf("len > send.len\n");
+ return EINVAL;
+ }
+ switch(fp->mode.common.tcode){
+ case FWTCODE_WREQQ:
+ case FWTCODE_WREQB:
+ case FWTCODE_RREQQ:
+ case FWTCODE_RREQB:
+ case FWTCODE_LREQ:
+ case FWTCODE_PHY:
+ case FWTCODE_STREAM:
+ xferq = fc->atq;
+ break;
+ case FWTCODE_WRES:
+ case FWTCODE_RRESQ:
+ case FWTCODE_RRESB:
+ case FWTCODE_LRES:
+ xferq = fc->ats;
+ break;
+ default:
+ return EINVAL;
+ }
+#else
+ 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 += ntohs(fp->mode.stream.len);
+ else if (info->flag & FWTI_BLOCK_ASY)
+ len += ntohs(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;
+#endif
+ if(xferq->start == NULL){
+ printf("xferq->start == NULL\n");
+ return EINVAL;
+ }
+ if(!(xferq->queued < xferq->maxq)){
+ printf("%s:Discard a packet (queued=%d)\n",
+ device_get_nameunit(fc->dev), xferq->queued);
+ return EINVAL;
+ }
+
+
+#if 0
+ switch(tcode){
+ case FWTCODE_WREQQ:
+ case FWTCODE_WREQB:
+ case FWTCODE_RREQQ:
+ case FWTCODE_RREQB:
+ case FWTCODE_LREQ:
+ if((tl = fw_get_tlabel(fc, xfer)) == -1 ){
+ return EIO;
+ }
+ fp->mode.hdr.tlrt = tl << 2;
+ break;
+ default:
+ break;
+ }
+#else
+ if (info->flag & FWTI_TLABEL) {
+ if((tl = fw_get_tlabel(fc, xfer)) == -1 )
+ return EIO;
+ fp->mode.hdr.tlrt = tl << 2;
+ }
+#endif
+
+ xfer->tl = tl;
+ xfer->tcode = tcode;
+ xfer->resp = 0;
+ xfer->fc = fc;
+ xfer->q = xferq;
+ xfer->act_type = FWACT_XFER;
+ 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){
+#if 0
+ printf("fw_asybusy\n");
+#endif
+#if XFER_TIMEOUT
+ untimeout(fw_xfer_timeout, (void *)xfer, xfer->ch);
+#endif
+/*
+ xfer->ch = timeout((timeout_t *)fw_asystart, (void *)xfer, 20000);
+*/
+ DELAY(20000);
+ fw_asystart(xfer);
+ return;
+}
+#if XFER_TIMEOUT
+/*
+ * Post timeout for async. request.
+ */
+void
+fw_xfer_timeout(void *arg)
+{
+ int s;
+ struct fw_xfer *xfer;
+
+ xfer = (struct fw_xfer *)arg;
+ printf("fw_xfer_timeout status=%d resp=%d\n", xfer->state, xfer->resp);
+ /* XXX set error code */
+ s = splfw();
+ xfer->act.hand(xfer);
+ splx(s);
+}
+#endif
+/*
+ * 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){
+ 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);
+#if XFER_TIMEOUT
+ if (xfer->act.hand != NULL)
+ xfer->ch = timeout(fw_xfer_timeout, (void *)xfer, hz);
+#endif
+ return;
+}
+static int
+fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
+{
+ struct firewire_softc *fc;
+ int unit = DEV2UNIT(dev);
+
+ if (DEV_FWMEM(dev))
+ return fwmem_mmap(dev, offset, nproto);
+
+ fc = devclass_get_softc(firewire_devclass, unit);
+
+ return EINVAL;
+}
+static int
+firewire_match( device_t dev )
+{
+ device_set_desc(dev, "IEEE1394(Firewire) bus");
+ return -140;
+}
+/*
+ * 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;
+ sc->fc->dev = dev;
+
+ 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, 0770,
+ "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, 0770,
+ "fwmem%d", device_get_unit(dev));
+#if __FreeBSD_version >= 500000
+ dev_depends(sc->dev, d);
+#else
+ sc->dev[i] = d;
+#endif
+ printf("%s: firewire bus attach\n", device_get_nameunit(sc->fc->dev));
+ sc->fc->timeouthandle = timeout((timeout_t *)sc->fc->timeout, (void *)sc->fc, hz * 10);
+
+ /* 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;
+
+ sc = (struct firewire_softc *)device_get_softc(dev);
+#if 0
+ printf("%s:dettach prevented", device_get_nameunit(dev));
+ return(EINVAL);
+#endif
+#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 */
+ untimeout((timeout_t *)sc->fc->timeout, sc->fc, sc->fc->timeouthandle);
+ free(sc->fc->topology_map, M_DEVBUF);
+ free(sc->fc->speed_map, M_DEVBUF);
+ bus_generic_detach(dev);
+ return(0);
+}
+#if 0
+static int
+firewire_shutdown( device_t dev )
+{
+ return 0;
+}
+#endif
+/*
+ * Call ater bus reset.
+ */
+void fw_busreset(struct firewire_comm *fc)
+{
+ int i;
+ struct fw_xfer *xfer;
+
+ switch(fc->status){
+ case FWBUSMGRELECT:
+ untimeout((timeout_t *)fw_try_bmr, (void *)fc, fc->bmrhandle);
+ break;
+ default:
+ break;
+ }
+ fc->status = FWBUSRESET;
+/* XXX: discard all queued packet */
+ while((xfer = STAILQ_FIRST(&fc->atq->q)) != NULL){
+ STAILQ_REMOVE_HEAD(&fc->atq->q, link);
+ xfer->resp = EAGAIN;
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ fw_xfer_done(xfer);
+ break;
+ default:
+ break;
+ }
+ fw_xfer_free( xfer);
+ }
+ while((xfer = STAILQ_FIRST(&fc->ats->q)) != NULL){
+ STAILQ_REMOVE_HEAD(&fc->ats->q, link);
+ xfer->resp = EAGAIN;
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ fw_xfer_done(xfer);
+ default:
+ break;
+ }
+ fw_xfer_free( xfer);
+ }
+ for(i = 0; i < fc->nisodma; i++)
+ while((xfer = STAILQ_FIRST(&fc->it[i]->q)) != NULL){
+ STAILQ_REMOVE_HEAD(&fc->it[i]->q, link);
+ xfer->resp = 0;
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ fw_xfer_done(xfer);
+ break;
+ default:
+ break;
+ }
+ fw_xfer_free( xfer);
+ }
+
+ 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);
+}
+/* Call once after reboot */
+void fw_init(fc)
+ struct firewire_comm *fc;
+{
+ int i;
+ struct fw_xfer *xfer;
+ struct fw_bind *fwb;
+ struct csrdir *csrd;
+
+ fc->max_asyretry = FW_MAXASYRTY;
+
+ fc->arq->queued = 0;
+ fc->ars->queued = 0;
+ fc->atq->queued = 0;
+ fc->ats->queued = 0;
+
+ fc->arq->psize = FWPMAX_S400;
+ fc->ars->psize = FWPMAX_S400;
+ fc->atq->psize = FWPMAX_S400;
+ fc->ats->psize = FWPMAX_S400;
+
+
+ fc->arq->buf = NULL;
+ fc->ars->buf = NULL;
+ fc->atq->buf = NULL;
+ fc->ats->buf = NULL;
+
+ fc->arq->flag = FWXFERQ_PACKET;
+ fc->ars->flag = FWXFERQ_PACKET;
+ fc->atq->flag = FWXFERQ_PACKET;
+ fc->ats->flag = FWXFERQ_PACKET;
+
+ 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_DEVBUF, M_DONTWAIT | M_ZERO);
+ fc->speed_map = (struct fw_speed_map *)malloc(
+ sizeof(struct fw_speed_map),
+ M_DEVBUF, M_DONTWAIT | 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;
+
+ TAILQ_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_DEVBUF,M_DONTWAIT);
+ 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
+
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL) return;
+
+ fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_DEVBUF, M_DONTWAIT);
+ if(fwb == NULL){
+ fw_xfer_free(xfer);
+ }
+ xfer->act.hand = fw_vmaccess;
+ xfer->act_type = FWACT_XFER;
+ xfer->fc = fc;
+ xfer->sc = NULL;
+
+ fwb->start_hi = 0x2;
+ fwb->start_lo = 0;
+ fwb->addrlen = 0xffffffff;
+ fwb->xfer = xfer;
+ fw_bindadd(fc, fwb);
+}
+/*
+ * To lookup binded process from IEEE1394 address.
+ */
+static struct fw_bind *
+fw_bindlookup(fc, dest_hi, dest_lo)
+struct firewire_comm *fc;
+u_int32_t dest_lo, dest_hi;
+{
+ struct fw_bind *tfw;
+ for(tfw = STAILQ_FIRST(&fc->binds) ; tfw != NULL ;
+ tfw = STAILQ_NEXT(tfw, fclist)){
+ if(tfw->xfer->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->xfer->act_type == FWACT_CH){
+ STAILQ_INSERT_HEAD(&fc->ir[fwb->xfer->sub]->binds, fwb, chlist);
+ }
+ return err;
+}
+
+/*
+ * To free IEEE1394 address block.
+ */
+int fw_bindremove(struct firewire_comm *fc, struct fw_bind *fwb)
+{
+ int s;
+
+ s = splfw();
+ /* shall we check the existance? */
+ STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist);
+ splx(s);
+ if (fwb->xfer)
+ fw_xfer_free(fwb->xfer);
+
+ 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_DEVBUF);
+ 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;
+#if 0
+ STAILQ_REMOVE(&fc->tlabels[tlabel], tl, tlabel, link);
+ free(tl, M_DEVBUF);
+#endif
+ splx(s);
+ return(xfer);
+ }
+ }
+ splx(s);
+ return(NULL);
+}
+/*
+ * To allocate IEEE1394 XFER structure.
+ */
+struct fw_xfer *fw_xfer_alloc()
+{
+ struct fw_xfer *xfer;
+#if 0
+ xfer = malloc(sizeof(struct fw_xfer), M_DEVBUF, M_DONTWAIT);
+#else
+ xfer = malloc(sizeof(struct fw_xfer), M_DEVBUF, M_DONTWAIT | M_ZERO);
+#endif
+ if(xfer == NULL) return xfer;
+#if 0 /* xfer->tl = 0 was missing.. */
+ xfer->act_type = FWACT_NULL;
+ xfer->fc = NULL;
+ xfer->retry = 0;
+ xfer->resp = 0;
+ xfer->state = FWXF_INIT;
+ xfer->time = time_second;
+ xfer->sub = -1;
+ xfer->send.buf = NULL;
+ xfer->send.off = 0;
+ xfer->send.len = 0;
+ xfer->recv.buf = NULL;
+ xfer->recv.off = 0;
+ xfer->recv.len = 0;
+ xfer->retry_req = NULL;
+ xfer->act.hand = NULL;
+ xfer->sc = NULL;
+#else
+ xfer->time = time_second;
+ xfer->sub = -1;
+#endif
+ return xfer;
+}
+/*
+ * IEEE1394 XFER post process.
+ */
+void
+fw_xfer_done(struct fw_xfer *xfer)
+{
+ if (xfer->act.hand == NULL)
+ return;
+
+#if XFER_TIMEOUT
+ untimeout(fw_xfer_timeout, (void *)xfer, xfer->ch);
+#endif
+
+ 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?");
+ }
+}
+
+/*
+ * To free IEEE1394 XFER structure.
+ */
+void fw_xfer_free( 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(xfer->state == FWXF_START){
+#if 0 /* this could happen if we call fwohci_arcv() before fwohci_txd() */
+ printf("fw_xfer_free FWXF_START\n");
+#endif
+ s = splfw();
+ xfer->q->drain(xfer->fc, xfer);
+ splx(s);
+ }
+ }
+ if(xfer->send.buf != NULL){
+ free(xfer->send.buf, M_DEVBUF);
+ }
+ if(xfer->recv.buf != NULL){
+ free(xfer->recv.buf, M_DEVBUF);
+ }
+ if(xfer->fc != NULL){
+ fw_tl_free(xfer->fc, xfer);
+ }
+ free(xfer, M_DEVBUF);
+}
+
+/*
+ * Callback for PHY configuration.
+ */
+static void
+fw_phy_config_callback(struct fw_xfer *xfer)
+{
+#if 0
+ printf("phy_config done state=%d resp=%d\n",
+ xfer->state, xfer->resp);
+#endif
+ fw_xfer_free(xfer);
+ /* XXX need bus reset ?? */
+ /* sc->fc->ibr(xfer->fc); LOOP */
+}
+
+/*
+ * 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;
+
+ DELAY(100000);
+ xfer = fw_xfer_alloc();
+ xfer->send.len = 12;
+ xfer->send.off = 0;
+ xfer->fc = fc;
+ xfer->retry_req = fw_asybusy;
+ xfer->act.hand = fw_phy_config_callback;
+
+ xfer->send.buf = malloc(sizeof(u_int32_t),
+ M_DEVBUF, M_DONTWAIT | M_ZERO);
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.ld[1] = 0;
+ if (root_node >= 0)
+ fp->mode.ld[1] |= htonl((root_node & 0x3f) << 24 | 1 << 23);
+ if (gap_count >= 0)
+ fp->mode.ld[1] |= htonl(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;
+
+ 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)
+{
+#if 0
+ 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",
+ FWPHYSIDNODE(sid), FWPHYSIDLINK(sid), FWPHYSIDGAP(sid),
+ FWPHYSIDSPD(sid), FWPHYSIDDEL(sid), FWPHYSIDCON(sid),
+ FWPHYSIDPWR(sid), FWPHYSIDP0(sid), FWPHYSIDP1(sid),
+ FWPHYSIDP2(sid), FWPHYSIDIR(sid), FWPHYSIDMORE(sid));
+#else
+ 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
+}
+#endif
+
+/*
+ * To receive self ID.
+ */
+void fw_sidrcv(struct firewire_comm* fc, caddr_t buf, u_int len, u_int off)
+{
+ u_int32_t *p, *sid = (u_int32_t *)(buf + off);
+ 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 ++;
+ }
+ printf("%s: %d nodes", device_get_nameunit(fc->dev), 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;
+#if 1
+ printf(", maxhop <= %d", fc->max_hop);
+#endif
+
+ if(fc->irm == -1 ){
+ printf(", Not found IRM capable node");
+ }else{
+ printf(", cable IRM = %d", fc->irm);
+ if (fc->irm == fc->nodeid)
+ printf(" (me)\n");
+ else
+ printf("\n");
+ }
+
+ if((fc->irm != -1) && (CSRARC(fc, BUS_MGR_ID) == 0x3f) ){
+ if(fc->irm == ((CSRARC(fc, NODE_IDS) >> 16 ) & 0x3f)){
+ fc->status = FWBUSMGRDONE;
+ CSRARC(fc, BUS_MGR_ID) = fc->set_bmr(fc, fc->irm);
+ }else{
+ fc->status = FWBUSMGRELECT;
+ fc->bmrhandle = timeout((timeout_t *)fw_try_bmr,(void *)fc, hz / 8);
+ }
+ }else{
+ fc->status = FWBUSMGRDONE;
+ printf("%s: BMR = %x\n", device_get_nameunit(fc->dev), CSRARC(fc, BUS_MGR_ID));
+ }
+ free(buf, M_DEVBUF);
+#if 1
+ /* XXX optimize gap_count, if I am BMGR */
+ if(fc->irm == ((CSRARC(fc, NODE_IDS) >> 16 ) & 0x3f)){
+ fw_phy_config(fc, -1, gap_cnt[fc->max_hop]);
+ }
+#endif
+#if 1
+ timeout((timeout_t *)fw_bus_probe, (void *)fc, hz/4);
+#else
+ fw_bus_probe(fc);
+#endif
+}
+/*
+ * 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 = TAILQ_FIRST(&fc->devices); fwdev != NULL; fwdev = next) {
+ next = TAILQ_NEXT(fwdev, link);
+ if(fwdev->status != FWDEVINVAL){
+ fwdev->status = FWDEVINVAL;
+ fwdev->rcnt = 0;
+ }else if(fwdev->rcnt < FW_MAXDEVRCNT){
+ fwdev->rcnt ++;
+ }else{
+ TAILQ_REMOVE(&fc->devices, fwdev, link);
+ free(fwdev, M_DEVBUF);
+ }
+ }
+ 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, *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) {
+ printf("fw_bus_explore: 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){
+ for(fwdev = TAILQ_FIRST(&fc->devices); fwdev != NULL;
+ fwdev = TAILQ_NEXT(fwdev, link)){
+ if(fwdev->eui.hi == fc->ongoeui.hi && fwdev->eui.lo == fc->ongoeui.lo){
+ 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_DEVBUF, M_DONTWAIT);
+ if(fwdev == NULL)
+ return;
+ fwdev->rommax = 0;
+ fwdev->dst = fc->ongonode;
+ fwdev->eui.hi = fc->ongoeui.hi; fwdev->eui.lo = fc->ongoeui.lo;
+ fwdev->status = FWDEVINIT;
+#if 0
+ fwdev->speed = CSRARC(fc, SPED_MAP + 8 + fc->ongonode / 4)
+ >> ((3 - (fc->ongonode % 4)) * 8);
+#else
+ fwdev->speed = fc->speed_map->speed[fc->nodeid][fc->ongonode];
+#endif
+
+#if 0
+ TAILQ_INSERT_TAIL(&fc->devices, fwdev, link);
+#else
+ tfwdev = TAILQ_FIRST(&fc->devices);
+ while( tfwdev != NULL &&
+ (tfwdev->eui.hi > fwdev->eui.hi) &&
+ ((tfwdev->eui.hi == fwdev->eui.hi) &&
+ tfwdev->eui.lo > fwdev->eui.lo)){
+ tfwdev = TAILQ_NEXT( tfwdev, link);
+ }
+ if(tfwdev == NULL){
+ TAILQ_INSERT_TAIL(&fc->devices, fwdev, link);
+ }else{
+ TAILQ_INSERT_BEFORE(tfwdev, fwdev, link);
+ }
+#endif
+
+ printf("%s:Discover new %s device ID:%08x%08x\n", device_get_nameunit(fc->dev), 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();
+ if(xfer == NULL){
+ goto done;
+ }
+ xfer->send.len = 16;
+ xfer->spd = 0;
+ xfer->send.buf = malloc(16, M_DEVBUF, M_DONTWAIT);
+ if(xfer->send.buf == NULL){
+ fw_xfer_free( xfer);
+ return;
+ }
+
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.rreqq.dest_hi = htons(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 = htons(xfer->dst);
+ fp->mode.rreqq.dest_lo = htonl(addr);
+ xfer->act.hand = fw_bus_explore_callback;
+
+ err = fw_asyreq(fc, -1, xfer);
+ if(err){
+ fw_xfer_free( xfer);
+ return;
+ }
+#endif
+ return;
+done:
+ /* fw_attach_devs */
+ fc->status = FWBUSEXPDONE;
+ 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 *xfer;
+ struct fw_pkt *fp;
+ int err;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ return NULL;
+ }
+ xfer->send.len = 16;
+ xfer->spd = spd; /* XXX:min(spd, fc->spd) */
+ xfer->send.buf = malloc(16, M_DEVBUF, M_DONTWAIT);
+ if(xfer->send.buf == NULL){
+ fw_xfer_free( xfer);
+ return NULL;
+ }
+
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.rreqq.dest_hi = htons(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 = htons(xfer->dst);
+ fp->mode.rreqq.dest_lo = htonl(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) return;
+ fc = xfer->fc;
+ if(xfer->resp != 0){
+ printf("resp != 0: node=%d addr=0x%x\n",
+ fc->ongonode, fc->ongoaddr);
+ fc->retry_count++;
+ goto nextnode;
+ }
+
+ if(xfer->send.buf == NULL){
+ printf("send.buf == NULL: node=%d addr=0x%x\n",
+ fc->ongonode, fc->ongoaddr);
+ printf("send.buf == NULL\n");
+ fc->retry_count++;
+ goto nextnode;
+ }
+ sfp = (struct fw_pkt *)xfer->send.buf;
+
+ if(xfer->recv.buf == NULL){
+ printf("recv.buf == NULL: node=%d addr=0x%x\n",
+ fc->ongonode, fc->ongoaddr);
+ fc->retry_count++;
+ goto nextnode;
+ }
+ 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 ", ntohl(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 == htonl((0xf0000000 | CSRROMOFF))){
+ rfp->mode.rresq.data = ntohl(rfp->mode.rresq.data);
+ chdr = (struct csrhdr *)(&rfp->mode.rresq.data);
+/* If CSR is minimul confinguration, more investgation is not needed. */
+ if(chdr->info_len == 1){
+ goto nextnode;
+ }else{
+ fc->ongoaddr = CSRROMOFF + 0xc;
+ }
+ }else if(sfp->mode.rreqq.dest_lo == htonl((0xf0000000 |(CSRROMOFF + 0xc)))){
+ fc->ongoeui.hi = ntohl(rfp->mode.rresq.data);
+ fc->ongoaddr = CSRROMOFF + 0x10;
+ }else if(sfp->mode.rreqq.dest_lo == htonl((0xf0000000 |(CSRROMOFF + 0x10)))){
+ fc->ongoeui.lo = ntohl(rfp->mode.rresq.data);
+ if (fc->ongoeui.hi == 0 && fc->ongoeui.lo == 0)
+ 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;
+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;
+}
+/*
+ * Async. write responce support for kernel internal use.
+ */
+int fw_writeres(struct firewire_comm *fc, u_int32_t dst, u_int32_t tlrt){
+ int err = 0;
+ struct fw_xfer *xfer;
+ struct fw_pkt *fp;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ xfer->send.len = 12;
+ xfer->spd = 0;
+ xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT);
+ if(xfer->send.buf == NULL){
+ return ENOMEM;
+ }
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+
+ fp->mode.wres.tlrt = tlrt;
+ fp->mode.wres.tcode = FWTCODE_WRES;
+ fp->mode.wres.pri = 0;
+ fp->mode.wres.dst = htons(dst);
+
+ xfer->act.hand = fw_asy_callback;
+ err = fw_asyreq(fc, -1, xfer);
+ if(err){
+ fw_xfer_free( xfer);
+ return err;
+ }
+ err = tsleep((caddr_t)xfer, FWPRI, "asyreq", 0);
+ fw_xfer_free( xfer);
+
+ return err;
+}
+/*
+ * Async. read responce block support for kernel internal use.
+ */
+int fw_readresb(struct firewire_comm *fc, u_int32_t dst, u_int32_t tlrt, u_int32_t len, u_int32_t *buf){
+ int err = 0;
+ struct fw_xfer *xfer ;
+ struct fw_pkt *fp;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ xfer->send.len = sizeof(struct fw_pkt) + len;
+ xfer->spd = 0;
+ xfer->send.buf = malloc(sizeof(struct fw_pkt) + 1024, M_DEVBUF, M_DONTWAIT);
+ if(xfer->send.buf == NULL){
+ return ENOMEM;
+ }
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.rresb.tlrt = tlrt;
+ fp->mode.rresb.tcode = FWTCODE_RRESB;
+ fp->mode.rresb.pri = 0;
+ fp->mode.rresb.dst = htons(dst);
+ fp->mode.rresb.rtcode = 0;
+ fp->mode.rresb.extcode = 0;
+ fp->mode.rresb.len = htons(len);
+ bcopy(buf, fp->mode.rresb.payload, len);
+ xfer->act.hand = fw_asy_callback;
+ err = fw_asyreq(fc, -1, xfer);
+ if(err){
+ fw_xfer_free( xfer);
+ return err;
+ }
+ err = tsleep((caddr_t)xfer, FWPRI, "asyreq", 0);
+
+ fw_xfer_free( xfer);
+ return err;
+}
+/*
+ * Async. write request block support for kernel internal use.
+ */
+int fw_writereqb(struct firewire_comm *fc, u_int32_t addr_hi, u_int32_t addr_lo, u_int len, u_int32_t *buf){
+ int err = 0;
+ struct fw_xfer *xfer ;
+ struct fw_pkt *fp;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ xfer->send.len = sizeof(struct fw_pkt) + len;
+ xfer->spd = 0;
+ xfer->send.buf = malloc(sizeof(struct fw_pkt) + 1024, M_DEVBUF, M_DONTWAIT);
+ if(xfer->send.buf == NULL){
+ return ENOMEM;
+ }
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.wreqb.dest_hi = htonl(addr_hi & 0xffff);
+ fp->mode.wreqb.tlrt = 0;
+ fp->mode.wreqb.tcode = FWTCODE_WREQB;
+ fp->mode.wreqb.pri = 0;
+ fp->mode.wreqb.dst = htons(addr_hi >> 16);
+ fp->mode.wreqb.dest_lo = htonl(addr_lo);
+ fp->mode.wreqb.len = htons(len);
+ fp->mode.wreqb.extcode = 0;
+ bcopy(buf, fp->mode.wreqb.payload, len);
+ xfer->act.hand = fw_asy_callback;
+ err = fw_asyreq(fc, -1, xfer);
+ if(err){
+ fw_xfer_free( xfer);
+ return err;
+ }
+ err = tsleep((caddr_t)xfer, FWPRI, "asyreq", 0);
+
+ fw_xfer_free( xfer);
+ return err;
+}
+/*
+ * Async. read request support for kernel internal use.
+ */
+int fw_readreqq(struct firewire_comm *fc, u_int32_t addr_hi, u_int32_t addr_lo, u_int32_t *ret){
+ int err = 0;
+ struct fw_xfer *xfer ;
+ struct fw_pkt *fp, *rfp;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ xfer->send.len = 16;
+ xfer->spd = 0;
+ xfer->send.buf = malloc(16, M_DEVBUF, M_DONTWAIT);
+ if(xfer->send.buf == NULL){
+ return ENOMEM;
+ }
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.rreqq.dest_hi = htonl(addr_hi & 0xffff);
+ fp->mode.rreqq.tlrt = 0;
+ fp->mode.rreqq.tcode = FWTCODE_RREQQ;
+ fp->mode.rreqq.pri = 0;
+ xfer->dst = addr_hi >> 16;
+ fp->mode.rreqq.dst = htons(xfer->dst);
+ fp->mode.rreqq.dest_lo = htonl(addr_lo);
+ xfer->act.hand = fw_asy_callback;
+ err = fw_asyreq(fc, -1, xfer);
+ if(err){
+ fw_xfer_free( xfer);
+ return err;
+ }
+ err = tsleep((caddr_t)xfer, FWPRI, "asyreq", 0);
+
+ if(err == 0 && xfer->recv.buf != NULL){
+ rfp = (struct fw_pkt *)xfer->recv.buf;
+ *ret = ntohl(rfp->mode.rresq.data);
+ }
+ fw_xfer_free( xfer);
+ return err;
+}
+/*
+ * To obtain CSR register values.
+ */
+u_int32_t getcsrdata(struct fw_device *fwdev, u_int8_t key)
+{
+ int i;
+ struct csrhdr *chdr;
+ struct csrreg *creg;
+ chdr = (struct csrhdr *)&fwdev->csrrom[0];
+ for( i = chdr->info_len + 4; i <= fwdev->rommax - CSRROMOFF; i+=4){
+ creg = (struct csrreg *)&fwdev->csrrom[i/4];
+ if(creg->key == key){
+ return (u_int32_t)creg->val;
+ }
+ }
+ return 0;
+}
+/*
+ * 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;
+
+ for(fwdev = TAILQ_FIRST(&fc->devices); fwdev != NULL;
+ fwdev = TAILQ_NEXT(fwdev, link)){
+ if(fwdev->status == FWDEVINIT){
+ fwdev->spec = getcsrdata(fwdev, CSRKEY_SPEC);
+ if(fwdev->spec == 0)
+ continue;
+ fwdev->ver = getcsrdata(fwdev, CSRKEY_VER);
+ if(fwdev->ver == 0)
+ continue;
+ fwdev->maxrec = (fwdev->csrrom[2] >> 12) & 0xf;
+
+ switch(fwdev->spec){
+ case CSRVAL_ANSIT10:
+ switch(fwdev->ver){
+ case CSRVAL_T10SBP2:
+ printf("Device SBP-II");
+ break;
+ default:
+ break;
+ }
+ break;
+ case CSRVAL_1394TA:
+ switch(fwdev->ver){
+ case CSR_PROTAVC:
+ printf("Device AV/C");
+ break;
+ case CSR_PROTCAL:
+ printf("Device CAL");
+ break;
+ case CSR_PROTEHS:
+ printf("Device EHS");
+ break;
+ case CSR_PROTHAVI:
+ printf("Device HAVi");
+ break;
+ case CSR_PROTCAM104:
+ printf("Device 1394 Cam 1.04");
+ break;
+ case CSR_PROTCAM120:
+ printf("Device 1394 Cam 1.20");
+ break;
+ case CSR_PROTCAM130:
+ printf("Device 1394 Cam 1.30");
+ break;
+ case CSR_PROTDPP:
+ printf("Device 1394 Direct print");
+ break;
+ case CSR_PROTIICP:
+ printf("Device Industrial & Instrument");
+ break;
+ default:
+ printf("Device unkwon 1394TA");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ fwdev->status = FWDEVATTACHED;
+ printf("\n");
+ }
+ }
+ err = device_get_children(fc->dev, &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("retry_count = %d\n", fc->retry_count);
+ fc->retry_probe_handle = timeout((timeout_t *)fc->ibr,
+ (void *)fc, hz*2);
+ }
+ 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_DEVBUF,M_DONTWAIT);
+ if (tl == NULL) {
+ splx(s);
+ return (-1);
+ }
+ tl->xfer = xfer;
+ STAILQ_INSERT_TAIL(&fc->tlabels[label], tl, link);
+ splx(s);
+ return(label);
+ }
+ }
+ splx(s);
+
+ printf("fw_get_tlabel: no free tlabel\n");
+ return(-1);
+}
+/*
+ * Generic packet receving process.
+ */
+void fw_rcv(struct firewire_comm* fc, caddr_t buf, u_int len, u_int sub, u_int off, u_int spd)
+{
+ struct fw_pkt *fp, *resfp;
+ struct fw_xfer *xfer;
+ struct fw_bind *bind;
+ struct firewire_softc *sc;
+ int s;
+#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 *)(buf + off);
+ switch(fp->mode.common.tcode){
+ case FWTCODE_WRES:
+ case FWTCODE_RRESQ:
+ case FWTCODE_RRESB:
+ case FWTCODE_LRES:
+ xfer = fw_tl2xfer(fc, ntohs(fp->mode.hdr.src),
+ fp->mode.hdr.tlrt >> 2);
+ if(xfer == NULL) {
+ printf("fw_rcv: unknown response "
+ "tcode=%d src=0x%x tl=%x rt=%d data=0x%x\n",
+ fp->mode.common.tcode,
+ ntohs(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, ntohs(fp->mode.hdr.src),
+ (fp->mode.hdr.tlrt >> 2)^3);
+ if (xfer == NULL) {
+ printf("no use...\n");
+ goto err;
+ }
+#else
+ goto err;
+#endif
+ }
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ if((xfer->sub >= 0) &&
+ ((fc->ir[xfer->sub]->flag & FWXFERQ_MODEMASK ) == 0)){
+ xfer->resp = EINVAL;
+ fw_xfer_done(xfer);
+ goto err;
+ }
+ xfer->recv.len = len;
+ xfer->recv.off = off;
+ xfer->recv.buf = buf;
+ xfer->resp = 0;
+ fw_xfer_done(xfer);
+ return;
+ break;
+ case FWACT_CH:
+ default:
+ goto err;
+ break;
+ }
+ break;
+ case FWTCODE_WREQQ:
+ case FWTCODE_WREQB:
+ case FWTCODE_RREQQ:
+ case FWTCODE_RREQB:
+ case FWTCODE_LREQ:
+ bind = fw_bindlookup(fc, ntohs(fp->mode.rreqq.dest_hi),
+ ntohl(fp->mode.rreqq.dest_lo));
+ if(bind == NULL){
+ printf("Unknown service addr 0x%08x:0x%08x tcode=%x\n",
+ ntohs(fp->mode.rreqq.dest_hi),
+ ntohl(fp->mode.rreqq.dest_lo),
+ fp->mode.common.tcode);
+ if (fc->status == FWBUSRESET) {
+ printf("fw_rcv: cannot response(bus reset)!\n");
+ goto err;
+ }
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ return;
+ }
+ xfer->spd = spd;
+ xfer->send.buf = malloc(16, M_DEVBUF, M_DONTWAIT);
+ resfp = (struct fw_pkt *)xfer->send.buf;
+ switch(fp->mode.common.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;
+ }
+ switch(bind->xfer->act_type){
+ case FWACT_XFER:
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL) goto err;
+ xfer->fc = bind->xfer->fc;
+ xfer->sc = bind->xfer->sc;
+ xfer->recv.buf = buf;
+ xfer->recv.len = len;
+ xfer->recv.off = off;
+ xfer->spd = spd;
+ xfer->act.hand = bind->xfer->act.hand;
+ 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->xfer->sub]->queued >=
+ fc->ir[bind->xfer->sub]->maxq){
+ printf("%s:Discard a packet %x %d\n",
+ device_get_nameunit(fc->dev),
+ bind->xfer->sub,
+ fc->ir[bind->xfer->sub]->queued);
+ goto err;
+ }
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL) goto err;
+ xfer->recv.buf = buf;
+ xfer->recv.len = len;
+ xfer->recv.off = off;
+ xfer->spd = spd;
+ s = splfw();
+ fc->ir[bind->xfer->sub]->queued++;
+ STAILQ_INSERT_TAIL(&fc->ir[bind->xfer->sub]->q, xfer, link);
+ splx(s);
+
+ wakeup((caddr_t)fc->ir[bind->xfer->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;
+ }
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL) goto err;
+ xfer->recv.buf = buf;
+ xfer->recv.len = len;
+ xfer->recv.off = off;
+ 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\n");
+ break;
+ }
+err:
+ free(buf, M_DEVBUF);
+}
+/*
+ * Post process for Bus Manager election process.
+ */
+static void
+fw_try_bmr_callback(struct fw_xfer *xfer)
+{
+ struct fw_pkt *sfp,*rfp;
+ struct firewire_comm *fc;
+
+ if(xfer == NULL) return;
+ fc = xfer->fc;
+ if(xfer->resp != 0){
+ goto error;
+ }
+
+ if(xfer->send.buf == NULL){
+ goto error;
+ }
+ sfp = (struct fw_pkt *)xfer->send.buf;
+
+ if(xfer->recv.buf == NULL){
+ goto error;
+ }
+ rfp = (struct fw_pkt *)xfer->recv.buf;
+ CSRARC(fc, BUS_MGR_ID)
+ = fc->set_bmr(fc, ntohl(rfp->mode.lres.payload[0]) & 0x3f);
+ printf("%s: new bus manager %d ",
+ device_get_nameunit(fc->dev), CSRARC(fc, BUS_MGR_ID));
+ if((htonl(rfp->mode.lres.payload[0]) & 0x3f) == fc->nodeid){
+ printf("(me)\n");
+/* If I am bus manager, optimize gapcount */
+ if(fc->max_hop <= MAX_GAPHOP ){
+ fw_phy_config(fc, -1, gap_cnt[fc->max_hop]);
+ }
+ }else{
+ printf("\n");
+ }
+error:
+ fw_xfer_free(xfer);
+}
+/*
+ * To candidate Bus Manager election process.
+ */
+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();
+ if(xfer == NULL){
+ return;
+ }
+ xfer->send.len = 24;
+ xfer->spd = 0;
+ xfer->send.buf = malloc(24, M_DEVBUF, M_DONTWAIT);
+ if(xfer->send.buf == NULL){
+ fw_xfer_free( xfer);
+ return;
+ }
+
+ fc->status = FWBUSMGRELECT;
+
+ xfer->send.off = 0;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.lreq.dest_hi = htons(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 = htons(8);
+ fp->mode.lreq.extcode = htons(FW_LREQ_CMPSWAP);
+ xfer->dst = FWLOCALBUS | fc->irm;
+ fp->mode.lreq.dst = htons(xfer->dst);
+ fp->mode.lreq.dest_lo = htonl(0xf0000000 | BUS_MGR_ID);
+ fp->mode.lreq.payload[0] = 0x3f;
+ fp->mode.lreq.payload[1] = fc->nodeid;
+ xfer->act_type = FWACT_XFER;
+ xfer->act.hand = fw_try_bmr_callback;
+
+ err = fw_asyreq(fc, -1, xfer);
+ if(err){
+ fw_xfer_free( xfer);
+ return;
+ }
+ return;
+}
+/*
+ * 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 + xfer->recv.off);
+
+ printf("vmaccess spd:%2x len:%03x %d data:%08x %08x %08x %08x\n",
+ xfer->spd, xfer->recv.len, xfer->recv.off, 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_DEVBUF, 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_DEVBUF, 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_DEVBUF, 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_DEVBUF, 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;
+ }
+ xfer->send.off = 0;
+ 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;
+}
+/*
+ * 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);
+}
+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..e24ddea
--- /dev/null
+++ b/sys/dev/firewire/firewire.h
@@ -0,0 +1,565 @@
+/*
+ * 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 dv_data{
+ u_int32_t n_write;
+ u_int32_t a_write;
+ u_int32_t k_write;
+ u_int32_t write_done;
+ u_int32_t write_len[16];
+ u_int32_t write_off[16];
+ u_int32_t n_read;
+ u_int32_t a_read;
+ u_int32_t k_read;
+ u_int32_t read_done;
+ u_int32_t read_len[16];
+ u_int32_t read_off[16];
+};
+
+struct dv_data_req_t {
+ unsigned long index;
+ unsigned long len;
+ unsigned long off;
+};
+
+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 FWPMAX_S400 (2048 + 20) /* MAXREC plus space for control data */
+#define FWMAXQUEUE 128
+
+#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];
+};
+#define FWPHYSIDSUBS(SID) (((SID) >> 23) & 1)
+#define FWPHYSIDNODE(SID) (((SID) >> 24) & 0x3f)
+#define FWPHYSIDLINK(SID) (((SID) >> 22) & 1)
+#define FWPHYSIDGAP(SID) (((SID) >> 16) & 0x3f)
+#define FWPHYSIDSPD(SID) (((SID) >> 14) & 0x3)
+#define FWPHYSIDDEL(SID) (((SID) >> 12) & 0x3)
+#define FWPHYSIDCON(SID) (((SID) >> 11) & 1)
+#define FWPHYSIDPWR(SID) (((SID) >> 8) & 0x7)
+#define FWPHYSIDP0(SID) (((SID) >> 6) & 0x3)
+#define FWPHYSIDP1(SID) (((SID) >> 4) & 0x3)
+#define FWPHYSIDP2(SID) (((SID) >> 2) & 0x3)
+#define FWPHYSIDIR(SID) (((SID) >> 1) & 1)
+#define FWPHYSIDMORE(SID) ((SID) & 1)
+#define FWPHYSIDSEQ(SID) (((SID) >> 20) & 0x7)
+#define FWPHYSIDPA(SID) (((SID) >> 16) & 0x3)
+#define FWPHYSIDPB(SID) (((SID) >> 14) & 0x3)
+#define FWPHYSIDPC(SID) (((SID) >> 12) & 0x3)
+#define FWPHYSIDPD(SID) (((SID) >> 10) & 0x3)
+#define FWPHYSIDPE(SID) (((SID) >> 8) & 0x3)
+#define FWPHYSIDPF(SID) (((SID) >> 6) & 0x3)
+#define FWPHYSIDPG(SID) (((SID) >> 4) & 0x3)
+#define FWPHYSIDPH(SID) (((SID) >> 2) & 0x3)
+struct fw_pkt{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ union{
+ u_int32_t ld[0];
+ struct {
+ u_int32_t :28,
+ tcode:4;
+ }common;
+ struct {
+ u_int16_t len;
+ u_int8_t chtag;
+ u_int8_t sy:4,
+ tcode:4;
+ u_int32_t payload[0];
+ }stream;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ }hdr;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t dest_hi;
+ u_int32_t dest_lo;
+ }rreqq;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t res1:4,
+ rtcode:4,
+ res2:8;
+ u_int32_t res3;
+ }wres;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t dest_hi;
+ u_int32_t dest_lo;
+ u_int16_t len;
+ u_int16_t extcode:16;
+ }rreqb;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t dest_hi:16;
+ u_int32_t dest_lo;
+ u_int32_t data;
+ }wreqq;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t dest_hi;
+ u_int32_t dest_lo;
+ u_int32_t data;
+ }cyc;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t res1:4,
+ rtcode:4,
+ res2:8;
+ u_int32_t res3;
+ u_int32_t data;
+ }rresq;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t dest_hi;
+ u_int32_t dest_lo;
+ u_int16_t len;
+ u_int16_t extcode;
+ u_int32_t payload[0];
+ }wreqb;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t dest_hi;
+ u_int32_t dest_lo;
+ u_int16_t len;
+ u_int16_t 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 {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t res1:4,
+ rtcode:4,
+ res2:8;
+ u_int32_t res3;
+ u_int16_t len;
+ u_int16_t extcode;
+ u_int32_t payload[0];
+ }rresb;
+ struct {
+ u_int16_t dst;
+ u_int8_t tlrt;
+ u_int8_t pri:4,
+ tcode:4;
+ u_int16_t src;
+ u_int16_t res1:4,
+ rtcode:4,
+ res2:8;
+ u_int32_t res3;
+ u_int16_t len;
+ u_int16_t extcode;
+ u_int32_t payload[0];
+ }lres;
+ }mode;
+#else
+ union{
+ u_int32_t ld[0];
+ struct {
+ u_int32_t :4,
+ tcode:4,
+ :24;
+ }common;
+ struct {
+ u_int8_t sy:4,
+ tcode:4;
+ u_int8_t chtag;
+ u_int16_t len;
+ u_int32_t payload[0];
+ }stream;
+ struct {
+ u_int32_t pri:4,
+ tcode:4,
+ tlrt:8,
+ dst:16;
+ u_int32_t :16,
+ src:16;
+ }hdr;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t dest_hi;
+ u_int16_t src;
+ u_int32_t dest_lo;
+ }rreqq;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t res1:12,
+ rtcode:4;
+ u_int16_t src;
+ u_int32_t res3;
+ }wres;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t dest_hi;
+ u_int16_t src;
+ u_int32_t dest_lo;
+ u_int16_t extcode:16;
+ u_int16_t len;
+ }rreqb;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t dest_hi:16;
+ u_int16_t src;
+ u_int32_t dest_lo;
+ u_int32_t data;
+ }wreqq;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t dest_hi;
+ u_int16_t src;
+ u_int32_t dest_lo;
+ u_int32_t data;
+ }cyc;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t res1:12,
+ rtcode:4;
+ u_int16_t src;
+ u_int32_t res3;
+ u_int32_t data;
+ }rresq;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t dest_hi;
+ u_int16_t src;
+ u_int32_t dest_lo;
+ u_int16_t extcode;
+ u_int16_t len;
+ u_int32_t payload[0];
+ }wreqb;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t dest_hi;
+ u_int16_t src;
+ u_int32_t dest_lo;
+ u_int16_t extcode;
+ u_int16_t len;
+ u_int32_t payload[0];
+ }lreq;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t res1:12,
+ rtcode:4;
+ u_int16_t src;
+ u_int32_t res3;
+ u_int16_t extcode;
+ u_int16_t len;
+ u_int32_t payload[0];
+ }rresb;
+ struct {
+ u_int8_t pri:4,
+ tcode:4;
+ u_int8_t tlrt;
+ u_int16_t dst;
+ u_int16_t res1:12,
+ rtcode:4;
+ u_int16_t src;
+ u_int32_t res3;
+ u_int16_t extcode;
+ u_int16_t len;
+ u_int32_t payload[0];
+ }lres;
+ }mode;
+#endif
+};
+struct fw_eui64 {
+ u_int32_t hi, 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_devlstreq {
+ int n;
+ struct fw_eui64 eui[64];
+ u_int16_t dst[64];
+ u_int16_t status[64];
+};
+#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
+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;
+};
+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_map_buf {
+ int len;
+ void *ptr;
+};
+struct fw_crom_buf {
+ struct fw_eui64 eui;
+ int len;
+ void *ptr;
+};
+#define FWSTMAXCHUNK 16
+/*
+ * Firewire specific system requests.
+ */
+#define FW_SSTDV _IOWR('S', 85, unsigned int)
+#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_GSPMAP _IOW('S', 6, struct fw_speed_map *)
+#define FW_GCROM _IOWR('S', 7, struct fw_crom_buf)
+
+#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..de30b22
--- /dev/null
+++ b/sys/dev/firewire/firewire_phy.h
@@ -0,0 +1,87 @@
+/*
+ * 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/firewirebusreg.h b/sys/dev/firewire/firewirebusreg.h
new file mode 100644
index 0000000..63886a8
--- /dev/null
+++ b/sys/dev/firewire/firewirebusreg.h
@@ -0,0 +1,38 @@
+/*
+ * 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$
+ *
+ */
+struct firewire_bus{
+ device_t bdev;
+};
diff --git a/sys/dev/firewire/firewirereg.h b/sys/dev/firewire/firewirereg.h
new file mode 100644
index 0000000..d84e25c
--- /dev/null
+++ b/sys/dev/firewire/firewirereg.h
@@ -0,0 +1,349 @@
+/*
+ * 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
+
+#define splfw splimp
+
+struct fw_device{
+ u_int16_t dst;
+ struct fw_eui64 eui;
+ u_int32_t spec;
+ u_int32_t ver;
+ 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;
+ device_t dev;
+ u_int32_t status;
+#define FWDEVINIT 1
+#define FWDEVATTACHED 2
+#define FWDEVINVAL 3
+ TAILQ_ENTRY(fw_device) link;
+ LIST_HEAD(, fw_xfer) txqueue;
+ LIST_HEAD(, fw_xfer) rxqueue;
+};
+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_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;
+ struct csrdir{
+ u_int32_t ongoaddr;
+ u_int32_t off;
+ SLIST_ENTRY(csrdir) link;
+ };
+ 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;
+ u_int8_t eui[8];
+ STAILQ_HEAD(fw_queue, fw_xfer);
+ struct fw_xferq {
+ int flag;
+#define FWXFERQ_CHTAGMASK 0xff
+#define FWXFERQ_RUNNING (1 << 8)
+#define FWXFERQ_STREAM (1 << 9)
+
+#define FWXFERQ_PACKET (1 << 10)
+#define FWXFERQ_BULK (1 << 11)
+#define FWXFERQ_DV (1 << 12)
+#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*));
+ void (*drain) __P((struct firewire_comm*, struct fw_xfer*));
+ struct fw_queue q;
+ u_int queued;
+ u_int maxq;
+ u_int psize;
+ u_int packets;
+ u_int error;
+ STAILQ_HEAD(, fw_bind) binds;
+ caddr_t buf;
+ u_int bnchunk;
+ u_int bnpacket;
+ u_int btpacket;
+ struct fw_bulkxfer *bulkxfer;
+ STAILQ_HEAD(, fw_bulkxfer) stvalid;
+ STAILQ_HEAD(, fw_bulkxfer) stfree;
+ struct fw_bulkxfer *stdma;
+ struct fw_bulkxfer *stdma2;
+ struct fw_bulkxfer *stproc;
+ u_int procptr;
+ int dvdbc, dvdiff, dvsync;
+ struct fw_dvbuf *dvbuf;
+ STAILQ_HEAD(, fw_dvbuf) dvvalid;
+ STAILQ_HEAD(, fw_dvbuf) dvfree;
+ struct fw_dvbuf *dvdma;
+ struct fw_dvbuf *dvproc;
+ u_int dvptr;
+ u_int dvpacket;
+ u_int need_wakeup;
+ struct selinfo rsel;
+ caddr_t sc;
+ void (*hand) __P((struct fw_xferq *));
+ };
+ struct fw_xferq
+ *arq, *atq, *ars, *ats, *it[FW_MAX_DMACH],*ir[FW_MAX_DMACH];
+ struct fw_bulkxfer{
+ u_int32_t flag;
+ caddr_t buf;
+ STAILQ_ENTRY(fw_bulkxfer) link;
+ caddr_t start;
+ caddr_t end;
+ u_int npacket;
+ };
+ struct fw_dvbuf{
+ caddr_t buf;
+ STAILQ_ENTRY(fw_dvbuf) link;
+ };
+ struct tlabel{
+ struct fw_xfer *xfer;
+ STAILQ_ENTRY(tlabel) link;
+ };
+ STAILQ_HEAD(, tlabel) tlabels[0x40];
+ struct fw_bind{
+ u_int32_t start_hi, start_lo, addrlen;
+ struct fw_xfer* xfer;
+ STAILQ_ENTRY(fw_bind) fclist;
+ STAILQ_ENTRY(fw_bind) chlist;
+ };
+ STAILQ_HEAD(, fw_bind) binds;
+ TAILQ_HEAD(, fw_device) devices;
+ STAILQ_HEAD(, fw_xfer) pending;
+ volatile u_int32_t *sid_buf;
+ u_int sid_cnt;
+#define CSRSIZE 0x4000
+ u_int32_t csr_arc[CSRSIZE/4];
+#define CROMSIZE 0x400
+ u_int32_t *config_rom;
+ struct fw_topology_map *topology_map;
+ struct fw_speed_map *speed_map;
+ struct callout_handle tlhandle;
+ struct callout_handle bmrhandle;
+ struct callout_handle timeouthandle;
+ struct callout_handle retry_probe_handle;
+ 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;
+};
+#define CSRARC(sc, offset) ((sc)->csr_arc[(offset)/4])
+
+
+struct fw_xfer{
+ caddr_t sc;
+ struct firewire_comm *fc;
+ struct fw_xferq *q;
+ struct callout_handle ch;
+ time_t time;
+ struct fw_tlabel *tlabel;
+ u_int8_t spd;
+ u_int8_t tcode;
+ int 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
+ int state;
+ u_int8_t retry;
+ u_int8_t tl;
+ int sub;
+ int32_t dst;
+ u_int8_t act_type;
+#define FWACT_NULL 0
+#define FWACT_XFER 2
+#define FWACT_CH 3
+ void (*retry_req) __P((struct fw_xfer *));
+ union{
+ void (*hand) __P((struct fw_xfer *));
+
+ } act;
+ union{
+ struct {
+ struct fw_device *device;
+ } req;
+ struct {
+ struct stch *channel;
+ } stream;
+ } mode;
+ struct {
+ u_int16_t len, off;
+ caddr_t buf;
+ } send, recv;
+ struct mbuf *mbuf;
+ STAILQ_ENTRY(fw_xfer) link;
+};
+void fw_sidrcv __P((struct firewire_comm *, caddr_t, u_int, u_int));
+void fw_rcv __P((struct firewire_comm *, caddr_t, u_int, u_int, u_int, u_int));
+void fw_xfer_free __P(( struct fw_xfer*));
+struct fw_xfer *fw_xfer_alloc __P((void));
+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));
+int fw_readreqq __P((struct firewire_comm *, u_int32_t, u_int32_t, u_int32_t *));
+int fw_writereqb __P((struct firewire_comm *, u_int32_t, u_int32_t, u_int32_t, u_int32_t *));
+int fw_readresb __P((struct firewire_comm *, u_int32_t, u_int32_t, u_int32_t, u_int32_t*));
+int fw_writeres __P((struct firewire_comm *, u_int32_t, u_int32_t));
+u_int32_t getcsrdata __P((struct fw_device *, u_int8_t));
+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 *));
+
+extern int firewire_debug;
+extern devclass_t firewire_devclass;
+
+#define DV_BROADCAST_ON (1<<30)
+#define IP_CHANNELS 0x0234
+
+#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 0x0210
+#define BUS_MGR_ID 0x021c
+#define BANDWIDTH_AV 0x0220
+#define CHANNELS_AV_HI 0x0224
+#define CHANNELS_AV_LO 0x0228
+
+#define CONF_ROM 0x0400
+
+#define TOPO_MAP 0x1000
+#define SPED_MAP 0x2000
+
+#define oMPR 0x900
+#define oPCR 0x904
+
+#define iMPR 0x980
+#define iPCR 0x984
+
+#define FWPRI ((PZERO+8)|PCATCH)
+
+#ifdef __alpha__
+#undef vtophys
+#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)(va))
+#endif /* __alpha__ */
diff --git a/sys/dev/firewire/fw_tap.c b/sys/dev/firewire/fw_tap.c
new file mode 100644
index 0000000..765c83c
--- /dev/null
+++ b/sys/dev/firewire/fw_tap.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <dev/firewire/firewire.h>
+
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <err.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+
+struct fwinfo {
+ int fd;
+ struct fw_asyreq *asyreq;
+} fw;
+
+void
+usage(void)
+{
+ printf("fw_tap [-f fwdev] [-t tapdev] channel\n");
+ exit(0);
+}
+static struct fwinfo
+init_fw(char *fwdev, int ch)
+{
+ struct fw_isochreq chreq;
+ struct fw_asyreq *asyreq;
+ int fd;
+ struct fwinfo fw;
+
+ if ((fd = open(fwdev, O_RDWR)) < 0)
+ err(1, "open");
+
+ printf("ch=%d\n", ch);
+ chreq.ch = ch;
+ chreq.tag = 0;
+ if (ioctl(fd, FW_SRSTREAM, &chreq) < 0)
+ err(1, "ioctl");
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq));
+ asyreq->req.type = FWASREQSTREAM;
+ asyreq->req.sped = 2; /* S400 */
+ asyreq->pkt.mode.stream.tcode = FWTCODE_STREAM;
+ asyreq->pkt.mode.stream.sy = 0;
+ asyreq->pkt.mode.stream.chtag = ch;
+
+ fw.asyreq = asyreq;
+ fw.fd = fd;
+
+ return fw;
+}
+
+static int
+init_tap(char *tapdev)
+{
+ int fd;
+
+ if ((fd = open(tapdev, O_RDWR)) < 0)
+ err(1, "open");
+ /*
+ * XXX We need to make it be promiscuous mode to discard packets
+ * which upper layer shouldn't see in ether_demux() of if_ethersub.c.
+ * use netgraph?
+ */
+
+ return fd;
+}
+
+
+static void
+send_stream(struct fwinfo *fw, int len)
+{
+ struct fw_asyreq *asyreq;
+
+ asyreq = fw->asyreq;
+ asyreq->req.len = len + 4;
+ asyreq->pkt.mode.stream.len = htons(len);
+ if (ioctl(fw->fd, FW_ASYREQ, asyreq) < 0)
+ err(1, "ioctl");
+}
+
+
+#define MTU 2048
+#define HDR 4
+static void
+fw2tap(struct fwinfo fw, int tapd)
+{
+ int res, size;
+ struct pollfd pfd[2];
+ char *buf, *fwbuf;
+ int fwd;
+
+ fwd = fw.fd;
+ pfd[0].fd = fwd;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = tapd;
+ pfd[1].events = POLLIN;
+ fwbuf = (char *)&fw.asyreq->pkt.mode.stream.payload[0];
+ if ((buf = malloc(MTU + HDR)) == NULL)
+ err(1, "malloc");
+
+
+ while (1) {
+ res = poll(pfd, 2, -1);
+ if (pfd[0].revents & POLLIN) {
+ size = read(fwd, buf, MTU + HDR);
+#if 0
+ printf("in %5d bytes\n", size - HDR);
+#endif
+ write(tapd, buf + HDR, size - HDR);
+ }
+ if (pfd[1].revents & POLLIN) {
+ size = read(tapd, fwbuf, MTU);
+#if 0
+ printf("out %5d bytes\n", size);
+#endif
+ send_stream(&fw, size);
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ int tapd;
+ struct fwinfo fw;
+ char *fwdev, *tapdev;
+
+ if (argc < 2) {
+ usage();
+ }
+
+ fwdev = "/dev/fw2";
+ tapdev = "/dev/tap0";
+ ch = 0;
+
+ argv++;
+ argc--;
+ while (argc > 0) {
+ if (strcmp(*argv, "-f") == 0) {
+ /* fw device */
+ argv++;
+ argc--;
+ if (argc > 0) {
+ fwdev = *argv;
+ argv++;
+ argc--;
+ } else {
+ usage();
+ }
+ } else if (strcmp(*argv, "-t") == 0) {
+ /* tap device */
+ argv++;
+ argc--;
+ if (argc > 0) {
+ tapdev = *argv;
+ argv++;
+ argc--;
+ } else {
+ usage();
+ }
+ } else if (argc > 0) {
+ ch = strtoul(*argv, (char **)NULL, 0);
+ argv++;
+ argc--;
+ } else {
+ usage();
+ }
+ }
+ fw = init_fw(fwdev, ch);
+ tapd = init_tap(tapdev);
+ fw2tap(fw, tapd);
+ return 0;
+}
diff --git a/sys/dev/firewire/fwmem.c b/sys/dev/firewire/fwmem.c
new file mode 100644
index 0000000..6005fc8
--- /dev/null
+++ b/sys/dev/firewire/fwmem.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $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/uio.h>
+#include <sys/sysctl.h>
+
+#include <sys/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_node=0, fwmem_speed=2, fwmem_debug=0;
+SYSCTL_DECL(_hw_firewire);
+SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
+ "Firewire Memory Access");
+SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, node, CTLFLAG_RW, &fwmem_node, 0,
+ "Fwmem target node");
+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");
+
+struct fw_xfer *
+fwmem_read_quad(
+ struct firewire_comm *fc,
+ int dst,
+ u_int16_t dst_hi,
+ u_int32_t dst_lo
+ )
+{
+ struct fw_xfer *xfer;
+ struct fw_pkt *fp;
+ int err = 0;
+
+ xfer = fw_xfer_alloc();
+ if (xfer == NULL) {
+ err = ENOMEM;
+ return NULL;
+ }
+ xfer->fc = fc;
+ xfer->dst = FWLOCALBUS | dst;
+ xfer->spd = fwmem_speed; /* XXX */
+ xfer->send.len = 12;
+ xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (xfer->send.buf == NULL) {
+ err = ENOMEM;
+ goto error;
+ }
+ xfer->send.off = 0;
+ xfer->act.hand = fw_asy_callback;
+ xfer->retry_req = fw_asybusy;
+ xfer->sc = NULL;
+
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.rreqq.tcode = FWTCODE_RREQQ;
+ fp->mode.rreqq.dst = htons(xfer->dst);
+ fp->mode.rreqq.dest_hi = htons(dst_hi);
+ fp->mode.rreqq.dest_lo = htonl(dst_lo);
+
+ if (fwmem_debug)
+ printf("fwmem: %d %04x:%08x\n", dst, dst_hi, dst_lo);
+ err = fw_asyreq(fc, -1, xfer);
+ if (err)
+ goto error;
+ err = tsleep((caddr_t)xfer, FWPRI, "fwmem", hz);
+ if (err == 0) {
+ return xfer;
+ }
+error:
+ fw_xfer_free(xfer);
+ return NULL;
+}
+
+struct fw_xfer *
+fwmem_read_block(
+ struct firewire_comm *fc,
+ int dst,
+ u_int16_t dst_hi,
+ u_int32_t dst_lo,
+ int len
+ )
+{
+ struct fw_xfer *xfer;
+ struct fw_pkt *fp;
+ int err = 0;
+
+ xfer = fw_xfer_alloc();
+ if (xfer == NULL) {
+ err = ENOMEM;
+ return NULL;
+ }
+ xfer->fc = fc;
+ xfer->dst = FWLOCALBUS | dst;
+ xfer->spd = fwmem_speed; /* XXX */
+ xfer->send.len = 16;
+ xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (xfer->send.buf == NULL) {
+ err = ENOMEM;
+ goto error;
+ }
+ xfer->send.off = 0;
+ xfer->act.hand = fw_asy_callback;
+ xfer->retry_req = fw_asybusy;
+ xfer->sc = NULL;
+
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.rreqb.tcode = FWTCODE_RREQB;
+ fp->mode.rreqb.dst = htons(xfer->dst);
+ fp->mode.rreqb.dest_hi = htons(dst_hi);
+ fp->mode.rreqb.dest_lo = htonl(dst_lo);
+ fp->mode.rreqb.len = htons(len);
+
+ if (fwmem_debug)
+ printf("fwmem: %d %04x:%08x %d\n", dst, dst_hi, dst_lo, len);
+ err = fw_asyreq(fc, -1, xfer);
+ if (err)
+ goto error;
+ err = tsleep((caddr_t)xfer, FWPRI, "fwmem", hz);
+ if (err == 0) {
+ return xfer;
+ }
+error:
+ fw_xfer_free(xfer);
+ return NULL;
+}
+
+int
+fwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
+{
+ int err = 0;
+ return err;
+}
+
+int
+fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
+{
+ int err = 0;
+ return err;
+}
+
+#define MAXLEN 2048
+#define USE_QUAD 0
+int
+fwmem_read (dev_t dev, struct uio *uio, int ioflag)
+{
+ struct firewire_softc *sc;
+ struct firewire_comm *fc;
+ struct fw_xfer *xfer;
+ int err = 0, pad;
+ 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);
+ fc = sc->fc;
+
+ pad = uio->uio_offset % 4;
+ if (fwmem_debug && pad != 0)
+ printf("unaligned\n");
+ while(uio->uio_resid > 0) {
+ offset = uio->uio_offset;
+ offset -= pad;
+ dst_hi = (offset >> 32) & 0xffff;
+ dst_lo = offset & 0xffffffff;
+#if USE_QUAD
+ xfer = fwmem_read_quad(fc, fwmem_node, dst_hi, dst_lo);
+ if (xfer == NULL || xfer->resp != 0 || xfer->recv.buf == NULL)
+ return EINVAL; /* XXX */
+ err = uiomove(xfer->recv.buf + xfer->recv.off + 4*3 + pad,
+ 4 - pad, uio);
+#else
+ len = uio->uio_resid;
+ if (len > MAXLEN)
+ len = MAXLEN;
+ xfer = fwmem_read_block(fc, fwmem_node, dst_hi, dst_lo, len);
+ if (xfer == NULL || xfer->resp != 0 || xfer->recv.buf == NULL)
+ return EINVAL; /* XXX */
+ err = uiomove(xfer->recv.buf + xfer->recv.off + 4*4 + pad,
+ len - pad, uio);
+#endif
+ if (err)
+ return err;
+ fw_xfer_free(xfer);
+ pad = 0;
+ }
+ return err;
+}
+int
+fwmem_write (dev_t dev, struct uio *uio, int ioflag)
+{
+ return EINVAL;
+}
+int
+fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
+{
+ return EINVAL;
+}
+int
+fwmem_poll (dev_t dev, int events, fw_proc *td)
+{
+ return EINVAL;
+}
+int
+fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
+{
+ return EINVAL;
+}
diff --git a/sys/dev/firewire/fwmem.h b/sys/dev/firewire/fwmem.h
new file mode 100644
index 0000000..30f7641
--- /dev/null
+++ b/sys/dev/firewire/fwmem.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct fw_xfer * fwmem_read_quad(struct firewire_comm *,
+ int, u_int16_t, u_int32_t);
+struct fw_xfer * fwmem_read_block(struct firewire_comm *,
+ int, u_int16_t, u_int32_t, int);
+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..4cc6846
--- /dev/null
+++ b/sys/dev/firewire/fwohci.c
@@ -0,0 +1,2649 @@
+/*
+ * 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$
+ *
+ */
+extern int nxfer;
+#define DEBUG_PACKET
+#undef DEBUG_PACKET
+#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/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/uio.h>
+#include <sys/sockio.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#ifdef __FreeBSD__
+#include <machine/cpufunc.h> /* for rdtsc proto for clock.h below */
+#include <machine/clock.h>
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h> /* for vtophys proto */
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirebusreg.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/fwohcireg.h>
+#include <dev/firewire/fwohcivar.h>
+#include <dev/firewire/firewire_phy.h>
+
+#define OHCI_DEBUG
+#undef OHCI_DEBUG
+/*
+#define 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"};
+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 2
+extern char linkspeed[MAX_SPEED+1][0x10];
+extern int maxrec[MAX_SPEED+1];
+static char dbcond[4][0x10]={"NEV","C=1", "C=0", "ALL"};
+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))
+
+#endif /* __FreeBSD__ */
+
+#define senderr(e) { error = (e); goto bad;}
+
+static void fwohci_ibr __P((struct firewire_comm *));
+static void fwohci_db_init __P((struct fwohci_dbch *));
+static void fwohci_db_free __P((struct fwohci_dbch *));
+static void fwohci_arcv __P((struct fwohci_softc *, struct fwohci_dbch *));
+static void fwohci_ircv __P((struct fwohci_softc *, struct fwohci_dbch *));
+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 void fwohci_drain_atq __P((struct firewire_comm *, struct fw_xfer *));
+static void fwohci_drain_ats __P((struct firewire_comm *, struct fw_xfer *));
+static void fwohci_drain __P((struct firewire_comm *, struct fw_xfer *, 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_irxpp_enable __P((struct firewire_comm *, int));
+static int fwohci_irxbuf_enable __P((struct firewire_comm *, int));
+static int fwohci_irx_disable __P((struct firewire_comm *, int));
+static void fwohci_irx_post __P((struct firewire_comm *, u_int32_t *));
+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 fwohcidb_tr *, unsigned short, int, void *, void *));
+static int fwohci_add_tx_buf __P((struct fwohcidb_tr *, unsigned short, int, void *));
+static void dump_db __P((struct fwohci_softc *, u_int32_t));
+static void print_db __P((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 *));
+
+/*
+ * 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_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_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(100);
+ bm = OREAD(sc, OHCI_CSR_DATA);
+ if((bm & 0x3f) == 0x3f){
+ printf("fw_set_bus_manager: %d->%d (loop=%d)\n", bm, node, i);
+ bm = node;
+ }else{
+ printf("fw_set_bus_manager: %d-X%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;
+ u_int i;
+
+ addr &= 0xf;
+ fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR);
+ OWRITE(sc, OHCI_PHYACCESS, fun);
+ for ( i = 0 ; i < 1000 ; i ++ ){
+ fun = OREAD(sc, OHCI_PHYACCESS);
+ if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0)
+ break;
+ DELAY(1000);
+ }
+ if( i >= 1000)
+ device_printf(sc->fc.dev, "cannot read phy\n");
+ 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;
+}
+int fwohci_init(struct fwohci_softc *sc, device_t dev)
+{
+ int err = 0;
+ int i;
+ u_int32_t reg, reg2;
+ struct fwohcidb_tr *db_tr;
+ int e1394a = 1;
+
+ reg = OREAD(sc, OHCI_VERSION);
+ device_printf(dev, "OHCI version %x.%x (ROM=%d)\n",
+ (reg>>16) & 0xff, reg & 0xff, (reg>>24) & 1);
+
+#if 0
+/* XXX: Not support bridge function yet, then clear bus ID */
+ OWRITE(sc, FWOHCI_NODEID, (OREAD(sc, FWOHCI_NODEID)) & 0xffff003f);
+#endif
+
+/* XXX: Available Isochrounous DMA channel probe */
+ for( i = 0 ; i < 0x20 ; i ++ ){
+ OWRITE(sc, OHCI_IRCTL(i), OHCI_CNTL_DMA_RUN);
+ reg = OREAD(sc, OHCI_IRCTL(i));
+ if(!(reg & OHCI_CNTL_DMA_RUN)) break;
+ OWRITE(sc, OHCI_ITCTL(i), OHCI_CNTL_DMA_RUN);
+ reg = OREAD(sc, OHCI_ITCTL(i));
+ if(!(reg & OHCI_CNTL_DMA_RUN)) 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.start = NULL;
+ sc->arrs.xferq.start = NULL;
+ sc->atrq.xferq.start = fwohci_start_atq;
+ sc->atrs.xferq.start = fwohci_start_ats;
+
+ sc->arrq.xferq.drain = NULL;
+ sc->arrs.xferq.drain = NULL;
+ sc->atrq.xferq.drain = fwohci_drain_atq;
+ sc->atrs.xferq.drain = fwohci_drain_ats;
+
+ sc->arrq.ndesc = 1;
+ sc->arrs.ndesc = 1;
+ sc->atrq.ndesc = 10;
+ sc->atrs.ndesc = 10 / 2;
+
+ sc->arrq.ndb = NDB;
+ sc->arrs.ndb = NDB / 2;
+ sc->atrq.ndb = NDB;
+ sc->atrs.ndb = NDB / 2;
+
+ sc->arrq.dummy = NULL;
+ sc->arrs.dummy = NULL;
+ sc->atrq.dummy = NULL;
+ sc->atrs.dummy = NULL;
+ 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->cromptr = (u_int32_t *)
+ contigmalloc(CROMSIZE * 2, M_DEVBUF, M_NOWAIT, 0, ~0, 1<<10, 0);
+
+ if(sc->cromptr == NULL){
+ return ENOMEM;
+ }
+ sc->fc.dev = dev;
+ sc->fc.config_rom = &(sc->cromptr[CROMSIZE/4]);
+
+ 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);
+
+
+ fw_init(&sc->fc);
+
+/* 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);
+ device_printf(dev, "resetting OHCI...");
+ i = 0;
+ while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) {
+ if (i++ > 100) break;
+ DELAY(1000);
+ }
+ printf("done (%d)\n", i);
+#if 0
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_LINKEN | OHCI_HCC_LPS);
+#endif
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS);
+ /* XXX wait for SCLK. */
+ DELAY(10000);
+
+ reg = OREAD(sc, OHCI_BUS_OPT);
+ reg2 = reg | OHCI_BUSFNC;
+ /* XXX */
+ if (((reg & 0x0000f000) >> 12) < 10)
+ reg2 = (reg2 & 0xffff0fff) | (10 << 12);
+ device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2);
+ OWRITE(sc, OHCI_BUS_OPT, reg2);
+
+ OWRITE(sc, OHCI_CROMHDR, sc->fc.config_rom[0]);
+ OWRITE(sc, OHCI_CROMPTR, vtophys(&sc->fc.config_rom[0]));
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND);
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR);
+
+/*
+ * 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 .
+ */
+ /* Wait a while */
+ reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
+#if 0
+ /* try again */
+ DELAY(1000);
+ reg = fwphy_rddata(sc, FW_PHY_SPD_REG);
+#endif
+ 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;
+ }
+ sc->fc.maxrec = maxrec[sc->fc.speed];
+ device_printf(dev,
+ "Link 1394 only %s, %d ports, maxrec %d bytes.\n",
+ linkspeed[sc->fc.speed], sc->fc.nport, sc->fc.maxrec);
+ }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;
+ }
+ sc->fc.maxrec = maxrec[sc->fc.speed];
+ device_printf(dev,
+ "Link 1394a available %s, %d ports, maxrec %d bytes.\n",
+ linkspeed[sc->fc.speed], sc->fc.nport, sc->fc.maxrec);
+
+ /* check programPhyEnable */
+ reg2 = fwphy_rddata(sc, 5);
+#if 0
+ if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) {
+#else /* XXX f¤force to enable 1394a */
+ if (e1394a) {
+#endif
+ 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);
+ }
+
+/* SID recieve buffer must allign 2^11 */
+#define OHCI_SIDSIZE (1 << 11)
+ sc->fc.sid_buf = (u_int32_t *) vm_page_alloc_contig( OHCI_SIDSIZE,
+ 0x10000, 0xffffffff, OHCI_SIDSIZE);
+ OWRITE(sc, OHCI_SID_BUF, vtophys(sc->fc.sid_buf));
+ sc->fc.sid_buf++;
+ OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID);
+
+ fwohci_db_init(&sc->arrq);
+ fwohci_db_init(&sc->arrs);
+
+ fwohci_db_init(&sc->atrq);
+ fwohci_db_init(&sc->atrs);
+
+ reg = OREAD(sc, FWOHCIGUID_H);
+ for( i = 0 ; i < 4 ; i ++){
+ sc->fc.eui[3 - i] = reg & 0xff;
+ reg = reg >> 8;
+ }
+ reg = OREAD(sc, FWOHCIGUID_L);
+ for( i = 0 ; i < 4 ; i ++){
+ sc->fc.eui[7 - i] = reg & 0xff;
+ reg = reg >> 8;
+ }
+ device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ sc->fc.eui[0], sc->fc.eui[1], sc->fc.eui[2], sc->fc.eui[3],
+ sc->fc.eui[4], sc->fc.eui[5], sc->fc.eui[6], sc->fc.eui[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;
+ sc->fc.irx_post = fwohci_irx_post;
+ sc->fc.itx_post = NULL;
+ sc->fc.timeout = fwohci_timeout;
+ sc->fc.poll = fwohci_poll;
+ sc->fc.set_intr = fwohci_set_intr;
+#if 0
+ /* why this need twice? */
+ fwohci_db_init(&sc->arrq);
+#endif
+ /* enable link */
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN);
+ fw_busreset(&sc->fc);
+ fwohci_rx_enable(sc, &sc->arrq);
+ fwohci_rx_enable(sc, &sc->arrs);
+
+ 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;
+ }
+ sc->atrq.flags = sc->atrs.flags = 0;
+
+ OWRITE(sc, FWOHCI_RETRY,
+ (0xffff << 16 )| (0x0f << 8) | (0x0f << 4) | 0x0f) ;
+ OWRITE(sc, FWOHCI_INTMASKCLR, ~0);
+ 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);
+
+ OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD);
+ OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD);
+
+#if 0
+ fwohci_ibr(sc);
+#endif
+
+ return err;
+}
+void fwohci_timeout(void *arg)
+{
+/*
+ fwohci_txd(sc, &(sc->atrq));
+ fwohci_txd(sc, &(sc->atrs));
+ fw_expire_tlabel(&sc->fc);
+*/
+ struct fwohci_softc *sc;
+
+ sc = (struct fwohci_softc *)arg;
+ sc->fc.timeouthandle = timeout(fwohci_timeout,
+ (void *)sc, FW_XFERTIMEOUT * hz * 10);
+}
+u_int32_t fwohci_cyctimer(struct firewire_comm *fc)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ return(OREAD(sc, OHCI_CYCLETIMER));
+}
+
+#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_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch)
+{
+ int i, s;
+ int tcode, hdr_len, hdr_off, len;
+ int fsegment = -1;
+ u_int32_t off;
+#if 0
+ u_int32_t reg;
+#endif
+ struct fw_xfer *xfer;
+ struct fw_pkt *fp;
+ volatile struct fwohci_txpkthdr *ohcifp;
+ struct fwohcidb_tr *db_tr;
+ volatile struct fwohcidb *db;
+ struct mbuf *m;
+ struct tcode_info *info;
+
+ 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;
+ dbch->xferq.packets++;
+
+ fp = (struct fw_pkt *)(xfer->send.buf + xfer->send.off);
+ tcode = fp->mode.common.tcode;
+
+ ohcifp = (volatile struct fwohci_txpkthdr *) db_tr->db[1].db.immed;
+#if 0
+ switch(tcode){
+ case FWTCODE_STREAM:
+ hdr_off = 4;
+ hdr_len = 8;
+ len = ntohs(fp->mode.stream.len) + 4;
+ break;
+ case FWTCODE_RREQQ:
+ case FWTCODE_WRES:
+ hdr_off = 12;
+ hdr_len = 12;
+ len = 12;
+ break;
+ case FWTCODE_WREQQ:
+ case FWTCODE_RRESQ:
+ case FWTCODE_RREQB:
+ hdr_off = 16;
+ hdr_len = 16;
+ len = 16;
+ break;
+ case FWTCODE_PHY:
+ hdr_off = 12;
+ hdr_len = 12;
+ len = 12;
+ break;
+ default:
+ hdr_off = 16;
+ hdr_len = 16;
+ /* presume block request len */
+ len = ntohs(fp->mode.rresb.len) + 16;
+ break;
+ }
+#else
+ info = &tinfo[tcode];
+ hdr_len = hdr_off = info->hdr_len;
+ /* fw_asyreq must pass valid send.len */
+ len = xfer->send.len;
+#endif
+ for( i = 0 ; i < hdr_off ; i+= 4){
+ ohcifp->mode.ld[i/4] = ntohl(fp->mode.ld[i/4]);
+ }
+ ohcifp->mode.common.spd = xfer->spd;
+ if (tcode == FWTCODE_STREAM ){
+ hdr_len = 8;
+ ohcifp->mode.stream.len = ntohs(fp->mode.stream.len);
+ } else if (tcode == FWTCODE_PHY) {
+ hdr_len = 12;
+ ohcifp->mode.ld[1] = ntohl(fp->mode.ld[1]);
+ ohcifp->mode.ld[2] = ntohl(fp->mode.ld[2]);
+ ohcifp->mode.common.spd = 0;
+ ohcifp->mode.common.tcode = FWOHCITCODE_PHY;
+ } else {
+ ohcifp->mode.asycomm.dst = ntohs(fp->mode.hdr.dst);
+ ohcifp->mode.asycomm.srcbus = OHCI_ASYSRCBUS;
+ ohcifp->mode.asycomm.tlrt |= FWRETRY_X;
+ }
+ db = &db_tr->db[0];
+ db->db.desc.cmd = OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | hdr_len;
+ db->db.desc.status = 0;
+/* Specify bound timer of asy. responce */
+ if(&sc->atrs == dbch){
+ db->db.desc.count
+ = (OREAD(sc, OHCI_CYCLETIMER) >> 12) + (1 << 13);
+ }
+
+ db_tr->dbcnt = 2;
+ db = &db_tr->db[db_tr->dbcnt];
+ if(len > hdr_off){
+ if (xfer->mbuf == NULL) {
+ db->db.desc.addr
+ = vtophys(xfer->send.buf + xfer->send.off) + hdr_off;
+ db->db.desc.cmd
+ = OHCI_OUTPUT_MORE | ((len - hdr_off) & 0xffff);
+ db->db.desc.status = 0;
+
+ db_tr->dbcnt++;
+ } else {
+ /* XXX we assume mbuf chain is shorter than ndesc */
+ m = xfer->mbuf;
+#if 0
+ m_adj(m, hdr_off);
+#endif
+ do {
+ db->db.desc.addr
+ = vtophys(mtod(m, caddr_t));
+ db->db.desc.cmd = OHCI_OUTPUT_MORE | m->m_len;
+ db->db.desc.status = 0;
+ db++;
+ db_tr->dbcnt++;
+ m = m->m_next;
+ } while (m != NULL);
+ }
+ }
+ /* last db */
+ LAST_DB(db_tr, db);
+ db->db.desc.cmd |= OHCI_OUTPUT_LAST
+ | OHCI_INTERRUPT_ALWAYS
+ | OHCI_BRANCH_ALWAYS;
+ db->db.desc.depend = vtophys(STAILQ_NEXT(db_tr, link)->db);
+
+ if(fsegment == -1 )
+ fsegment = db_tr->dbcnt;
+ if (dbch->pdb_tr != NULL) {
+ LAST_DB(dbch->pdb_tr, db);
+ 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 {
+ printf("fwohci_start: lack of db_trq\n");
+ dbch->flags |= FWOHCI_DBCH_FULL;
+ }
+kick:
+ if (firewire_debug) printf("kick\n");
+ /* kick asy q */
+#if 0
+ if(!(OREAD(sc, OHCI_DMACTL(off)) & OHCI_CNTL_DMA_ACTIVE)
+ && fsegment != -1){
+ if (OREAD(sc, OHCI_DMACTL(off)) & OHCI_CNTL_DMA_RUN) {
+ OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE);
+ } else if (dbch->top != db_tr) {
+ /* db_tr contains next unfilled db */
+#if 0
+ printf("start DMA\n");
+ print_db(dbch->top->db, 0, 2);
+#endif
+ OWRITE(sc, OHCI_DMACMD(off),
+ vtophys(dbch->top->db) | fsegment);
+ OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN);
+ } else
+ printf("fwohci_start: nothing to kick\n");
+ }
+#else
+
+#if 1
+ if(dbch->xferq.flag & FWXFERQ_RUNNING) {
+#else
+ reg = OREAD(sc, OHCI_DMACTL(off));
+ if ((reg & OHCI_CNTL_DMA_RUN) && !(reg & OHCI_CNTL_DMA_DEAD)) {
+#endif
+ OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE);
+ } else {
+ printf("start AT DMA status=%x\n",
+ OREAD(sc, OHCI_DMACTL(off)));
+ OWRITE(sc, OHCI_DMACMD(off), vtophys(dbch->top->db) | fsegment);
+ OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN);
+ dbch->xferq.flag |= FWXFERQ_RUNNING;
+ }
+#endif
+ dbch->top = db_tr;
+ splx(s);
+ return;
+}
+static void fwohci_drain_atq(struct firewire_comm *fc, struct fw_xfer *xfer)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ fwohci_drain(&sc->fc, xfer, &(sc->atrq));
+ return;
+}
+static void fwohci_drain_ats(struct firewire_comm *fc, struct fw_xfer *xfer)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ fwohci_drain(&sc->fc, xfer, &(sc->atrs));
+ 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, err = 0;
+ struct fwohcidb_tr *tr;
+ volatile struct fwohcidb *db;
+ struct fw_xfer *xfer;
+ u_int32_t off;
+ u_int stat;
+ int packets;
+ struct firewire_comm *fc = (struct firewire_comm *)sc;
+ if(&sc->atrq == dbch){
+ off = OHCI_ATQOFF;
+ }else if(&sc->atrs == dbch){
+ off = OHCI_ATSOFF;
+ }else{
+ return;
+ }
+ s = splfw();
+ tr = dbch->bottom;
+ packets = 0;
+ while(dbch->xferq.queued > 0){
+#if 0
+ cmd = 0xfffffff0 & OREAD(sc, OHCI_DMACMD(off));
+#endif
+ LAST_DB(tr, db);
+ if(!(db->db.desc.status & OHCI_CNTL_DMA_ACTIVE)){
+ if (fc->status != FWBUSRESET)
+ /* maybe out of order?? */
+ goto out;
+ }
+#if 0
+ if(OREAD(sc, OHCI_DMACTL(off)) & OHCI_CNTL_DMA_DEAD ){
+#else
+ if(db->db.desc.status & OHCI_CNTL_DMA_DEAD) {
+#endif
+#ifdef OHCI_DEBUG
+ dump_dma(sc, ch);
+ dump_db(sc, ch);
+#endif
+/* 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 = db->db.desc.status & FWOHCIEV_MASK;
+ switch(stat){
+ case FWOHCIEV_ACKCOMPL:
+ case FWOHCIEV_ACKPEND:
+ err = 0;
+ break;
+ case FWOHCIEV_ACKBSA:
+ case FWOHCIEV_ACKBSB:
+ device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]);
+ case FWOHCIEV_ACKBSX:
+ 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;
+ xfer->state = FWXF_SENT;
+ if(err == EBUSY && fc->status != FWBUSRESET){
+ xfer->state = FWXF_BUSY;
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ xfer->resp = err;
+ if(xfer->retry_req != NULL){
+ xfer->retry_req(xfer);
+ }
+ break;
+ default:
+ break;
+ }
+ } else if( stat != FWOHCIEV_ACKPEND){
+ if (stat != FWOHCIEV_ACKCOMPL)
+ xfer->state = FWXF_SENTERR;
+ xfer->resp = err;
+ switch(xfer->act_type){
+ case FWACT_XFER:
+ fw_xfer_done(xfer);
+ break;
+ default:
+ break;
+ }
+ }
+ dbch->xferq.queued --;
+#if 0
+ } else {
+ /* already drained after timeout or getting response? */
+ printf("fwohci_txd: no xfer stat=%d\n", stat);
+#endif
+ }
+ tr->xfer = NULL;
+
+ packets ++;
+ tr = STAILQ_NEXT(tr, link);
+ dbch->bottom = tr;
+ }
+out:
+#if 0
+ if (packets < 1)
+ printf("fwohci_txd: no packets..out of order execution??\n");
+#endif
+ 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_drain(struct firewire_comm *fc, struct fw_xfer *xfer, struct fwohci_dbch *dbch)
+{
+ int i, s;
+ struct fwohcidb_tr *tr;
+
+ if(xfer->state != FWXF_START) return;
+
+ s = splfw();
+ tr = dbch->bottom;
+ for( i = 0 ; i <= dbch->xferq.queued ; i ++){
+ if(tr->xfer == xfer){
+ s = splfw();
+ tr->xfer = NULL;
+ dbch->xferq.queued --;
+#if 1
+ /* XXX */
+ if (tr == dbch->bottom)
+ dbch->bottom = STAILQ_NEXT(tr, link);
+#endif
+ if (dbch->flags & FWOHCI_DBCH_FULL) {
+ printf("fwohci_drain: make slot\n");
+ dbch->flags &= ~FWOHCI_DBCH_FULL;
+ fwohci_start((struct fwohci_softc *)fc, dbch);
+ }
+
+ splx(s);
+ break;
+ }
+ tr = STAILQ_NEXT(tr, link);
+ }
+ splx(s);
+ return;
+}
+
+static void fwohci_db_free(struct fwohci_dbch *dbch)
+{
+ struct fwohcidb_tr *db_tr;
+ int idb;
+
+ if(!(dbch->xferq.flag & FWXFERQ_EXTBUF)){
+ for(db_tr = STAILQ_FIRST(&dbch->db_trq), idb = 0;
+ idb < dbch->ndb;
+ db_tr = STAILQ_NEXT(db_tr, link), idb++){
+ free(db_tr->buf, M_DEVBUF);
+ db_tr->buf = NULL;
+ }
+ }
+ dbch->ndb = 0;
+ db_tr = STAILQ_FIRST(&dbch->db_trq);
+ contigfree((void *)(uintptr_t)(volatile void *)db_tr->db,
+ sizeof(struct fwohcidb) * dbch->ndesc * dbch->ndb, M_DEVBUF);
+ /* Attach DB to DMA ch. */
+ free(db_tr, M_DEVBUF);
+ STAILQ_INIT(&dbch->db_trq);
+}
+static void fwohci_db_init(struct fwohci_dbch *dbch)
+{
+ int idb;
+ struct fwohcidb *db;
+ struct fwohcidb_tr *db_tr;
+ /* allocate DB entries and attach one to each DMA channels */
+ /* DB entry must start at 16 bytes bounary. */
+ dbch->frag.buf = NULL;
+ dbch->frag.len = 0;
+ dbch->frag.plen = 0;
+ dbch->xferq.queued = 0;
+ dbch->pdb_tr = NULL;
+
+ STAILQ_INIT(&dbch->db_trq);
+ db_tr = (struct fwohcidb_tr *)
+ malloc(sizeof(struct fwohcidb_tr) * dbch->ndb,
+ M_DEVBUF, M_DONTWAIT);
+ if(db_tr == NULL){
+ return;
+ }
+ db = (struct fwohcidb *)
+ contigmalloc(sizeof (struct fwohcidb) * dbch->ndesc * dbch->ndb,
+ M_DEVBUF, M_DONTWAIT, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
+ if(db == NULL){
+ printf("fwochi_db_init: contigmalloc failed\n");
+ return;
+ }
+ bzero(db, sizeof (struct fwohcidb) * dbch->ndesc * dbch->ndb);
+ /* Attach DB to DMA ch. */
+ for(idb = 0 ; idb < dbch->ndb ; idb++){
+ db_tr->dbcnt = 0;
+ db_tr->db = &db[idb * dbch->ndesc];
+ STAILQ_INSERT_TAIL(&dbch->db_trq, db_tr, link);
+ if(!(dbch->xferq.flag & FWXFERQ_PACKET) &&
+ (idb % dbch->xferq.bnpacket == 0)){
+ dbch->xferq.bulkxfer[idb/dbch->xferq.bnpacket].start
+ = (caddr_t)db_tr;
+ }
+ if((!(dbch->xferq.flag & FWXFERQ_PACKET)) &&
+ ((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);
+ dbch->top = STAILQ_FIRST(&dbch->db_trq);
+ dbch->bottom = dbch->top;
+}
+static int fwohci_itx_disable(struct firewire_comm *fc, int dmach)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
+ OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach);
+ OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach);
+ 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;
+
+ OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
+ OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach);
+ OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach);
+ if(sc->ir[dmach].dummy != NULL){
+ free(sc->ir[dmach].dummy, M_DEVBUF);
+ }
+ sc->ir[dmach].dummy = NULL;
+ fwohci_db_free(&sc->ir[dmach]);
+ sc->ir[dmach].xferq.flag &= ~FWXFERQ_RUNNING;
+ return 0;
+}
+static void fwohci_irx_post (struct firewire_comm *fc , u_int32_t *qld)
+{
+ qld[0] = ntohl(qld[0]);
+ return;
+}
+static int fwohci_irxpp_enable(struct firewire_comm *fc, int dmach)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ int err = 0;
+ unsigned short tag, ich;
+
+ tag = (sc->ir[dmach].xferq.flag >> 6) & 3;
+ ich = sc->ir[dmach].xferq.flag & 0x3f;
+
+#if 0
+ if(STAILQ_FIRST(&fc->ir[dmach]->q) != NULL){
+ wakeup(fc->ir[dmach]);
+ return err;
+ }
+#endif
+
+ OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich);
+ if(!(sc->ir[dmach].xferq.flag & FWXFERQ_RUNNING)){
+ sc->ir[dmach].xferq.queued = 0;
+ sc->ir[dmach].ndb = NDB;
+ sc->ir[dmach].xferq.psize = FWPMAX_S400;
+ sc->ir[dmach].ndesc = 1;
+ fwohci_db_init(&sc->ir[dmach]);
+ err = fwohci_rx_enable(sc, &sc->ir[dmach]);
+ }
+ if(err){
+ device_printf(sc->fc.dev, "err in IRX setting\n");
+ return err;
+ }
+ if(!(OREAD(sc, OHCI_IRCTL(dmach)) & OHCI_CNTL_DMA_ACTIVE)){
+ OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
+ 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), 0xf8000000);
+ OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_ISOHDR);
+ OWRITE(sc, OHCI_IRCMD(dmach),
+ vtophys(sc->ir[dmach].top->db) | 1);
+ OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN);
+ OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR);
+ }
+ return err;
+}
+static int fwohci_tx_enable(struct fwohci_softc *sc,
+ struct fwohci_dbch *dbch)
+{
+ int err = 0;
+ int idb, z, i, dmach = 0;
+ u_int32_t off = NULL;
+ struct fwohcidb_tr *db_tr;
+
+ 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(db_tr,
+ dbch->xferq.psize, dbch->xferq.flag,
+ dbch->xferq.buf + dbch->xferq.psize * idb);
+ if(STAILQ_NEXT(db_tr, link) == NULL){
+ break;
+ }
+ db_tr->db[0].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | z;
+ db_tr->db[db_tr->dbcnt - 1].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | z;
+ if(dbch->xferq.flag & FWXFERQ_EXTBUF){
+ if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){
+ db_tr->db[db_tr->dbcnt - 1].db.desc.cmd
+ |= OHCI_INTERRUPT_ALWAYS;
+ db_tr->db[0].db.desc.depend &= ~0xf;
+ db_tr->db[db_tr->dbcnt - 1].db.desc.depend &=
+ ~0xf;
+ }
+ }
+ db_tr = STAILQ_NEXT(db_tr, link);
+ }
+ dbch->bottom->db[db_tr->dbcnt - 1].db.desc.depend &= 0xfffffff0;
+ return err;
+}
+static int fwohci_rx_enable(struct fwohci_softc *sc,
+ struct fwohci_dbch *dbch)
+{
+ int err = 0;
+ int idb, z, i, dmach = 0;
+ u_int32_t off = NULL;
+ struct fwohcidb_tr *db_tr;
+
+ 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;
+ 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 ++){
+ if(!(dbch->xferq.flag & FWXFERQ_EXTBUF)){
+ fwohci_add_rx_buf(db_tr,
+ dbch->xferq.psize, dbch->xferq.flag, 0, NULL);
+ }else{
+ fwohci_add_rx_buf(db_tr,
+ dbch->xferq.psize, dbch->xferq.flag,
+ dbch->xferq.buf + dbch->xferq.psize * idb,
+ dbch->dummy + sizeof(u_int32_t) * idb);
+ }
+ if(STAILQ_NEXT(db_tr, link) == NULL){
+ break;
+ }
+ db_tr->db[db_tr->dbcnt - 1].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | z;
+ if(dbch->xferq.flag & FWXFERQ_EXTBUF){
+ if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){
+ db_tr->db[db_tr->dbcnt - 1].db.desc.cmd
+ |= OHCI_INTERRUPT_ALWAYS;
+ db_tr->db[db_tr->dbcnt - 1].db.desc.depend &=
+ ~0xf;
+ }
+ }
+ db_tr = STAILQ_NEXT(db_tr, link);
+ }
+ dbch->bottom->db[db_tr->dbcnt - 1].db.desc.depend &= 0xfffffff0;
+ dbch->buf_offset = 0;
+ if(dbch->xferq.flag & FWXFERQ_STREAM){
+ return err;
+ }else{
+ OWRITE(sc, OHCI_DMACMD(off), vtophys(dbch->top->db) | z);
+ }
+ OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN);
+ return err;
+}
+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;
+ struct fw_pkt *fp;
+ struct fwohcidb_tr *db_tr;
+
+ tag = (sc->it[dmach].xferq.flag >> 6) & 3;
+ ich = sc->it[dmach].xferq.flag & 0x3f;
+ dbch = &sc->it[dmach];
+ if(dbch->ndb == 0){
+ dbch->xferq.queued = 0;
+ dbch->ndb = dbch->xferq.bnpacket * dbch->xferq.bnchunk;
+ dbch->ndesc = 3;
+ fwohci_db_init(dbch);
+ err = fwohci_tx_enable(sc, dbch);
+ }
+ if(err)
+ return err;
+ if(OREAD(sc, OHCI_ITCTL(dmach)) & OHCI_CNTL_DMA_ACTIVE){
+ if(dbch->xferq.stdma2 != NULL){
+ fwohci_txbufdb(sc, dmach, dbch->xferq.stdma2);
+ ((struct fwohcidb_tr *)
+ (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.cmd
+ |= OHCI_BRANCH_ALWAYS;
+ ((struct fwohcidb_tr *)
+ (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
+ ((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[0].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
+ ((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
+ ((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf;
+ }
+ }else if(!(OREAD(sc, OHCI_ITCTL(dmach)) & OHCI_CNTL_DMA_ACTIVE)){
+ fw_tbuf_update(&sc->fc, dmach, 0);
+ if(dbch->xferq.stdma == NULL){
+ return err;
+ }
+ OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
+ OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach);
+ OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach);
+ OWRITE(sc, OHCI_IT_MASK, 1 << dmach);
+ OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xf0000000);
+ fwohci_txbufdb(sc, dmach, dbch->xferq.stdma);
+ if(dbch->xferq.stdma2 != NULL){
+ fwohci_txbufdb(sc, dmach, dbch->xferq.stdma2);
+ ((struct fwohcidb_tr *)
+ (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.cmd
+ |= OHCI_BRANCH_ALWAYS;
+ ((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
+ ((struct fwohcidb_tr *)(dbch->xferq.stdma->end))->db[0].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(dbch->xferq.stdma2->start))->db) | dbch->ndesc;
+ ((struct fwohcidb_tr *)(dbch->xferq.stdma2->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
+ ((struct fwohcidb_tr *) (dbch->xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf;
+ }else{
+ ((struct fwohcidb_tr *) (dbch->xferq.stdma->end))->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
+ ((struct fwohcidb_tr *) (dbch->xferq.stdma->end))->db[0].db.desc.depend &= ~0xf;
+ }
+ OWRITE(sc, OHCI_ITCMD(dmach),
+ vtophys(((struct fwohcidb_tr *)
+ (dbch->xferq.stdma->start))->db) | dbch->ndesc);
+ if(dbch->xferq.flag & FWXFERQ_DV){
+ db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma->start;
+ fp = (struct fw_pkt *)db_tr->buf;
+ fp->mode.ld[2] = htonl(0x80000000 +
+ ((fc->cyctimer(fc) + 0x3000) & 0xf000));
+ }
+
+ OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN);
+ OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT);
+ }
+ return err;
+}
+static int fwohci_irxbuf_enable(struct firewire_comm *fc, int dmach)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ int err = 0;
+ unsigned short tag, ich;
+ tag = (sc->ir[dmach].xferq.flag >> 6) & 3;
+ ich = sc->ir[dmach].xferq.flag & 0x3f;
+ OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich);
+
+ if(!(sc->ir[dmach].xferq.flag & FWXFERQ_RUNNING)){
+ sc->ir[dmach].xferq.queued = 0;
+ sc->ir[dmach].ndb = sc->ir[dmach].xferq.bnpacket *
+ sc->ir[dmach].xferq.bnchunk;
+ sc->ir[dmach].dummy =
+ malloc(sizeof(u_int32_t) * sc->ir[dmach].ndb,
+ M_DEVBUF, M_DONTWAIT);
+ if(sc->ir[dmach].dummy == NULL){
+ err = ENOMEM;
+ return err;
+ }
+ sc->ir[dmach].ndesc = 2;
+ fwohci_db_init(&sc->ir[dmach]);
+ err = fwohci_rx_enable(sc, &sc->ir[dmach]);
+ }
+ if(err)
+ return err;
+
+ if(OREAD(sc, OHCI_IRCTL(dmach)) & OHCI_CNTL_DMA_ACTIVE){
+ if(sc->ir[dmach].xferq.stdma2 != NULL){
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db) | sc->ir[dmach].ndesc;
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db);
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf;
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[0].db.desc.depend &= ~0xf;
+ }
+ }else if(!(OREAD(sc, OHCI_IRCTL(dmach)) & OHCI_CNTL_DMA_ACTIVE)
+ && !(sc->ir[dmach].xferq.flag & FWXFERQ_PACKET)){
+ fw_rbuf_update(&sc->fc, dmach, 0);
+
+ OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN);
+ 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);
+ if(sc->ir[dmach].xferq.stdma2 != NULL){
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db) | sc->ir[dmach].ndesc;
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend =
+ vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->start))->db);
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma2->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf;
+ }else{
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[sc->ir[dmach].ndesc - 1].db.desc.depend &= ~0xf;
+ ((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->end))->db[0].db.desc.depend &= ~0xf;
+ }
+ OWRITE(sc, OHCI_IRCMD(dmach),
+ vtophys(((struct fwohcidb_tr *)(sc->ir[dmach].xferq.stdma->start))->db) | sc->ir[dmach].ndesc);
+ OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN);
+ }
+ OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR);
+ return err;
+}
+static int fwohci_irx_enable(struct firewire_comm *fc, int dmach)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)fc;
+ int err = 0;
+
+ if(sc->ir[dmach].xferq.flag & FWXFERQ_PACKET){
+ err = fwohci_irxpp_enable(fc, dmach);
+ return err;
+ }else{
+ err = fwohci_irxbuf_enable(fc, dmach);
+ return err;
+ }
+}
+int fwohci_shutdown(device_t dev)
+{
+ u_int i;
+ struct fwohci_softc *sc = device_get_softc(dev);
+
+/* 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);
+ return 0;
+}
+
+#define ACK_ALL
+static void
+fwohci_intr_body(struct fwohci_softc *sc, u_int32_t stat)
+{
+ u_int32_t irstat, itstat;
+ u_int i;
+ struct firewire_comm *fc = (struct firewire_comm *)sc;
+
+#define OHCI_DEBUG
+#undef OHCI_DEBUG
+#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 ){
+ 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;
+
+#if 0
+ for( i = 0 ; i < fc->nisodma ; i ++ ){
+ OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN);
+ OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN);
+ }
+
+#endif
+ fw_busreset(fc);
+
+ /* XXX need to wait DMA to stop */
+#ifndef ACK_ALL
+ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_BUS_R);
+#endif
+#if 1
+ /* pending all pre-bus_reset packets */
+ fwohci_txd(sc, &sc->atrq);
+ fwohci_txd(sc, &sc->atrs);
+ fwohci_arcv(sc, &sc->arrs);
+ fwohci_arcv(sc, &sc->arrq);
+#endif
+
+
+ OWRITE(sc, OHCI_AREQHI, 1 << 31);
+ /* XXX insecure ?? */
+ OWRITE(sc, OHCI_PREQHI, 0x7fffffff);
+ OWRITE(sc, OHCI_PREQLO, 0xffffffff);
+ OWRITE(sc, OHCI_PREQUPPER, 0x10000);
+#if 0
+ OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_LINKEN);
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS);
+ OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN);
+#endif
+
+ }
+ if((stat & OHCI_INT_DMA_IR )){
+#ifndef ACK_ALL
+ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_IR);
+#endif
+#if 0
+ irstat = OREAD(sc, OHCI_IR_STAT) & OREAD(sc, OHCI_IR_MASK);
+#else
+ irstat = OREAD(sc, OHCI_IR_STAT);
+#endif
+ OWRITE(sc, OHCI_IR_STATCLR, ~0);
+ for(i = 0; i < fc->nisodma ; i++){
+ if((irstat & (1 << i)) != 0){
+ if(sc->ir[i].xferq.flag & FWXFERQ_PACKET){
+ fwohci_ircv(sc, &sc->ir[i]);
+ }else{
+ fwohci_rbuf_update(sc, i);
+ }
+ }
+ }
+ }
+ if((stat & OHCI_INT_DMA_IT )){
+#ifndef ACK_ALL
+ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_DMA_IT);
+#endif
+#if 0
+ itstat = OREAD(sc, OHCI_IT_STAT) & OREAD(sc, OHCI_IT_MASK);
+#else
+ itstat = OREAD(sc, OHCI_IT_STAT);
+#endif
+ OWRITE(sc, OHCI_IT_STATCLR, ~0);
+ 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);
+ }
+ 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);
+ }
+ if(stat & OHCI_INT_PHY_SID){
+ caddr_t buf;
+ int plen;
+
+#ifndef ACK_ALL
+ OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_SID);
+#endif
+/*
+** Checking whether the node is root or not. If root, turn on
+** cycle master.
+*/
+#if 0
+ OWRITE(sc, FWOHCI_NODEID, (OREAD(sc, FWOHCI_NODEID)) & 0xffff003f);
+#endif
+ device_printf(fc->dev, "node_id = 0x%08x, ", OREAD(sc, FWOHCI_NODEID));
+ if(!(OREAD(sc, FWOHCI_NODEID) & OHCI_NODE_VALID)){
+ printf("Bus reset failure\n");
+#if 0
+ fwohci_ibr(sc);
+#endif
+ goto sidout;
+ }
+ if( OREAD(sc, FWOHCI_NODEID) & 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 = OREAD(sc, FWOHCI_NODEID) & 0x3f;
+
+ plen = OREAD(sc, OHCI_SID_CNT) & OHCI_SID_CNT_MASK;
+ plen -= 4; /* chop control info */
+ buf = malloc( FWPMAX_S400, M_DEVBUF, M_NOWAIT);
+ if(buf == NULL) goto sidout;
+ bcopy((void *)(uintptr_t)(volatile void *)fc->sid_buf,
+ buf, plen);
+ fw_sidrcv(fc, buf, plen, 0);
+ }
+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;
+}
+
+void
+fwohci_intr(void *arg)
+{
+ struct fwohci_softc *sc = (struct fwohci_softc *)arg;
+ u_int32_t stat;
+
+ if (!(sc->intmask & OHCI_INT_EN)) {
+ /* polling mode */
+ return;
+ }
+
+ while ((stat = OREAD(sc, FWOHCI_INTSTAT)) != 0) {
+ if (stat == 0xffffffff) {
+ device_printf(sc->fc.dev,
+ "device physically ejected?\n");
+ return;
+ }
+#ifdef ACK_ALL
+ OWRITE(sc, FWOHCI_INTSTATCLR, stat);
+#endif
+ fwohci_intr_body(sc, stat);
+ }
+}
+
+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 = OREAD(sc, FWOHCI_INTSTAT);
+ if (stat == 0)
+ return;
+ if (stat == 0xffffffff) {
+ device_printf(sc->fc.dev,
+ "device physically ejected?\n");
+ return;
+ }
+#ifdef ACK_ALL
+ OWRITE(sc, FWOHCI_INTSTATCLR, stat);
+#endif
+ }
+ s = splfw();
+ fwohci_intr_body(sc, stat);
+ splx(s);
+}
+
+static void
+fwohci_set_intr(struct firewire_comm *fc, int enable)
+{
+ struct fwohci_softc *sc;
+
+ sc = (struct fwohci_softc *)fc;
+ printf("fwochi_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)
+{
+ int stat;
+ struct firewire_comm *fc = &sc->fc;
+ struct fw_pkt *fp;
+ struct fwohci_dbch *dbch;
+ struct fwohcidb_tr *db_tr;
+
+ dbch = &sc->it[dmach];
+ if((dbch->xferq.flag & FWXFERQ_DV) && (dbch->xferq.stdma2 != NULL)){
+ db_tr = (struct fwohcidb_tr *)dbch->xferq.stdma2->start;
+/*
+ * Overwrite highest significant 4 bits timestamp information
+ */
+ fp = (struct fw_pkt *)db_tr->buf;
+ fp->mode.ld[2] |= htonl(0x80000000 |
+ ((fc->cyctimer(fc) + 0x4000) & 0xf000));
+ }
+ stat = OREAD(sc, OHCI_ITCTL(dmach)) & 0x1f;
+ switch(stat){
+ case FWOHCIEV_ACKCOMPL:
+ fw_tbuf_update(fc, dmach, 1);
+ break;
+ default:
+ fw_tbuf_update(fc, dmach, 0);
+ break;
+ }
+ fwohci_itxbuf_enable(&sc->fc, dmach);
+}
+static void fwohci_rbuf_update(struct fwohci_softc *sc, int dmach)
+{
+ int stat;
+ stat = OREAD(sc, OHCI_IRCTL(dmach)) & 0x1f;
+ switch(stat){
+ case FWOHCIEV_ACKCOMPL:
+ fw_rbuf_update(&sc->fc, dmach, 1);
+ wakeup(sc->fc.ir[dmach]);
+ fwohci_irx_enable(&sc->fc, dmach);
+ break;
+ default:
+ break;
+ }
+}
+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, "dma ch %1x:dma regs 0x%08x 0x%08x 0x%08x 0x%08x \n",
+ ch,
+ cntl,
+ stat,
+ cmd,
+ match);
+ stat &= 0xffff ;
+ if(stat & 0xff00){
+ 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;
+ 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);
+ if(cp == NULL) break;
+ for(jdb = 0 ; jdb < dbch->ndesc ; jdb ++ ){
+ if((cmd & 0xfffffff0)
+ == vtophys(&(cp->db[jdb]))){
+ 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){
+ printf("Prev DB %d\n", ch);
+ print_db(prev, ch, dbch->ndesc);
+ printf("Current DB %d\n", ch);
+ print_db(curr, ch, dbch->ndesc);
+ printf("Next DB %d\n", ch);
+ print_db(next, ch, dbch->ndesc);
+ }else{
+ printf("dbdump err ch = %d cmd = 0x%08x\n", ch, cmd);
+ }
+ return;
+}
+void print_db(volatile struct fwohcidb *db, u_int32_t ch, u_int32_t max){
+ fwohcireg_t stat;
+ int i, key;
+
+ 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 ++){
+ key = db[i].db.desc.cmd & OHCI_KEY_MASK;
+ printf("%08x %s %s %s %s %5d %08x %08x %04x:%04x",
+ vtophys(&db[i]),
+ dbcode[(db[i].db.desc.cmd >> 28) & 0xf],
+ dbkey[(db[i].db.desc.cmd >> 24) & 0x7],
+ dbcond[(db[i].db.desc.cmd >> 20) & 0x3],
+ dbcond[(db[i].db.desc.cmd >> 18) & 0x3],
+ db[i].db.desc.cmd & 0xffff,
+ db[i].db.desc.addr,
+ db[i].db.desc.depend,
+ db[i].db.desc.status,
+ db[i].db.desc.count);
+ stat = db[i].db.desc.status;
+ 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",
+ db[i+1].db.immed[0],
+ db[i+1].db.immed[1],
+ db[i+1].db.immed[2],
+ db[i+1].db.immed[3]);
+ }
+ if(key == OHCI_KEY_DEVICE){
+ return;
+ }
+ if((db[i].db.desc.cmd & OHCI_BRANCH_MASK)
+ == OHCI_BRANCH_ALWAYS){
+ return;
+ }
+ if((db[i].db.desc.cmd & OHCI_CMD_MASK)
+ == OHCI_OUTPUT_LAST){
+ return;
+ }
+ if((db[i].db.desc.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;
+
+ sc = (struct fwohci_softc *)fc;
+#if 1
+ fun = fwphy_rddata(sc, FW_PHY_IBR_REG);
+ fun |= FW_PHY_IBR;
+ fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun);
+#else
+ fun = fwphy_rddata(sc, FW_PHY_ISBR_REG);
+ fun |= FW_PHY_ISBR;
+ 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;
+ 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, vtophys(db_tr->db), vtophys(fdb_tr->db));
+*/
+ if(bulkxfer->flag != 0){
+ return;
+ }
+ bulkxfer->flag = 1;
+ for( idb = 0 ; idb < bulkxfer->npacket ; idb ++){
+ db_tr->db[0].db.desc.cmd
+ = OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8;
+ fp = (struct fw_pkt *)db_tr->buf;
+ ohcifp = (volatile struct fwohci_txpkthdr *)
+ db_tr->db[1].db.immed;
+ ohcifp->mode.ld[0] = ntohl(fp->mode.ld[0]);
+ ohcifp->mode.stream.len = ntohs(fp->mode.stream.len);
+ ohcifp->mode.stream.chtag = chtag;
+ ohcifp->mode.stream.tcode = 0xa;
+ ohcifp->mode.stream.spd = 4;
+ ohcifp->mode.ld[2] = ntohl(fp->mode.ld[1]);
+ ohcifp->mode.ld[3] = ntohl(fp->mode.ld[2]);
+
+ db_tr->db[2].db.desc.cmd
+ = OHCI_OUTPUT_LAST
+ | OHCI_UPDATE
+ | OHCI_BRANCH_ALWAYS
+ | ((ntohs(fp->mode.stream.len) ) & 0xffff);
+ db_tr->db[2].db.desc.status = 0;
+ db_tr->db[2].db.desc.count = 0;
+ if(dbch->xferq.flag & FWXFERQ_DV){
+ db_tr->db[0].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | dbch->ndesc;
+ db_tr->db[dbch->ndesc - 1].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | dbch->ndesc;
+ }else{
+ db_tr->db[0].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | dbch->ndesc;
+ db_tr->db[dbch->ndesc - 1].db.desc.depend
+ = vtophys(STAILQ_NEXT(db_tr, link)->db) | dbch->ndesc;
+ }
+ bulkxfer->end = (caddr_t)db_tr;
+ db_tr = STAILQ_NEXT(db_tr, link);
+ }
+ db_tr = (struct fwohcidb_tr *)bulkxfer->end;
+ db_tr->db[0].db.desc.depend &= ~0xf;
+ db_tr->db[dbch->ndesc - 1].db.desc.depend &= ~0xf;
+/**/
+ db_tr->db[dbch->ndesc - 1].db.desc.cmd &= ~OHCI_BRANCH_ALWAYS;
+ db_tr->db[dbch->ndesc - 1].db.desc.cmd |= OHCI_BRANCH_NEVER;
+/**/
+ db_tr->db[dbch->ndesc - 1].db.desc.cmd |= OHCI_INTERRUPT_ALWAYS;
+
+ 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, vtophys(db_tr->db), vtophys(fdb_tr->db));
+*/
+ return;
+}
+static int fwohci_add_tx_buf(struct fwohcidb_tr *db_tr, unsigned short size, int mode, void *buf)
+{
+ volatile struct fwohcidb *db = db_tr->db;
+ int err = 0;
+ if(buf == 0){
+ err = EINVAL;
+ return err;
+ }
+ db_tr->buf = buf;
+ db_tr->dbcnt = 3;
+ db_tr->dummy = NULL;
+
+ db[0].db.desc.cmd = OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8;
+
+ db[2].db.desc.depend = 0;
+ db[2].db.desc.addr = vtophys(buf) + sizeof(u_int32_t);
+ db[2].db.desc.cmd = OHCI_OUTPUT_MORE;
+
+ db[0].db.desc.status = 0;
+ db[0].db.desc.count = 0;
+
+ db[2].db.desc.status = 0;
+ db[2].db.desc.count = 0;
+ if( mode & FWXFERQ_STREAM ){
+ db[2].db.desc.cmd |= OHCI_OUTPUT_LAST;
+ if(mode & FWXFERQ_PACKET ){
+ db[2].db.desc.cmd
+ |= OHCI_INTERRUPT_ALWAYS;
+ }
+ }
+ db[2].db.desc.cmd |= OHCI_BRANCH_ALWAYS;
+ return 1;
+}
+int fwohci_add_rx_buf(db_tr, size, mode, buf, dummy)
+struct fwohcidb_tr *db_tr;
+unsigned short size;
+int mode;
+void *buf, *dummy;
+{
+ volatile struct fwohcidb *db = db_tr->db;
+ int i;
+ void *dbuf[2];
+ int dsiz[2];
+
+ if(buf == 0){
+ buf = malloc(size, M_DEVBUF, M_NOWAIT);
+ if(buf == NULL) return 0;
+ db_tr->buf = buf;
+ db_tr->dbcnt = 1;
+ db_tr->dummy = NULL;
+ dsiz[0] = size;
+ dbuf[0] = buf;
+ }else if(dummy == NULL){
+ db_tr->buf = buf;
+ db_tr->dbcnt = 1;
+ db_tr->dummy = NULL;
+ dsiz[0] = size;
+ dbuf[0] = buf;
+ }else{
+ db_tr->buf = buf;
+ db_tr->dbcnt = 2;
+ db_tr->dummy = dummy;
+ dsiz[0] = sizeof(u_int32_t);
+ dsiz[1] = size;
+ dbuf[0] = dummy;
+ dbuf[1] = buf;
+ }
+ for(i = 0 ; i < db_tr->dbcnt ; i++){
+#if 0
+ db[i].db.desc.depend = 0;
+#endif
+ db[i].db.desc.addr = vtophys(dbuf[i]) ;
+ db[i].db.desc.cmd = OHCI_INPUT_MORE | dsiz[i];
+ if( mode & FWXFERQ_STREAM ){
+ db[i].db.desc.cmd |= OHCI_UPDATE;
+ }
+ db[i].db.desc.status = 0;
+ db[i].db.desc.count = dsiz[i];
+ }
+ if( mode & FWXFERQ_STREAM ){
+ db[db_tr->dbcnt - 1].db.desc.cmd |= OHCI_INPUT_LAST;
+ if(mode & FWXFERQ_PACKET ){
+ db[db_tr->dbcnt - 1].db.desc.cmd
+ |= OHCI_INTERRUPT_ALWAYS;
+ }
+ }
+ db[db_tr->dbcnt - 1].db.desc.cmd |= OHCI_BRANCH_ALWAYS;
+ return 1;
+}
+#if 0
+/* BUS parameter initialization after BUS reset */
+void fwohci_busreset(sc)
+struct fwohci_softc *sc;
+{
+}
+#endif
+static void fwohci_ircv(sc, dbch)
+struct fwohci_softc *sc;
+struct fwohci_dbch *dbch;
+{
+ struct fwohcidb_tr *db_tr = dbch->top, *odb_tr;
+ struct firewire_comm *fc = (struct firewire_comm *)sc;
+ int z = 1;
+ struct fw_pkt *fp;
+ u_int8_t *ld;
+ u_int32_t off = NULL;
+ u_int32_t stat;
+ u_int32_t *qld;
+ u_int32_t reg;
+ u_int spd;
+ u_int dmach;
+ int len, i, plen;
+ caddr_t buf;
+
+ for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){
+ if( &sc->ir[dmach] == dbch){
+ off = OHCI_IROFF(dmach);
+ break;
+ }
+ }
+ if(off == NULL){
+ return;
+ }
+ if(!(dbch->xferq.flag & FWXFERQ_RUNNING)){
+ fwohci_irx_disable(&sc->fc, dmach);
+ return;
+ }
+
+ odb_tr = NULL;
+ db_tr = dbch->top;
+ i = 0;
+ while ((reg = db_tr->db[0].db.desc.status) & 0x1f) {
+ ld = (u_int8_t *)db_tr->buf;
+ if (dbch->xferq.flag & FWXFERQ_PACKET) {
+ /* skip timeStamp */
+ ld += sizeof(struct fwohci_trailer);
+ }
+ qld = (u_int32_t *)ld;
+ len = dbch->xferq.psize - (db_tr->db[0].db.desc.count);
+/*
+{
+device_printf(sc->fc.dev, "%04x %2x 0x%08x 0x%08x 0x%08x 0x%08x\n", len,
+ db_tr->db[0].db.desc.status & 0x1f, qld[0],qld[1],qld[2],qld[3]);
+}
+*/
+#if 0
+ fp=(struct fw_pkt *)(ld + sizeof(struct fwohci_trailer));
+#else
+ fp=(struct fw_pkt *)ld;
+#endif
+ qld[0] = htonl(qld[0]);
+ plen = sizeof(struct fw_isohdr)
+ + ntohs(fp->mode.stream.len) + sizeof(u_int32_t);
+ ld += plen;
+ len -= plen;
+ buf = db_tr->buf;
+ db_tr->buf = NULL;
+ stat = reg & 0x1f;
+ spd = reg & 0x3;
+ switch(stat){
+ case FWOHCIEV_ACKCOMPL:
+ case FWOHCIEV_ACKPEND:
+ fw_rcv(&sc->fc, buf, plen - sizeof(u_int32_t), dmach, sizeof(u_int32_t), spd);
+ break;
+ default:
+ free(buf, M_DEVBUF);
+ device_printf(sc->fc.dev, "Isochronous receive err %02x\n", stat);
+ break;
+ }
+ i++;
+ fwohci_add_rx_buf(db_tr, dbch->xferq.psize,
+ dbch->xferq.flag, 0, NULL);
+ db_tr->db[0].db.desc.depend &= ~0xf;
+ if(dbch->pdb_tr != NULL){
+ dbch->pdb_tr->db[0].db.desc.depend |= z;
+ } else {
+ /* XXX should be rewritten in better way */
+ dbch->bottom->db[0].db.desc.depend |= z;
+ }
+ dbch->pdb_tr = db_tr;
+ db_tr = STAILQ_NEXT(db_tr, link);
+#if 0
+ if (!(reg & OHCI_CNTL_DMA_RUN) ||
+ !(reg & OHCI_CNTL_DMA_ACTIVE) ||
+ (reg & OHCI_CNTL_DMA_DEAD)) {
+ printf("reg = %x\n", reg);
+ }
+#endif
+ }
+ dbch->top = db_tr;
+ reg = OREAD(sc, OHCI_DMACTL(off));
+ if (reg & OHCI_CNTL_DMA_ACTIVE)
+ return;
+ device_printf(sc->fc.dev, "IR DMA %d stopped at %x status=%x (%d)\n",
+ dmach, OREAD(sc, OHCI_DMACMD(off)), reg, i);
+ dbch->top = db_tr;
+ fwohci_irx_enable(fc, dmach);
+}
+
+#define PLEN(x) (((ntohs(x))+0x3) & ~0x3)
+static int
+fwohci_get_plen(struct fwohci_softc *sc, struct fw_pkt *fp, int hlen)
+{
+ int i;
+
+ for( i = 4; i < hlen ; i+=4){
+ fp->mode.ld[i/4] = htonl(fp->mode.ld[i/4]);
+ }
+
+ switch(fp->mode.common.tcode){
+ case FWTCODE_RREQQ:
+ return sizeof(fp->mode.rreqq) + sizeof(u_int32_t);
+ case FWTCODE_WRES:
+ return sizeof(fp->mode.wres) + sizeof(u_int32_t);
+ case FWTCODE_WREQQ:
+ return sizeof(fp->mode.wreqq) + sizeof(u_int32_t);
+ case FWTCODE_RREQB:
+ return sizeof(fp->mode.rreqb) + sizeof(u_int32_t);
+ case FWTCODE_RRESQ:
+ return sizeof(fp->mode.rresq) + sizeof(u_int32_t);
+ case FWTCODE_WREQB:
+ return sizeof(struct fw_asyhdr) + PLEN(fp->mode.wreqb.len)
+ + sizeof(u_int32_t);
+ case FWTCODE_LREQ:
+ return sizeof(struct fw_asyhdr) + PLEN(fp->mode.lreq.len)
+ + sizeof(u_int32_t);
+ case FWTCODE_RRESB:
+ return sizeof(struct fw_asyhdr) + PLEN(fp->mode.rresb.len)
+ + sizeof(u_int32_t);
+ case FWTCODE_LRES:
+ return sizeof(struct fw_asyhdr) + PLEN(fp->mode.lres.len)
+ + sizeof(u_int32_t);
+ case FWOHCITCODE_PHY:
+ return 16;
+ }
+ device_printf(sc->fc.dev, "Unknown tcode %d\n", fp->mode.common.tcode);
+ return 0;
+}
+
+static void fwohci_arcv(sc, dbch)
+struct fwohci_softc *sc;
+struct fwohci_dbch *dbch;
+{
+ struct fwohcidb_tr *db_tr;
+ int z = 1;
+ struct fw_pkt *fp;
+ u_int8_t *ld;
+ u_int32_t stat, off;
+#if 0
+ u_int32_t *qld;
+ u_int32_t dbcmd;
+ int itr, i;
+#endif
+ u_int spd;
+ int len, plen, hlen, pcnt, poff = 0, rlen;
+ 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();
+#if 0
+ OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN);
+ dbcmd = OREAD(sc, OHCI_DMACMD(off)) & ~0xf;
+
+/*
+{
+db_tr = dbch->top;
+ld = (u_int8_t *)db_tr->buf;
+qld = (u_int32_t *)ld;
+len = dbch->xferq.psize - (db_tr->db[0].db.desc.count);
+device_printf(sc->fc.dev, "%08x %04x %2x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", ld, len,
+ db_tr->db[0].db.desc.status & 0x1f, qld[0],qld[1],qld[2],qld[3], dbcmd, vtophys(db_tr->db));
+}
+*/
+ for( db_tr = dbch->top, itr = 1;
+ dbcmd != vtophys(db_tr->db); itr++){
+ db_tr = STAILQ_NEXT(db_tr, link);
+ if( itr >= dbch->ndb ) break;
+/*
+if(itr != 1){
+ld = (u_int8_t *)db_tr->buf;
+qld = (u_int32_t *)ld;
+len = dbch->xferq.psize - (db_tr->db[0].db.desc.count);
+device_printf(sc->fc.dev, "%04x %2x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", len,
+ db_tr->db[0].db.desc.status & 0x1f, qld[0],qld[1],qld[2],qld[3], dbcmd, vtophys(db_tr->db));
+}
+*/
+ }
+/* OHCI does not support per packet receive mode in Aync receive. */
+ if( dbcmd != vtophys(db_tr->db)){
+ if(&sc->arrq == dbch){
+ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_DMA_PRRQ);
+ }else if(&sc->arrs == dbch){
+ OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_DMA_PRRS);
+ }
+ OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN);
+ splx(s);
+ return;
+ }else{
+ db_tr = STAILQ_NEXT(db_tr, link);
+ dbch->top = db_tr;
+ OWRITE(sc, OHCI_DMACMD(off),vtophys(dbch->top->db) | 1);
+ OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN);
+ }
+
+ db_tr = dbch->bottom;
+ while(itr > 0){
+ db_tr->db[0].db.desc.depend |= z;
+ db_tr = STAILQ_NEXT(db_tr, link);
+ ld = (u_int8_t *)db_tr->buf;
+ qld = (u_int32_t *)db_tr->buf;
+ len = dbch->xferq.psize - (db_tr->db[0].db.desc.count);
+ pcnt = 0;
+ do{
+#else
+ db_tr = dbch->top;
+ pcnt = 0;
+ /* XXX we cannot handle a packet which lies in more than two buf */
+ while (db_tr->db[0].db.desc.status & OHCI_CNTL_DMA_ACTIVE) {
+ ld = (u_int8_t *)db_tr->buf + dbch->buf_offset;
+ resCount = db_tr->db[0].db.desc.count;
+ len = dbch->xferq.psize - resCount
+ - dbch->buf_offset;
+#if 0
+ printf("len: %d resCount: %d offset: %d\n",
+ len, resCount, dbch->buf_offset);
+#endif
+ while (len > 0 ) {
+#endif
+ if(dbch->frag.buf != NULL){
+ buf = dbch->frag.buf;
+ if (dbch->frag.plen < 0) {
+ /* incomplete header */
+ int hlen;
+
+ hlen = - dbch->frag.plen;
+ rlen = hlen - dbch->frag.len;
+ bcopy(ld, dbch->frag.buf + dbch->frag.len, rlen);
+ ld += rlen;
+ len -= rlen;
+ dbch->frag.len += rlen;
+#if 0
+ printf("(1)frag.plen=%d frag.len=%d rlen=%d len=%d\n", dbch->frag.plen, dbch->frag.len, rlen, len);
+#endif
+ fp=(struct fw_pkt *)dbch->frag.buf;
+ dbch->frag.plen
+ = fwohci_get_plen(sc, fp, hlen);
+ if (dbch->frag.plen == 0)
+ goto out;
+ }
+ rlen = dbch->frag.plen - dbch->frag.len;
+#if 0
+ printf("(2)frag.plen=%d frag.len=%d rlen=%d len=%d\n", dbch->frag.plen, dbch->frag.len, rlen, len);
+#endif
+ bcopy(ld, dbch->frag.buf + dbch->frag.len,
+ rlen);
+ ld += rlen;
+ len -= rlen;
+ plen = dbch->frag.plen;
+ dbch->frag.buf = NULL;
+ dbch->frag.plen = 0;
+ dbch->frag.len = 0;
+ poff = 0;
+ }else{
+ fp=(struct fw_pkt *)ld;
+ fp->mode.ld[0] = htonl(fp->mode.ld[0]);
+ switch(fp->mode.common.tcode){
+ case FWTCODE_RREQQ:
+ case FWTCODE_WRES:
+ case FWTCODE_WREQQ:
+ case FWTCODE_RRESQ:
+ case FWOHCITCODE_PHY:
+ hlen = 12;
+ break;
+ case FWTCODE_RREQB:
+ case FWTCODE_WREQB:
+ case FWTCODE_LREQ:
+ case FWTCODE_RRESB:
+ case FWTCODE_LRES:
+ hlen = 16;
+ break;
+ default:
+ device_printf(sc->fc.dev, "Unknown tcode %d\n", fp->mode.common.tcode);
+ goto out;
+ }
+ if (len >= hlen) {
+ plen = fwohci_get_plen(sc, fp, hlen);
+ if (plen == 0)
+ goto out;
+ plen = (plen + 3) & ~3;
+ len -= plen;
+ } else {
+ plen = -hlen;
+ len -= hlen;
+ }
+ if(resCount > 0 || len > 0){
+ buf = malloc( dbch->xferq.psize,
+ M_DEVBUF, M_NOWAIT);
+ if(buf == NULL){
+ printf("cannot malloc!\n");
+ free(db_tr->buf, M_DEVBUF);
+ goto out;
+ }
+ bcopy(ld, buf, plen);
+ poff = 0;
+ dbch->frag.buf = NULL;
+ dbch->frag.plen = 0;
+ dbch->frag.len = 0;
+ }else if(len < 0){
+ dbch->frag.buf = db_tr->buf;
+ if (plen < 0) {
+#if 0
+ printf("plen < 0:"
+ "hlen: %d len: %d\n",
+ hlen, len);
+#endif
+ dbch->frag.len = hlen + len;
+ dbch->frag.plen = -hlen;
+ } else {
+ dbch->frag.len = plen + len;
+ dbch->frag.plen = plen;
+ }
+ bcopy(ld, db_tr->buf, dbch->frag.len);
+ buf = NULL;
+ }else{
+ buf = db_tr->buf;
+ poff = ld - (u_int8_t *)buf;
+ dbch->frag.buf = NULL;
+ dbch->frag.plen = 0;
+ dbch->frag.len = 0;
+ }
+ ld += plen;
+ }
+ if( buf != NULL){
+/* DMA result-code will be written at the tail of packet */
+ stat = ((struct fwohci_trailer *)(ld - sizeof(struct fwohci_trailer)))->stat;
+ spd = (stat >> 5) & 0x3;
+ stat &= 0x1f;
+ switch(stat){
+ case FWOHCIEV_ACKPEND:
+#if 0
+ printf("fwohci_arcv: ack pending..\n");
+#endif
+ /* fall through */
+ case FWOHCIEV_ACKCOMPL:
+ if( poff != 0 )
+ bcopy(buf+poff, buf, plen - 4);
+ fw_rcv(&sc->fc, buf, plen - sizeof(struct fwohci_trailer), 0, 0, spd);
+ break;
+ case FWOHCIEV_BUSRST:
+ free(buf, M_DEVBUF);
+ 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 ++;
+ };
+out:
+#if 0
+ itr--;
+ fwohci_add_rx_buf(db_tr, dbch->xferq.psize, dbch->xferq.flag, 0, NULL);
+#else
+ if (resCount == 0) {
+ /* done on this buffer */
+ fwohci_add_rx_buf(db_tr, dbch->xferq.psize,
+ dbch->xferq.flag, 0, NULL);
+ dbch->bottom->db[0].db.desc.depend |= z;
+ dbch->bottom = db_tr;
+ db_tr = STAILQ_NEXT(db_tr, link);
+ dbch->top = db_tr;
+ dbch->buf_offset = 0;
+ } else {
+ dbch->buf_offset = dbch->xferq.psize - resCount;
+ break;
+ }
+#endif
+ /* XXX make sure DMA is not dead */
+ }
+#if 0
+ dbch->bottom = db_tr;
+ dbch->bottom->db[0].db.desc.depend &= 0xfffffff0;
+#else
+#if 0
+ if (pcnt < 1)
+ printf("fwohci_arcv: no packets\n");
+#endif
+#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..1ca9866
--- /dev/null
+++ b/sys/dev/firewire/fwohci_pci.c
@@ -0,0 +1,294 @@
+/*
+ * 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 "opt_bus.h"
+
+#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 <machine/resource.h>
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirebusreg.h>
+#include <dev/firewire/firewirereg.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
+ if ((pci_get_vendor(dev) == FW_VENDORID_NEC) &&
+ (pci_get_device(dev) == FW_DEVICE_UPD861)) {
+ device_set_desc(dev, "NEC uPD72861");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_TI) &&
+ (pci_get_device(dev) == FW_DEVICE_TITSB22)) {
+ device_set_desc(dev, "Texas Instruments TSB12LV22");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_TI) &&
+ (pci_get_device(dev) == FW_DEVICE_TITSB23)) {
+ device_set_desc(dev, "Texas Instruments TSB12LV23");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_TI) &&
+ (pci_get_device(dev) == FW_DEVICE_TITSB26)) {
+ device_set_desc(dev, "Texas Instruments TSB12LV26");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_TI) &&
+ (pci_get_device(dev) == FW_DEVICE_TITSB43)) {
+ device_set_desc(dev, "Texas Instruments TSB43AA22");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_SONY) &&
+ (pci_get_device(dev) == FW_DEVICE_CX3022)) {
+ device_set_desc(dev, "SONY CX3022");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_VIA) &&
+ (pci_get_device(dev) == FW_DEVICE_VT6306)) {
+ device_set_desc(dev, "VIA VT6306");
+ return 0;
+ }
+ if ((pci_get_vendor(dev) == FW_VENDORID_RICOH) &&
+ (pci_get_device(dev) == FW_DEVICE_R5C552)) {
+ device_set_desc(dev, "Ricoh R5C552");
+ 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_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_attach(device_t self)
+{
+ fwohci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+ int intr;
+ int latency, cache_line;
+ u_int16_t cmd;
+ /* 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
+ return ENXIO;
+ }
+
+ cmd = pci_read_config(self, PCIR_COMMAND, 2);
+ cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN;
+ pci_write_config(self, PCIR_COMMAND, cmd, 2);
+
+ latency = pci_read_config(self, PCIR_LATTIMER, 1);
+#define DEF_LATENCY 250 /* Derived from Max Bulk Transfer size 512 Bytes*/
+ if( latency < DEF_LATENCY ) {
+ latency = DEF_LATENCY;
+ device_printf(self, "PCI bus latency was changing to");
+ pci_write_config(self, PCIR_LATTIMER,latency, 1);
+ } else
+ {
+ device_printf(self, "PCI bus latency is");
+ }
+ printf(" %d.\n", (int) latency);
+ cache_line = pci_read_config(self, PCIR_CACHELNSZ, 1);
+#if 0
+#define DEF_CACHE_LINE 0xc
+ cache_line = DEF_CACHE_LINE;
+ pci_write_config(self, PCIR_CACHELNSZ, cache_line, 1);
+#endif
+ printf("cache size %d.\n", (int) cache_line);
+/**/
+ 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, INTR_TYPE_NET,
+ (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 = 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;
+ }
+
+ return 0;
+}
+
+static int
+fwohci_pci_detach(device_t self)
+{
+ fwohci_softc_t *sc = device_get_softc(self);
+ int s;
+
+
+ s = splfw();
+ 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;
+ }
+ splx(s);
+
+ 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_shutdown, bus_generic_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..a766310
--- /dev/null
+++ b/sys/dev/firewire/fwohcireg.h
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 1998-2001 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_DEVICE_UPD861 0x0063
+#define FW_DEVICE_TITSB22 0x8009
+#define FW_DEVICE_TITSB23 0x8019
+#define FW_DEVICE_TITSB26 0x8020
+#define FW_DEVICE_TITSB43 0x8021
+#define FW_DEVICE_CX3022 0x8039
+#define FW_DEVICE_VT6306 0x3044
+#define FW_DEVICE_R5C552 0x1180
+
+#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;
+
+struct fwohcidb {
+ union {
+ struct {
+ volatile u_int32_t cmd;
+ volatile u_int32_t addr;
+ volatile u_int32_t depend;
+ volatile u_int32_t count:16,
+ status:16;
+ } desc;
+ volatile u_int32_t immed[4];
+ } db;
+#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_BIGEND (1 << 30)
+#define OHCI_HCC_PRPHY (1 << 23)
+#define OHCI_HCC_PHYEN (1 << 22)
+#define OHCI_HCC_LPS (1 << 19)
+#define OHCI_HCC_POSTWR (1 << 18)
+#define OHCI_HCC_LINKEN (1 << 17)
+#define OHCI_HCC_RESET (1 << 16)
+ 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;
+ caddr_t buf;
+ caddr_t dummy;
+ int dbcnt;
+};
+
+/*
+ * OHCI info structure.
+ */
+#if 0
+struct fwohci_softc {
+ struct fw_softc fc;
+ volatile struct ohci_registers *base;
+ int init;
+#define SIDPHASE 1
+ u_int32_t flags;
+ struct fwohcidb_tr *db_tr[OHCI_MAX_DMA_CH];
+ struct fwohcidb_tr *db_first[OHCI_MAX_DMA_CH];
+ struct fwohcidb_tr *db_last[OHCI_MAX_DMA_CH];
+ struct {
+ int tail;
+ struct fwohcidb_tr *db_tr;
+ struct fwohcidb *db;
+ }dbdvtx[MAX_DVFRAME], dbdvrx[MAX_DVFRAME];
+ int ndb[OHCI_MAX_DMA_CH];
+ u_int32_t isohdr[OHCI_MAX_DMA_CH];
+ int queued[OHCI_MAX_DMA_CH];
+ int dma_ch[OHCI_MAX_DMA_CH];
+};
+#endif
+struct fwohci_txpkthdr{
+ union{
+ u_int32_t ld[4];
+ struct {
+ u_int32_t res3:4,
+ tcode:4,
+ res2:8,
+ spd:3,
+ res1:13;
+ }common;
+ struct {
+ u_int32_t res3:4,
+ tcode:4,
+ tlrt:8,
+ spd:3,
+ res2:4,
+ srcbus:1,
+ res1:8;
+ u_int32_t res4:16,
+ dst:16;
+ }asycomm;
+ struct {
+ u_int32_t sy:4,
+ tcode:4,
+ chtag:8,
+ spd:3,
+ res1:13;
+ u_int32_t res2:16,
+ len:16;
+ }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_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..356bb19
--- /dev/null
+++ b/sys/dev/firewire/fwohcivar.h
@@ -0,0 +1,70 @@
+/*
+ * 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$
+ *
+ */
+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;
+ caddr_t dummy;
+ STAILQ_HEAD(, fwohcidb_tr) db_trq;
+ struct fwohcidb_tr *top, *bottom, *pdb_tr;
+ struct fw_xferq xferq;
+ struct {
+ int len;
+ int hlen;
+ int plen;
+ caddr_t buf;
+ } frag;
+ int flags;
+#define FWOHCI_DBCH_FULL (1<<1)
+ int buf_offset;
+ } arrq, arrs, atrq, atrs, it[OHCI_MAX_DMA_CH], ir[OHCI_MAX_DMA_CH];
+ u_int maxrec;
+ u_int32_t *cromptr;
+ u_int32_t intmask;
+} fwohci_softc_t;
+void fwohci_intr __P((void *arg));
+int fwohci_init __P((struct fwohci_softc *, device_t));
+int fwohci_shutdown __P((device_t dev));
diff --git a/sys/dev/firewire/iec13213.h b/sys/dev/firewire/iec13213.h
new file mode 100644
index 0000000..113613a
--- /dev/null
+++ b/sys/dev/firewire/iec13213.h
@@ -0,0 +1,92 @@
+/*
+ * 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 CSRKEY_MVID 0x3
+#define CSRKEY_NCAP 0xc
+#define CSRKEY_NUNQ 0x8d
+#define CSRKEY_NPWR 0x30
+#define CSRKEY_SPEC 0x12
+#define CSRVAL_1394TA 0x00a02d
+#define CSRVAL_ANSIT10 0x00609e
+#define CSRKEY_VER 0x13
+#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
+
+struct csrreg {
+ u_int32_t val:24,
+ key:8;
+};
+struct csrhdr {
+ u_int32_t crc:16,
+ crc_len:8,
+ info_len:8;
+};
+struct csrdirectory {
+ u_int32_t crc:16,
+ crc_len:16;
+ struct csrreg entry[0];
+};
+struct csrtext {
+ u_int32_t crc:16,
+ crc_len:16;
+ u_int32_t spec_id:16,
+ spec_type:16;
+ u_int32_t lang_id;
+ u_int32_t text[0];
+};
+struct businfo {
+ u_int32_t crc:16,
+ crc_len:8,
+ :12,
+ max_rec:4,
+ clk_acc:8,
+ :4,
+ bmc:1,
+ isc:1,
+ cmc:1,
+ irmc:1;
+ u_int32_t c_id_hi:8,
+ v_id:24;
+ u_int32_t c_id_lo;
+};
diff --git a/sys/dev/firewire/iec68113.h b/sys/dev/firewire/iec68113.h
new file mode 100644
index 0000000..4971dba
--- /dev/null
+++ b/sys/dev/firewire/iec68113.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1998-2001 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$
+ *
+ */
+struct ciphdr {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_int32_t src:8,
+ len:8,
+ dummy:8,
+ dbc:8;
+ u_int32_t :16,
+ cyc:16;
+#else
+ u_int32_t dbc:8,
+ dummy:8,
+ len:8,
+ src:8;
+ u_int32_t cyc:16,
+ :16;
+#endif
+};
+struct dvdbc{
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int32_t :8,
+ dbn:8,
+ rsv0:3,
+ z:1,
+ dseq:4,
+ seq:4,
+ rsv1:1,
+ sct:3;
+#else
+ u_int32_t seq:4,
+ :1,
+ sct:3,
+ :3,
+ z:1,
+ dseq:4,
+ dbn:8,
+ :8;
+#endif
+ u_int32_t ld[19];
+};
diff --git a/sys/dev/firewire/if_fwe.c b/sys/dev/firewire/if_fwe.c
new file mode 100644
index 0000000..2561a40
--- /dev/null
+++ b/sys/dev/firewire/if_fwe.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $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 <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 MAX_QUEUED IFQ_MAXLEN /* 50 */
+
+/* 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_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;
+
+MALLOC_DECLARE(M_FWE);
+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");
+
+#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;
+
+ 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];
+ eaddr[0] = (fwe->fd.fc->eui[0] | LOCAL) & ~GROUP;
+ eaddr[1] = fwe->fd.fc->eui[1];
+ eaddr[2] = fwe->fd.fc->eui[2];
+ eaddr[3] = fwe->fd.fc->eui[5];
+ eaddr[4] = fwe->fd.fc->eui[6];
+ eaddr[5] = fwe->fd.fc->eui[7];
+ printf("if_fwe%d: %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 = FWMAXQUEUE - 1;
+
+ s = splimp();
+ ether_ifattach(ifp, 1);
+ splx(s);
+
+ /* Tell the upper layer(s) we support long frames. */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+
+ ifp->if_snd.ifq_maxlen = MAX_QUEUED - 1;
+
+ 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;
+
+ 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_HANDLER);
+ /* XXX dequeue xferq->q */
+ 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);
+ ether_ifdetach(&fwe->fwe_if, 1);
+
+ 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;
+ 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_PACKET;
+ xferq->flag |= fwe->stream_ch & 0xff;
+ /* register fwe_input handler */
+ xferq->sc = (caddr_t) fwe;
+ xferq->hand = fwe_as_input;
+ xferq->flag |= FWXFERQ_HANDLER;
+ } 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 SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+ s = splimp();
+ error = ether_ioctl(ifp, cmd, data);
+ splx(s);
+ return (error);
+ 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;
+
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static void
+fwe_start(struct ifnet *ifp)
+{
+ struct fwe_softc *fwe = ((struct fwe_eth_softc *)ifp->if_softc)->fwe;
+ int s;
+
+#if 1
+ 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;
+ }
+
+#endif
+ 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);
+}
+
+
+static void
+fwe_output_callback(struct fw_xfer *xfer)
+{
+ struct fwe_softc *fwe;
+ struct ifnet *ifp;
+
+ fwe = (struct fwe_softc *)xfer->sc;
+ /* XXX error check */
+ FWEDEBUG("resp = %d\n", xfer->resp);
+ m_freem(xfer->mbuf);
+ xfer->send.buf = NULL;
+ fw_xfer_free(xfer);
+#if 1
+ /* XXX for queue full */
+ ifp = &fwe->fwe_if;
+ if (ifp->if_snd.ifq_head != NULL)
+ fwe_start(ifp);
+#endif
+}
+
+#define HDR_LEN 4
+#define ALIGN_PAD 2
+/* 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) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ xfer = fw_xfer_alloc();
+ if (xfer == NULL) {
+ return;
+ }
+ if (ifp->if_bpf != NULL)
+ bpf_mtap(ifp, m);
+
+ xfer->send.off = 0;
+ xfer->spd = 2;
+ xfer->fc = fwe->fd.fc;
+ xfer->retry_req = fw_asybusy;
+ xfer->sc = (caddr_t)fwe;
+ xfer->act.hand = fwe_output_callback;
+
+ /* keep ip packet alignment for alpha */
+ M_PREPEND(m, ALIGN_PAD, M_DONTWAIT);
+ fp = (struct fw_pkt *)&xfer->dst; /* XXX */
+ xfer->dst = *((int32_t *)&fwe->pkt_hdr);
+ fp->mode.stream.len = htons(m->m_pkthdr.len);
+ xfer->send.buf = (caddr_t) fp;
+ xfer->mbuf = m;
+ xfer->send.len = m->m_pkthdr.len + HDR_LEN;
+
+ i++;
+ if (fw_asyreq(xfer->fc, -1, xfer) != 0) {
+ /* error */
+ ifp->if_oerrors ++;
+ /* XXX set error code */
+ fwe_output_callback(xfer);
+ } else {
+ ifp->if_opackets ++;
+ }
+ }
+#if 0
+ if (i > 1)
+ printf("%d queued\n", i);
+#endif
+ if (xfer != NULL)
+ xferq->start(xfer->fc);
+}
+
+#if __FreeBSD_version >= 500000
+static void
+fwe_free(void *buf, void *args)
+{
+ FWEDEBUG("fwe_free:\n");
+ free(buf, M_DEVBUF);
+}
+
+#else
+static void
+fwe_free(caddr_t buf, u_int size)
+{
+ int *p;
+ FWEDEBUG("fwe_free:\n");
+ p = (int *)buf;
+ (*p) --;
+ if (*p < 1)
+ free(buf, M_DEVBUF);
+}
+
+static void
+fwe_ref(caddr_t buf, u_int size)
+{
+ int *p;
+
+ FWEDEBUG("fwe_ref: called\n");
+ p = (int *)buf;
+ (*p) ++;
+}
+#endif
+
+/* Async. stream output */
+static void
+fwe_as_input(struct fw_xferq *xferq)
+{
+ struct mbuf *m;
+ struct ether_header *eh;
+ struct ifnet *ifp;
+ struct fw_xfer *xfer;
+ struct fwe_softc *fwe;
+ u_char *c;
+ int len;
+ caddr_t p;
+
+ fwe = (struct fwe_softc *)xferq->sc;
+ ifp = &fwe->fwe_if;
+#if 0
+ FWE_POLL_REGISTER(fwe_poll, fwe, ifp);
+#endif
+ while ((xfer = STAILQ_FIRST(&xferq->q)) != NULL) {
+ STAILQ_REMOVE_HEAD(&xferq->q, link);
+ xferq->queued --;
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("MGETHDR failed\n");
+ fw_xfer_free(xfer);
+ return;
+ }
+ len = xfer->recv.off + xfer->recv.len;
+ FWEDEBUG("fwe_as_input len=%d\n", len);
+#if __FreeBSD_version >= 500000
+ MEXTADD(m, xfer->recv.buf, len, fwe_free, NULL, 0, EXT_NET_DRV);
+#else
+ m->m_flags |= M_EXT;
+ m->m_ext.ext_buf = xfer->recv.buf;
+ m->m_ext.ext_size = len;
+ m->m_ext.ext_free = fwe_free;
+ m->m_ext.ext_ref = fwe_ref;
+ *((int *)m->m_ext.ext_buf) = 1; /* XXX refcount */
+#endif
+ p = xfer->recv.buf + xfer->recv.off + HDR_LEN + ALIGN_PAD;
+ eh = (struct ether_header *)p;
+ p += sizeof(struct ether_header);
+ len -= xfer->recv.off + HDR_LEN + ALIGN_PAD
+ + sizeof(struct ether_header);
+ m->m_data = p;
+ m->m_len = m->m_pkthdr.len = len;
+ m->m_pkthdr.rcvif = ifp;
+ c = (char *)eh;
+#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
+ ether_input(ifp, eh, m);
+ ifp->if_ipackets ++;
+
+ xfer->recv.buf = NULL;
+ fw_xfer_free(xfer);
+ }
+}
+
+
+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(if_fwe, firewire, fwe_driver, fwe_devclass, 0, 0);
+MODULE_VERSION(if_fwe, 1);
+MODULE_DEPEND(if_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..afaa1ae
--- /dev/null
+++ b/sys/dev/firewire/if_fwevar.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $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;
+ struct fwe_eth_softc {
+ /* XXX this must be 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..931af4b
--- /dev/null
+++ b/sys/dev/firewire/sbp.c
@@ -0,0 +1,2214 @@
+/*
+ * Copyright (c) 1998,1999,2000,2001 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>
+#if __FreeBSD_version >= 500000
+#include <machine/bus_dma.h>
+#endif
+#include <sys/malloc.h>
+#include <sys/devicestat.h> /* for struct devstat */
+
+
+#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 <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/iec13213.h>
+
+#define ccb_sdev_ptr spriv_ptr0
+#define ccb_sbp_ptr spriv_ptr1
+
+#define SBP_NUM_TARGETS 8
+#define SBP_NUM_LUNS 8 /* limited by CAM_SCSI2_MAXLUN in cam_xpt.c */
+#define SBP_QUEUE_LEN 4
+#define SBP_NUM_OCB (SBP_QUEUE_LEN * SBP_NUM_TARGETS)
+#define SBP_INITIATOR 7
+#define SBP_ESELECT_TIMEOUT 1
+#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)
+
+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 = 1;
+static int auto_login = 1;
+static int max_speed = 2;
+
+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_IND_MAX 0x20
+struct sbp_ocb {
+ STAILQ_ENTRY(sbp_ocb) ocb;
+ union ccb *ccb;
+ volatile u_int32_t orb[8];
+ volatile struct ind_ptr ind_ptr[SBP_IND_MAX];
+ struct sbp_dev *sdev;
+ int flags;
+ bus_dmamap_t dmamap;
+};
+#define OCB_ACT_MGM 0
+#define OCB_ACT_CMD 1
+#define OCB_ACT_MASK 3
+#define OCB_RESERVED 0x10
+#define OCB_DONE 0x20
+
+#define SBP_RESOURCE_SHORTAGE 0x10
+
+struct sbp_login_res{
+#if FW_ENDIANSWAP == 0 && BYTE_ORDER == LITTLE_ENDIAN
+ 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;
+#else
+ u_int16_t id;
+ u_int16_t len;
+ u_int16_t cmd_hi;
+ u_int16_t res0;
+ u_int32_t cmd_lo;
+ u_int16_t recon_hold;
+ u_int16_t res1;
+#endif
+};
+struct sbp_status{
+#if FW_ENDIANSWAP == 0 && BYTE_ORDER == LITTLE_ENDIAN
+ u_int8_t len:3,
+ dead:1,
+ resp:2,
+ src:2;
+ u_int8_t status:8;
+ u_int16_t orb_hi;
+ u_int32_t orb_lo;
+ u_int32_t data[6];
+#else
+ u_int16_t orb_hi;
+ u_int8_t status:8;
+ u_int8_t len:3,
+ dead:1,
+ resp:2,
+ src:2;
+ u_int32_t orb_lo;
+ u_int32_t data[6];
+#endif
+};
+struct sbp_cmd_status{
+#define SBP_SFMT_CURR 0
+#define SBP_SFMT_DEFER 1
+#if FW_ENDIANSWAP == 0 && BYTE_ORDER == LITTLE_ENDIAN
+ u_int8_t status:6,
+ sfmt:2;
+ u_int8_t s_key:4,
+ ill_len:1,
+ eom:1,
+ mark:1,
+ valid:1;
+ u_int8_t s_code;
+ u_int8_t s_qlfr;
+ u_int32_t info;
+ u_int32_t cdb;
+ u_int32_t fru:8,
+ s_keydep:24;
+ u_int32_t vend[2];
+#else
+ u_int8_t s_qlfr;
+ u_int8_t s_code;
+ u_int8_t s_key:4,
+ ill_len:1,
+ eom:1,
+ mark:1,
+ valid:1;
+ u_int8_t status:6,
+ sfmt:2;
+ u_int32_t info;
+ u_int32_t cdb;
+ u_int32_t s_keydep:24,
+ fru:8;
+ u_int32_t vend[2];
+#endif
+};
+
+struct sbp_dev{
+#define SBP_DEV_RESET 0 /* accept login */
+#define SBP_DEV_LOGIN 1 /* to login */
+#define SBP_DEV_RECONN 2 /* to reconnect */
+#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 */
+ int status;
+ int lun_id;
+ struct cam_path *path;
+ struct sbp_target *target;
+ struct sbp_login_res login;
+ STAILQ_HEAD(, sbp_ocb) 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_softc {
+ struct firewire_dev_comm fd;
+ unsigned char flags;
+ struct cam_sim *sim;
+ struct sbp_target targets[SBP_NUM_TARGETS];
+ struct fw_bind fwb;
+ STAILQ_HEAD(, sbp_ocb) free_ocbs;
+ struct sbp_ocb *ocb;
+ bus_dma_tag_t dmat;
+};
+static void sbp_post_explore __P((void *));
+static void sbp_recv __P((struct fw_xfer *));
+static void sbp_login_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_softc *, 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_softc *));
+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 *, u_int32_t));
+static void sbp_detach_target __P((struct sbp_target *));
+static void sbp_timeout __P((void *arg));
+static void sbp_mgm_orb __P((struct sbp_dev *, int));
+
+MALLOC_DEFINE(M_SBP, "sbp", "SBP-II/Firewire");
+
+#define SBPPRI ((PZERO+8)|PCATCH)
+
+#define FOREACH_SDEV(tsdev, targets) \
+ tsdev = NULL; \
+ for (i = 0; i < SBP_NUM_TARGETS; i++) { \
+ if (targets[i].fwdev == NULL) \
+ continue; \
+ for (j = 0; j < targets[i].num_lun; j++) { \
+ tsdev = &targets[i].luns[j]; \
+ if (tsdev->status == SBP_DEV_DEAD) \
+ continue;
+#define FOREACH_SDEV_END \
+ } \
+ }
+
+/* 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_callback(struct cam_periph *periph,
+ union ccb *ccb);
+static void sbp_cam_scan_lun(struct sbp_dev *sdev);
+
+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");
+ return (0);
+}
+
+static void
+sbp_show_sdev_info(struct sbp_dev *sdev, int new)
+{
+ int lun;
+ 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;
+ lun = getcsrdata(fwdev, 0x14);
+ printf("ordered:%d type:%d EUI:%08x%08x node:%d "
+ "speed:%d maxrec:%d",
+ (lun & 0x00400000) >> 22,
+ (lun & 0x001f0000) >> 16,
+ 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 sbp_target *
+sbp_alloc_target(struct sbp_softc *sbp, struct fw_device *fwdev)
+{
+ int i, lun;
+ struct sbp_target *target;
+ struct sbp_dev *sdev;
+
+SBP_DEBUG(1)
+ printf("sbp_alloc_target\n");
+END_DEBUG
+ for (i = 0; i < SBP_NUM_TARGETS; i++)
+ if(sbp->targets[i].fwdev == NULL) break;
+ if (i == SBP_NUM_TARGETS) {
+ printf("increase SBP_NUM_TARGETS!\n");
+ return NULL;
+ }
+ /* new target */
+ target = &sbp->targets[i];
+ target->sbp = sbp;
+ target->fwdev = fwdev;
+ target->target_id = i;
+ if((target->mgm_lo = getcsrdata(fwdev, 0x54)) == 0 ){
+ /* bad target */
+ printf("NULL management address\n");
+ target->fwdev = NULL;
+ return NULL;
+ }
+ target->mgm_hi = 0xffff;
+ target->mgm_lo = 0xf0000000 | target->mgm_lo << 2;
+ /* XXX should probe all luns */
+ /* XXX num_lun may be changed. realloc luns? */
+ lun = getcsrdata(target->fwdev, 0x14) & 0xff;
+ target->num_lun = lun + 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);
+ if (i == lun)
+ sdev->status = SBP_DEV_RESET;
+ else
+ sdev->status = SBP_DEV_DEAD;
+ }
+ return target;
+}
+
+static void
+sbp_get_text_leaf(struct fw_device *fwdev, int key, char *buf, int len)
+{
+ static char *nullstr = "(null)";
+ int i, clen, found=0;
+ struct csrhdr *chdr;
+ struct csrreg *creg;
+ u_int32_t *src, *dst;
+
+ chdr = (struct csrhdr *)&fwdev->csrrom[0];
+ creg = (struct csrreg *)chdr;
+ creg += chdr->info_len;
+ for( i = chdr->info_len + 4; i <= fwdev->rommax; i+=4){
+ if((creg++)->key == key){
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ strncpy(buf, nullstr, len);
+ return;
+ }
+ src = (u_int32_t *) creg + creg->val;
+ clen = ((*src >> 16) - 2) * 4;
+ src += 3;
+ dst = (u_int32_t *) buf;
+ if (len < clen)
+ clen = len;
+ for (i = 0; i < clen/4; i++)
+ *dst++ = htonl(*src++);
+ buf[clen] = 0;
+}
+
+static void
+sbp_probe_lun(struct sbp_dev *sdev)
+{
+ struct fw_device *fwdev;
+ int rev;
+
+ fwdev = sdev->target->fwdev;
+ bzero(sdev->vendor, sizeof(sdev->vendor));
+ bzero(sdev->product, sizeof(sdev->product));
+ sbp_get_text_leaf(fwdev, 0x03, sdev->vendor, sizeof(sdev->vendor));
+ sbp_get_text_leaf(fwdev, 0x17, sdev->product, sizeof(sdev->product));
+ rev = getcsrdata(sdev->target->fwdev, 0x3c);
+ snprintf(sdev->revision, sizeof(sdev->revision), "%06x", rev);
+}
+static void
+sbp_probe_target(struct sbp_target *target, int alive)
+{
+ struct sbp_softc *sbp;
+ struct sbp_dev *sdev;
+ struct firewire_comm *fc;
+ int i;
+
+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;
+ 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);
+ }
+ sbp_abort_all_ocbs(sdev, CAM_REQUEUE_REQ);
+ switch (sdev->status) {
+ case SBP_DEV_ATTACHED:
+ sbp_mgm_orb(sdev, ORB_FUN_RCN);
+ break;
+ case SBP_DEV_RETRY:
+ sbp_probe_lun(sdev);
+ sbp_mgm_orb(sdev, ORB_FUN_LGI);
+ break;
+ default:
+ /* new or revived target */
+ sbp_probe_lun(sdev);
+ if (auto_login) {
+ sdev->status = SBP_DEV_TOATTACH;
+ sbp_mgm_orb(sdev, ORB_FUN_LGI);
+ }
+ break;
+ }
+ sbp_show_sdev_info(sdev,
+ (sdev->status == SBP_DEV_TOATTACH));
+ } 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->status = SBP_DEV_RETRY;
+ sbp_abort_all_ocbs(sdev, CAM_REQUEUE_REQ);
+ 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;
+ }
+ }
+ }
+}
+
+#if 0
+static void
+sbp_release_queue(void *arg)
+{
+ struct sbp_softc *sbp;
+
+SBP_DEBUG(0)
+ printf("sbp_release_queue\n");
+END_DEBUG
+ sbp = (struct sbp_softc *)arg;
+ xpt_release_simq(sbp->sim, 1);
+}
+
+static void
+sbp_release_devq(void *arg)
+{
+ struct sbp_dev *sdev;
+ int s;
+
+ sdev = (struct sbp_dev *)arg;
+SBP_DEBUG(0)
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_release_devq\n");
+END_DEBUG
+ s = splcam();
+ xpt_release_devq(sdev->path, 1, TRUE);
+ splx(s);
+}
+#endif
+
+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(1)
+ printf("sbp_post_explore\n");
+END_DEBUG
+#if 0
+ xpt_freeze_simq(sbp->sim, /*count*/ 1);
+#endif
+ /* Gabage Collection */
+ for(i = 0 ; i < SBP_NUM_TARGETS ; i ++){
+ target = &sbp->targets[i];
+ for( fwdev = TAILQ_FIRST(&sbp->fd.fc->devices);
+ fwdev != NULL; fwdev = TAILQ_NEXT(fwdev, link)){
+ if(target->fwdev == NULL) break;
+ if(target->fwdev == fwdev) break;
+ }
+ if(fwdev == NULL){
+ /* device has removed in lower driver */
+ sbp_detach_target(target);
+ }
+ }
+ /* traverse device list */
+ for( fwdev = TAILQ_FIRST(&sbp->fd.fc->devices);
+ fwdev != NULL; fwdev = TAILQ_NEXT(fwdev, link)){
+SBP_DEBUG(0)
+ printf("sbp_post_explore: EUI:%08x%08x ",
+ fwdev->eui.hi, fwdev->eui.lo);
+ if (fwdev->status == FWDEVATTACHED) {
+ printf("spec=%d key=%d.\n",
+ getcsrdata(fwdev, CSRKEY_SPEC) == CSRVAL_ANSIT10,
+ getcsrdata(fwdev, CSRKEY_VER) == CSRVAL_T10SBP2);
+ } else {
+ printf("not attached, state=%d.\n", fwdev->status);
+ }
+END_DEBUG
+ alive = (fwdev->status == FWDEVATTACHED)
+ && (getcsrdata(fwdev, CSRKEY_SPEC) == CSRVAL_ANSIT10)
+ && (getcsrdata(fwdev, CSRKEY_VER) == CSRVAL_T10SBP2);
+ 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(target, alive);
+ }
+#if 0
+ timeout(sbp_release_queue, (caddr_t)sbp, bus_reset_rest * hz / 1000);
+#endif
+}
+
+#if NEED_RESPONSE
+static void
+sbp_loginres_callback(struct fw_xfer *xfer){
+SBP_DEBUG(1)
+ struct sbp_dev *sdev;
+ sdev = (struct sbp_dev *)xfer->sc;
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_loginres_callback\n");
+END_DEBUG
+ fw_xfer_free(xfer);
+ return;
+}
+#endif
+
+static void
+sbp_login_callback(struct fw_xfer *xfer)
+{
+SBP_DEBUG(1)
+ struct sbp_dev *sdev;
+ sdev = (struct sbp_dev *)xfer->sc;
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_login_callback\n");
+END_DEBUG
+ fw_xfer_free(xfer);
+ 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
+ fw_xfer_free(xfer);
+ return;
+}
+
+static void
+sbp_cam_callback(struct cam_periph *periph, union ccb *ccb)
+{
+ struct sbp_dev *sdev;
+ sdev = (struct sbp_dev *) ccb->ccb_h.ccb_sdev_ptr;
+SBP_DEBUG(1)
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_cam_callback\n");
+END_DEBUG
+ sdev->status = SBP_DEV_ATTACHED;
+ free(ccb, M_SBP);
+}
+
+static void
+sbp_cam_scan_lun(struct sbp_dev *sdev)
+{
+ union ccb *ccb = malloc(sizeof(union ccb), M_SBP, M_WAITOK | M_ZERO);
+
+SBP_DEBUG(0)
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_cam_scan_lun\n");
+END_DEBUG
+ xpt_setup_ccb(&ccb->ccb_h, sdev->path, 5/*priority (low)*/);
+ ccb->ccb_h.func_code = XPT_SCAN_LUN;
+ ccb->ccb_h.cbfcnp = sbp_cam_callback;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ ccb->ccb_h.ccb_sdev_ptr = sdev;
+ xpt_action(ccb);
+
+ /* The scan is in progress now. */
+}
+
+
+static void
+sbp_ping_unit_callback(struct cam_periph *periph, union ccb *ccb)
+{
+ struct sbp_dev *sdev;
+ sdev = (struct sbp_dev *) ccb->ccb_h.ccb_sdev_ptr;
+SBP_DEBUG(1)
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_ping_unit_callback\n");
+END_DEBUG
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (--ccb->ccb_h.retry_count == 0) {
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_tur_callback: retry count exceeded\n");
+ sdev->status = SBP_DEV_RETRY;
+ free(ccb, M_SBP);
+ } else {
+ /* requeue */
+ xpt_action(ccb);
+ xpt_release_devq(sdev->path, 1, TRUE);
+ }
+ } else {
+ free(ccb->csio.data_ptr, M_SBP);
+ free(ccb, M_SBP);
+ sdev->status = SBP_DEV_ATTACHED;
+ xpt_release_devq(sdev->path, 1, TRUE);
+ }
+}
+
+/*
+ * XXX Some devices need to execute inquiry or read_capacity
+ * after bus_rest during busy transfer.
+ * Otherwise they return incorrect result for READ(and WRITE?)
+ * command without any SBP-II/SCSI error.
+ *
+ * e.g. Maxtor 3000XT, Yano A-dish.
+ */
+static void
+sbp_ping_unit(struct sbp_dev *sdev)
+{
+ union ccb *ccb;
+ struct scsi_inquiry_data *inq_buf;
+
+ ccb = malloc(sizeof(union ccb), M_SBP, M_WAITOK | M_ZERO);
+ inq_buf = (struct scsi_inquiry_data *)
+ malloc(sizeof(*inq_buf), M_SBP, M_WAITOK);
+
+SBP_DEBUG(1)
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_ping_unit\n");
+END_DEBUG
+
+ /*
+ * We need to execute this command before any other queued command.
+ * Make priority 0 and freeze queue after execution for retry.
+ * cam's scan_lun command doesn't provide this feature.
+ */
+ xpt_setup_ccb(&ccb->ccb_h, sdev->path, 0/*priority (high)*/);
+ scsi_inquiry(
+ &ccb->csio,
+ /*retries*/ 5,
+ sbp_ping_unit_callback,
+ MSG_SIMPLE_Q_TAG,
+ (u_int8_t *)inq_buf,
+ SHORT_INQUIRY_LENGTH,
+ /*evpd*/FALSE,
+ /*page_code*/0,
+ SSD_MIN_SIZE,
+ /*timeout*/60000
+ );
+ ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
+ xpt_action(ccb);
+}
+
+static void
+sbp_do_attach(struct fw_xfer *xfer)
+{
+ struct sbp_dev *sdev;
+
+ sdev = (struct sbp_dev *)xfer->sc;
+SBP_DEBUG(0)
+ sbp_show_sdev_info(sdev, 2);
+ printf("sbp_do_attach\n");
+END_DEBUG
+ fw_xfer_free(xfer);
+ if (sdev->path == NULL)
+ xpt_create_path(&sdev->path, xpt_periph,
+ cam_sim_path(sdev->target->sbp->sim),
+ sdev->target->target_id, sdev->lun_id);
+
+ if (sdev->status == SBP_DEV_RETRY) {
+ sdev->status = SBP_DEV_PROBE;
+ sbp_ping_unit(sdev);
+ /* freezed twice */
+ xpt_release_devq(sdev->path, 1, TRUE);
+ } else {
+ sdev->status = SBP_DEV_PROBE;
+ sbp_cam_scan_lun(sdev);
+ }
+ xpt_release_devq(sdev->path, 1, TRUE);
+ 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
+ fw_xfer_free(xfer);
+ sbp_abort_all_ocbs(sdev, CAM_REQUEUE_REQ);
+ if (sdev->path)
+ xpt_release_devq(sdev->path, 1, TRUE);
+}
+
+static void
+sbp_agent_reset(struct sbp_dev *sdev, int attach)
+{
+ 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 (attach)
+ xfer->act.hand = sbp_do_attach;
+ else
+ xfer->act.hand = sbp_agent_reset_callback;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.wreqq.data = htonl(0xf);
+ fw_asyreq(xfer->fc, -1, xfer);
+}
+
+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_but_timeout_callback\n");
+END_DEBUG
+ fw_xfer_free(xfer);
+ sbp_agent_reset(sdev, 1);
+}
+
+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 = htons(0xffff);
+ fp->mode.wreqq.dest_lo = htonl(0xf0000000 | BUS_TIME);
+ fp->mode.wreqq.data = htonl(0xf);
+ fw_asyreq(xfer->fc, -1, xfer);
+}
+
+#if 0
+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_busy_timeout;
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.wreqq.dest_hi = htons(0xffff);
+ fp->mode.wreqq.dest_lo = htonl(0xf0000000 | RESET_START);
+ fp->mode.wreqq.data = htonl(0xf);
+ fw_asyreq(xfer->fc, -1, xfer);
+}
+#endif
+
+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 = htons(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(vtophys(&ocb->orb[0]));
+
+ if(fw_asyreq(xfer->fc, -1, xfer) != 0){
+ fw_xfer_free(xfer);
+ ocb->ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ocb->ccb);
+ }
+}
+
+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);
+}
+
+static struct fw_xfer *
+sbp_write_cmd(struct sbp_dev *sdev, int tcode, int offset)
+{
+ struct fw_xfer *xfer;
+ struct fw_pkt *fp;
+
+ xfer = fw_xfer_alloc();
+ if(xfer == NULL){
+ return NULL;
+ }
+ if (tcode == FWTCODE_WREQQ)
+ xfer->send.len = 16;
+ else
+ xfer->send.len = 24;
+
+ xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT);
+ if(xfer->send.buf == NULL){
+ fw_xfer_free( xfer);
+ return NULL;
+ }
+
+ xfer->send.off = 0;
+ xfer->spd = min(sdev->target->fwdev->speed, max_speed);
+ xfer->sc = (caddr_t)sdev;
+ xfer->fc = sdev->target->sbp->fd.fc;
+ xfer->retry_req = fw_asybusy;
+
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.wreqq.dest_hi = htons(sdev->login.cmd_hi);
+ fp->mode.wreqq.dest_lo = htonl(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 = htons(xfer->dst);
+
+ return xfer;
+
+}
+
+static void
+sbp_mgm_orb(struct sbp_dev *sdev, int func)
+{
+ struct fw_xfer *xfer;
+ struct fw_pkt *fp;
+ struct sbp_ocb *ocb;
+ int s, nid;
+
+ if ((ocb = sbp_get_ocb(sdev->target->sbp)) == NULL) {
+ s = splfw();
+ sdev->target->sbp->flags |= SBP_RESOURCE_SHORTAGE;
+ splx(s);
+ return;
+ }
+ ocb->flags = OCB_ACT_MGM;
+ ocb->sdev = sdev;
+ ocb->ccb = NULL;
+
+ nid = sdev->target->sbp->fd.fc->nodeid | FWLOCALBUS;
+ 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(sdev->target->sbp->fd.dev),
+ sdev->target->target_id,
+ sdev->lun_id));
+
+ sbp_show_sdev_info(sdev, 2);
+ printf("%s\n", orb_fun_name[(func>>16)&0xf]);
+ switch (func) {
+ case ORB_FUN_LGI:
+ ocb->orb[2] = htonl(nid << 16);
+ ocb->orb[3] = htonl(vtophys(&sdev->login));
+ ocb->orb[4] = htonl(ORB_NOTIFY | ORB_EXV | sdev->lun_id);
+ ocb->orb[5] = htonl(sizeof(struct sbp_login_res));
+ break;
+ case ORB_FUN_RCN:
+ case ORB_FUN_LGO:
+ case ORB_FUN_LUR:
+ case ORB_FUN_RST:
+ case ORB_FUN_ATA:
+ case ORB_FUN_ATS:
+ ocb->orb[4] = htonl(ORB_NOTIFY | func | sdev->login.id);
+ break;
+ }
+
+ xfer = sbp_write_cmd(sdev, FWTCODE_WREQB, 0);
+ if(xfer == NULL){
+ return;
+ }
+ xfer->act.hand = sbp_login_callback;
+
+ fp = (struct fw_pkt *)xfer->send.buf;
+ fp->mode.wreqb.dest_hi = htons(sdev->target->mgm_hi);
+ fp->mode.wreqb.dest_lo = htonl(sdev->target->mgm_lo);
+ fp->mode.wreqb.len = htons(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(vtophys(&ocb->orb[0]));
+ sbp_enqueue_ocb(sdev, ocb);
+
+ fw_asyreq(xfer->fc, -1, xfer);
+}
+
+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;
+ struct ccb_scsiio *csio;
+
+
+ sbp_cmd_status = (struct sbp_cmd_status *)sbp_status->data;
+ sense = &ocb->ccb->csio.sense_data;
+
+SBP_DEBUG(0)
+ 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);
+ /* 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",
+ 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
+ );
+#if 0 /* XXX */
+ if (sbp_cmd_status->status == SCSI_STATUS_CHECK_COND) {
+ printf(" %s\n", scsi_sense_key_text[sbp_cmd_status->s_key]);
+ scsi_sense_desc(
+ sbp_cmd_status->s_code,
+ sbp_cmd_status->s_qlfr,
+ ocb->ccb->ccb_h.path->device->inq_data
+ )
+ } else {
+ printf("\n");
+ }
+#else
+ printf("\n");
+#endif
+END_DEBUG
+
+
+ if(sbp_cmd_status->status == SCSI_STATUS_CHECK_COND ||
+ sbp_cmd_status->status == 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]);
+ }
+
+}
+*/
+ } else {
+ printf("sbp_scsi_status: unknown scsi status\n");
+ }
+}
+
+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:
+ /* disable tag queuing */
+ inq->flags &= ~SID_CmdQue;
+ /*
+ * Override vendor/product/revision information.
+ * Some devices sometimes return strange strings.
+ */
+ 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));
+ 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_valid;
+ 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]));
+*/
+ if(xfer->resp != 0){
+ printf("sbp_recv: xfer->resp != 0\n");
+ fw_xfer_free( xfer);
+ return;
+ }
+ if(xfer->recv.buf == NULL){
+ printf("sbp_recv: xfer->recv.buf == NULL\n");
+ fw_xfer_free( xfer);
+ return;
+ }
+ 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);
+ fw_xfer_free( xfer);
+ return;
+ }
+ sbp_status = (struct sbp_status *)rfp->mode.wreqb.payload;
+ addr = ntohl(rfp->mode.wreqb.dest_lo);
+SBP_DEBUG(2)
+ printf("received address 0x%x\n", addr);
+END_DEBUG
+ target = &sbp->targets[SBP_ADDR2TRG(addr)];
+ sdev = &target->luns[SBP_ADDR2LUN(addr)];
+
+ status_valid = (sbp_status->resp == ORB_RES_CMPL
+ && sbp_status->dead == 0
+ && sbp_status->status == 0);
+
+SBP_DEBUG(0)
+ if (!status_valid || debug > 1){
+ int status;
+
+ sbp_show_sdev_info(sdev, 2);
+ printf("ORB status src:%x resp:%x dead:%x"
+ " len:%x stat:%x orb:%x%08x\n",
+ sbp_status->src, sbp_status->resp, sbp_status->dead,
+ sbp_status->len, sbp_status->status,
+ ntohl(sbp_status->orb_hi), ntohl(sbp_status->orb_lo));
+ 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("Object: %s, Serial Bus Error: %s\n",
+ orb_status1_object[(status>>6) & 3],
+ orb_status1_serial_bus_error[status & 0xf]);
+ break;
+ default:
+ printf("unknown respose code\n");
+ }
+ }
+END_DEBUG
+ ocb = sbp_dequeue_ocb(sdev, ntohl(sbp_status->orb_lo));
+
+ /* we have to reset the fetch agent if it's dead */
+ if (sbp_status->dead) {
+ if (sdev->path)
+ xpt_freeze_devq(sdev->path, 1);
+ sbp_agent_reset(sdev, 0);
+ }
+
+
+ if (ocb == NULL) {
+ printf("No ocb on the queue for target %d.\n", sdev->target->target_id);
+ fw_xfer_free( xfer);
+ return;
+ }
+
+ 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 & OCB_ACT_MASK){
+ case OCB_ACT_MGM:
+ orb_fun = ntohl(ocb->orb[4]) & ORB_FUN_MSK;
+ switch(orb_fun) {
+ case ORB_FUN_LGI:
+ 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
+#if 1
+ sbp_busy_timeout(sdev);
+#else
+ sbp_mgm_orb(sdev, ORB_FUN_ATS);
+#endif
+ } else {
+ /* forgot logout ? */
+ printf("login failed\n");
+ sdev->status = SBP_DEV_RESET;
+ }
+ break;
+ case ORB_FUN_RCN:
+ login_res = &sdev->login;
+ if (status_valid) {
+ sdev->status = SBP_DEV_ATTACHED;
+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
+ sbp_ping_unit(sdev);
+ xpt_release_devq(sdev->path, 1, TRUE);
+#else
+ sbp_mgm_orb(sdev, ORB_FUN_ATS);
+#endif
+ } else {
+ /* reconnection hold time exceed? */
+ printf("reconnect failed\n");
+ sbp_mgm_orb(sdev, ORB_FUN_LGI);
+ }
+ break;
+ case ORB_FUN_LGO:
+ sdev->status = SBP_DEV_RESET;
+ break;
+ case ORB_FUN_LUR:
+ case ORB_FUN_RST:
+ case ORB_FUN_ATA:
+ case ORB_FUN_ATS:
+ if (sdev->status == SBP_DEV_ATTACHED) {
+ xpt_release_devq(sdev->path, 1, TRUE);
+ } else {
+ sbp_busy_timeout(sdev);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case OCB_ACT_CMD:
+ 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;
+ }
+ }
+
+ if (!(ocb->flags & OCB_RESERVED))
+ sbp_free_ocb(sbp, ocb);
+
+/* 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.buf = malloc(12, M_SBP, M_NOWAIT | M_ZERO);
+ xfer->send.len = 12;
+ xfer->send.off = 0;
+ sfp = (struct fw_pkt *)xfer->send.buf;
+ sfp->mode.wres.dst = rfp->mode.wreqb.src;
+ xfer->dst = ntohs(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
+ fw_xfer_free(xfer);
+#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\n");
+END_DEBUG
+
+ 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*/NULL, /*alignment*/1,
+ /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/0x100000, /*nsegments*/SBP_IND_MAX,
+ /*maxsegsz*/0x8000,
+ /*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*/ SBP_QUEUE_LEN,
+ /*tagged*/0, devq);
+
+ if (sbp->sim == NULL) {
+ cam_simq_free(devq);
+ return (ENXIO);
+ }
+
+ sbp->ocb = (struct sbp_ocb *) contigmalloc(
+ sizeof (struct sbp_ocb) * SBP_NUM_OCB,
+ M_SBP, M_DONTWAIT, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
+ bzero(sbp->ocb, sizeof (struct sbp_ocb) * SBP_NUM_OCB);
+
+ if (sbp->ocb == NULL) {
+ printf("sbp0: ocb alloction failure\n");
+ return (ENOMEM);
+ }
+
+ STAILQ_INIT(&sbp->free_ocbs);
+ for (i = 0; i < SBP_NUM_OCB; i++) {
+ sbp_free_ocb(sbp, &sbp->ocb[i]);
+ }
+
+ if (xpt_bus_register(sbp->sim, /*bus*/0) != CAM_SUCCESS) {
+ cam_sim_free(sbp->sim, /*free_devq*/TRUE);
+ contigfree(sbp->ocb, sizeof (struct sbp_ocb) * SBP_NUM_OCB,
+ M_SBP);
+ return (ENXIO);
+ }
+
+ xfer = fw_xfer_alloc();
+ xfer->act.hand = sbp_recv;
+ xfer->act_type = FWACT_XFER;
+#if NEED_RESPONSE
+ xfer->fc = sbp->fd.fc;
+#endif
+ xfer->sc = (caddr_t)sbp;
+
+ 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.xfer = xfer;
+ fw_bindadd(sbp->fd.fc, &sbp->fwb);
+
+ sbp->fd.post_explore = sbp_post_explore;
+ s = splfw();
+ sbp_post_explore((void *)sbp);
+ splx(s);
+
+ 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;
+ int i;
+
+SBP_DEBUG(0)
+ printf("sbp_detach\n");
+END_DEBUG
+
+ /* bus reset for logout */
+ sbp->fd.post_explore = NULL;
+ fc->ibr(fc);
+
+ contigfree(sbp->ocb, sizeof (struct sbp_ocb) * SBP_NUM_OCB, M_SBP);
+ fw_bindremove(fc, &sbp->fwb);
+ for (i = 0; i < SBP_NUM_TARGETS; i ++)
+ sbp_detach_target(&sbp->targets[i]);
+ xpt_bus_deregister(cam_sim_path(sbp->sim));
+ bus_dma_tag_destroy(sbp->dmat);
+ return (0);
+}
+
+static void
+sbp_detach_target(struct sbp_target *target)
+{
+ int i;
+ struct sbp_dev *sdev;
+
+ if (target->luns != NULL) {
+ printf("sbp_detach_target %d\n", target->target_id);
+ for (i=0; i < target->num_lun; i++) {
+ sdev = &target->luns[i];
+ if (sdev->status == SBP_DEV_RESET ||
+ sdev->status == SBP_DEV_DEAD)
+ continue;
+ if (sdev->path)
+ 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);
+ }
+ free(target->luns, M_SBP);
+ target->luns = NULL;
+ }
+ target->fwdev = NULL;
+}
+
+static void
+sbp_timeout(void *arg)
+{
+ struct sbp_ocb *ocb = (struct sbp_ocb *)arg;
+ struct sbp_dev *sdev = ocb->sdev;
+ int s;
+
+ sbp_show_sdev_info(sdev, 2);
+ printf("request timeout ... requeue\n");
+
+ /* XXX need reset? */
+
+ s = splfw();
+ sbp_abort_ocb(ocb, CAM_CMD_TIMEOUT);
+ splx(s);
+ return;
+}
+
+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_TID_INVALID;
+ 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_TID_INVALID;
+ 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 s, speed;
+
+ 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(sbp)) == NULL) {
+ s = splfw();
+ sbp->flags |= SBP_RESOURCE_SHORTAGE;
+ splx(s);
+ 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(vtophys(ocb->ind_ptr));
+ 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) {
+ bcopy(csio->cdb_io.cdb_ptr,
+ (void *)(uintptr_t)(volatile void *)&ocb->orb[5],
+ (csio->cdb_len + 3) & ~0x3);
+ } else {
+ bcopy(&csio->cdb_io.cdb_bytes,
+ (void *)(uintptr_t)(volatile void *)&ocb->orb[5],
+ (csio->cdb_len + 3) &~0x3);
+ }
+/*
+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;
+
+ if (bus_dmamap_create(sbp->dmat, 0, &ocb->dmamap)) {
+ printf("sbp_action1: cannot create dmamap\n");
+ break;
+ }
+
+ s = splsoftvm();
+ 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);
+ } else
+ sbp_execute_ocb(ocb, NULL, 0, 0);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ struct ccb_calc_geometry *ccg;
+ u_int32_t size_mb;
+ u_int32_t secs_per_cylinder;
+ int extended = 1;
+ 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: "
+ "Volume size = %d\n",
+ device_get_nameunit(sbp->fd.dev), cam_sim_path(sbp->sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccg->volume_size);
+END_DEBUG
+
+ 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;
+ 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 = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = 0;
+ 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
+ /* Disable disconnect and tagged queuing */
+ cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID;
+ cts->flags = 0;
+
+ 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;
+ 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;
+ union ccb *ccb;
+
+ if (error)
+ printf("sbp_execute_ocb: error=%d\n", error);
+
+ ocb = (struct sbp_ocb *)arg;
+ if (seg == 1) {
+ /* direct pointer */
+ ocb->orb[3] = htonl(segments[0].ds_addr);
+ ocb->orb[4] |= htonl(segments[0].ds_len);
+ } else if(seg > 1) {
+ /* page table */
+SBP_DEBUG(1)
+ printf("sbp_execute_ocb: seg %d", seg);
+ for (i = 0; i < seg; i++)
+ printf(", %x:%d", segments[i].ds_addr,
+ segments[i].ds_len);
+ printf("\n");
+END_DEBUG
+ for (i = 0; i < seg; i++) {
+ if (segments[i].ds_len < 16)
+ printf("sbp_execute_ocb: segment length is "
+ "less than 16.\n");
+ ocb->ind_ptr[i].hi = htonl(segments[i].ds_len << 16);
+ ocb->ind_ptr[i].lo = htonl(segments[i].ds_addr);
+ }
+ ocb->orb[4] |= htonl(ORB_CMD_PTBL | seg);
+ }
+
+ ccb = ocb->ccb;
+ prev = sbp_enqueue_ocb(ocb->sdev, ocb);
+ if (prev)
+ sbp_doorbell(ocb->sdev);
+ else
+ 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, u_int32_t orb_lo)
+{
+ 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)
+ printf("orb: 0x%x next: 0x%x, flags %x\n",
+ vtophys(&ocb->orb[0]), ntohl(ocb->orb[1]), flags);
+END_DEBUG
+ if (vtophys(&ocb->orb[0]) == orb_lo) {
+ /* found */
+ if (ocb->flags & OCB_RESERVED)
+ ocb->flags |= OCB_DONE;
+ else
+ 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 (ocb->dmamap != NULL) {
+ bus_dmamap_destroy(sdev->target->sbp->dmat,
+ ocb->dmamap);
+ ocb->dmamap = NULL;
+ }
+ break;
+ } else {
+ if ((ocb->flags & OCB_RESERVED) &&
+ (ocb->flags & OCB_DONE)) {
+ /* next orb must be fetched already */
+ STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb);
+ sbp_free_ocb(sdev->target->sbp, ocb);
+ } 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);
+ printf("sbp_enqueue_ocb orb=0x%x in physical memory\n", vtophys(&ocb->orb[0]));
+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
+ && ((prev->flags & OCB_ACT_MASK) == OCB_ACT_CMD)
+ && ((ocb->flags & OCB_ACT_MASK) == OCB_ACT_CMD)) {
+SBP_DEBUG(1)
+ printf("linking chain 0x%x -> 0x%x\n", vtophys(&prev->orb[0]),
+ vtophys(&ocb->orb[0]));
+END_DEBUG
+ prev->flags |= OCB_RESERVED;
+ prev->orb[1] = htonl(vtophys(&ocb->orb[0]));
+ prev->orb[0] = 0;
+ } else {
+ prev = NULL;
+ }
+ splx(s);
+
+ return prev;
+}
+
+static struct sbp_ocb *
+sbp_get_ocb(struct sbp_softc *sbp)
+{
+ struct sbp_ocb *ocb;
+ int s = splfw();
+ ocb = STAILQ_FIRST(&sbp->free_ocbs);
+ if (ocb == NULL) {
+ printf("ocb shortage!!!\n");
+ return NULL;
+ }
+ STAILQ_REMOVE(&sbp->free_ocbs, ocb, sbp_ocb, ocb);
+ splx(s);
+ ocb->ccb = NULL;
+ return (ocb);
+}
+
+static void
+sbp_free_ocb(struct sbp_softc *sbp, struct sbp_ocb *ocb)
+{
+#if 0 /* XXX make sure that ocb has ccb */
+ if ((sbp->flags & SBP_RESOURCE_SHORTAGE) != 0 &&
+ (ocb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
+ ocb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ sbp->flags &= ~SBP_RESOURCE_SHORTAGE;
+ }
+#else
+ if ((sbp->flags & SBP_RESOURCE_SHORTAGE) != 0)
+ sbp->flags &= ~SBP_RESOURCE_SHORTAGE;
+#endif
+ ocb->flags = 0;
+ ocb->ccb = NULL;
+ STAILQ_INSERT_TAIL(&sbp->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);
+ printf("sbp_abort_ocb 0x%x\n", status);
+END_DEBUG
+ STAILQ_REMOVE(&sdev->ocbs, ocb, sbp_ocb, ocb);
+ if (ocb->ccb != NULL && !(ocb->flags & OCB_DONE)) {
+ if (status != CAM_CMD_TIMEOUT)
+ untimeout(sbp_timeout, (caddr_t)ocb,
+ ocb->ccb->ccb_h.timeout_ch);
+ ocb->ccb->ccb_h.status = status;
+ xpt_done(ocb->ccb);
+ }
+ if (ocb->dmamap != NULL) {
+ bus_dmamap_destroy(sdev->target->sbp->dmat, ocb->dmamap);
+ ocb->dmamap = NULL;
+ }
+ sbp_free_ocb(sdev->target->sbp, ocb);
+}
+
+static void
+sbp_abort_all_ocbs(struct sbp_dev *sdev, int status)
+{
+ int s;
+ struct sbp_ocb *ocb;
+
+ s = splfw();
+ while ((ocb = STAILQ_FIRST(&sdev->ocbs))) {
+ 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),
+
+ { 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);
diff --git a/sys/modules/firewire/Makefile b/sys/modules/firewire/Makefile
new file mode 100644
index 0000000..3cbed99
--- /dev/null
+++ b/sys/modules/firewire/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+SUBDIR =
+SUBDIR += firewire
+SUBDIR += sbp
+#SUBDIR += fwe
+
+.include <bsd.subdir.mk>
diff --git a/sys/modules/firewire/firewire/Makefile b/sys/modules/firewire/firewire/Makefile
new file mode 100644
index 0000000..522cfad
--- /dev/null
+++ b/sys/modules/firewire/firewire/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+# Makefile for the IEEE1394 OHCI chipset
+
+.PATH: ${.CURDIR}/../../../dev/firewire
+
+KMOD= firewire
+SRCS = bus_if.h device_if.h pci_if.h \
+ opt_bus.h opt_firewire.h \
+ firewire.c firewire.h firewire_phy.h firewirebusreg.h firewirereg.h \
+ fwohci.c fwohci_pci.c fwohcireg.h fwohcivar.h \
+ iec13213.h iec68113.h \
+ fwmem.c fwmem.h
+
+opt_firewire.h:
+ echo "#define FIREWIRE_FREEBSD_MODULE 1" > opt_firewire.h
+
+#EXPORT_SYMS= fw_asybusy \
+# fw_asyreq \
+# fw_bindadd \
+# fw_bindremove \
+# getcsrdata \
+# fw_xfer_alloc \
+# fw_xfer_free \
+
+EXPORT_SYMS= YES
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/firewire/fwe/Makefile b/sys/modules/firewire/fwe/Makefile
new file mode 100644
index 0000000..551299d
--- /dev/null
+++ b/sys/modules/firewire/fwe/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+# Makefile for the SBP-II (Serial Bus Protocol 2/SCSI over IEEE1394)
+
+.PATH: ${.CURDIR}/../../../dev/firewire
+
+CFLAGS+= -g
+
+KMOD = if_fwe
+SRCS = bus_if.h device_if.h\
+ opt_bus.h opt_firewire.h opt_inet.h\
+ if_fwe.c if_fwevar.h\
+ firewire.h firewirereg.h
+
+opt_fwe.h:
+ echo "#define FIREWIRE_ETHEREMU_FREEBSD 1" > opt_fwe.h
+
+.include <bsd.kmod.mk>
+
diff --git a/sys/modules/firewire/sbp/Makefile b/sys/modules/firewire/sbp/Makefile
new file mode 100644
index 0000000..3e08db5
--- /dev/null
+++ b/sys/modules/firewire/sbp/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+# Makefile for the SBP-II (Serial Bus Protocol 2/SCSI over IEEE1394)
+
+.PATH: ${.CURDIR}/../../../dev/firewire
+
+CFLAGS+= -g
+
+KMOD = sbp
+SRCS = bus_if.h device_if.h\
+ opt_bus.h opt_firewire.h opt_cam.h opt_scsi.h\
+ sbp.c \
+ firewire.h firewirereg.h \
+ iec13213.h
+
+opt_sbp.h:
+ echo "#define SBP2_FREEBSD_MODULE 1" > opt_sbp.h
+
+.include <bsd.kmod.mk>
+
OpenPOWER on IntegriCloud