diff options
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> + |