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 = &top;
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