if_ie.c revision 581
1581Srgrimes/*- 2581Srgrimes * Copyright (c) 1992, 1993, University of Vermont and State 3581Srgrimes * Agricultural College. 4581Srgrimes * Copyright (c) 1992, 1993, Garrett A. Wollman. 5581Srgrimes * 6581Srgrimes * Portions: 7581Srgrimes * Copyright (c) 1990, 1991, William F. Jolitz 8581Srgrimes * Copyright (c) 1990, The Regents of the University of California 9581Srgrimes * 10581Srgrimes * All rights reserved. 11581Srgrimes * 12581Srgrimes * Redistribution and use in source and binary forms, with or without 13581Srgrimes * modification, are permitted provided that the following conditions 14581Srgrimes * are met: 15581Srgrimes * 1. Redistributions of source code must retain the above copyright 16581Srgrimes * notice, this list of conditions and the following disclaimer. 17581Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 18581Srgrimes * notice, this list of conditions and the following disclaimer in the 19581Srgrimes * documentation and/or other materials provided with the distribution. 20581Srgrimes * 3. All advertising materials mentioning features or use of this software 21581Srgrimes * must display the following acknowledgement: 22581Srgrimes * This product includes software developed by the University of 23581Srgrimes * Vermont and State Agricultural College and Garrett A. Wollman, 24581Srgrimes * by William F. Jolitz, by the University of California, 25581Srgrimes * Berkeley, by Larwence Berkeley Laboratory, and its contributors. 26581Srgrimes * 4. Neither the names of the Universities nor the names of the authors 27581Srgrimes * may be used to endorse or promote products derived from this software 28581Srgrimes * without specific prior written permission. 29581Srgrimes * 30581Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31581Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32581Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33581Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE 34581Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35581Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36581Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37581Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38581Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39581Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40581Srgrimes * SUCH DAMAGE. 41581Srgrimes * 42581Srgrimes * $Id$ 43581Srgrimes */ 44581Srgrimes 45581Srgrimes/* 46581Srgrimes * Intel 82586 Ethernet chip 47581Srgrimes * Register, bit, and structure definitions. 48581Srgrimes * 49581Srgrimes * Written by GAW with reference to the Clarkson Packet Driver code for this 50581Srgrimes * chip written by Russ Nelson and others. 51581Srgrimes * 52581Srgrimes * BPF support code stolen directly from hpdev/if_le.c, supplied with 53581Srgrimes * tcpdump. 54581Srgrimes */ 55581Srgrimes 56581Srgrimes/* 57581Srgrimes * The i82586 is a very versatile chip, found in many implementations. 58581Srgrimes * Programming this chip is mostly the same, but certain details differ 59581Srgrimes * from card to card. This driver is written so that different cards 60581Srgrimes * can be automatically detected at run-time. Currently, only the 61581Srgrimes * AT&T EN100/StarLAN 10 series are supported. 62581Srgrimes */ 63581Srgrimes 64581Srgrimes/* 65581SrgrimesMode of operation: 66581Srgrimes 67581SrgrimesWe run the 82586 in a standard Ethernet mode. We keep NFRAMES received 68581Srgrimesframe descriptors around for the receiver to use, and NBUFFS associated 69581Srgrimesreceive buffer descriptors, both in a circular list. Whenever a frame is 70581Srgrimesreceived, we rotate both lists as necessary. (The 586 treats both lists 71581Srgrimesas a simple queue.) We also keep a transmit command around so that packets 72581Srgrimescan be sent off quickly. 73581Srgrimes 74581SrgrimesWe configure the adapter in AL-LOC = 1 mode, which means that the 75581SrgrimesEthernet/802.3 MAC header is placed at the beginning of the receive buffer 76581Srgrimesrather than being split off into various fields in the RFD. This also 77581Srgrimesmeans that we must include this header in the transmit buffer as well. 78581Srgrimes 79581SrgrimesBy convention, all transmit commands, and only transmit commands, shall 80581Srgrimeshave the I (IE_CMD_INTR) bit set in the command. This way, when an 81581Srgrimesinterrupt arrives at ieintr(), it is immediately possible to tell 82581Srgrimeswhat precisely caused it. ANY OTHER command-sending routines should 83581Srgrimesrun at splimp(), and should post an acknowledgement to every interrupt 84581Srgrimesthey generate. 85581Srgrimes 86581SrgrimesThe 82586 has a 24-bit address space internally, and the adaptor's 87581Srgrimesmemory is located at the top of this region. However, the value we are 88581Srgrimesgiven in configuration is normally the *bottom* of the adaptor RAM. So, 89581Srgrimeswe must go through a few gyrations to come up with a kernel virtual address 90581Srgrimeswhich represents the actual beginning of the 586 address space. First, 91581Srgrimeswe autosize the RAM by running through several possible sizes and trying 92581Srgrimesto initialize the adapter under the assumption that the selected size 93581Srgrimesis correct. Then, knowing the correct RAM size, we set up our pointers 94581Srgrimesin ie_softc[unit]. `iomem' represents the computed base of the 586 95581Srgrimesaddress space. `iomembot' represents the actual configured base 96581Srgrimesof adapter RAM. Finally, `iosize' represents the calculated size 97581Srgrimesof 586 RAM. Then, when laying out commands, we use the interval 98581Srgrimes[iomembot, iomembot + iosize); to make 24-pointers, we subtract 99581Srgrimesiomem, and to make 16-pointers, we subtract iomem and and with 0xffff. 100581Srgrimes 101581Srgrimes*/ 102581Srgrimes 103581Srgrimes#include "ie.h" 104581Srgrimes#if NIE > 0 105581Srgrimes 106581Srgrimes#include "param.h" 107581Srgrimes#include "systm.h" 108581Srgrimes#include "mbuf.h" 109581Srgrimes#include "buf.h" 110581Srgrimes#include "protosw.h" 111581Srgrimes#include "socket.h" 112581Srgrimes#include "ioctl.h" 113581Srgrimes#include "errno.h" 114581Srgrimes#include "syslog.h" 115581Srgrimes 116581Srgrimes#include "net/if.h" 117581Srgrimes#include "net/if_types.h" 118581Srgrimes#include "net/if_dl.h" 119581Srgrimes#include "net/netisr.h" 120581Srgrimes#include "net/route.h" 121581Srgrimes 122581Srgrimes#include "bpfilter.h" 123581Srgrimes 124581Srgrimes#ifdef INET 125581Srgrimes#include "netinet/in.h" 126581Srgrimes#include "netinet/in_systm.h" 127581Srgrimes#include "netinet/in_var.h" 128581Srgrimes#include "netinet/ip.h" 129581Srgrimes#include "netinet/if_ether.h" 130581Srgrimes#endif 131581Srgrimes 132581Srgrimes#ifdef NS 133581Srgrimes#include "netns/ns.h" 134581Srgrimes#include "netns/ns_if.h" 135581Srgrimes#endif 136581Srgrimes 137581Srgrimes#include "i386/isa/isa.h" 138581Srgrimes/*#include "machine/cpufunc.h"*/ 139581Srgrimes#include "i386/isa/isa_device.h" 140581Srgrimes#include "i386/isa/ic/i82586.h" 141581Srgrimes#include "i386/isa/if_iereg.h" 142581Srgrimes#include "i386/isa/icu.h" 143581Srgrimes 144581Srgrimes#include "vm/vm.h" 145581Srgrimes 146581Srgrimes#if NBPFILTER > 0 147581Srgrimes#include "net/bpf.h" 148581Srgrimes#include "net/bpfdesc.h" 149581Srgrimes#endif 150581Srgrimes 151581Srgrimes#if (NBPFILTER > 0) || defined(MULTICAST) 152581Srgrimes#define FILTER 153581Srgrimesstatic struct mbuf *last_not_for_us; 154581Srgrimes#endif 155581Srgrimes 156581Srgrimes#ifdef DEBUG 157581Srgrimes#define IED_RINT 1 158581Srgrimes#define IED_TINT 2 159581Srgrimes#define IED_RNR 4 160581Srgrimes#define IED_CNA 8 161581Srgrimes#define IED_READFRAME 16 162581Srgrimesint ie_debug = IED_RNR; 163581Srgrimes#endif 164581Srgrimes 165581Srgrimes#ifndef ETHERMINLEN 166581Srgrimes#define ETHERMINLEN 60 167581Srgrimes#endif 168581Srgrimes 169581Srgrimes#define IE_BUF_LEN 1512 /* length of transmit buffer */ 170581Srgrimes 171581Srgrimes/* Forward declaration */ 172581Srgrimesstruct ie_softc; 173581Srgrimes 174581Srgrimesint ieprobe(struct isa_device *dvp); 175581Srgrimesint ieattach(struct isa_device *dvp); 176581Srgrimesint ieinit(int unit); 177581Srgrimesint ieioctl(struct ifnet *ifp, int command, void *data); 178581Srgrimesint iestart(struct ifnet *ifp); 179581Srgrimesstatic void sl_reset_586(int unit); 180581Srgrimesstatic void sl_chan_attn(int unit); 181581Srgrimesint iereset(int unit, int dummy); 182581Srgrimesstatic void ie_readframe(int unit, struct ie_softc *ie, int bufno); 183581Srgrimesstatic void ie_drop_packet_buffer(int unit, struct ie_softc *ie); 184581Srgrimesstatic void sl_read_ether(int unit, unsigned char addr[6]); 185581Srgrimesstatic void find_ie_mem_size(int unit); 186581Srgrimesstatic int command_and_wait(int unit, int command, void volatile *pcmd, int); 187581Srgrimesstatic int ierint(int unit, struct ie_softc *ie); 188581Srgrimesstatic int ietint(int unit, struct ie_softc *ie); 189581Srgrimesstatic int iernr(int unit, struct ie_softc *ie); 190581Srgrimesstatic void start_receiver(int unit); 191581Srgrimesstatic int ieget(int, struct ie_softc *, struct mbuf **, 192581Srgrimes struct ether_header *, int *); 193581Srgrimesstatic caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie); 194581Srgrimesstatic int mc_setup(int, caddr_t, volatile struct ie_sys_ctl_block *); 195581Srgrimes#ifdef MULTICAST 196581Srgrimesstatic void ie_mc_reset(int unit); 197581Srgrimes#endif 198581Srgrimes 199581Srgrimes#ifdef DEBUG 200581Srgrimesvoid print_rbd(volatile struct ie_recv_buf_desc *rbd); 201581Srgrimes 202581Srgrimesint in_ierint = 0; 203581Srgrimesint in_ietint = 0; 204581Srgrimes#endif 205581Srgrimes 206581Srgrimes/* 207581Srgrimes * This tells the autoconf code how to set us up. 208581Srgrimes */ 209581Srgrimesstruct isa_driver iedriver = { 210581Srgrimes ieprobe, ieattach, "ie", 211581Srgrimes}; 212581Srgrimes 213581Srgrimesenum ie_hardware { 214581Srgrimes IE_STARLAN10, 215581Srgrimes IE_EN100, 216581Srgrimes IE_SLFIBER, 217581Srgrimes IE_UNKNOWN 218581Srgrimes}; 219581Srgrimes 220581Srgrimesconst char *ie_hardware_names[] = { 221581Srgrimes "StarLAN 10", 222581Srgrimes "EN100", 223581Srgrimes "StarLAN Fiber", 224581Srgrimes "Unknown" 225581Srgrimes}; 226581Srgrimes 227581Srgrimes/* 228581Srgrimessizeof(iscp) == 1+1+2+4 == 8 229581Srgrimessizeof(scb) == 2+2+2+2+2+2+2+2 == 16 230581SrgrimesNFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 231581Srgrimessizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 232581Srgrimessizeof(transmit buffer) == 1512 233581Srgrimessizeof(transmit buffer desc) == 8 234581Srgrimes----- 235581Srgrimes1946 236581Srgrimes 237581SrgrimesNBUFFS * sizeof(rbd) == NBUFFS*(2+2+4+2+2) == NBUFFS*12 238581SrgrimesNBUFFS * IE_RBUF_SIZE == NBUFFS*256 239581Srgrimes 240581SrgrimesNBUFFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 241581Srgrimes 242581SrgrimesWith NBUFFS == 48, this leaves us 1574 bytes for another command or 243581Srgrimesmore buffers. Another transmit command would be 18+8+1512 == 1538 244581Srgrimes---just barely fits! 245581Srgrimes 246581SrgrimesObviously all these would have to be reduced for smaller memory sizes. 247581SrgrimesWith a larger memory, it would be possible to roughly double the number of 248581Srgrimesboth transmit and receive buffers. 249581Srgrimes*/ 250581Srgrimes 251581Srgrimes#define NFRAMES 16 /* number of frames to allow for receive */ 252581Srgrimes#define NBUFFS 48 /* number of buffers to allocate */ 253581Srgrimes#define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ 254581Srgrimes 255581Srgrimes/* 256581Srgrimes * Ethernet status, per interface. 257581Srgrimes */ 258581Srgrimesstruct ie_softc { 259581Srgrimes struct arpcom arpcom; 260581Srgrimes void (*ie_reset_586)(int); 261581Srgrimes void (*ie_chan_attn)(int); 262581Srgrimes enum ie_hardware hard_type; 263581Srgrimes int hard_vers; 264581Srgrimes 265581Srgrimes u_short port; 266581Srgrimes caddr_t iomem; 267581Srgrimes caddr_t iomembot; 268581Srgrimes unsigned iosize; 269581Srgrimes 270581Srgrimes int want_mcsetup; 271581Srgrimes int promisc; 272581Srgrimes volatile struct ie_int_sys_conf_ptr *iscp; 273581Srgrimes volatile struct ie_sys_ctl_block *scb; 274581Srgrimes volatile struct ie_recv_frame_desc *rframes[NFRAMES]; 275581Srgrimes volatile struct ie_recv_buf_desc *rbuffs[NBUFFS]; 276581Srgrimes volatile char *cbuffs[NBUFFS]; 277581Srgrimes int rfhead, rftail, rbhead, rbtail; 278581Srgrimes 279581Srgrimes volatile struct ie_xmit_cmd *xmit_cmds[2]; 280581Srgrimes volatile struct ie_xmit_buf *xmit_buffs[2]; 281581Srgrimes int xmit_count; 282581Srgrimes u_char *xmit_cbuffs[2]; 283581Srgrimes 284581Srgrimes struct ie_en_addr mcast_addrs[MAXMCAST + 1]; 285581Srgrimes int mcast_count; 286581Srgrimes 287581Srgrimes#if NBPFILTER > 0 288581Srgrimes caddr_t ie_bpf; 289581Srgrimes#endif 290581Srgrimes 291581Srgrimes} ie_softc[NIE]; 292581Srgrimes 293581Srgrimes#define MK_24(base, ptr) ((caddr_t)((u_long)ptr - (u_long)base)) 294581Srgrimes#define MK_16(base, ptr) ((u_short)(u_long)MK_24(base, ptr)) 295581Srgrimes 296581Srgrimes#define PORT ie_softc[unit].port 297581Srgrimes#define MEM ie_softc[unit].iomem 298581Srgrimes 299581Srgrimes 300581Srgrimesint ieprobe(dvp) 301581Srgrimes struct isa_device *dvp; 302581Srgrimes{ 303581Srgrimes int unit = dvp->id_unit; 304581Srgrimes u_char c; 305581Srgrimes 306581Srgrimes ie_softc[unit].port = dvp->id_iobase; 307581Srgrimes ie_softc[unit].iomembot = dvp->id_maddr; 308581Srgrimes ie_softc[unit].iomem = 0; 309581Srgrimes 310581Srgrimes c = inb(PORT + IEATT_REVISION); 311581Srgrimes switch(SL_BOARD(c)) { 312581Srgrimes case SL10_BOARD: 313581Srgrimes ie_softc[unit].hard_type = IE_STARLAN10; 314581Srgrimes ie_softc[unit].ie_reset_586 = sl_reset_586; 315581Srgrimes ie_softc[unit].ie_chan_attn = sl_chan_attn; 316581Srgrimes break; 317581Srgrimes case EN100_BOARD: 318581Srgrimes ie_softc[unit].hard_type = IE_EN100; 319581Srgrimes ie_softc[unit].ie_reset_586 = sl_reset_586; 320581Srgrimes ie_softc[unit].ie_chan_attn = sl_chan_attn; 321581Srgrimes break; 322581Srgrimes case SLFIBER_BOARD: 323581Srgrimes ie_softc[unit].hard_type = IE_SLFIBER; 324581Srgrimes ie_softc[unit].ie_reset_586 = sl_reset_586; 325581Srgrimes ie_softc[unit].ie_chan_attn = sl_chan_attn; 326581Srgrimes break; 327581Srgrimes 328581Srgrimes /* 329581Srgrimes * Anything else is not recognized or cannot be used. 330581Srgrimes */ 331581Srgrimes default: 332581Srgrimes return 0; 333581Srgrimes } 334581Srgrimes 335581Srgrimes ie_softc[unit].hard_vers = SL_REV(c); 336581Srgrimes 337581Srgrimes /* 338581Srgrimes * Divine memory size on-board the card. Ususally 16k. 339581Srgrimes */ 340581Srgrimes find_ie_mem_size(unit); 341581Srgrimes 342581Srgrimes if(!ie_softc[unit].iosize) { 343581Srgrimes return 0; 344581Srgrimes } 345581Srgrimes 346581Srgrimes dvp->id_msize = ie_softc[unit].iosize; 347581Srgrimes 348581Srgrimes switch(ie_softc[unit].hard_type) { 349581Srgrimes case IE_EN100: 350581Srgrimes case IE_STARLAN10: 351581Srgrimes case IE_SLFIBER: 352581Srgrimes sl_read_ether(unit, ie_softc[unit].arpcom.ac_enaddr); 353581Srgrimes break; 354581Srgrimes 355581Srgrimes default: 356581Srgrimes printf("ie%d: unknown AT&T board type code %d\n", unit, 357581Srgrimes ie_softc[unit].hard_type); 358581Srgrimes return 0; 359581Srgrimes } 360581Srgrimes 361581Srgrimes return 1; 362581Srgrimes} 363581Srgrimes 364581Srgrimes/* 365581Srgrimes * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. 366581Srgrimes */ 367581Srgrimesint 368581Srgrimesieattach(dvp) 369581Srgrimes struct isa_device *dvp; 370581Srgrimes{ 371581Srgrimes int unit = dvp->id_unit; 372581Srgrimes struct ie_softc *ie = &ie_softc[unit]; 373581Srgrimes struct ifnet *ifp = &ie->arpcom.ac_if; 374581Srgrimes 375581Srgrimes ifp->if_unit = unit; 376581Srgrimes ifp->if_name = iedriver.name; 377581Srgrimes ifp->if_mtu = ETHERMTU; 378581Srgrimes printf("<%s R%d> ethernet address %s", 379581Srgrimes ie_hardware_names[ie_softc[unit].hard_type], 380581Srgrimes ie_softc[unit].hard_vers + 1, 381581Srgrimes ether_sprintf(ie->arpcom.ac_enaddr)); 382581Srgrimes 383581Srgrimes ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; 384581Srgrimes#ifdef MULTICAST 385581Srgrimes ifp->if_flags |= IFF_MULTICAST; 386581Srgrimes#endif /* MULTICAST */ 387581Srgrimes 388581Srgrimes ifp->if_init = ieinit; 389581Srgrimes ifp->if_output = ether_output; 390581Srgrimes ifp->if_start = iestart; 391581Srgrimes ifp->if_ioctl = ieioctl; 392581Srgrimes ifp->if_reset = iereset; 393581Srgrimes ifp->if_type = IFT_ETHER; 394581Srgrimes ifp->if_addrlen = 6; 395581Srgrimes ifp->if_hdrlen = 14; 396581Srgrimes 397581Srgrimes#if NBPFILTER > 0 398581Srgrimes printf("\n"); 399581Srgrimes bpfattach(&ie_softc[unit].ie_bpf, ifp, DLT_EN10MB, 400581Srgrimes sizeof(struct ether_header)); 401581Srgrimes#endif 402581Srgrimes 403581Srgrimes if_attach(ifp); 404581Srgrimes { 405581Srgrimes struct ifaddr *ifa = ifp->if_addrlist; 406581Srgrimes struct sockaddr_dl *sdl; 407581Srgrimes while(ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK) 408581Srgrimes ifa = ifa->ifa_next; 409581Srgrimes 410581Srgrimes if(!ifa || !ifa->ifa_addr) return; 411581Srgrimes 412581Srgrimes /* Provide our ether address to the higher layers */ 413581Srgrimes sdl = (struct sockaddr_dl *)ifa->ifa_addr; 414581Srgrimes sdl->sdl_type = IFT_ETHER; 415581Srgrimes sdl->sdl_alen = 6; 416581Srgrimes sdl->sdl_slen = 0; 417581Srgrimes bcopy(ie->arpcom.ac_enaddr, LLADDR(sdl), 6); 418581Srgrimes } 419581Srgrimes} 420581Srgrimes 421581Srgrimes/* 422581Srgrimes * What to do upon receipt of an interrupt. 423581Srgrimes */ 424581Srgrimesint ieintr(unit) 425581Srgrimes int unit; 426581Srgrimes{ 427581Srgrimes register struct ie_softc *ie = &ie_softc[unit]; 428581Srgrimes register u_short status; 429581Srgrimes 430581Srgrimes status = ie->scb->ie_status; 431581Srgrimes 432581Srgrimesloop: 433581Srgrimes if(status & (IE_ST_RECV | IE_ST_RNR)) { 434581Srgrimes#ifdef DEBUG 435581Srgrimes in_ierint++; 436581Srgrimes if(ie_debug & IED_RINT) 437581Srgrimes printf("ie%d: rint\n", unit); 438581Srgrimes#endif 439581Srgrimes ierint(unit, ie); 440581Srgrimes#ifdef DEBUG 441581Srgrimes in_ierint--; 442581Srgrimes#endif 443581Srgrimes } 444581Srgrimes 445581Srgrimes if(status & IE_ST_DONE) { 446581Srgrimes#ifdef DEBUG 447581Srgrimes in_ietint++; 448581Srgrimes if(ie_debug & IED_TINT) 449581Srgrimes printf("ie%d: tint\n", unit); 450581Srgrimes#endif 451581Srgrimes ietint(unit, ie); 452581Srgrimes#ifdef DEBUG 453581Srgrimes in_ietint--; 454581Srgrimes#endif 455581Srgrimes } 456581Srgrimes 457581Srgrimes if(status & IE_ST_RNR) { 458581Srgrimes#ifdef DEBUG 459581Srgrimes if(ie_debug & IED_RNR) 460581Srgrimes printf("ie%d: rnr\n", unit); 461581Srgrimes#endif 462581Srgrimes iernr(unit, ie); 463581Srgrimes } 464581Srgrimes 465581Srgrimes#ifdef DEBUG 466581Srgrimes if((status & IE_ST_ALLDONE) 467581Srgrimes && (ie_debug & IED_CNA)) 468581Srgrimes printf("ie%d: cna\n", unit); 469581Srgrimes#endif 470581Srgrimes 471581Srgrimes /* Don't ack interrupts which we didn't receive */ 472581Srgrimes ie_ack(ie->scb, IE_ST_WHENCE & status, unit, ie->ie_chan_attn); 473581Srgrimes 474581Srgrimes if((status = ie->scb->ie_status) & IE_ST_WHENCE) 475581Srgrimes goto loop; 476581Srgrimes 477581Srgrimes return unit; 478581Srgrimes} 479581Srgrimes 480581Srgrimes/* 481581Srgrimes * Process a received-frame interrupt. 482581Srgrimes */ 483581Srgrimesstatic int ierint(unit, ie) 484581Srgrimes int unit; 485581Srgrimes struct ie_softc *ie; 486581Srgrimes{ 487581Srgrimes int i, status; 488581Srgrimes static int timesthru = 1024; 489581Srgrimes 490581Srgrimes i = ie->rfhead; 491581Srgrimes while(1) { 492581Srgrimes status = ie->rframes[i]->ie_fd_status; 493581Srgrimes 494581Srgrimes if((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { 495581Srgrimes ie->arpcom.ac_if.if_ipackets++; 496581Srgrimes if(!--timesthru) { 497581Srgrimes ie->arpcom.ac_if.if_ierrors += ie->scb->ie_err_crc + ie->scb->ie_err_align + 498581Srgrimes ie->scb->ie_err_resource + ie->scb->ie_err_overrun; 499581Srgrimes ie->scb->ie_err_crc = 0; 500581Srgrimes ie->scb->ie_err_align = 0; 501581Srgrimes ie->scb->ie_err_resource = 0; 502581Srgrimes ie->scb->ie_err_overrun = 0; 503581Srgrimes timesthru = 1024; 504581Srgrimes } 505581Srgrimes ie_readframe(unit, ie, i); 506581Srgrimes } else { 507581Srgrimes if(status & IE_FD_RNR) { 508581Srgrimes if(!(ie->scb->ie_status & IE_RU_READY)) { 509581Srgrimes ie->rframes[0]->ie_fd_next = MK_16(MEM, ie->rbuffs[0]); 510581Srgrimes ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); 511581Srgrimes command_and_wait(unit, IE_RU_START, 0, 0); 512581Srgrimes } 513581Srgrimes } 514581Srgrimes break; 515581Srgrimes } 516581Srgrimes i = (i + 1) % NFRAMES; 517581Srgrimes } 518581Srgrimes return 0; 519581Srgrimes} 520581Srgrimes 521581Srgrimes/* 522581Srgrimes * Process a command-complete interrupt. These are only generated by 523581Srgrimes * the transmission of frames. This routine is deceptively simple, since 524581Srgrimes * most of the real work is done by iestart(). 525581Srgrimes */ 526581Srgrimesstatic int ietint(unit, ie) 527581Srgrimes int unit; 528581Srgrimes struct ie_softc *ie; 529581Srgrimes{ 530581Srgrimes int status; 531581Srgrimes int i; 532581Srgrimes 533581Srgrimes ie->arpcom.ac_if.if_timer = 0; 534581Srgrimes ie->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 535581Srgrimes 536581Srgrimes for(i = 0; i < ie->xmit_count; i++) { 537581Srgrimes status = ie->xmit_cmds[i]->ie_xmit_status; 538581Srgrimes 539581Srgrimes if(status & IE_XS_LATECOLL) { 540581Srgrimes printf("ie%d: late collision\n", unit); 541581Srgrimes ie->arpcom.ac_if.if_collisions++; 542581Srgrimes ie->arpcom.ac_if.if_oerrors++; 543581Srgrimes } else if(status & IE_XS_NOCARRIER) { 544581Srgrimes printf("ie%d: no carrier\n", unit); 545581Srgrimes ie->arpcom.ac_if.if_oerrors++; 546581Srgrimes } else if(status & IE_XS_LOSTCTS) { 547581Srgrimes printf("ie%d: lost CTS\n", unit); 548581Srgrimes ie->arpcom.ac_if.if_oerrors++; 549581Srgrimes } else if(status & IE_XS_UNDERRUN) { 550581Srgrimes printf("ie%d: DMA underrun\n", unit); 551581Srgrimes ie->arpcom.ac_if.if_oerrors++; 552581Srgrimes } else if(status & IE_XS_EXCMAX) { 553581Srgrimes printf("ie%d: too many collisions\n", unit); 554581Srgrimes ie->arpcom.ac_if.if_collisions += 16; 555581Srgrimes ie->arpcom.ac_if.if_oerrors++; 556581Srgrimes } else { 557581Srgrimes ie->arpcom.ac_if.if_opackets++; 558581Srgrimes ie->arpcom.ac_if.if_collisions += status & IE_XS_MAXCOLL; 559581Srgrimes } 560581Srgrimes } 561581Srgrimes ie->xmit_count = 0; 562581Srgrimes 563581Srgrimes /* 564581Srgrimes * If multicast addresses were added or deleted while we were transmitting, 565581Srgrimes * ie_mc_reset() set the want_mcsetup flag indicating that we should do it. 566581Srgrimes */ 567581Srgrimes if(ie->want_mcsetup) { 568581Srgrimes mc_setup(unit, (caddr_t)ie->xmit_cbuffs[0], ie->scb); 569581Srgrimes ie->want_mcsetup = 0; 570581Srgrimes } 571581Srgrimes 572581Srgrimes /* Wish I knew why this seems to be necessary... */ 573581Srgrimes ie->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; 574581Srgrimes 575581Srgrimes iestart(&ie->arpcom.ac_if); 576581Srgrimes return 0; /* shouldn't be necessary */ 577581Srgrimes} 578581Srgrimes 579581Srgrimes/* 580581Srgrimes * Process a receiver-not-ready interrupt. I believe that we get these 581581Srgrimes * when there aren't enough buffers to go around. For now (FIXME), we 582581Srgrimes * just restart the receiver, and hope everything's ok. 583581Srgrimes */ 584581Srgrimesstatic int iernr(unit, ie) 585581Srgrimes int unit; 586581Srgrimes struct ie_softc *ie; 587581Srgrimes{ 588581Srgrimes#ifdef doesnt_work 589581Srgrimes setup_rfa((caddr_t)ie->rframes[0], ie); 590581Srgrimes 591581Srgrimes ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); 592581Srgrimes command_and_wait(unit, IE_RU_START, 0, 0); 593581Srgrimes#else 594581Srgrimes /* This doesn't work either, but it doesn't hang either. */ 595581Srgrimes command_and_wait(unit, IE_RU_DISABLE, 0, 0); /* just in case */ 596581Srgrimes setup_rfa((caddr_t)ie->rframes[0], ie); /* ignore cast-qual */ 597581Srgrimes 598581Srgrimes ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); 599581Srgrimes command_and_wait(unit, IE_RU_START, 0, 0); /* was ENABLE */ 600581Srgrimes 601581Srgrimes#endif 602581Srgrimes ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); 603581Srgrimes 604581Srgrimes ie->arpcom.ac_if.if_ierrors++; 605581Srgrimes return 0; 606581Srgrimes} 607581Srgrimes 608581Srgrimes#ifdef FILTER 609581Srgrimes/* 610581Srgrimes * Compare two Ether/802 addresses for equality, inlined and 611581Srgrimes * unrolled for speed. I'd love to have an inline assembler 612581Srgrimes * version of this... 613581Srgrimes */ 614581Srgrimesstatic inline int ether_equal(u_char *one, u_char *two) { 615581Srgrimes if(one[0] != two[0]) return 0; 616581Srgrimes if(one[1] != two[1]) return 0; 617581Srgrimes if(one[2] != two[2]) return 0; 618581Srgrimes if(one[3] != two[3]) return 0; 619581Srgrimes if(one[4] != two[4]) return 0; 620581Srgrimes if(one[5] != two[5]) return 0; 621581Srgrimes return 1; 622581Srgrimes} 623581Srgrimes 624581Srgrimes/* 625581Srgrimes * Check for a valid address. to_bpf is filled in with one of the following: 626581Srgrimes * 0 -> BPF doesn't get this packet 627581Srgrimes * 1 -> BPF does get this packet 628581Srgrimes * 2 -> BPF does get this packet, but we don't 629581Srgrimes * Return value is true if the packet is for us, and false otherwise. 630581Srgrimes * 631581Srgrimes * This routine is a mess, but it's also critical that it be as fast 632581Srgrimes * as possible. It could be made cleaner if we can assume that the 633581Srgrimes * only client which will fiddle with IFF_PROMISC is BPF. This is 634581Srgrimes * probably a good assumption, but we do not make it here. (Yet.) 635581Srgrimes */ 636581Srgrimesstatic inline int check_eh(struct ie_softc *ie, 637581Srgrimes struct ether_header *eh, 638581Srgrimes int *to_bpf) { 639581Srgrimes int i; 640581Srgrimes 641581Srgrimes switch(ie->promisc) { 642581Srgrimes case IFF_ALLMULTI: 643581Srgrimes /* 644581Srgrimes * Receiving all multicasts, but no unicasts except those destined for us. 645581Srgrimes */ 646581Srgrimes#if NBPFILTER > 0 647581Srgrimes *to_bpf = (ie->ie_bpf != 0); /* BPF gets this packet if anybody cares */ 648581Srgrimes#endif 649581Srgrimes if(eh->ether_dhost[0] & 1) { 650581Srgrimes return 1; 651581Srgrimes } 652581Srgrimes if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; 653581Srgrimes return 0; 654581Srgrimes 655581Srgrimes case IFF_PROMISC: 656581Srgrimes /* 657581Srgrimes * Receiving all packets. These need to be passed on to BPF. 658581Srgrimes */ 659581Srgrimes#if NBPFILTER > 0 660581Srgrimes *to_bpf = (ie->ie_bpf != 0); 661581Srgrimes#endif 662581Srgrimes /* If for us, accept and hand up to BPF */ 663581Srgrimes if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; 664581Srgrimes 665581Srgrimes#if NBPFILTER > 0 666581Srgrimes if(*to_bpf) *to_bpf = 2; /* we don't need to see it */ 667581Srgrimes#endif 668581Srgrimes 669581Srgrimes#ifdef MULTICAST 670581Srgrimes /* 671581Srgrimes * Not a multicast, so BPF wants to see it but we don't. 672581Srgrimes */ 673581Srgrimes if(!(eh->ether_dhost[0] & 1)) return 1; 674581Srgrimes 675581Srgrimes /* 676581Srgrimes * If it's one of our multicast groups, accept it and pass it 677581Srgrimes * up. 678581Srgrimes */ 679581Srgrimes for(i = 0; i < ie->mcast_count; i++) { 680581Srgrimes if(ether_equal(eh->ether_dhost, (u_char *)&ie->mcast_addrs[i])) { 681581Srgrimes#if NBPFILTER > 0 682581Srgrimes if(*to_bpf) *to_bpf = 1; 683581Srgrimes#endif 684581Srgrimes return 1; 685581Srgrimes } 686581Srgrimes } 687581Srgrimes#endif /* MULTICAST */ 688581Srgrimes return 1; 689581Srgrimes 690581Srgrimes case IFF_ALLMULTI | IFF_PROMISC: 691581Srgrimes /* 692581Srgrimes * Acting as a multicast router, and BPF running at the same time. 693581Srgrimes * Whew! (Hope this is a fast machine...) 694581Srgrimes */ 695581Srgrimes#if NBPFILTER > 0 696581Srgrimes *to_bpf = (ie->ie_bpf != 0); 697581Srgrimes#endif 698581Srgrimes /* We want to see multicasts. */ 699581Srgrimes if(eh->ether_dhost[0] & 1) return 1; 700581Srgrimes 701581Srgrimes /* We want to see our own packets */ 702581Srgrimes if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; 703581Srgrimes 704581Srgrimes /* Anything else goes to BPF but nothing else. */ 705581Srgrimes#if NBPFILTER > 0 706581Srgrimes if(*to_bpf) *to_bpf = 2; 707581Srgrimes#endif 708581Srgrimes return 1; 709581Srgrimes 710581Srgrimes default: 711581Srgrimes /* 712581Srgrimes * Only accept unicast packets destined for us, or multicasts 713581Srgrimes * for groups that we belong to. For now, we assume that the 714581Srgrimes * '586 will only return packets that we asked it for. This 715581Srgrimes * isn't strictly true (it uses hashing for the multicast filter), 716581Srgrimes * but it will do in this case, and we want to get out of here 717581Srgrimes * as quickly as possible. 718581Srgrimes */ 719581Srgrimes#if NBPFILTER > 0 720581Srgrimes *to_bpf = (ie->ie_bpf != 0); 721581Srgrimes#endif 722581Srgrimes return 1; 723581Srgrimes } 724581Srgrimes return 0; 725581Srgrimes} 726581Srgrimes#endif /* FILTER */ 727581Srgrimes 728581Srgrimes/* 729581Srgrimes * We want to isolate the bits that have meaning... This assumes that 730581Srgrimes * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds 731581Srgrimes * the size of the buffer, then we are screwed anyway. 732581Srgrimes */ 733581Srgrimesstatic inline int ie_buflen(struct ie_softc *ie, int head) { 734581Srgrimes return (ie->rbuffs[head]->ie_rbd_actual 735581Srgrimes & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); 736581Srgrimes} 737581Srgrimes 738581Srgrimesstatic inline int ie_packet_len(int unit, struct ie_softc *ie) { 739581Srgrimes int i; 740581Srgrimes int head = ie->rbhead; 741581Srgrimes int acc = 0; 742581Srgrimes 743581Srgrimes do { 744581Srgrimes if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { 745581Srgrimes#ifdef DEBUG 746581Srgrimes print_rbd(ie->rbuffs[ie->rbhead]); 747581Srgrimes#endif 748581Srgrimes log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", 749581Srgrimes unit, ie->rbhead); 750581Srgrimes iereset(unit, 0); 751581Srgrimes return -1; 752581Srgrimes } 753581Srgrimes 754581Srgrimes i = ie->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; 755581Srgrimes 756581Srgrimes acc += ie_buflen(ie, head); 757581Srgrimes head = (head + 1) % NBUFFS; 758581Srgrimes } while(!i); 759581Srgrimes 760581Srgrimes return acc; 761581Srgrimes} 762581Srgrimes 763581Srgrimes/* 764581Srgrimes * Read data off the interface, and turn it into an mbuf chain. 765581Srgrimes * 766581Srgrimes * This code is DRAMATICALLY different from the previous version; this 767581Srgrimes * version tries to allocate the entire mbuf chain up front, given the 768581Srgrimes * length of the data available. This enables us to allocate mbuf 769581Srgrimes * clusters in many situations where before we would have had a long 770581Srgrimes * chain of partially-full mbufs. This should help to speed up the 771581Srgrimes * operation considerably. (Provided that it works, of course.) 772581Srgrimes */ 773581Srgrimesstatic inline int ieget(unit, ie, mp, ehp, to_bpf) 774581Srgrimes int unit; 775581Srgrimes struct ie_softc *ie; 776581Srgrimes struct mbuf **mp; 777581Srgrimes struct ether_header *ehp; 778581Srgrimes int *to_bpf; 779581Srgrimes{ 780581Srgrimes struct mbuf *m, *top, **mymp; 781581Srgrimes int i; 782581Srgrimes int offset; 783581Srgrimes int totlen, resid; 784581Srgrimes int thismboff; 785581Srgrimes int head; 786581Srgrimes 787581Srgrimes totlen = ie_packet_len(unit, ie); 788581Srgrimes if(totlen <= 0) return -1; 789581Srgrimes 790581Srgrimes i = ie->rbhead; 791581Srgrimes 792581Srgrimes /* 793581Srgrimes * Snarf the Ethernet header. 794581Srgrimes */ 795581Srgrimes bcopy((caddr_t)ie->cbuffs[i], (caddr_t)ehp, sizeof *ehp); 796581Srgrimes /* ignore cast-qual warning here */ 797581Srgrimes 798581Srgrimes /* 799581Srgrimes * As quickly as possible, check if this packet is for us. 800581Srgrimes * If not, don't waste a single cycle copying the rest of the 801581Srgrimes * packet in. 802581Srgrimes * This is only a consideration when FILTER is defined; i.e., when 803581Srgrimes * we are either running BPF or doing multicasting. 804581Srgrimes */ 805581Srgrimes#ifdef FILTER 806581Srgrimes if(!check_eh(ie, ehp, to_bpf)) { 807581Srgrimes ie_drop_packet_buffer(unit, ie); 808581Srgrimes ie->arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */ 809581Srgrimes return -1; 810581Srgrimes } 811581Srgrimes#endif 812581Srgrimes totlen -= (offset = sizeof *ehp); 813581Srgrimes 814581Srgrimes MGETHDR(*mp, M_DONTWAIT, MT_DATA); 815581Srgrimes if(!*mp) { 816581Srgrimes ie_drop_packet_buffer(unit, ie); 817581Srgrimes return -1; 818581Srgrimes } 819581Srgrimes 820581Srgrimes m = *mp; 821581Srgrimes m->m_pkthdr.rcvif = &ie->arpcom.ac_if; 822581Srgrimes m->m_len = MHLEN; 823581Srgrimes resid = m->m_pkthdr.len = totlen; 824581Srgrimes top = 0; 825581Srgrimes mymp = ⊤ 826581Srgrimes 827581Srgrimes /* 828581Srgrimes * This loop goes through and allocates mbufs for all the data we will 829581Srgrimes * be copying in. It does not actually do the copying yet. 830581Srgrimes */ 831581Srgrimes do { /* while(resid > 0) */ 832581Srgrimes /* 833581Srgrimes * Try to allocate an mbuf to hold the data that we have. If we 834581Srgrimes * already allocated one, just get another one and stick it on the 835581Srgrimes * end (eventually). If we don't already have one, try to allocate 836581Srgrimes * an mbuf cluster big enough to hold the whole packet, if we think it's 837581Srgrimes * reasonable, or a single mbuf which may or may not be big enough. 838581Srgrimes * Got that? 839581Srgrimes */ 840581Srgrimes if(top) { 841581Srgrimes MGET(m, M_DONTWAIT, MT_DATA); 842581Srgrimes if(!m) { 843581Srgrimes m_freem(top); 844581Srgrimes ie_drop_packet_buffer(unit, ie); 845581Srgrimes return -1; 846581Srgrimes } 847581Srgrimes m->m_len = MLEN; 848581Srgrimes } 849581Srgrimes 850581Srgrimes if(resid >= MINCLSIZE) { 851581Srgrimes MCLGET(m, M_DONTWAIT); 852581Srgrimes if(m->m_flags & M_EXT) 853581Srgrimes m->m_len = min(resid, MCLBYTES); 854581Srgrimes } else { 855581Srgrimes if(resid < m->m_len) { 856581Srgrimes if(!top && resid + max_linkhdr <= m->m_len) 857581Srgrimes m->m_data += max_linkhdr; 858581Srgrimes m->m_len = resid; 859581Srgrimes } 860581Srgrimes } 861581Srgrimes resid -= m->m_len; 862581Srgrimes *mymp = m; 863581Srgrimes mymp = &m->m_next; 864581Srgrimes } while(resid > 0); 865581Srgrimes 866581Srgrimes resid = totlen; 867581Srgrimes m = top; 868581Srgrimes thismboff = 0; 869581Srgrimes head = ie->rbhead; 870581Srgrimes 871581Srgrimes /* 872581Srgrimes * Now we take the mbuf chain (hopefully only one mbuf most of the 873581Srgrimes * time) and stuff the data into it. There are no possible failures 874581Srgrimes * at or after this point. 875581Srgrimes */ 876581Srgrimes while(resid > 0) { /* while there's stuff left */ 877581Srgrimes int thislen = ie_buflen(ie, head) - offset; 878581Srgrimes 879581Srgrimes /* 880581Srgrimes * If too much data for the current mbuf, then fill the current one 881581Srgrimes * up, go to the next one, and try again. 882581Srgrimes */ 883581Srgrimes if(thislen > m->m_len - thismboff) { 884581Srgrimes int newlen = m->m_len - thismboff; 885581Srgrimes bcopy((caddr_t)(ie->cbuffs[head] + offset), 886581Srgrimes mtod(m, caddr_t) + thismboff, (unsigned)newlen); 887581Srgrimes /* ignore cast-qual warning */ 888581Srgrimes m = m->m_next; 889581Srgrimes thismboff = 0; /* new mbuf, so no offset */ 890581Srgrimes offset += newlen; /* we are now this far into the packet */ 891581Srgrimes resid -= newlen; /* so there is this much left to get */ 892581Srgrimes continue; 893581Srgrimes } 894581Srgrimes 895581Srgrimes /* 896581Srgrimes * If there is more than enough space in the mbuf to hold the 897581Srgrimes * contents of this buffer, copy everything in, advance pointers, 898581Srgrimes * and so on. 899581Srgrimes */ 900581Srgrimes if(thislen < m->m_len - thismboff) { 901581Srgrimes bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ 902581Srgrimes mtod(m, caddr_t) + thismboff, (unsigned)thislen); 903581Srgrimes thismboff += thislen; /* we are this far into the mbuf */ 904581Srgrimes resid -= thislen; /* and this much is left */ 905581Srgrimes goto nextbuf; 906581Srgrimes } 907581Srgrimes 908581Srgrimes /* 909581Srgrimes * Otherwise, there is exactly enough space to put this buffer's 910581Srgrimes * contents into the current mbuf. Do the combination of the above 911581Srgrimes * actions. 912581Srgrimes */ 913581Srgrimes bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ 914581Srgrimes mtod(m, caddr_t) + thismboff, (unsigned)thislen); 915581Srgrimes m = m->m_next; 916581Srgrimes thismboff = 0; /* new mbuf, start at the beginning */ 917581Srgrimes resid -= thislen; /* and we are this far through */ 918581Srgrimes 919581Srgrimes /* 920581Srgrimes * Advance all the pointers. We can get here from either of the 921581Srgrimes * last two cases, but never the first. 922581Srgrimes */ 923581Srgrimesnextbuf: 924581Srgrimes offset = 0; 925581Srgrimes ie->rbuffs[head]->ie_rbd_actual = 0; 926581Srgrimes ie->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; 927581Srgrimes ie->rbhead = head = (head + 1) % NBUFFS; 928581Srgrimes ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; 929581Srgrimes ie->rbtail = (ie->rbtail + 1) % NBUFFS; 930581Srgrimes } 931581Srgrimes 932581Srgrimes /* 933581Srgrimes * Unless something changed strangely while we were doing the copy, 934581Srgrimes * we have now copied everything in from the shared memory. 935581Srgrimes * This means that we are done. 936581Srgrimes */ 937581Srgrimes return 0; 938581Srgrimes} 939581Srgrimes 940581Srgrimes/* 941581Srgrimes * Read frame NUM from unit UNIT (pre-cached as IE). 942581Srgrimes * 943581Srgrimes * This routine reads the RFD at NUM, and copies in the buffers from 944581Srgrimes * the list of RBD, then rotates the RBD and RFD lists so that the receiver 945581Srgrimes * doesn't start complaining. Trailers are DROPPED---there's no point 946581Srgrimes * in wasting time on confusing code to deal with them. Hopefully, 947581Srgrimes * this machine will never ARP for trailers anyway. 948581Srgrimes */ 949581Srgrimesstatic void ie_readframe(unit, ie, num) 950581Srgrimes int unit; 951581Srgrimes struct ie_softc *ie; 952581Srgrimes int num; /* frame number to read */ 953581Srgrimes{ 954581Srgrimes struct ie_recv_frame_desc rfd; 955581Srgrimes struct mbuf *m = 0; 956581Srgrimes struct ether_header eh; 957581Srgrimes#if NBPFILTER > 0 958581Srgrimes int bpf_gets_it = 0; 959581Srgrimes#endif 960581Srgrimes 961581Srgrimes bcopy((caddr_t)(ie->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc)); 962581Srgrimes 963581Srgrimes /* Immediately advance the RFD list, since we we have copied ours now. */ 964581Srgrimes ie->rframes[num]->ie_fd_status = 0; 965581Srgrimes ie->rframes[num]->ie_fd_last |= IE_FD_LAST; 966581Srgrimes ie->rframes[ie->rftail]->ie_fd_last &= ~IE_FD_LAST; 967581Srgrimes ie->rftail = (ie->rftail + 1) % NFRAMES; 968581Srgrimes ie->rfhead = (ie->rfhead + 1) % NFRAMES; 969581Srgrimes 970581Srgrimes if(rfd.ie_fd_status & IE_FD_OK) { 971581Srgrimes if( 972581Srgrimes#if NBPFILTER > 0 973581Srgrimes ieget(unit, ie, &m, &eh, &bpf_gets_it) 974581Srgrimes#else 975581Srgrimes ieget(unit, ie, &m, &eh, (int *)0) 976581Srgrimes#endif 977581Srgrimes ) { 978581Srgrimes ie->arpcom.ac_if.if_ierrors++; /* this counts as an error */ 979581Srgrimes return; 980581Srgrimes } 981581Srgrimes } 982581Srgrimes 983581Srgrimes#ifdef DEBUG 984581Srgrimes if(ie_debug & IED_READFRAME) { 985581Srgrimes printf("ie%d: frame from ether %s type %x\n", unit, 986581Srgrimes ether_sprintf(eh.ether_shost), (unsigned)eh.ether_type); 987581Srgrimes } 988581Srgrimes if(ntohs(eh.ether_type) > ETHERTYPE_TRAIL 989581Srgrimes && ntohs(eh.ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) 990581Srgrimes printf("received trailer!\n"); 991581Srgrimes#endif 992581Srgrimes 993581Srgrimes if(!m) return; 994581Srgrimes 995581Srgrimes#ifdef FILTER 996581Srgrimes if(last_not_for_us) { 997581Srgrimes m_freem(last_not_for_us); 998581Srgrimes last_not_for_us = 0; 999581Srgrimes } 1000581Srgrimes 1001581Srgrimes#if NBPFILTER > 0 1002581Srgrimes /* 1003581Srgrimes * Check for a BPF filter; if so, hand it up. 1004581Srgrimes * Note that we have to stick an extra mbuf up front, because 1005581Srgrimes * bpf_mtap expects to have the ether header at the front. 1006581Srgrimes * It doesn't matter that this results in an ill-formatted mbuf chain, 1007581Srgrimes * since BPF just looks at the data. (It doesn't try to free the mbuf, 1008581Srgrimes * tho' it will make a copy for tcpdump.) 1009581Srgrimes */ 1010581Srgrimes if(bpf_gets_it) { 1011581Srgrimes struct mbuf m0; 1012581Srgrimes m0.m_len = sizeof eh; 1013581Srgrimes m0.m_data = (caddr_t)&eh; 1014581Srgrimes m0.m_next = m; 1015581Srgrimes 1016581Srgrimes /* Pass it up */ 1017581Srgrimes bpf_mtap(ie->ie_bpf, &m0); 1018581Srgrimes } 1019581Srgrimes /* 1020581Srgrimes * A signal passed up from the filtering code indicating that the 1021581Srgrimes * packet is intended for BPF but not for the protocol machinery. 1022581Srgrimes * We can save a few cycles by not handing it off to them. 1023581Srgrimes */ 1024581Srgrimes if(bpf_gets_it == 2) { 1025581Srgrimes last_not_for_us = m; 1026581Srgrimes return; 1027581Srgrimes } 1028581Srgrimes#endif /* NBPFILTER > 0 */ 1029581Srgrimes /* 1030581Srgrimes * In here there used to be code to check destination addresses upon 1031581Srgrimes * receipt of a packet. We have deleted that code, and replaced it 1032581Srgrimes * with code to check the address much earlier in the cycle, before 1033581Srgrimes * copying the data in; this saves us valuable cycles when operating 1034581Srgrimes * as a multicast router or when using BPF. 1035581Srgrimes */ 1036581Srgrimes#endif /* FILTER */ 1037581Srgrimes 1038581Srgrimes eh.ether_type = ntohs(eh.ether_type); 1039581Srgrimes 1040581Srgrimes /* 1041581Srgrimes * Finally pass this packet up to higher layers. 1042581Srgrimes */ 1043581Srgrimes ether_input(&ie->arpcom.ac_if, &eh, m); 1044581Srgrimes} 1045581Srgrimes 1046581Srgrimesstatic void ie_drop_packet_buffer(int unit, struct ie_softc *ie) { 1047581Srgrimes int i; 1048581Srgrimes 1049581Srgrimes do { 1050581Srgrimes /* 1051581Srgrimes * This means we are somehow out of sync. So, we reset the 1052581Srgrimes * adapter. 1053581Srgrimes */ 1054581Srgrimes if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { 1055581Srgrimes#ifdef DEBUG 1056581Srgrimes print_rbd(ie->rbuffs[ie->rbhead]); 1057581Srgrimes#endif 1058581Srgrimes log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", 1059581Srgrimes unit, ie->rbhead); 1060581Srgrimes iereset(unit, 0); 1061581Srgrimes return; 1062581Srgrimes } 1063581Srgrimes 1064581Srgrimes i = ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_LAST; 1065581Srgrimes 1066581Srgrimes ie->rbuffs[ie->rbhead]->ie_rbd_length |= IE_RBD_LAST; 1067581Srgrimes ie->rbuffs[ie->rbhead]->ie_rbd_actual = 0; 1068581Srgrimes ie->rbhead = (ie->rbhead + 1) % NBUFFS; 1069581Srgrimes ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; 1070581Srgrimes ie->rbtail = (ie->rbtail + 1) % NBUFFS; 1071581Srgrimes } while(!i); 1072581Srgrimes} 1073581Srgrimes 1074581Srgrimes 1075581Srgrimes/* 1076581Srgrimes * Start transmission on an interface. 1077581Srgrimes */ 1078581Srgrimesint iestart(ifp) 1079581Srgrimes struct ifnet *ifp; 1080581Srgrimes{ 1081581Srgrimes struct ie_softc *ie = &ie_softc[ifp->if_unit]; 1082581Srgrimes struct mbuf *m0, *m; 1083581Srgrimes unsigned char *buffer; 1084581Srgrimes u_short len; 1085581Srgrimes /* This is not really volatile, in this routine, but it makes gcc happy. */ 1086581Srgrimes volatile u_short *bptr = &ie->scb->ie_command_list; 1087581Srgrimes 1088581Srgrimes if(!(ifp->if_flags & IFF_RUNNING)) 1089581Srgrimes return 0; 1090581Srgrimes if(ifp->if_flags & IFF_OACTIVE) 1091581Srgrimes return 0; 1092581Srgrimes 1093581Srgrimes do { 1094581Srgrimes IF_DEQUEUE(&ie->arpcom.ac_if.if_snd, m); 1095581Srgrimes if(!m) 1096581Srgrimes break; 1097581Srgrimes 1098581Srgrimes buffer = ie->xmit_cbuffs[ie->xmit_count]; 1099581Srgrimes len = 0; 1100581Srgrimes 1101581Srgrimes for(m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { 1102581Srgrimes bcopy(mtod(m, caddr_t), buffer, m->m_len); 1103581Srgrimes buffer += m->m_len; 1104581Srgrimes len += m->m_len; 1105581Srgrimes } 1106581Srgrimes 1107581Srgrimes m_freem(m0); 1108581Srgrimes len = MAX(len, ETHERMINLEN); 1109581Srgrimes 1110581Srgrimes#if NBPFILTER > 0 1111581Srgrimes /* 1112581Srgrimes * See if bpf is listening on this interface, let it see the packet 1113581Srgrimes * before we commit it to the wire. 1114581Srgrimes */ 1115581Srgrimes if(ie->ie_bpf) 1116581Srgrimes bpf_tap(ie->ie_bpf, ie->xmit_cbuffs[ie->xmit_count], len); 1117581Srgrimes#endif 1118581Srgrimes 1119581Srgrimes ie->xmit_buffs[ie->xmit_count]->ie_xmit_flags = IE_XMIT_LAST | len; 1120581Srgrimes ie->xmit_buffs[ie->xmit_count]->ie_xmit_next = 0xffff; 1121581Srgrimes ie->xmit_buffs[ie->xmit_count]->ie_xmit_buf = 1122581Srgrimes MK_24(ie->iomem, ie->xmit_cbuffs[ie->xmit_count]); 1123581Srgrimes 1124581Srgrimes ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; 1125581Srgrimes ie->xmit_cmds[ie->xmit_count]->ie_xmit_status = 0; 1126581Srgrimes ie->xmit_cmds[ie->xmit_count]->ie_xmit_desc = 1127581Srgrimes MK_16(ie->iomem, ie->xmit_buffs[ie->xmit_count]); 1128581Srgrimes 1129581Srgrimes *bptr = MK_16(ie->iomem, ie->xmit_cmds[ie->xmit_count]); 1130581Srgrimes bptr = &ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_link; 1131581Srgrimes ie->xmit_count++; 1132581Srgrimes } while(ie->xmit_count < 2); 1133581Srgrimes 1134581Srgrimes /* 1135581Srgrimes * If we queued up anything for transmission, send it. 1136581Srgrimes */ 1137581Srgrimes if(ie->xmit_count) { 1138581Srgrimes ie->xmit_cmds[ie->xmit_count - 1]->com.ie_cmd_cmd |= 1139581Srgrimes IE_CMD_LAST | IE_CMD_INTR; 1140581Srgrimes 1141581Srgrimes /* 1142581Srgrimes * By passing the command pointer as a null, we tell 1143581Srgrimes * command_and_wait() to pretend that this isn't an action 1144581Srgrimes * command. I wish I understood what was happening here. 1145581Srgrimes */ 1146581Srgrimes command_and_wait(ifp->if_unit, IE_CU_START, 0, 0); 1147581Srgrimes ifp->if_flags |= IFF_OACTIVE; 1148581Srgrimes } 1149581Srgrimes 1150581Srgrimes return 0; 1151581Srgrimes} 1152581Srgrimes 1153581Srgrimes/* 1154581Srgrimes * Check to see if there's an 82586 out there. 1155581Srgrimes */ 1156581Srgrimesint check_ie_present(unit, where, size) 1157581Srgrimes int unit; 1158581Srgrimes caddr_t where; 1159581Srgrimes unsigned size; 1160581Srgrimes{ 1161581Srgrimes volatile struct ie_sys_conf_ptr *scp; 1162581Srgrimes volatile struct ie_int_sys_conf_ptr *iscp; 1163581Srgrimes volatile struct ie_sys_ctl_block *scb; 1164581Srgrimes u_long realbase; 1165581Srgrimes int s; 1166581Srgrimes 1167581Srgrimes s = splimp(); 1168581Srgrimes 1169581Srgrimes realbase = (u_long)where + size - (1 << 24); 1170581Srgrimes 1171581Srgrimes scp = (volatile struct ie_sys_conf_ptr *)(realbase + IE_SCP_ADDR); 1172581Srgrimes bzero((char *)scp, sizeof *scp); /* ignore cast-qual */ 1173581Srgrimes 1174581Srgrimes /* 1175581Srgrimes * First we put the ISCP at the bottom of memory; this tests to make 1176581Srgrimes * sure that our idea of the size of memory is the same as the controller's. 1177581Srgrimes * This is NOT where the ISCP will be in normal operation. 1178581Srgrimes */ 1179581Srgrimes iscp = (volatile struct ie_int_sys_conf_ptr *)where; 1180581Srgrimes bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ 1181581Srgrimes 1182581Srgrimes scb = (volatile struct ie_sys_ctl_block *)where; 1183581Srgrimes bzero((char *)scb, sizeof *scb); /* ignore cast-qual */ 1184581Srgrimes 1185581Srgrimes scp->ie_bus_use = 0; /* 16-bit */ 1186581Srgrimes scp->ie_iscp_ptr = (caddr_t)((volatile caddr_t)iscp - /* ignore cast-qual */ 1187581Srgrimes (volatile caddr_t)realbase); 1188581Srgrimes 1189581Srgrimes iscp->ie_busy = 1; 1190581Srgrimes iscp->ie_scb_offset = MK_16(realbase, scb) + 256; 1191581Srgrimes 1192581Srgrimes (*ie_softc[unit].ie_reset_586)(unit); 1193581Srgrimes (*ie_softc[unit].ie_chan_attn)(unit); 1194581Srgrimes 1195581Srgrimes DELAY(100); /* wait a while... */ 1196581Srgrimes 1197581Srgrimes if(iscp->ie_busy) { 1198581Srgrimes splx(s); 1199581Srgrimes return 0; 1200581Srgrimes } 1201581Srgrimes 1202581Srgrimes /* 1203581Srgrimes * Now relocate the ISCP to its real home, and reset the controller 1204581Srgrimes * again. 1205581Srgrimes */ 1206581Srgrimes iscp = (void *)Align((caddr_t)(realbase + IE_SCP_ADDR - 1207581Srgrimes sizeof(struct ie_int_sys_conf_ptr))); 1208581Srgrimes bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ 1209581Srgrimes 1210581Srgrimes scp->ie_iscp_ptr = (caddr_t)((caddr_t)iscp - (caddr_t)realbase); 1211581Srgrimes /* ignore cast-qual */ 1212581Srgrimes 1213581Srgrimes iscp->ie_busy = 1; 1214581Srgrimes iscp->ie_scb_offset = MK_16(realbase, scb); 1215581Srgrimes 1216581Srgrimes (*ie_softc[unit].ie_reset_586)(unit); 1217581Srgrimes (*ie_softc[unit].ie_chan_attn)(unit); 1218581Srgrimes 1219581Srgrimes DELAY(100); 1220581Srgrimes 1221581Srgrimes if(iscp->ie_busy) { 1222581Srgrimes splx(s); 1223581Srgrimes return 0; 1224581Srgrimes } 1225581Srgrimes 1226581Srgrimes ie_softc[unit].iosize = size; 1227581Srgrimes ie_softc[unit].iomem = (caddr_t)realbase; 1228581Srgrimes 1229581Srgrimes ie_softc[unit].iscp = iscp; 1230581Srgrimes ie_softc[unit].scb = scb; 1231581Srgrimes 1232581Srgrimes /* 1233581Srgrimes * Acknowledge any interrupts we may have caused... 1234581Srgrimes */ 1235581Srgrimes ie_ack(scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); 1236581Srgrimes splx(s); 1237581Srgrimes 1238581Srgrimes return 1; 1239581Srgrimes} 1240581Srgrimes 1241581Srgrimes/* 1242581Srgrimes * Divine the memory size of ie board UNIT. 1243581Srgrimes * Better hope there's nothing important hiding just below the ie card... 1244581Srgrimes */ 1245581Srgrimesstatic void find_ie_mem_size(unit) 1246581Srgrimes int unit; 1247581Srgrimes{ 1248581Srgrimes unsigned size; 1249581Srgrimes 1250581Srgrimes ie_softc[unit].iosize = 0; 1251581Srgrimes 1252581Srgrimes for(size = 65536; size >= 16384; size -= 16384) { 1253581Srgrimes if(check_ie_present(unit, ie_softc[unit].iomembot, size)) { 1254581Srgrimes return; 1255581Srgrimes } 1256581Srgrimes } 1257581Srgrimes 1258581Srgrimes return; 1259581Srgrimes} 1260581Srgrimes 1261581Srgrimesvoid sl_reset_586(unit) 1262581Srgrimes int unit; 1263581Srgrimes{ 1264581Srgrimes outb(PORT + IEATT_RESET, 0); 1265581Srgrimes} 1266581Srgrimes 1267581Srgrimesvoid sl_chan_attn(unit) 1268581Srgrimes int unit; 1269581Srgrimes{ 1270581Srgrimes outb(PORT + IEATT_ATTN, 0); 1271581Srgrimes} 1272581Srgrimes 1273581Srgrimesvoid sl_read_ether(unit, addr) 1274581Srgrimes int unit; 1275581Srgrimes unsigned char addr[6]; 1276581Srgrimes{ 1277581Srgrimes int i; 1278581Srgrimes 1279581Srgrimes for(i = 0; i < 6; i++) 1280581Srgrimes addr[i] = inb(PORT + i); 1281581Srgrimes} 1282581Srgrimes 1283581Srgrimes 1284581Srgrimesint iereset(unit, dummy) 1285581Srgrimes int unit, dummy; 1286581Srgrimes{ 1287581Srgrimes int s = splimp(); 1288581Srgrimes 1289581Srgrimes if(unit >= NIE) { 1290581Srgrimes splx(s); 1291581Srgrimes return -1; 1292581Srgrimes } 1293581Srgrimes 1294581Srgrimes printf("ie%d: reset\n", unit); 1295581Srgrimes ie_softc[unit].arpcom.ac_if.if_flags &= ~IFF_UP; 1296581Srgrimes ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); 1297581Srgrimes 1298581Srgrimes /* 1299581Srgrimes * Stop i82586 dead in its tracks. 1300581Srgrimes */ 1301581Srgrimes if(command_and_wait(unit, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) 1302581Srgrimes printf("ie%d: abort commands timed out\n", unit); 1303581Srgrimes 1304581Srgrimes if(command_and_wait(unit, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) 1305581Srgrimes printf("ie%d: disable commands timed out\n", unit); 1306581Srgrimes 1307581Srgrimes#ifdef notdef 1308581Srgrimes if(!check_ie_present(unit, ie_softc[unit].iomembot, ie_softc[unit].iosize)) 1309581Srgrimes panic("ie disappeared!\n"); 1310581Srgrimes#endif 1311581Srgrimes 1312581Srgrimes ie_softc[unit].arpcom.ac_if.if_flags |= IFF_UP; 1313581Srgrimes ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); 1314581Srgrimes 1315581Srgrimes splx(s); 1316581Srgrimes return 0; 1317581Srgrimes} 1318581Srgrimes 1319581Srgrimes/* 1320581Srgrimes * This is called if we time out. 1321581Srgrimes */ 1322581Srgrimesstatic int chan_attn_timeout(rock) 1323581Srgrimes caddr_t rock; 1324581Srgrimes{ 1325581Srgrimes *(int *)rock = 1; 1326581Srgrimes return 0; 1327581Srgrimes} 1328581Srgrimes 1329581Srgrimes/* 1330581Srgrimes * Send a command to the controller and wait for it to either 1331581Srgrimes * complete or be accepted, depending on the command. If the 1332581Srgrimes * command pointer is null, then pretend that the command is 1333581Srgrimes * not an action command. If the command pointer is not null, 1334581Srgrimes * and the command is an action command, wait for 1335581Srgrimes * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK 1336581Srgrimes * to become true. 1337581Srgrimes */ 1338581Srgrimesstatic int command_and_wait(unit, cmd, pcmd, mask) 1339581Srgrimes int unit; 1340581Srgrimes int cmd; 1341581Srgrimes volatile void *pcmd; 1342581Srgrimes int mask; 1343581Srgrimes{ 1344581Srgrimes volatile struct ie_cmd_common *cc = pcmd; 1345581Srgrimes volatile int timedout = 0; 1346581Srgrimes extern int hz; 1347581Srgrimes 1348581Srgrimes ie_softc[unit].scb->ie_command = (u_short)cmd; 1349581Srgrimes 1350581Srgrimes if(IE_ACTION_COMMAND(cmd) && pcmd) { 1351581Srgrimes (*ie_softc[unit].ie_chan_attn)(unit); 1352581Srgrimes 1353581Srgrimes /* 1354581Srgrimes * According to the packet driver, the minimum timeout should be 1355581Srgrimes * .369 seconds, which we round up to .37. 1356581Srgrimes */ 1357581Srgrimes timeout(chan_attn_timeout, (caddr_t)&timedout, 37 * hz / 100); 1358581Srgrimes /* ignore cast-qual */ 1359581Srgrimes 1360581Srgrimes /* 1361581Srgrimes * Now spin-lock waiting for status. This is not a very nice 1362581Srgrimes * thing to do, but I haven't figured out how, or indeed if, we 1363581Srgrimes * can put the process waiting for action to sleep. (We may 1364581Srgrimes * be getting called through some other timeout running in the 1365581Srgrimes * kernel.) 1366581Srgrimes */ 1367581Srgrimes while(1) { 1368581Srgrimes if((cc->ie_cmd_status & mask) || timedout) 1369581Srgrimes break; 1370581Srgrimes } 1371581Srgrimes 1372581Srgrimes untimeout(chan_attn_timeout, (caddr_t)&timedout); 1373581Srgrimes /* ignore cast-qual */ 1374581Srgrimes 1375581Srgrimes return timedout; 1376581Srgrimes } else { 1377581Srgrimes 1378581Srgrimes /* 1379581Srgrimes * Otherwise, just wait for the command to be accepted. 1380581Srgrimes */ 1381581Srgrimes (*ie_softc[unit].ie_chan_attn)(unit); 1382581Srgrimes 1383581Srgrimes while(ie_softc[unit].scb->ie_command) 1384581Srgrimes ; /* spin lock */ 1385581Srgrimes 1386581Srgrimes return 0; 1387581Srgrimes } 1388581Srgrimes} 1389581Srgrimes 1390581Srgrimes/* 1391581Srgrimes * Run the time-domain reflectometer... 1392581Srgrimes */ 1393581Srgrimesstatic void run_tdr(unit, cmd) 1394581Srgrimes int unit; 1395581Srgrimes struct ie_tdr_cmd *cmd; 1396581Srgrimes{ 1397581Srgrimes int result; 1398581Srgrimes 1399581Srgrimes cmd->com.ie_cmd_status = 0; 1400581Srgrimes cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; 1401581Srgrimes cmd->com.ie_cmd_link = 0xffff; 1402581Srgrimes cmd->ie_tdr_time = 0; 1403581Srgrimes 1404581Srgrimes ie_softc[unit].scb->ie_command_list = MK_16(MEM, cmd); 1405581Srgrimes cmd->ie_tdr_time = 0; 1406581Srgrimes 1407581Srgrimes if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)) 1408581Srgrimes result = 0x2000; 1409581Srgrimes else 1410581Srgrimes result = cmd->ie_tdr_time; 1411581Srgrimes 1412581Srgrimes ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, 1413581Srgrimes ie_softc[unit].ie_chan_attn); 1414581Srgrimes 1415581Srgrimes if(result & IE_TDR_SUCCESS) 1416581Srgrimes return; 1417581Srgrimes 1418581Srgrimes if(result & IE_TDR_XCVR) { 1419581Srgrimes printf("ie%d: transceiver problem\n", unit); 1420581Srgrimes } else if(result & IE_TDR_OPEN) { 1421581Srgrimes printf("ie%d: TDR detected an open %d clocks away\n", unit, 1422581Srgrimes result & IE_TDR_TIME); 1423581Srgrimes } else if(result & IE_TDR_SHORT) { 1424581Srgrimes printf("ie%d: TDR detected a short %d clocks away\n", unit, 1425581Srgrimes result & IE_TDR_TIME); 1426581Srgrimes } else { 1427581Srgrimes printf("ie%d: TDR returned unknown status %x\n", result); 1428581Srgrimes } 1429581Srgrimes} 1430581Srgrimes 1431581Srgrimesstatic void start_receiver(unit) 1432581Srgrimes int unit; 1433581Srgrimes{ 1434581Srgrimes int s = splimp(); 1435581Srgrimes 1436581Srgrimes ie_softc[unit].scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); 1437581Srgrimes command_and_wait(unit, IE_RU_START, 0, 0); 1438581Srgrimes 1439581Srgrimes ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); 1440581Srgrimes 1441581Srgrimes splx(s); 1442581Srgrimes} 1443581Srgrimes 1444581Srgrimes/* 1445581Srgrimes * Here is a helper routine for iernr() and ieinit(). This sets up 1446581Srgrimes * the RFA. 1447581Srgrimes */ 1448581Srgrimesstatic caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie) { 1449581Srgrimes volatile struct ie_recv_frame_desc *rfd = (void *)ptr; 1450581Srgrimes volatile struct ie_recv_buf_desc *rbd; 1451581Srgrimes int i; 1452581Srgrimes int unit = ie - &ie_softc[0]; 1453581Srgrimes 1454581Srgrimes /* First lay them out */ 1455581Srgrimes for(i = 0; i < NFRAMES; i++) { 1456581Srgrimes ie->rframes[i] = rfd; 1457581Srgrimes bzero((char *)rfd, sizeof *rfd); /* ignore cast-qual */ 1458581Srgrimes rfd++; 1459581Srgrimes } 1460581Srgrimes 1461581Srgrimes ptr = (caddr_t)Align((caddr_t)rfd); /* ignore cast-qual */ 1462581Srgrimes 1463581Srgrimes /* Now link them together */ 1464581Srgrimes for(i = 0; i < NFRAMES; i++) { 1465581Srgrimes ie->rframes[i]->ie_fd_next = 1466581Srgrimes MK_16(MEM, ie->rframes[(i + 1) % NFRAMES]); 1467581Srgrimes } 1468581Srgrimes 1469581Srgrimes /* Finally, set the EOL bit on the last one. */ 1470581Srgrimes ie->rframes[NFRAMES - 1]->ie_fd_last |= IE_FD_LAST; 1471581Srgrimes 1472581Srgrimes /* 1473581Srgrimes * Now lay out some buffers for the incoming frames. Note that 1474581Srgrimes * we set aside a bit of slop in each buffer, to make sure that 1475581Srgrimes * we have enough space to hold a single frame in every buffer. 1476581Srgrimes */ 1477581Srgrimes rbd = (void *)ptr; 1478581Srgrimes 1479581Srgrimes for(i = 0; i < NBUFFS; i++) { 1480581Srgrimes ie->rbuffs[i] = rbd; 1481581Srgrimes bzero((char *)rbd, sizeof *rbd); /* ignore cast-qual */ 1482581Srgrimes ptr = (caddr_t)Align(ptr + sizeof *rbd); 1483581Srgrimes rbd->ie_rbd_length = IE_RBUF_SIZE; 1484581Srgrimes rbd->ie_rbd_buffer = MK_24(MEM, ptr); 1485581Srgrimes ie->cbuffs[i] = (void *)ptr; 1486581Srgrimes ptr += IE_RBUF_SIZE; 1487581Srgrimes rbd = (void *)ptr; 1488581Srgrimes } 1489581Srgrimes 1490581Srgrimes /* Now link them together */ 1491581Srgrimes for(i = 0; i < NBUFFS; i++) { 1492581Srgrimes ie->rbuffs[i]->ie_rbd_next = MK_16(MEM, ie->rbuffs[(i + 1) % NBUFFS]); 1493581Srgrimes } 1494581Srgrimes 1495581Srgrimes /* Tag EOF on the last one */ 1496581Srgrimes ie->rbuffs[NBUFFS - 1]->ie_rbd_length |= IE_RBD_LAST; 1497581Srgrimes 1498581Srgrimes /* We use the head and tail pointers on receive to keep track of 1499581Srgrimes * the order in which RFDs and RBDs are used. */ 1500581Srgrimes ie->rfhead = 0; 1501581Srgrimes ie->rftail = NFRAMES - 1; 1502581Srgrimes ie->rbhead = 0; 1503581Srgrimes ie->rbtail = NBUFFS - 1; 1504581Srgrimes 1505581Srgrimes ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); 1506581Srgrimes ie->rframes[0]->ie_fd_buf_desc = MK_16(MEM, ie->rbuffs[0]); 1507581Srgrimes 1508581Srgrimes ptr = Align(ptr); 1509581Srgrimes return ptr; 1510581Srgrimes} 1511581Srgrimes 1512581Srgrimes/* 1513581Srgrimes * Run the multicast setup command. 1514581Srgrimes * Call at splimp(). 1515581Srgrimes */ 1516581Srgrimesstatic int mc_setup(int unit, caddr_t ptr, 1517581Srgrimes volatile struct ie_sys_ctl_block *scb) { 1518581Srgrimes struct ie_softc *ie = &ie_softc[unit]; 1519581Srgrimes volatile struct ie_mcast_cmd *cmd = (void *)ptr; 1520581Srgrimes 1521581Srgrimes cmd->com.ie_cmd_status = 0; 1522581Srgrimes cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; 1523581Srgrimes cmd->com.ie_cmd_link = 0xffff; 1524581Srgrimes 1525581Srgrimes /* ignore cast-qual */ 1526581Srgrimes bcopy((caddr_t)ie->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs, 1527581Srgrimes ie->mcast_count * sizeof *ie->mcast_addrs); 1528581Srgrimes 1529581Srgrimes cmd->ie_mcast_bytes = ie->mcast_count * 6; /* grrr... */ 1530581Srgrimes 1531581Srgrimes scb->ie_command_list = MK_16(MEM, cmd); 1532581Srgrimes if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) 1533581Srgrimes || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1534581Srgrimes printf("ie%d: multicast address setup command failed\n", unit); 1535581Srgrimes return 0; 1536581Srgrimes } 1537581Srgrimes return 1; 1538581Srgrimes} 1539581Srgrimes 1540581Srgrimes/* 1541581Srgrimes * This routine takes the environment generated by check_ie_present() 1542581Srgrimes * and adds to it all the other structures we need to operate the adapter. 1543581Srgrimes * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, 1544581Srgrimes * starting the receiver unit, and clearing interrupts. 1545581Srgrimes * 1546581Srgrimes * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER. 1547581Srgrimes */ 1548581Srgrimesint ieinit(unit) 1549581Srgrimes int unit; 1550581Srgrimes{ 1551581Srgrimes struct ie_softc *ie = &ie_softc[unit]; 1552581Srgrimes volatile struct ie_sys_ctl_block *scb = ie->scb; 1553581Srgrimes caddr_t ptr; 1554581Srgrimes 1555581Srgrimes ptr = (caddr_t)Align((caddr_t)scb + sizeof *scb); /* ignore cast-qual */ 1556581Srgrimes 1557581Srgrimes /* 1558581Srgrimes * Send the configure command first. 1559581Srgrimes */ 1560581Srgrimes { 1561581Srgrimes volatile struct ie_config_cmd *cmd = (void *)ptr; 1562581Srgrimes 1563581Srgrimes ie_setup_config(cmd, ie->promisc, ie->hard_type == IE_STARLAN10); 1564581Srgrimes cmd->com.ie_cmd_status = 0; 1565581Srgrimes cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; 1566581Srgrimes cmd->com.ie_cmd_link = 0xffff; 1567581Srgrimes 1568581Srgrimes scb->ie_command_list = MK_16(MEM, cmd); 1569581Srgrimes 1570581Srgrimes if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) 1571581Srgrimes || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1572581Srgrimes printf("ie%d: configure command failed\n", unit); 1573581Srgrimes return 0; 1574581Srgrimes } 1575581Srgrimes } 1576581Srgrimes /* 1577581Srgrimes * Now send the Individual Address Setup command. 1578581Srgrimes */ 1579581Srgrimes { 1580581Srgrimes volatile struct ie_iasetup_cmd *cmd = (void *)ptr; 1581581Srgrimes 1582581Srgrimes cmd->com.ie_cmd_status = 0; 1583581Srgrimes cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; 1584581Srgrimes cmd->com.ie_cmd_link = 0xffff; 1585581Srgrimes 1586581Srgrimes bcopy((char *)ie_softc[unit].arpcom.ac_enaddr, (char *)&cmd->ie_address, 1587581Srgrimes sizeof cmd->ie_address); /* ignore cast-qual */ 1588581Srgrimes 1589581Srgrimes scb->ie_command_list = MK_16(MEM, cmd); 1590581Srgrimes if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) 1591581Srgrimes || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { 1592581Srgrimes printf("ie%d: individual address setup command failed\n", unit); 1593581Srgrimes return 0; 1594581Srgrimes } 1595581Srgrimes } 1596581Srgrimes 1597581Srgrimes /* 1598581Srgrimes * Now run the time-domain reflectometer. 1599581Srgrimes */ 1600581Srgrimes run_tdr(unit, (void *)ptr); 1601581Srgrimes 1602581Srgrimes /* 1603581Srgrimes * Acknowledge any interrupts we have generated thus far. 1604581Srgrimes */ 1605581Srgrimes ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); 1606581Srgrimes 1607581Srgrimes /* 1608581Srgrimes * Set up the RFA. 1609581Srgrimes */ 1610581Srgrimes ptr = setup_rfa(ptr, ie); 1611581Srgrimes 1612581Srgrimes /* 1613581Srgrimes * Finally, the transmit command and buffer are the last little bit of work. 1614581Srgrimes */ 1615581Srgrimes ie->xmit_cmds[0] = (void *)ptr; 1616581Srgrimes ptr += sizeof *ie->xmit_cmds[0]; 1617581Srgrimes ptr = Align(ptr); 1618581Srgrimes ie->xmit_buffs[0] = (void *)ptr; 1619581Srgrimes ptr += sizeof *ie->xmit_buffs[0]; 1620581Srgrimes ptr = Align(ptr); 1621581Srgrimes 1622581Srgrimes /* Second transmit command */ 1623581Srgrimes ie->xmit_cmds[1] = (void *)ptr; 1624581Srgrimes ptr += sizeof *ie->xmit_cmds[1]; 1625581Srgrimes ptr = Align(ptr); 1626581Srgrimes ie->xmit_buffs[1] = (void *)ptr; 1627581Srgrimes ptr += sizeof *ie->xmit_buffs[1]; 1628581Srgrimes ptr = Align(ptr); 1629581Srgrimes 1630581Srgrimes /* Both transmit buffers */ 1631581Srgrimes ie->xmit_cbuffs[0] = (void *)ptr; 1632581Srgrimes ptr += IE_BUF_LEN; 1633581Srgrimes ptr = Align(ptr); 1634581Srgrimes ie->xmit_cbuffs[1] = (void *)ptr; 1635581Srgrimes 1636581Srgrimes bzero((caddr_t)ie->xmit_cmds[0], sizeof *ie->xmit_cmds[0]); /* ignore */ 1637581Srgrimes bzero((caddr_t)ie->xmit_buffs[0], sizeof *ie->xmit_buffs[0]); /* cast-qual */ 1638581Srgrimes bzero((caddr_t)ie->xmit_cmds[1], sizeof *ie->xmit_cmds[0]); /* warnings */ 1639581Srgrimes bzero((caddr_t)ie->xmit_buffs[1], sizeof *ie->xmit_buffs[0]); /* here */ 1640581Srgrimes 1641581Srgrimes /* 1642581Srgrimes * This must be coordinated with iestart() and ietint(). 1643581Srgrimes */ 1644581Srgrimes ie->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; 1645581Srgrimes 1646581Srgrimes ie->arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */ 1647581Srgrimes start_receiver(unit); 1648581Srgrimes return 0; 1649581Srgrimes} 1650581Srgrimes 1651581Srgrimesstatic void ie_stop(unit) 1652581Srgrimes int unit; 1653581Srgrimes{ 1654581Srgrimes command_and_wait(unit, IE_RU_DISABLE, 0, 0); 1655581Srgrimes} 1656581Srgrimes 1657581Srgrimesint ieioctl(ifp, command, data) 1658581Srgrimes struct ifnet *ifp; 1659581Srgrimes int command; 1660581Srgrimes void *data; 1661581Srgrimes{ 1662581Srgrimes struct ifaddr *ifa = (struct ifaddr *)data; 1663581Srgrimes struct ie_softc *ie = &ie_softc[ifp->if_unit]; 1664581Srgrimes int s, error = 0; 1665581Srgrimes 1666581Srgrimes s = splimp(); 1667581Srgrimes 1668581Srgrimes switch(command) { 1669581Srgrimes case SIOCSIFADDR: 1670581Srgrimes ifp->if_flags |= IFF_UP; 1671581Srgrimes 1672581Srgrimes switch(ifa->ifa_addr->sa_family) { 1673581Srgrimes#ifdef INET 1674581Srgrimes case AF_INET: 1675581Srgrimes ieinit(ifp->if_unit); 1676581Srgrimes ((struct arpcom *)ifp)->ac_ipaddr = 1677581Srgrimes IA_SIN(ifa)->sin_addr; 1678581Srgrimes arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 1679581Srgrimes break; 1680581Srgrimes#endif /* INET */ 1681581Srgrimes 1682581Srgrimes#ifdef NS 1683581Srgrimes /* This magic copied from if_is.c; I don't use XNS, so I have no 1684581Srgrimes * way of telling if this actually works or not. 1685581Srgrimes */ 1686581Srgrimes case AF_NS: 1687581Srgrimes { 1688581Srgrimes struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 1689581Srgrimes 1690581Srgrimes if(ns_nullhost(*ina)) { 1691581Srgrimes ina->x_host = *(union ns_host *)(ie->arpcom.ac_enaddr); 1692581Srgrimes } else { 1693581Srgrimes ifp->if_flags &= ~IFF_RUNNING; 1694581Srgrimes bcopy((caddr_t)ina->x_host.c_host, 1695581Srgrimes (caddr_t)ie->arpcom.ac_enaddr, 1696581Srgrimes sizeof ie->arpcom.ac_enaddr); 1697581Srgrimes } 1698581Srgrimes 1699581Srgrimes ieinit(ifp->if_unit); 1700581Srgrimes } 1701581Srgrimes break; 1702581Srgrimes#endif /* NS */ 1703581Srgrimes 1704581Srgrimes default: 1705581Srgrimes ieinit(ifp->if_unit); 1706581Srgrimes break; 1707581Srgrimes } 1708581Srgrimes break; 1709581Srgrimes 1710581Srgrimes case SIOCSIFFLAGS: 1711581Srgrimes /* 1712581Srgrimes * Note that this device doesn't have an "all multicast" mode, so we 1713581Srgrimes * must turn on promiscuous mode and do the filtering manually. 1714581Srgrimes */ 1715581Srgrimes if((ifp->if_flags & IFF_UP) == 0 && 1716581Srgrimes (ifp->if_flags & IFF_RUNNING)) { 1717581Srgrimes ifp->if_flags &= ~IFF_RUNNING; 1718581Srgrimes ie_stop(ifp->if_unit); 1719581Srgrimes } else if((ifp->if_flags & IFF_UP) && 1720581Srgrimes (ifp->if_flags & IFF_RUNNING) == 0) { 1721581Srgrimes ie_softc[ifp->if_unit].promisc = 1722581Srgrimes ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); 1723581Srgrimes ieinit(ifp->if_unit); 1724581Srgrimes } else if(ie_softc[ifp->if_unit].promisc ^ 1725581Srgrimes (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { 1726581Srgrimes ie_softc[ifp->if_unit].promisc = 1727581Srgrimes ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); 1728581Srgrimes ieinit(ifp->if_unit); 1729581Srgrimes } 1730581Srgrimes break; 1731581Srgrimes 1732581Srgrimes#ifdef MULTICAST 1733581Srgrimes case SIOCADDMULTI: 1734581Srgrimes case SIOCDELMULTI: 1735581Srgrimes /* 1736581Srgrimes * Update multicast listeners 1737581Srgrimes */ 1738581Srgrimes error = ((command == SIOCADDMULTI) 1739581Srgrimes ? ether_addmulti((struct ifreq *)data, &ie->arpcom) 1740581Srgrimes : ether_delmulti((struct ifreq *)data, &ie->arpcom)); 1741581Srgrimes 1742581Srgrimes if(error == ENETRESET) { 1743581Srgrimes /* reset multicast filtering */ 1744581Srgrimes ie_mc_reset(ifp->if_unit); 1745581Srgrimes error = 0; 1746581Srgrimes } 1747581Srgrimes break; 1748581Srgrimes#endif /* MULTICAST */ 1749581Srgrimes 1750581Srgrimes default: 1751581Srgrimes error = EINVAL; 1752581Srgrimes } 1753581Srgrimes 1754581Srgrimes splx(s); 1755581Srgrimes return error; 1756581Srgrimes} 1757581Srgrimes 1758581Srgrimes#ifdef MULTICAST 1759581Srgrimesstatic void ie_mc_reset(int unit) { 1760581Srgrimes struct ie_softc *ie = &ie_softc[unit]; 1761581Srgrimes struct ether_multi *enm; 1762581Srgrimes struct ether_multistep step; 1763581Srgrimes 1764581Srgrimes /* 1765581Srgrimes * Step through the list of addresses. 1766581Srgrimes */ 1767581Srgrimes ie->mcast_count = 0; 1768581Srgrimes ETHER_FIRST_MULTI(step, &ie->arpcom, enm); 1769581Srgrimes while(enm) { 1770581Srgrimes if(ie->mcast_count >= MAXMCAST 1771581Srgrimes || bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { 1772581Srgrimes ie->arpcom.ac_if.if_flags |= IFF_ALLMULTI; 1773581Srgrimes ieioctl(&ie->arpcom.ac_if, SIOCSIFFLAGS, (void *)0); 1774581Srgrimes goto setflag; 1775581Srgrimes } 1776581Srgrimes 1777581Srgrimes bcopy(enm->enm_addrlo, &(ie->mcast_addrs[ie->mcast_count]), 6); 1778581Srgrimes ie->mcast_count++; 1779581Srgrimes ETHER_NEXT_MULTI(step, enm); 1780581Srgrimes } 1781581Srgrimes 1782581Srgrimessetflag: 1783581Srgrimes ie->want_mcsetup = 1; 1784581Srgrimes} 1785581Srgrimes 1786581Srgrimes#endif 1787581Srgrimes 1788581Srgrimes#ifdef DEBUG 1789581Srgrimesvoid print_rbd(volatile struct ie_recv_buf_desc *rbd) { 1790581Srgrimes printf("RBD at %08lx:\n" 1791581Srgrimes "actual %04x, next %04x, buffer %08x\n" 1792581Srgrimes "length %04x, mbz %04x\n", 1793581Srgrimes (unsigned long)rbd, 1794581Srgrimes rbd->ie_rbd_actual, rbd->ie_rbd_next, rbd->ie_rbd_buffer, 1795581Srgrimes rbd->ie_rbd_length, rbd->mbz); 1796581Srgrimes} 1797581Srgrimes#endif /* DEBUG */ 1798581Srgrimes#endif /* NIE > 0 */ 1799581Srgrimes 1800