From 07489c563c706b6c4a660268e30e2242c5294049 Mon Sep 17 00:00:00 2001 From: phk Date: Fri, 17 Feb 1995 02:22:57 +0000 Subject: This is the latest version of the APM stuff from HOSOKAWA, I have looked briefly over it, and see some serious architectural issues in this stuff. On the other hand, I doubt that we will have any solution to these issues before 2.1, so we might as well leave this in. Most of the stuff is bracketed by #ifdef's so it shouldn't matter too much in the normal case. Reviewed by: phk Submitted by: HOSOKAWA, Tatsumi --- sys/i386/isa/icu.s | 9 +- sys/i386/isa/if_ze.c | 404 ++------- sys/i386/isa/if_zp.c | 2266 +++++++++++++++++++++++++++++++++++++++++++++++ sys/i386/isa/if_zpreg.h | 295 ++++++ sys/i386/isa/isa.c | 4 +- sys/i386/isa/pcic.c | 249 ++++++ sys/i386/isa/pcic.h | 181 ++++ sys/i386/isa/syscons.c | 33 +- 8 files changed, 3126 insertions(+), 315 deletions(-) create mode 100644 sys/i386/isa/if_zp.c create mode 100644 sys/i386/isa/if_zpreg.h create mode 100644 sys/i386/isa/pcic.c create mode 100644 sys/i386/isa/pcic.h (limited to 'sys/i386/isa') diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s index e4e031d..ddab5de 100644 --- a/sys/i386/isa/icu.s +++ b/sys/i386/isa/icu.s @@ -36,7 +36,7 @@ * * @(#)icu.s 7.2 (Berkeley) 5/21/91 * - * $Id: icu.s,v 1.18 1995/01/14 11:00:27 bde Exp $ + * $Id: icu.s,v 1.19 1995/02/08 21:46:44 bde Exp $ */ /* @@ -154,6 +154,13 @@ doreti_unpend: jae doreti_swi cli movl %eax,_cpl +#if NAPM > 0 && defined(APM_SLOWSTART) + movl _apm_slowstart_stat, %eax + orl %eax, %eax + jz 1: + call _apm_idle_cpu +1: +#endif MEXITCOUNT jmp %edx diff --git a/sys/i386/isa/if_ze.c b/sys/i386/isa/if_ze.c index bd52940..f447cc0 100644 --- a/sys/i386/isa/if_ze.c +++ b/sys/i386/isa/if_ze.c @@ -47,7 +47,7 @@ */ /* - * $Id: if_ze.c,v 1.9 1994/11/24 14:29:26 davidg Exp $ + * $Id: if_ze.c,v 1.10 1995/01/23 18:02:32 phk Exp $ */ #include "ze.h" @@ -89,316 +89,15 @@ #include "i386/isa/isa_device.h" #include "i386/isa/icu.h" #include "i386/isa/if_zereg.h" +#include "i386/isa/pcic.h" +#include "apm.h" +#if NAPM > 0 +#include "i386/include/apm_bios.h" +#endif /* NAPM > 0 */ /***************************************************************************** - * pcmcia controller chip (PCIC) support * - * (eventually, move this to a separate file) * - *****************************************************************************/ -#include "ic/i82365.h" - -/* - * Each PCIC chip (82365SL or clone) can handle two card slots, and there - * can be up to four PCICs in a system. (On some machines, not all of the - * address lines are decoded, so a card may appear to be in more than one - * slot.) - */ -#define MAXSLOT 8 - -/* - * To access a register on the PCIC for a particular slot, you - * first write the correct OFFSET value for that slot in the - * INDEX register for the PCIC controller. You then read or write - * the value from or to the DATA register for that controller. - * - * The first pair of chips shares I/O addresss for DATA and INDEX, - * as does the second pair. (To the programmer, it looks like each - * pair is a single chip.) The i/o port addresses are hard-wired - * into the PCIC; so the following addresses should be valid for - * any machine that uses this chip. - */ - -#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */ -#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */ -#define PCIC_INDEX_1 0x3E2 /* index reg, chips 1 and 2 */ -#define PCIC_DATA_1 0x3E3 /* data register, chips 1 and 2 */ - -/* - * Given a slot number, calculate the INDEX and DATA registers - * to talk to that slot. OFFSET is added to the register number - * to address the registers for a particular slot. - */ -#define INDEX(slot) ((slot) < 4 ? PCIC_INDEX_0 : PCIC_INDEX_1) -#define DATA(slot) ((slot) < 4 ? PCIC_DATA_0 : PCIC_DATA_1) -#define OFFSET(slot) ((slot) % 4 * 0x40) - -/* - * There are 5 sets (windows) of memory mapping registers on the PCIC chip - * for each slot, numbered 0..4. - * - * They start at 10/50 hex within the chip's register space (not system - * I/O space), and are eight addresses apart. These are actually pairs of - * 8-bit-wide registers (low byte first, then high byte) since the - * address fields are actually 12 bits long. The upper bits are used - * for other things like 8/16-bit select and wait states. - * - * Memory mapping registers include start/stop addresses to define the - * region to be mapped (in terms of system memory addresses), and - * an offset register to allow for translation from system space - * to card space. The lower 12 bits aren't included in these, so memory is - * mapped in 4K chunks. - */ -#define MEM_START_ADDR(window) (((window) * 0x08) + 0x10) -#define MEM_STOP_ADDR(window) (((window) * 0x08) + 0x12) -#define MEM_OFFSET(window) (((window) * 0x08) + 0x14) -/* - * this bit gets set in the address window enable register (PCIC_ADDRWINE) - * to enable a particular address window. - */ -#define MEM_ENABLE_BIT(window) ((1) << (window)) - -/* - * There are two i/o port addressing windows. I/O ports cannot be - * relocated within system i/o space (unless the card doesn't decode - * all of the address bits); unlike card memory, there is no address - * translation offset. - */ -#define IO_START_ADDR(window) ((window) ? PCIC_IO1_STL : PCIC_IO0_STL) -#define IO_STOP_ADDR(window) ((window) ? PCIC_IO1_SPL : PCIC_IO0_SPL) -#define IO_ENABLE_BIT(window) ((window) ? PCIC_IO1_EN : PCIC_IO0_EN) -#define IO_CS16_BIT(window) ((window) ? PCIC_IO1_CS16 : PCIC_IO0_CS16) - -/* - * read a byte from a pcic register for a particular slot - */ -static inline unsigned char -pcic_getb (int slot, int reg) -{ - outb (INDEX(slot), OFFSET (slot) + reg); - return inb (DATA (slot)); -} - -/* - * write a byte to a pcic register for a particular slot - */ -static inline void -pcic_putb (int slot, int reg, unsigned char val) -{ - outb (INDEX(slot), OFFSET (slot) + reg); - outb (DATA (slot), val); -} - -/* - * read a word from a pcic register for a particular slot - */ -static inline unsigned short -pcic_getw (int slot, int reg) -{ - return pcic_getb (slot, reg) | (pcic_getb (slot, reg+1) << 8); -} - -/* - * write a word to a pcic register at a particular slot - */ -static inline void -pcic_putw (int slot, int reg, unsigned short val) -{ - pcic_putb (slot, reg, val & 0xff); - pcic_putb (slot, reg + 1, (val >> 8) & 0xff); -} - -static void -pcic_print_regs (int slot) -{ - int i, j; - - for (i = 0; i < 0x40; i += 16) { - for (j = 0; j < 16; ++j) - printf ("%02x ", pcic_getb (slot, i + j)); - printf ("\n"); - } -} - -/* - * map a portion of the card's memory space into system memory - * space. - * - * slot = # of the slot the card is plugged into - * window = which pcic memory map registers to use (0..4) - * sys_addr = base system PHYSICAL memory address where we want it. must - * be on an appropriate boundary (lower 12 bits are zero). - * card_addr = the base address of the card's memory to correspond - * to sys_addr - * length = length of the segment to map (may be rounded up as necessary) - * type = which card memory space to map (attribute or shared) - * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping. - */ - -enum memtype { COMMON, ATTRIBUTE }; - -static void -pcic_map_memory (int slot, int window, unsigned long sys_addr, - unsigned long card_addr, unsigned long length, - enum memtype type, int width) -{ - unsigned short offset; - unsigned short mem_start_addr; - unsigned short mem_stop_addr; - - sys_addr >>= 12; - card_addr >>= 12; - length >>= 12; - /* - * compute an offset for the chip such that - * (sys_addr + offset) = card_addr - * but the arithmetic is done modulo 2^14 - */ - offset = (card_addr - sys_addr) & 0x3FFF; - /* - * now OR in the bit for "attribute memory" if necessary - */ - if (type == ATTRIBUTE) { - offset |= (PCIC_REG << 8); - /* REG == "region active" pin on card */ - } - /* - * okay, set up the chip memory mapping registers, and turn - * on the enable bit for this window. - * if we are doing 16-bit wide accesses (width == 2), - * turn on the appropriate bit. - * - * XXX for now, we set all of the wait state bits to zero. - * Not really sure how they should be set. - */ - mem_start_addr = sys_addr & 0xFFF; - if (width == 2) - mem_start_addr |= (PCIC_DATA16 << 8); - mem_stop_addr = (sys_addr + length) & 0xFFF; - - pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr); - pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr); - pcic_putw (slot, MEM_OFFSET(window), offset); - /* - * Assert the bit (PCIC_MEMCS16) that says to decode all of - * the address lines. - */ - pcic_putb (slot, PCIC_ADDRWINE, - pcic_getb (slot, PCIC_ADDRWINE) | - MEM_ENABLE_BIT(window) | PCIC_MEMCS16); -} - -static void -pcic_unmap_memory (int slot, int window) -{ - /* - * seems like we need to turn off the enable bit first, after which - * we can clear the registers out just to be sure. - */ - pcic_putb (slot, PCIC_ADDRWINE, - pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window)); - pcic_putw (slot, MEM_START_ADDR(window), 0); - pcic_putw (slot, MEM_STOP_ADDR(window), 0); - pcic_putw (slot, MEM_OFFSET(window), 0); -} - -/* - * map a range of addresses into system i/o space - * (no translation of i/o addresses is possible) - * - * 'width' is: - * + 0 to tell the PCIC to generate the ISA IOCS16* signal from - * the PCMCIA IOIS16* signal. - * + 1 to select 8-bit width - * + 2 to select 16-bit width - */ - -static void -pcic_map_io (int slot, int window, unsigned short base, unsigned short length, - unsigned short width) -{ - unsigned char x; - - pcic_putw (slot, IO_START_ADDR(window), base); - pcic_putw (slot, IO_STOP_ADDR(window), base+length-1); - /* - * select the bits that determine whether - * an i/o operation is 8 or 16 bits wide - */ - x = pcic_getb (slot, PCIC_IOCTL); - switch (width) { - case 0: /* PCMCIA card decides */ - if (window) - x = (x & 0xf0) | PCIC_IO1_CS16; - else - x = (x & 0x0f) | PCIC_IO0_CS16; - break; - case 1: /* 8 bits wide */ - break; - case 2: /* 16 bits wide */ - if (window) - x = (x & 0xf0) | PCIC_IO1_16BIT; - else - x = (x & 0x0f) | PCIC_IO0_16BIT; - break; - } - pcic_putb (slot, PCIC_IOCTL, x); - pcic_putb (slot, PCIC_ADDRWINE, - pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window)); -} - -#ifdef TEST -static void -pcic_unmap_io (int slot, int window) -{ - pcic_putb (slot, PCIC_ADDRWINE, - pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window)); - pcic_putw (slot, IO_START_ADDR(window), 0); - pcic_putw (slot, IO_STOP_ADDR(window), 0); -} -#endif /* TEST */ - -/* - * tell the PCIC which irq we want to use. only the following are legal: - * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 - * - * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device. - */ - -static void -pcic_map_irq (int slot, int irq) -{ - if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) { - printf ("ze: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq); - return; - } - pcic_putb (slot, PCIC_INT_GEN, - pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F)); -} - -static void -pcic_power_on (int slot) -{ - pcic_putb (slot, PCIC_POWER, - pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE); - DELAY (100000); - pcic_putb (slot, PCIC_POWER, - pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA); -} - -static void -pcic_reset (int slot) -{ - /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */ - pcic_putb (slot, PCIC_INT_GEN, - pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET); - DELAY (100000); - pcic_putb (slot, PCIC_INT_GEN, - pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET); -} - - -/***************************************************************************** * Driver for Ethernet Adapter * *****************************************************************************/ /* @@ -437,8 +136,13 @@ struct ze_softc { u_char rec_page_start; /* first page of RX ring-buffer */ u_char rec_page_stop; /* last page of RX ring-buffer */ u_char next_packet; /* pointer to next unread RX packet */ + int slot; /* information for reconfiguration */ u_char last_alive; /* information for reconfiguration */ u_char last_up; /* information for reconfiguration */ +#if NAPM > 0 + struct apmhook s_hook; /* reconfiguration support */ + struct apmhook r_hook; /* reconfiguration support */ +#endif /* NAPM > 0 */ } ze_softc[NZE]; int ze_attach(), ze_ioctl(), ze_probe(); @@ -529,12 +233,14 @@ ze_check_cis (unsigned char *scratch) * + Leaves product/vendor id of last card probed in 'card_info' */ +static int prev_slot = 0; + static int ze_find_adapter (unsigned char *scratch, int reconfig) { int slot; - for (slot = 0; slot < MAXSLOT; ++slot) { + for (slot = prev_slot; slot < MAXSLOT; ++slot) { /* * see if there's a PCMCIA controller here * Intel PCMCIA controllers use 0x82 and 0x83 @@ -558,6 +264,9 @@ ze_find_adapter (unsigned char *scratch, int reconfig) if (!reconfig) { printf ("ze: slot %d: no card in slot\n", slot); } + else { + log (LOG_NOTICE, "ze: slot %d: no card in slot\n", slot); + } /* no card in slot */ continue; } @@ -576,15 +285,24 @@ ze_find_adapter (unsigned char *scratch, int reconfig) if (!reconfig) { printf ("ze: found card in slot %d\n", slot); } + else { + log (LOG_NOTICE, "ze: found card in slot %d\n", slot); + } + prev_slot = (prev_slot == MAXSLOT - 1) ? 0 : prev_slot+1; + return slot; } else { if (!reconfig) { printf ("ze: pcmcia slot %d: %s\n", slot, card_info); } + else { + log (LOG_NOTICE, "ze: pcmcia slot %d: %s\n", slot, card_info); + } } pcic_unmap_memory (slot, 0); } + prev_slot = 0; return -1; } @@ -611,7 +329,7 @@ ze_probe(isa_dev) struct isa_device *isa_dev; { struct ze_softc *sc = &ze_softc[isa_dev->id_unit]; - int i, x; + int i, x, re_init_flag; u_int memsize; u_char iptr, memwidth, sum, tmp; int slot; @@ -652,6 +370,8 @@ ze_probe(isa_dev) enet_addr[5] = PEEK(isa_dev->id_maddr+0xffa); pcic_unmap_memory (slot, 0); + re_init_flag = 0; +re_init: /* * (2) map card configuration registers. these are offset * in card memory space by 0x20000. normally we could get @@ -749,10 +469,26 @@ ze_probe(isa_dev) outb (isa_dev->id_iobase + ZE_RESET, tmp); DELAY(20000); +#if 0 + tmp = inb(isa_dev->id_iobase); + printf("CR = 0x%x\n", tmp); +#endif /* * query MAM bit in misc register for 10base2 */ tmp = inb (isa_dev->id_iobase + ZE_MISC); + + /* + * Some Intel-compatible PCICs of Cirrus Logic fails in + * initializing them. This is a quick hack to fix this + * problem. + * HOSOKAWA, Tatsumi + */ + if (!tmp && !re_init_flag) { + re_init_flag++; + goto re_init; + } + sc->mau = tmp & 0x09 ? "10base2" : "10baseT"; /* set width/size */ @@ -779,8 +515,34 @@ ze_probe(isa_dev) /* information for reconfiguration */ sc->last_alive = 0; sc->last_up = 0; + sc->slot = slot; + return 32; } + +#if NAPM > 0 +static int +ze_suspend(isa_dev) + struct isa_device *isa_dev; +{ + struct ze_softc *sc = &ze_softc[isa_dev->id_unit]; + + pcic_power_off(sc->slot); + return 0; +} + +static int +ze_resume(isa_dev) + struct isa_device *isa_dev; +{ +#if 0 + printf("Resume ze:\n"); +#endif + prev_slot = 0; + reconfig_isadev(isa_dev, &net_imask); + return 0; +} +#endif /* NAPM > 0 */ /* * Install interface into kernel networking data structures @@ -794,21 +556,26 @@ ze_attach(isa_dev) struct ifnet *ifp = &sc->arpcom.ac_if; struct ifaddr *ifa; struct sockaddr_dl *sdl; + int pl; /* PCMCIA card can be offlined. Reconfiguration is required */ if (isa_dev->id_reconfig) { + ze_reset(isa_dev->id_unit); if (!isa_dev->id_alive && sc->last_alive) { + pl = splimp(); sc->last_up = (ifp->if_flags & IFF_UP); - ifp->if_flags &= ~(IFF_UP); + if_down(ifp); + splx(pl); sc->last_alive = 0; } if (isa_dev->id_alive && !sc->last_alive) { if (sc->last_up) { - ifp->if_flags |= IFF_UP; + pl = splimp(); + if_up(ifp); + splx(pl); } sc->last_alive = 1; } - ze_reset(isa_dev->id_unit); return 1; } else { @@ -888,6 +655,19 @@ ze_attach(isa_dev) bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif +#if NAPM > 0 + sc->s_hook.ah_fun = ze_suspend; + sc->s_hook.ah_arg = (void *)isa_dev; + sc->s_hook.ah_name = "IBM PCMCIA Ethernet I/II"; + sc->s_hook.ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_SUSPEND , &sc->s_hook); + sc->r_hook.ah_fun = ze_resume; + sc->r_hook.ah_arg = (void *)isa_dev; + sc->r_hook.ah_name = "IBM PCMCIA Ethernet I/II"; + sc->r_hook.ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_RESUME , &sc->r_hook); +#endif /* NAPM > 0 */ + return 1; } diff --git a/sys/i386/isa/if_zp.c b/sys/i386/isa/if_zp.c new file mode 100644 index 0000000..27c7f3d --- /dev/null +++ b/sys/i386/isa/if_zp.c @@ -0,0 +1,2266 @@ +#define ZP_DEBUG 1 +/* + * This code is based on + * (1) FreeBSD implementation on ISA/EISA Ethelink III by Herb Peyerl + * (2) Linux implementation on PCMCIA Etherlink III by Devid Hinds + * (3) FreeBSD implementation on PCMCIA IBM Ethernet Card I/II + * by David Greenman + * (4) RT-Mach implementation on PCMCIA/ISA/EISA Etherlink III + * by Seiji Murata + * + * Copyright (c) by HOSOKAWA, Tatsumi + * Copyright (c) by Seiji Murata + */ +/* + * Copyright (c) 1993 Herb Peyerl + * 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 without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ + * $Id: if_ep.c,v 1.9 1994/05/02 22:27:33 ats Exp $ + */ +/*- + * TODO: + * [1] integrate into current if_ed.c + * [2] parse tuples to find out where to map the shared memory buffer, + * and what to write into the configuration register + * [3] move pcic-specific code into a separate module. + * + * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet, + * if_ze.c + * + * Based on the Device driver for National Semiconductor DS8390 ethernet + * adapters by David Greenman. Modifications for PCMCIA by Keith Moore. + * Adapted for FreeBSD 1.1.5 by Jordan Hubbard. + * + * Currently supports only the IBM Credit Card Adapter for Ethernet, but + * could probably work with other PCMCIA cards also, if it were modified + * to get the locations of the PCMCIA configuration option register (COR) + * by parsing the configuration tuples, rather than by hard-coding in + * the value expected by IBM's card. + * + * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver: + * + * [1] _Local Area Network Credit Card Adapters Technical Reference_, + * IBM Corp., SC30-3585-00, part # 33G9243. + * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan. + * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel + * Order Number 290423-002 + * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network + * Interface Controller for Twisted Pair data sheet. + * + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + */ +/*====================================================================== + + A PCMCIA ethernet driver for the 3com 3c589 card. + + Written by David Hinds, dhinds@allegro.stanford.edu + + The network driver code is based on Donald Becker's 3c589 code: + + Written 1994 by Donald Becker. + Copyright 1993 United States Government as represented by the + Director, National Security Agency. This software may be used and + distributed according to the terms of the GNU Public License, + incorporated herein by reference. + Donald Becker may be reached at becker@cesdis1.gsfc.nasa.gov + +======================================================================*/ +/* + * I doubled delay loops in this file because it is not enough for some + * laptop machines' PCIC (especially, on my Chaplet ILFA 350 ^^;). + * HOSOKAWA, Tatsumi + */ +/* + * Very small patch for IBM Ethernet PCMCIA Card II and IBM ThinkPad230Cs. + * ETO, Toshihisa + */ + +#include "zp.h" +#if NZP > 0 +#ifdef MACH_KERNEL + +#define IF_CNTRS MACH + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define SPLNET spl6 + +#if MACH_TTD +#include +#endif /* MACH_TTD */ + +#include "i82365.h" + +#define MAXSLOT 8 +#define SHARED_MEMORY + +enum memtype { COMMON, ATTRIBUTE }; + +#else /* MACH_KERNEL */ + +#include "bpfilter.h" + +#include +#if defined(__FreeBSD__) +#include +#include +#endif +#include +#include +#include +#include +#include +#if defined(__NetBSD__) +#include +#endif + +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#ifdef NS +#include +#include +#endif + +#if NBPFILTER > 0 +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "apm.h" +#if NAPM > 0 +#include "i386/include/apm_bios.h" +#endif /* NAPM > 0 */ + +#endif /* MACH_KERNEL */ + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 + + +/***************************************************************************** + * Driver for Ethernet Adapter * + *****************************************************************************/ +/* + * zp_softc: per line info and status + */ +struct zp_softc { +#ifdef MACH_KERNEL + struct ifnet ds_if; /* generic interface header */ + u_char ds_addr[6]; /* Ethernet hardware address */ +#else /* MACH_KERNEL */ + struct arpcom arpcom; /* Ethernet common part */ +#define MAX_MBS 8 /* # of mbufs we keep around */ + struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ + int next_mb; /* Which mbuf to use next. */ + int last_mb; /* Last mbuf. */ + caddr_t bpf; /* BPF "magic cookie" */ +#endif /* MACH_KERNEL */ + short ep_io_addr; /* i/o bus address */ + char ep_connectors; /* Connectors on this card. */ + int tx_start_thresh; /* Current TX_start_thresh. */ + char bus32bit; /* 32bit access possible */ +#ifdef MACH_KERNEL + u_char attached; +#endif +#ifndef ORIGINAL + u_short if_port; +#endif /* ORIGINAL */ + u_char last_alive; /* information for reconfiguration */ + u_char last_up; /* information for reconfiguration */ + int slot; /* PCMCIA slot */ +#if NAPM > 0 + struct apmhook s_hook; /* reconfiguration support */ + struct apmhook r_hook; /* reconfiguration support */ +#endif /* NAPM > 0 */ +} zp_softc[NZP]; + +#ifdef MACH_KERNEL + +static int send_ID_sequence(),f_is_eeprom_busy(); +static u_short get_eeprom_data(); +int zpprobe(),zpopen(),zpoutput(),zpsetinput(),zpgetstat(), + zpsetstat(),zpintr(); +void zpattach(),zpinit(),zpstart(),zpread(),zpreset(), + zpwatchdog(),zpstop(); + +static vm_offset_t zp_std[NZP] = {0}; +static struct bus_device *zp_info[NZP]; + +struct bus_driver zpdriver = + {zpprobe, 0, zpattach, 0, zp_std, "zp", zp_info, 0, 0, 0}; + +typedef struct zp_softc zp_softc_t; + +char *zp_name = "zp"; + +static unsigned char card_info[256]; + +#define splnet spl7 +#define splimp spl7 + +#else /* MACH_KERNEL */ + +int zpprobe __P((struct isa_device *)); +int zpattach __P((struct isa_device *)); +int zpioctl __P((struct ifnet * ifp, int, caddr_t)); +static u_short read_eeprom_data __P((int, int)); + +void zpinit __P((int)); +void zpintr __P((int)); +void zpmbuffill __P((caddr_t)); +void zpmbufempty __P((struct zp_softc *)); +void zpread __P((struct zp_softc *)); +void zpreset __P((int)); +void zpstart __P((struct ifnet *)); +void zpstop __P((int)); +void zpwatchdog __P((int)); + +struct isa_driver zpdriver = { + zpprobe, + zpattach, + "zp" +}; + +static int send_ID_sequence __P((u_short)); +static u_short get_eeprom_data __P((int, int)); +static int f_is_eeprom_busy __P((struct isa_device *)); + +#endif /* MACH_KERNEL */ + +#define CARD_INFO "3Com Corporation~3C589" + +static unsigned char card_info[256]; + +/* + * scan the card information structure looking for the version/product info + * tuple. when we find it, compare it to the string we are looking for. + * return 1 if we find it, 0 otherwise. + */ + +static int +zp_check_cis (unsigned char *scratch) +{ + int i,j,k; + + card_info[0] = '\0'; + i = 0; + while (scratch[i] != 0xff && i < 1024) { + unsigned char link = scratch[i+2]; + +#if 0 + printf ("[%02x] %02x ", i, link); + for (j = 4; j < 2 * link + 4 && j < 32; j += 2) + printf ("%02x ", scratch[j + i]); + printf ("\n"); +#endif + if (scratch[i] == 0x15) { + /* + * level 1 version/product info + * copy to card_info, translating '\0' to '~' + */ + k = 0; + for (j = i+8; scratch[j] != 0xff; j += 2) + card_info[k++] = scratch[j] == '\0' ? '~' : scratch[j]; + card_info[k++] = '\0'; +#ifdef ZP_DEBUG + printf("card info = %s\n", card_info); + printf("result = %d\n", memcmp (card_info, CARD_INFO, sizeof(CARD_INFO)-1) == 0); +#endif + return (memcmp (card_info, CARD_INFO, sizeof(CARD_INFO)-1) == 0); + } + i += 4 + 2 * link; + } + return 0; +} + +/* + * Probe each slot looking for an IBM Credit Card Adapter for Ethernet + * For each card that we find, map its card information structure + * into system memory at 'scratch' and see whether it's one of ours. + * Return the slot number if we find a card, or -1 otherwise. + * + * Side effects: + * + On success, leaves CIS mapped into memory at 'scratch'; + * caller must free it. + * + On success, leaves ethernet address in enet_addr. + * + Leaves product/vendor id of last card probed in 'card_info' + */ + +int prev_slot = 0; + +static int +zp_find_adapter (unsigned char *scratch, int reconfig) +{ + int slot; + + for (slot = prev_slot; slot < MAXSLOT; ++slot) { + /* + * see if there's a PCMCIA controller here + * Intel PCMCIA controllers use 0x82 and 0x83 + * IBM clone chips use 0x88 and 0x89, apparently + */ + /* + * IBM ThinkPad230Cs use 0x84. + */ + unsigned char idbyte = pcic_getb (slot, PCIC_ID_REV); + + if (idbyte != 0x82 && idbyte != 0x83 && + idbyte != 0x84 && /* for IBM ThinkPad 230Cs */ + idbyte != 0x88 && idbyte != 0x89) { +#if 0 + printf ("ibmccae: pcic slot %d: wierd id/rev code 0x%02x\n", + slot, idbyte); +#endif + continue; + } + if ((pcic_getb (slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) { + if (!reconfig) { + printf ("zp: slot %d: no card in slot\n", slot); + } + else { + log (LOG_NOTICE, "zp: slot %d: no card in slot\n", slot); + } + /* no card in slot */ + continue; + } + pcic_power_on (slot); + pcic_reset (slot); + /* + * map the card's attribute memory and examine its + * card information structure tuples for something + * we recognize. + */ +#ifdef MACH_KERNEL + pcic_map_memory (slot, 0, scratch, 0L, + 0xFFFL, ATTRIBUTE, 1); +#else /* MACH_KERNEL */ + pcic_map_memory (slot, 0, kvtop (scratch), 0L, + 0xFFFL, ATTRIBUTE, 1); +#endif /* MACH_KERNEL */ + + if ((zp_check_cis (scratch)) > 0) { + /* found it */ + if (!reconfig) { + printf ("zp: found card in slot %d\n", slot); + } + else { + log (LOG_NOTICE, "zp: found card in slot %d\n", slot); + } + prev_slot = (prev_slot == MAXSLOT - 1) ? 0 : prev_slot+1; + + return slot; + } + else { + if (!reconfig) { + printf ("zp: pcmcia slot %d: %s\n", slot, card_info); + } + else { + log (LOG_NOTICE, "zp: pcmcia slot %d: %s\n", slot, card_info); + } + } + pcic_unmap_memory (slot, 0); + } + prev_slot = 0; + return -1; +} + + +/* + * macros to handle casting unsigned long to (char *) so we can + * read/write into physical memory space. + */ + +#define PEEK(addr) (*((unsigned char *)(addr))) +#define POKE(addr,val) do { PEEK(addr) = (val); } while (0) + +/* + * Determine if the device is present + * + * on entry: + * a pointer to an isa_device struct + * on exit: + * NULL if device not found + * or # of i/o addresses used (if found) + */ +#ifdef MACH_KERNEL +int zpprobe(port,dev) + struct bus_device *dev; +#else /* MACH_KERNEL */ +int +zpprobe(struct isa_device *isa_dev) +#endif /* MACH_KERNEL */ +{ +#ifdef MACH_KERNEL + zp_softc_t *sc = &zp_softc[dev->unit]; +#else /* MACH_KERNEL */ + struct zp_softc *sc = &zp_softc[isa_dev->id_unit]; +#endif /* MACH_KERNEL */ + int i, x; + u_int memsize; + u_char iptr, memwidth, sum, tmp; + int slot; + u_short k; + int id_port = 0x100; /* XXX */ + int re_init_flag; + +#ifdef ZP_DEBUG + printf("### zpprobe ####\n"); +#ifdef MACH_KERNEL + cngetc(); +#endif /* MACH_KERNEL */ +#endif /* ZP_DEBUG */ + + +#ifdef MACH_KERNEL +#define DELAY(x) delay(x * 10) + sc->attached = 0; + BASE = dev->address; +#endif /* MACH_KERNEL */ + + if ((slot = zp_find_adapter (isa_dev->id_maddr, isa_dev->id_reconfig)) < 0) + return NULL; + + /* + * okay, we found a card, so set it up + */ + /* + * Inhibit 16 bit memory delay. + * POINTETH.SYS apparently does this, for what reason I don't know. + */ + pcic_putb (slot, PCIC_CDGC, + pcic_getb (slot, PCIC_CDGC) | PCIC_16_DL_INH); + /* + * things to map + * (1) card's EEPROM is already mapped by the find_adapter routine + * but we still need to get the card's ethernet address. + * after that we unmap that part of attribute memory. + * (2) card configuration registers need to be mapped in so we + * can set the configuration and socket # registers. + * (3) shared memory packet buffer + * (4) i/o ports + * (5) IRQ + */ +#ifdef notdef + /* + * Sigh. Location of the ethernet address isn't documented in [1]. + * It was derived by doing a hex dump of all of attribute memory + * and looking for the IBM vendor prefix. + */ + enet_addr[0] = PEEK(isa_dev->id_maddr+0xff0); + enet_addr[1] = PEEK(isa_dev->id_maddr+0xff2); + enet_addr[2] = PEEK(isa_dev->id_maddr+0xff4); + enet_addr[3] = PEEK(isa_dev->id_maddr+0xff6); + enet_addr[4] = PEEK(isa_dev->id_maddr+0xff8); + enet_addr[5] = PEEK(isa_dev->id_maddr+0xffa); +#endif +#if 0 + pcic_unmap_memory (slot, 0); +#endif + re_init_flag = 0; +re_init: + /* + * (2) map card configuration registers. these are offset + * in card memory space by 0x20000. normally we could get + * this offset from the card information structure, but I'm + * too lazy and am not quite sure if I understand the CIS anyway. + * + * XXX IF YOU'RE TRYING TO PORT THIS DRIVER FOR A DIFFERENT + * PCMCIA CARD, the most likely thing to change is the constant + * 0x20000 in the next statement. Oh yes, also change the + * card id string that we probe for. + */ +#ifdef MACH_KERNEL + pcic_map_memory (slot, 0, dev->phys_address, 0x10000, 8L, + ATTRIBUTE, 1); + POKE(phystokv(dev->phys_address), 0x80); /* reset the card (how long?) */ + DELAY (10000); +#else + pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x10000, 8L, + ATTRIBUTE, 1); + POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */ + DELAY (40000); +#endif + /* + * Set the configuration index. According to [1], the adapter won't + * respond to any i/o signals until we do this; it uses the + * Memory Only interface (whatever that is; it's not documented). + * Also turn on "level" (not pulse) interrupts. + * + * XXX probably should init the socket and copy register also, + * so that we can deal with multiple instances of the same card. + */ +#ifdef MACH_KERNEL + POKE(phystokv(dev->phys_address), 0x41); + pcic_unmap_memory (slot, 0); +#else /* MACH_KERNEL */ + POKE(isa_dev->id_maddr, 0x41); + pcic_unmap_memory (slot, 0); +#endif /* MACH_KERNEL*/ + +#ifdef notdef + /* + * (3) now map in the shared memory buffer. This has to be mapped + * as words, not bytes, and on a 16k boundary. The offset value + * was derived by installing IBM's POINTETH.SYS under DOS and + * looking at the PCIC registers; it's not documented in IBM's + * tech ref manual ([1]). + */ + pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x4000L, 0x4000L, + COMMON, 2); +#endif + + /* + * (4) map i/o ports. + * + * XXX is it possible that the config file leaves this unspecified, + * in which case we have to pick one? + * + * At least one PCMCIA device driver I'v seen maps a block + * of 32 consecutive i/o ports as two windows of 16 ports each. + * Maybe some other pcic chips are restricted to 16-port windows; + * the 82365SL doesn't seem to have that problem. But since + * we have an extra window anyway... + */ +#ifdef MACH_KERNEL + pcic_map_io (slot, 0, dev->address, 16, 2); +#else +#if 1 + pcic_map_io (slot, 0, isa_dev->id_iobase, 16, 2); +#else + pcic_map_io (slot, 0, isa_dev->id_iobase, 16, 1); + pcic_map_io (slot, 1, isa_dev->id_iobase + 16, 16, 1); +#endif +#endif + + /* + * (5) configure the card for the desired interrupt + * + * XXX is it possible that the config file leaves this unspecified? + */ + pcic_map_irq (slot, ffs (isa_dev->id_irq) - 1); + + /* tell the PCIC that this is an I/O card (not memory) */ + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDTYPE); + +#if 0 + /* tell the PCIC to use level-mode interrupts */ + /* XXX this register may not be present on all controllers */ + pcic_putb (slot, PCIC_GLO_CTRL, + pcic_getb (slot, PCIC_GLO_CTRL) | PCIC_LVL_MODE); +#endif + +#ifdef ZP_DEBUG + pcic_print_regs (slot); +#endif +#ifdef notdef + /* I couldn't find the following part in linux. seiji */ + + /* + * Setup i/o addresses + */ + sc->nic_addr = isa_dev->id_iobase; +#if 0 + sc->vector = isa_dev->id_irq; +#endif + sc->smem_start = (caddr_t)isa_dev->id_maddr; + +#if 0 + sc->vendor = ZE_VENDOR_IBM; + sc->type = xxx; +#endif + + /* reset card to force it into a known state */ + tmp = inb (isa_dev->id_iobase + ZE_RESET); + DELAY(20000); + outb (isa_dev->id_iobase + ZE_RESET, tmp); + DELAY(20000); + + /* + * query MAM bit in misc register for 10base2 + */ + tmp = inb (isa_dev->id_iobase + ZE_MISC); + if (!tmp && !re_init_flag) { + re_init_flag++; + goto re_init; + } + sc->mau = tmp & 0x09 ? "10base2" : "10baseT"; +#endif + +#ifdef MACH_KERNEL + sc->ep_io_addr = dev->address; +#else /* MACH_KERNEL */ + sc->ep_io_addr = isa_dev->id_iobase; +#endif /* MACH_KERNEL */ + GO_WINDOW(0); +#if 0 + k = get_eeprom_data(BASE, EEPROM_ADDR_CFG); /* get addr cfg */ +#endif + k = read_eeprom_data(BASE, EEPROM_ADDR_CFG); /* get addr cfg */ +#ifndef ORIGINAL + sc->if_port = k >> 14; +#endif /* ORIGINAL */ +#ifdef ZP_DEBUG + printf("EEPROM data = 0x%x\n", k); +#endif + k = (k & 0x1f) * 0x10 + 0x200; /* decode base addr. */ +#ifdef MACH_KERNEL + if (k != (u_short)dev->address) +#else /* MACH_KERNEL */ + if (k != (u_short)isa_dev->id_iobase) +#endif /* MACH_KERNEL */ + { + if (!re_init_flag) { + re_init_flag++; + goto re_init; + } + return(0); + } + + k = read_eeprom_data(BASE, EEPROM_RESOURCE_CFG); + + k >>= 12; + +#ifdef MACH_KERNEL +#ifdef ZP_DEBUG + printf("!!!IRQ Mach config: %d, board config: %d!!!\n", + dev->sysdep1, (k == 2) ? 9 : k); +#ifdef MACH_KERNEL + cngetc(); +#endif /* MACH_KERNEL */ +#endif /* ZP_DEBUG */ + if (dev->sysdep1 != ((k == 2) ? 9 : k)) +#else /* MACH_KERNEL */ + if (isa_dev->id_irq != (1 << ((k == 2) ? 9 : k))) +#endif /* MACH_KERNEL */ +#ifdef ZP_DEBUG + { + printf("Unmatched !!!!!!\n"); + return(0); + } +#else /* ZP_DEBUG */ + return (0); +#endif /* ZP_DEBUG */ + +#if 0 + outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG); +#else + outb(BASE, ACTIVATE_ADAPTER_TO_CONFIG); +#endif + +#ifdef MACH_KERNEL + dev->name = zp_name; + return (1); +#else /* MACH_KERNEL */ + + /* information for reconfiguration */ + sc->last_alive = 0; + sc->last_up = 0; + sc->slot = slot; + + return (0x10); /* 16 bytes of I/O space used. */ +#endif /* MACH_KERNEL */ +} + +#if NAPM > 0 +static int +zp_suspend(isa_dev) + struct isa_device *isa_dev; +{ + struct zp_softc *sc = &zp_softc[isa_dev->id_unit]; + + pcic_power_off(sc->slot); + return 0; +} + +static int +zp_resume(isa_dev) + struct isa_device *isa_dev; +{ + prev_slot = 0; + reconfig_isadev(isa_dev, &net_imask); + return 0; +} +#endif /* NAPM > 0 */ + + +/* + * Install interface into kernel networking data structures + */ + +#ifdef MACH_KERNEL +void zpattach(dev) + struct bus_device *dev; +#else /* MACH_KERNEL */ +int +zpattach(isa_dev) + struct isa_device *isa_dev; +#endif /* MACH_KERNEL */ +{ +#ifdef MACH_KERNEL + zp_softc_t *sc = &zp_softc[dev->unit]; + struct ifnet *ifp = &(sc->ds_if); +#else /* MACH_KERNEL */ + struct zp_softc *sc = &zp_softc[isa_dev->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; +#endif /* MACH_KERNEL */ + u_short i; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + int pl; + +#ifdef ZP_DEBUG + printf("### zpattach ####\n"); + cngetc(); +#endif /* ZP_DEBUG */ + + /* PCMCIA card can be offlined. Reconfiguration is required */ + if (isa_dev->id_reconfig) { + zpreset(isa_dev->id_unit); + if (!isa_dev->id_alive && sc->last_alive) { + pl = splimp(); + sc->last_up = (ifp->if_flags & IFF_UP); + if_down(ifp); + splx(pl); + sc->last_alive = 0; + } + if (isa_dev->id_alive && !sc->last_alive) { + if (sc->last_up) { + pl = splimp(); + if_up(ifp); + splx(pl); + } + sc->last_alive = 1; + } + return 1; + } + else { + sc->last_alive = 1; + } + + +#ifdef MACH_KERNEL + printf(", port = %x, spl = %d, pic = %d. ", + dev->address, dev->sysdep, dev->sysdep1); + + take_dev_irq(dev); + sc->ep_io_addr = dev->address; +#else /* MACH_KERNEL */ + sc->ep_io_addr = isa_dev->id_iobase; + printf("zp%d: ", isa_dev->id_unit); +#endif /* MACH_KERNEL */ + + sc->ep_connectors = 0; + +#ifdef MACH_KERNEL + i = inw(sc->ep_io_addr + EP_W0_CONFIG_CTRL); +#else /* MACH_KERNEL */ + i = inw(isa_dev->id_iobase + EP_W0_CONFIG_CTRL); +#endif /* MACH_KERNEL */ + +#ifdef ZP_DEBUG + { + short if_port; + if_port = read_eeprom_data(BASE, 8) >> 14; + sc->if_port = if_port; + printf("Linux select:%x\n",if_port); + cngetc(); + } + + printf("SELECT connectors:%x\n",i); +#endif /* ZP_DEBUG */ + + if (i & IS_AUI) { + printf("aui"); + sc->ep_connectors |= AUI; + } + if (i & IS_BNC) { + if (sc->ep_connectors) + printf("/"); + printf("bnc"); + sc->ep_connectors |= BNC; + } + if (i & IS_UTP) { + if (sc->ep_connectors) + printf("/"); + printf("utp"); + sc->ep_connectors |= UTP; + } + if (!sc->ep_connectors) + printf("no connectors!"); + +#ifdef MACH_KERNEL +#ifndef ORIGINAL + printf(":%s was selected.\n",if_names[sc->if_port]); +#ifdef ZP_DEBUG + cngetc(); +#endif /* EP_DEBUG */ +#endif /* ORIGINAL */ +#endif /* MACH_KERNEL */ + + GO_WINDOW(0); + { + short tmp_addr[3]; + int i; + for (i = 0;i < 3;i++) { + tmp_addr[i] = htons(read_eeprom_data(BASE, i)); + } +#ifdef MACH_KERNEL + bcopy(tmp_addr, sc->ds_addr, 6); +#else /* MACH_KERNEL */ + bcopy(tmp_addr, sc->arpcom.ac_enaddr, 6); +#endif /* MACH_KERNEL */ + } + +#ifdef MACH_KERNEL + printf("id [%x:%x:%x:%x:%x:%x]", + sc->ds_addr[0],sc->ds_addr[1],sc->ds_addr[2], + sc->ds_addr[3],sc->ds_addr[4],sc->ds_addr[5]); + printf(" address %s\n", ether_sprintf(sc->ds_addr)); +#ifdef ZP_DEBUG + cngetc(); +#endif /* ZP_DEBUG */ +#else /* MACH_KERNEL */ + printf(" address %s\n", ether_sprintf(sc->arpcom.ac_enaddr)); +#endif /* MACH_KERNEL */ + + ifp->if_mtu = ETHERMTU; +#ifdef MACH_KERNEL + ifp->if_flags = IFF_BROADCAST; + ifp->if_unit = dev->unit; + ifp->if_header_size = sizeof(struct ether_header); + ifp->if_header_format = HDR_ETHERNET; + ifp->if_address_size = 6; + ifp->if_address = (char *)&sc->ds_addr[0]; + +#define IFF_ALTPHYS 0x8000 + +#ifdef ORIGINAL + /* + * This is a temporary. + * Mach can not select link with ifconfig, + * so I select AUI statically. + */ + ifp->if_flags |= IFF_ALTPHYS; +#else ORIGINAL + /* + * Select connector according to board setting. + */ + if (sc->if_port != 3) { + ifp->if_flags |= IFF_ALTPHYS; + } +#endif ORIGINAL + + if_init_queues(ifp); + sc->attached = 1; + +#else /* MACH_KERNEL */ + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + ifp->if_unit = isa_dev->id_unit; + ifp->if_name = "zp"; + ifp->if_init = zpinit; + ifp->if_output = ether_output; + ifp->if_start = zpstart; + ifp->if_ioctl = zpioctl; + ifp->if_watchdog = zpwatchdog; + /* + * Select connector according to board setting. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) +#if 0 + if (sc->if_port != 3) { + ifp->if_flags |= IFF_LINK0; + } +#else + ifp->if_flags |= IFF_LINK0; +#endif +#endif /* __NetBSD__ || __FreeBSD__ */ + + if_attach(ifp); +#endif /* MACH_KERNEL */ + +#ifndef MACH_KERNEL + /* + * Fill the hardware address into ifa_addr if we find an + * AF_LINK entry. We need to do this so bpf's can get the hardware + * addr of this card. netstat likes this too! + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +#if NAPM > 0 + sc->s_hook.ah_fun = zp_suspend; + sc->s_hook.ah_arg = (void *)isa_dev; + sc->s_hook.ah_name = "3Com PCMCIA Etherlink III 3C589"; + sc->s_hook.ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_SUSPEND , &sc->s_hook); + sc->r_hook.ah_fun = zp_resume; + sc->r_hook.ah_arg = (void *)isa_dev; + sc->r_hook.ah_name = "3Com PCMCIA Etherlink III 3C589"; + sc->r_hook.ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_RESUME , &sc->r_hook); +#endif /* NAPM > 0 */ + return 1; +#endif /* MACH_KERNEL */ +} + +/* + * The order in here seems important. Otherwise we may not receive + * interrupts. ?! + */ +void +zpinit(unit) + int unit; +{ + register struct zp_softc *sc = &zp_softc[unit]; +#ifdef MACH_KERNEL + register struct ifnet *ifp = &sc->ds_if; +#else /* MACH_KERNEL */ + register struct ifnet *ifp = &sc->arpcom.ac_if; +#endif /* MACH_KERNEL */ + int s, i; + +#ifdef ZP_DEBUG + printf("### zpinit ####\n"); +#endif /* ZP_DEBUG */ + +#ifndef MACH_KERNEL + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; +#endif /* MACH_KERNEL */ + + s = splimp(); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + + GO_WINDOW(0); + + /* Disable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, 0); + + /* Enable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); + + GO_WINDOW(2); + + /* Reload the ether_addr. */ + for (i = 0; i < 6; i++) +#ifdef MACH_KERNEL + outb(BASE + EP_W2_ADDR_0 + i, sc->ds_addr[i]); +#else /* MACH_KERNEL */ + outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); +#endif /* MACH_KERNEL */ + + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + EP_W1_TX_STATUS); + + /* get rid of stray intr's */ + outw(BASE + EP_COMMAND, ACK_INTR | 0xff); + + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL); + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL); + +#ifndef ORIGINAL +#ifndef IFF_MULTICAST +#define IFF_MULTICAST 0x10000 +#endif + +#ifdef MACH_KERNEL + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + ((sc->ds_if.if_flags & IFF_MULTICAST) ? FIL_GROUP : 0) | + FIL_BRDCST | + ((sc->ds_if.if_flags & IFF_PROMISC) ? FIL_ALL : 0)); +#else + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + ((sc->arpcom.ac_if.if_flags & IFF_MULTICAST) ? FIL_GROUP : 0) | + FIL_BRDCST | + ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) ? FIL_ALL : 0)); +#endif MACH_KERNEL +#else /* ORIGINAL */ + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST); +#endif /* ORIGINAL */ + /* + * you can `ifconfig (link0|-link0) ep0' to get the following + * behaviour: + * -link0 disable AUI/UTP. enable BNC. + * link0 disable BNC. enable AUI. if the card has a UTP + * connector, that is enabled too. not sure, but it + * seems you have to be careful to not plug things + * into both AUI & UTP. + */ + +#if defined(__NetBSD__) || defined(__FreeBSD__) + if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) { +#else + if (!(ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & BNC)) { +#endif +#ifdef ZP_DEBUG + printf("START TRANCEIVER"); +#endif /* ZP_DEBUG */ + outw(BASE + EP_COMMAND, START_TRANSCEIVER); + DELAY(1000); + } + +#if defined(__NetBSD__) || defined(__FreeBSD__) + if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) { +#else + if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) { +#endif +#ifdef ZP_DEBUG + printf("ENABLE UTP"); +#endif /* ZP_DEBUG */ + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); + GO_WINDOW(1); + } + + outw(BASE + EP_COMMAND, RX_ENABLE); + outw(BASE + EP_COMMAND, TX_ENABLE); + + ifp->if_flags |= IFF_RUNNING; +#ifndef MACH_KERNEL + ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ +#endif /* MACH_KERNEL */ + sc->tx_start_thresh = 20; /* probably a good starting point. */ +#ifndef MACH_KERNEL + /* + * Store up a bunch of mbuf's for use later. (MAX_MBS). First we + * free up any that we had in case we're being called from intr or + * somewhere else. + */ + sc->last_mb = 0; + sc->next_mb = 0; + zpmbuffill((caddr_t)sc); +#endif /* MACH_KERNEL */ +#ifdef MACH_KERNEL +#if 0 /* seiji */ + sc->tbusy = 0; +#endif + zpstart(unit); +#else /* MACH_KERNEL */ + zpstart(ifp); +#endif /* MACH_KERNEL */ + splx(s); +#ifdef ZP_DEBUG + printf("### zpinit done ####\n"); +#endif /* ZP_DEBUG */ +} + +static const char padmap[] = {0, 3, 2, 1}; + +#ifdef MACH_KERNEL +void +zpstart(unit) + int unit; +#else /* MACH_KERNEL */ +void +zpstart(ifp) + struct ifnet *ifp; +#endif /* MACH_KERNEL */ +{ +#ifdef MACH_KERNEL + register struct zp_softc *sc = &zp_softc[unit]; + struct ifnet *ifp = &sc->ds_if; + io_req_t m; +#else /* MACH_KERNEL */ + register struct zp_softc *sc = &zp_softc[ifp->if_unit]; + struct mbuf *m, *top; +#endif /* MACH_KERNEL */ + + int s, len, pad; + +#ifdef ZP_DEBUG + printf("### zpstart ####\n"); +#endif /* ZP_DEBUG */ +#ifdef ZP_DEBUG + printf("head1 = 0x%x\n", sc->arpcom.ac_if.if_snd.ifq_head); + printf("BASE = 0x%x\n", BASE); +#endif + + s = splimp(); + +#ifndef MACH_KERNEL + if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { + splx(s); +#ifdef ZP_DEBUG + printf("### zpstart oactive ####\n"); +#endif /* ZP_DEBUG */ + return; + } +#endif /* MACH_KERNEL */ + +startagain: + +#ifdef MACH_KERNEL + +#if 0 /* seiji */ + if (sc->tbusy) { + return; + } +#endif + /* Sneak a peek at the next packet */ + m = (io_req_t) + ((ifp->if_snd.ifq_head.next == (queue_t)&(ifp->if_snd.ifq_head)) ? + 0 : ifp->if_snd.ifq_head.next); + if (m == 0) { + splx(s); +#ifdef ZP_DEBUG + printf("### zpstart none data 1 ####\n"); +#endif /* ZP_DEBUG */ + return; + } +#if 0 + IF_DEQUEUE(&ifp->if_snd, m); + if (NULL == m) { + return; + } +#endif + +#if 0 /* seiji */ + sc->tbusy++; + zp_cntrs[unit].xmt++; +#endif + len = m->io_count; +#else /* MACH_KERNEL */ + + /* Sneak a peek at the next packet */ + m = sc->arpcom.ac_if.if_snd.ifq_head; +#ifdef ZP_DEBUG + printf("head2 = 0x%x\n", sc->arpcom.ac_if.if_snd.ifq_head); +#endif + if (m == 0) { + splx(s); +#ifdef ZP_DEBUG + printf("### zpstart none data 2 ####\n"); +#endif /* EP_DEBUG */ + return; + } +#if 0 + len = m->m_pkthdr.len; +#else + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; +#endif +#endif /* MACH_KERNEL */ + + pad = padmap[len & 3]; + + /* + * The 3c509 automatically pads short packets to minimum ethernet + * length, but we drop packets that are too large. Perhaps we should + * truncate them instead? + */ + if (len + pad > ETHER_MAX_LEN) { + /* packet is obviously too large: toss it */ +#ifdef MACH_KERNEL + ++(ifp->if_oerrors); + IF_DEQUEUE(&(ifp->if_snd), m); + iodone(m); + +#else /* MACH_KERNEL */ + ++sc->arpcom.ac_if.if_oerrors; + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + m_freem(m); +#endif /* MACH_KERNEL */ + goto readcheck; + } +#if 1 + if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { + /* no room in FIFO */ + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); +#ifndef MACH_KERNEL + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; +#endif /* MACH_KERNEL */ + splx(s); + +#ifdef ZP_DEBUG + printf("### zpstart no room ####\n"); +#endif /* EP_DEBUG */ + return; + } +#else + {int i; + if ((i = inw(BASE + EP_W1_FREE_TX)) < len + pad + 4) { + printf("BASE + EP_W1_FREE_TX = 0x%x\n", i); + /* no room in FIFO */ + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); +#ifndef MACH_KERNEL + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; +#endif /* MACH_KERNEL */ + splx(s); + + printf("### zpstart no room ####\n"); + return; + } + printf("BASE + EP_W1_FREE_TX = 0x%x\n", i); + } +#endif +#ifdef MACH_KERNEL + IF_DEQUEUE(&(ifp->if_snd), m); +#else /* MACH_KERNEL */ + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); +#endif /* MACH_KERNEL */ + + if (m == 0) { /* not really needed */ + splx(s); +#ifdef ZP_DEBUG + printf("### zpstart ??? ####\n"); +#endif /* ZP_DEBUG */ + return; + } + outw(BASE + EP_COMMAND, SET_TX_START_THRESH | + (len / 4 + sc->tx_start_thresh)); + + outw(BASE + EP_W1_TX_PIO_WR_1, len); + outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ + +#ifdef MACH_KERNEL + if (sc->bus32bit) { + loutl(BASE + EP_W1_TX_PIO_WR_1, m->io_data,len/4); + if (len & 3) + loutb(BASE + EP_W1_TX_PIO_WR_1, + m->io_data + (len & ~3), len & 3); + } else { + loutw(BASE + EP_W1_TX_PIO_WR_1, m->io_data, len/2); + if (len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, *(m->io_data + len - 1)); + } +#else /* MACH_KERNEL */ + for (top = m; m != 0; m = m->m_next) { + if (sc->bus32bit) { + outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), + m->m_len/4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + m->m_len/4, + m->m_len & 3); + } else { +#ifdef ZP_DEBUG + printf("Output len = %d\n", m->m_len); +#endif /* ZP_DEBUG */ + outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); + } + } +#endif /* MACH_KERNEL */ + while (pad--) + outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ + +#ifndef MACH_KERNEL +#if NBPFILTER > 0 + if (sc->bpf) { +#if 0 + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(top, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + eh->ether_type = etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(top, off, sizeof(struct trailer_header), + (caddr_t)&trailer_header.ether_type); + + /* copy residual data */ + resid = trailer_header.ether_residual - + sizeof(struct trailer_header); + resid = ntohs(resid); + m_copydata(top, off + sizeof(struct trailer_header), + resid, ep); + ep += resid; + + /* copy data */ + m_copydata(top, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, top); +#endif + + bpf_mtap(sc->bpf, top); + } +#endif +#endif /* MACH_KERNEL */ + +#ifdef MACH_KERNEL + ++(ifp->if_opackets); + iodone(m); +#else /* MACH_KERNEL */ + m_freem(top); + ++sc->arpcom.ac_if.if_opackets; +#endif /* MACH_KERNEL */ + /* + * Is another packet coming in? We don't want to overflow the + * tiny RX fifo. + */ +readcheck: + if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { + splx(s); +#ifdef ZP_DEBUG + printf("### zpstart done ####\n"); +#endif /* ZP_DEBUG */ + return; + } +#ifdef ZP_DEBUG2 + printf("### zpstart startagain ####\n"); +#endif /* ZP_DEBUG */ + goto startagain; +} + +#ifdef MACH_KERNEL +int zpopen(dev, flag) + dev_t dev; + int flag; +{ + register int unit = minor(dev); + +#ifdef ZP_DEBUG + printf("### zpopen ####\n"); +#endif /* ZP_DEBUG */ + + if (unit < 0 || unit >= NZP || zp_softc[unit].attached == 0) { + return (ENXIO); + } + + zp_softc[unit].ds_if.if_flags |= IFF_UP; + zpinit(unit); + return (0); +} + +int zpoutput(dev, ior) + dev_t dev; + io_req_t ior; +{ + register int unit = minor(dev); + io_return_t result; + +#ifdef ZP_DEBUG + printf("### zpoutput ####\n"); +#endif /* ZP_DEBUG */ + + if (unit < 0 || unit >= NZP || zp_softc[unit].attached == 0) { + return (ENXIO); + } + result = net_write(&zp_softc[unit].ds_if, zpstart, ior); +#ifdef ZP_DEBUG + printf("### zpoutput done ####\n"); +#endif /* ZP_DEBUG */ + return (result); +} + +int zpsetinput(dev, receive_port, priority, filter, filter_count) + dev_t dev; + mach_port_t receive_port; + int priority; + filter_t filter[]; + unsigned int filter_count; +{ + register int unit = minor(dev); + +#ifdef ZP_DEBUG + printf("### zpsetinput ####\n"); +#endif /* ZP_DEBUG */ + + if (unit < 0 || unit >= NZP || zp_softc[unit].attached == 0) { + return (ENXIO); + } + return (net_set_filter(&zp_softc[unit].ds_if, + receive_port, priority, filter, + filter_count)); +} + +#endif /* MACH_KERNEL */ + +#ifdef MACH_KERNEL +int +#else /* MACH_KERNEL */ +void +#endif /* MACH_KERNEL */ +zpintr(unit) + int unit; +{ + int status, i; + register struct zp_softc *sc = &zp_softc[unit]; + +#ifdef MACH_KERNEL + struct ifnet *ifp = &sc->ds_if; +#else /* MACH_KERNEL */ + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; +#endif /* MACH_KERNEL */ + +#ifdef ZP_DEBUG + printf("### zpintr ####\n"); +#endif /* ZP_DEBUG */ + + status = 0; +checkintr: + status = inw(BASE + EP_STATUS) & + (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE); +#ifndef ORIGINAL +checkintr2: +#endif /* ORIGINAL */ + if (status == 0) { + /* No interrupts. */ + outw(BASE + EP_COMMAND, C_INTR_LATCH); +#ifdef ZP_DEBUG + printf("### zpintr done ####\n"); +#endif /* ZP_DEBUG */ + +#ifndef ORIGINAL + if (status = inw(BASE + EP_STATUS) & + (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | + S_CARD_FAILURE)) { + goto checkintr2; + } +#endif /* ORIGINAL */ + +#ifdef MACH_KERNEL + return (0); +#else /* MACH_KERNEL */ + return; +#endif /* MACH_KERNEL */ + } + /* important that we do this first. */ + outw(BASE + EP_COMMAND, ACK_INTR | status); + + if (status & S_TX_AVAIL) { + status &= ~S_TX_AVAIL; + inw(BASE + EP_W1_FREE_TX); +#ifdef MACH_KERNEL + zpstart(unit); +#else /* MACH_KERNEL */ + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + zpstart(&sc->arpcom.ac_if); +#endif /* MACH_KERNEL */ + + } + if (status & S_RX_COMPLETE) { + status &= ~S_RX_COMPLETE; + zpread(sc); + } + if (status & S_CARD_FAILURE) { + printf("zp%d: reset (status: %x)\n", unit, status); + outw(BASE + EP_COMMAND, C_INTR_LATCH); + zpinit(unit); +#ifdef ZP_DEBUG + printf("### zpintr error ####\n"); +#endif /* ZP_DEBUG */ +#ifdef MACH_KERNEL + return (0); +#else /* MACH_KERNEL */ + return; +#endif /* MACH_KERNEL */ + } + if (status & S_TX_COMPLETE) { + status &= ~S_TX_COMPLETE; + /* + * We need to read TX_STATUS until we get a 0 status in + * order to turn off the interrupt flag. + */ + while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { + outw(BASE + EP_W1_TX_STATUS, 0x0); +#if ZE_DEBUG + printf("EP_W1_TX_STATUS = 0x%x\n", i); +#endif + if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) { + if (i & TXS_MAX_COLLISION) +#ifdef MACH_KERNEL + ++(ifp->if_collisions); +#else /* MACH_KERNEL */ + ++sc->arpcom.ac_if.if_collisions; +#endif /* MACH_KERNEL */ + if (i & (TXS_JABBER | TXS_UNDERRUN)) { + outw(BASE + EP_COMMAND, TX_RESET); + if (i & TXS_UNDERRUN) { + if (sc->tx_start_thresh < ETHER_MAX_LEN) { + sc->tx_start_thresh += 20; + outw(BASE + EP_COMMAND, + SET_TX_START_THRESH | + sc->tx_start_thresh); + } + } + } + outw(BASE + EP_COMMAND, TX_ENABLE); +#ifdef MACH_KERNEL + ++(ifp->if_oerrors); +#else /* MACH_KERNEL */ + ++sc->arpcom.ac_if.if_oerrors; +#endif /* MACH_KERNEL */ + } + } +#ifdef MACH_KERNEL + zpstart(unit); +#else /* MACH_KERNEL */ + zpstart(ifp); +#endif /* MACH_KERNEL */ + } + goto checkintr; +} + +void +zpread(sc) + register struct zp_softc *sc; +{ + struct ether_header *eh; +#ifdef MACH_KERNEL + struct ether_header eth; + ipc_kmsg_t new_kmsg; + struct packet_header *pkt; + int totlen; +#else /* MACH_KERNEL */ + struct mbuf *mcur, *m, *m0, *top; + int totlen, lenthisone; + int save_totlen; + u_short etype; + int off, resid; + int count, spinwait; +#endif /* MACH_KERNEL */ + int i; + +#ifdef ZP_DEBUG + printf("### zpread ####\n"); +#endif /* ZP_DEBUG */ + + totlen = inw(BASE + EP_W1_RX_STATUS); +#ifndef MACH_KERNEL + off = 0; + top = 0; +#endif /* MACH_KERNEL */ + + if (totlen & ERR_RX) { +#ifdef MACH_KERNEL + ++(sc->ds_if.if_ierrors); +#else /* MACH_KERNEL */ + ++sc->arpcom.ac_if.if_ierrors; +#endif /* MACH_KERNEL */ + goto out; + } +#ifdef MACH_KERNEL + totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ +#else /* MACH_KERNEL */ + save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ +#endif /* MACH_KERNEL */ + +#ifdef MACH_KERNEL + /* Get Etherheader */ + + linw(BASE + EP_W1_RX_PIO_RD_1, + (char *)ð, sizeof(struct ether_header) / 2); + totlen -= sizeof(struct ether_header); + + new_kmsg = net_kmsg_get(); + if (new_kmsg == IKM_NULL) { + /* Drop the packet */ + ++(sc->ds_if.if_rcvdrops); + /* + * Is this true ? + * Do I have to remove the packet ? + * Maybe out discard incoming packet. + */ + goto out; + } + eh = (struct ether_header *)(&net_kmsg(new_kmsg)->header[0]); + pkt = (struct packet_header *)(&net_kmsg(new_kmsg)->packet[0]); + + *eh = eth; /* Is this true ? */ + + if (sc->bus32bit) { + linl(BASE + EP_W1_RX_PIO_RD_1, (char *)(pkt + 1), totlen/4); + if (totlen & 3) + linb(BASE + EP_W1_RX_PIO_RD_1, + (char *)(pkt + 1) + (totlen & ~3), totlen & 3); + } else { + linw(BASE + EP_W1_RX_PIO_RD_1, + (char *)(pkt + 1),totlen/2); + if (totlen & 1) + *((char *)(pkt + 1) + totlen - 1) = + inb(BASE + EP_W1_RX_PIO_RD_1); + } + + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + ++(sc->ds_if.if_ipackets); + + pkt->type = eh->ether_type; + pkt->length = totlen + sizeof(struct packet_header); + + net_packet(&(sc->ds_if), new_kmsg, pkt->length, + ethernet_priority(new_kmsg, pkt->type)); + +#ifdef ZP_DEBUG + printf("### zpread done ####\n"); +#endif /* ZP_DEBUG */ + + return; + +#else /* MACH_KERNEL */ + m = sc->mb[sc->next_mb]; + sc->mb[sc->next_mb] = 0; + + if (m == 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto out; + } else { + /* Convert one of our saved mbuf's */ + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + m->m_data = m->m_pktdat; + m->m_flags = M_PKTHDR; + } + + top = m0 = m; /* We assign top so we can "goto out" */ +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + m0->m_data += EOFF; + /* Read what should be the header. */ + insw(BASE + EP_W1_RX_PIO_RD_1, + mtod(m0, caddr_t), sizeof(struct ether_header) / 2); + m->m_len = sizeof(struct ether_header); + totlen -= sizeof(struct ether_header); + /* + * mostly deal with trailer here. (untested) + * We do this in a couple of parts. First we check for a trailer, if + * we have one we convert the mbuf back to a regular mbuf and set the offset and + * subtract sizeof(struct ether_header) from the pktlen. + * After we've read the packet off the interface (all except for the trailer + * header, we then get a header mbuf, read the trailer into it, and fix up + * the mbuf pointer chain. + */ + eh = mtod(m, struct ether_header *); + eh->ether_type = etype = ntohs((u_short) eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { + m->m_data = m->m_dat; /* Convert back to regular mbuf. */ + m->m_flags = 0; /* This sucks but non-trailers are the norm */ + off = (etype - ETHERTYPE_TRAIL) * 512; + if (off >= ETHERMTU) { + m_freem(m); + return; /* sanity */ + } + totlen -= sizeof(struct ether_header); /* We don't read the trailer */ + m->m_data += 2 * sizeof(u_short); /* Get rid of type & len */ + } + while (totlen > 0) { + lenthisone = min(totlen, M_TRAILINGSPACE(m)); + if (lenthisone == 0) { /* no room in this one */ + mcur = m; + m = sc->mb[sc->next_mb]; + sc->mb[sc->next_mb] = 0; + if (!m) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) + goto out; + } else { + timeout(zpmbuffill, (caddr_t)sc, 0); + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + } + if (totlen >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + m->m_len = 0; + mcur->m_next = m; + lenthisone = min(totlen, M_TRAILINGSPACE(m)); + } + if (sc->bus32bit) { + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); + } else { + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + } + totlen -= lenthisone; + } + if (off) { + top = sc->mb[sc->next_mb]; + sc->mb[sc->next_mb] = 0; + if (top == 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (top == 0) { + top = m0; + goto out; + } + } else { + /* Convert one of our saved mbuf's */ + sc->next_mb = (sc->next_mb + 1) % MAX_MBS; + top->m_data = top->m_pktdat; + top->m_flags = M_PKTHDR; + } + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), + sizeof(struct ether_header)); + top->m_next = m0; + top->m_len = sizeof(struct ether_header); + /* XXX Accomodate for type and len from beginning of trailer */ + top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short)); + } else { + top = m0; + top->m_pkthdr.len = save_totlen; + } + + top->m_pkthdr.rcvif = &sc->arpcom.ac_if; + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + ++sc->arpcom.ac_if.if_ipackets; +#if NBPFILTER > 0 + if (sc->bpf) { + bpf_mtap(sc->bpf, top); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + (eh->ether_dhost[0] & 1) == 0 && + bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + m_freem(top); + return; + } + } +#endif + m_adj(top, sizeof(struct ether_header)); + ether_input(&sc->arpcom.ac_if, eh, top); + return; +#endif /* MACH_KERNEL */ + +out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; +#ifndef MACH_KERNEL + if (top) + m_freem(top); +#endif /* MACH_KERNEL */ + +#ifdef ZP_DEBUG + printf("### zpread Error ####\n"); +#endif /* ZP_DEBUG */ +} + + +#ifdef MACH_KERNEL + +int zpgetstat(dev, flavor, status, count) + dev_t dev; + int flavor; + dev_status_t status; + unsigned int *count; +{ + register int unit = minor(dev); + +#ifdef ZP_DEBUG + printf("### zpgetstat ####\n"); +#endif /* ZP_DEBUG */ + + if (unit < 0 || unit >= NZP || zp_softc[unit].attached == 0) { + return (ENXIO); + } + return (net_getstat(&zp_softc[unit].ds_if,flavor,status,count)); +} + +int zpsetstat(dev, flavor, status, count) + dev_t dev; + int flavor; + dev_status_t status; + unsigned int count; +{ + register int unit = minor(dev); + register zp_softc_t *sc; + +#ifdef ZP_DEBUG + printf("### zpsetstat ####\n"); +#endif /* ZP_DEBUG */ + + if (unit < 0 || unit >= NZP || zp_softc[unit].attached == 0) { + return (ENXIO); + } + + sc = &zp_softc[unit]; + + switch (flavor) { + case NET_STATUS: + { + register struct net_status *ns = + (struct net_status *)status; + + if (count < NET_STATUS_COUNT) { + return (D_INVALID_SIZE); + } + if (sc->ds_if.if_flags != ns->flags) { + sc->ds_if.if_flags = ns->flags; + if (sc->ds_if.if_flags & IFF_RUNNING) { + zpinit(sc->ds_if.if_unit); + } + } + } + break; + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + +#else /* MACH_KERNEL */ + +/* + * Look familiar? + */ +static int +zpioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *) data; + struct zp_softc *sc = &zp_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; + +#ifdef ZP_DEBUG + printf("### zpioctl ####\n"); +#endif /* ZP_DEBUG */ + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + zpinit(ifp->if_unit); /* before arpwhohas */ +#if 1 + arp_ifinit((struct arpcom *)ifp, ifa); +#else + ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); +#endif + break; +#endif +#ifdef NS + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->arpcom.ac_enaddr); + else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + zpinit(ifp->if_unit); + break; + } +#endif + default: + zpinit(ifp->if_unit); + break; + } + break; + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + zpstop(ifp->if_unit); + zpmbufempty(sc); + break; + } + if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) + zpinit(ifp->if_unit); + break; +#ifdef notdef + case SIOCGHWADDR: + bcopy((caddr_t) sc->sc_addr, (caddr_t) &ifr->ifr_data, + sizeof(sc->sc_addr)); + break; +#endif + default: + error = EINVAL; + } + return (error); +} + +#endif /* MACH_KERNEL */ + +void +zpreset(unit) + int unit; +{ + int s = splimp(); + +#ifdef ZP_DEBUG + printf("### zpreset ####\n"); +#endif /* ZP_DEBUG */ + + zpstop(unit); + zpinit(unit); + splx(s); +} + +void +zpwatchdog(unit) + int unit; +{ + struct zp_softc *sc = &zp_softc[unit]; + +#ifdef ZP_DEBUG + printf("### zpwatchdog ####\n"); +#endif /* ZP_DEBUG */ + +#ifdef MACH_KERNEL + ++sc->ds_if.if_oerrors; +#else /* MACH_KERNEL */ + log(LOG_ERR, "zp%d: watchdog\n", unit); + ++sc->arpcom.ac_if.if_oerrors; +#endif /* MACH_KERNEL */ + zpreset(unit); +} + +void +zpstop(unit) + int unit; +{ + struct zp_softc *sc = &zp_softc[unit]; + +#ifdef ZP_DEBUG + printf("### zpstop ####\n"); +#endif /* ZP_DEBUG */ + + outw(BASE + EP_COMMAND, RX_DISABLE); + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + outw(BASE + EP_COMMAND, TX_DISABLE); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + outw(BASE + EP_COMMAND, C_INTR_LATCH); + outw(BASE + EP_COMMAND, SET_RD_0_MASK); + outw(BASE + EP_COMMAND, SET_INTR_MASK); + outw(BASE + EP_COMMAND, SET_RX_FILTER); +} + + +/* + * This is adapted straight from the book. There's probably a better way. + */ +static int +send_ID_sequence(port) + u_short port; +{ + char cx, al; + +#ifdef ZP_DEBUG2 + printf("### send_ID_sequence ####\n"); +#ifdef MACH_KERNEL + cngetc(); +#endif /* MACH_KERNEL */ +#endif /* ZP_DEBUG */ + + cx = 0x0ff; + al = 0x0ff; + + outb(port, 0x0); + DELAY(1000); + outb(port, 0x0); + DELAY(1000); + +loop1: cx--; + outb(port, al); + if (!(al & 0x80)) { + al = al << 1; + goto loop1; + } + al = al << 1; + al ^= 0xcf; + if (cx) + goto loop1; + + return(1); +} + + +/* + * We get eeprom data from the id_port given an offset into the + * eeprom. Basically; after the ID_sequence is sent to all of + * the cards; they enter the ID_CMD state where they will accept + * command requests. 0x80-0xbf loads the eeprom data. We then + * read the port 16 times and with every read; the cards check + * for contention (ie: if one card writes a 0 bit and another + * writes a 1 bit then the host sees a 0. At the end of the cycle; + * each card compares the data on the bus; if there is a difference + * then that card goes into ID_WAIT state again). In the meantime; + * one bit of data is returned in the AX register which is conveniently + * returned to us by inb(). Hence; we read 16 times getting one + * bit of data with each read. + */ +static u_short +get_eeprom_data(id_port, offset) + int id_port; + int offset; +{ + int i, data = 0; + +#ifdef ZP_DEBUG2 + printf("### get_eeprom_data ####\n"); +#ifdef MACH_KERNEL + cngetc(); +#endif /* MACH_KERNEL */ +#endif /* ZP_DEBUG */ + + outb(id_port, 0x80 + offset); + DELAY(1000); + for (i = 0; i < 16; i++) + data = (data << 1) | (inw(id_port) & 1); + return (data); +} + + +static u_short +read_eeprom_data(id_port, offset) + int id_port; + int offset; +{ + int i, data = 0; + +#ifdef ZP_DEBUG + printf("### read_eeprom_data ####\n"); +#ifdef MACH_KERNEL + cngetc(); +#endif /* MACH_KERNEL */ +#endif /* ZP_DEBUG */ + + outb(id_port + 10, 0x80 + offset); + DELAY(1000); + return inw(id_port + 12); +} + + + +static int +#ifdef MACH_KERNEL +f_is_eeprom_busy(dev) + struct bus_device *dev; +#else /* MACH_KERNEL */ +f_is_eeprom_busy(is) + struct isa_device *is; +#endif /* MACH_KERNEL */ +{ + int i = 0, j; +#ifdef MACH_KERNEL + register struct zp_softc *sc = &zp_softc[dev->unit]; +#else /* MACH_KERNEL */ + register struct zp_softc *sc = &zp_softc[is->id_unit]; +#endif /* MACH_KERNEL */ + +#ifdef ZP_DEBUG + printf("### f_is_eeprom_busy ####\n"); + printf("BASE: %x\n", BASE); +#ifdef MACH_KERNEL + cngetc(); +#endif /* MACH_KERNEL */ +#endif /* ZP_DEBUG */ + + while (i++ < 100) { + j = inw(BASE + EP_W0_EEPROM_COMMAND); + if (j & EEPROM_BUSY) + DELAY(100); + else + break; + } + if (i >= 100) { +#ifdef MACH_KERNEL + printf("\nzp%d: eeprom failed to come ready.\n", dev->unit); +#else /* MACH_KERNEL */ + printf("\nzp%d: eeprom failed to come ready.\n", is->id_unit); +#endif /* MACH_KERNEL */ + return (1); + } + if (j & EEPROM_TST_MODE) { +#ifdef MACH_KERNEL + printf("\nzp%d: 3c589 in test mode. Erase pencil mark!\n", dev->unit); +#else /* MACH_KERNEL */ + printf("\nzp%d: 3c589 in test mode. Erase pencil mark!\n", is->id_unit); +#endif /* MACH_KERNEL */ + + return (1); + } + return (0); +} + +#ifndef MACH_KERNEL +void +zpmbuffill(sp) + caddr_t sp; +{ + struct zp_softc *sc = (struct zp_softc *)sp; + int s, i; + +#ifdef ZP_DEBUG + printf("### zpmbuffill ####\n"); +#endif /* ZP_DEBUG */ + + s = splimp(); + i = sc->last_mb; + do { + if(sc->mb[i] == NULL) + MGET(sc->mb[i], M_DONTWAIT, MT_DATA); + if(sc->mb[i] == NULL) + break; + i = (i + 1) % MAX_MBS; + } while (i != sc->next_mb); + sc->last_mb = i; + splx(s); +} + +static void +zpmbufempty(sc) + struct zp_softc *sc; +{ + int s, i; + +#ifdef ZP_DEBUG + printf("### zpmbufempty ####\n"); +#endif /* ZP_DEBUG */ + + s = splimp(); + for (i = 0; imb[i]) { + m_freem(sc->mb[i]); + sc->mb[i] = NULL; + } + } + sc->last_mb = sc->next_mb = 0; + untimeout(zpmbuffill, sc); + splx(s); +} + +#endif /* MACH_KERNEL */ + +#endif /* NZP > 0 */ diff --git a/sys/i386/isa/if_zpreg.h b/sys/i386/isa/if_zpreg.h new file mode 100644 index 0000000..f8f184a --- /dev/null +++ b/sys/i386/isa/if_zpreg.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) + * 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_epreg.h,v 1.2 1994/01/10 19:13:50 ats Exp $ + */ +/************************************************************************** + * * + * These define the EEPROM data structure. They are used in the probe + * function to verify the existance of the adapter after having sent + * the ID_Sequence. + * + * There are others but only the ones we use are defined here. + * + **************************************************************************/ + +#define EEPROM_NODE_ADDR_0 0x0 /* Word */ +#define EEPROM_NODE_ADDR_1 0x1 /* Word */ +#define EEPROM_NODE_ADDR_2 0x2 /* Word */ +#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ +#define EEPROM_MFG_ID 0x7 /* 0x6d50 */ +#define EEPROM_ADDR_CFG 0x8 /* Base addr */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ + +/************************************************************************** + * * + * These are the registers for the 3Com 3c509 and their bit patterns when * + * applicable. They have been taken out the the "EtherLink III Parallel * + * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * + * from 3com. * + * * + **************************************************************************/ + +#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */ +#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */ +#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */ +/* + * Window 0 registers. Setup. + */ + /* Write */ +#define EP_W0_EEPROM_DATA 0x0c +#define EP_W0_EEPROM_COMMAND 0x0a +#define EP_W0_RESOURCE_CFG 0x08 +#define EP_W0_ADDRESS_CFG 0x06 +#define EP_W0_CONFIG_CTRL 0x04 + /* Read */ +#define EP_W0_PRODUCT_ID 0x02 +#define EP_W0_MFG_ID 0x00 + +/* + * Window 1 registers. Operating Set. + */ + /* Write */ +#define EP_W1_TX_PIO_WR_2 0x02 +#define EP_W1_TX_PIO_WR_1 0x00 + /* Read */ +#define EP_W1_FREE_TX 0x0c +#define EP_W1_TX_STATUS 0x0b /* byte */ +#define EP_W1_TIMER 0x0a /* byte */ +#define EP_W1_RX_STATUS 0x08 +#define EP_W1_RX_PIO_RD_2 0x02 +#define EP_W1_RX_PIO_RD_1 0x00 + +/* + * Window 2 registers. Station Address Setup/Read + */ + /* Read/Write */ +#define EP_W2_ADDR_5 0x05 +#define EP_W2_ADDR_4 0x04 +#define EP_W2_ADDR_3 0x03 +#define EP_W2_ADDR_2 0x02 +#define EP_W2_ADDR_1 0x01 +#define EP_W2_ADDR_0 0x00 + +/* + * Window 3 registers. FIFO Management. + */ + /* Read */ +#define EP_W3_FREE_TX 0x0c +#define EP_W3_FREE_RX 0x0a + +/* + * Window 4 registers. Diagnostics. + */ + /* Read/Write */ +#define EP_W4_MEDIA_TYPE 0x0a +#define EP_W4_CTRLR_STATUS 0x08 +#define EP_W4_NET_DIAG 0x06 +#define EP_W4_FIFO_DIAG 0x04 +#define EP_W4_HOST_DIAG 0x02 +#define EP_W4_TX_DIAG 0x00 + +/* + * Window 5 Registers. Results and Internal status. + */ + /* Read */ +#define EP_W5_READ_0_MASK 0x0c +#define EP_W5_INTR_MASK 0x0a +#define EP_W5_RX_FILTER 0x08 +#define EP_W5_RX_EARLY_THRESH 0x06 +#define EP_W5_TX_AVAIL_THRESH 0x02 +#define EP_W5_TX_START_THRESH 0x00 + +/* + * Window 6 registers. Statistics. + */ + /* Read/Write */ +#define TX_TOTAL_OK 0x0c +#define RX_TOTAL_OK 0x0a +#define TX_DEFERRALS 0x08 +#define RX_FRAMES_OK 0x07 +#define TX_FRAMES_OK 0x06 +#define RX_OVERRUNS 0x05 +#define TX_COLLISIONS 0x04 +#define TX_AFTER_1_COLLISION 0x03 +#define TX_AFTER_X_COLLISIONS 0x02 +#define TX_NO_SQE 0x01 +#define TX_CD_LOST 0x00 + +/**************************************** + * + * Register definitions. + * + ****************************************/ + +/* + * Command register. All windows. + * + * 16 bit register. + * 15-11: 5-bit code for command to be executed. + * 10-0: 11-bit arg if any. For commands with no args; + * this can be set to anything. + */ +#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */ +#define WINDOW_SELECT (u_short) (0x1<<11) +#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine + whether this is needed. If so; + wait 800 uSec before using trans- + ceiver. */ +#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */ +#define RX_ENABLE (u_short) (0x4<<11) +#define RX_RESET (u_short) (0x5<<11) +#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) +#define TX_ENABLE (u_short) (0x9<<11) +#define TX_DISABLE (u_short) (0xa<<11) +#define TX_RESET (u_short) (0xb<<11) +#define REQ_INTR (u_short) (0xc<<11) + /* + * The following C_* acknowledge the various interrupts. + * Some of them don't do anything. See the manual. + */ +#define ACK_INTR (u_short) (0x6800) +# define C_INTR_LATCH (u_short) (ACK_INTR|0x1) +# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) +# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) +# define C_TX_AVAIL (u_short) (ACK_INTR|0x8) +# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) +# define C_RX_EARLY (u_short) (ACK_INTR|0x20) +# define C_INT_RQD (u_short) (ACK_INTR|0x40) +# define C_UPD_STATS (u_short) (ACK_INTR|0x80) +#define SET_INTR_MASK (u_short) (0xe<<11) +#define SET_RD_0_MASK (u_short) (0xf<<11) +#define SET_RX_FILTER (u_short) (0x10<<11) +# define FIL_INDIVIDUAL (u_short) (0x1) +# define FIL_GROUP (u_short) (0x2) +# define FIL_BRDCST (u_short) (0x4) +# define FIL_ALL (u_short) (0x8) +#define SET_RX_EARLY_THRESH (u_short) (0x11<<11) +#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) +#define SET_TX_START_THRESH (u_short) (0x13<<11) +#define STATS_ENABLE (u_short) (0x15<<11) +#define STATS_DISABLE (u_short) (0x16<<11) +#define STOP_TRANSCEIVER (u_short) (0x17<<11) + +/* + * Status register. All windows. + * + * 15-13: Window number(0-7). + * 12: Command_in_progress. + * 11: reserved. + * 10: reserved. + * 9: reserved. + * 8: reserved. + * 7: Update Statistics. + * 6: Interrupt Requested. + * 5: RX Early. + * 4: RX Complete. + * 3: TX Available. + * 2: TX Complete. + * 1: Adapter Failure. + * 0: Interrupt Latch. + */ +#define S_INTR_LATCH (u_short) (0x1) +#define S_CARD_FAILURE (u_short) (0x2) +#define S_TX_COMPLETE (u_short) (0x4) +#define S_TX_AVAIL (u_short) (0x8) +#define S_RX_COMPLETE (u_short) (0x10) +#define S_RX_EARLY (u_short) (0x20) +#define S_INT_RQD (u_short) (0x40) +#define S_UPD_STATS (u_short) (0x80) +#define S_COMMAND_IN_PROGRESS (u_short) (0x1000) + +/* + * FIFO Registers. RX Status. + * + * 15: Incomplete or FIFO empty. + * 14: 1: Error in RX Packet 0: Incomplete or no error. + * 13-11: Type of error. + * 1000 = Overrun. + * 1011 = Run Packet Error. + * 1100 = Alignment Error. + * 1101 = CRC Error. + * 1001 = Oversize Packet Error (>1514 bytes) + * 0010 = Dribble Bits. + * (all other error codes, no errors.) + * + * 10-0: RX Bytes (0-1514) + */ +#define ERR_INCOMPLETE (u_short) (0x8000) +#define ERR_RX (u_short) (0x4000) +#define ERR_RX_PACKET (u_short) (0x2000) +#define ERR_OVERRUN (u_short) (0x1000) +#define ERR_RUNT (u_short) (0x1300) +#define ERR_ALIGNMENT (u_short) (0x1400) +#define ERR_CRC (u_short) (0x1500) +#define ERR_OVERSIZE (u_short) (0x1100) +#define ERR_DRIBBLE (u_short) (0x200) + +/* + * TX Status + * + * Reports the transmit status of a completed transmission. Writing this + * register pops the transmit completion stack. + * + * Window 1/Port 0x0b. + * + * 7: Complete + * 6: Interrupt on successful transmission requested. + * 5: Jabber Error (TP Only, TX Reset required. ) + * 4: Underrun (TX Reset required. ) + * 3: Maximum Collisions. + * 2: TX Status Overflow. + * 1-0: Undefined. + * + */ +#define TXS_COMPLETE 0x80 +#define TXS_INTR_REQ 0x40 +#define TXS_JABBER 0x20 +#define TXS_UNDERRUN 0x10 +#define TXS_MAX_COLLISION 0x8 +#define TXS_STATUS_OVERFLOW 0x4 + +/* + * Misc defines for various things. + */ +#define TAG_ADAPTER_0 0xd0 +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff +#define ENABLE_DRQ_IRQ 0x0001 +#define MFG_ID 0x6d50 +#define PROD_ID 0x9150 +#define BASE sc->ep_io_addr +#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x) +#define AUI 0x1 +#define BNC 0x2 +#define UTP 0x4 +#define IS_AUI (1<<13) +#define IS_BNC (1<<12) +#define IS_UTP (1<<9) +#define EEPROM_BUSY (1<<15) +#define EEPROM_TST_MODE (1<<14) +#define READ_EEPROM (1<<7) +#define ETHER_ADDR_LEN 6 +#define ETHER_MAX 1536 +#define ENABLE_UTP 0xc0 +#define DISABLE_UTP 0x0 +#define RX_BYTES_MASK (u_short) (0x07ff) diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c index 2246826..3b4aa1f 100644 --- a/sys/i386/isa/isa.c +++ b/sys/i386/isa/isa.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: isa.c,v 1.35 1994/11/01 17:26:29 ache Exp $ + * $Id: isa.c,v 1.36 1994/11/03 04:15:03 jkh Exp $ */ /* @@ -417,9 +417,11 @@ config_isadev_c(isdp, mp, reconfig) } if (reconfig) { last_alive = isdp->id_alive; + isdp->id_reconfig = 1; } else { last_alive = 0; + isdp->id_reconfig = 0; } id_alive = (*dp->probe)(isdp); if (id_alive) { diff --git a/sys/i386/isa/pcic.c b/sys/i386/isa/pcic.c new file mode 100644 index 0000000..4c5977a --- /dev/null +++ b/sys/i386/isa/pcic.c @@ -0,0 +1,249 @@ +/*- + * TODO: + * [1] integrate into current if_ed.c + * [2] parse tuples to find out where to map the shared memory buffer, + * and what to write into the configuration register + * [3] move pcic-specific code into a separate module. + * + * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet, + * if_ze.c + * + * Based on the Device driver for National Semiconductor DS8390 ethernet + * adapters by David Greenman. Modifications for PCMCIA by Keith Moore. + * Adapted for FreeBSD 1.1.5 by Jordan Hubbard. + * + * Currently supports only the IBM Credit Card Adapter for Ethernet, but + * could probably work with other PCMCIA cards also, if it were modified + * to get the locations of the PCMCIA configuration option register (COR) + * by parsing the configuration tuples, rather than by hard-coding in + * the value expected by IBM's card. + * + * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver: + * + * [1] _Local Area Network Credit Card Adapters Technical Reference_, + * IBM Corp., SC30-3585-00, part # 33G9243. + * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan. + * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel + * Order Number 290423-002 + * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network + * Interface Controller for Twisted Pair data sheet. + * + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + */ +#include +#if defined(__FreeBSD__) +#include +#include +#endif +#include +#include +#include +#include + +void +pcic_print_regs (int slot) +{ + int i, j; + + for (i = 0; i < 0x40; i += 16) { + for (j = 0; j < 16; ++j) + printf ("%02x ", pcic_getb (slot, i + j)); + printf ("\n"); + } +} + +/* + * map a portion of the card's memory space into system memory + * space. + * + * slot = # of the slot the card is plugged into + * window = which pcic memory map registers to use (0..4) + * sys_addr = base system PHYSICAL memory address where we want it. must + * be on an appropriate boundary (lower 12 bits are zero). + * card_addr = the base address of the card's memory to correspond + * to sys_addr + * length = length of the segment to map (may be rounded up as necessary) + * type = which card memory space to map (attribute or shared) + * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping. + */ + +void +pcic_map_memory (int slot, int window, unsigned long sys_addr, + unsigned long card_addr, unsigned long length, + enum memtype type, int width) +{ + unsigned short offset; + unsigned short mem_start_addr; + unsigned short mem_stop_addr; + + sys_addr >>= 12; + card_addr >>= 12; + length >>= 12; + /* + * compute an offset for the chip such that + * (sys_addr + offset) = card_addr + * but the arithmetic is done modulo 2^14 + */ + offset = (card_addr - sys_addr) & 0x3FFF; + /* + * now OR in the bit for "attribute memory" if necessary + */ + if (type == ATTRIBUTE) { + offset |= (PCIC_REG << 8); + /* REG == "region active" pin on card */ + } + /* + * okay, set up the chip memory mapping registers, and turn + * on the enable bit for this window. + * if we are doing 16-bit wide accesses (width == 2), + * turn on the appropriate bit. + * + * XXX for now, we set all of the wait state bits to zero. + * Not really sure how they should be set. + */ + mem_start_addr = sys_addr & 0xFFF; + if (width == 2) + mem_start_addr |= (PCIC_DATA16 << 8); + mem_stop_addr = (sys_addr + length) & 0xFFF; + + pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr); + pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr); + pcic_putw (slot, MEM_OFFSET(window), offset); + /* + * Assert the bit (PCIC_MEMCS16) that says to decode all of + * the address lines. + */ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) | + MEM_ENABLE_BIT(window) | PCIC_MEMCS16); +} + +void +pcic_unmap_memory (int slot, int window) +{ + /* + * seems like we need to turn off the enable bit first, after which + * we can clear the registers out just to be sure. + */ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window)); + pcic_putw (slot, MEM_START_ADDR(window), 0); + pcic_putw (slot, MEM_STOP_ADDR(window), 0); + pcic_putw (slot, MEM_OFFSET(window), 0); +} + +/* + * map a range of addresses into system i/o space + * (no translation of i/o addresses is possible) + * + * 'width' is: + * + 0 to tell the PCIC to generate the ISA IOCS16* signal from + * the PCMCIA IOIS16* signal. + * + 1 to select 8-bit width + * + 2 to select 16-bit width + */ + +void +pcic_map_io (int slot, int window, unsigned short base, unsigned short length, + unsigned short width) +{ + unsigned char x; + + pcic_putw (slot, IO_START_ADDR(window), base); + pcic_putw (slot, IO_STOP_ADDR(window), base+length-1); + /* + * select the bits that determine whether + * an i/o operation is 8 or 16 bits wide + */ + x = pcic_getb (slot, PCIC_IOCTL); + switch (width) { + case 0: /* PCMCIA card decides */ + if (window) + x = (x & 0xf0) | PCIC_IO1_CS16; + else + x = (x & 0x0f) | PCIC_IO0_CS16; + break; + case 1: /* 8 bits wide */ + break; + case 2: /* 16 bits wide */ + if (window) + x = (x & 0xf0) | PCIC_IO1_16BIT; + else + x = (x & 0x0f) | PCIC_IO0_16BIT; + break; + } + pcic_putb (slot, PCIC_IOCTL, x); + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window)); +} + +#ifdef TEST +void +pcic_unmap_io (int slot, int window) +{ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window)); + pcic_putw (slot, IO_START_ADDR(window), 0); + pcic_putw (slot, IO_STOP_ADDR(window), 0); +} +#endif /* TEST */ + +/* + * tell the PCIC which irq we want to use. only the following are legal: + * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 + * + * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device. + */ + +void +pcic_map_irq (int slot, int irq) +{ + if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) { + printf ("zp: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq); + return; + } + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F)); +} + +void +pcic_power_on (int slot) +{ + pcic_putb (slot, PCIC_STATUS, + pcic_getb (slot, PCIC_STATUS) | PCIC_POW); + DELAY (100000); + pcic_putb (slot, PCIC_POWER, + pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE); + DELAY (100000); + pcic_putb (slot, PCIC_POWER, + pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA); +} + +void +pcic_power_off (int slot) +{ + /* + * If you success to power-off the PCMCIA slots and + * resume safely, please tell me your way to implement it. + * + * HOSOKAWA, Tatsumi + */ +} + +void +pcic_reset (int slot) +{ + /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */ + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET); + DELAY (100000); + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET); +} + diff --git a/sys/i386/isa/pcic.h b/sys/i386/isa/pcic.h new file mode 100644 index 0000000..852f332 --- /dev/null +++ b/sys/i386/isa/pcic.h @@ -0,0 +1,181 @@ +/*- + * TODO: + * [1] integrate into current if_ed.c + * [2] parse tuples to find out where to map the shared memory buffer, + * and what to write into the configuration register + * [3] move pcic-specific code into a separate module. + * + * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet, + * if_ze.c + * + * Based on the Device driver for National Semiconductor DS8390 ethernet + * adapters by David Greenman. Modifications for PCMCIA by Keith Moore. + * Adapted for FreeBSD 1.1.5 by Jordan Hubbard. + * + * Currently supports only the IBM Credit Card Adapter for Ethernet, but + * could probably work with other PCMCIA cards also, if it were modified + * to get the locations of the PCMCIA configuration option register (COR) + * by parsing the configuration tuples, rather than by hard-coding in + * the value expected by IBM's card. + * + * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver: + * + * [1] _Local Area Network Credit Card Adapters Technical Reference_, + * IBM Corp., SC30-3585-00, part # 33G9243. + * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan. + * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel + * Order Number 290423-002 + * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network + * Interface Controller for Twisted Pair data sheet. + * + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + */ + +#ifndef __PCIC_H__ +#define __PCIC_H__ + +/***************************************************************************** + * pcmcia controller chip (PCIC) support * + * (eventually, move this to a separate file) * + *****************************************************************************/ +#include "ic/i82365.h" + +/* + * Each PCIC chip (82365SL or clone) can handle two card slots, and there + * can be up to four PCICs in a system. (On some machines, not all of the + * address lines are decoded, so a card may appear to be in more than one + * slot.) + */ +#define MAXSLOT 8 + +/* + * To access a register on the PCIC for a particular slot, you + * first write the correct OFFSET value for that slot in the + * INDEX register for the PCIC controller. You then read or write + * the value from or to the DATA register for that controller. + * + * The first pair of chips shares I/O addresss for DATA and INDEX, + * as does the second pair. (To the programmer, it looks like each + * pair is a single chip.) The i/o port addresses are hard-wired + * into the PCIC; so the following addresses should be valid for + * any machine that uses this chip. + */ + +#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */ +#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */ +#define PCIC_INDEX_1 0x3E2 /* index reg, chips 1 and 2 */ +#define PCIC_DATA_1 0x3E3 /* data register, chips 1 and 2 */ + +/* + * Given a slot number, calculate the INDEX and DATA registers + * to talk to that slot. OFFSET is added to the register number + * to address the registers for a particular slot. + */ +#define INDEX(slot) ((slot) < 4 ? PCIC_INDEX_0 : PCIC_INDEX_1) +#define DATA(slot) ((slot) < 4 ? PCIC_DATA_0 : PCIC_DATA_1) +#define OFFSET(slot) ((slot) % 4 * 0x40) + +/* + * There are 5 sets (windows) of memory mapping registers on the PCIC chip + * for each slot, numbered 0..4. + * + * They start at 10/50 hex within the chip's register space (not system + * I/O space), and are eight addresses apart. These are actually pairs of + * 8-bit-wide registers (low byte first, then high byte) since the + * address fields are actually 12 bits long. The upper bits are used + * for other things like 8/16-bit select and wait states. + * + * Memory mapping registers include start/stop addresses to define the + * region to be mapped (in terms of system memory addresses), and + * an offset register to allow for translation from system space + * to card space. The lower 12 bits aren't included in these, so memory is + * mapped in 4K chunks. + */ +#define MEM_START_ADDR(window) (((window) * 0x08) + 0x10) +#define MEM_STOP_ADDR(window) (((window) * 0x08) + 0x12) +#define MEM_OFFSET(window) (((window) * 0x08) + 0x14) +/* + * this bit gets set in the address window enable register (PCIC_ADDRWINE) + * to enable a particular address window. + */ +#define MEM_ENABLE_BIT(window) ((1) << (window)) + +/* + * There are two i/o port addressing windows. I/O ports cannot be + * relocated within system i/o space (unless the card doesn't decode + * all of the address bits); unlike card memory, there is no address + * translation offset. + */ +#define IO_START_ADDR(window) ((window) ? PCIC_IO1_STL : PCIC_IO0_STL) +#define IO_STOP_ADDR(window) ((window) ? PCIC_IO1_SPL : PCIC_IO0_SPL) +#define IO_ENABLE_BIT(window) ((window) ? PCIC_IO1_EN : PCIC_IO0_EN) +#define IO_CS16_BIT(window) ((window) ? PCIC_IO1_CS16 : PCIC_IO0_CS16) + +/* + * types of mapped memory + */ +enum memtype { COMMON, ATTRIBUTE }; + +/* + * read a byte from a pcic register for a particular slot + */ +static inline unsigned char +pcic_getb (int slot, int reg) +{ + outb (INDEX(slot), OFFSET (slot) + reg); + return inb (DATA (slot)); +} + +/* + * write a byte to a pcic register for a particular slot + */ +static inline void +pcic_putb (int slot, int reg, unsigned char val) +{ + outb (INDEX(slot), OFFSET (slot) + reg); + outb (DATA (slot), val); +} + +/* + * read a word from a pcic register for a particular slot + */ +static inline unsigned short +pcic_getw (int slot, int reg) +{ + return pcic_getb (slot, reg) | (pcic_getb (slot, reg+1) << 8); +} + +/* + * write a word to a pcic register at a particular slot + */ +static inline void +pcic_putw (int slot, int reg, unsigned short val) +{ + pcic_putb (slot, reg, val & 0xff); + pcic_putb (slot, reg + 1, (val >> 8) & 0xff); +} + + +void pcic_print_regs (int slot); +void pcic_map_memory (int slot, int window, unsigned long sys_addr, + unsigned long card_addr, unsigned long length, + enum memtype type, int width); +void pcic_unmap_memory (int slot, int window); +void pcic_map_io (int slot, int window, unsigned short base, + unsigned short length, unsigned short width); +#ifdef TEST +void pcic_unmap_io (int slot, int window); +#endif /* TEST */ +void pcic_map_irq (int slot, int irq); +void pcic_power_on (int slot); +void pcic_power_off (int slot); +void pcic_reset (int slot); + + +#endif /* __PCIC_H__ */ diff --git a/sys/i386/isa/syscons.c b/sys/i386/isa/syscons.c index 24a952c..102cc0e 100644 --- a/sys/i386/isa/syscons.c +++ b/sys/i386/isa/syscons.c @@ -35,7 +35,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: syscons.c,v 1.101 1995/02/07 11:53:27 sos Exp $ + * $Id: syscons.c,v 1.102 1995/02/14 14:37:53 sos Exp $ */ #include "sc.h" @@ -72,6 +72,10 @@ #if !defined(MAXCONS) #define MAXCONS 16 #endif +#include "apm.h" +#if NAPM > 0 +#include "machine/apm_bios.h" +#endif /* this may break on older VGA's but is usefull on real 32 bit systems */ #define bcopyw bcopy @@ -188,6 +192,9 @@ typedef struct scr_stat { u_short *history_pos; /* position shown on screen */ u_short *history_save; /* save area index */ int history_size; /* size of history buffer */ +#if NAPM > 0 + struct apmhook r_hook; /* reconfiguration support */ +#endif /* NAPM > 0 */ } scr_stat; typedef struct default_attr { @@ -414,6 +421,20 @@ sc_registerdev(struct isa_device *id) dev_attach(&kdc_sc[id->id_unit]); } +#if NAPM > 0 +/* ARGSUSED */ +static int +pcresume(void *dummy) +{ + shfts = 0; + ctls = 0; + alts = 0; + agrs = 0; + metas = 0; + return 0; +} +#endif /* NAPM > 0 */ + int scattach(struct isa_device *dev) @@ -464,6 +485,13 @@ scattach(struct isa_device *dev) update_leds(scp->status); sc_registerdev(dev); +#if NAPM > 0 + scp->r_hook.ah_fun = pcresume; + scp->r_hook.ah_arg = NULL; + scp->r_hook.ah_name = "pccons keyboard"; + scp->r_hook.ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); +#endif /* NAPM > 0*/ return 0; } @@ -2827,6 +2855,9 @@ next_code: shutdown_nice(); break; case SUSP: +#if NAPM > 0 + apm_suspend(); +#endif break; case DBG: -- cgit v1.1