if_sn.c revision 55161
154773Simp/* 254773Simp * Copyright (c) 1996 Gardner Buchanan <gbuchanan@shl.com> 354773Simp * All rights reserved. 454773Simp * 554773Simp * Redistribution and use in source and binary forms, with or without 654773Simp * modification, are permitted provided that the following conditions 754773Simp * are met: 854773Simp * 1. Redistributions of source code must retain the above copyright 954773Simp * notice, this list of conditions and the following disclaimer. 1054773Simp * 2. Redistributions in binary form must reproduce the above copyright 1154773Simp * notice, this list of conditions and the following disclaimer in the 1254773Simp * documentation and/or other materials provided with the distribution. 1354773Simp * 3. All advertising materials mentioning features or use of this software 1454773Simp * must display the following acknowledgement: 1554773Simp * This product includes software developed by Gardner Buchanan. 1654773Simp * 4. The name of Gardner Buchanan may not be used to endorse or promote 1754773Simp * products derived from this software without specific prior written 1854773Simp * permission. 1954773Simp * 2054773Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2154773Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2254773Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2354773Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2454773Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2554773Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2654773Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2754773Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2854773Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2954773Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3054773Simp * 3154773Simp * $FreeBSD: head/sys/dev/sn/if_sn.c 55161 1999-12-28 01:07:16Z imp $ 3254773Simp */ 3354773Simp 3454773Simp/* 3554773Simp * This is a driver for SMC's 9000 series of Ethernet adapters. 3654773Simp * 3754773Simp * This FreeBSD driver is derived from the smc9194 Linux driver by 3854773Simp * Erik Stahlman and is Copyright (C) 1996 by Erik Stahlman. 3954773Simp * This driver also shamelessly borrows from the FreeBSD ep driver 4054773Simp * which is Copyright (C) 1994 Herb Peyerl <hpeyerl@novatel.ca> 4154773Simp * All rights reserved. 4254773Simp * 4354773Simp * It is set up for my SMC91C92 equipped Ampro LittleBoard embedded 4454773Simp * PC. It is adapted from Erik Stahlman's Linux driver which worked 4554773Simp * with his EFA Info*Express SVC VLB adaptor. According to SMC's databook, 4654773Simp * it will work for the entire SMC 9xxx series. (Ha Ha) 4754773Simp * 4854773Simp * "Features" of the SMC chip: 4954773Simp * 4608 byte packet memory. (for the 91C92. Others have more) 5054773Simp * EEPROM for configuration 5154773Simp * AUI/TP selection 5254773Simp * 5354773Simp * Authors: 5454773Simp * Erik Stahlman erik@vt.edu 5554773Simp * Herb Peyerl hpeyerl@novatel.ca 5654773Simp * Andres Vega Garcia avega@sophia.inria.fr 5754773Simp * Serge Babkin babkin@hq.icb.chel.su 5854773Simp * Gardner Buchanan gbuchanan@shl.com 5954773Simp * 6054773Simp * Sources: 6154773Simp * o SMC databook 6254773Simp * o "smc9194.c:v0.10(FIXED) 02/15/96 by Erik Stahlman (erik@vt.edu)" 6354773Simp * o "if_ep.c,v 1.19 1995/01/24 20:53:45 davidg Exp" 6454773Simp * 6554773Simp * Known Bugs: 6654773Simp * o The hardware multicast filter isn't used yet. 6754773Simp * o Setting of the hardware address isn't supported. 6854773Simp * o Hardware padding isn't used. 6954773Simp */ 7054773Simp 7154773Simp/* 7254773Simp * Modifications for Megahertz X-Jack Ethernet Card (XJ-10BT) 7354773Simp * 7454773Simp * Copyright (c) 1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org> 7554773Simp * BSD-nomads, Tokyo, Japan. 7654773Simp */ 7754773Simp/* 7854773Simp * Multicast support by Kei TANAKA <kei@pal.xerox.com> 7954773Simp * Special thanks to itojun@itojun.org 8054773Simp */ 8154773Simp 8254773Simp#undef SN_DEBUG /* (by hosokawa) */ 8354773Simp 8454773Simp#include <sys/param.h> 8554773Simp#include <sys/systm.h> 8654773Simp#include <sys/kernel.h> 8754773Simp#include <sys/errno.h> 8854773Simp#include <sys/sockio.h> 8954773Simp#include <sys/malloc.h> 9054773Simp#include <sys/mbuf.h> 9154773Simp#include <sys/socket.h> 9254773Simp#include <sys/syslog.h> 9354773Simp 9454994Simp#include <sys/module.h> 9554994Simp#include <sys/bus.h> 9654994Simp 9754994Simp#include <machine/bus.h> 9854994Simp#include <machine/resource.h> 9954994Simp#include <sys/rman.h> 10054994Simp 10154773Simp#include <net/ethernet.h> 10254773Simp#include <net/if.h> 10354773Simp#include <net/if_arp.h> 10454773Simp#include <net/if_dl.h> 10554773Simp#include <net/if_types.h> 10654773Simp#include <net/if_mib.h> 10754773Simp 10854773Simp#ifdef INET 10954773Simp#include <netinet/in.h> 11054773Simp#include <netinet/in_systm.h> 11154773Simp#include <netinet/in_var.h> 11254773Simp#include <netinet/ip.h> 11354773Simp#endif 11454773Simp 11554773Simp#ifdef NS 11654773Simp#include <netns/ns.h> 11754773Simp#include <netns/ns_if.h> 11854773Simp#endif 11954773Simp 12054773Simp#include <net/bpf.h> 12154773Simp#include <net/bpfdesc.h> 12254773Simp 12354773Simp#include <machine/clock.h> 12454773Simp 12554773Simp#include <dev/sn/if_snreg.h> 12654994Simp#include <dev/sn/if_snvar.h> 12754773Simp 12854994Simp/* Exported variables */ 12954994Simpdevclass_t sn_devclass; 13054773Simp 13154994Simpstatic int snioctl(struct ifnet * ifp, u_long, caddr_t); 13254773Simp 13354994Simpstatic void snresume(struct ifnet *); 13454773Simp 13554994Simpvoid sninit(void *); 13654994Simpvoid snread(struct ifnet *); 13754994Simpvoid snreset(struct sn_softc *); 13854994Simpvoid snstart(struct ifnet *); 13954994Simpvoid snstop(struct sn_softc *); 14054994Simpvoid snwatchdog(struct ifnet *); 14154773Simp 14254773Simpstatic void sn_setmcast(struct sn_softc *); 14354773Simpstatic int sn_getmcf(struct arpcom *ac, u_char *mcf); 14454773Simpstatic u_int smc_crc(u_char *); 14554773Simp 14654773Simp/* I (GB) have been unlucky getting the hardware padding 14754773Simp * to work properly. 14854773Simp */ 14954773Simp#define SW_PAD 15054773Simp 15154994Simp/* XXX KLUDGE XXX */ 15254994Simpu_char sn_pccard_macaddr[6] = { 0x00, 0x00, 0x86, 0x10, 0x2b, 0xc0 }; 15354773Simp 15454994Simpstatic const char *chip_ids[15] = { 15554994Simp NULL, NULL, NULL, 15654994Simp /* 3 */ "SMC91C90/91C92", 15754994Simp /* 4 */ "SMC91C94", 15854994Simp /* 5 */ "SMC91C95", 15954994Simp NULL, 16054994Simp /* 7 */ "SMC91C100", 16154994Simp NULL, NULL, NULL, NULL, 16254994Simp NULL, NULL, NULL 16354773Simp}; 16454773Simp 16554773Simpint 16654994Simpsn_attach(device_t dev) 16754773Simp{ 16854994Simp struct sn_softc *sc = device_get_softc(dev); 16954773Simp struct ifnet *ifp = &sc->arpcom.ac_if; 17054773Simp u_short i; 17154773Simp u_char *p; 17254773Simp struct ifaddr *ifa; 17354773Simp struct sockaddr_dl *sdl; 17454773Simp int rev; 17554773Simp u_short address; 17654773Simp 17754994Simp sn_activate(dev); 17854773Simp 17954994Simp snstop(sc); 18054773Simp 18154994Simp sc->dev = dev; 18254773Simp sc->pages_wanted = -1; 18354773Simp 18454994Simp device_printf(dev, " "); 18554773Simp 18654773Simp SMC_SELECT_BANK(3); 18754773Simp rev = inw(BASE + REVISION_REG_W); 18854773Simp if (chip_ids[(rev >> 4) & 0xF]) 18954773Simp printf("%s ", chip_ids[(rev >> 4) & 0xF]); 19054773Simp 19154773Simp SMC_SELECT_BANK(1); 19254773Simp i = inw(BASE + CONFIG_REG_W); 19354773Simp printf(i & CR_AUI_SELECT ? "AUI" : "UTP"); 19454773Simp 19554994Simp if (1) { 19654994Simp /* XXX The pccard probe routine for megahearts needs to */ 19754994Simp /* XXX snag this from your info 2 */ 19854994Simp int j; 19954994Simp 20054773Simp for (j = 0; j < 3; j++) { 20154773Simp u_short w; 20254773Simp 20354994Simp w = (u_short)sn_pccard_macaddr[j * 2] | 20454994Simp (((u_short)sn_pccard_macaddr[j * 2 + 1]) << 8); 20554773Simp outw(BASE + IAR_ADDR0_REG_W + j * 2, w); 20654773Simp } 20754773Simp } 20854773Simp 20954773Simp /* 21054773Simp * Read the station address from the chip. The MAC address is bank 1, 21154773Simp * regs 4 - 9 21254773Simp */ 21354773Simp SMC_SELECT_BANK(1); 21454773Simp p = (u_char *) & sc->arpcom.ac_enaddr; 21554773Simp for (i = 0; i < 6; i += 2) { 21654773Simp address = inw(BASE + IAR_ADDR0_REG_W + i); 21754773Simp p[i + 1] = address >> 8; 21854773Simp p[i] = address & 0xFF; 21954773Simp } 22054773Simp printf(" MAC address %6D\n", sc->arpcom.ac_enaddr, ":"); 22154773Simp ifp->if_softc = sc; 22254994Simp ifp->if_unit = device_get_unit(dev); 22354773Simp ifp->if_name = "sn"; 22454773Simp ifp->if_mtu = ETHERMTU; 22554773Simp ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 22654773Simp ifp->if_output = ether_output; 22754773Simp ifp->if_start = snstart; 22854773Simp ifp->if_ioctl = snioctl; 22954773Simp ifp->if_watchdog = snwatchdog; 23054773Simp ifp->if_init = sninit; 23155161Simp ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 23254773Simp ifp->if_timer = 0; 23354773Simp 23454773Simp if_attach(ifp); 23554773Simp ether_ifattach(ifp); 23654994Simp 23754773Simp /* 23854773Simp * Fill the hardware address into ifa_addr if we find an AF_LINK 23954773Simp * entry. We need to do this so bpf's can get the hardware addr of 24054773Simp * this card. netstat likes this too! 24154773Simp */ 24254773Simp ifa = TAILQ_FIRST(&ifp->if_addrhead); 24354773Simp while ((ifa != 0) && (ifa->ifa_addr != 0) && 24454773Simp (ifa->ifa_addr->sa_family != AF_LINK)) 24554773Simp ifa = TAILQ_NEXT(ifa, ifa_link); 24654773Simp 24754773Simp if ((ifa != 0) && (ifa->ifa_addr != 0)) { 24854773Simp sdl = (struct sockaddr_dl *) ifa->ifa_addr; 24954773Simp sdl->sdl_type = IFT_ETHER; 25054773Simp sdl->sdl_alen = ETHER_ADDR_LEN; 25154773Simp sdl->sdl_slen = 0; 25254773Simp bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); 25354773Simp } 25454773Simp 25554773Simp bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 25654773Simp 25754994Simp return 0; 25854773Simp} 25954773Simp 26054773Simp 26154773Simp/* 26254773Simp * Reset and initialize the chip 26354773Simp */ 26454773Simpvoid 26554773Simpsninit(void *xsc) 26654773Simp{ 26754773Simp register struct sn_softc *sc = xsc; 26854773Simp register struct ifnet *ifp = &sc->arpcom.ac_if; 26954773Simp int s; 27054773Simp int flags; 27154773Simp int mask; 27254773Simp 27354773Simp s = splimp(); 27454773Simp 27554773Simp /* 27654773Simp * This resets the registers mostly to defaults, but doesn't affect 27754773Simp * EEPROM. After the reset cycle, we pause briefly for the chip to 27854773Simp * be happy. 27954773Simp */ 28054773Simp SMC_SELECT_BANK(0); 28154773Simp outw(BASE + RECV_CONTROL_REG_W, RCR_SOFTRESET); 28254773Simp SMC_DELAY(); 28354773Simp outw(BASE + RECV_CONTROL_REG_W, 0x0000); 28454773Simp SMC_DELAY(); 28554773Simp SMC_DELAY(); 28654773Simp 28754773Simp outw(BASE + TXMIT_CONTROL_REG_W, 0x0000); 28854773Simp 28954773Simp /* 29054773Simp * Set the control register to automatically release succesfully 29154773Simp * transmitted packets (making the best use out of our limited 29254773Simp * memory) and to enable the EPH interrupt on certain TX errors. 29354773Simp */ 29454773Simp SMC_SELECT_BANK(1); 29554773Simp outw(BASE + CONTROL_REG_W, (CTR_AUTO_RELEASE | CTR_TE_ENABLE | 29654773Simp CTR_CR_ENABLE | CTR_LE_ENABLE)); 29754773Simp 29854773Simp /* Set squelch level to 240mV (default 480mV) */ 29954773Simp flags = inw(BASE + CONFIG_REG_W); 30054773Simp flags |= CR_SET_SQLCH; 30154773Simp outw(BASE + CONFIG_REG_W, flags); 30254773Simp 30354773Simp /* 30454773Simp * Reset the MMU and wait for it to be un-busy. 30554773Simp */ 30654773Simp SMC_SELECT_BANK(2); 30754773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_RESET); 30854773Simp while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ 30954773Simp ; 31054773Simp 31154773Simp /* 31254773Simp * Disable all interrupts 31354773Simp */ 31454773Simp outb(BASE + INTR_MASK_REG_B, 0x00); 31554773Simp 31654773Simp sn_setmcast(sc); 31754773Simp 31854773Simp /* 31954773Simp * Set the transmitter control. We want it enabled. 32054773Simp */ 32154773Simp flags = TCR_ENABLE; 32254773Simp 32354773Simp#ifndef SW_PAD 32454773Simp /* 32554773Simp * I (GB) have been unlucky getting this to work. 32654773Simp */ 32754773Simp flags |= TCR_PAD_ENABLE; 32854773Simp#endif /* SW_PAD */ 32954773Simp 33054773Simp outw(BASE + TXMIT_CONTROL_REG_W, flags); 33154773Simp 33254773Simp 33354773Simp /* 33454773Simp * Now, enable interrupts 33554773Simp */ 33654773Simp SMC_SELECT_BANK(2); 33754773Simp 33854773Simp mask = IM_EPH_INT | 33954773Simp IM_RX_OVRN_INT | 34054773Simp IM_RCV_INT | 34154773Simp IM_TX_INT; 34254773Simp 34354773Simp outb(BASE + INTR_MASK_REG_B, mask); 34454773Simp sc->intr_mask = mask; 34554773Simp sc->pages_wanted = -1; 34654773Simp 34754773Simp 34854773Simp /* 34954773Simp * Mark the interface running but not active. 35054773Simp */ 35154773Simp ifp->if_flags |= IFF_RUNNING; 35254773Simp ifp->if_flags &= ~IFF_OACTIVE; 35354773Simp 35454773Simp /* 35554773Simp * Attempt to push out any waiting packets. 35654773Simp */ 35754773Simp snstart(ifp); 35854773Simp 35954773Simp splx(s); 36054773Simp} 36154773Simp 36254773Simp 36354773Simpvoid 36454773Simpsnstart(struct ifnet *ifp) 36554773Simp{ 36654994Simp register struct sn_softc *sc = ifp->if_softc; 36754773Simp register u_int len; 36854773Simp register struct mbuf *m; 36954773Simp struct mbuf *top; 37054773Simp int s, pad; 37154773Simp int mask; 37254773Simp u_short length; 37354773Simp u_short numPages; 37454773Simp u_char packet_no; 37554773Simp int time_out; 37654773Simp 37754773Simp s = splimp(); 37854773Simp 37954773Simp if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { 38054773Simp splx(s); 38154773Simp return; 38254773Simp } 38354773Simp if (sc->pages_wanted != -1) { 38454773Simp splx(s); 38554773Simp printf("sn%d: snstart() while memory allocation pending\n", 38654773Simp ifp->if_unit); 38754773Simp return; 38854773Simp } 38954773Simpstartagain: 39054773Simp 39154773Simp /* 39254773Simp * Sneak a peek at the next packet 39354773Simp */ 39454773Simp m = sc->arpcom.ac_if.if_snd.ifq_head; 39554773Simp if (m == 0) { 39654773Simp splx(s); 39754773Simp return; 39854773Simp } 39954773Simp /* 40054773Simp * Compute the frame length and set pad to give an overall even 40154773Simp * number of bytes. Below we assume that the packet length is even. 40254773Simp */ 40354773Simp for (len = 0, top = m; m; m = m->m_next) 40454773Simp len += m->m_len; 40554773Simp 40654773Simp pad = (len & 1); 40754773Simp 40854773Simp /* 40954773Simp * We drop packets that are too large. Perhaps we should truncate 41054773Simp * them instead? 41154773Simp */ 41254773Simp if (len + pad > ETHER_MAX_LEN - ETHER_CRC_LEN) { 41354773Simp printf("sn%d: large packet discarded (A)\n", ifp->if_unit); 41454773Simp ++sc->arpcom.ac_if.if_oerrors; 41554773Simp IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); 41654773Simp m_freem(m); 41754773Simp goto readcheck; 41854773Simp } 41954773Simp#ifdef SW_PAD 42054773Simp 42154773Simp /* 42254773Simp * If HW padding is not turned on, then pad to ETHER_MIN_LEN. 42354773Simp */ 42454773Simp if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) 42554773Simp pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len; 42654773Simp 42754773Simp#endif /* SW_PAD */ 42854773Simp 42954773Simp length = pad + len; 43054773Simp 43154773Simp /* 43254773Simp * The MMU wants the number of pages to be the number of 256 byte 43354773Simp * 'pages', minus 1 (A packet can't ever have 0 pages. We also 43454773Simp * include space for the status word, byte count and control bytes in 43554773Simp * the allocation request. 43654773Simp */ 43754773Simp numPages = (length + 6) >> 8; 43854773Simp 43954773Simp 44054773Simp /* 44154773Simp * Now, try to allocate the memory 44254773Simp */ 44354773Simp SMC_SELECT_BANK(2); 44454773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_ALLOC | numPages); 44554773Simp 44654773Simp /* 44754773Simp * Wait a short amount of time to see if the allocation request 44854773Simp * completes. Otherwise, I enable the interrupt and wait for 44954773Simp * completion asyncronously. 45054773Simp */ 45154773Simp 45254773Simp time_out = MEMORY_WAIT_TIME; 45354773Simp do { 45454773Simp if (inb(BASE + INTR_STAT_REG_B) & IM_ALLOC_INT) 45554773Simp break; 45654773Simp } while (--time_out); 45754773Simp 45854773Simp if (!time_out) { 45954773Simp 46054773Simp /* 46154773Simp * No memory now. Oh well, wait until the chip finds memory 46254773Simp * later. Remember how many pages we were asking for and 46354773Simp * enable the allocation completion interrupt. Also set a 46454773Simp * watchdog in case we miss the interrupt. We mark the 46554773Simp * interface active since there is no point in attempting an 46654773Simp * snstart() until after the memory is available. 46754773Simp */ 46854773Simp mask = inb(BASE + INTR_MASK_REG_B) | IM_ALLOC_INT; 46954773Simp outb(BASE + INTR_MASK_REG_B, mask); 47054773Simp sc->intr_mask = mask; 47154773Simp 47254773Simp sc->arpcom.ac_if.if_timer = 1; 47354773Simp sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; 47454773Simp sc->pages_wanted = numPages; 47554773Simp 47654773Simp splx(s); 47754773Simp return; 47854773Simp } 47954773Simp /* 48054773Simp * The memory allocation completed. Check the results. 48154773Simp */ 48254773Simp packet_no = inb(BASE + ALLOC_RESULT_REG_B); 48354773Simp if (packet_no & ARR_FAILED) { 48454773Simp printf("sn%d: Memory allocation failed\n", ifp->if_unit); 48554773Simp goto startagain; 48654773Simp } 48754773Simp /* 48854773Simp * We have a packet number, so tell the card to use it. 48954773Simp */ 49054773Simp outb(BASE + PACKET_NUM_REG_B, packet_no); 49154773Simp 49254773Simp /* 49354773Simp * Point to the beginning of the packet 49454773Simp */ 49554773Simp outw(BASE + POINTER_REG_W, PTR_AUTOINC | 0x0000); 49654773Simp 49754773Simp /* 49854773Simp * Send the packet length (+6 for status, length and control byte) 49954773Simp * and the status word (set to zeros) 50054773Simp */ 50154773Simp outw(BASE + DATA_REG_W, 0); 50254773Simp outb(BASE + DATA_REG_B, (length + 6) & 0xFF); 50354773Simp outb(BASE + DATA_REG_B, (length + 6) >> 8); 50454773Simp 50554773Simp /* 50654773Simp * Get the packet from the kernel. This will include the Ethernet 50754773Simp * frame header, MAC Addresses etc. 50854773Simp */ 50954773Simp IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); 51054773Simp 51154773Simp /* 51254773Simp * Push out the data to the card. 51354773Simp */ 51454773Simp for (top = m; m != 0; m = m->m_next) { 51554773Simp 51654773Simp /* 51754773Simp * Push out words. 51854773Simp */ 51954773Simp outsw(BASE + DATA_REG_W, mtod(m, caddr_t), m->m_len / 2); 52054773Simp 52154773Simp /* 52254773Simp * Push out remaining byte. 52354773Simp */ 52454773Simp if (m->m_len & 1) 52554773Simp outb(BASE + DATA_REG_B, *(mtod(m, caddr_t) + m->m_len - 1)); 52654773Simp } 52754773Simp 52854773Simp /* 52954773Simp * Push out padding. 53054773Simp */ 53154773Simp while (pad > 1) { 53254773Simp outw(BASE + DATA_REG_W, 0); 53354773Simp pad -= 2; 53454773Simp } 53554773Simp if (pad) 53654773Simp outb(BASE + DATA_REG_B, 0); 53754773Simp 53854773Simp /* 53954773Simp * Push out control byte and unused packet byte The control byte is 0 54054773Simp * meaning the packet is even lengthed and no special CRC handling is 54154773Simp * desired. 54254773Simp */ 54354773Simp outw(BASE + DATA_REG_W, 0); 54454773Simp 54554773Simp /* 54654773Simp * Enable the interrupts and let the chipset deal with it Also set a 54754773Simp * watchdog in case we miss the interrupt. 54854773Simp */ 54954773Simp mask = inb(BASE + INTR_MASK_REG_B) | (IM_TX_INT | IM_TX_EMPTY_INT); 55054773Simp outb(BASE + INTR_MASK_REG_B, mask); 55154773Simp sc->intr_mask = mask; 55254773Simp 55354773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_ENQUEUE); 55454773Simp 55554773Simp sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; 55654773Simp sc->arpcom.ac_if.if_timer = 1; 55754773Simp 55854773Simp if (ifp->if_bpf) { 55954773Simp bpf_mtap(ifp, top); 56054773Simp } 56154773Simp 56254773Simp sc->arpcom.ac_if.if_opackets++; 56354773Simp m_freem(top); 56454773Simp 56554773Simp 56654773Simpreadcheck: 56754773Simp 56854773Simp /* 56954773Simp * Is another packet coming in? We don't want to overflow the tiny 57054773Simp * RX FIFO. If nothing has arrived then attempt to queue another 57154773Simp * transmit packet. 57254773Simp */ 57354773Simp if (inw(BASE + FIFO_PORTS_REG_W) & FIFO_REMPTY) 57454773Simp goto startagain; 57554773Simp 57654773Simp splx(s); 57754773Simp return; 57854773Simp} 57954773Simp 58054773Simp 58154773Simp 58254773Simp/* Resume a packet transmit operation after a memory allocation 58354773Simp * has completed. 58454773Simp * 58554773Simp * This is basically a hacked up copy of snstart() which handles 58654773Simp * a completed memory allocation the same way snstart() does. 58754773Simp * It then passes control to snstart to handle any other queued 58854773Simp * packets. 58954773Simp */ 59054773Simpstatic void 59154773Simpsnresume(struct ifnet *ifp) 59254773Simp{ 59354994Simp register struct sn_softc *sc = ifp->if_softc; 59454773Simp register u_int len; 59554773Simp register struct mbuf *m; 59654773Simp struct mbuf *top; 59754773Simp int pad; 59854773Simp int mask; 59954773Simp u_short length; 60054773Simp u_short numPages; 60154773Simp u_short pages_wanted; 60254773Simp u_char packet_no; 60354773Simp 60454773Simp if (sc->pages_wanted < 0) 60554773Simp return; 60654773Simp 60754773Simp pages_wanted = sc->pages_wanted; 60854773Simp sc->pages_wanted = -1; 60954773Simp 61054773Simp /* 61154773Simp * Sneak a peek at the next packet 61254773Simp */ 61354773Simp m = sc->arpcom.ac_if.if_snd.ifq_head; 61454773Simp if (m == 0) { 61554773Simp printf("sn%d: snresume() with nothing to send\n", ifp->if_unit); 61654773Simp return; 61754773Simp } 61854773Simp /* 61954773Simp * Compute the frame length and set pad to give an overall even 62054773Simp * number of bytes. Below we assume that the packet length is even. 62154773Simp */ 62254773Simp for (len = 0, top = m; m; m = m->m_next) 62354773Simp len += m->m_len; 62454773Simp 62554773Simp pad = (len & 1); 62654773Simp 62754773Simp /* 62854773Simp * We drop packets that are too large. Perhaps we should truncate 62954773Simp * them instead? 63054773Simp */ 63154773Simp if (len + pad > ETHER_MAX_LEN - ETHER_CRC_LEN) { 63254773Simp printf("sn%d: large packet discarded (B)\n", ifp->if_unit); 63354773Simp ++sc->arpcom.ac_if.if_oerrors; 63454773Simp IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); 63554773Simp m_freem(m); 63654773Simp return; 63754773Simp } 63854773Simp#ifdef SW_PAD 63954773Simp 64054773Simp /* 64154773Simp * If HW padding is not turned on, then pad to ETHER_MIN_LEN. 64254773Simp */ 64354773Simp if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) 64454773Simp pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len; 64554773Simp 64654773Simp#endif /* SW_PAD */ 64754773Simp 64854773Simp length = pad + len; 64954773Simp 65054773Simp 65154773Simp /* 65254773Simp * The MMU wants the number of pages to be the number of 256 byte 65354773Simp * 'pages', minus 1 (A packet can't ever have 0 pages. We also 65454773Simp * include space for the status word, byte count and control bytes in 65554773Simp * the allocation request. 65654773Simp */ 65754773Simp numPages = (length + 6) >> 8; 65854773Simp 65954773Simp 66054773Simp SMC_SELECT_BANK(2); 66154773Simp 66254773Simp /* 66354773Simp * The memory allocation completed. Check the results. If it failed, 66454773Simp * we simply set a watchdog timer and hope for the best. 66554773Simp */ 66654773Simp packet_no = inb(BASE + ALLOC_RESULT_REG_B); 66754773Simp if (packet_no & ARR_FAILED) { 66854773Simp printf("sn%d: Memory allocation failed. Weird.\n", ifp->if_unit); 66954773Simp sc->arpcom.ac_if.if_timer = 1; 67054773Simp goto try_start; 67154773Simp } 67254773Simp /* 67354773Simp * We have a packet number, so tell the card to use it. 67454773Simp */ 67554773Simp outb(BASE + PACKET_NUM_REG_B, packet_no); 67654773Simp 67754773Simp /* 67854773Simp * Now, numPages should match the pages_wanted recorded when the 67954773Simp * memory allocation was initiated. 68054773Simp */ 68154773Simp if (pages_wanted != numPages) { 68254773Simp printf("sn%d: memory allocation wrong size. Weird.\n", ifp->if_unit); 68354773Simp /* 68454773Simp * If the allocation was the wrong size we simply release the 68554773Simp * memory once it is granted. Wait for the MMU to be un-busy. 68654773Simp */ 68754773Simp while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ 68854773Simp ; 68954773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_FREEPKT); 69054773Simp 69154773Simp return; 69254773Simp } 69354773Simp /* 69454773Simp * Point to the beginning of the packet 69554773Simp */ 69654773Simp outw(BASE + POINTER_REG_W, PTR_AUTOINC | 0x0000); 69754773Simp 69854773Simp /* 69954773Simp * Send the packet length (+6 for status, length and control byte) 70054773Simp * and the status word (set to zeros) 70154773Simp */ 70254773Simp outw(BASE + DATA_REG_W, 0); 70354773Simp outb(BASE + DATA_REG_B, (length + 6) & 0xFF); 70454773Simp outb(BASE + DATA_REG_B, (length + 6) >> 8); 70554773Simp 70654773Simp /* 70754773Simp * Get the packet from the kernel. This will include the Ethernet 70854773Simp * frame header, MAC Addresses etc. 70954773Simp */ 71054773Simp IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); 71154773Simp 71254773Simp /* 71354773Simp * Push out the data to the card. 71454773Simp */ 71554773Simp for (top = m; m != 0; m = m->m_next) { 71654773Simp 71754773Simp /* 71854773Simp * Push out words. 71954773Simp */ 72054773Simp outsw(BASE + DATA_REG_W, mtod(m, caddr_t), m->m_len / 2); 72154773Simp 72254773Simp /* 72354773Simp * Push out remaining byte. 72454773Simp */ 72554773Simp if (m->m_len & 1) 72654773Simp outb(BASE + DATA_REG_B, *(mtod(m, caddr_t) + m->m_len - 1)); 72754773Simp } 72854773Simp 72954773Simp /* 73054773Simp * Push out padding. 73154773Simp */ 73254773Simp while (pad > 1) { 73354773Simp outw(BASE + DATA_REG_W, 0); 73454773Simp pad -= 2; 73554773Simp } 73654773Simp if (pad) 73754773Simp outb(BASE + DATA_REG_B, 0); 73854773Simp 73954773Simp /* 74054773Simp * Push out control byte and unused packet byte The control byte is 0 74154773Simp * meaning the packet is even lengthed and no special CRC handling is 74254773Simp * desired. 74354773Simp */ 74454773Simp outw(BASE + DATA_REG_W, 0); 74554773Simp 74654773Simp /* 74754773Simp * Enable the interrupts and let the chipset deal with it Also set a 74854773Simp * watchdog in case we miss the interrupt. 74954773Simp */ 75054773Simp mask = inb(BASE + INTR_MASK_REG_B) | (IM_TX_INT | IM_TX_EMPTY_INT); 75154773Simp outb(BASE + INTR_MASK_REG_B, mask); 75254773Simp sc->intr_mask = mask; 75354773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_ENQUEUE); 75454773Simp 75554773Simp if (ifp->if_bpf) { 75654773Simp bpf_mtap(ifp, top); 75754773Simp } 75854773Simp 75954773Simp sc->arpcom.ac_if.if_opackets++; 76054773Simp m_freem(top); 76154773Simp 76254773Simptry_start: 76354773Simp 76454773Simp /* 76554773Simp * Now pass control to snstart() to queue any additional packets 76654773Simp */ 76754773Simp sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 76854773Simp snstart(ifp); 76954773Simp 77054773Simp /* 77154773Simp * We've sent something, so we're active. Set a watchdog in case the 77254773Simp * TX_EMPTY interrupt is lost. 77354773Simp */ 77454773Simp sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; 77554773Simp sc->arpcom.ac_if.if_timer = 1; 77654773Simp 77754773Simp return; 77854773Simp} 77954773Simp 78054773Simp 78154773Simpvoid 78254994Simpsn_intr(void *arg) 78354773Simp{ 78454773Simp int status, interrupts; 78554994Simp register struct sn_softc *sc = (struct sn_softc *) arg; 78654773Simp struct ifnet *ifp = &sc->arpcom.ac_if; 78754773Simp int x; 78854773Simp 78954773Simp /* 79054773Simp * Chip state registers 79154773Simp */ 79254773Simp u_char mask; 79354773Simp u_char packet_no; 79454773Simp u_short tx_status; 79554773Simp u_short card_stats; 79654773Simp 79754773Simp /* 79854773Simp * if_ep.c did this, so I do too. Yet if_ed.c doesn't. I wonder... 79954773Simp */ 80054773Simp x = splbio(); 80154773Simp 80254773Simp /* 80354773Simp * Clear the watchdog. 80454773Simp */ 80554773Simp ifp->if_timer = 0; 80654773Simp 80754773Simp SMC_SELECT_BANK(2); 80854773Simp 80954773Simp /* 81054773Simp * Obtain the current interrupt mask and clear the hardware mask 81154773Simp * while servicing interrupts. 81254773Simp */ 81354773Simp mask = inb(BASE + INTR_MASK_REG_B); 81454773Simp outb(BASE + INTR_MASK_REG_B, 0x00); 81554773Simp 81654773Simp /* 81754773Simp * Get the set of interrupts which occurred and eliminate any which 81854773Simp * are masked. 81954773Simp */ 82054773Simp interrupts = inb(BASE + INTR_STAT_REG_B); 82154773Simp status = interrupts & mask; 82254773Simp 82354773Simp /* 82454773Simp * Now, process each of the interrupt types. 82554773Simp */ 82654773Simp 82754773Simp /* 82854773Simp * Receive Overrun. 82954773Simp */ 83054773Simp if (status & IM_RX_OVRN_INT) { 83154773Simp 83254773Simp /* 83354773Simp * Acknowlege Interrupt 83454773Simp */ 83554773Simp SMC_SELECT_BANK(2); 83654773Simp outb(BASE + INTR_ACK_REG_B, IM_RX_OVRN_INT); 83754773Simp 83854773Simp ++sc->arpcom.ac_if.if_ierrors; 83954773Simp } 84054773Simp /* 84154773Simp * Got a packet. 84254773Simp */ 84354773Simp if (status & IM_RCV_INT) { 84454773Simp#if 1 84554773Simp int packet_number; 84654773Simp 84754773Simp SMC_SELECT_BANK(2); 84854773Simp packet_number = inw(BASE + FIFO_PORTS_REG_W); 84954773Simp 85054773Simp if (packet_number & FIFO_REMPTY) { 85154773Simp 85254773Simp /* 85354773Simp * we got called , but nothing was on the FIFO 85454773Simp */ 85554773Simp printf("sn: Receive interrupt with nothing on FIFO\n"); 85654773Simp 85754773Simp goto out; 85854773Simp } 85954773Simp#endif 86054773Simp snread(ifp); 86154773Simp } 86254773Simp /* 86354773Simp * An on-card memory allocation came through. 86454773Simp */ 86554773Simp if (status & IM_ALLOC_INT) { 86654773Simp 86754773Simp /* 86854773Simp * Disable this interrupt. 86954773Simp */ 87054773Simp mask &= ~IM_ALLOC_INT; 87154773Simp sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 87254773Simp snresume(&sc->arpcom.ac_if); 87354773Simp } 87454773Simp /* 87554773Simp * TX Completion. Handle a transmit error message. This will only be 87654773Simp * called when there is an error, because of the AUTO_RELEASE mode. 87754773Simp */ 87854773Simp if (status & IM_TX_INT) { 87954773Simp 88054773Simp /* 88154773Simp * Acknowlege Interrupt 88254773Simp */ 88354773Simp SMC_SELECT_BANK(2); 88454773Simp outb(BASE + INTR_ACK_REG_B, IM_TX_INT); 88554773Simp 88654773Simp packet_no = inw(BASE + FIFO_PORTS_REG_W); 88754773Simp packet_no &= FIFO_TX_MASK; 88854773Simp 88954773Simp /* 89054773Simp * select this as the packet to read from 89154773Simp */ 89254773Simp outb(BASE + PACKET_NUM_REG_B, packet_no); 89354773Simp 89454773Simp /* 89554773Simp * Position the pointer to the first word from this packet 89654773Simp */ 89754773Simp outw(BASE + POINTER_REG_W, PTR_AUTOINC | PTR_READ | 0x0000); 89854773Simp 89954773Simp /* 90054773Simp * Fetch the TX status word. The value found here will be a 90154773Simp * copy of the EPH_STATUS_REG_W at the time the transmit 90254773Simp * failed. 90354773Simp */ 90454773Simp tx_status = inw(BASE + DATA_REG_W); 90554773Simp 90654773Simp if (tx_status & EPHSR_TX_SUC) { 90754994Simp device_printf(sc->dev, 90854994Simp "Successful packet caused interrupt\n"); 90954773Simp } else { 91054773Simp ++sc->arpcom.ac_if.if_oerrors; 91154773Simp } 91254773Simp 91354773Simp if (tx_status & EPHSR_LATCOL) 91454773Simp ++sc->arpcom.ac_if.if_collisions; 91554773Simp 91654773Simp /* 91754773Simp * Some of these errors will have disabled transmit. 91854773Simp * Re-enable transmit now. 91954773Simp */ 92054773Simp SMC_SELECT_BANK(0); 92154773Simp 92254773Simp#ifdef SW_PAD 92354773Simp outw(BASE + TXMIT_CONTROL_REG_W, TCR_ENABLE); 92454773Simp#else 92554773Simp outw(BASE + TXMIT_CONTROL_REG_W, TCR_ENABLE | TCR_PAD_ENABLE); 92654773Simp#endif /* SW_PAD */ 92754773Simp 92854773Simp /* 92954773Simp * kill the failed packet. Wait for the MMU to be un-busy. 93054773Simp */ 93154773Simp SMC_SELECT_BANK(2); 93254773Simp while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ 93354773Simp ; 93454773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_FREEPKT); 93554773Simp 93654773Simp /* 93754773Simp * Attempt to queue more transmits. 93854773Simp */ 93954773Simp sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 94054773Simp snstart(&sc->arpcom.ac_if); 94154773Simp } 94254773Simp /* 94354773Simp * Transmit underrun. We use this opportunity to update transmit 94454773Simp * statistics from the card. 94554773Simp */ 94654773Simp if (status & IM_TX_EMPTY_INT) { 94754773Simp 94854773Simp /* 94954773Simp * Acknowlege Interrupt 95054773Simp */ 95154773Simp SMC_SELECT_BANK(2); 95254773Simp outb(BASE + INTR_ACK_REG_B, IM_TX_EMPTY_INT); 95354773Simp 95454773Simp /* 95554773Simp * Disable this interrupt. 95654773Simp */ 95754773Simp mask &= ~IM_TX_EMPTY_INT; 95854773Simp 95954773Simp SMC_SELECT_BANK(0); 96054773Simp card_stats = inw(BASE + COUNTER_REG_W); 96154773Simp 96254773Simp /* 96354773Simp * Single collisions 96454773Simp */ 96554773Simp sc->arpcom.ac_if.if_collisions += card_stats & ECR_COLN_MASK; 96654773Simp 96754773Simp /* 96854773Simp * Multiple collisions 96954773Simp */ 97054773Simp sc->arpcom.ac_if.if_collisions += (card_stats & ECR_MCOLN_MASK) >> 4; 97154773Simp 97254773Simp SMC_SELECT_BANK(2); 97354773Simp 97454773Simp /* 97554773Simp * Attempt to enqueue some more stuff. 97654773Simp */ 97754773Simp sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 97854773Simp snstart(&sc->arpcom.ac_if); 97954773Simp } 98054773Simp /* 98154773Simp * Some other error. Try to fix it by resetting the adapter. 98254773Simp */ 98354773Simp if (status & IM_EPH_INT) { 98454994Simp snstop(sc); 98554773Simp sninit(sc); 98654773Simp } 98754773Simp 98854773Simpout: 98954773Simp /* 99054773Simp * Handled all interrupt sources. 99154773Simp */ 99254773Simp 99354773Simp SMC_SELECT_BANK(2); 99454773Simp 99554773Simp /* 99654773Simp * Reestablish interrupts from mask which have not been deselected 99754773Simp * during this interrupt. Note that the hardware mask, which was set 99854773Simp * to 0x00 at the start of this service routine, may have been 99954773Simp * updated by one or more of the interrupt handers and we must let 100054773Simp * those new interrupts stay enabled here. 100154773Simp */ 100254773Simp mask |= inb(BASE + INTR_MASK_REG_B); 100354773Simp outb(BASE + INTR_MASK_REG_B, mask); 100454773Simp sc->intr_mask = mask; 100554773Simp 100654773Simp splx(x); 100754773Simp} 100854773Simp 100954773Simpvoid 101054773Simpsnread(register struct ifnet *ifp) 101154773Simp{ 101254994Simp struct sn_softc *sc = ifp->if_softc; 101354773Simp struct ether_header *eh; 101454773Simp struct mbuf *m; 101554773Simp short status; 101654773Simp int packet_number; 101754773Simp u_short packet_length; 101854773Simp u_char *data; 101954773Simp 102054773Simp SMC_SELECT_BANK(2); 102154773Simp#if 0 102254773Simp packet_number = inw(BASE + FIFO_PORTS_REG_W); 102354773Simp 102454773Simp if (packet_number & FIFO_REMPTY) { 102554773Simp 102654773Simp /* 102754773Simp * we got called , but nothing was on the FIFO 102854773Simp */ 102954773Simp printf("sn: Receive interrupt with nothing on FIFO\n"); 103054773Simp return; 103154773Simp } 103254773Simp#endif 103354773Simpread_another: 103454773Simp 103554773Simp /* 103654773Simp * Start reading from the start of the packet. Since PTR_RCV is set, 103754773Simp * packet number is found in FIFO_PORTS_REG_W, FIFO_RX_MASK. 103854773Simp */ 103954773Simp outw(BASE + POINTER_REG_W, PTR_READ | PTR_RCV | PTR_AUTOINC | 0x0000); 104054773Simp 104154773Simp /* 104254773Simp * First two words are status and packet_length 104354773Simp */ 104454773Simp status = inw(BASE + DATA_REG_W); 104554773Simp packet_length = inw(BASE + DATA_REG_W) & RLEN_MASK; 104654773Simp 104754773Simp /* 104854773Simp * The packet length contains 3 extra words: status, length, and a 104954773Simp * extra word with the control byte. 105054773Simp */ 105154773Simp packet_length -= 6; 105254773Simp 105354773Simp /* 105454773Simp * Account for receive errors and discard. 105554773Simp */ 105654773Simp if (status & RS_ERRORS) { 105754773Simp ++sc->arpcom.ac_if.if_ierrors; 105854773Simp goto out; 105954773Simp } 106054773Simp /* 106154773Simp * A packet is received. 106254773Simp */ 106354773Simp 106454773Simp /* 106554773Simp * Adjust for odd-length packet. 106654773Simp */ 106754773Simp if (status & RS_ODDFRAME) 106854773Simp packet_length++; 106954773Simp 107054773Simp /* 107154773Simp * Allocate a header mbuf from the kernel. 107254773Simp */ 107354773Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 107454773Simp if (m == NULL) 107554773Simp goto out; 107654773Simp 107754773Simp m->m_pkthdr.rcvif = &sc->arpcom.ac_if; 107854773Simp m->m_pkthdr.len = m->m_len = packet_length; 107954773Simp 108054773Simp /* 108154773Simp * Attach an mbuf cluster 108254773Simp */ 108354773Simp MCLGET(m, M_DONTWAIT); 108454773Simp 108554773Simp /* 108654773Simp * Insist on getting a cluster 108754773Simp */ 108854773Simp if ((m->m_flags & M_EXT) == 0) { 108954773Simp m_freem(m); 109054773Simp ++sc->arpcom.ac_if.if_ierrors; 109154773Simp printf("sn: snread() kernel memory allocation problem\n"); 109254773Simp goto out; 109354773Simp } 109454773Simp eh = mtod(m, struct ether_header *); 109554773Simp 109654773Simp /* 109754773Simp * Get packet, including link layer address, from interface. 109854773Simp */ 109954773Simp 110054773Simp data = (u_char *) eh; 110154773Simp insw(BASE + DATA_REG_W, data, packet_length >> 1); 110254773Simp if (packet_length & 1) { 110354773Simp data += packet_length & ~1; 110454773Simp *data = inb(BASE + DATA_REG_B); 110554773Simp } 110654773Simp ++sc->arpcom.ac_if.if_ipackets; 110754773Simp 110854773Simp if (sc->arpcom.ac_if.if_bpf) 110954773Simp { 111054773Simp bpf_mtap(&sc->arpcom.ac_if, m); 111154773Simp 111254773Simp /* 111354773Simp * Note that the interface cannot be in promiscuous mode if 111454773Simp * there are no BPF listeners. And if we are in promiscuous 111554773Simp * mode, we have to check if this packet is really ours. 111654773Simp */ 111754773Simp if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && 111854773Simp (eh->ether_dhost[0] & 1) == 0 && 111954773Simp bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 112054773Simp sizeof(eh->ether_dhost)) != 0 && 112154773Simp bcmp(eh->ether_dhost, etherbroadcastaddr, 112254773Simp sizeof(eh->ether_dhost)) != 0) { 112354773Simp m_freem(m); 112454773Simp goto out; 112554773Simp } 112654773Simp } 112754773Simp 112854773Simp /* 112954773Simp * Remove link layer addresses and whatnot. 113054773Simp */ 113154773Simp m->m_pkthdr.len = m->m_len = packet_length - sizeof(struct ether_header); 113254773Simp m->m_data += sizeof(struct ether_header); 113354773Simp 113454773Simp ether_input(&sc->arpcom.ac_if, eh, m); 113554773Simp 113654773Simpout: 113754773Simp 113854773Simp /* 113954773Simp * Error or good, tell the card to get rid of this packet Wait for 114054773Simp * the MMU to be un-busy. 114154773Simp */ 114254773Simp SMC_SELECT_BANK(2); 114354773Simp while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ 114454773Simp ; 114554773Simp outw(BASE + MMU_CMD_REG_W, MMUCR_RELEASE); 114654773Simp 114754773Simp /* 114854773Simp * Check whether another packet is ready 114954773Simp */ 115054773Simp packet_number = inw(BASE + FIFO_PORTS_REG_W); 115154773Simp if (packet_number & FIFO_REMPTY) { 115254773Simp return; 115354773Simp } 115454773Simp goto read_another; 115554773Simp} 115654773Simp 115754773Simp 115854773Simp/* 115954773Simp * Handle IOCTLS. This function is completely stolen from if_ep.c 116054773Simp * As with its progenitor, it does not handle hardware address 116154773Simp * changes. 116254773Simp */ 116354773Simpstatic int 116454773Simpsnioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 116554773Simp{ 116654994Simp struct sn_softc *sc = ifp->if_softc; 116754773Simp int s, error = 0; 116854773Simp 116954773Simp s = splimp(); 117054773Simp 117154773Simp switch (cmd) { 117254773Simp case SIOCSIFADDR: 117354773Simp case SIOCGIFADDR: 117454773Simp case SIOCSIFMTU: 117554773Simp error = ether_ioctl(ifp, cmd, data); 117654773Simp break; 117754773Simp 117854773Simp case SIOCSIFFLAGS: 117954773Simp if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { 118054773Simp ifp->if_flags &= ~IFF_RUNNING; 118154994Simp snstop(sc); 118254773Simp break; 118354773Simp } else { 118454773Simp /* reinitialize card on any parameter change */ 118554773Simp sninit(sc); 118654773Simp break; 118754773Simp } 118854773Simp break; 118954773Simp 119054773Simp#ifdef notdef 119154773Simp case SIOCGHWADDR: 119254773Simp bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data, 119354773Simp sizeof(sc->sc_addr)); 119454773Simp break; 119554773Simp#endif 119654773Simp 119754773Simp case SIOCADDMULTI: 119854773Simp /* update multicast filter list. */ 119954773Simp sn_setmcast(sc); 120054773Simp error = 0; 120154773Simp break; 120254773Simp case SIOCDELMULTI: 120354773Simp /* update multicast filter list. */ 120454773Simp sn_setmcast(sc); 120554773Simp error = 0; 120654773Simp break; 120754773Simp default: 120854773Simp error = EINVAL; 120954773Simp } 121054773Simp 121154773Simp splx(s); 121254773Simp 121354773Simp return (error); 121454773Simp} 121554773Simp 121654773Simpvoid 121754994Simpsnreset(struct sn_softc *sc) 121854773Simp{ 121954773Simp int s; 122054994Simp 122154773Simp s = splimp(); 122254994Simp snstop(sc); 122354773Simp sninit(sc); 122454773Simp 122554773Simp splx(s); 122654773Simp} 122754773Simp 122854773Simpvoid 122954773Simpsnwatchdog(struct ifnet *ifp) 123054773Simp{ 123154773Simp int s; 123254773Simp s = splimp(); 123354994Simp sn_intr(ifp->if_softc); 123454773Simp splx(s); 123554773Simp} 123654773Simp 123754773Simp 123854773Simp/* 1. zero the interrupt mask 123954773Simp * 2. clear the enable receive flag 124054773Simp * 3. clear the enable xmit flags 124154773Simp */ 124254773Simpvoid 124354994Simpsnstop(struct sn_softc *sc) 124454773Simp{ 124554994Simp 124654773Simp struct ifnet *ifp = &sc->arpcom.ac_if; 124754773Simp 124854773Simp /* 124954773Simp * Clear interrupt mask; disable all interrupts. 125054773Simp */ 125154773Simp SMC_SELECT_BANK(2); 125254773Simp outb(BASE + INTR_MASK_REG_B, 0x00); 125354773Simp 125454773Simp /* 125554773Simp * Disable transmitter and Receiver 125654773Simp */ 125754773Simp SMC_SELECT_BANK(0); 125854773Simp outw(BASE + RECV_CONTROL_REG_W, 0x0000); 125954773Simp outw(BASE + TXMIT_CONTROL_REG_W, 0x0000); 126054773Simp 126154773Simp /* 126254773Simp * Cancel watchdog. 126354773Simp */ 126454773Simp ifp->if_timer = 0; 126554773Simp} 126654773Simp 126754773Simp 126854994Simpint 126954994Simpsn_activate(device_t dev) 127054994Simp{ 127154994Simp struct sn_softc *sc = device_get_softc(dev); 127254994Simp int err; 127354773Simp 127454994Simp sc->port_rid = 0; 127554994Simp sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, 127654994Simp 0, ~0, SMC_IO_EXTENT, RF_ACTIVE); 127754994Simp if (!sc->port_res) { 127854994Simp#ifdef SN_DEBUG 127954994Simp device_printf(dev, "Cannot allocate ioport\n"); 128054994Simp#endif 128154994Simp return ENOMEM; 128254994Simp } 128354994Simp 128454994Simp sc->irq_rid = 0; 128554994Simp sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 128654994Simp 0, ~0, 1, RF_ACTIVE); 128754994Simp if (!sc->irq_res) { 128854994Simp#ifdef SN_DEBUG 128955161Simp device_printf(dev, "Cannot allocate irq\n"); 129054994Simp#endif 129154994Simp sn_deactivate(dev); 129254994Simp return ENOMEM; 129354994Simp } 129454994Simp if ((err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET, sn_intr, sc, 129554994Simp &sc->intrhand)) != 0) { 129654994Simp sn_deactivate(dev); 129754994Simp return err; 129854994Simp } 129954994Simp 130054994Simp sc->sn_io_addr = rman_get_start(sc->port_res); 130154994Simp return (0); 130254994Simp} 130354994Simp 130454994Simpvoid 130554994Simpsn_deactivate(device_t dev) 130654994Simp{ 130754994Simp struct sn_softc *sc = device_get_softc(dev); 130854994Simp 130954994Simp if (sc->intrhand) 131054994Simp bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 131154994Simp sc->intrhand = 0; 131254994Simp if (sc->port_res) 131354994Simp bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, 131454994Simp sc->port_res); 131554994Simp sc->port_res = 0; 131654994Simp if (sc->irq_res) 131754994Simp bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, 131854994Simp sc->irq_res); 131954994Simp sc->irq_res = 0; 132054994Simp return; 132154994Simp} 132254994Simp 132354773Simp/* 132454994Simp * Function: sn_probe( device_t dev, int pccard ) 132554773Simp * 132654773Simp * Purpose: 132754773Simp * Tests to see if a given ioaddr points to an SMC9xxx chip. 132854773Simp * Tries to cause as little damage as possible if it's not a SMC chip. 132954773Simp * Returns a 0 on success 133054773Simp * 133154773Simp * Algorithm: 133254773Simp * (1) see if the high byte of BANK_SELECT is 0x33 133354773Simp * (2) compare the ioaddr with the base register's address 133454773Simp * (3) see if I recognize the chip ID in the appropriate register 133554773Simp * 133654773Simp * 133754773Simp */ 133854994Simpint 133954994Simpsn_probe(device_t dev, int pccard) 134054773Simp{ 134154994Simp struct sn_softc *sc = device_get_softc(dev); 134254773Simp u_int bank; 134354773Simp u_short revision_register; 134454773Simp u_short base_address_register; 134554994Simp u_short ioaddr; 134654994Simp int err; 134754773Simp 134854994Simp if ((err = sn_activate(dev)) != 0) 134954994Simp return err; 135054994Simp 135154994Simp ioaddr = sc->sn_io_addr; 135254994Simp 135354773Simp /* 135454773Simp * First, see if the high byte is 0x33 135554773Simp */ 135654773Simp bank = inw(ioaddr + BANK_SELECT_REG_W); 135754773Simp if ((bank & BSR_DETECT_MASK) != BSR_DETECT_VALUE) { 135854773Simp#ifdef SN_DEBUG 135954994Simp device_printf(dev, "test1 failed\n"); 136054773Simp#endif 136154994Simp goto error; 136254773Simp } 136354773Simp /* 136454773Simp * The above MIGHT indicate a device, but I need to write to further 136554773Simp * test this. Go to bank 0, then test that the register still 136654773Simp * reports the high byte is 0x33. 136754773Simp */ 136854773Simp outw(ioaddr + BANK_SELECT_REG_W, 0x0000); 136954773Simp bank = inw(ioaddr + BANK_SELECT_REG_W); 137054773Simp if ((bank & BSR_DETECT_MASK) != BSR_DETECT_VALUE) { 137154773Simp#ifdef SN_DEBUG 137254994Simp device_printf(dev, "test2 failed\n"); 137354773Simp#endif 137454994Simp goto error; 137554773Simp } 137654773Simp /* 137754773Simp * well, we've already written once, so hopefully another time won't 137854773Simp * hurt. This time, I need to switch the bank register to bank 1, so 137954773Simp * I can access the base address register. The contents of the 138054773Simp * BASE_ADDR_REG_W register, after some jiggery pokery, is expected 138154773Simp * to match the I/O port address where the adapter is being probed. 138254773Simp */ 138354773Simp outw(ioaddr + BANK_SELECT_REG_W, 0x0001); 138454773Simp base_address_register = inw(ioaddr + BASE_ADDR_REG_W); 138554773Simp 138654773Simp /* 138754773Simp * This test is nonsence on PC-card architecture, so if 138854773Simp * pccard == 1, skip this test. (hosokawa) 138954773Simp */ 139054773Simp if (!pccard && (ioaddr != (base_address_register >> 3 & 0x3E0))) { 139154773Simp 139254773Simp /* 139354773Simp * Well, the base address register didn't match. Must not 139454773Simp * have been a SMC chip after all. 139554773Simp */ 139654773Simp /* 139754773Simp * printf("sn: ioaddr %x doesn't match card configuration 139854773Simp * (%x)\n", ioaddr, base_address_register >> 3 & 0x3E0 ); 139954773Simp */ 140054773Simp 140154773Simp#ifdef SN_DEBUG 140254994Simp device_printf(dev, "test3 failed ioaddr = 0x%x, " 140354994Simp "base_address_register = 0x%x\n", ioaddr, 140454994Simp base_address_register >> 3 & 0x3E0); 140554773Simp#endif 140654994Simp goto error; 140754773Simp } 140854773Simp /* 140954773Simp * Check if the revision register is something that I recognize. 141054773Simp * These might need to be added to later, as future revisions could 141154773Simp * be added. 141254773Simp */ 141354773Simp outw(ioaddr + BANK_SELECT_REG_W, 0x3); 141454773Simp revision_register = inw(ioaddr + REVISION_REG_W); 141554773Simp if (!chip_ids[(revision_register >> 4) & 0xF]) { 141654773Simp 141754773Simp /* 141854773Simp * I don't regonize this chip, so... 141954773Simp */ 142054773Simp#ifdef SN_DEBUG 142154994Simp device_printf(dev, "test4 failed\n"); 142254773Simp#endif 142354994Simp goto error; 142454773Simp } 142554773Simp /* 142654773Simp * at this point I'll assume that the chip is an SMC9xxx. It might be 142754773Simp * prudent to check a listing of MAC addresses against the hardware 142854773Simp * address, or do some other tests. 142954773Simp */ 143054994Simp sn_deactivate(dev); 143154773Simp return 0; 143254994Simp error: 143354994Simp sn_deactivate(dev); 143454994Simp return ENXIO; 143554773Simp} 143654773Simp 143754773Simp#define MCFSZ 8 143854773Simp 143954773Simpstatic void 144054773Simpsn_setmcast(struct sn_softc *sc) 144154773Simp{ 144254773Simp struct ifnet *ifp = (struct ifnet *)sc; 144354773Simp int flags; 144454773Simp 144554773Simp /* 144654773Simp * Set the receiver filter. We want receive enabled and auto strip 144754773Simp * of CRC from received packet. If we are promiscuous then set that 144854773Simp * bit too. 144954773Simp */ 145054773Simp flags = RCR_ENABLE | RCR_STRIP_CRC; 145154773Simp 145254773Simp if (ifp->if_flags & IFF_PROMISC) { 145354773Simp flags |= RCR_PROMISC | RCR_ALMUL; 145454773Simp } else if (ifp->if_flags & IFF_ALLMULTI) { 145554773Simp flags |= RCR_ALMUL; 145654773Simp } else { 145754773Simp u_char mcf[MCFSZ]; 145854773Simp if (sn_getmcf(&sc->arpcom, mcf)) { 145954773Simp /* set filter */ 146054773Simp SMC_SELECT_BANK(3); 146154773Simp outw(BASE + MULTICAST1_REG_W, 146254773Simp ((u_short)mcf[1] << 8) | mcf[0]); 146354773Simp outw(BASE + MULTICAST2_REG_W, 146454773Simp ((u_short)mcf[3] << 8) | mcf[2]); 146554773Simp outw(BASE + MULTICAST3_REG_W, 146654773Simp ((u_short)mcf[5] << 8) | mcf[4]); 146754773Simp outw(BASE + MULTICAST4_REG_W, 146854773Simp ((u_short)mcf[7] << 8) | mcf[6]); 146954773Simp } else { 147054773Simp flags |= RCR_ALMUL; 147154773Simp } 147254773Simp } 147354773Simp SMC_SELECT_BANK(0); 147454773Simp outw(BASE + RECV_CONTROL_REG_W, flags); 147554773Simp} 147654773Simp 147754773Simpstatic int 147854773Simpsn_getmcf(struct arpcom *ac, u_char *mcf) 147954773Simp{ 148054773Simp int i; 148154773Simp register u_int index, index2; 148254773Simp register u_char *af = (u_char *) mcf; 148354773Simp struct ifmultiaddr *ifma; 148454773Simp 148554773Simp bzero(mcf, MCFSZ); 148654773Simp 148754773Simp for (ifma = ac->ac_if.if_multiaddrs.lh_first; ifma; 148854773Simp ifma = ifma->ifma_link.le_next) { 148954773Simp if (ifma->ifma_addr->sa_family != AF_LINK) 149054773Simp return 0; 149154773Simp index = smc_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)) & 0x3f; 149254773Simp index2 = 0; 149354773Simp for (i = 0; i < 6; i++) { 149454773Simp index2 <<= 1; 149554773Simp index2 |= (index & 0x01); 149654773Simp index >>= 1; 149754773Simp } 149854773Simp af[index2 >> 3] |= 1 << (index2 & 7); 149954773Simp } 150054773Simp return 1; /* use multicast filter */ 150154773Simp} 150254773Simp 150354773Simpstatic u_int 150454773Simpsmc_crc(u_char *s) 150554773Simp{ 150654773Simp int perByte; 150754773Simp int perBit; 150854773Simp const u_int poly = 0xedb88320; 150954773Simp u_int v = 0xffffffff; 151054773Simp u_char c; 151154773Simp 151254773Simp for (perByte = 0; perByte < ETHER_ADDR_LEN; perByte++) { 151354773Simp c = s[perByte]; 151454773Simp for (perBit = 0; perBit < 8; perBit++) { 151554773Simp v = (v >> 1)^(((v ^ c) & 0x01) ? poly : 0); 151654773Simp c >>= 1; 151754773Simp } 151854773Simp } 151954773Simp return v; 152054773Simp} 1521