xref: /netbsd/sys/arch/sun2/dev/if_ec.c (revision c4a72b64)
1 /*	$NetBSD: if_ec.c,v 1.6 2002/10/02 16:02:22 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matthew Fredette.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * 3Com 3C400 device driver
41  */
42 
43 #include "opt_inet.h"
44 #include "opt_ns.h"
45 #include "bpfilter.h"
46 #include "rnd.h"
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/errno.h>
51 #include <sys/ioctl.h>
52 #include <sys/mbuf.h>
53 #include <sys/socket.h>
54 #include <sys/syslog.h>
55 #include <sys/device.h>
56 #include <sys/endian.h>
57 #if NRND > 0
58 #include <sys/rnd.h>
59 #endif
60 
61 #include <net/if.h>
62 #include <net/if_dl.h>
63 #include <net/if_types.h>
64 
65 #include <net/if_ether.h>
66 #include <net/if_media.h>
67 
68 #ifdef INET
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/in_var.h>
72 #include <netinet/ip.h>
73 #include <netinet/if_inarp.h>
74 #endif
75 
76 #ifdef NS
77 #include <netns/ns.h>
78 #include <netns/ns_if.h>
79 #endif
80 
81 #if NBPFILTER > 0
82 #include <net/bpf.h>
83 #include <net/bpfdesc.h>
84 #endif
85 
86 #include <machine/cpu.h>
87 #include <machine/autoconf.h>
88 #include <machine/idprom.h>
89 #include <machine/bus.h>
90 #include <machine/intr.h>
91 
92 #include <sun2/dev/if_ecreg.h>
93 
94 /*
95  * Interface softc.
96  */
97 struct ec_softc {
98 	struct device sc_dev;
99 	void *sc_ih;
100 
101 	struct ethercom sc_ethercom;	/* ethernet common */
102 	struct ifmedia sc_media;	/* our supported media */
103 
104 	bus_space_tag_t sc_iot;	/* bus space tag */
105 	bus_space_handle_t sc_ioh;	/* bus space handle */
106 
107 	u_char sc_jammed;	/* nonzero if the net is jammed */
108 	u_char sc_colliding;	/* nonzero if the net is colliding */
109 	u_int32_t sc_backoff_seed;	/* seed for the backoff PRNG */
110 
111 #if NRND > 0
112 	rndsource_element_t rnd_source;
113 #endif
114 };
115 
116 /* Macros to read and write the CSR. */
117 #define	ECREG_CSR_RD bus_space_read_2(sc->sc_iot, sc->sc_ioh, ECREG_CSR)
118 #define	ECREG_CSR_WR(val) bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECREG_CSR, val)
119 
120 /* After this many collisions, the packet is dropped. */
121 #define	EC_COLLISIONS_JAMMED		16
122 
123 /*
124  * Various constants used in the backoff pseudorandom
125  * number generator.
126  */
127 #define	EC_BACKOFF_PRNG_COLL_MAX	10
128 #define	EC_BACKOFF_PRNG_MUL		1103515245
129 #define	EC_BACKOFF_PRNG_ADD		12345
130 #define	EC_BACKOFF_PRNG_MASK		0x7fffffff
131 
132 /*
133  * Prototypes
134  */
135 int ec_intr __P((void *));
136 void ec_reset __P((struct ifnet *));
137 int ec_init __P((struct ifnet *));
138 int ec_ioctl __P((struct ifnet *, u_long, caddr_t));
139 void ec_watchdog __P((struct ifnet *));
140 void ec_start __P((struct ifnet *));
141 
142 void ec_recv __P((struct ec_softc *, int));
143 void ec_coll __P((struct ec_softc *));
144 void ec_copyin __P((struct ec_softc *, void *, int, size_t));
145 void ec_copyout __P((struct ec_softc *, const void *, int, size_t));
146 
147 int ec_mediachange __P((struct ifnet *));
148 void ec_mediastatus __P((struct ifnet *, struct ifmediareq *));
149 
150 int ec_match __P((struct device *, struct cfdata *, void *));
151 void ec_attach __P((struct device *, struct device *, void *));
152 
153 CFATTACH_DECL(ec, sizeof(struct ec_softc),
154     ec_match, ec_attach, NULL, NULL);
155 
156 /*
157  * Copy board memory to kernel.
158  */
159 void
160 ec_copyin(sc, p, offset, size)
161 	struct ec_softc *sc;
162 	void *p;
163 	int offset;
164 	size_t size;
165 {
166 	bus_space_copyin(sc->sc_iot, sc->sc_ioh, offset, p, size);
167 }
168 
169 /*
170  * Copy from kernel space to board memory.
171  */
172 void
173 ec_copyout(sc, p, offset, size)
174 	struct ec_softc *sc;
175 	const void *p;
176 	int offset;
177 	size_t size;
178 {
179 	bus_space_copyout(sc->sc_iot, sc->sc_ioh, offset, p, size);
180 }
181 
182 int
183 ec_match(parent, match, aux)
184 	struct device *parent;
185 	struct cfdata *match;
186 	void *aux;
187 {
188 	struct mbmem_attach_args *mbma = aux;
189 	bus_space_handle_t bh;
190 	int matched;
191 
192 	/* No default Multibus address. */
193 	if (mbma->mbma_paddr == -1)
194 		return (0);
195 
196 	/* Make sure there is something there... */
197 	if (bus_space_map(mbma->mbma_bustag, mbma->mbma_paddr, ECREG_BANK_SZ,
198 			  0, &bh))
199 		return (0);
200 	matched = (bus_space_peek_2(mbma->mbma_bustag, bh, 0, NULL) == 0);
201 	bus_space_unmap(mbma->mbma_bustag, bh, ECREG_BANK_SZ);
202 	if (!matched)
203 		return (0);
204 
205 	/* Default interrupt priority. */
206 	if (mbma->mbma_pri == -1)
207 		mbma->mbma_pri = 3;
208 
209 	return (1);
210 }
211 
212 void
213 ec_attach(parent, self, aux)
214 	struct device *parent, *self;
215 	void *aux;
216 {
217 	struct ec_softc *sc = (void *) self;
218 	struct mbmem_attach_args *mbma = aux;
219 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
220 	u_int8_t myaddr[ETHER_ADDR_LEN];
221 
222 	printf("\n");
223 
224 	/* Map in the board control regs. */
225 	sc->sc_iot = mbma->mbma_bustag;
226 	if (bus_space_map(mbma->mbma_bustag, mbma->mbma_paddr, ECREG_BANK_SZ,
227 		0, &sc->sc_ioh))
228 		panic("ec_attach: can't map regs");
229 
230 	/* Reset the board. */
231 	ECREG_CSR_WR(EC_CSR_RESET);
232 	delay(160);
233 
234 	/*
235 	 * Copy out the board ROM Ethernet address,
236 	 * and use the non-vendor-ID part to seed
237 	 * our backoff pseudorandom number generator.
238 	 */
239 	bus_space_read_region_1(sc->sc_iot, sc->sc_ioh, ECREG_AROM, myaddr, ETHER_ADDR_LEN);
240 	sc->sc_backoff_seed = (myaddr[3] << 16) | (myaddr[4] << 8) | (myaddr[5]) | 1;
241 
242 	/* Initialize ifnet structure. */
243 	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
244 	ifp->if_softc = sc;
245 	ifp->if_start = ec_start;
246 	ifp->if_ioctl = ec_ioctl;
247 	ifp->if_init = ec_init;
248 	ifp->if_watchdog = ec_watchdog;
249 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
250 	IFQ_SET_READY(&ifp->if_snd);
251 
252         /* Initialize ifmedia structures. */
253 	ifmedia_init(&sc->sc_media, 0, ec_mediachange, ec_mediastatus);
254 	ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
255 	ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
256 
257 	/* Now we can attach the interface. */
258 	if_attach(ifp);
259 	idprom_etheraddr(myaddr);
260 	ether_ifattach(ifp, myaddr);
261 	printf("%s: address %s\n", self->dv_xname, ether_sprintf(myaddr));
262 
263 	bus_intr_establish(mbma->mbma_bustag, mbma->mbma_pri, IPL_NET, 0,
264 	    ec_intr, sc);
265 
266 #if NRND > 0
267 	rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname,
268 	    RND_TYPE_NET, 0);
269 #endif
270 }
271 
272 /*
273  * Reset interface.
274  */
275 void
276 ec_reset(ifp)
277         struct ifnet *ifp;
278 {
279         int s;
280 
281         s = splnet();
282         ec_init(ifp);
283         splx(s);
284 }
285 
286 
287 /*
288  * Initialize interface.
289  */
290 int
291 ec_init(ifp)
292 	struct ifnet *ifp;
293 {
294 	struct ec_softc *sc = ifp->if_softc;
295 
296 	/* Reset the board. */
297 	ECREG_CSR_WR(EC_CSR_RESET);
298 	delay(160);
299 
300 	/* Set the Ethernet address. */
301 	bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, ECREG_ARAM, LLADDR(sc->sc_ethercom.ec_if.if_sadl), ETHER_ADDR_LEN);
302 	ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | EC_CSR_AMSW);
303 	ECREG_CSR_WR(ECREG_CSR_RD & 0);
304 
305 	/* Enable interrupts. */
306 	ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | EC_CSR_BBSW | EC_CSR_ABSW | EC_CSR_BINT | EC_CSR_AINT | (ifp->if_flags & IFF_PROMISC ? EC_CSR_PROMISC : EC_CSR_PA));
307 
308 	/* Set flags appropriately. */
309 	ifp->if_flags |= IFF_RUNNING;
310 	ifp->if_flags &= ~IFF_OACTIVE;
311 
312 	/* Start output. */
313 	ec_start(ifp);
314 
315 	return (0);
316 }
317 
318 /*
319  * Start output on interface.
320  */
321 void
322 ec_start(ifp)
323 	struct ifnet *ifp;
324 {
325 	struct ec_softc *sc = ifp->if_softc;
326 	struct mbuf *m, *m0;
327 	int s;
328 	u_int count;
329 	bus_size_t off;
330 
331 	s = splnet();
332 
333 	/* Don't do anything if output is active. */
334 	if ((ifp->if_flags & IFF_OACTIVE) != 0) {
335 		splx(s);
336 		return;
337 	}
338 	/* Don't do anything if the output queue is empty. */
339 	IFQ_DEQUEUE(&ifp->if_snd, m0);
340 	if (m0 == NULL) {
341 		splx(s);
342 		return;
343 	}
344 
345 #if NBPFILTER > 0
346 	/* The BPF tap. */
347 	if (ifp->if_bpf)
348 		bpf_mtap(ifp->if_bpf, m0);
349 #endif
350 
351 	/* Size the packet. */
352 	for (count = EC_BUF_SZ, m = m0; m != NULL; m = m->m_next)
353 		count -= m->m_len;
354 
355 	/* Copy the packet into the xmit buffer. */
356 	count = MIN(count, EC_PKT_MAXTDOFF);
357 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECREG_TBUF, count);
358 	for (off = count, m = m0; m != 0; off += m->m_len, m = m->m_next)
359 		ec_copyout(sc, mtod(m, u_int8_t *), ECREG_TBUF + off, m->m_len);
360 	m_freem(m0);
361 
362 	/* Enable the transmitter. */
363 	ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_PA) | EC_CSR_TBSW | EC_CSR_TINT | EC_CSR_JINT);
364 	ifp->if_flags |= IFF_OACTIVE;
365 
366 	/* Done. */
367 	splx(s);
368 }
369 
370 /*
371  * Controller interrupt.
372  */
373 int
374 ec_intr(arg)
375 	void *arg;
376 {
377 	struct ec_softc *sc = arg;
378 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
379 	int recv_first;
380 	int recv_second;
381 	int retval;
382 	struct mbuf *m0;
383 
384 	retval = 0;
385 
386 	/* Check for received packet(s). */
387 	recv_first = recv_second = 0;
388 	switch (ECREG_CSR_RD & (EC_CSR_BBSW | EC_CSR_ABSW | EC_CSR_RBBA)) {
389 
390 	case (EC_CSR_BBSW | EC_CSR_ABSW):
391 	case (EC_CSR_BBSW | EC_CSR_ABSW | EC_CSR_RBBA):
392 		/* Neither buffer is full.  Is this a transmit interrupt?
393 		 * Acknowledge the interrupt ourselves. */
394 		ECREG_CSR_WR(ECREG_CSR_RD & (EC_CSR_TINT | EC_CSR_JINT | EC_CSR_PAMASK));
395 		ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | EC_CSR_BINT | EC_CSR_AINT);
396 		break;
397 
398 	case EC_CSR_BBSW:
399 	case (EC_CSR_BBSW | EC_CSR_RBBA):
400 		/* Only the A buffer is full. */
401 		recv_first = EC_CSR_AINT;
402 		break;
403 
404 	case EC_CSR_ABSW:
405 	case (EC_CSR_ABSW | EC_CSR_RBBA):
406 		/* Only the B buffer is full. */
407 		recv_first = EC_CSR_BINT;
408 		break;
409 
410 	case 0:
411 		/* Both the A buffer and the B buffer are full, and the A
412 		 * buffer is older than the B buffer. */
413 		recv_first = EC_CSR_AINT;
414 		recv_second = EC_CSR_BINT;
415 		break;
416 
417 	case EC_CSR_RBBA:
418 		/* Both the A buffer and the B buffer are full, and the B
419 		 * buffer is older than the A buffer. */
420 		recv_first = EC_CSR_BINT;
421 		recv_second = EC_CSR_AINT;
422 		break;
423 	}
424 
425 	/* Receive packets. */
426 	if (recv_first) {
427 
428 		/* Acknowledge the interrupt. */
429 		ECREG_CSR_WR(ECREG_CSR_RD & ((EC_CSR_BINT | EC_CSR_AINT | EC_CSR_TINT | EC_CSR_JINT | EC_CSR_PAMASK) ^ (recv_first | recv_second)));
430 
431 		/* Receive a packet. */
432 		ec_recv(sc, recv_first);
433 
434 		/* Receive a packet. */
435 		if (recv_second)
436 			ec_recv(sc, recv_second);
437 
438 		retval++;
439 	}
440 	/* Check for a transmitted packet. */
441 	if (ifp->if_flags & IFF_OACTIVE) {
442 
443 		/* If we got a collision. */
444 		if (ECREG_CSR_RD & EC_CSR_JAM) {
445 			ECREG_CSR_WR(ECREG_CSR_RD & (EC_CSR_BINT | EC_CSR_AINT | EC_CSR_PAMASK));
446 			sc->sc_ethercom.ec_if.if_collisions++;
447 			retval++;
448 			ec_coll(sc);
449 
450 		}
451 		/* If we transmitted a packet. */
452 		else if ((ECREG_CSR_RD & EC_CSR_TBSW) == 0) {
453 			ECREG_CSR_WR(ECREG_CSR_RD & (EC_CSR_BINT | EC_CSR_AINT | EC_CSR_PAMASK));
454 			retval++;
455 			sc->sc_ethercom.ec_if.if_opackets++;
456 			sc->sc_jammed = 0;
457 			ifp->if_flags &= ~IFF_OACTIVE;
458 			IFQ_POLL(&ifp->if_snd, m0);
459 			if (m0 != NULL)
460 				ec_start(ifp);
461 		}
462 	} else {
463 
464 		/* Make sure we disable transmitter interrupts. */
465 		ECREG_CSR_WR(ECREG_CSR_RD & (EC_CSR_BINT | EC_CSR_AINT | EC_CSR_PAMASK));
466 	}
467 
468 	return retval;
469 }
470 
471 /*
472  * Read in a packet from the board.
473  */
474 void
475 ec_recv(sc, intbit)
476 	struct ec_softc *sc;
477 	int intbit;
478 {
479 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
480 	struct mbuf *m0, *m, *newm;
481 	bus_size_t buf;
482 	u_int16_t status;
483 	u_int16_t doff;
484 	int length, total_length;
485 
486 	buf = EC_CSR_INT_BUF(intbit);
487 
488 	/* Read in the packet status. */
489 	status = bus_space_read_2(sc->sc_iot, sc->sc_ioh, buf);
490 	doff = status & EC_PKT_DOFF;
491 
492 	for (total_length = -1, m0 = 0;;) {
493 
494 		/* Check for an error. */
495 		if (status & (EC_PKT_FCSERR | EC_PKT_RGERR | EC_PKT_FRERR) ||
496 		    doff < EC_PKT_MINRDOFF ||
497 		    doff > EC_PKT_MAXRDOFF) {
498 			printf("%s: garbled packet, status 0x%04x; dropping\n",
499 			    sc->sc_dev.dv_xname, (unsigned int) status);
500 			break;
501 		}
502 
503 		/* Adjust for the header. */
504 		total_length = doff - EC_PKT_RDOFF;
505 		buf += EC_PKT_RDOFF;
506 
507 		/* XXX - sometimes the card reports a large data offset. */
508 		if (total_length > (ETHER_MAX_LEN - ETHER_CRC_LEN)) {
509 #ifdef DEBUG
510 			printf("%s: fixing too-large length of %d\n",
511 			    sc->sc_dev.dv_xname, total_length);
512 #endif
513 			total_length = (ETHER_MAX_LEN - ETHER_CRC_LEN);
514 		}
515 
516 		MGETHDR(m0, M_DONTWAIT, MT_DATA);
517 		if (m0 == 0)
518 			break;
519 		m0->m_pkthdr.rcvif = ifp;
520 		m0->m_pkthdr.len = total_length;
521 		length = MHLEN;
522 		m = m0;
523 
524 		while (total_length > 0) {
525 			if (total_length >= MINCLSIZE) {
526 				MCLGET(m, M_DONTWAIT);
527 				if ((m->m_flags & M_EXT) == 0)
528 					break;
529 				length = MCLBYTES;
530 			}
531 			m->m_len = length = min(total_length, length);
532 			ec_copyin(sc, mtod(m, u_int8_t *), buf, length);
533 			total_length -= length;
534 			buf += length;
535 
536 			if (total_length > 0) {
537 				MGET(newm, M_DONTWAIT, MT_DATA);
538 				if (newm == 0)
539 					break;
540 				length = MLEN;
541 				m = m->m_next = newm;
542 			}
543 		}
544 		break;
545 	}
546 
547 	if (total_length == 0) {
548 		ifp->if_ipackets++;
549 
550 #if NBPFILTER > 0
551 		/*
552 	 	* Check if there's a BPF listener on this interface.
553 	 	* If so, hand off the raw packet to BPF.
554 	 	*/
555 		if (ifp->if_bpf)
556 			bpf_mtap(ifp->if_bpf, m0);
557 #endif
558 
559 		/* Pass the packet up. */
560 		(*ifp->if_input) (ifp, m0);
561 
562 	} else {
563 		/* Something went wrong. */
564 		if (m0)
565 			m_freem(m0);
566 		ifp->if_ierrors++;
567 	}
568 
569 	/* Give the receive buffer back to the card. */
570 	buf = EC_CSR_INT_BUF(intbit);
571 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, buf, 0);
572 	ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | EC_CSR_INT_BSW(intbit) | intbit);
573 }
574 
575 int
576 ec_mediachange(ifp)
577 	struct ifnet *ifp;
578 {
579 	return (0);
580 }
581 
582 void
583 ec_mediastatus(ifp, ifmr)
584 	struct ifnet *ifp;
585 	struct ifmediareq *ifmr;
586 {
587 	if ((ifp->if_flags & IFF_UP) == 0)
588 		return;
589 
590 	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
591 }
592 
593 /*
594  * Process an ioctl request. This code needs some work - it looks pretty ugly.
595  */
596 int
597 ec_ioctl(ifp, cmd, data)
598 	struct ifnet *ifp;
599 	u_long cmd;
600 	caddr_t data;
601 {
602 	struct ifaddr *ifa = (struct ifaddr *) data;
603 	struct ifreq *ifr = (struct ifreq *)data;
604 	struct ec_softc *sc = ifp->if_softc;
605 	int s, error = 0;
606 
607 	s = splnet();
608 
609 	switch (cmd) {
610 
611 	case SIOCSIFADDR:
612 		ifp->if_flags |= IFF_UP;
613 
614 		switch (ifa->ifa_addr->sa_family) {
615 #ifdef INET
616 		case AF_INET:
617 			ec_init(ifp);
618 			arp_ifinit(ifp, ifa);
619 			break;
620 #endif
621 #ifdef NS
622 			/* XXX - This code is probably wrong. */
623 		case AF_NS:
624 			{
625 				struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
626 
627 				if (ns_nullhost(*ina))
628 					ina->x_host =
629 					    *(union ns_host *) LLADDR(ifp->if_sadl);
630 				else
631 					bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl),
632 					    ETHER_ADDR_LEN);
633 				/* Set new address. */
634 				ec_init(ifp);
635 				break;
636 			}
637 #endif
638 		default:
639 			ec_init(ifp);
640 			break;
641 		}
642 		break;
643 
644 	case SIOCSIFFLAGS:
645 		if ((ifp->if_flags & IFF_UP) == 0 &&
646 		    (ifp->if_flags & IFF_RUNNING) != 0) {
647 			/*
648 			 * If interface is marked down and it is running, then
649 			 * stop it.
650 			 */
651 			ifp->if_flags &= ~IFF_RUNNING;
652 		} else if ((ifp->if_flags & IFF_UP) != 0 &&
653 		    (ifp->if_flags & IFF_RUNNING) == 0) {
654 			/*
655 			 * If interface is marked up and it is stopped, then
656 			 * start it.
657 			 */
658 			ec_init(ifp);
659 		} else {
660 			/*
661 			 * Some other important flag might have changed, so
662 			 * reset.
663 			 */
664 			ec_reset(ifp);
665 		}
666 		break;
667 
668 	case SIOCGIFMEDIA:
669 	case SIOCSIFMEDIA:
670 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
671 		break;
672 
673 	default:
674 		error = EINVAL;
675 		break;
676 	}
677 
678 	splx(s);
679 	return error;
680 }
681 
682 /*
683  * Collision routine.
684  */
685 void
686 ec_coll(sc)
687 	struct ec_softc *sc;
688 {
689 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
690 	u_short jams;
691 	struct mbuf *m0;
692 
693 	if ((++sc->sc_colliding) >= EC_COLLISIONS_JAMMED) {
694 		sc->sc_ethercom.ec_if.if_oerrors++;
695 		if (!sc->sc_jammed)
696 			printf("%s: ethernet jammed\n",
697 			    sc->sc_dev.dv_xname);
698 		sc->sc_jammed = 1;
699 		sc->sc_colliding = 0;
700 		ifp->if_flags &= ~IFF_OACTIVE;
701 		IFQ_POLL(&ifp->if_snd, m0);
702 		if (m0 != NULL)
703 			ec_start(ifp);
704 	} else {
705 		jams = MAX(sc->sc_colliding, EC_BACKOFF_PRNG_COLL_MAX);
706 		sc->sc_backoff_seed = ((sc->sc_backoff_seed * EC_BACKOFF_PRNG_MUL) + EC_BACKOFF_PRNG_ADD) & EC_BACKOFF_PRNG_MASK;
707 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECREG_BACKOFF, -(((sc->sc_backoff_seed >> 8) & ~(-1 << jams)) + 1));
708 		ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | EC_CSR_JAM | EC_CSR_TINT | EC_CSR_JINT);
709 	}
710 }
711 
712 /*
713  * Device timeout routine.
714  */
715 void
716 ec_watchdog(ifp)
717 	struct ifnet *ifp;
718 {
719 	struct ec_softc *sc = ifp->if_softc;
720 
721 	log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
722 	sc->sc_ethercom.ec_if.if_oerrors++;
723 
724 	ec_reset(ifp);
725 }
726