386BSD 0.1 development
[unix-history] / usr / src / sys.386bsd / i386 / isa / if_is.c
CommitLineData
d747097c
WJ
1/*-
2 * Copyright (c) 1990, 1991 William F. Jolitz.
3 * Copyright (c) 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * @(#)if_ne.c 7.4 (Berkeley) 5/21/91
35 */
36
37/*
38 * Isolink 4110-2 Ethernet driver
39 */
40
41#include "is.h"
42#if NIS > 0
43
44#include "param.h"
45#include "systm.h"
46#include "mbuf.h"
47#include "buf.h"
48#include "protosw.h"
49#include "socket.h"
50#include "ioctl.h"
51#include "errno.h"
52#include "syslog.h"
53
54#include "net/if.h"
55#include "net/netisr.h"
56#include "net/route.h"
57
58#ifdef INET
59#include "netinet/in.h"
60#include "netinet/in_systm.h"
61#include "netinet/in_var.h"
62#include "netinet/ip.h"
63#include "netinet/if_ether.h"
64#endif
65
66#ifdef NS
67#include "netns/ns.h"
68#include "netns/ns_if.h"
69#endif
70
71#include "i386/isa/isa_device.h"
72#include "i386/isa/if_isreg.h"
73#include "i386/isa/icu.h"
74
75#include "vm/vm.h"
76
77/* Function prototypes */
78int isprobe(), isattach();
79int isioctl(),isinit(),isstart();
80
81struct isa_driver isdriver = {
82 isprobe, isattach, "is",
83};
84
85
86struct mbuf *isget();
87
88#define ETHER_MIN_LEN 64
89
90/*
91 * Ethernet software status per interface.
92 *
93 * Each interface is referenced by a network interface structure,
94 * ns_if, which the routing code uses to locate the interface.
95 * This structure contains the output queue for the interface, its address, ...
96 */
97struct is_softc {
98 struct arpcom ns_ac; /* Ethernet common part */
99#define ns_if ns_ac.ac_if /* network-visible interface */
100#define ns_addr ns_ac.ac_enaddr /* hardware Ethernet address */
101 int last_rd;
102 int last_td;
103 int no_td;
104} is_softc[NIS] ;
105
106struct init_block init_block;
107struct mds *td, *rd;
108unsigned char *rbuf,*tbuf;
109
110int isc;
111
112iswrcsr(port,val)
113 u_short port;
114 u_short val;
115{
116 outw(isc+RAP,port);
117 outw(isc+RDP,val);
118}
119
120u_short isrdcsr(port)
121 u_short port;
122{
123 outw(isc+RAP,port);
124 return(inw(isc+RDP));
125}
126
127isprobe(dvp)
128 struct isa_device *dvp;
129{
130 int val,i,s;
131 register struct is_softc *ns = &is_softc[0];
132
133 isc = dvp->id_iobase;
134 s = splimp();
135
136 /* Stop the lance chip, put it known state */
137 iswrcsr(0,STOP);
138 DELAY(100);
139
140 /* is there a lance? */
141 iswrcsr(3, 0xffff);
142 if (isrdcsr(3) != 7) {
143 isc = 0;
144 return (0);
145 }
146 iswrcsr(3, 0);
147
148 /* Extract board address */
149 for(i=0; i < 6; i++) ns->ns_addr[i] = inb(isc+(i*2));
150
151 splx(s);
152 return (1);
153}
154
155
156
157/*
158 * Reset of interface.
159 */
160isreset(unit, uban)
161 int unit, uban;
162{
163 if (unit >= NIS)
164 return;
165 printf("is%d: reset\n", unit);
166 isinit(unit);
167}
168
169/*
170 * Interface exists: make available by filling in network interface
171 * record. System will initialize the interface when it is ready
172 * to accept packets. We get the ethernet address here.
173 */
174isattach(dvp)
175 struct isa_device *dvp;
176{
177 int unit = dvp->id_unit;
178 register struct is_softc *is = &is_softc[unit];
179 register struct ifnet *ifp = &is->ns_if;
180
181 /* Set up DMA */
182 isa_dmacascade(dvp->id_drq);
183
184 ifp->if_unit = unit;
185 ifp->if_name = isdriver.name ;
186 ifp->if_mtu = ETHERMTU;
187 printf (" ethernet address %s", ether_sprintf(is->ns_addr)) ;
188 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
189 ifp->if_init = isinit;
190 ifp->if_output = ether_output;
191 ifp->if_start = isstart;
192 ifp->if_ioctl = isioctl;
193 ifp->if_reset = isreset;
194 ifp->if_watchdog = 0;
195 if_attach(ifp);
196}
197
198
199/* Lance initialisation block set up */
200init_mem()
201{
202 int i;
203 u_long temp;
204
205 /* Allocate memory */
206/* Temporary hack, will use kmem_alloc in future */
207#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) + 8)
208static u_char lance_mem[MAXMEM];
209
210
211 /* Align message descriptors on quad word boundary
212 (this is essential) */
213
214 temp = (u_long) &lance_mem;
215 temp = (temp+8) - (temp%8);
216 rd = (struct mds *) temp;
217 td = (struct mds *) (temp + (NRBUF*sizeof(struct mds)));
218 temp += (NRBUF+NTBUF) * sizeof(struct mds);
219
220 init_block.mode = 0;
221
222 /* Get ethernet address */
223 for (i=0; i<6; i++)
224 init_block.padr[i] = inb(isc+(i*2));
225
226 /* Clear multicast address for now */
227 for (i=0; i<8; i++)
228 init_block.ladrf[i] = 0;
229
230 init_block.rdra = kvtop(rd);
231 init_block.rlen = ((kvtop(rd) >> 16) & 0xff) | (RLEN<<13);
232 init_block.tdra = kvtop(td);
233 init_block.tlen = ((kvtop(td) >> 16) & 0xff) | (TLEN<<13);
234
235 /* Set up receive ring descriptors */
236 rbuf = (unsigned char *)temp;
237 for (i=0; i<NRBUF; i++) {
238 (rd+i)->addr = kvtop(temp);
239 (rd+i)->flags= ((kvtop(temp) >> 16) & 0xff) | OWN;
240 (rd+i)->bcnt = -BUFSIZE;
241 (rd+i)->mcnt = 0;
242 temp += BUFSIZE;
243 }
244
245 /* Set up transmit ring descriptors */
246 tbuf = (unsigned char *)temp;
247#ifdef ISDEBUG
248 printf("rd = %x,td = %x, rbuf = %x, tbuf = %x,td+1=%x\n",rd,td,rbuf,tbuf,td+1);
249#endif
250 for (i=0; i<NTBUF; i++) {
251 (td+i)->addr = kvtop(temp);
252 (td+i)->flags= ((kvtop(temp) >> 16) & 0xff);
253 (td+i)->bcnt = 0;
254 (td+i)->mcnt = 0;
255 temp += BUFSIZE;
256 }
257
258}
259
260/*
261 * Initialization of interface; set up initialization block
262 * and transmit/receive descriptor rings.
263 */
264isinit(unit)
265 int unit;
266{
267 register struct is_softc *ns = &is_softc[unit];
268 struct ifnet *ifp = &ns->ns_if;
269 int s;
270 register i;
271
272 if (ifp->if_addrlist == (struct ifaddr *)0) return;
273
274 ns->last_rd = ns->last_td = ns->no_td = 0;
275 s = splimp();
276
277 /* Set up lance's memory area */
278 init_mem();
279
280 /* Stop Lance to get access to other registers */
281 iswrcsr(0,STOP);
282
283 /* I wish I knew what this was */
284 iswrcsr(3,0);
285
286 /* Give lance the physical address of its memory area */
287 iswrcsr(1,kvtop(&init_block));
288 iswrcsr(2,(kvtop(&init_block) >> 16) & 0xff);
289
290 /* OK, let's try and initialise the Lance */
291 iswrcsr(0,INIT);
292
293 /* Wait for initialisation to finish */
294 for(i=0; i<1000; i++){
295 if (isrdcsr(0)&IDON)
296 break;
297 }
298 if (isrdcsr(0)&IDON) {
299 /* Start lance */
300 iswrcsr(0,STRT|IDON|INEA);
301 ns->ns_if.if_flags |= IFF_RUNNING;
302 isstart(ifp);
303 }
304 else
305 printf("Isolink card failed to initialise\n");
306
307 splx(s);
308}
309
310/*
311 * Setup output on interface.
312 * Get another datagram to send off of the interface queue,
313 * and map it to the interface before starting the output.
314 * called only at splimp or interrupt level.
315 */
316isstart(ifp)
317 struct ifnet *ifp;
318{
319 register struct is_softc *ns = &is_softc[ifp->if_unit];
320 struct mbuf *m0, *m;
321 unsigned char *buffer;
322 u_short len;
323 int i;
324 struct mds *cdm;
325
326
327 if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
328 return;
329
330 do {
331 cdm = (td + ns->last_td);
332 if (cdm->flags&OWN)
333 return;
334
335 IF_DEQUEUE(&ns->ns_if.if_snd, m);
336
337 if (m == 0)
338 return;
339
340 /*
341 * Copy the mbuf chain into the transmit buffer
342 */
343
344 buffer = tbuf+(BUFSIZE*ns->last_td);
345 len=0;
346 for (m0=m; m != 0; m=m->m_next) {
347 bcopy(mtod(m,caddr_t),buffer,m->m_len);
348 buffer += m->m_len;
349 len += m->m_len;
350 }
351
352 m_freem(m0);
353 len = MAX(len,ETHER_MIN_LEN);
354
355 /*
356 * Init transmit registers, and set transmit start flag.
357 */
358
359 cdm->flags |= (OWN|STP|ENP);
360 cdm->bcnt = -len;
361 cdm->mcnt = 0;
362#ifdef ISDEBUG
363 xmit_print(ns->last_td);
364#endif
365
366 iswrcsr(0,TDMD|INEA);
367 if (++ns->last_td >= NTBUF)
368 ns->last_td=0;
369 }while(++ns->no_td < NTBUF);
370 ns->no_td = NTBUF;
371 ns->ns_if.if_flags |= IFF_OACTIVE;
372#ifdef ISDEBUG
373 printf("no_td = %x, last_td = %x\n",ns->no_td, ns->last_td);
374#endif
375 return(0);
376}
377
378
379/*
380 * Controller interrupt.
381 */
382isintr(unit)
383{
384 register struct is_softc *ns = &is_softc[unit];
385 u_short isr;
386
387 while((isr=isrdcsr(0))&INTR) {
388 if (isr&ERR) {
389 if (isr&BABL)
390 printf("BABL\n");
391 if (isr&CERR)
392 printf("CERR\n");
393 if (isr&MISS)
394 printf("MISS\n");
395 if (isr&MERR)
396 printf("MERR\n");
397 iswrcsr(0,BABL|CERR|MISS|MERR|INEA);
398 }
399 if (!(isr&TXON)) {
400 isreset(unit);
401 return(1);
402 }
403 if (!(isr&RXON)) {
404 isreset(unit);
405 return(1);
406 }
407 if (isr&RINT) {
408 isrint(unit);
409 }
410 if (isr&TINT) {
411 iswrcsr(0,TINT|INEA);
412 istint(unit);
413 }
414 }
415}
416
417istint(unit)
418 int unit;
419{
420 struct is_softc *is = &is_softc[unit];
421 register struct ifnet *ifp = &is->ns_if;
422 int i,loopcount=0;
423 struct mds *cdm;
424
425 do {
426 if ((i=is->last_td - is->no_td) < 0)
427 i+=NTBUF;
428 cdm = (td+i);
429#ifdef ISDEBUG
430 printf("Trans cdm = %x\n",cdm);
431#endif
432 if (cdm->flags&OWN) {
433 if (loopcount)
434 break;
435 return;
436 }
437 loopcount++;
438 is->ns_if.if_flags &= ~IFF_OACTIVE;
439 }while(--is->no_td > 0);
440 isstart(ifp);
441
442}
443
444#define NEXTRDS \
445 if (++rmd == NRBUF) rmd=0, cdm=rd; else ++cdm
446
447isrint(unit)
448 int unit;
449{
450 register struct is_softc *is=&is_softc[unit];
451 register int rmd = is->last_rd;
452 struct mds *cdm = (rd + rmd);
453
454 /* Out of sync with hardware, should never happen */
455
456 if (cdm->flags & OWN) {
457 printf("is0 error: out of sync\n");
458 iswrcsr(0,RINT|INEA);
459 return;
460 }
461
462 /* Process all buffers with valid data */
463 while (!(cdm->flags&OWN)) {
464 /* Clear interrupt to avoid race condition */
465 iswrcsr(0,RINT|INEA);
466 if (cdm->flags&ERR) {
467 if (cdm->flags&FRAM)
468 printf("FRAM\n");
469 if (cdm->flags&OFLO)
470 printf("OFLO\n");
471 if (cdm->flags&CRC)
472 printf("CRC\n");
473 if (cdm->flags&RBUFF)
474 printf("RBUFF\n");
475 }else
476 if (cdm->flags&(STP|ENP) != (STP|ENP)) {
477 do {
478 iswrcsr(0,RINT|INEA);
479 cdm->mcnt = 0;
480 cdm->flags |= OWN;
481 NEXTRDS;
482 }while (!(cdm->flags&(OWN|ERR|STP|ENP)));
483 is->last_rd = rmd;
484 printf("Chained buffer\n");
485 if ((cdm->flags & (OWN|ERR|STP|ENP)) != ENP) {
486 isreset(unit);
487 return;
488 }
489 }else
490 {
491#ifdef ISDEBUG
492 recv_print(is->last_rd);
493#endif
494 isread(is,rbuf+(BUFSIZE*rmd),cdm->mcnt);
495 }
496
497 cdm->flags |= OWN;
498 cdm->mcnt = 0;
499 NEXTRDS;
500#ifdef ISDEBUG
501 printf("is->last_rd = %x, cdm = %x\n",is->last_rd,cdm);
502#endif
503 } /* while */
504 is->last_rd = rmd;
505} /* isrint */
506
507/*
508 * Pass a packet to the higher levels.
509 * We deal with the trailer protocol here.
510 */
511isread(ns, buf, len)
512 register struct is_softc *ns;
513 char *buf;
514 int len;
515{
516 register struct ether_header *eh;
517 struct mbuf *m;
518 int off, resid;
519 register struct ifqueue *inq;
520
521 /*
522 * Deal with trailer protocol: if type is trailer type
523 * get true type from first 16-bit word past data.
524 * Remember that type was trailer by setting off.
525 */
526 eh = (struct ether_header *)buf;
527 eh->ether_type = ntohs((u_short)eh->ether_type);
528 len = len - sizeof(struct ether_header) - 4;
529#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
530 if (eh->ether_type >= ETHERTYPE_TRAIL &&
531 eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
532 off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
533 if (off >= ETHERMTU) return; /* sanity */
534 eh->ether_type = ntohs(*nedataaddr(eh, off, u_short *));
535 resid = ntohs(*(nedataaddr(eh, off+2, u_short *)));
536 if (off + resid > len) return; /* sanity */
537 len = off + resid;
538 } else off = 0;
539
540 if (len == 0) return;
541
542 /*
543 * Pull packet off interface. Off is nonzero if packet
544 * has trailing header; neget will then force this header
545 * information to be at the front, but we still have to drop
546 * the type and length which are at the front of any trailer data.
547 */
548 m = isget(buf, len, off, &ns->ns_if);
549 if (m == 0) return;
550
551 ether_input(&ns->ns_if, eh, m);
552}
553
554/*
555 * Supporting routines
556 */
557
558/*
559 * Pull read data off a interface.
560 * Len is length of data, with local net header stripped.
561 * Off is non-zero if a trailer protocol was used, and
562 * gives the offset of the trailer information.
563 * We copy the trailer information and then all the normal
564 * data into mbufs. When full cluster sized units are present
565 * we copy into clusters.
566 */
567struct mbuf *
568isget(buf, totlen, off0, ifp)
569 caddr_t buf;
570 int totlen, off0;
571 struct ifnet *ifp;
572{
573 struct mbuf *top, **mp, *m, *p;
574 int off = off0, len;
575 register caddr_t cp = buf;
576 char *epkt;
577
578 buf += sizeof(struct ether_header);
579 cp = buf;
580 epkt = cp + totlen;
581
582
583 if (off) {
584 cp += off + 2 * sizeof(u_short);
585 totlen -= 2 * sizeof(u_short);
586 }
587
588 MGETHDR(m, M_DONTWAIT, MT_DATA);
589 if (m == 0)
590 return (0);
591 m->m_pkthdr.rcvif = ifp;
592 m->m_pkthdr.len = totlen;
593 m->m_len = MHLEN;
594
595 top = 0;
596 mp = &top;
597 while (totlen > 0) {
598 if (top) {
599 MGET(m, M_DONTWAIT, MT_DATA);
600 if (m == 0) {
601 m_freem(top);
602 return (0);
603 }
604 m->m_len = MLEN;
605 }
606 len = min(totlen, epkt - cp);
607 if (len >= MINCLSIZE) {
608 MCLGET(m, M_DONTWAIT);
609 if (m->m_flags & M_EXT)
610 m->m_len = len = min(len, MCLBYTES);
611 else
612 len = m->m_len;
613 } else {
614 /*
615 * Place initial small packet/header at end of mbuf.
616 */
617 if (len < m->m_len) {
618 if (top == 0 && len + max_linkhdr <= m->m_len)
619 m->m_data += max_linkhdr;
620 m->m_len = len;
621 } else
622 len = m->m_len;
623 }
624 bcopy(cp, mtod(m, caddr_t), (unsigned)len);
625 cp += len;
626 *mp = m;
627 mp = &m->m_next;
628 totlen -= len;
629 if (cp == epkt)
630 cp = buf;
631 }
632 return (top);
633}
634
635/*
636 * Process an ioctl request.
637 */
638isioctl(ifp, cmd, data)
639 register struct ifnet *ifp;
640 int cmd;
641 caddr_t data;
642{
643 register struct ifaddr *ifa = (struct ifaddr *)data;
644 struct is_softc *ns = &is_softc[ifp->if_unit];
645 struct ifreq *ifr = (struct ifreq *)data;
646 int s = splimp(), error = 0;
647
648
649 switch (cmd) {
650
651 case SIOCSIFADDR:
652 ifp->if_flags |= IFF_UP;
653
654 switch (ifa->ifa_addr->sa_family) {
655#ifdef INET
656 case AF_INET:
657 isinit(ifp->if_unit); /* before arpwhohas */
658 ((struct arpcom *)ifp)->ac_ipaddr =
659 IA_SIN(ifa)->sin_addr;
660 arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
661 break;
662#endif
663#ifdef NS
664 case AF_NS:
665 {
666 register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
667
668 if (ns_nullhost(*ina))
669 ina->x_host = *(union ns_host *)(ns->ns_addr);
670 else {
671 /*
672 * The manual says we can't change the address
673 * while the receiver is armed,
674 * so reset everything
675 */
676 ifp->if_flags &= ~IFF_RUNNING;
677 bcopy((caddr_t)ina->x_host.c_host,
678 (caddr_t)ns->ns_addr, sizeof(ns->ns_addr));
679 }
680 isinit(ifp->if_unit); /* does ne_setaddr() */
681 break;
682 }
683#endif
684 default:
685 isinit(ifp->if_unit);
686 break;
687 }
688 break;
689
690 case SIOCSIFFLAGS:
691 if ((ifp->if_flags & IFF_UP) == 0 &&
692 ifp->if_flags & IFF_RUNNING) {
693 ifp->if_flags &= ~IFF_RUNNING;
694 iswrcsr(0,STOP);
695 } else if (ifp->if_flags & IFF_UP &&
696 (ifp->if_flags & IFF_RUNNING) == 0)
697 isinit(ifp->if_unit);
698 break;
699
700#ifdef notdef
701 case SIOCGHWADDR:
702 bcopy((caddr_t)ns->ns_addr, (caddr_t) &ifr->ifr_data,
703 sizeof(ns->ns_addr));
704 break;
705#endif
706
707 default:
708 error = EINVAL;
709 }
710 splx(s);
711 return (error);
712}
713
714recv_print(no)
715 int no;
716{
717 struct mds *rmd;
718 int len,i;
719
720 rmd = (rd+no);
721 len = rmd->mcnt;
722 printf("Receive buffer %d, len = %d\n",no,len);
723 printf("Status %x\n",isrdcsr(0));
724 for (i=0; i<len; i++)
725 printf("%x ",*(rbuf+(BUFSIZE*no)+i));
726 printf("\n");
727}
728
729xmit_print(no)
730 int no;
731{
732 struct mds *rmd;
733 int i;
734 u_short len;
735
736 rmd = (td+no);
737 len = -(rmd->bcnt);
738 printf("Transmit buffer %d, len = %d\n",no,len);
739 printf("Status %x\n",isrdcsr(0));
740 printf("addr %x, flags %x, bcnt %x, mcnt %x\n",
741 rmd->addr,rmd->flags,rmd->bcnt,rmd->mcnt);
742 for (i=0; i<len; i++)
743 printf("%x ",*(tbuf+(BUFSIZE*no)+i));
744 printf("\n");
745}
746
747#endif