diff options
author | dg <dg@FreeBSD.org> | 1995-03-14 09:16:07 +0000 |
---|---|---|
committer | dg <dg@FreeBSD.org> | 1995-03-14 09:16:07 +0000 |
commit | 68d73d5130f963a87bdedcdb3f5394b24aa5c625 (patch) | |
tree | fed6b95624cd94f06f0197d39cc53a6dd6ce2e61 | |
parent | 070760fddb4ae1da9706f541b233a655df351177 (diff) | |
download | FreeBSD-src-68d73d5130f963a87bdedcdb3f5394b24aa5c625.zip FreeBSD-src-68d73d5130f963a87bdedcdb3f5394b24aa5c625.tar.gz |
Added support for generic FDDI and the DEC DEFEA and DEFPA FDDI adapters.
Submitted by: Matt Thomas
-rw-r--r-- | sys/conf/NOTES | 16 | ||||
-rw-r--r-- | sys/conf/files | 5 | ||||
-rw-r--r-- | sys/i386/conf/LINT | 16 | ||||
-rw-r--r-- | sys/i386/conf/NOTES | 16 | ||||
-rw-r--r-- | sys/net/bpf.c | 7 | ||||
-rw-r--r-- | sys/net/fddi.h | 84 | ||||
-rw-r--r-- | sys/net/if_fddisubr.c | 567 | ||||
-rw-r--r-- | sys/netinet/if_fddi.h | 84 | ||||
-rw-r--r-- | sys/pci/if_pdq.c | 844 | ||||
-rw-r--r-- | sys/pci/pdq.c | 1558 | ||||
-rw-r--r-- | sys/pci/pdq_os.h | 198 | ||||
-rw-r--r-- | sys/pci/pdqreg.h | 1081 |
12 files changed, 4463 insertions, 13 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index d8dca2f..1b34a50 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.154 1995/03/12 23:43:14 swallace Exp $ +# $Id: LINT,v 1.155 1995/03/13 18:49:24 swallace Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -144,6 +144,7 @@ options TPCONS #ISO TP class 0 over X.25 # The `ether' pseudo-device provides generic code to handle # Ethernets; it is mandatory when a Ethernet device driver is # configured. +# The 'fddi' pseudo-device provides generic code to support FDDI. # The `sppp' pseudo-device serves a similar role for certain types # of synchronous PPP links (like `cx'). # The `sl' pseudo-device implements the Serial Line IP (SLIP) service. @@ -158,6 +159,7 @@ options TPCONS #ISO TP class 0 over X.25 # The `tun' pseudo-device implements the User Process PPP (iijppp) # pseudo-device ether #Generic Ethernet +pseudo-device fddi #Generic FDDI pseudo-device sppp #Generic Synchronous PPP pseudo-device loop #Network loopback device pseudo-device sl 2 #Serial Line IP @@ -492,6 +494,7 @@ options DSI_SOFT_MODEM #code for DSI Softmodems # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # el: 3Com 3C501 (slow!) # ep: 3Com 3C509 (buggy) +# fea: DEC DEFEA EISA FDDI adapter # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210 # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) @@ -504,11 +507,12 @@ options DSI_SOFT_MODEM #code for DSI Softmodems device cx0 at isa? port 0x240 net irq 15 drq 7 vector cxintr device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr -device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr -device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr -device ep0 at isa? port 0x300 net irq 10 vector epintr device el0 at isa? port 0x300 net irq 9 vector elintr +device ep0 at isa? port 0x300 net irq 10 vector epintr +device fea0 at isa? net irq ? vector feaintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 vector le_intr +device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 vector zpintr @@ -626,10 +630,14 @@ device apm0 at isa? # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # +# The `fpa' device provides support for the Digital DEFPA PCI FDDI +# adapter. pseudo-device fddi is also needed. +# # The PROBE_VERBOSE option enables a long listing of chip set registers # for supported PCI chip sets (currently only intel Saturn and Mercury). # controller pci0 device ncr0 device de0 +device fpa0 options PROBE_VERBOSE diff --git a/sys/conf/files b/sys/conf/files index 3b1180c..0a3f7b4 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -147,6 +147,7 @@ net/bpf_filter.c optional bpfilter net/if.c standard net/if_disc.c optional disc net/if_ethersubr.c optional ether +net/if_fddisubr.c optional fddi net/if_loop.c optional loop net/if_ppp.c optional ppp net/if_sl.c optional sl @@ -277,6 +278,10 @@ pci/if_de.c optional de device-driver pci/ncr.c optional ncr device-driver pci/pci.c optional pci device-driver pci/pcisupport.c optional pci +pci/if_pdq.c optional fea device-driver +pci/pdq.c optional fea device-driver +pci/if_pdq.c optional fpa device-driver +pci/pdq.c optional fpa device-driver scsi/cd.c optional cd scsi/ch.c optional ch scsi/scsi_base.c optional scbus diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index d8dca2f..1b34a50 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.154 1995/03/12 23:43:14 swallace Exp $ +# $Id: LINT,v 1.155 1995/03/13 18:49:24 swallace Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -144,6 +144,7 @@ options TPCONS #ISO TP class 0 over X.25 # The `ether' pseudo-device provides generic code to handle # Ethernets; it is mandatory when a Ethernet device driver is # configured. +# The 'fddi' pseudo-device provides generic code to support FDDI. # The `sppp' pseudo-device serves a similar role for certain types # of synchronous PPP links (like `cx'). # The `sl' pseudo-device implements the Serial Line IP (SLIP) service. @@ -158,6 +159,7 @@ options TPCONS #ISO TP class 0 over X.25 # The `tun' pseudo-device implements the User Process PPP (iijppp) # pseudo-device ether #Generic Ethernet +pseudo-device fddi #Generic FDDI pseudo-device sppp #Generic Synchronous PPP pseudo-device loop #Network loopback device pseudo-device sl 2 #Serial Line IP @@ -492,6 +494,7 @@ options DSI_SOFT_MODEM #code for DSI Softmodems # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # el: 3Com 3C501 (slow!) # ep: 3Com 3C509 (buggy) +# fea: DEC DEFEA EISA FDDI adapter # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210 # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) @@ -504,11 +507,12 @@ options DSI_SOFT_MODEM #code for DSI Softmodems device cx0 at isa? port 0x240 net irq 15 drq 7 vector cxintr device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr -device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr -device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr -device ep0 at isa? port 0x300 net irq 10 vector epintr device el0 at isa? port 0x300 net irq 9 vector elintr +device ep0 at isa? port 0x300 net irq 10 vector epintr +device fea0 at isa? net irq ? vector feaintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 vector le_intr +device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 vector zpintr @@ -626,10 +630,14 @@ device apm0 at isa? # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # +# The `fpa' device provides support for the Digital DEFPA PCI FDDI +# adapter. pseudo-device fddi is also needed. +# # The PROBE_VERBOSE option enables a long listing of chip set registers # for supported PCI chip sets (currently only intel Saturn and Mercury). # controller pci0 device ncr0 device de0 +device fpa0 options PROBE_VERBOSE diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index d8dca2f..1b34a50 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.154 1995/03/12 23:43:14 swallace Exp $ +# $Id: LINT,v 1.155 1995/03/13 18:49:24 swallace Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -144,6 +144,7 @@ options TPCONS #ISO TP class 0 over X.25 # The `ether' pseudo-device provides generic code to handle # Ethernets; it is mandatory when a Ethernet device driver is # configured. +# The 'fddi' pseudo-device provides generic code to support FDDI. # The `sppp' pseudo-device serves a similar role for certain types # of synchronous PPP links (like `cx'). # The `sl' pseudo-device implements the Serial Line IP (SLIP) service. @@ -158,6 +159,7 @@ options TPCONS #ISO TP class 0 over X.25 # The `tun' pseudo-device implements the User Process PPP (iijppp) # pseudo-device ether #Generic Ethernet +pseudo-device fddi #Generic FDDI pseudo-device sppp #Generic Synchronous PPP pseudo-device loop #Network loopback device pseudo-device sl 2 #Serial Line IP @@ -492,6 +494,7 @@ options DSI_SOFT_MODEM #code for DSI Softmodems # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # el: 3Com 3C501 (slow!) # ep: 3Com 3C509 (buggy) +# fea: DEC DEFEA EISA FDDI adapter # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210 # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) @@ -504,11 +507,12 @@ options DSI_SOFT_MODEM #code for DSI Softmodems device cx0 at isa? port 0x240 net irq 15 drq 7 vector cxintr device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr -device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr -device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr -device ep0 at isa? port 0x300 net irq 10 vector epintr device el0 at isa? port 0x300 net irq 9 vector elintr +device ep0 at isa? port 0x300 net irq 10 vector epintr +device fea0 at isa? net irq ? vector feaintr +device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 vector le_intr +device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 vector zpintr @@ -626,10 +630,14 @@ device apm0 at isa? # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # +# The `fpa' device provides support for the Digital DEFPA PCI FDDI +# adapter. pseudo-device fddi is also needed. +# # The PROBE_VERBOSE option enables a long listing of chip set registers # for supported PCI chip sets (currently only intel Saturn and Mercury). # controller pci0 device ncr0 device de0 +device fpa0 options PROBE_VERBOSE diff --git a/sys/net/bpf.c b/sys/net/bpf.c index 5ad17a1..b10782d 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -37,7 +37,7 @@ * * @(#)bpf.c 8.2 (Berkeley) 3/28/94 * - * $Id: bpf.c,v 1.3 1994/08/20 03:48:55 davidg Exp $ + * $Id: bpf.c,v 1.4 1994/10/09 07:35:03 davidg Exp $ */ #include "bpfilter.h" @@ -168,9 +168,14 @@ bpf_movein(uio, linktype, mp, sockp, datlen) break; case DLT_FDDI: +#if defined(__FreeBSD__) || defined(__bsdi__) + sockp->sa_family = AF_IMPLINK; + hlen = 0; +#else sockp->sa_family = AF_UNSPEC; /* XXX 4(FORMAC)+6(dst)+6(src)+3(LLC)+5(SNAP) */ hlen = 24; +#endif break; case DLT_NULL: diff --git a/sys/net/fddi.h b/sys/net/fddi.h new file mode 100644 index 0000000..a8efc5a --- /dev/null +++ b/sys/net/fddi.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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. + * + * @(#)if_fddi.h 8.1 (Berkeley) 6/10/93 + * $Id: if_fddi.h,v 1.1 1995/03/10 17:43:45 thomas Exp $ + */ + +#ifndef _NETINET_IF_FDDI_H_ +#define _NETINET_IF_FDDI_H_ + +/* + * Structure of an 100Mb/s FDDI header. + */ +struct fddi_header { + u_char fddi_fc; + u_char fddi_dhost[6]; + u_char fddi_shost[6]; +}; + +#define FDDIMTU 4470 +#define FDDIMIN 3 + +#define FDDIFC_C 0x80 /* 0b10000000 */ +#define FDDIFC_L 0x40 /* 0b01000000 */ +#define FDDIFC_F 0x30 /* 0b00110000 */ +#define FDDIFC_Z 0x0F /* 0b00001111 */ + +#define FDDIFC_LLC_ASYNC 0x50 +#define FDDIFC_LLC_PRIO0 0 +#define FDDIFC_LLC_PRIO1 1 +#define FDDIFC_LLC_PRIO2 2 +#define FDDIFC_LLC_PRIO3 3 +#define FDDIFC_LLC_PRIO4 4 +#define FDDIFC_LLC_PRIO5 5 +#define FDDIFC_LLC_PRIO6 6 +#define FDDIFC_LLC_PRIO7 7 +#define FDDIFC_LLC_SYNC 0xd0 +#define FDDIFC_SMT 0x40 + +#ifdef KERNEL +#define fddibroadcastaddr etherbroadcastaddr +#define fddi_ipmulticast_min ether_ipmulticast_min +#define fddi_ipmulticast_max ether_ipmulticast_max +#define fddi_addmulti ether_addmulti +#define fddi_delmulti ether_delmulti +#define fddi_sprintf ether_sprintf + +void fddi_ifattach __P((struct ifnet *)); +void fddi_input __P((struct ifnet *, struct fddi_header *, struct mbuf *)); +int fddi_output __P((struct ifnet *, + struct mbuf *, struct sockaddr *, struct rtentry *)); + +#endif + +#endif diff --git a/sys/net/if_fddisubr.c b/sys/net/if_fddisubr.c new file mode 100644 index 0000000..5eae14b --- /dev/null +++ b/sys/net/if_fddisubr.c @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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. + * + * @(#)if_fddisubr.c 8.1 (Berkeley) 6/10/93 + * if_fddisubr.c,v 1.5 1994/12/13 22:31:45 wollman Exp + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <machine/cpu.h> + +#include <net/if.h> +#include <net/netisr.h> +#include <net/route.h> +#include <net/if_llc.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_var.h> +#endif +#include <netinet/if_ether.h> +#include <netinet/if_fddi.h> + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#ifdef DECNET +#include <netdnet/dn.h> +#endif + +#ifdef ISO +#include <netiso/argo_debug.h> +#include <netiso/iso.h> +#include <netiso/iso_var.h> +#include <netiso/iso_snpac.h> +#endif + +#include "bpfilter.h" + +#ifdef LLC +#include <netccitt/dll.h> +#include <netccitt/llc_var.h> +#endif + +#if defined(LLC) && defined(CCITT) +extern struct ifqueue pkintrq; +#endif + +extern struct ifnet loif; +#define senderr(e) { error = (e); goto bad;} + +/* + * This really should be defined in if_llc.h but in case it isn't. + */ +#ifndef llc_snap +#define llc_snap llc_un.type_snap +#endif + +#ifdef __bsdi__ +#define RTALLOC1(a, b) rtalloc1(a, b) +#define ARPRESOLVE(a, b, c, d, e, f) arpresolve(a, b, c, d, e) +#else +#define RTALLOC1(a, b) rtalloc1(a, b, 0UL) +#define ARPRESOLVE(a, b, c, d, e, f) arpresolve(a, b, c, d, e, f) +#endif +/* + * FDDI output routine. + * Encapsulate a packet of type family for the local net. + * Use trailer local net encapsulation if enough data in first + * packet leaves a multiple of 512 bytes of data in remainder. + * Assumes that ifp is actually pointer to arpcom structure. + */ +int +fddi_output(ifp, m0, dst, rt0) + register struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; + struct rtentry *rt0; +{ + short type; + int s, error = 0; + u_char edst[6]; + register struct mbuf *m = m0; + register struct rtentry *rt; + struct mbuf *mcopy = (struct mbuf *)0; + register struct fddi_header *fh; + struct arpcom *ac = (struct arpcom *)ifp; + + if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + senderr(ENETDOWN); + ifp->if_lastchange = time; + if (rt = rt0) { + if ((rt->rt_flags & RTF_UP) == 0) { + if (rt0 = rt = RTALLOC1(dst, 1)) + rt->rt_refcnt--; + else + senderr(EHOSTUNREACH); + } + if (rt->rt_flags & RTF_GATEWAY) { + if (rt->rt_gwroute == 0) + goto lookup; + if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { + rtfree(rt); rt = rt0; + lookup: rt->rt_gwroute = RTALLOC1(rt->rt_gateway, 1); + if ((rt = rt->rt_gwroute) == 0) + senderr(EHOSTUNREACH); + } + } + if (rt->rt_flags & RTF_REJECT) + if (rt->rt_rmx.rmx_expire == 0 || + time.tv_sec < rt->rt_rmx.rmx_expire) + senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); + } + switch (dst->sa_family) { + +#ifdef INET + case AF_INET: + if (!ARPRESOLVE(ac, rt, m, dst, edst, rt0)) + return (0); /* if not yet resolved */ + /* If broadcasting on a simplex interface, loopback a copy */ + if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)) + mcopy = m_copy(m, 0, (int)M_COPYALL); + type = ETHERTYPE_IP; + break; +#endif +#ifdef NS + case AF_NS: + type = ETHERTYPE_NS; + bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), + (caddr_t)edst, sizeof (edst)); + if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, sizeof(edst))) + return (looutput(ifp, m, dst, rt)); + /* If broadcasting on a simplex interface, loopback a copy */ + if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX)) + mcopy = m_copy(m, 0, (int)M_COPYALL); + break; +#endif +#ifdef ISO + case AF_ISO: { + int snpalen; + struct llc *l; + register struct sockaddr_dl *sdl; + + if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway) && + sdl->sdl_family == AF_LINK && sdl->sdl_alen > 0) { + bcopy(LLADDR(sdl), (caddr_t)edst, sizeof(edst)); + } else if (error = + iso_snparesolve(ifp, (struct sockaddr_iso *)dst, + (char *)edst, &snpalen)) + goto bad; /* Not Resolved */ + /* If broadcasting on a simplex interface, loopback a copy */ + if (*edst & 1) + m->m_flags |= (M_BCAST|M_MCAST); + if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) && + (mcopy = m_copy(m, 0, (int)M_COPYALL))) { + M_PREPEND(mcopy, sizeof (*fh), M_DONTWAIT); + if (mcopy) { + fh = mtod(mcopy, struct fddi_header *); + bcopy((caddr_t)edst, + (caddr_t)fh->fddi_dhost, sizeof (edst)); + bcopy((caddr_t)ac->ac_enaddr, + (caddr_t)fh->fddi_shost, sizeof (edst)); + } + } + M_PREPEND(m, 3, M_DONTWAIT); + if (m == NULL) + return (0); + type = 0; + l = mtod(m, struct llc *); + l->llc_dsap = l->llc_ssap = LLC_ISO_LSAP; + l->llc_control = LLC_UI; + IFDEBUG(D_ETHER) + int i; + printf("unoutput: sending pkt to: "); + for (i=0; i<6; i++) + printf("%x ", edst[i] & 0xff); + printf("\n"); + ENDDEBUG + } break; +#endif /* ISO */ +#ifdef LLC +/* case AF_NSAP: */ + case AF_CCITT: { + register struct sockaddr_dl *sdl = + (struct sockaddr_dl *) rt -> rt_gateway; + + if (sdl && sdl->sdl_family == AF_LINK + && sdl->sdl_alen > 0) { + bcopy(LLADDR(sdl), (char *)edst, + sizeof(edst)); + } else goto bad; /* Not a link interface ? Funny ... */ + if ((ifp->if_flags & IFF_SIMPLEX) && (*edst & 1) && + (mcopy = m_copy(m, 0, (int)M_COPYALL))) { + M_PREPEND(mcopy, sizeof (*fh), M_DONTWAIT); + if (mcopy) { + fh = mtod(mcopy, struct fddi_header *); + bcopy((caddr_t)edst, + (caddr_t)fh->fddi_dhost, sizeof (edst)); + bcopy((caddr_t)ac->ac_enaddr, + (caddr_t)fh->fddi_shost, sizeof (edst)); + fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4; + } + } + type = 0; +#ifdef LLC_DEBUG + { + int i; + register struct llc *l = mtod(m, struct llc *); + + printf("fddi_output: sending LLC2 pkt to: "); + for (i=0; i<6; i++) + printf("%x ", edst[i] & 0xff); + printf(" len 0x%x dsap 0x%x ssap 0x%x control 0x%x\n", + type & 0xff, l->llc_dsap & 0xff, l->llc_ssap &0xff, + l->llc_control & 0xff); + + } +#endif /* LLC_DEBUG */ + } break; +#endif /* LLC */ + + case AF_UNSPEC: + { + struct ether_header *eh; + eh = (struct ether_header *)dst->sa_data; + bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); + if (*edst & 1) + m->m_flags |= (M_BCAST|M_MCAST); + type = eh->ether_type; + break; + } + +#if NBPFILTER > 0 + case AF_IMPLINK: + { + fh = mtod(m, struct fddi_header *); + error = EPROTONOSUPPORT; + switch (fh->fddi_fc & (FDDIFC_C|FDDIFC_L|FDDIFC_F)) { + case FDDIFC_LLC_ASYNC: { + /* legal priorities are 0 through 7 */ + if ((fh->fddi_fc & FDDIFC_Z) > 7) + goto bad; + break; + } + case FDDIFC_LLC_SYNC: { + /* FDDIFC_Z bits reserved, must be zero */ + if (fh->fddi_fc & FDDIFC_Z) + goto bad; + break; + } + case FDDIFC_SMT: { + /* FDDIFC_Z bits must be non zero */ + if ((fh->fddi_fc & FDDIFC_Z) == 0) + goto bad; + break; + } + default: { + /* anything else is too dangerous */ + goto bad; + } + } + error = 0; + if (fh->fddi_dhost[0] & 1) + m->m_flags |= (M_BCAST|M_MCAST); + goto queue_it; + } +#endif + default: + printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, + dst->sa_family); + senderr(EAFNOSUPPORT); + } + + + if (mcopy) + (void) looutput(ifp, mcopy, dst, rt); + if (type != 0) { + register struct llc *l; + M_PREPEND(m, sizeof (struct llc), M_DONTWAIT); + if (m == 0) + senderr(ENOBUFS); + l = mtod(m, struct llc *); + l->llc_control = LLC_UI; + l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP; + l->llc_snap.org_code[0] = l->llc_snap.org_code[1] = l->llc_snap.org_code[2] = 0; + type = ntohs(type); + bcopy((caddr_t) &type, (caddr_t) &l->llc_snap.ether_type, + sizeof(u_short)); + } + /* + * Add local net header. If no space in first mbuf, + * allocate another. + */ + M_PREPEND(m, sizeof (struct fddi_header), M_DONTWAIT); + if (m == 0) + senderr(ENOBUFS); + fh = mtod(m, struct fddi_header *); + fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4; + bcopy((caddr_t)edst, (caddr_t)fh->fddi_dhost, sizeof (edst)); + queue_it: + bcopy((caddr_t)ac->ac_enaddr, (caddr_t)fh->fddi_shost, + sizeof(fh->fddi_shost)); + s = splimp(); + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + if (IF_QFULL(&ifp->if_snd)) { + IF_DROP(&ifp->if_snd); + splx(s); + senderr(ENOBUFS); + } + ifp->if_obytes += m->m_pkthdr.len; + IF_ENQUEUE(&ifp->if_snd, m); + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + if (m->m_flags & M_MCAST) + ifp->if_omcasts++; + return (error); + +bad: + if (m) + m_freem(m); + return (error); +} + +/* + * Process a received FDDI packet; + * the packet is in the mbuf chain m without + * the fddi header, which is provided separately. + */ +void +fddi_input(ifp, fh, m) + struct ifnet *ifp; + register struct fddi_header *fh; + struct mbuf *m; +{ + register struct ifqueue *inq; + register struct llc *l; + int s; + + if ((ifp->if_flags & IFF_UP) == 0) { + m_freem(m); + return; + } + ifp->if_lastchange = time; + ifp->if_ibytes += m->m_pkthdr.len + sizeof (*fh); + if (bcmp((caddr_t)fddibroadcastaddr, (caddr_t)fh->fddi_dhost, + sizeof(fddibroadcastaddr)) == 0) + m->m_flags |= M_BCAST; + else if (fh->fddi_dhost[0] & 1) + m->m_flags |= M_MCAST; + if (m->m_flags & (M_BCAST|M_MCAST)) + ifp->if_imcasts++; + + l = mtod(m, struct llc *); + switch (l->llc_dsap) { +#if defined(INET) || defined(NS) || defined(DECNET) + case LLC_SNAP_LSAP: + { + unsigned fddi_type; + if (l->llc_control != LLC_UI || l->llc_ssap != LLC_SNAP_LSAP) + goto dropanyway; + if (l->llc_snap.org_code[0] != 0 || l->llc_snap.org_code[1] != 0|| l->llc_snap.org_code[2] != 0) + goto dropanyway; + fddi_type = ntohs(l->llc_snap.ether_type); + m_adj(m, 8); + switch (fddi_type) { +#ifdef INET + case ETHERTYPE_IP: + schednetisr(NETISR_IP); + inq = &ipintrq; + break; + + case ETHERTYPE_ARP: + schednetisr(NETISR_ARP); + inq = &arpintrq; + break; +#endif +#ifdef NS + case ETHERTYPE_NS: + schednetisr(NETISR_NS); + inq = &nsintrq; + break; +#endif +#ifdef DECNET + case ETHERTYPE_DECENT: + schednetisr(NETISR_DECNET); + inq = &decnetintrq; + break; +#endif + default: + printf("fddi_input: unknown protocol 0x%x\n", fddi_type); + ifp->if_noproto++; + goto dropanyway; + } + break; + } +#endif /* INET || NS */ +#ifdef ISO + case LLC_ISO_LSAP: + switch (l->llc_control) { + case LLC_UI: + /* LLC_UI_P forbidden in class 1 service */ + if ((l->llc_dsap == LLC_ISO_LSAP) && + (l->llc_ssap == LLC_ISO_LSAP)) { + /* LSAP for ISO */ + m->m_data += 3; /* XXX */ + m->m_len -= 3; /* XXX */ + m->m_pkthdr.len -= 3; /* XXX */ + M_PREPEND(m, sizeof *fh, M_DONTWAIT); + if (m == 0) + return; + *mtod(m, struct fddi_header *) = *fh; + IFDEBUG(D_ETHER) + printf("clnp packet"); + ENDDEBUG + schednetisr(NETISR_ISO); + inq = &clnlintrq; + break; + } + goto dropanyway; + + case LLC_XID: + case LLC_XID_P: + if(m->m_len < 6) + goto dropanyway; + l->llc_window = 0; + l->llc_fid = 9; + l->llc_class = 1; + l->llc_dsap = l->llc_ssap = 0; + /* Fall through to */ + case LLC_TEST: + case LLC_TEST_P: + { + struct sockaddr sa; + register struct ether_header *eh2; + int i; + u_char c = l->llc_dsap; + + l->llc_dsap = l->llc_ssap; + l->llc_ssap = c; + if (m->m_flags & (M_BCAST | M_MCAST)) + bcopy((caddr_t)ac->ac_enaddr, + (caddr_t)eh->ether_dhost, 6); + sa.sa_family = AF_UNSPEC; + sa.sa_len = sizeof(sa); + eh2 = (struct ether_header *)sa.sa_data; + for (i = 0; i < 6; i++) { + eh2->ether_shost[i] = c = eh->fddi_dhost[i]; + eh2->ether_dhost[i] = + eh->ether_dhost[i] = eh->fddi_shost[i]; + eh2->ether_shost[i] = c; + } + eh2->ether_type = 0; + ifp->if_output(ifp, m, &sa, NULL); + return; + } + default: + m_freem(m); + return; + } + break; +#endif /* ISO */ +#ifdef LLC + case LLC_X25_LSAP: + { + M_PREPEND(m, sizeof(struct sdl_hdr) , M_DONTWAIT); + if (m == 0) + return; + if ( !sdl_sethdrif(ifp, fh->fddi_shost, LLC_X25_LSAP, + fh->fddi_dhost, LLC_X25_LSAP, 6, + mtod(m, struct sdl_hdr *))) + panic("ETHER cons addr failure"); + mtod(m, struct sdl_hdr *)->sdlhdr_len = m->m_pkthdr.len - sizeof(struct sdl_hdr); +#ifdef LLC_DEBUG + printf("llc packet\n"); +#endif /* LLC_DEBUG */ + schednetisr(NETISR_CCITT); + inq = &llcintrq; + break; + } +#endif /* LLC */ + + default: + printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); + ifp->if_noproto++; + dropanyway: + m_freem(m); + return; + } + + s = splimp(); + if (IF_QFULL(inq)) { + IF_DROP(inq); + m_freem(m); + } else + IF_ENQUEUE(inq, m); + splx(s); +} +/* + * Perform common duties while attaching to interface list + */ +void +fddi_ifattach(ifp) + register struct ifnet *ifp; +{ + register struct ifaddr *ifa; + register struct sockaddr_dl *sdl; + + ifp->if_type = IFT_FDDI; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 21; + ifp->if_mtu = FDDIMTU; + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && + sdl->sdl_family == AF_LINK) { + sdl->sdl_type = IFT_FDDI; + sdl->sdl_alen = ifp->if_addrlen; + bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr, + LLADDR(sdl), ifp->if_addrlen); + break; + } +} diff --git a/sys/netinet/if_fddi.h b/sys/netinet/if_fddi.h new file mode 100644 index 0000000..a8efc5a --- /dev/null +++ b/sys/netinet/if_fddi.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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. + * + * @(#)if_fddi.h 8.1 (Berkeley) 6/10/93 + * $Id: if_fddi.h,v 1.1 1995/03/10 17:43:45 thomas Exp $ + */ + +#ifndef _NETINET_IF_FDDI_H_ +#define _NETINET_IF_FDDI_H_ + +/* + * Structure of an 100Mb/s FDDI header. + */ +struct fddi_header { + u_char fddi_fc; + u_char fddi_dhost[6]; + u_char fddi_shost[6]; +}; + +#define FDDIMTU 4470 +#define FDDIMIN 3 + +#define FDDIFC_C 0x80 /* 0b10000000 */ +#define FDDIFC_L 0x40 /* 0b01000000 */ +#define FDDIFC_F 0x30 /* 0b00110000 */ +#define FDDIFC_Z 0x0F /* 0b00001111 */ + +#define FDDIFC_LLC_ASYNC 0x50 +#define FDDIFC_LLC_PRIO0 0 +#define FDDIFC_LLC_PRIO1 1 +#define FDDIFC_LLC_PRIO2 2 +#define FDDIFC_LLC_PRIO3 3 +#define FDDIFC_LLC_PRIO4 4 +#define FDDIFC_LLC_PRIO5 5 +#define FDDIFC_LLC_PRIO6 6 +#define FDDIFC_LLC_PRIO7 7 +#define FDDIFC_LLC_SYNC 0xd0 +#define FDDIFC_SMT 0x40 + +#ifdef KERNEL +#define fddibroadcastaddr etherbroadcastaddr +#define fddi_ipmulticast_min ether_ipmulticast_min +#define fddi_ipmulticast_max ether_ipmulticast_max +#define fddi_addmulti ether_addmulti +#define fddi_delmulti ether_delmulti +#define fddi_sprintf ether_sprintf + +void fddi_ifattach __P((struct ifnet *)); +void fddi_input __P((struct ifnet *, struct fddi_header *, struct mbuf *)); +int fddi_output __P((struct ifnet *, + struct mbuf *, struct sockaddr *, struct rtentry *)); + +#endif + +#endif diff --git a/sys/pci/if_pdq.c b/sys/pci/if_pdq.c new file mode 100644 index 0000000..609a541 --- /dev/null +++ b/sys/pci/if_pdq.c @@ -0,0 +1,844 @@ +/*- + * Copyright (c) 1995 Matt Thomas (thomas@lkg.dec.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough 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. + * + * $Id: if_pdq.c,v 1.7 1995/03/14 01:52:52 thomas Exp $ + * + * $Log: if_pdq.c,v $ + * Revision 1.7 1995/03/14 01:52:52 thomas + * Update for new FreeBSD PCI Interrupt interface + * + * Revision 1.6 1995/03/10 17:06:59 thomas + * Update for latest version of FreeBSD. + * Compensate for the fast that the ifp will not be first thing + * in softc on BSDI. + * + * Revision 1.5 1995/03/07 19:59:42 thomas + * First pass at BSDI EISA support + * + * Revision 1.4 1995/03/06 17:06:03 thomas + * Add transmit timeout support. + * Add support DEFEA (untested). + * + * Revision 1.3 1995/03/03 13:48:35 thomas + * more fixes + * + * + */ + +/* + * DEC PDQ FDDI Controller; code for BSD derived operating systems + * + * Written by Matt Thomas + * + * This driver supports the following FDDI controllers: + * + * Device: Config file entry: + * DEC DEFPA (PCI) device fpa0 + * DEC DEFEA (EISA) device fea0 at isa0 net irq ? vector feaintr + * + * Eventually, the following adapters will also be supported: + * + * DEC DEFTA (TC) device fta0 at tc? slot * vector ftaintr + * DEC DEFQA (Q-Bus) device fta0 at uba? csr 0?? vector fqaintr + * DEC DEFAA (FB+) device faa0 at fbus? slot * vector faaintr + */ + + +#include "fea.h" /* DEFPA EISA FDDI */ +#ifndef __bsdi__ +#include "fpa.h" /* DEFPA PCI FDDI */ +#endif +#if NFPA > 0 || NFEA > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/malloc.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif +#include <netinet/if_fddi.h> + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/vm_param.h> + +#if NFPA > 0 +#include <pci/pcivar.h> +#include <i386/isa/icu.h> +#endif + +#if NFEA > 0 +#include <i386/isa/isa.h> +#include <i386/isa/icu.h> +#ifdef __FreeBSD__ +#include <sys/devconf.h> +#include <i386/isa/isa_device.h> +#endif +#ifdef __bsdi__ +#include <sys/device.h> +#include <i386/isa/isavar.h> +#include <i386/eisa/eisa.h> +#endif +#endif + +#include "pdqreg.h" +#include "pdq_os.h" + +typedef struct { +#ifdef __bsdi__ + struct device sc_dev; /* base device */ + struct isadev sc_id; /* ISA device */ + struct intrhand sc_ih; /* intrrupt vectoring */ + struct atshutdown sc_ats; /* shutdown routine */ +#endif + struct arpcom sc_ac; + pdq_t *sc_pdq; +#if NBPFILTER > 0 && !defined(__FreeBSD__) && !defined(__bsdi__) + caddr_t sc_bpf; +#endif +#if NFEA > 0 + unsigned sc_iobase; +#endif +} pdq_softc_t; + +#define sc_if sc_ac.ac_if + +#if defined(__FreeBSD__) +#define sc_bpf sc_if.if_bpf +typedef void ifnet_ret_t; +#elif defined(__bsdi__) +#define sc_bpf sc_if.if_bpf +typedef int ifnet_ret_t; +#endif + + +static void +pdq_ifreset( + pdq_softc_t *sc) +{ + pdq_stop(sc->sc_pdq); +} + +static void +pdq_ifinit( + pdq_softc_t *sc) +{ + if (sc->sc_if.if_flags & IFF_UP) { + sc->sc_if.if_flags |= IFF_RUNNING; + if (sc->sc_if.if_flags & IFF_PROMISC) { + sc->sc_pdq->pdq_flags |= PDQ_PROMISC; + } else { + sc->sc_pdq->pdq_flags &= ~PDQ_PROMISC; + } + if (sc->sc_if.if_flags & IFF_ALLMULTI) { + sc->sc_pdq->pdq_flags |= PDQ_ALLMULTI; + } else { + sc->sc_pdq->pdq_flags &= ~PDQ_ALLMULTI; + } + if (sc->sc_if.if_flags & IFF_LINK1) { + sc->sc_pdq->pdq_flags |= PDQ_PASS_SMT; + } else { + sc->sc_pdq->pdq_flags &= ~PDQ_PASS_SMT; + } + sc->sc_pdq->pdq_flags |= PDQ_RUNNING; + pdq_run(sc->sc_pdq); + } else { + sc->sc_if.if_flags &= ~IFF_RUNNING; + sc->sc_pdq->pdq_flags &= ~PDQ_RUNNING; + pdq_stop(sc->sc_pdq); + } +} + +static void +pdq_ifwatchdog( + pdq_softc_t *sc) +{ + struct mbuf *m; + /* + * No progress was made on the transmit queue for PDQ_OS_TX_TRANSMIT + * seconds. Remove all queued packets. + */ + + sc->sc_if.if_flags &= ~IFF_OACTIVE; + sc->sc_if.if_timer = 0; + for (;;) { + IF_DEQUEUE(&sc->sc_if.if_snd, m); + if (m == NULL) + return; + m_freem(m); + } +} + +static ifnet_ret_t +pdq_ifstart( + struct ifnet *ifp) +{ + pdq_softc_t *sc = (pdq_softc_t *) ((caddr_t) ifp - offsetof(pdq_softc_t, sc_ac.ac_if)); + struct ifqueue *ifq = &ifp->if_snd; + struct mbuf *m; + int tx = 0; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + if (sc->sc_if.if_timer == 0) + sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT; + + if ((sc->sc_pdq->pdq_flags & PDQ_TXOK) == 0) { + sc->sc_if.if_flags |= IFF_OACTIVE; + return; + } + for (;; tx = 1) { + IF_DEQUEUE(ifq, m); + if (m == NULL) + break; + + if (pdq_queue_transmit_data(sc->sc_pdq, m) == PDQ_FALSE) { + ifp->if_flags |= IFF_OACTIVE; + IF_PREPEND(ifq, m); + break; + } + } + if (tx) + PDQ_DO_TYPE2_PRODUCER(sc->sc_pdq); +} + +void +pdq_os_receive_pdu( + pdq_t *pdq, + struct mbuf *m, + size_t pktlen) +{ + pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; + struct fddi_header *fh = mtod(m, struct fddi_header *); + + sc->sc_if.if_ipackets++; +#if NBPFILTER > 0 + if (sc->sc_bpf != NULL) + bpf_mtap(sc->sc_bpf, m); + if ((fh->fddi_fc & (FDDIFC_L|FDDIFC_F)) != FDDIFC_LLC_ASYNC) { + m_freem(m); + return; + } +#endif + + m->m_data += sizeof(struct fddi_header); + m->m_len -= sizeof(struct fddi_header); + m->m_pkthdr.len = pktlen - sizeof(struct fddi_header); + m->m_pkthdr.rcvif = &sc->sc_if; + fddi_input(&sc->sc_if, fh, m); +} + +void +pdq_os_restart_transmitter( + pdq_t *pdq) +{ + pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; + sc->sc_if.if_flags &= ~IFF_OACTIVE; + if (sc->sc_if.if_snd.ifq_head != NULL) { + sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT; + pdq_ifstart(&sc->sc_if); + } else { + sc->sc_if.if_timer = 0; + } +} + +void +pdq_os_transmit_done( + pdq_t *pdq, + struct mbuf *m) +{ + pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; +#if NBPFILTER > 0 + if (sc->sc_bpf != NULL) + bpf_mtap(sc->sc_bpf, m); +#endif + m_freem(m); + sc->sc_if.if_opackets++; +} + +void +pdq_os_addr_fill( + pdq_t *pdq, + pdq_lanaddr_t *addr, + size_t num_addrs) +{ + pdq_softc_t *sc = (pdq_softc_t *) pdq->pdq_os_ctx; + struct ether_multistep step; + struct ether_multi *enm; + + ETHER_FIRST_MULTI(step, &sc->sc_ac, enm); + while (enm != NULL && num_addrs > 0) { + ((u_short *) addr->lanaddr_bytes)[0] = ((u_short *) enm->enm_addrlo)[0]; + ((u_short *) addr->lanaddr_bytes)[1] = ((u_short *) enm->enm_addrlo)[1]; + ((u_short *) addr->lanaddr_bytes)[2] = ((u_short *) enm->enm_addrlo)[2]; + ETHER_NEXT_MULTI(step, enm); + addr++; + num_addrs--; + } +} + +static int +pdq_ifioctl( + struct ifnet *ifp, + int cmd, + caddr_t data) +{ + pdq_softc_t *sc = (pdq_softc_t *) ((caddr_t) ifp - offsetof(pdq_softc_t, sc_ac.ac_if)); + int s, error = 0; + + s = splimp(); + + switch (cmd) { + case SIOCSIFADDR: { + struct ifaddr *ifa = (struct ifaddr *)data; + + ifp->if_flags |= IFF_UP; + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: { + ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + (*ifp->if_init)(ifp->if_unit); +#ifdef __FreeBSD__ + arp_ifinit((struct arpcom *)ifp, ifa); +#else + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); +#endif + break; + } +#endif /* INET */ + +#ifdef NS + /* This magic copied from if_is.c; I don't use XNS, + * so I have no way of telling if this actually + * works or not. + */ + case AF_NS: { + struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + if (ns_nullhost(*ina)) { + ina->x_host = *(union ns_host *)(sc->pdq_ac.ac_enaddr); + } else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->pdq_ac.ac_enaddr, + sizeof sc->pdq_ac.ac_enaddr); + } + + (*ifp->if_init)(ifp->if_unit); + break; + } +#endif /* NS */ + + default: { + (*ifp->if_init)(ifp->if_unit); + break; + } + } + break; + } + + case SIOCSIFFLAGS: { + (*ifp->if_init)(ifp->if_unit); + break; + } + + case SIOCADDMULTI: + case SIOCDELMULTI: { + /* + * Update multicast listeners + */ + if (cmd == SIOCADDMULTI) + error = ether_addmulti((struct ifreq *)data, &sc->sc_ac); + else + error = ether_delmulti((struct ifreq *)data, &sc->sc_ac); + + if (error == ENETRESET) { + if (sc->sc_if.if_flags & IFF_RUNNING) + pdq_run(sc->sc_pdq); + error = 0; + } + break; + } + + default: { + error = EINVAL; + break; + } + } + + splx(s); + return error; +} + +static void +pdq_ifattach( + pdq_softc_t *sc, + ifnet_ret_t (*ifinit)(int unit), + ifnet_ret_t (*ifreset)(int unit), + ifnet_ret_t (*ifwatchdog)(int unit)) +{ + struct ifnet *ifp = &sc->sc_if; + + ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX|IFF_NOTRAILERS|IFF_MULTICAST; + + ifp->if_init = ifinit; +#ifndef __bsdi__ + ifp->if_reset = ifreset; +#endif + ifp->if_watchdog = ifwatchdog; + + ifp->if_ioctl = pdq_ifioctl; + ifp->if_output = fddi_output; + ifp->if_start = pdq_ifstart; + + if_attach(ifp); + fddi_ifattach(ifp); +#if NBPFILTER > 0 + bpfattach(&sc->sc_bpf, ifp, DLT_FDDI, sizeof(struct fddi_header)); +#endif + +} + +#if NFPA > 0 +/* + * This is the PCI configuration support. Since the PDQ is available + * on both EISA and PCI boards, one must be careful in how defines the + * PDQ in the config file. + */ +static char *pdq_pci_probe (pcici_t config_id, pcidi_t device_id); +static void pdq_pci_attach(pcici_t config_id, int unit); +static u_long pdq_pci_count; + +struct pci_device fpadevice = { + "fpa", + pdq_pci_probe, + pdq_pci_attach, + &pdq_pci_count, +}; + +#if defined(__FreeBSD__) +static pdq_softc_t *pdqs_pci[NFPA]; +#define PDQ_PCI_UNIT_TO_SOFTC(unit) (pdqs_pci[unit]) + +#ifdef DATA_SET +DATA_SET (pcidevice_set, fpadevice); +#endif +#endif + +#define PCI_CBMA 0x10 /* Configuration Base Memory Address */ + +static ifnet_ret_t +pdq_pci_ifreset( + int unit) +{ + pdq_ifreset(PDQ_PCI_UNIT_TO_SOFTC(unit)); +} + +static ifnet_ret_t +pdq_pci_ifinit( + int unit) +{ + pdq_ifinit(PDQ_PCI_UNIT_TO_SOFTC(unit)); +} + +static ifnet_ret_t +pdq_pci_ifwatchdog( + int unit) +{ + pdq_ifwatchdog(PDQ_PCI_UNIT_TO_SOFTC(unit)); +} + +static int +pdq_pci_ifintr( + pdq_softc_t *sc) +{ + return pdq_interrupt(sc->sc_pdq); +} + +static char * +pdq_pci_probe( + pcici_t config_id, + pcidi_t device_id) +{ + if (device_id == 0x000f1011ul) + return "Digital DEFPA PCI FDDI Controller"; + return NULL; +} + +static void +pdq_pci_attach( + pcici_t config_id, + int unit) +{ + pdq_softc_t *sc; + vm_offset_t va_csrs, pa_csrs; + + if (unit > NFPA) { + printf("fpa%d: not configured; kernel is built for only %d device%s.\n", + unit, NFPA, NFPA == 1 ? "" : "s"); + return; + } + + sc = (pdq_softc_t *) malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (sc == NULL) + return; + + bzero(sc, sizeof(pdq_softc_t)); /* Zero out the softc*/ + if (!pci_map_mem(config_id, PCI_CBMA, &va_csrs, &pa_csrs)) { + free((void *) sc, M_DEVBUF); + return; + } + + sc->sc_if.if_name = "fpa"; + sc->sc_if.if_unit = unit; + sc->sc_pdq = pdq_initialize((void *) va_csrs, "fpa", unit, + (void *) sc, PDQ_DEFPA); + if (sc->sc_pdq == NULL) { + free((void *) sc, M_DEVBUF); + return; + } + bcopy((caddr_t) sc->sc_pdq->pdq_hwaddr.lanaddr_bytes, sc->sc_ac.ac_enaddr, 6); + pdqs_pci[unit] = sc; + pdq_ifattach(sc, pdq_pci_ifinit, pdq_pci_ifreset, pdq_pci_ifwatchdog); + pci_map_int(config_id, pdq_pci_ifintr, (void*) sc, &net_imask); +} +#endif /* NFPA > 0 */ + +#if NFEA > 0 +/* + * + */ +static const int pdq_eisa_irqs[4] = { IRQ9, IRQ10, IRQ11, IRQ15 }; + +#ifdef __FreeBSD__ +static pdq_softc_t *pdqs_eisa[NFEA]; +#define PDQ_EISA_UNIT_TO_SOFTC(unit) (pdqs_eisa[unit]) +#endif +#ifdef __bsdi__ +extern struct cfdriver feacd; +#define PDQ_EISA_UNIT_TO_SOFTC(unit) ((pdq_softc_t *)feacd.cd_devs[unit]) +#endif + +static ifnet_ret_t +pdq_eisa_ifreset( + int unit) +{ + pdq_ifreset(PDQ_EISA_UNIT_TO_SOFTC(unit)); +} + +static ifnet_ret_t +pdq_eisa_ifinit( + int unit) +{ + pdq_ifinit(PDQ_EISA_UNIT_TO_SOFTC(unit)); +} + +static ifnet_ret_t +pdq_eisa_ifwatchdog( + int unit) +{ + pdq_ifwatchdog(PDQ_EISA_UNIT_TO_SOFTC(unit)); +} + +int +feaintr( + int unit) +{ + pdq_interrupt(PDQ_EISA_UNIT_TO_SOFTC(unit)->sc_pdq); + return unit; +} + +void +pdq_eisa_subprobe( + pdq_uint32_t iobase, + pdq_uint32_t *maddr, + pdq_uint32_t *msize, + pdq_uint32_t *irq) +{ + if (irq != NULL) + *irq = pdq_eisa_irqs[PDQ_OS_IORD_8(iobase + PDQ_EISA_IO_CONFIG_STAT_0) & 3]; + *maddr = (PDQ_OS_IORD_8(iobase + PDQ_EISA_MEM_ADD_CMP_0) << 16) + | (PDQ_OS_IORD_8(iobase + PDQ_EISA_MEM_ADD_CMP_1) << 8); + *msize = (PDQ_OS_IORD_8(iobase + PDQ_EISA_MEM_ADD_MASK_0) + 4) << 8; +} + +void +pdq_eisa_devinit( + pdq_softc_t *sc) +{ + pdq_uint8_t data; + + /* + * Do the standard initialization for the DEFEA registers. + */ + PDQ_OS_IOWR_8(sc->sc_iobase + PDQ_EISA_FUNCTION_CTRL, 0x23); + PDQ_OS_IOWR_8(sc->sc_iobase + PDQ_EISA_IO_CMP_1_1, (sc->sc_iobase >> 8) & 0x0F); + PDQ_OS_IOWR_8(sc->sc_iobase + PDQ_EISA_IO_CMP_1_0, (sc->sc_iobase >> 8) & 0x0F); + PDQ_OS_IOWR_8(sc->sc_iobase + PDQ_EISA_SLOT_CTRL, 0x01); + data = PDQ_OS_IORD_8(sc->sc_iobase + PDQ_EISA_BURST_HOLDOFF); + PDQ_OS_IOWR_8(sc->sc_iobase + PDQ_EISA_BURST_HOLDOFF, data | 1); + data = PDQ_OS_IORD_8(sc->sc_iobase + PDQ_EISA_IO_CONFIG_STAT_0); + PDQ_OS_IOWR_8(sc->sc_iobase + PDQ_EISA_IO_CONFIG_STAT_0, data | 8); +} + +#ifdef __FreeBSD__ +static struct kern_devconf kdc_fea[NFEA] = { { + 0, 0, 0, /* filled in by dev_attach */ + "fea", 0, { MDDT_ISA, 0, "net" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, + &kdc_isa0, /* parent */ + 0, /* parentdata */ + DC_BUSY, /* host adapters are always ``in use'' */ + "DEC DEFEA EISA FDDI Controller" +} }; + +static inline void +pdq_eisa_registerdev( + struct isa_device *id) +{ + if (id->id_unit) + kdc_fea[id->id_unit] = kdc_fea[0]; + kdc_fea[id->id_unit].kdc_unit = id->id_unit; + kdc_fea[id->id_unit].kdc_parentdata = id; + dev_attach(&kdc_fea[id->id_unit]); +} + +static int pdq_eisa_slots = ~1; + +static int +pdq_eisa_probe( + struct isa_device *id) +{ + pdq_softc_t *sc; + int slot; + pdq_uint32_t data, irq, maddr, msize; + + slot = 0x1000 * (ffs(pdq_eisa_slots) - 1); + for (; slot <= 0xF000; slot++) { + pdq_eisa_slots &= ~(1 << (slot >> 12)); + data = PDQ_OS_IORD_32(slot + PDQ_EISA_SLOT_ID); + if ((data & 0xFFFFFF) != 0x30A310) + continue; + id->id_iobase = slot; + pdq_eisa_subprobe(slot, &maddr, &msize, &irq); + if (id->id_irq != 0 && irq != id->id_irq) { + printf("fea%d: error: desired IRQ of %d does not match device's actual IRQ (%d),\n", + id->id_unit, + ffs(id->id_irq) - 1, ffs(irq) - 1); + return 0; + } + id->id_irq = irq; + if (maddr == 0) { + printf("fea%d: error: memory not enabled! ECU reconfiguration required\n", + id->id_unit); + return 0; + } +#if 0 + id->id_maddr = (char *) NULL + atdevbase + maddr; + id->id_msize = msize; +#endif + sc = (pdq_softc_t *) malloc(sizeof(pdq_softc_t), M_DEVBUF, M_WAITOK); + if (sc == NULL) + return 0; + PDQ_EISA_UNIT_TO_SOFTC(id->id_unit) = sc; + return 0x1000; + } + return 0; +} + +static int +pdq_eisa_attach( + struct isa_device *id) +{ + pdq_softc_t *sc = PDQ_EISA_UNIT_TO_SOFTC(id->id_unit); + vm_offset_t va_csrs; + pdq_uint32_t maddr, msize; + + bzero(sc, sizeof(pdq_softc_t)); /* Zero out the softc*/ + + sc->sc_if.if_name = "fea"; + sc->sc_if.if_unit = id->id_unit; + sc->sc_iobase = id->id_iobase; + + pdq_eisa_devinit(sc); + pdq_eisa_subprobe(sc->sc_iobase, &maddr, &msize, NULL); + va_csrs = (vm_offset_t) pmap_mapdev(maddr, msize); + if (va_csrs == (vm_offset_t) 0) { + printf("fea%s: mapping of device memory failed\n", sc->sc_if.if_unit); + return 0; + } + + sc->sc_pdq = pdq_initialize((void *) va_csrs, "fea", sc->sc_if.if_unit, + (void *) sc, PDQ_DEFEA); + if (sc->sc_pdq == NULL) { + printf("fea%s: initialization failed\n", sc->sc_if.if_unit); + return 0; + } + + bcopy((caddr_t) sc->sc_pdq->pdq_hwaddr.lanaddr_bytes, sc->sc_ac.ac_enaddr, 6); + pdq_ifattach(sc, pdq_eisa_ifinit, pdq_eisa_ifreset, pdq_eisa_ifwatchdog); + pdq_eisa_registerdev(id); + return 1; +} + +/* + * + */ +struct isa_driver feadriver = { + pdq_eisa_probe, + pdq_eisa_attach, + "fea" +}; +#endif /* __FreeBSD__ */ + +#ifdef __bsdi__ +int +pdq_eisa_probe( + struct device *parent, + struct cfdata *cf, + void *aux) +{ + struct isa_attach_args *ia = (struct isa_attach_args *) aux; + int slot; + pdq_uint32_t irq, maddr, msize; + + if (isa_bustype != BUS_EISA) + return (0); + + if ((slot = eisa_match(cf, ia)) == 0) + return (0); + ia->ia_iobase = slot << 12; + ia->ia_iosize = EISA_NPORT; + eisa_slotalloc(slot); + + pdq_eisa_subprobe(ia->ia_iobase, &maddr, &msize, &irq); + if (ia->ia_irq != IRQUNK && irq != ia->ia_irq) { + printf("fea%d: error: desired IRQ of %d does not match device's actual IRQ (%d),\n", + cf->cf_unit, + ffs(ia->ia_irq) - 1, ffs(irq) - 1); + return 0; + } + if (ia->ia_irq == IRQUNK) { + if ((irq = isa_irqalloc(irq)) == 0) + return 0; + ia->ia_irq = irq; + } + if (maddr == 0) { + printf("fea%d: error: memory not enabled! ECU reconfiguration required\n", + cf->cf_unit); + return 0; + } + + /* EISA bus masters don't use host DMA channels */ + ia->ia_drq = 0; /* XXX should be DRQUNK or DRQBUSMASTER? */ + +#if 0 + ia->ia_maddr = maddr; + ia->ia_msize = msize; +#else + ia->ia_maddr = 0; + ia->ia_msize = 0; +#endif + return 1; +} + +void +pdq_eisa_attach( + struct device *parent, + struct device *self, + void *aux) +{ + pdq_softc_t *sc = (pdq_softc_t *) self; + register struct isa_attach_args *ia = (struct isa_attach_args *) aux; + register struct ifnet *ifp = &sc->sc_if; + int i; + + sc->sc_if.if_unit = sc->sc_dev.dv_unit; + sc->sc_if.if_name = "fea"; + sc->sc_if.if_flags = 0; + + sc->sc_iobase = ia->ia_iobase; + + sc->sc_pdq = pdq_initialize((void *) ISA_HOLE_VADDR(ia->ia_maddr), "fea", + sc->sc_if.if_unit, (void *) sc, PDQ_DEFEA); + if (sc->sc_pdq == NULL) { + printf("fea%s: initialization failed\n", sc->sc_if.if_unit); + return; + } + + bcopy((caddr_t) sc->sc_pdq->pdq_hwaddr.lanaddr_bytes, sc->sc_ac.ac_enaddr, 6); + + pdq_ifattach(sc, pdq_eisa_ifinit, pdq_eisa_ifreset, pdq_eisa_ifwatchdog); + + isa_establish(&sc->sc_id, &sc->sc_dev); + + sc->sc_ih.ih_fun = feaintr; + sc->sc_ih.ih_arg = (void *)sc; + intr_establish(ia->ia_irq, &sc->sc_ih, DV_NET); + + sc->sc_ats.func = (void (*)(void *)) pdq_stop; + sc->sc_ats.arg = (void *) sc->sc_pdq; + atshutdown(&sc->sc_ats, ATSH_ADD); +} + +static char *pdq_eisa_ids[] = { + "DEC3001", /* 0x0130A310 */ + "DEC3002", /* 0x0230A310 */ + "DEC3003", /* 0x0330A310 */ + "DEC3004", /* 0x0430A310 */ +}; + +struct cfdriver feacd = { + 0, "fea", pdq_eisa_probe, pdq_eisa_attach, DV_IFNET, sizeof(pdq_softc_t), + pdq_eisa_ids +}; +#endif /* __bsdi__ */ +#endif /* NFEA > 0 */ +#endif /* NFPA > 0 || NFEA > 0 */ diff --git a/sys/pci/pdq.c b/sys/pci/pdq.c new file mode 100644 index 0000000..499ed80 --- /dev/null +++ b/sys/pci/pdq.c @@ -0,0 +1,1558 @@ +/*- + * Copyright (c) 1995 Matt Thomas (matt@lkg.dec.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough 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. + * + * $Id: pdq.c,v 1.8 1995/03/14 01:52:52 thomas Exp $ + * + * $Log: pdq.c,v $ + * Revision 1.8 1995/03/14 01:52:52 thomas + * Update for new FreeBSD PCI Interrupt interface + * + * Revision 1.7 1995/03/07 23:03:16 thomas + * Fix SMT queue processing + * + * Revision 1.6 1995/03/06 18:03:47 thomas + * restart trasmitter once link is available + * + * Revision 1.5 1995/03/06 17:07:56 thomas + * Add copyright/disclaimer + * Add error recovery code. + * Add BPF SMT support + * + * Revision 1.3 1995/03/03 13:48:35 thomas + * more fixes + * + * + */ + +/* + * DEC PDQ FDDI Controller O/S independent code + * + * Written by Matt Thomas <matt@lkg.dec.com> + * + * This module should work any PDQ based board. Note that changes for + * MIPS and Alpha architectures (or any other architecture which requires + * a flushing of memory or write buffers and/or has incoherent caches) + * have yet to be made. + */ + +#include "pdqreg.h" +#include "pdq_os.h" + +#define PDQ_ROUNDUP(n, x) (((n) + ((x) - 1)) & ~((x) - 1)) +#define PDQ_CMD_RX_ALIGNMENT 16 + +#if defined(PDQTEST) && !defined(PDQ_NOPRINTF) +#define PDQ_PRINTF(x) printf x +#else +#define PDQ_PRINTF(x) +#endif + +const char * const pdq_halt_codes[] = { + "Selftest Timeout", "Host Bus Parity Error", "Host Directed Fault", + "Software Fault", "Hardware Fault", "PC Trace Path Test", + "DMA Error", "Image CRC Error", "Adapter Processer Error" +}; + +const char * const pdq_adapter_states[] = { + "Reset", "Upgrade", "DMA Unavailable", "DMA Available", + "Link Available", "Link Unavailable", "Halted", "Ring Member" +}; + +/* + * The following are used in conjunction with + * unsolicited events + */ +const char * const pdq_entities[] = { + "Station", "Link", "Phy Port" +}; + +const char * const pdq_station_events[] = { + "Trace Received" +}; + +const char * const pdq_station_arguments[] = { + "Reason" +}; + +const char * const pdq_link_events[] = { + "Transmit Underrun", + "Transmit Failed", + "Block Check Error (CRC)", + "Frame Status Error", + "PDU Length Error", + NULL, + NULL, + "Receive Data Overrun", + NULL, + "No User Buffer", + "Ring Initialization Initiated", + "Ring Initialization Received", + "Ring Beacon Initiated", + "Duplicate Address Failure", + "Duplicate Token Detected", + "Ring Purger Error", + "FCI Strip Error", + "Trace Initiated", + "Directed Beacon Received", +}; + +const char * const pdq_link_arguments[] = { + "Reason", + "Data Link Header", + "Source", + "Upstream Neighbor" +}; + +const char * const pdq_phy_events[] = { + "LEM Error Monitor Reject", + "Elasticy Buffer Error", + "Link Confidence Test Reject" +}; + +const char * const pdq_phy_arguments[] = { + "Direction" +}; + +const char * const * const pdq_event_arguments[] = { + pdq_station_arguments, + pdq_link_arguments, + pdq_phy_arguments +}; + +const char * const * const pdq_event_codes[] = { + pdq_station_events, + pdq_link_events, + pdq_phy_events +}; + +const char * const pdq_station_types[] = { + "SAS", "DAC", "SAC", "NAC", "DAS" +}; + +const char * const pdq_smt_versions[] = { "", "V6.2", "V7.2" }; + +const char pdq_phy_types[] = "ABSM"; + +const char * const pdq_pmd_types0[] = { + "ANSI Multi-Mode", "ANSI Single-Mode Type 1", "ANSI Single-Mode Type 2", + "ANSI Sonet" +}; + +const char * const pdq_pmd_types100[] = { + "Low Power", "Thin Wire", "Shielded Twisted Pair", + "Unshielded Twisted Pair" +}; + +const char * const * const pdq_pmd_types[] = { + pdq_pmd_types0, pdq_pmd_types100 +}; + +const char * const pdq_descriptions[] = { + "DEFPA PCI", + "DEFEA EISA", +}; + +void +pdq_print_fddi_chars( + pdq_t *pdq, + const pdq_response_status_chars_get_t *rsp) +{ + const char hexchars[] = "0123456789abcdef"; + + printf(PDQ_OS_PREFIX "DEC %s FDDI %s Controller\n", + PDQ_OS_PREFIX_ARGS, pdq_descriptions[pdq->pdq_type], + pdq_station_types[rsp->status_chars_get.station_type]); + + printf(PDQ_OS_PREFIX "FDDI address %c%c:%c%c:%c%c:%c%c:%c%c:%c%c, FW=%c%c%c%c, HW=%c", + PDQ_OS_PREFIX_ARGS, + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[0] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[0] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[1] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[1] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[2] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[2] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[3] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[3] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[4] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[4] & 0x0F], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[5] >> 4], + hexchars[pdq->pdq_hwaddr.lanaddr_bytes[5] & 0x0F], + pdq->pdq_fwrev.fwrev_bytes[0], pdq->pdq_fwrev.fwrev_bytes[1], + pdq->pdq_fwrev.fwrev_bytes[2], pdq->pdq_fwrev.fwrev_bytes[3], + rsp->status_chars_get.module_rev.fwrev_bytes[0]); + + if (rsp->status_chars_get.smt_version_id < PDQ_ARRAY_SIZE(pdq_smt_versions)) { + printf(", SMT %s\n", pdq_smt_versions[rsp->status_chars_get.smt_version_id]); + } + + printf(PDQ_OS_PREFIX "FDDI Port%s = %c (PMD = %s)", + PDQ_OS_PREFIX_ARGS, + rsp->status_chars_get.station_type == PDQ_STATION_TYPE_DAS ? "[A]" : "", + pdq_phy_types[rsp->status_chars_get.phy_type[0]], + pdq_pmd_types[rsp->status_chars_get.pmd_type[0] / 100][rsp->status_chars_get.pmd_type[0] % 100]); + + if (rsp->status_chars_get.station_type == PDQ_STATION_TYPE_DAS) + printf(", FDDI Port[B] = %c (PMD = %s)", + pdq_phy_types[rsp->status_chars_get.phy_type[1]], + pdq_pmd_types[rsp->status_chars_get.pmd_type[1] / 100][rsp->status_chars_get.pmd_type[1] % 100]); + + printf("\n"); +} + +void +pdq_init_csrs( + pdq_csrs_t *csrs, + void *csr_va, + size_t csrsize) +{ + volatile pdq_uint32_t *csr_base = (volatile pdq_uint32_t *) csr_va; + + csrs->csr_port_reset = &csr_base[0 * csrsize]; + csrs->csr_host_data = &csr_base[1 * csrsize]; + csrs->csr_port_control = &csr_base[2 * csrsize]; + csrs->csr_port_data_a = &csr_base[3 * csrsize]; + csrs->csr_port_data_b = &csr_base[4 * csrsize]; + csrs->csr_port_status = &csr_base[5 * csrsize]; + csrs->csr_host_int_type_0 = &csr_base[6 * csrsize]; + csrs->csr_host_int_enable = &csr_base[7 * csrsize]; + csrs->csr_type_2_producer = &csr_base[8 * csrsize]; + csrs->csr_cmd_response_producer = &csr_base[10 * csrsize]; + csrs->csr_cmd_request_producer = &csr_base[11 * csrsize]; + csrs->csr_host_smt_producer = &csr_base[12 * csrsize]; + csrs->csr_unsolicited_producer = &csr_base[13 * csrsize]; +} + +void +pdq_init_pci_csrs( + pdq_pci_csrs_t *csrs, + void *csr_va, + size_t csrsize) +{ + volatile pdq_uint32_t *csr_base = (volatile pdq_uint32_t *) csr_va; + + csrs->csr_pfi_mode_control = &csr_base[16 * csrsize]; + csrs->csr_pfi_status = &csr_base[17 * csrsize]; + csrs->csr_fifo_write = &csr_base[18 * csrsize]; + csrs->csr_fifo_read = &csr_base[19 * csrsize]; +} + +void +pdq_flush_databuf_queue( + pdq_databuf_queue_t *q) +{ + PDQ_OS_DATABUF_T *pdu; + for (;;) { + PDQ_OS_DATABUF_DEQUEUE(q, pdu); + if (pdu == NULL) + return; + PDQ_OS_DATABUF_FREE(pdu); + } +} + +pdq_boolean_t +pdq_do_port_control( + const pdq_csrs_t * const csrs, + pdq_uint32_t cmd) +{ + int cnt = 0; + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_CSR_CMD_DONE; + *csrs->csr_port_control = PDQ_PCTL_CMD_ERROR | cmd; + while ((*csrs->csr_host_int_type_0 & PDQ_HOST_INT_CSR_CMD_DONE) == 0 && cnt < 33000000) + cnt++; + PDQ_PRINTF(("CSR cmd spun %d times\n", cnt)); + if (*csrs->csr_host_int_type_0 & PDQ_HOST_INT_CSR_CMD_DONE) { + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_CSR_CMD_DONE; + return (*csrs->csr_port_control & PDQ_PCTL_CMD_ERROR) ? PDQ_FALSE : PDQ_TRUE; + } + /* adapter failure */ + PDQ_ASSERT(0); + return PDQ_FALSE; +} + +void +pdq_read_mla( + const pdq_csrs_t * const csrs, + pdq_lanaddr_t *hwaddr) +{ + pdq_uint32_t data; + + *csrs->csr_port_data_a = 0; + pdq_do_port_control(csrs, PDQ_PCTL_MLA_READ); + data = *csrs->csr_host_data; + + hwaddr->lanaddr_bytes[0] = (data >> 0) & 0xFF; + hwaddr->lanaddr_bytes[1] = (data >> 8) & 0xFF; + hwaddr->lanaddr_bytes[2] = (data >> 16) & 0xFF; + hwaddr->lanaddr_bytes[3] = (data >> 24) & 0xFF; + + *csrs->csr_port_data_a = 1; + pdq_do_port_control(csrs, PDQ_PCTL_MLA_READ); + data = *csrs->csr_host_data; + + hwaddr->lanaddr_bytes[4] = (data >> 0) & 0xFF; + hwaddr->lanaddr_bytes[5] = (data >> 8) & 0xFF; +} + +void +pdq_read_fwrev( + const pdq_csrs_t * const csrs, + pdq_fwrev_t *fwrev) +{ + pdq_uint32_t data; + + pdq_do_port_control(csrs, PDQ_PCTL_FW_REV_READ); + data = *csrs->csr_host_data; + + fwrev->fwrev_bytes[3] = (data >> 0) & 0xFF; + fwrev->fwrev_bytes[2] = (data >> 8) & 0xFF; + fwrev->fwrev_bytes[1] = (data >> 16) & 0xFF; + fwrev->fwrev_bytes[0] = (data >> 24) & 0xFF; +} + +pdq_boolean_t +pdq_read_error_log( + pdq_t *pdq, + pdq_response_error_log_get_t *log_entry) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_uint32_t *ptr = (pdq_uint32_t *) log_entry; + + pdq_do_port_control(csrs, PDQ_PCTL_ERROR_LOG_START); + + while (pdq_do_port_control(csrs, PDQ_PCTL_FW_REV_READ) == PDQ_TRUE) { + *ptr++ = *csrs->csr_host_data; + if ((pdq_uint8_t *) ptr - (pdq_uint8_t *) log_entry == sizeof(*log_entry)) + break; + } + return (ptr == (pdq_uint32_t *) log_entry) ? PDQ_FALSE : PDQ_TRUE; +} + +pdq_chip_rev_t +pdq_read_chiprev( + const pdq_csrs_t * const csrs) +{ + pdq_uint32_t data; + + *csrs->csr_port_data_a = PDQ_SUB_CMD_PDQ_REV_GET; + pdq_do_port_control(csrs, PDQ_PCTL_SUB_CMD); + data = *csrs->csr_host_data; + + return (pdq_chip_rev_t) data; +} + +static const struct { + size_t cmd_len; + size_t rsp_len; + const char *cmd_name; +} pdq_cmd_info[] = { + { sizeof(pdq_cmd_generic_t), /* 0 - PDQC_START */ + sizeof(pdq_response_generic_t), + "Start" + }, + { sizeof(pdq_cmd_filter_set_t), /* 1 - PDQC_FILTER_SET */ + sizeof(pdq_response_generic_t), + "Filter Set" + }, + { sizeof(pdq_cmd_generic_t), /* 2 - PDQC_FILTER_GET */ + sizeof(pdq_response_filter_get_t), + "Filter Get" + }, + { sizeof(pdq_cmd_chars_set_t), /* 3 - PDQC_CHARS_SET */ + sizeof(pdq_response_generic_t), + "Chars Set" + }, + { sizeof(pdq_cmd_generic_t), /* 4 - PDQC_STATUS_CHARS_GET */ + sizeof(pdq_response_status_chars_get_t), + "Status Chars Get" + }, +#if 0 + { sizeof(pdq_cmd_generic_t), /* 5 - PDQC_COUNTERS_GET */ + sizeof(pdq_response_counters_get_t), + "Counters Get" + }, + { sizeof(pdq_cmd_counters_set_t), /* 6 - PDQC_COUNTERS_SET */ + sizeof(pdq_response_generic_t), + "Counters Set" + }, +#else + { 0, 0, "Counters Get" }, + { 0, 0, "Counters Set" }, +#endif + { sizeof(pdq_cmd_addr_filter_set_t), /* 7 - PDQC_ADDR_FILTER_SET */ + sizeof(pdq_response_generic_t), + "Addr Filter Set" + }, + { sizeof(pdq_cmd_generic_t), /* 8 - PDQC_ADDR_FILTER_GET */ + sizeof(pdq_response_addr_filter_get_t), + "Addr Filter Get" + }, +#if 0 + { sizeof(pdq_cmd_generic_t), /* 9 - PDQC_ERROR_LOG_CLEAR */ + sizeof(pdq_response_generic_t), + "Error Log Clear" + }, + { sizeof(pdq_cmd_generic_t), /* 10 - PDQC_ERROR_LOG_SET */ + sizeof(pdq_response_generic_t), + "Error Log Set" + }, + { sizeof(pdq_cmd_generic_t), /* 11 - PDQC_FDDI_MIB_GET */ + sizeof(pdq_response_generic_t), + "FDDI MIB Get" + }, + { sizeof(pdq_cmd_generic_t), /* 12 - PDQC_DEC_EXT_MIB_GET */ + sizeof(pdq_response_generic_t), + "DEC Ext MIB Get" + }, + { sizeof(pdq_cmd_generic_t), /* 13 - PDQC_DEC_SPECIFIC_GET */ + sizeof(pdq_response_generic_t), + "DEC Specific Get" + }, + { sizeof(pdq_cmd_generic_t), /* 14 - PDQC_SNMP_SET */ + sizeof(pdq_response_generic_t), + "SNMP Set" + }, + { 0, 0, "N/A" }, + { sizeof(pdq_cmd_generic_t), /* 16 - PDQC_SMT_MIB_GET */ + sizeof(pdq_response_generic_t), + "SMT MIB Get" + }, + { sizeof(pdq_cmd_generic_t), /* 17 - PDQC_SMT_MIB_SET */ + sizeof(pdq_response_generic_t), + "SMT MIB Set", + }, +#endif +}; + +void +pdq_queue_commands( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_command_info_t *ci = &pdq->pdq_command_info; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + pdq_txdesc_t *txd; + pdq_rxdesc_t *rxd; + pdq_cmd_code_t op; + pdq_uint32_t cmdlen, rsplen; + pdq_uint8_t *bufptr; + unsigned cmds; + + for (cmds = 0; ci->ci_pending_commands != 0; cmds++) { + pdq_uint32_t mask; + /* + * If there is no free space in the queues, stop queuing + * commands. + */ + if (ci->ci_request_free == 0 || ci->ci_response_free == 0) + break; + /* + * If there are no commands nor responses pending, then reset + * the command/response buffer back to the start. + */ + if (ci->ci_request_free == ci->ci_request_max + && ci->ci_response_free == ci->ci_response_max) { + ci->ci_bufptr = ci->ci_bufstart; + ci->ci_buffree = sizeof(pdq->pdq_dbp->pdqdb_command_pool); + } + /* + * Determine which commands need to be queued. + */ + op = PDQC_SMT_MIB_SET; + for (mask = 1 << ((int) op); (mask & ci->ci_pending_commands) == 0; mask >>= 1) + op = (pdq_cmd_code_t) ((int) op - 1); + /* + * Obtain the sizes needed for the command and response. + * Round up to PDQ_CMD_RX_ALIGNMENT so the receive buffer is + * always properly aligned. + */ + cmdlen = PDQ_ROUNDUP(pdq_cmd_info[op].cmd_len, PDQ_CMD_RX_ALIGNMENT); + rsplen = PDQ_ROUNDUP(pdq_cmd_info[op].rsp_len, PDQ_CMD_RX_ALIGNMENT); + if (cmdlen < rsplen) + cmdlen = rsplen; + /* + * If there's not enough space or if there isn't enough continguous + * space, then wait for some to be freed. + */ + if (cmdlen > ci->ci_buffree) + break; + if (cmdlen > ci->ci_bufend - ci->ci_bufptr) + break; + + /* + * Allocate the space for the command and the repsonse. + */ + bufptr = ci->ci_bufptr; + ci->ci_bufptr += cmdlen; + ci->ci_buffree -= cmdlen; + if (ci->ci_bufptr == ci->ci_bufend) + ci->ci_bufptr = ci->ci_bufstart; + + /* + * Obtain and fill in the descriptor for the command + */ + txd = &dbp->pdqdb_command_requests[ci->ci_request_producer]; + PDQ_ADVANCE(ci->ci_request_producer, 1, PDQ_RING_MASK(dbp->pdqdb_command_requests)); + ci->ci_request_free--; + + txd->txd_pa_lo = ci->ci_pa_bufstart + (bufptr - ci->ci_bufstart); + txd->txd_eop = txd->txd_sop = 1; + txd->txd_seg_len = cmdlen; + txd->txd_pa_hi = 0; + + /* + * Obtain and fill in the descriptor for the response + */ + rxd = &dbp->pdqdb_command_responses[ci->ci_response_producer]; + PDQ_ADVANCE(ci->ci_response_producer, 1, PDQ_RING_MASK(dbp->pdqdb_command_responses)); + ci->ci_response_free--; + + rxd->rxd_pa_lo = ci->ci_pa_bufstart + (bufptr - ci->ci_bufstart); + rxd->rxd_sop = 1; + rxd->rxd_seg_cnt = 0; + rxd->rxd_seg_len_lo = 0; + rxd->rxd_seg_len_hi = cmdlen / 16; + rxd->rxd_pa_hi = 0; + + /* + * Clear the command area, set the opcode, and the command from the pending + * mask. + */ + + PDQ_OS_MEMZERO(bufptr, cmdlen); + *(pdq_cmd_code_t *) bufptr = op; + ci->ci_pending_commands &= ~mask; + + /* + * Fill in the command area, if needed. + */ + switch (op) { + case PDQC_FILTER_SET: { + pdq_cmd_filter_set_t *filter_set = (pdq_cmd_filter_set_t *) bufptr; + unsigned idx = 0; + filter_set->filter_set_items[idx].item_code = PDQI_IND_GROUP_PROM; + filter_set->filter_set_items[idx].filter_state = (pdq->pdq_flags & PDQ_PROMISC ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_GROUP_PROM; + filter_set->filter_set_items[idx].filter_state = (pdq->pdq_flags & PDQ_ALLMULTI ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_SMT_PROM; + filter_set->filter_set_items[idx].filter_state = ((pdq->pdq_flags & (PDQ_PROMISC|PDQ_PASS_SMT)) == (PDQ_PROMISC|PDQ_PASS_SMT) ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_SMT_USER; + filter_set->filter_set_items[idx].filter_state = (pdq->pdq_flags & PDQ_PASS_SMT ? PDQ_FILTER_PASS : PDQ_FILTER_BLOCK); + idx++; + filter_set->filter_set_items[idx].item_code = PDQI_EOL; + break; + } + case PDQC_ADDR_FILTER_SET: { + pdq_cmd_addr_filter_set_t *addr_filter_set = (pdq_cmd_addr_filter_set_t *) bufptr; + pdq_lanaddr_t *addr = addr_filter_set->addr_filter_set_addresses; + addr->lanaddr_bytes[0] = 0xFF; + addr->lanaddr_bytes[1] = 0xFF; + addr->lanaddr_bytes[2] = 0xFF; + addr->lanaddr_bytes[3] = 0xFF; + addr->lanaddr_bytes[4] = 0xFF; + addr->lanaddr_bytes[5] = 0xFF; + addr++; + pdq_os_addr_fill(pdq, addr, 61); + break; + } + } + /* + * At this point the command is done. All that needs to be done is to + * produce it to the PDQ. That will be done after the loop exits. + */ + PDQ_PRINTF(("PDQ Queue Command Request: %s queued\n", + pdq_cmd_info[op].cmd_name)); + } + if (cmds > 0) { + *csrs->csr_cmd_response_producer = ci->ci_response_producer | (ci->ci_response_completion << 8); + *csrs->csr_cmd_request_producer = ci->ci_request_producer | (ci->ci_request_completion << 8); + } +} + +void +pdq_process_command_responses( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_command_info_t *ci = &pdq->pdq_command_info; + volatile const pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + pdq_txdesc_t *txd; + pdq_rxdesc_t *rxd; + const pdq_response_generic_t *rspgen; + unsigned cmds; + + /* + * We have to process the command and response in tandem so + * just wait for the response to be consumed. If it has been + * consumed then the command must have been as well. + */ + + for (cmds = 0; cbp->pdqcb_command_response != ci->ci_response_completion; cmds = 1) { + PDQ_ASSERT (cbp->pdqcb_command_request != ci->ci_request_completion); + + txd = &dbp->pdqdb_command_requests[ci->ci_request_completion]; + rxd = &dbp->pdqdb_command_responses[ci->ci_response_completion]; + + rspgen = (const pdq_response_generic_t *) (ci->ci_bufstart + rxd->rxd_pa_lo - ci->ci_pa_bufstart); + PDQ_ASSERT(rspgen->generic_status == PDQR_SUCCESS); + PDQ_PRINTF(("PDQ Process Command Response: %s completed\n", + pdq_cmd_info[rspgen->generic_op].cmd_name)); + + if (rspgen->generic_op == PDQC_STATUS_CHARS_GET && (pdq->pdq_flags & PDQ_PRINTCHARS)) { + pdq->pdq_flags &= ~PDQ_PRINTCHARS; + pdq_print_fddi_chars(pdq, (const pdq_response_status_chars_get_t *) rspgen); + } + + PDQ_ADVANCE(ci->ci_request_completion, 1, PDQ_RING_MASK(dbp->pdqdb_command_requests)); + PDQ_ADVANCE(ci->ci_response_completion, 1, PDQ_RING_MASK(dbp->pdqdb_command_responses)); + ci->ci_response_free++; + ci->ci_request_free++; + ci->ci_buffree += txd->txd_seg_len; + } + + if (cmds > 0) { + *csrs->csr_cmd_response_producer = ci->ci_response_producer | (ci->ci_response_completion << 8); + *csrs->csr_cmd_request_producer = ci->ci_request_producer | (ci->ci_request_completion << 8); + if (ci->ci_pending_commands != 0) + pdq_queue_commands(pdq); + } +} + +/* + * This following routine processes unsolicited events. + * In addition, it also fills the unsolicited queue with + * event buffers so it can be used to initialize the queue + * as well. + */ +void +pdq_process_unsolicited_events( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_unsolicited_info_t *ui = &pdq->pdq_unsolicited_info; + volatile const pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + const pdq_unsolicited_event_t *event; + pdq_rxdesc_t *rxd; + + /* + * Process each unsolicited event (if any). + */ + + while (cbp->pdqcb_unsolicited_event != ui->ui_completion) { + rxd = &dbp->pdqdb_unsolicited_events[ui->ui_completion]; + event = &ui->ui_events[ui->ui_completion & (PDQ_NUM_UNSOLICITED_EVENTS-1)]; + + switch (event->event_type) { + case PDQ_UNSOLICITED_EVENT: { + printf(PDQ_OS_PREFIX "Unsolicited Event: %s: %s", + PDQ_OS_PREFIX_ARGS, + pdq_entities[event->event_entity], + pdq_event_codes[event->event_entity][event->event_code.value]); + if (event->event_type == PDQ_ENTITY_PHY_PORT) + printf("[%d]", event->event_index); + printf("\n"); + break; + } + case PDQ_UNSOLICITED_COUNTERS: { + break; + } + } + PDQ_ADVANCE(ui->ui_completion, 1, PDQ_RING_MASK(dbp->pdqdb_unsolicited_events)); + ui->ui_free++; + } + + /* + * Now give back the event buffers back to the PDQ. + */ + PDQ_ADVANCE(ui->ui_producer, ui->ui_free, PDQ_RING_MASK(dbp->pdqdb_unsolicited_events)); + ui->ui_free = 0; + + *csrs->csr_unsolicited_producer = ui->ui_producer | (ui->ui_completion << 8); +} + +void +pdq_process_received_data( + pdq_t *pdq, + pdq_rx_info_t *rx, + pdq_rxdesc_t *receives, + pdq_uint32_t completion_goal, + pdq_uint32_t ring_mask) +{ + pdq_uint32_t completion = rx->rx_completion; + PDQ_OS_DATABUF_T **buffers = (PDQ_OS_DATABUF_T **) rx->rx_buffers; + pdq_rxdesc_t *rxd; + pdq_uint32_t idx; + + while (completion != completion_goal) { + PDQ_OS_DATABUF_T *fpdu, *lpdu, *npdu; + pdq_uint8_t *dataptr; + pdq_uint32_t fc, datalen, pdulen, segcnt; + pdq_rxstatus_t status; + + fpdu = lpdu = buffers[completion]; + PDQ_ASSERT(fpdu != NULL); + + dataptr = PDQ_OS_DATABUF_PTR(fpdu); + status = *(pdq_rxstatus_t *) dataptr; + if ((status.rxs_status & 0x200000) == 0) { + datalen = status.rxs_status & 0x1FFF; + fc = dataptr[PDQ_RX_FC_OFFSET]; + switch (fc & (PDQ_FDDIFC_C|PDQ_FDDIFC_L|PDQ_FDDIFC_F)) { + case PDQ_FDDI_LLC_ASYNC: + case PDQ_FDDI_LLC_SYNC: + case PDQ_FDDI_IMP_ASYNC: + case PDQ_FDDI_IMP_SYNC: { + if (datalen > PDQ_FDDI_MAX || datalen < PDQ_FDDI_LLC_MIN) { + printf("discard: bad length %d\n", datalen); + goto discard_frame; + } + break; + } + case PDQ_FDDI_SMT: { + if (datalen > PDQ_FDDI_MAX || datalen < PDQ_FDDI_SMT_MIN) + goto discard_frame; + break; + } + default: { + printf("discard: bad fc 0x%x\n", fc); + goto discard_frame; + } + } + /* + * Update the lengths of the data buffers now that we know + * the real length. + */ + pdulen = datalen - 4 /* CRC */ + 1 /* FC */; + segcnt = (pdulen + PDQ_RX_FC_OFFSET + PDQ_OS_DATABUF_SIZE - 1) / PDQ_OS_DATABUF_SIZE; + PDQ_OS_DATABUF_ALLOC(npdu); + if (npdu == NULL) { + printf("discard: no databuf #0\n"); + goto discard_frame; + } + buffers[completion] = npdu; + for (idx = 1; idx < segcnt; idx++) { + PDQ_OS_DATABUF_ALLOC(npdu); + if (npdu == NULL) { + PDQ_OS_DATABUF_NEXT_SET(lpdu, NULL); + PDQ_OS_DATABUF_FREE(fpdu); + goto discard_frame; + } + PDQ_OS_DATABUF_NEXT_SET(lpdu, buffers[(completion + idx) & ring_mask]); + lpdu = PDQ_OS_DATABUF_NEXT(lpdu); + buffers[(completion + idx) & ring_mask] = npdu; + } + PDQ_OS_DATABUF_NEXT_SET(lpdu, NULL); + for (idx = 0; idx < PDQ_RX_SEGCNT; idx++) { + buffers[(rx->rx_producer + idx) & ring_mask] = + buffers[(completion + idx) & ring_mask]; + buffers[(completion + idx) & ring_mask] = NULL; + } + PDQ_OS_DATABUF_PTR_ADJ(fpdu, PDQ_RX_FC_OFFSET); + if (segcnt == 1) { + PDQ_OS_DATABUF_LEN_SET(fpdu, pdulen); + } else { + PDQ_OS_DATABUF_LEN_SET(lpdu, pdulen + PDQ_RX_FC_OFFSET - (segcnt - 1) * PDQ_OS_DATABUF_SIZE); + PDQ_OS_DATABUF_LEN_ADJ(fpdu, -PDQ_RX_FC_OFFSET); + } + pdq_os_receive_pdu(pdq, fpdu, pdulen); + rx->rx_free += PDQ_RX_SEGCNT; + PDQ_ADVANCE(completion, PDQ_RX_SEGCNT, ring_mask); + continue; + } else { + printf("discard: bad pdu 0x%x(%d.%d.%d.%d.%d)\n", status.rxs_status, + status.rxs_rcc_badpdu, status.rxs_rcc_badcrc, + status.rxs_rcc_reason, status.rxs_fsc, status.rxs_fsb_e); + if (status.rxs_rcc_reason == 7) + goto discard_frame; + if (status.rxs_rcc_reason != 0) + /* hardware fault */ + if (status.rxs_rcc_badcrc) { + /* rx->rx_badcrc++; */ + } else if (status.rxs_fsc == 0 | status.rxs_fsb_e == 1) { + /* rx->rx_frame_status_errors++; */ + } else { + /* hardware fault */ + } + } + discard_frame: + /* + * Discarded frames go right back on the queue; therefore + * ring entries were freed. + */ + for (idx = 0; idx < PDQ_RX_SEGCNT; idx++) { + buffers[rx->rx_producer] = buffers[completion]; + buffers[completion] = NULL; + rxd = &receives[rx->rx_producer]; + if (idx == 0) { + rxd->rxd_sop = 1; rxd->rxd_seg_cnt = PDQ_RX_SEGCNT - 1; + } else { + rxd->rxd_sop = 0; rxd->rxd_seg_cnt = 0; + } + rxd->rxd_pa_hi = 0; + rxd->rxd_seg_len_hi = PDQ_OS_DATABUF_SIZE / 16; + rxd->rxd_pa_lo = PDQ_OS_VA_TO_PA(PDQ_OS_DATABUF_PTR(buffers[rx->rx_producer])); + PDQ_ADVANCE(rx->rx_producer, 1, ring_mask); + PDQ_ADVANCE(completion, 1, ring_mask); + } + } + rx->rx_completion = completion; + + while (rx->rx_free > PDQ_RX_SEGCNT && rx->rx_free > rx->rx_target) { + PDQ_OS_DATABUF_T *pdu; + /* + * Allocate the needed number of data buffers. + * Try to obtain them from our free queue before + * asking the system for more. + */ + for (idx = 0; idx < PDQ_RX_SEGCNT; idx++) { + if ((pdu = buffers[(rx->rx_producer + idx) & ring_mask]) == NULL) { + PDQ_OS_DATABUF_ALLOC(pdu); + if (pdu == NULL) + break; + buffers[(rx->rx_producer + idx) & ring_mask] = pdu; + } + rxd = &receives[(rx->rx_producer + idx) & ring_mask]; + if (idx == 0) { + rxd->rxd_sop = 1; rxd->rxd_seg_cnt = PDQ_RX_SEGCNT - 1; + } else { + rxd->rxd_sop = 0; rxd->rxd_seg_cnt = 0; + } + rxd->rxd_pa_hi = 0; + rxd->rxd_seg_len_hi = PDQ_OS_DATABUF_SIZE / 16; + rxd->rxd_pa_lo = PDQ_OS_VA_TO_PA(PDQ_OS_DATABUF_PTR(pdu)); + } + if (idx < PDQ_RX_SEGCNT) { + /* + * We didn't get all databufs required to complete a new + * receive buffer. Keep the ones we got and retry a bit + * later for the rest. + */ + break; + } + PDQ_ADVANCE(rx->rx_producer, PDQ_RX_SEGCNT, ring_mask); + rx->rx_free -= PDQ_RX_SEGCNT; + } +} + +pdq_boolean_t +pdq_queue_transmit_data( + pdq_t *pdq, + PDQ_OS_DATABUF_T *pdu) +{ + pdq_tx_info_t *tx = &pdq->pdq_tx_info; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + pdq_uint32_t producer = tx->tx_producer; + pdq_txdesc_t *eop = NULL; + PDQ_OS_DATABUF_T *pdu0; + pdq_uint32_t freecnt; + + dbp->pdqdb_transmits[producer] = tx->tx_hdrdesc; + PDQ_ADVANCE(producer, 1, PDQ_RING_MASK(dbp->pdqdb_transmits)); + + for (freecnt = tx->tx_free - 1, pdu0 = pdu; pdu0 != NULL && freecnt > 0;) { + pdq_uint32_t fraglen, datalen = PDQ_OS_DATABUF_LEN(pdu0); + const pdq_uint8_t *dataptr = PDQ_OS_DATABUF_PTR(pdu0); + + /* + * The first segment is limited to the space remaining in + * page. All segments after that can be up to a full page + * in size. + */ + fraglen = PDQ_OS_PAGESIZE - ((dataptr - (pdq_uint8_t *) NULL) & (PDQ_OS_PAGESIZE-1)); + while (datalen > 0 && freecnt > 0) { + pdq_uint32_t seglen = (fraglen < datalen ? fraglen : datalen); + + /* + * Initialize the transmit descriptor + */ + eop = &dbp->pdqdb_transmits[producer]; + eop->txd_seg_len = seglen; + eop->txd_pa_lo = PDQ_OS_VA_TO_PA(dataptr); + eop->txd_sop = eop->txd_eop = eop->txd_pa_hi = 0; + + datalen -= seglen; + dataptr += seglen; + fraglen = PDQ_OS_PAGESIZE; + freecnt--; + PDQ_ADVANCE(producer, 1, PDQ_RING_MASK(dbp->pdqdb_transmits)); + } + pdu0 = PDQ_OS_DATABUF_NEXT(pdu0); + } + if (pdu0 != NULL) { + PDQ_ASSERT(free == 0); + /* + * If we still have data to process then the ring was too full + * to store the PDU. Return FALSE so the caller will requeue + * the PDU for later. + */ + return PDQ_FALSE; + } + /* + * Everything went fine. Finish it up. + */ + tx->tx_descriptor_count[tx->tx_producer] = tx->tx_free - freecnt; + eop->txd_eop = 1; + PDQ_OS_DATABUF_ENQUEUE(&tx->tx_txq, pdu); + tx->tx_producer = producer; + tx->tx_free = freecnt; + PDQ_DO_TYPE2_PRODUCER(pdq); + return PDQ_TRUE; +} + +void +pdq_process_transmitted_data( + pdq_t *pdq) +{ + pdq_tx_info_t *tx = &pdq->pdq_tx_info; + volatile const pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_descriptor_block_t *dbp = pdq->pdq_dbp; + pdq_uint32_t completion = tx->tx_completion; + + while (completion != cbp->pdqcb_transmits) { + PDQ_OS_DATABUF_T *pdu; + pdq_uint32_t descriptor_count = tx->tx_descriptor_count[completion]; + PDQ_ASSERT(dbp->pdqdb_transmits[tx->tx_completion].txd_sop == 1); + PDQ_ASSERT(dbp->pdqdb_transmits[(completion + descriptor_count - 1) & PDQ_RING_MASK(dbp->pdqdb_transmits)].txd_eop == 1); + PDQ_OS_DATABUF_DEQUEUE(&tx->tx_txq, pdu); + pdq_os_transmit_done(pdq, pdu); + tx->tx_free += descriptor_count; + + PDQ_ADVANCE(completion, descriptor_count, PDQ_RING_MASK(dbp->pdqdb_transmits)); + } + if (tx->tx_completion != completion) { + tx->tx_completion = completion; + pdq_os_restart_transmitter(pdq); + } + PDQ_DO_TYPE2_PRODUCER(pdq); +} + +void +pdq_flush_transmitter( + pdq_t *pdq) +{ + volatile pdq_consumer_block_t *cbp = pdq->pdq_cbp; + pdq_tx_info_t *tx = &pdq->pdq_tx_info; + + for (;;) { + PDQ_OS_DATABUF_T *pdu; + PDQ_OS_DATABUF_DEQUEUE(&tx->tx_txq, pdu); + if (pdu == NULL) + break; + /* + * Don't call transmit done since the packet never made it + * out on the wire. + */ + PDQ_OS_DATABUF_FREE(pdu); + } + + tx->tx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_transmits); + tx->tx_completion = cbp->pdqcb_transmits = tx->tx_producer; + + PDQ_DO_TYPE2_PRODUCER(pdq); +} + +/* + * The following routine brings the PDQ from whatever state it is + * in to DMA_UNAVAILABLE (ie. like a RESET but without doing a RESET). + */ +pdq_state_t +pdq_stop( + pdq_t *pdq) +{ + pdq_state_t state; + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + int cnt, pass = 0, idx; + PDQ_OS_DATABUF_T **buffers; + + restart: + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state != PDQS_DMA_UNAVAILABLE) { + *csrs->csr_port_data_a = (state == PDQS_HALTED) ? 0 : PDQ_PRESET_SKIP_SELFTEST; + *csrs->csr_port_reset = 1; + PDQ_OS_USEC_DELAY(100); + *csrs->csr_port_reset = 0; + for (cnt = 45000;;cnt--) { + PDQ_OS_USEC_DELAY(1000); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state == PDQS_DMA_UNAVAILABLE || cnt == 0) + break; + } + PDQ_PRINTF(("PDQ Reset spun %d cycles\n", 45000 - cnt)); + PDQ_OS_USEC_DELAY(10000); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_UNAVAILABLE); + PDQ_ASSERT(cnt > 0); + } +#if 0 + switch (state) { + case PDQS_RING_MEMBER: + case PDQS_LINK_UNAVAILABLE: + case PDQS_LINK_AVAILABLE: { + *csrs->csr_port_data_a = PDQ_SUB_CMD_LINK_UNINIT; + *csrs->csr_port_data_b = 0; + pdq_do_port_control(csrs, PDQ_PCTL_SUB_CMD); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_AVAILABLE); + /* FALL THROUGH */ + } + case PDQS_DMA_AVAILABLE: { + *csrs->csr_port_data_a = 0; + *csrs->csr_port_data_b = 0; + pdq_do_port_control(csrs, PDQ_PCTL_DMA_UNINIT); + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state == PDQS_DMA_UNAVAILABLE); + /* FALL THROUGH */ + } + case PDQS_DMA_UNAVAILABLE: { + break; + } + } +#endif + /* + * Now we should be in DMA_UNAVAILABLE. So bring the PDQ into + * DMA_AVAILABLE. + */ + + /* + * Obtain the hardware address and firmware revisions + * (MLA = my long address which is FDDI speak for hardware address) + */ + pdq_read_mla(&pdq->pdq_csrs, &pdq->pdq_hwaddr); + pdq_read_fwrev(&pdq->pdq_csrs, &pdq->pdq_fwrev); + pdq->pdq_chip_rev = pdq_read_chiprev(&pdq->pdq_csrs); + + if (pdq->pdq_type == PDQ_DEFPA) { + /* + * Disable interrupts and DMA. + */ + *pdq->pdq_pci_csrs.csr_pfi_mode_control = 0; + *pdq->pdq_pci_csrs.csr_pfi_status = 0x10; + } + + /* + * Flush all the databuf queues. + */ + pdq_flush_databuf_queue(&pdq->pdq_tx_info.tx_txq); + buffers = (PDQ_OS_DATABUF_T **) pdq->pdq_rx_info.rx_buffers; + for (idx = 0; idx < PDQ_RING_SIZE(pdq->pdq_dbp->pdqdb_receives); idx++) { + if (buffers[idx] != NULL) { + PDQ_OS_DATABUF_FREE(buffers[idx]); + buffers[idx] = NULL; + } + } + pdq->pdq_rx_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives); + buffers = (PDQ_OS_DATABUF_T **) pdq->pdq_host_smt_info.rx_buffers; + for (idx = 0; idx < PDQ_RING_SIZE(pdq->pdq_dbp->pdqdb_host_smt); idx++) { + if (buffers[idx] != NULL) { + PDQ_OS_DATABUF_FREE(buffers[idx]); + buffers[idx] = NULL; + } + } + pdq->pdq_host_smt_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt); + + /* + * Reset the consumer indexes to 0. + */ + pdq->pdq_cbp->pdqcb_receives = 0; + pdq->pdq_cbp->pdqcb_transmits = 0; + pdq->pdq_cbp->pdqcb_host_smt = 0; + pdq->pdq_cbp->pdqcb_unsolicited_event = 0; + pdq->pdq_cbp->pdqcb_command_response = 0; + pdq->pdq_cbp->pdqcb_command_request = 0; + + /* + * Reset the producer and completion indexes to 0. + */ + pdq->pdq_command_info.ci_request_producer = 0; + pdq->pdq_command_info.ci_response_producer = 0; + pdq->pdq_command_info.ci_request_completion = 0; + pdq->pdq_command_info.ci_response_completion = 0; + pdq->pdq_unsolicited_info.ui_producer = 0; + pdq->pdq_unsolicited_info.ui_completion = 0; + pdq->pdq_rx_info.rx_producer = 0; + pdq->pdq_rx_info.rx_completion = 0; + pdq->pdq_tx_info.tx_producer = 0; + pdq->pdq_tx_info.tx_completion = 0; + pdq->pdq_host_smt_info.rx_producer = 0; + pdq->pdq_host_smt_info.rx_completion = 0; + + pdq->pdq_command_info.ci_request_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_command_requests); + pdq->pdq_command_info.ci_response_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_command_responses); + pdq->pdq_unsolicited_info.ui_free = PDQ_NUM_UNSOLICITED_EVENTS; + pdq->pdq_tx_info.tx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_transmits); + + /* + * Allow the DEFPA to do DMA. Then program the physical + * addresses of the consumer and descriptor blocks. + */ + if (pdq->pdq_type == PDQ_DEFPA) { +#ifdef PDQTEST + *pdq->pdq_pci_csrs.csr_pfi_mode_control = PDQ_PFI_MODE_DMA_ENABLE; +#else + *pdq->pdq_pci_csrs.csr_pfi_mode_control = PDQ_PFI_MODE_DMA_ENABLE + |PDQ_PFI_MODE_PFI_PCI_INTR|PDQ_PFI_MODE_PDQ_PCI_INTR; +#endif + } + + /* + * Make the unsolicited queue has events ... + */ + pdq_process_unsolicited_events(pdq); + + *csrs->csr_port_data_b = PDQ_DMA_BURST_8LW; + *csrs->csr_port_data_a = PDQ_SUB_CMD_DMA_BURST_SIZE_SET; + pdq_do_port_control(csrs, PDQ_PCTL_SUB_CMD); + + *csrs->csr_port_data_b = 0; + *csrs->csr_port_data_a = PDQ_OS_VA_TO_PA(pdq->pdq_cbp); + pdq_do_port_control(csrs, PDQ_PCTL_CONSUMER_BLOCK); + + *csrs->csr_port_data_b = 0; + *csrs->csr_port_data_a = PDQ_OS_VA_TO_PA(pdq->pdq_dbp) | PDQ_DMA_INIT_LW_BSWAP_DATA; + pdq_do_port_control(csrs, PDQ_PCTL_DMA_INIT); + + for (cnt = 0; cnt < 1000; cnt++) { + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + if (state == PDQS_HALTED) { + if (pass > 0) + return PDQS_HALTED; + pass = 1; + goto restart; + } + if (state == PDQS_DMA_AVAILABLE) { + PDQ_PRINTF(("Transition to DMA Available took %d spins\n", cnt)); + break; + } + PDQ_OS_USEC_DELAY(1000); + } + PDQ_ASSERT(state == PDQS_DMA_AVAILABLE); + + *csrs->csr_host_int_type_0 = 0xFF; + *csrs->csr_host_int_enable = 0 /* PDQ_HOST_INT_STATE_CHANGE + |PDQ_HOST_INT_FATAL_ERROR|PDQ_HOST_INT_CMD_RSP_ENABLE + |PDQ_HOST_INT_UNSOL_ENABLE */; + + /* + * Any other command but START should be valid. + */ + pdq->pdq_command_info.ci_pending_commands &= ~(PDQ_BITMASK(PDQC_START)); + if (pdq->pdq_flags & PDQ_PRINTCHARS) + pdq->pdq_command_info.ci_pending_commands |= PDQ_BITMASK(PDQC_STATUS_CHARS_GET); + pdq_queue_commands(pdq); + + if (pdq->pdq_flags & PDQ_PRINTCHARS) { + /* + * Now wait (up to 100ms) for the command(s) to finish. + */ + for (cnt = 0; cnt < 1000; cnt++) { + pdq_process_command_responses(pdq); + if (pdq->pdq_command_info.ci_response_producer == pdq->pdq_command_info.ci_response_completion) + break; + PDQ_OS_USEC_DELAY(1000); + } + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + } + + return state; +} + +void +pdq_run( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_state_t state; + + state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + PDQ_ASSERT(state != PDQS_DMA_UNAVAILABLE); + PDQ_ASSERT(state != PDQS_RESET); + PDQ_ASSERT(state != PDQS_HALTED); + PDQ_ASSERT(state != PDQS_UPGRADE); + PDQ_ASSERT(state != PDQS_RING_MEMBER); + switch (state) { + case PDQS_DMA_AVAILABLE: { + /* + * The PDQ after being reset screws up some of its state. + * So we need to clear all the errors/interrupts so the real + * ones will get through. + */ + *csrs->csr_host_int_type_0 = 0xFF; + *csrs->csr_host_int_enable = PDQ_HOST_INT_STATE_CHANGE|PDQ_HOST_INT_XMT_DATA_FLUSH + |PDQ_HOST_INT_FATAL_ERROR|PDQ_HOST_INT_CMD_RSP_ENABLE|PDQ_HOST_INT_UNSOL_ENABLE + |PDQ_HOST_INT_RX_ENABLE|PDQ_HOST_INT_TX_ENABLE|PDQ_HOST_INT_HOST_SMT_ENABLE; + /* + * Set the MAC and address filters and start up the PDQ. + */ + pdq_process_unsolicited_events(pdq); + pdq_process_received_data(pdq, &pdq->pdq_rx_info, + pdq->pdq_dbp->pdqdb_receives, + pdq->pdq_cbp->pdqcb_receives, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives)); + PDQ_DO_TYPE2_PRODUCER(pdq); + if (pdq->pdq_flags & PDQ_PASS_SMT) { + pdq_process_received_data(pdq, &pdq->pdq_host_smt_info, + pdq->pdq_dbp->pdqdb_host_smt, + pdq->pdq_cbp->pdqcb_host_smt, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt)); + *csrs->csr_host_smt_producer = pdq->pdq_host_smt_info.rx_producer | (pdq->pdq_host_smt_info.rx_completion << 8); + } + pdq->pdq_command_info.ci_pending_commands = PDQ_BITMASK(PDQC_FILTER_SET) + | PDQ_BITMASK(PDQC_ADDR_FILTER_SET) | PDQ_BITMASK(PDQC_START); + if (pdq->pdq_flags & PDQ_PRINTCHARS) + pdq->pdq_command_info.ci_pending_commands |= PDQ_BITMASK(PDQC_STATUS_CHARS_GET); + pdq_queue_commands(pdq); + break; + } + case PDQS_LINK_UNAVAILABLE: + case PDQS_LINK_AVAILABLE: { + pdq->pdq_command_info.ci_pending_commands = PDQ_BITMASK(PDQC_FILTER_SET) + | PDQ_BITMASK(PDQC_ADDR_FILTER_SET); + if (pdq->pdq_flags & PDQ_PRINTCHARS) + pdq->pdq_command_info.ci_pending_commands |= PDQ_BITMASK(PDQC_STATUS_CHARS_GET); + if (pdq->pdq_flags & PDQ_PASS_SMT) { + pdq_process_received_data(pdq, &pdq->pdq_host_smt_info, + pdq->pdq_dbp->pdqdb_host_smt, + pdq->pdq_cbp->pdqcb_host_smt, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt)); + *csrs->csr_host_smt_producer = pdq->pdq_host_smt_info.rx_producer | (pdq->pdq_host_smt_info.rx_completion << 8); + } + pdq_process_unsolicited_events(pdq); + pdq_queue_commands(pdq); + break; + } + case PDQS_RING_MEMBER: { + } + } +} + +int +pdq_interrupt( + pdq_t *pdq) +{ + const pdq_csrs_t * const csrs = &pdq->pdq_csrs; + pdq_uint32_t data; + int progress = 0; + + if (pdq->pdq_type == PDQ_DEFPA) + if (*pdq->pdq_pci_csrs.csr_pfi_status & 0x10) + *pdq->pdq_pci_csrs.csr_pfi_status = 0x10; + + while ((data = *csrs->csr_port_status) & PDQ_PSTS_INTR_PENDING) { + progress = 1; + PDQ_PRINTF(("PDQ Interrupt: Status = 0x%08x\n", data)); + if (data & PDQ_PSTS_RCV_DATA_PENDING) { + pdq_process_received_data(pdq, &pdq->pdq_rx_info, + pdq->pdq_dbp->pdqdb_receives, + pdq->pdq_cbp->pdqcb_receives, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives)); + PDQ_DO_TYPE2_PRODUCER(pdq); + } + if (data & PDQ_PSTS_HOST_SMT_PENDING) { + pdq_process_received_data(pdq, &pdq->pdq_host_smt_info, + pdq->pdq_dbp->pdqdb_host_smt, + pdq->pdq_cbp->pdqcb_host_smt, + PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt)); + *csrs->csr_host_smt_producer = pdq->pdq_host_smt_info.rx_producer | (pdq->pdq_host_smt_info.rx_completion << 8); + } + if (data & PDQ_PSTS_XMT_DATA_PENDING) + pdq_process_transmitted_data(pdq); + if (data & PDQ_PSTS_UNSOL_PENDING) + pdq_process_unsolicited_events(pdq); + if (data & PDQ_PSTS_CMD_RSP_PENDING) + pdq_process_command_responses(pdq); + if (data & PDQ_PSTS_TYPE_0_PENDING) { + data = *csrs->csr_host_int_type_0; + if (data & PDQ_HOST_INT_STATE_CHANGE) { + pdq_state_t state = PDQ_PSTS_ADAPTER_STATE(*csrs->csr_port_status); + printf(PDQ_OS_PREFIX "%s", PDQ_OS_PREFIX_ARGS, pdq_adapter_states[state]); + if (state == PDQS_LINK_UNAVAILABLE) { + pdq->pdq_flags &= ~PDQ_TXOK; + } else if (state == PDQS_LINK_AVAILABLE) { + pdq->pdq_flags |= PDQ_TXOK; + pdq_os_restart_transmitter(pdq); + } else if (state == PDQS_HALTED) { + pdq_response_error_log_get_t log_entry; + pdq_halt_code_t halt_code = PDQ_PSTS_HALT_ID(*csrs->csr_port_status); + printf(": halt code = %d (%s)\n", + halt_code, pdq_halt_codes[halt_code]); + if (halt_code == PDQH_DMA_ERROR) { + PDQ_PRINTF(("\tPFI status = 0x%x, Host 0 Fatal Interrupt = 0x%x\n", + *pdq->pdq_pci_csrs.csr_pfi_status, + data & PDQ_HOST_INT_FATAL_ERROR)); + } + pdq_read_error_log(pdq, &log_entry); + pdq_stop(pdq); + if (pdq->pdq_flags & PDQ_RUNNING) + pdq_run(pdq); + return 1; + } + printf("\n"); + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_STATE_CHANGE; + } + if (data & PDQ_HOST_INT_FATAL_ERROR) { + pdq_stop(pdq); + if (pdq->pdq_flags & PDQ_RUNNING) + pdq_run(pdq); + return 1; + } + if (data & PDQ_HOST_INT_XMT_DATA_FLUSH) { + printf(PDQ_OS_PREFIX "Flushing transmit queue\n", PDQ_OS_PREFIX_ARGS); + pdq->pdq_flags &= ~PDQ_TXOK; + pdq_flush_transmitter(pdq); + pdq_do_port_control(csrs, PDQ_PCTL_XMT_DATA_FLUSH_DONE); + *csrs->csr_host_int_type_0 = PDQ_HOST_INT_XMT_DATA_FLUSH; + } + } + if (pdq->pdq_type == PDQ_DEFPA) + if (*pdq->pdq_pci_csrs.csr_pfi_status & 0x10) + *pdq->pdq_pci_csrs.csr_pfi_status = 0x10; + } + return progress; +} + +pdq_t * +pdq_initialize( + void *csr_va, + const char *name, + int unit, + void *ctx, + pdq_type_t type) +{ + pdq_t *pdq; + pdq_state_t state; + const pdq_uint32_t contig_bytes = (sizeof(pdq_descriptor_block_t) * 2) - PDQ_OS_PAGESIZE; + pdq_uint8_t *p; + int idx; + + PDQ_ASSERT(sizeof(pdq_descriptor_block_t) == 8192); + PDQ_ASSERT(sizeof(pdq_consumer_block_t) == 64); + PDQ_ASSERT(sizeof(pdq_response_filter_get_t) == PDQ_SIZE_RESPONSE_FILTER_GET); + PDQ_ASSERT(sizeof(pdq_cmd_addr_filter_set_t) == PDQ_SIZE_CMD_ADDR_FILTER_SET); + PDQ_ASSERT(sizeof(pdq_response_addr_filter_get_t) == PDQ_SIZE_RESPONSE_ADDR_FILTER_GET); + PDQ_ASSERT(sizeof(pdq_response_status_chars_get_t) == PDQ_SIZE_RESPONSE_STATUS_CHARS_GET); + PDQ_ASSERT(sizeof(pdq_response_fddi_mib_get_t) == PDQ_SIZE_RESPONSE_FDDI_MIB_GET); + PDQ_ASSERT(sizeof(pdq_response_dec_ext_mib_get_t) == PDQ_SIZE_RESPONSE_DEC_EXT_MIB_GET); + PDQ_ASSERT(sizeof(pdq_unsolicited_event_t) == 512); + + pdq = (pdq_t *) PDQ_OS_MEMALLOC(sizeof(pdq_t)); + if (pdq == NULL) { + PDQ_PRINTF(("malloc(%d) failed\n", sizeof(*pdq))); + return NULL; + } + PDQ_OS_MEMZERO(pdq, sizeof(pdq_t)); + pdq->pdq_type = type; + pdq->pdq_unit = unit; + pdq->pdq_os_ctx = (void *) ctx; + pdq->pdq_os_name = name; + pdq->pdq_flags = PDQ_PRINTCHARS; + /* + * Allocate the additional data structures required by + * the PDQ driver. Allocate a contiguous region of memory + * for the descriptor block. We need to allocated enough + * to guarantee that we will a get 8KB block of memory aligned + * on a 8KB boundary. This turns to require that we allocate + * (N*2 - 1 page) pages of memory. On machine with less than + * a 8KB page size, it mean we will allocate more memory than + * we need. The extra will be used for the unsolicited event + * buffers (though on machines with 8KB pages we will to allocate + * them separately since there will be nothing left overs.) + */ + p = (pdq_uint8_t *) PDQ_OS_MEMALLOC_CONTIG(contig_bytes); + if (p != NULL) { + pdq_physaddr_t physaddr = PDQ_OS_VA_TO_PA(p) & 0x1FFF; + if (physaddr) { + pdq->pdq_unsolicited_info.ui_events = (pdq_unsolicited_event_t *) p; + pdq->pdq_dbp = (pdq_descriptor_block_t *) &p[0x2000 - physaddr]; + } else { + pdq->pdq_dbp = (pdq_descriptor_block_t *) p; + pdq->pdq_unsolicited_info.ui_events = (pdq_unsolicited_event_t *) &p[0x2000]; + } + } + if (contig_bytes == sizeof(pdq_descriptor_block_t)) { + pdq->pdq_unsolicited_info.ui_events = + (pdq_unsolicited_event_t *) PDQ_OS_MEMALLOC( + PDQ_NUM_UNSOLICITED_EVENTS * sizeof(pdq_unsolicited_event_t)); + } + + /* + * Make sure everything got allocated. If not, free what did + * get allocated and return. + */ + if (pdq->pdq_dbp == NULL || pdq->pdq_unsolicited_info.ui_events == NULL) { + cleanup_and_return: + if (pdq->pdq_dbp != NULL) + PDQ_OS_MEMFREE_CONTIG(pdq->pdq_dbp, contig_bytes); + if (contig_bytes == sizeof(pdq_descriptor_block_t) && pdq->pdq_unsolicited_info.ui_events != NULL) + PDQ_OS_MEMFREE(pdq->pdq_unsolicited_info.ui_events, + PDQ_NUM_UNSOLICITED_EVENTS * sizeof(pdq_unsolicited_event_t)); + PDQ_OS_MEMFREE(pdq, sizeof(pdq_t)); + return NULL; + } + + pdq->pdq_cbp = (volatile pdq_consumer_block_t *) &pdq->pdq_dbp->pdqdb_consumer; + pdq->pdq_command_info.ci_bufstart = (pdq_uint8_t *) pdq->pdq_dbp->pdqdb_command_pool; + pdq->pdq_rx_info.rx_buffers = (void *) pdq->pdq_dbp->pdqdb_receive_buffers; + + pdq->pdq_host_smt_info.rx_buffers = (void *) pdq->pdq_dbp->pdqdb_host_smt_buffers; + + PDQ_PRINTF(("PDQ Descriptor Block = %p\n", pdq->pdq_dbp)); + PDQ_PRINTF((" Recieve Queue = %p\n", pdq->pdq_dbp->pdqdb_receives)); + PDQ_PRINTF((" Transmit Queue = %p\n", pdq->pdq_dbp->pdqdb_transmits)); + PDQ_PRINTF((" Host SMT Queue = %p\n", pdq->pdq_dbp->pdqdb_host_smt)); + PDQ_PRINTF((" Command Response Queue = %p\n", pdq->pdq_dbp->pdqdb_command_responses)); + PDQ_PRINTF((" Command Request Queue = %p\n", pdq->pdq_dbp->pdqdb_command_requests)); + PDQ_PRINTF(("PDQ Consumer Block = %p\n", pdq->pdq_cbp)); + + /* + * Zero out the descriptor block. Not really required but + * it pays to be neat. This will also zero out the consumer + * block, command pool, and buffer pointers for the receive + * host_smt rings. + */ + PDQ_OS_MEMZERO(pdq->pdq_dbp, sizeof(*pdq->pdq_dbp)); + + /* + * Initialize the CSR references. + */ + pdq_init_csrs(&pdq->pdq_csrs, csr_va, 1); + switch (pdq->pdq_type) { + case PDQ_DEFPA: pdq_init_pci_csrs(&pdq->pdq_pci_csrs, csr_va, 1); break; +#ifdef PDQ_DO_EISA + case PDQ_DEFEA: pdq_init_esia_csrs(&pdq->pdq_eisa_csrs, csr_va, 1); break; +#endif + } + + PDQ_PRINTF(("PDQ CSRs:\n")); + PDQ_PRINTF((" Port Reset = %p [0x%08x]\n", + pdq->pdq_csrs.csr_port_reset, *pdq->pdq_csrs.csr_port_reset)); + PDQ_PRINTF((" Host Data = %p [0x%08x]\n", + pdq->pdq_csrs.csr_host_data, *pdq->pdq_csrs.csr_host_data)); + PDQ_PRINTF((" Port Control = %p [0x%08x]\n", + pdq->pdq_csrs.csr_port_control, *pdq->pdq_csrs.csr_port_control)); + PDQ_PRINTF((" Port Data A = %p [0x%08x]\n", + pdq->pdq_csrs.csr_port_data_a, *pdq->pdq_csrs.csr_port_data_a)); + PDQ_PRINTF((" Port Data B = %p [0x%08x]\n", + pdq->pdq_csrs.csr_port_data_b, *pdq->pdq_csrs.csr_port_data_b)); + PDQ_PRINTF((" Port Status = %p [0x%08x]\n", + pdq->pdq_csrs.csr_port_status, *pdq->pdq_csrs.csr_port_status)); + PDQ_PRINTF((" Host Int Type 0 = %p [0x%08x]\n", + pdq->pdq_csrs.csr_host_int_type_0, *pdq->pdq_csrs.csr_host_int_type_0)); + PDQ_PRINTF((" Host Int Enable = %p [0x%08x]\n", + pdq->pdq_csrs.csr_host_int_enable, *pdq->pdq_csrs.csr_host_int_enable)); + PDQ_PRINTF((" Type 2 Producer = %p [0x%08x]\n", + pdq->pdq_csrs.csr_type_2_producer, *pdq->pdq_csrs.csr_type_2_producer)); + PDQ_PRINTF((" Command Response Producer = %p [0x%08x]\n", + pdq->pdq_csrs.csr_cmd_response_producer, *pdq->pdq_csrs.csr_cmd_response_producer)); + PDQ_PRINTF((" Command Request Producer = %p [0x%08x]\n", + pdq->pdq_csrs.csr_cmd_request_producer, *pdq->pdq_csrs.csr_cmd_request_producer)); + PDQ_PRINTF((" Host SMT Producer = %p [0x%08x]\n", + pdq->pdq_csrs.csr_host_smt_producer, *pdq->pdq_csrs.csr_host_smt_producer)); + PDQ_PRINTF((" Unsolicited Producer = %p [0x%08x]\n", + pdq->pdq_csrs.csr_unsolicited_producer, *pdq->pdq_csrs.csr_unsolicited_producer)); + + /* + * Initialize the command information block + */ + pdq->pdq_command_info.ci_buffree = sizeof(pdq->pdq_dbp->pdqdb_command_pool); + pdq->pdq_command_info.ci_bufend = pdq->pdq_command_info.ci_bufstart + pdq->pdq_command_info.ci_buffree; + pdq->pdq_command_info.ci_bufptr = pdq->pdq_command_info.ci_bufstart; + pdq->pdq_command_info.ci_pa_bufstart = PDQ_OS_VA_TO_PA(pdq->pdq_command_info.ci_bufstart); + pdq->pdq_command_info.ci_request_max = pdq->pdq_command_info.ci_request_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_command_requests); + pdq->pdq_command_info.ci_response_max = pdq->pdq_command_info.ci_response_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_command_responses); + + /* + * Initialize the unsolicited event information block + */ + pdq->pdq_unsolicited_info.ui_free = PDQ_NUM_UNSOLICITED_EVENTS; + pdq->pdq_unsolicited_info.ui_pa_bufstart = PDQ_OS_VA_TO_PA(pdq->pdq_unsolicited_info.ui_events); + for (idx = 0; idx < sizeof(pdq->pdq_dbp->pdqdb_unsolicited_events)/sizeof(pdq->pdq_dbp->pdqdb_unsolicited_events[0]); idx++) { + pdq_rxdesc_t *rxd = &pdq->pdq_dbp->pdqdb_unsolicited_events[idx]; + pdq_unsolicited_event_t *event = &pdq->pdq_unsolicited_info.ui_events[idx & (PDQ_NUM_UNSOLICITED_EVENTS-1)]; + + rxd->rxd_sop = 1; + rxd->rxd_seg_cnt = 0; + rxd->rxd_seg_len_hi = sizeof(pdq_unsolicited_event_t) / 16; + rxd->rxd_pa_lo = pdq->pdq_unsolicited_info.ui_pa_bufstart + (const pdq_uint8_t *) event + - (const pdq_uint8_t *) pdq->pdq_unsolicited_info.ui_events; + rxd->rxd_pa_hi = 0; + } + /* + * Initialize the receive information blocks (normal and SMT). + */ + pdq->pdq_rx_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_receives); + pdq->pdq_rx_info.rx_target = pdq->pdq_rx_info.rx_free - PDQ_RX_SEGCNT * 8; + + pdq->pdq_host_smt_info.rx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_host_smt); + pdq->pdq_host_smt_info.rx_target = pdq->pdq_host_smt_info.rx_free - PDQ_RX_SEGCNT * 3; + + /* + * Initialize the transmit information block. + */ + pdq->pdq_tx_hdr[0] = PDQ_FDDI_PH0; + pdq->pdq_tx_hdr[1] = PDQ_FDDI_PH1; + pdq->pdq_tx_hdr[2] = PDQ_FDDI_PH2; + pdq->pdq_tx_info.tx_free = PDQ_RING_MASK(pdq->pdq_dbp->pdqdb_transmits); + pdq->pdq_tx_info.tx_hdrdesc.txd_seg_len = sizeof(pdq->pdq_tx_hdr); + pdq->pdq_tx_info.tx_hdrdesc.txd_sop = 1; + pdq->pdq_tx_info.tx_hdrdesc.txd_pa_lo = PDQ_OS_VA_TO_PA(pdq->pdq_tx_hdr); + + state = PDQ_PSTS_ADAPTER_STATE(*pdq->pdq_csrs.csr_port_status); + PDQ_PRINTF(("PDQ Adapter State = %s\n", pdq_adapter_states[state])); + + /* + * Stop the PDQ if it is running and put it into a known state. + */ + state = pdq_stop(pdq); + + /* state = PDQ_PSTS_ADAPTER_STATE(*pdq->pdq_csrs.csr_port_status); */ + PDQ_PRINTF(("PDQ Adapter State = %s\n", pdq_adapter_states[state])); + PDQ_ASSERT(state == PDQS_DMA_AVAILABLE); + /* + * If the adapter is not the state we expect, then the initialization + * failed. Cleanup and exit. + */ + if (state == PDQS_RESET || state == PDQS_HALTED || state == PDQS_UPGRADE) + goto cleanup_and_return; + + PDQ_PRINTF(("PDQ Hardware Address = %02x-%02x-%02x-%02x-%02x-%02x\n", + pdq->pdq_hwaddr.lanaddr_bytes[0], pdq->pdq_hwaddr.lanaddr_bytes[1], + pdq->pdq_hwaddr.lanaddr_bytes[2], pdq->pdq_hwaddr.lanaddr_bytes[3], + pdq->pdq_hwaddr.lanaddr_bytes[4], pdq->pdq_hwaddr.lanaddr_bytes[5])); + PDQ_PRINTF(("PDQ Firmware Revision = %c%c%c%c\n", + pdq->pdq_fwrev.fwrev_bytes[0], pdq->pdq_fwrev.fwrev_bytes[1], + pdq->pdq_fwrev.fwrev_bytes[2], pdq->pdq_fwrev.fwrev_bytes[3])); + PDQ_PRINTF(("PDQ Chip Revision = ")); + switch (pdq->pdq_chip_rev) { + case PDQ_CHIP_REV_A_B_OR_C: PDQ_PRINTF(("Rev C or below")); break; + case PDQ_CHIP_REV_D: PDQ_PRINTF(("Rev D")); break; + case PDQ_CHIP_REV_E: PDQ_PRINTF(("Rev E")); break; + default: PDQ_PRINTF(("Unknown Rev %d", (int) pdq->pdq_chip_rev)); + } + PDQ_PRINTF(("\n")); + + return pdq; +} diff --git a/sys/pci/pdq_os.h b/sys/pci/pdq_os.h new file mode 100644 index 0000000..f417f24 --- /dev/null +++ b/sys/pci/pdq_os.h @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 1995 Matt Thomas (thomas@lkg.dec.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough 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. + * + * $Id: pdq_os.h,v 1.6 1995/03/14 01:52:52 thomas Exp $ + * + * $Log: pdq_os.h,v $ + * Revision 1.6 1995/03/14 01:52:52 thomas + * Update for new FreeBSD PCI Interrupt interface + * Use inl/inb/... inline macros provided by FreeBSD and BSDI + * + * Revision 1.5 1995/03/10 17:42:24 thomas + * More changes for BSDI + * + * Revision 1.4 1995/03/06 17:08:56 thomas + * Add copyright/disclaimer + * Add inx/outx macros + * + * Revision 1.3 1995/03/03 13:48:35 thomas + * more fixes + * + * + */ + +/* + * DEC PDQ FDDI Controller; PDQ O/S dependent definitions + * + * Written by Matt Thomas + * + */ + +#ifndef _PDQ_OS_H +#define _PDQ_OS_H + +#define PDQ_OS_TX_TIMEOUT 5 /* seconds */ + +#ifdef PDQTEST +#include <pdq_os_test.h> +#elif defined(__FreeBSD__) || defined(__bsdi__) + +#include <sys/param.h> +#ifndef M_MCAST +#include <sys/mbuf.h> +#endif +#include <sys/malloc.h> +#include <vm/vm.h> +#include <vm/vm_kern.h> + +#define PDQ_USE_MBUFS +#define PDQ_OS_PREFIX "%s%d: " +#define PDQ_OS_PREFIX_ARGS pdq->pdq_os_name, pdq->pdq_unit + +#define PDQ_OS_PAGESIZE NBPG +#define PDQ_OS_USEC_DELAY(n) DELAY(n) +#define PDQ_OS_MEMZERO(p, n) bzero((caddr_t)(p), (n)) +#define PDQ_OS_VA_TO_PA(p) vtophys(p) +#define PDQ_OS_MEMALLOC(n) malloc(n, M_DEVBUF, M_NOWAIT) +#define PDQ_OS_MEMFREE(p, n) free((void *) p, M_DEVBUF) +#ifdef __FreeBSD__ +#define PDQ_OS_MEMALLOC_CONTIG(n) vm_page_alloc_contig(n, 0, 0xffffffff, PAGE_SIZE) +#define PDQ_OS_MEMFREE_CONTIG(p, n) kmem_free(kernel_map, (vm_offset_t) p, n) +#else +#define PDQ_OS_MEMALLOC_CONTIG PDQ_OS_MEMALLOC +#define PDQ_OS_MEMFREE_CONTIG PDQ_OS_MEMFREE +#endif + +#if defined(__FreeBSD__) +#include <machine/cpufunc.h> +#elif defined(__bsdi__) +#include <machine/inline.h> +#endif +#define PDQ_OS_IORD_32(port) inl(port) +#define PDQ_OS_IOWR_32(port, data) outl(port, data) +#define PDQ_OS_IORD_8(port) inb(port) +#define PDQ_OS_IOWR_8(port, data) outb(port, data) +#endif + +#ifdef PDQ_USE_MBUFS +#define PDQ_OS_DATABUF_SIZE (MCLBYTES) +#define PDQ_OS_DATABUF_FREE(b) (m_freem(b)) +#define PDQ_OS_DATABUF_NEXT(b) ((b)->m_next) +#define PDQ_OS_DATABUF_NEXT_SET(b, b1) ((b)->m_next = (b1)) +#define PDQ_OS_DATABUF_NEXTPKT(b) ((b)->m_nextpkt) +#define PDQ_OS_DATABUF_NEXTPKT_SET(b, b1) ((b)->m_nextpkt = (b1)) +#define PDQ_OS_DATABUF_LEN(b) ((b)->m_len) +#define PDQ_OS_DATABUF_LEN_SET(b, n) ((b)->m_len = (n)) +#define PDQ_OS_DATABUF_LEN_ADJ(b, n) ((b)->m_len += (n)) +#define PDQ_OS_DATABUF_PTR(b) (mtod((b), pdq_uint8_t *)) +#define PDQ_OS_DATABUF_PTR_ADJ(b, n) ((b)->m_data += (n)) +typedef struct mbuf PDQ_OS_DATABUF_T; + +#define PDQ_OS_DATABUF_ALLOC(b) do { \ + PDQ_OS_DATABUF_T *x_m0; \ + MGETHDR(x_m0, M_DONTWAIT, MT_DATA); \ + if (x_m0 != NULL) { \ + MCLGET(x_m0, M_DONTWAIT); \ + if ((x_m0->m_flags & M_EXT) == 0) { \ + m_free(x_m0); \ + (b) = NULL; \ + } else { \ + (b) = x_m0; \ + x_m0->m_len = PDQ_OS_DATABUF_SIZE; \ + } \ + } else { \ + (b) = NULL; \ + } \ +} while (0) +#define PDQ_OS_DATABUF_RESET(b) ((b)->m_data = (b)->m_ext.ext_buf, (b)->m_len = MCLBYTES) +#endif /* PDQ_USE_MBUFS */ + +#ifdef PDQ_USE_STREAMS +#define PDQ_OS_DATABUF_FREE(b) (freemsg(b)) +#define PDQ_OS_DATABUF_NEXT(b) ((b)->b_cont) +#define PDQ_OS_DATABUF_NEXTPKT(b) ((b)->b_next) +#define PDQ_OS_DATABUF_NEXTPKT_SET(b, b1) ((b)->b_next = (b1)) +#define PDQ_OS_DATABUF_LEN(b) ((b)->b_wptr - (b)->b_rptr) +#define PDQ_OS_DATABUF_PTR(b) ((pdq_uint8_t *) (b)->b_rptr) +typedef mblk_t PDQ_OS_DATABUF_T; +#endif /* PDQ_USE_STREAMS */ + +#define PDQ_OS_TX_TRANSMIT 5 + +#define PDQ_OS_DATABUF_ENQUEUE(q, b) do { \ + PDQ_OS_DATABUF_NEXTPKT_SET(b, NULL); \ + if ((q)->q_tail == NULL) \ + (q)->q_head = (b); \ + else \ + PDQ_OS_DATABUF_NEXTPKT_SET(((PDQ_OS_DATABUF_T *)(q)->q_tail), b); \ + (q)->q_tail = (b); \ +} while (0) + +#define PDQ_OS_DATABUF_DEQUEUE(q, b) do { \ + if (((b) = (PDQ_OS_DATABUF_T *) (q)->q_head) != NULL) { \ + if (((q)->q_head = PDQ_OS_DATABUF_NEXTPKT(b)) == NULL) \ + (q)->q_tail = NULL; \ + PDQ_OS_DATABUF_NEXTPKT_SET(b, NULL); \ + } \ +} while (0) + +extern void pdq_os_addr_fill(pdq_t *pdq, pdq_lanaddr_t *addrs, size_t numaddrs); +extern void pdq_os_receive_pdu(pdq_t *, PDQ_OS_DATABUF_T *pdu, size_t pdulen); +extern void pdq_os_restart_transmitter(pdq_t *pdq); +extern void pdq_os_transmit_done(pdq_t *pdq, PDQ_OS_DATABUF_T *pdu); + +extern void pdq_print_fddi_chars(pdq_t *pdq, const pdq_response_status_chars_get_t *rsp); + +extern void pdq_init_csrs(pdq_csrs_t *csrs, void *csrs_va, size_t csr_size); +extern void pdq_init_pci_csrs(pdq_pci_csrs_t *csrs, void *csrs_va, size_t csr_size); + +extern void pdq_flush_databuf_queue(pdq_databuf_queue_t *q); + +extern pdq_boolean_t pdq_do_port_control(const pdq_csrs_t * const csrs, pdq_uint32_t cmd); +extern void pdq_read_mla(const pdq_csrs_t * const csrs, pdq_lanaddr_t *hwaddr); +extern void pdq_read_fwrev(const pdq_csrs_t * const csrs, pdq_fwrev_t *fwrev); +extern pdq_boolean_t pdq_read_error_log(pdq_t *pdq, pdq_response_error_log_get_t *log_entry); +extern pdq_chip_rev_t pdq_read_chiprev(const pdq_csrs_t * const csrs); + +extern void pdq_queue_commands(pdq_t *pdq); +extern void pdq_process_command_responses(pdq_t *pdq); +extern void pdq_process_unsolicited_events(pdq_t *pdq); + +extern void pdq_process_received_data(pdq_t *pdq, pdq_rx_info_t *rx, + pdq_rxdesc_t *receives, + pdq_uint32_t completion_goal, + pdq_uint32_t ring_mask); + +extern pdq_boolean_t pdq_queue_transmit_data(pdq_t *pdq, PDQ_OS_DATABUF_T *pdu); +extern void pdq_process_transmitted_data(pdq_t *pdq); +extern void pdq_flush_transmitter(pdq_t *pdq); + + +extern pdq_state_t pdq_stop(pdq_t *pdq); +extern void pdq_run(pdq_t *pdq); + +extern int pdq_interrupt(pdq_t *pdq); +extern pdq_t *pdq_initialize(void *csr_va, const char *name, int unit, void *ctx, pdq_type_t type); + + +#endif /* _PDQ_OS_H */ diff --git a/sys/pci/pdqreg.h b/sys/pci/pdqreg.h new file mode 100644 index 0000000..d664aa9 --- /dev/null +++ b/sys/pci/pdqreg.h @@ -0,0 +1,1081 @@ +/*- + * Copyright (c) 1995 Matt Thomas (thomas@lkg.dec.com) + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough 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. + * + * $Id: pdqreg.h,v 1.5 1995/03/10 17:41:55 thomas Exp $ + * + * $Log: pdqreg.h,v $ + * Revision 1.5 1995/03/10 17:41:55 thomas + * Add DEFTA, DEFQA, and DEFAA + * + * Revision 1.4 1995/03/06 17:07:05 thomas + * Add copyright/disclaimer + * Add EISA register definitions + * + * Revision 1.3 1995/03/03 13:48:35 thomas + * more fixes + * + * + */ + +/* + * DEC PDQ FDDI Controller; PDQ port driver definitions + * + * Written by Matt Thomas + * + */ + +#ifndef _PDQREG_H +#define _PDQREG_H + +#include <stddef.h> +#if defined(PDQTEST) && !defined(PDQ_NDEBUG) +#include <assert.h> +#define PDQ_ASSERT assert +#else +#define PDQ_ASSERT(x) +#endif + +#define PDQ_RING_SIZE(array) ((sizeof(array) / sizeof(array[0]))) +#define PDQ_ARRAY_SIZE(array) ((sizeof(array) / sizeof(array[0]))) +#define PDQ_RING_MASK(array) (PDQ_RING_SIZE(array) - 1) +#define PDQ_BITMASK(n) (1L << (pdq_uint32_t) (n)) + +#define PDQ_FDDI_MAX 4495 +#define PDQ_FDDI_LLC_MIN 20 +#define PDQ_FDDI_SMT_MIN 37 + +#define PDQ_FDDI_SMT 0x40 +#define PDQ_FDDI_LLC_ASYNC 0x50 +#define PDQ_FDDI_LLC_SYNC 0xD0 +#define PDQ_FDDI_IMP_ASYNC 0x60 +#define PDQ_FDDI_IMP_SYNC 0xE0 + +#define PDQ_FDDIFC_C 0x80 +#define PDQ_FDDIFC_L 0x40 +#define PDQ_FDDIFC_F 0x30 +#define PDQ_FDDIFC_Z 0x0F + +#define PDQ_FDDI_PH0 0x20 +#define PDQ_FDDI_PH1 0x38 +#define PDQ_FDDI_PH2 0x00 + +typedef unsigned int pdq_uint32_t; +typedef unsigned short pdq_uint16_t; +typedef unsigned char pdq_uint8_t; + +typedef pdq_uint32_t pdq_physaddr_t; + +typedef struct { + pdq_uint8_t lanaddr_bytes[8]; +} pdq_lanaddr_t; + +typedef struct { + pdq_uint8_t fwrev_bytes[4]; +} pdq_fwrev_t; + +typedef enum { + PDQS_RESET=0, + PDQS_UPGRADE=1, + PDQS_DMA_UNAVAILABLE=2, + PDQS_DMA_AVAILABLE=3, + PDQS_LINK_AVAILABLE=4, + PDQS_LINK_UNAVAILABLE=5, + PDQS_HALTED=6, + PDQS_RING_MEMBER=7 +} pdq_state_t; + +typedef struct { + volatile pdq_uint32_t *csr_port_reset; /* 0x00 [RW] */ + volatile pdq_uint32_t *csr_host_data; /* 0x04 [R] */ + volatile pdq_uint32_t *csr_port_control; /* 0x08 [RW] */ + volatile pdq_uint32_t *csr_port_data_a; /* 0x0C [RW] */ + volatile pdq_uint32_t *csr_port_data_b; /* 0x10 [RW] */ + volatile pdq_uint32_t *csr_port_status; /* 0x14 [R] */ + volatile pdq_uint32_t *csr_host_int_type_0; /* 0x18 [RW] */ + volatile pdq_uint32_t *csr_host_int_enable; /* 0x1C [RW] */ + volatile pdq_uint32_t *csr_type_2_producer; /* 0x20 [RW] */ + volatile pdq_uint32_t *csr_cmd_response_producer; /* 0x28 [RW] */ + volatile pdq_uint32_t *csr_cmd_request_producer; /* 0x2C [RW] */ + volatile pdq_uint32_t *csr_host_smt_producer; /* 0x30 [RW] */ + volatile pdq_uint32_t *csr_unsolicited_producer; /* 0x34 [RW] */ +} pdq_csrs_t; + +typedef struct { + volatile pdq_uint32_t *csr_pfi_mode_control; /* 0x40 [RW] */ + volatile pdq_uint32_t *csr_pfi_status; /* 0x44 [RW] */ + volatile pdq_uint32_t *csr_fifo_write; /* 0x48 [RW] */ + volatile pdq_uint32_t *csr_fifo_read; /* 0x4C [RW] */ +} pdq_pci_csrs_t; + +#define PDQ_PFI_MODE_DMA_ENABLE 0x01 /* DMA Enable */ +#define PDQ_PFI_MODE_PFI_PCI_INTR 0x02 /* PFI-to-PCI Int Enable */ +#define PDQ_PFI_MODE_PDQ_PCI_INTR 0x04 /* PDQ-to-PCI Int Enable */ + +#define PDQ_PFI_STATUS_PDQ_INTR 0x10 /* PDQ Int received */ +#define PDQ_PFI_STATUS_DMA_ABORT 0x08 /* PDQ DMA Abort asserted */ + +#define PDQ_EISA_BURST_HOLDOFF 0x0040 +#define PDQ_EISA_SLOT_ID 0x0C80 +#define PDQ_EISA_SLOT_CTRL 0x0C84 +#define PDQ_EISA_MEM_ADD_CMP_0 0x0C85 +#define PDQ_EISA_MEM_ADD_CMP_1 0x0C86 +#define PDQ_EISA_MEM_ADD_CMP_2 0x0C87 +#define PDQ_EISA_MEM_ADD_HI_CMP_0 0x0C88 +#define PDQ_EISA_MEM_ADD_HI_CMP_1 0x0C89 +#define PDQ_EISA_MEM_ADD_HI_CMP_2 0x0C8A +#define PDQ_EISA_MEM_ADD_MASK_0 0x0C8B +#define PDQ_EISA_MEM_ADD_MASK_1 0x0C8C +#define PDQ_EISA_MEM_ADD_MASK_2 0x0C8D +#define PDQ_EISA_MEM_ADD_LO_CMP_0 0x0C8E +#define PDQ_EISA_MEM_ADD_LO_CMP_1 0x0C8F +#define PDQ_EISA_MEM_ADD_LO_CMP_2 0x0C90 +#define PDQ_EISA_IO_CMP_0_0 0x0C91 +#define PDQ_EISA_IO_CMP_0_1 0x0C92 +#define PDQ_EISA_IO_CMP_1_0 0x0C93 +#define PDQ_EISA_IO_CMP_1_1 0x0C94 +#define PDQ_EISA_IO_CMP_2_0 0x0C95 +#define PDQ_EISA_IO_CMP_2_1 0x0C96 +#define PDQ_EISA_IO_CMP_3_0 0x0C97 +#define PDQ_EISA_IO_CMP_3_1 0x0C98 +#define PDQ_EISA_IO_ADD_MASK_0_0 0x0C99 +#define PDQ_EISA_IO_ADD_MASK_0_1 0x0C9A +#define PDQ_EISA_IO_ADD_MASK_1_0 0x0C9B +#define PDQ_EISA_IO_ADD_MASK_1_1 0x0C9C +#define PDQ_EISA_IO_ADD_MASK_2_0 0x0C9D +#define PDQ_EISA_IO_ADD_MASK_2_1 0x0C9E +#define PDQ_EISA_IO_ADD_MASK_3_0 0x0C9F +#define PDQ_EISA_IO_ADD_MASK_3_1 0x0CA0 +#define PDQ_EISA_MOD_CONFIG_1 0x0CA1 +#define PDQ_EISA_MOD_CONFIG_2 0x0CA2 +#define PDQ_EISA_MOD_CONFIG_3 0x0CA3 +#define PDQ_EISA_MOD_CONFIG_4 0x0CA4 +#define PDQ_EISA_MOD_CONFIG_5 0x0CA5 +#define PDQ_EISA_MOD_CONFIG_6 0x0CA6 +#define PDQ_EISA_MOD_CONFIG_7 0x0CA7 +#define PDQ_EISA_DIP_SWITCH 0x0CA8 +#define PDQ_EISA_IO_CONFIG_STAT_0 0x0CA9 +#define PDQ_EISA_IO_CONFIG_STAT_1 0x0CAA +#define PDQ_EISA_DMA_CONFIG 0x0CAB +#define PDQ_EISA_INPUT_PORT 0x0CAC +#define PDQ_EISA_OUTPUT_PORT 0x0CAD +#define PDQ_EISA_FUNCTION_CTRL 0x0CAE + +/* + * Port Reset Data A Definitions + */ +#define PDQ_PRESET_SKIP_SELFTEST 0x0004 +#define PDQ_PRESET_SOFT_RESET 0x0002 +#define PDQ_PRESET_UPGRADE 0x0001 +/* + * Port Control Register Definitions + */ +#define PDQ_PCTL_CMD_ERROR 0x8000 +#define PDQ_PCTL_FLASH_BLAST 0x4000 +#define PDQ_PCTL_HALT 0x2000 +#define PDQ_PCTL_COPY_DATA 0x1000 +#define PDQ_PCTL_ERROR_LOG_START 0x0800 +#define PDQ_PCTL_ERROR_LOG_READ 0x0400 +#define PDQ_PCTL_XMT_DATA_FLUSH_DONE 0x0200 +#define PDQ_PCTL_DMA_INIT 0x0100 +#define PDQ_DMA_INIT_LW_BSWAP_DATA 0x02 +#define PDQ_DMA_INIT_LW_BSWAP_LITERAL 0x01 +#define PDQ_PCTL_INIT_START 0x0080 +#define PDQ_PCTL_CONSUMER_BLOCK 0x0040 +#define PDQ_PCTL_DMA_UNINIT 0x0020 +#define PDQ_PCTL_RING_MEMBER 0x0010 +#define PDQ_PCTL_MLA_READ 0x0008 +#define PDQ_PCTL_FW_REV_READ 0x0004 +#define PDQ_PCTL_DEVICE_SPECIFIC 0x0002 +#define PDQ_PCTL_SUB_CMD 0x0001 + +typedef enum { + PDQ_SUB_CMD_LINK_UNINIT=1, + PDQ_SUB_CMD_DMA_BURST_SIZE_SET=2, + PDQ_SUB_CMD_PDQ_REV_GET=4 +} pdq_sub_cmd_t; + +typedef enum { + PDQ_DMA_BURST_4LW=0, + PDQ_DMA_BURST_8LW=1, + PDQ_DMA_BURST_16LW=2, + PDQ_DMA_BURST_32LW=3 +} pdq_dma_burst_size_t; + +typedef enum { + PDQ_CHIP_REV_A_B_OR_C=0, + PDQ_CHIP_REV_D=2, + PDQ_CHIP_REV_E=4 +} pdq_chip_rev_t; +/* + * Port Status Register Definitions + */ +#define PDQ_PSTS_RCV_DATA_PENDING 0x80000000ul +#define PDQ_PSTS_XMT_DATA_PENDING 0x40000000ul +#define PDQ_PSTS_HOST_SMT_PENDING 0x20000000ul +#define PDQ_PSTS_UNSOL_PENDING 0x10000000ul +#define PDQ_PSTS_CMD_RSP_PENDING 0x08000000ul +#define PDQ_PSTS_CMD_REQ_PENDING 0x04000000ul +#define PDQ_PSTS_TYPE_0_PENDING 0x02000000ul +#define PDQ_PSTS_INTR_PENDING 0xFE000000ul +#define PDQ_PSTS_ADAPTER_STATE(sts) ((pdq_state_t) (((sts) >> 8) & 0x07)) +#define PDQ_PSTS_HALT_ID(sts) ((pdq_halt_code_t) ((sts) & 0xFF)) +/* + * Host Interrupt Register Definitions + */ +#define PDQ_HOST_INT_TX_ENABLE 0x80000000ul +#define PDQ_HOST_INT_RX_ENABLE 0x40000000ul +#define PDQ_HOST_INT_UNSOL_ENABLE 0x20000000ul +#define PDQ_HOST_INT_HOST_SMT_ENABLE 0x10000000ul +#define PDQ_HOST_INT_CMD_RSP_ENABLE 0x08000000ul +#define PDQ_HOST_INT_CMD_RQST_ENABLE 0x04000000ul + +#define PDQ_HOST_INT_1MS 0x80 +#define PDQ_HOST_INT_20MS 0x40 +#define PDQ_HOST_INT_CSR_CMD_DONE 0x20 +#define PDQ_HOST_INT_STATE_CHANGE 0x10 +#define PDQ_HOST_INT_XMT_DATA_FLUSH 0x08 +#define PDQ_HOST_INT_NXM 0x04 +#define PDQ_HOST_INT_PM_PARITY_ERROR 0x02 +#define PDQ_HOST_INT_HOST_BUS_PARITY_ERROR 0x01 +#define PDQ_HOST_INT_FATAL_ERROR 0x07 + +typedef enum { + PDQH_SELFTEST_TIMEOUT=0, + PDQH_HOST_BUS_PARITY_ERROR=1, + PDQH_HOST_DIRECTED_HALT=2, + PDQH_SOFTWARE_FAULT=3, + PDQH_HARDWARE_FAULT=4, + PDQH_PC_TRACE_PATH_TEST=5, + PDQH_DMA_ERROR=6, + PDQH_IMAGE_CRC_ERROR=7, + PDQH_ADAPTER_PROCESSOR_ERROR=8, + PDQH_MAX=9 +} pdq_halt_code_t; + +typedef struct { + pdq_uint16_t pdqcb_receives; + pdq_uint16_t pdqcb_transmits; + pdq_uint32_t pdqcb__filler1; + pdq_uint32_t pdqcb_host_smt; + pdq_uint32_t pdqcb__filler2; + pdq_uint32_t pdqcb_unsolicited_event; + pdq_uint32_t pdqcb__filler3; + pdq_uint32_t pdqcb_command_response; + pdq_uint32_t pdqcb__filler4; + pdq_uint32_t pdqcb_command_request; + pdq_uint32_t pdqcb__filler5[7]; +} pdq_consumer_block_t; + +#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN +#define PDQ_BITFIELD2(a, b) b, a +#define PDQ_BITFIELD3(a, b, c) c, b, a +#define PDQ_BITFIELD4(a, b, c, d) d, c, b, a +#define PDQ_BITFIELD5(a, b, c, d, e) e, d, c, b, a +#define PDQ_BITFIELD12(a, b, c, d, e, f, g, h, i, j, k, l) \ + l, k, j, i, h, g, f, e, d, c, b, a +#else +#define PDQ_BITFIELD2(a, b) a, b +#define PDQ_BITFIELD3(a, b, c) a, b, c +#define PDQ_BITFIELD4(a, b, c, d) a, b, c, d +#define PDQ_BITFIELD5(a, b, c, d, e) a, b, c, d, e +#define PDQ_BITFIELD12(a, b, c, d, e, f, g, h, i, j, k, l) \ + a, b, c, d, e, f, g, h, i, j, k, l +#endif + +typedef struct { + pdq_uint32_t PDQ_BITFIELD5(rxd_pa_hi : 16, + rxd_seg_cnt : 4, + rxd_seg_len_hi : 9, + rxd_seg_len_lo : 2, + rxd_sop : 1); + pdq_uint32_t rxd_pa_lo; +} pdq_rxdesc_t; + +typedef union { + pdq_uint32_t rxs_status; + pdq_uint32_t PDQ_BITFIELD12(rxs_len : 13, + rxs_rcc_ss : 2, + rxs_rcc_dd : 2, + rxs_rcc_reason : 3, + rxs_rcc_badcrc : 1, + rxs_rcc_badpdu : 1, + rxs_fsb__reserved : 2, + rxs_fsb_c : 1, + rxs_fsb_a : 1, + rxs_fsb_e : 1, + rxs_fsc : 3, + rxs__reserved : 2); +} pdq_rxstatus_t; + +typedef struct { + pdq_uint32_t PDQ_BITFIELD5(txd_pa_hi : 16, + txd_seg_len : 13, + txd_mbz : 1, + txd_eop : 1, + txd_sop : 1); + pdq_uint32_t txd_pa_lo; +} pdq_txdesc_t; + +typedef struct { + pdq_rxdesc_t pdqdb_receives[256]; /* 2048; 0x0000..0x07FF */ + pdq_txdesc_t pdqdb_transmits[256]; /* 2048; 0x0800..0x0FFF */ + pdq_rxdesc_t pdqdb_host_smt[64]; /* 512; 0x1000..0x11FF */ + pdq_rxdesc_t pdqdb_unsolicited_events[16]; /* 128; 0x1200..0x127F */ + pdq_rxdesc_t pdqdb_command_responses[16]; /* 128; 0x1280..0x12FF */ + pdq_txdesc_t pdqdb_command_requests[16]; /* 128; 0x1300..0x137F */ + /* + * The rest of the descriptor block is unused. + * As such we could use it for other things. + */ + pdq_consumer_block_t pdqdb_consumer; /* 64; 0x1380..0x13BF */ + void *pdqdb_receive_buffers[256]; /* 1024/2048; 0x13C0..0x17BF 0x13C0..0x1BBF */ + void *pdqdb_host_smt_buffers[64]; /* 256/ 512; 0x17C0..0x18BF 0x1BC0..0x1DBF */ + /* + * The maximum command size is 512 so as long as thes + * command is at least that long all will be fine. + */ +#ifdef __alpha + pdq_uint32_t pdqdb_command_pool[144]; +#else + pdq_uint32_t pdqdb_command_pool[464]; +#endif +} pdq_descriptor_block_t; + +typedef enum { + PDQ_DEFPA, /* PCI-bus */ + PDQ_DEFEA, /* EISA-bus */ + PDQ_DEFTA, /* TurboChannel */ + PDQ_DEFAA, /* FutureBus+ */ + PDQ_DEFQA /* Q-bus */ +} pdq_type_t; + +typedef struct { + /* + * These value manage the available space in command/response + * buffer area. + */ + pdq_physaddr_t ci_pa_bufstart; + pdq_uint8_t *ci_bufstart; + pdq_uint8_t *ci_bufend; + pdq_uint8_t *ci_bufptr; + size_t ci_buffree; + /* + * Bitmask of commands to sent to the PDQ + */ + pdq_uint32_t ci_pending_commands; + /* + * Variables to maintain the PDQ queues. + */ + pdq_uint32_t ci_request_free; + pdq_uint32_t ci_response_free; + pdq_uint32_t ci_request_max; + pdq_uint32_t ci_response_max; + pdq_uint32_t ci_request_producer; + pdq_uint32_t ci_response_producer; + pdq_uint32_t ci_request_completion; + pdq_uint32_t ci_response_completion; +} pdq_command_info_t; + +#define PDQ_SIZE_UNSOLICITED_EVENT 512 +#define PDQ_NUM_UNSOLICITED_EVENTS (PDQ_OS_PAGESIZE / PDQ_SIZE_UNSOLICITED_EVENT) + +typedef struct _pdq_unsolicited_event_t pdq_unsolicited_event_t; + +typedef struct { + pdq_physaddr_t ui_pa_bufstart; + pdq_unsolicited_event_t *ui_events; + + pdq_uint32_t ui_free; + pdq_uint32_t ui_producer; + pdq_uint32_t ui_completion; +} pdq_unsolicited_info_t; + +#define PDQ_RX_FC_OFFSET (sizeof(pdq_rxstatus_t) + 3) +#define PDQ_RX_SEGCNT ((PDQ_FDDI_MAX + PDQ_OS_DATABUF_SIZE - 1) / PDQ_OS_DATABUF_SIZE) +#define PDQ_DO_TYPE2_PRODUCER(pdq) \ + (*pdq->pdq_csrs.csr_type_2_producer = (pdq->pdq_rx_info.rx_producer << 0) \ + | (pdq->pdq_tx_info.tx_producer << 8) \ + | (pdq->pdq_rx_info.rx_completion << 16) \ + | (pdq->pdq_tx_info.tx_completion << 24)) +#define PDQ_ADVANCE(n, a, m) ((n) = ((n) + (a)) & (m)) + +typedef struct { + void *q_head; + void *q_tail; +} pdq_databuf_queue_t; + +typedef struct { + void *rx_buffers; + + pdq_uint32_t rx_target; + pdq_uint32_t rx_free; + pdq_uint32_t rx_producer; + pdq_uint32_t rx_completion; +} pdq_rx_info_t; + +typedef struct { + pdq_databuf_queue_t tx_txq; + pdq_txdesc_t tx_hdrdesc; + pdq_uint8_t tx_descriptor_count[256]; + + pdq_uint32_t tx_free; + pdq_uint32_t tx_producer; + pdq_uint32_t tx_completion; +} pdq_tx_info_t; + +typedef struct { + pdq_csrs_t pdq_csrs; + pdq_pci_csrs_t pdq_pci_csrs; + pdq_type_t pdq_type; + pdq_chip_rev_t pdq_chip_rev; + pdq_lanaddr_t pdq_hwaddr; + pdq_fwrev_t pdq_fwrev; + pdq_descriptor_block_t *pdq_dbp; + volatile pdq_consumer_block_t *pdq_cbp; + pdq_uint32_t pdq_flags; +#define PDQ_PROMISC 0x0001 +#define PDQ_ALLMULTI 0x0002 +#define PDQ_PASS_SMT 0x0004 +#define PDQ_RUNNING 0x0008 +#define PDQ_PRINTCHARS 0x0010 +#define PDQ_TXOK 0x0020 + const char *pdq_os_name; + void *pdq_os_ctx; + pdq_uint32_t pdq_unit; + pdq_command_info_t pdq_command_info; + pdq_unsolicited_info_t pdq_unsolicited_info; + pdq_tx_info_t pdq_tx_info; + pdq_rx_info_t pdq_rx_info; + pdq_rx_info_t pdq_host_smt_info; + pdq_uint8_t pdq_tx_hdr[3]; +} pdq_t; + +typedef enum { + PDQC_START=0, + PDQC_FILTER_SET=1, + PDQC_FILTER_GET=2, + PDQC_CHARS_SET=3, + PDQC_STATUS_CHARS_GET=4, + PDQC_COUNTERS_GET=5, + PDQC_COUNTERS_SET=6, + PDQC_ADDR_FILTER_SET=7, + PDQC_ADDR_FILTER_GET=8, + PDQC_ERROR_LOG_CLEAR=9, + PDQC_ERROR_LOG_GET=10, + PDQC_FDDI_MIB_GET=11, + PDQC_DEC_EXT_MIB_GET=12, + PDQC_DEV_SPECIFIC_GET=13, + PDQC_SNMP_SET=14, + PDQC_SMT_MIB_GET=16, + PDQC_SMT_MIB_SET=17 +} pdq_cmd_code_t; + +typedef enum { + PDQR_SUCCESS=0, + PDQR_FAILURE=1, + PDQR_WARNING=2, + PDQR_LOOP_MODE_BAD=3, + PDQR_ITEM_CODE_BAD=4, + PDQR_TVX_BAD=5, + PDQR_TREQ_BAD=6, + PDQR_RESTRICTED_TOKEN_BAD=7, + PDQR_NO_EOL=12, + PDQR_FILTER_STATE_BAD=13, + PDQR_CMD_TYPE_BAD=14, + PDQR_ADAPTER_STATE_BAD=15, + PDQR_RING_PURGER_BAD=16, + PDQR_LEM_THRESHOLD_BAD=17, + PDQR_LOOP_NOT_SUPPORTED=18, + PDQR_FLUSH_TIME_BAD=19, + PDQR_NOT_YET_IMPLEMENTED=20, + PDQR_CONFIG_POLICY_BAD=21, + PDQR_STATION_ACTION_BAD=22, + PDQR_MAC_ACTION_BAD=23, + PDQR_CON_POLICIES_BAD=24, + PDQR_MAC_LOOP_TIME_BAD=25, + PDQR_TB_MAX_BAD=26, + PDQR_LER_CUTOFF_BAD=27, + PDQR_LER_ALARM_BAD=28, + PDQR_MAC_PATHS_REQ_BAD=29, + PDQR_MAC_T_REQ_BAD=30, + PDQR_EMAC_RING_PURGER_BAD=31, + PDQR_EMAC_RTOKEN_TIMOUT_AD=32, + PDQR_NO_SUCH_ENTRY=33, + PDQR_T_NOTIFY_BAD=34, + PDQR_TR_MAX_EXP_BAD=35, + PDQR_FRAME_ERR_THRESHOLD_BAD=36, + PDQR_MAX_TREQ_BAD=37, + PDQR_FULL_DUPLEX_ENABLE_BAD=38, + PDQR_ITEM_INDEX_BAD=39 +} pdq_response_code_t; + +typedef enum { + PDQI_EOL=0, + PDQI_T_REQ=1, + PDQI_TVX=2, + PDQI_RESTRICTED_TOKEN=3, + PDQI_LEM_THRESHOLD=4, + PDQI_RING_PURGER=5, + PDQI_COUNTER_INTERVAL=6, + PDQI_IND_GROUP_PROM=7, + PDQI_GROUP_PROM=8, + PDQI_BROADCAST=9, + PDQI_SMT_PROM=10, + PDQI_SMT_USER=11, + PDQI_RESERVED=12, + PDQI_IMPLEMENTOR=13, + PDQI_LOOPBACK_MODE=14, + PDQI_SMT_CONFIG_POLICY=16, + PDQI_SMT_CONNECTION_POLICY=17, + PDQI_SMT_T_NOTIFY=18, + PDQI_SMT_STATION_ACTION=19, + PDQI_MAC_PATHS_REQUESTED=21, + PDQI_MAC_ACTION=23, + PDQI_PORT_CONNECTION_POLICIES=24, + PDQI_PORT_PATHS_REQUESTED=25, + PDQI_PORT_MAC_LOOP_TIME=26, + PDQI_PORT_TB_MAX=27, + PDQI_PORT_LER_CUTOFF=28, + PDQI_PORT_LER_ALARM=29, + PDQI_PORT_ACTION=30, + PDQI_FLUSH_TIME=32, + PDQI_SMT_USER_DATA=33, + PDQI_SMT_STATUS_REPORT_POLICY=34, + PDQI_SMT_TRACE_MAX_EXPIRATION=35, + PDQI_MAC_FRAME_ERR_THRESHOLD=36, + PDQI_MAC_UNIT_DATA_ENABLE=37, + PDQI_PATH_TVX_LOWER_BOUND=38, + PDQI_PATH_TMAX_LOWER_BOUND=39, + PDQI_PATH_MAX_TREQ=40, + PDQI_MAC_TREQ=41, + PDQI_EMAC_RING_PURGER=42, + PDQI_EMAC_RTOKEN_TIMEOUT=43, + PDQI_FULL_DUPLEX_ENABLE=44 +} pdq_item_code_t; + +typedef enum { + PDQ_FALSE=0, + PDQ_TRUE=1 +} pdq_boolean_t; + +typedef enum { + PDQ_FILTER_BLOCK=0, + PDQ_FILTER_PASS=1 +} pdq_filter_state_t; + +typedef enum { + PDQ_STATION_TYPE_SAS=0, + PDQ_STATION_TYPE_DAC=1, + PDQ_STATION_TYPE_SAC=2, + PDQ_STATION_TYPE_NAC=3, + PDQ_STATION_TYPE_DAS=4 +} pdq_station_type_t; + +typedef enum { + PDQ_STATION_STATE_OFF=0, + PDQ_STATION_STATE_ON=1, + PDQ_STATION_STATE_LOOPBACK=2 +} pdq_station_state_t; + +typedef enum { + PDQ_LINK_STATE_OFF_READY=1, + PDQ_LINK_STATE_OFF_FAULT_RECOVERY=2, + PDQ_LINK_STATE_ON_RING_INIT=3, + PDQ_LINK_STATE_ON_RING_RUN=4, + PDQ_LINK_STATE_BROKEN=5 +} pdq_link_state_t; + +typedef enum { + PDQ_DA_TEST_STATE_UNKNOWN=0, + PDQ_DA_TEST_STATE_SUCCESS=1, + PDQ_DA_TEST_STATE_DUPLICATE=2 +} pdq_da_test_state_t; + +typedef enum { + PDQ_RING_PURGER_STATE_OFF=0, + PDQ_RING_PURGER_STATE_CANDIDATE=1, + PDQ_RING_PURGER_STATE_NON_PURGER=2, + PDQ_RING_PURGER_STATE_PURGER=3 +} pdq_ring_purger_state_t; + +typedef enum { + PDQ_FRAME_STRING_MODE_SA_MATCH=0, + PDQ_FRAME_STRING_MODE_FCI_STRIP=1 +} pdq_frame_strip_mode_t; + +typedef enum { + PDQ_RING_ERROR_REASON_NO_ERROR=0, + PDQ_RING_ERROR_REASON_RING_INIT_INITIATED=5, + PDQ_RING_ERROR_REASON_RING_INIT_RECEIVED=6, + PDQ_RING_ERROR_REASON_RING_BEACONING_INITIATED=7, + PDQ_RING_ERROR_REASON_DUPLICATE_ADDRESS_DETECTED=8, + PDQ_RING_ERROR_REASON_DUPLICATE_TOKEN_DETECTED=9, + PDQ_RING_ERROR_REASON_RING_PURGER_ERROR=10, + PDQ_RING_ERROR_REASON_FCI_STRIP_ERROR=11, + PDQ_RING_ERROR_REASON_RING_OP_OSCILLATION=12, + PDQ_RING_ERROR_REASON_DIRECTED_BEACON_RECEVIED=13, + PDQ_RING_ERROR_REASON_PC_TRACE_INITIATED=14, + PDQ_RING_ERROR_REASON_PC_TRACE_RECEVIED=15 +} pdq_ring_error_reason_t; + +typedef enum { + PDQ_STATION_MODE_NORMAL=0, + PDQ_STATION_MODE_INTERNAL_LOOPBACK=1 +} pdq_station_mode_t; + +typedef enum { + PDQ_PHY_TYPE_A=0, + PDQ_PHY_TYPE_B=1, + PDQ_PHY_TYPE_S=2, + PDQ_PHY_TYPE_M=3, + PDQ_PHY_TYPE_UNKNOWN=4 +} pdq_phy_type_t; + +typedef enum { + PDQ_PMD_TYPE_ANSI_MUTLI_MODE=0, + PDQ_PMD_TYPE_ANSI_SINGLE_MODE_TYPE_1=1, + PDQ_PMD_TYPE_ANSI_SIGNLE_MODE_TYPE_2=2, + PDQ_PMD_TYPE_ANSI_SONET=3, + PDQ_PMD_TYPE_LOW_POWER=100, + PDQ_PMD_TYPE_THINWIRE=101, + PDQ_PMD_TYPE_SHIELDED_TWISTED_PAIR=102, + PDQ_PMD_TYPE_UNSHIELDED_TWISTED_PAIR=103 +} pdq_pmd_type_t; + +typedef enum { + PDQ_PMD_CLASS_ANSI_MULTI_MODE=0, + PDQ_PMD_CLASS_SINGLE_MODE_TYPE_1=1, + PDQ_PMD_CLASS_SINGLE_MODE_TYPE_2=2, + PDQ_PMD_CLASS_SONET=3, + PDQ_PMD_CLASS_LOW_COST_POWER_FIBER=4, + PDQ_PMD_CLASS_TWISTED_PAIR=5, + PDQ_PMD_CLASS_UNKNOWN=6, + PDQ_PMD_CLASS_UNSPECIFIED=7 +} pdq_pmd_class_t; + +typedef enum { + PDQ_PHY_STATE_INTERNAL_LOOPBACK=0, + PDQ_PHY_STATE_BROKEN=1, + PDQ_PHY_STATE_OFF_READY=2, + PDQ_PHY_STATE_WAITING=3, + PDQ_PHY_STATE_STARTING=4, + PDQ_PHY_STATE_FAILED=5, + PDQ_PHY_STATE_WATCH=6, + PDQ_PHY_STATE_INUSE=7 +} pdq_phy_state_t; + +typedef enum { + PDQ_REJECT_REASON_NONE=0, + PDQ_REJECT_REASON_LOCAL_LCT=1, + PDQ_REJECT_REASON_REMOTE_LCT=2, + PDQ_REJECT_REASON_LCT_BOTH_SIDES=3, + PDQ_REJECT_REASON_LEM_REJECT=4, + PDQ_REJECT_REASON_TOPOLOGY_ERROR=5, + PDQ_REJECT_REASON_NOISE_REJECT=6, + PDQ_REJECT_REASON_REMOTE_REJECT=7, + PDQ_REJECT_REASON_TRACE_IN_PROGRESS=8, + PDQ_REJECT_REASON_TRACE_RECEIVED_DISABLED=9, + PDQ_REJECT_REASON_STANDBY=10, + PDQ_REJECT_REASON_LCT_PROTOCOL_ERROR=11 +} pdq_reject_reason_t; + +typedef enum { + PDQ_BROKEN_REASON_NONE=0 +} pdq_broken_reason_t; + +typedef enum { + PDQ_RI_REASON_TVX_EXPIRED=0, + PDQ_RI_REASON_TRT_EXPIRED=1, + PDQ_RI_REASON_RING_PURGER_ELECTION_ATTEMPT_LIMIT_EXCEEDED=2, + PDQ_RI_REASON_PURGE_ERROR_LIMIT_EXCEEDED=3, + PDQ_RI_REASON_RESTRICTED_TOKEN_TIMEOUT=4 +} pdq_ri_reason_t; + +typedef enum { + PDQ_LCT_DIRECTION_LOCAL_LCT=0, + PDQ_LCT_DIRECTION_REMOTE_LCT=1, + PDQ_LCT_DIRECTION_LCT_BOTH_SIDES=2 +} pdq_lct_direction_t; + +typedef enum { + PDQ_PORT_A=0, + PDQ_PORT_B=1 +} pdq_port_type_t; + +typedef struct { + pdq_uint8_t station_id_bytes[8]; +} pdq_station_id_t; + +typedef pdq_uint32_t pdq_fdditimer_t; +/* + * Command format for Start, Filter_Get, ... commands + */ +typedef struct { + pdq_cmd_code_t generic_op; +} pdq_cmd_generic_t; + +/* + * Response format for Start, Filter_Set, ... commands + */ +typedef struct { + pdq_uint32_t generic_reserved; + pdq_cmd_code_t generic_op; + pdq_response_code_t generic_status; +} pdq_response_generic_t; + +/* + * Command format for Filter_Set command + */ +typedef struct { + pdq_cmd_code_t filter_set_op; + struct { + pdq_item_code_t item_code; + pdq_filter_state_t filter_state; + } filter_set_items[7]; + pdq_item_code_t filter_set_eol_item_code; +} pdq_cmd_filter_set_t; + +/* + * Response format for Filter_Get command. + */ +typedef struct { + pdq_uint32_t filter_get_reserved; + pdq_cmd_code_t filter_get_op; + pdq_response_code_t filter_get_status; + pdq_filter_state_t filter_get_ind_group_prom; + pdq_filter_state_t filter_get_group_prom; + pdq_filter_state_t filter_get_broadcast_all; + pdq_filter_state_t filter_get_smt_prom; + pdq_filter_state_t filter_get_smt_user; + pdq_filter_state_t filter_get_reserved_all; + pdq_filter_state_t filter_get_implementor_all; +} pdq_response_filter_get_t; + +#define PDQ_SIZE_RESPONSE_FILTER_GET 0x28 + +typedef struct { + pdq_cmd_code_t chars_set_op; + struct { + pdq_item_code_t item_code; + pdq_uint32_t item_value; + pdq_port_type_t item_port; + } chars_set_items[1]; + pdq_item_code_t chars_set_eol_item_code; +} pdq_cmd_chars_set_t; + +typedef struct { + pdq_cmd_code_t addr_filter_set_op; + pdq_lanaddr_t addr_filter_set_addresses[62]; +} pdq_cmd_addr_filter_set_t; + +#define PDQ_SIZE_CMD_ADDR_FILTER_SET 0x1F4 + +typedef struct { + pdq_uint32_t addr_filter_get_reserved; + pdq_cmd_code_t addr_filter_get_op; + pdq_response_code_t addr_filter_get_status; + pdq_lanaddr_t addr_filter_get_addresses[62]; +} pdq_response_addr_filter_get_t; + +#define PDQ_SIZE_RESPONSE_ADDR_FILTER_GET 0x1FC + +typedef struct { + pdq_uint32_t status_chars_get_reserved; + pdq_cmd_code_t status_chars_get_op; + pdq_response_code_t status_chars_get_status; + struct { + /* Station Characteristic Attributes */ + pdq_station_id_t station_id; + pdq_station_type_t station_type; + pdq_uint32_t smt_version_id; + pdq_uint32_t smt_max_version_id; + pdq_uint32_t smt_min_version_id; + /* Station Status Attributes */ + pdq_station_state_t station_state; + /* Link Characteristic Attributes */ + pdq_lanaddr_t link_address; + pdq_fdditimer_t t_req; + pdq_fdditimer_t tvx; + pdq_fdditimer_t restricted_token_timeout; + pdq_boolean_t ring_purger_enable; + pdq_link_state_t link_state; + pdq_fdditimer_t negotiated_trt; + pdq_da_test_state_t dup_addr_flag; + /* Link Status Attributes */ + pdq_lanaddr_t upstream_neighbor; + pdq_lanaddr_t old_upstream_neighbor; + pdq_boolean_t upstream_neighbor_dup_addr_flag; + pdq_lanaddr_t downstream_neighbor; + pdq_lanaddr_t old_downstream_neighbor; + pdq_ring_purger_state_t ring_purger_state; + pdq_frame_strip_mode_t frame_strip_mode; + pdq_ring_error_reason_t ring_error_reason; + pdq_boolean_t loopback; + pdq_fdditimer_t ring_latency; + pdq_lanaddr_t last_dir_beacon_sa; + pdq_lanaddr_t last_dir_beacon_una; + /* Phy Characteristic Attributes */ + pdq_phy_type_t phy_type[2]; + pdq_pmd_type_t pmd_type[2]; + pdq_uint32_t lem_threshold[2]; + /* Phy Status Attributes */ + pdq_phy_state_t phy_state[2]; + pdq_phy_type_t neighbor_phy_type[2]; + pdq_uint32_t link_error_estimate[2]; + pdq_broken_reason_t broken_reason[2]; + pdq_reject_reason_t reject_reason[2]; + /* Miscellaneous */ + pdq_uint32_t counter_interval; + pdq_fwrev_t module_rev; + pdq_fwrev_t firmware_rev; + pdq_uint32_t mop_device_type; + pdq_uint32_t fddi_led[2]; + pdq_uint32_t flush; + } status_chars_get; +} pdq_response_status_chars_get_t; + +#define PDQ_SIZE_RESPONSE_STATUS_CHARS_GET 0xF0 + +typedef struct { + pdq_uint32_t fddi_mib_get_reserved; + pdq_cmd_code_t fddi_mib_get_op; + pdq_response_code_t fddi_mib_get_status; + struct { + /* SMT Objects */ + pdq_station_id_t smt_station_id; + pdq_uint32_t smt_op_version_id; + pdq_uint32_t smt_hi_version_id; + pdq_uint32_t smt_lo_version_id; + pdq_uint32_t smt_mac_ct; + pdq_uint32_t smt_non_master_ct; + pdq_uint32_t smt_master_ct; + pdq_uint32_t smt_paths_available; + pdq_uint32_t smt_config_capabilities; + pdq_uint32_t smt_config_policy; + pdq_uint32_t smt_connection_policy; + pdq_uint32_t smt_t_notify; + pdq_uint32_t smt_status_reporting; + pdq_uint32_t smt_ecm_state; + pdq_uint32_t smt_cf_state; + pdq_uint32_t smt_hold_state; + pdq_uint32_t smt_remote_disconnect_flag; + pdq_uint32_t smt_station_action; + /* MAC Objects */ + pdq_uint32_t mac_frame_status_capabilities; + pdq_uint32_t mac_t_max_greatest_lower_bound; + pdq_uint32_t mac_tvx_greatest_lower_bound; + pdq_uint32_t mac_paths_available; + pdq_uint32_t mac_current_path; + pdq_lanaddr_t mac_upstream_neighbor; + pdq_lanaddr_t mac_old_upstream_neighbor; + pdq_uint32_t mac_dup_addr_test; + pdq_uint32_t mac_paths_requested; + pdq_uint32_t mac_downstream_port_type; + pdq_lanaddr_t mac_smt_address; + pdq_uint32_t mac_t_req; + pdq_uint32_t mac_t_neg; + pdq_uint32_t mac_t_max; + pdq_uint32_t mac_tvx_value; + pdq_uint32_t mac_t_min; + pdq_uint32_t mac_current_frame_status; + pdq_uint32_t mac_frame_error_threshold; + pdq_uint32_t mac_frame_error_ratio; + pdq_uint32_t mac_rmt_state; + pdq_uint32_t mac_da_flag; + pdq_uint32_t mac_una_da_flag; + pdq_uint32_t mac_frame_condition; + pdq_uint32_t mac_chip_set; + pdq_uint32_t mac_action; + /* Port Objects */ + pdq_uint32_t port_pc_type[2]; + pdq_uint32_t port_pc_neighbor[2]; + pdq_uint32_t port_connection_policies[2]; + pdq_uint32_t port_remote_mac_indicated[2]; + pdq_uint32_t port_ce_state[2]; + pdq_uint32_t port_paths_requested[2]; + pdq_uint32_t port_mac_placement[2]; + pdq_uint32_t port_available_paths[2]; + pdq_uint32_t port_mac_loop_time[2]; + pdq_uint32_t port_tb_max[2]; + pdq_uint32_t port_bs_flag[2]; + pdq_uint32_t port_ler_estimate[2]; + pdq_uint32_t port_ler_cutoff[2]; + pdq_uint32_t port_ler_alarm[2]; + pdq_uint32_t port_connect_state[2]; + pdq_uint32_t port_pcm_state[2]; + pdq_uint32_t port_pc_withhold[2]; + pdq_uint32_t port_ler_condition[2]; + pdq_uint32_t port_chip_set[2]; + pdq_uint32_t port_action[2]; + /* Attachment Objects */ + pdq_uint32_t attachment_class; + pdq_uint32_t attachment_optical_bypass_present; + pdq_uint32_t attachment_imax_expiration; + pdq_uint32_t attachment_inserted_status; + pdq_uint32_t attachment_insert_policy; + } fddi_mib_get; +} pdq_response_fddi_mib_get_t; + +#define PDQ_SIZE_RESPONSE_FDDI_MIB_GET 0x17C + +typedef enum { + PDQ_FDX_STATE_IDLE=0, + PDQ_FDX_STATE_REQUEST=1, + PDQ_FDX_STATE_CONFIRM=2, + PDQ_FDX_STATE_OPERATION=3 +} pdq_fdx_state_t; + +typedef struct { + pdq_uint32_t dec_ext_mib_get_reserved; + pdq_cmd_code_t dec_ext_mib_get_op; + pdq_response_code_t dec_ext_mib_get_response; + struct { + /* SMT Objects */ + pdq_uint32_t esmt_station_type; + /* MAC Objects */ + pdq_uint32_t emac_link_state; + pdq_uint32_t emac_ring_purger_state; + pdq_uint32_t emac_ring_purger_enable; + pdq_uint32_t emac_frame_strip_mode; + pdq_uint32_t emac_ring_error_reason; + pdq_uint32_t emac_upstream_nbr_dupl_address_flag; + pdq_uint32_t emac_restricted_token_timeout; + /* Port Objects */ + pdq_uint32_t eport_pmd_type[2]; + pdq_uint32_t eport_phy_state[2]; + pdq_uint32_t eport_reject_reason[2]; + /* Full Duplex Objects */ + pdq_boolean_t fdx_enable; + pdq_boolean_t fdx_operational; + pdq_fdx_state_t fdx_state; + } dec_ext_mib_get; +} pdq_response_dec_ext_mib_get_t; + +#define PDQ_SIZE_RESPONSE_DEC_EXT_MIB_GET 0x50 + +typedef enum { + PDQ_CALLER_ID_NONE=0, + PDQ_CALLER_ID_SELFTEST=1, + PDQ_CALLER_ID_MFG=2, + PDQ_CALLER_ID_FIRMWARE=5, + PDQ_CALLER_ID_CONSOLE=8 +} pdq_caller_id_t; + +typedef struct { + pdq_uint32_t error_log_get__reserved; + pdq_cmd_code_t error_log_get_op; + pdq_response_code_t error_log_get_status; + /* Error Header */ + pdq_uint32_t error_log_get_event_status; + /* Event Information Block */ + pdq_caller_id_t error_log_get_caller_id; + pdq_uint32_t error_log_get_timestamp[2]; + pdq_uint32_t error_log_get_write_count; + /* Diagnostic Information */ + pdq_uint32_t error_log_get_fru_implication_mask; + pdq_uint32_t error_log_get_test_id; + pdq_uint32_t error_log_get_diag_reserved[6]; + /* Firmware Information */ + pdq_uint32_t error_log_get_fw_reserved[112]; +} pdq_response_error_log_get_t; + + +/* + * Definitions for the Unsolicited Event Queue. + */ +typedef enum { + PDQ_UNSOLICITED_EVENT=0, + PDQ_UNSOLICITED_COUNTERS=1 +} pdq_event_t; + +typedef enum { + PDQ_ENTITY_STATION=0, + PDQ_ENTITY_LINK=1, + PDQ_ENTITY_PHY_PORT=2 +} pdq_entity_t; + +typedef enum { + PDQ_STATION_EVENT_TRACE_RECEIVED=1 +} pdq_station_event_t; + +typedef enum { + PDQ_STATION_EVENT_ARGUMENT_REASON=0, /* pdq_uint32_t */ + PDQ_STATION_EVENT_ARGUMENT_EOL=0xFF +} pdq_station_event_argument_t; + +typedef enum { + PDQ_LINK_EVENT_TRANSMIT_UNDERRUN=0, + PDQ_LINK_EVENT_TRANSMIT_FAILED=1, + PDQ_LINK_EVENT_BLOCK_CHECK_ERROR=2, + PDQ_LINK_EVENT_FRAME_STATUS_ERROR=3, + PDQ_LINK_EVENT_PDU_LENGTH_ERROR=4, + PDQ_LINK_EVENT_RECEIVE_DATA_OVERRUN=7, + PDQ_LINK_EVENT_NO_USER_BUFFER=9, + PDQ_LINK_EVENT_RING_INITIALIZATION_INITIATED=10, + PDQ_LINK_EVENT_RING_INITIALIZATION_RECEIVED=11, + PDQ_LINK_EVENT_RING_BEACON_INITIATED=12, + PDQ_LINK_EVENT_DUPLICATE_ADDRESS_FAILURE=13, + PDQ_LINK_EVENT_DUPLICATE_TOKEN_DETECTED=14, + PDQ_LINK_EVENT_RING_PURGE_ERROR=15, + PDQ_LINK_EVENT_FCI_STRIP_ERROR=16, + PDQ_LINK_EVENT_TRACE_INITIATED=17, + PDQ_LINK_EVENT_DIRECTED_BEACON_RECEIVED=18 +} pdq_link_event_t; + +typedef enum { + PDQ_LINK_EVENT_ARGUMENT_REASON=0, /* pdq_rireason_t */ + PDQ_LINK_EVENT_ARGUMENT_DATA_LINK_HEADER=1, /* pdq_dlhdr_t */ + PDQ_LINK_EVENT_ARGUMENT_SOURCE=2, /* pdq_lanaddr_t */ + PDQ_LINK_EVENT_ARGUMENT_UPSTREAM_NEIGHBOR=3,/* pdq_lanaddr_t */ + PDQ_LINK_EVENT_ARGUMENT_EOL=0xFF +} pdq_link_event_argument_t; + +typedef enum { + PDQ_PHY_EVENT_LEM_ERROR_MONITOR_REJECT=0, + PDQ_PHY_EVENT_ELASTICITY_BUFFER_ERROR=1, + PDQ_PHY_EVENT_LINK_CONFIDENCE_TEST_REJECT=2 +} pdq_phy_event_t; + +typedef enum { + PDQ_PHY_EVENT_ARGUMENT_DIRECTION=0, /* pdq_lct_direction_t */ + PDQ_PHY_EVENT_ARGUMENT_EOL=0xFF +} pdq_phy_event_arguments; + +struct _pdq_unsolicited_event_t { + pdq_uint32_t rvent_reserved; + pdq_event_t event_type; + pdq_entity_t event_entity; + pdq_uint32_t event_index; + union { + pdq_station_event_t station_event; + pdq_link_event_t link_event; + pdq_phy_event_t phy_event; + pdq_uint32_t value; + } event_code; + /* + * The remainder of this event is an argument list. + */ + pdq_uint32_t event__filler[123]; +}; + +#endif /* _PDQREG_H */ |