Make all network interrupt service routines MPSAFE part 1/3.
[dragonfly.git] / sys / dev / netif / cm / smc90cx6.c
CommitLineData
984263bc
MD
1/* $NetBSD: smc90cx6.c,v 1.38 2001/07/07 15:57:53 thorpej Exp $ */
2/* $FreeBSD: src/sys/dev/cm/smc90cx6.c,v 1.1.2.3 2003/02/05 18:42:14 fjoe Exp $ */
78195a76 3/* $DragonFly: src/sys/dev/netif/cm/Attic/smc90cx6.c,v 1.20 2005/11/28 17:13:41 dillon Exp $ */
984263bc
MD
4
5/*-
6 * Copyright (c) 1994, 1995, 1998 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Ignatios Souvatzis.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the NetBSD
23 * Foundation, Inc. and its contributors.
24 * 4. Neither the name of The NetBSD Foundation nor the names of its
25 * contributors may be used to endorse or promote products derived
26 * from this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 */
40
41/*
42 * Chip core driver for the SMC90c26 / SMC90c56 (and SMC90c66 in '56
43 * compatibility mode) boards
44 */
45
984263bc
MD
46#define CMRETRANSMIT /**/
47/* #define CM_DEBUG */
48
49#include <sys/param.h>
50#include <sys/systm.h>
51#include <sys/sockio.h>
52#include <sys/mbuf.h>
53#include <sys/module.h>
54#include <sys/kernel.h>
55#include <sys/socket.h>
56#include <sys/syslog.h>
1fe12906 57#include <sys/thread2.h>
984263bc
MD
58#include <sys/bus.h>
59
60#include <machine/bus.h>
61#include <sys/rman.h>
62#include <machine/resource.h>
63
984263bc
MD
64#include <net/if.h>
65#include <net/if_dl.h>
66#include <net/if_types.h>
67#include <net/if_arc.h>
68
1f2de5d4
MD
69#include "smc90cx6reg.h"
70#include "smc90cx6var.h"
984263bc 71
32832096 72DECLARE_DUMMY_MODULE(if_cm);
984263bc
MD
73MODULE_DEPEND(if_cm, arcnet, 1, 1, 1);
74
75/* these should be elsewhere */
76
77#define ARC_MIN_LEN 1
78#define ARC_MIN_FORBID_LEN 254
79#define ARC_MAX_FORBID_LEN 256
80#define ARC_MAX_LEN 508
81#define ARC_ADDR_LEN 1
82
83/* for watchdog timer. This should be more than enough. */
84#define ARCTIMEOUT (5*IFNET_SLOWHZ)
85
86/* short notation */
87
88#define GETREG(off) \
89 bus_space_read_1(rman_get_bustag((sc)->port_res), \
90 rman_get_bushandle((sc)->port_res), \
91 (off))
92#define PUTREG(off, value) \
93 bus_space_write_1(rman_get_bustag((sc)->port_res), \
94 rman_get_bushandle((sc)->port_res), \
95 (off), (value))
96#define GETMEM(off) \
97 bus_space_read_1(rman_get_bustag((sc)->mem_res), \
98 rman_get_bushandle((sc)->mem_res), \
99 (off))
100#define PUTMEM(off, value) \
101 bus_space_write_1(rman_get_bustag((sc)->mem_res), \
102 rman_get_bushandle((sc)->mem_res), \
103 (off), (value))
104
105devclass_t cm_devclass;
106
107/*
108 * This currently uses 2 bufs for tx, 2 for rx
109 *
110 * New rx protocol:
111 *
112 * rx has a fillcount variable. If fillcount > (NRXBUF-1),
113 * rx can be switched off from rx hard int.
114 * Else rx is restarted on the other receiver.
115 * rx soft int counts down. if it is == (NRXBUF-1), it restarts
116 * the receiver.
117 * To ensure packet ordering (we need that for 1201 later), we have a counter
118 * which is incremented modulo 256 on each receive and a per buffer
119 * variable, which is set to the counter on filling. The soft int can
120 * compare both values to determine the older packet.
121 *
122 * Transmit direction:
123 *
124 * cm_start checks tx_fillcount
125 * case 2: return
126 *
127 * else fill tx_act ^ 1 && inc tx_fillcount
128 *
129 * check tx_fillcount again.
130 * case 2: set IFF_OACTIVE to stop arc_output from filling us.
131 * case 1: start tx
132 *
133 * tint clears IFF_OCATIVE, decrements and checks tx_fillcount
134 * case 1: start tx on tx_act ^ 1, softcall cm_start
135 * case 0: softcall cm_start
136 *
137 * #define fill(i) get mbuf && copy mbuf to chip(i)
138 */
139
b5101a88
RG
140void cm_init (void *);
141void cm_reset (struct cm_softc *);
142void cm_start (struct ifnet *);
9974b71d 143int cm_ioctl (struct ifnet *, unsigned long, caddr_t, struct ucred *);
b5101a88
RG
144void cm_watchdog (struct ifnet *);
145void cm_srint (void *vsc);
146static void cm_tint (struct cm_softc *, int);
984263bc
MD
147void cm_reconwatch(void *);
148
149int
150cm_probe(dev)
151 device_t dev;
152{
153 int error;
154 struct cm_softc *sc = device_get_softc(dev);
155
156 error = cm_alloc_port(dev, 0, CM_IO_PORTS);
157 if (error)
158 return error;
159
160 if (GETREG(CMSTAT) == 0xff)
161 return ENXIO;
162
163 error = cm_alloc_memory(dev, 0, 0x800);
164 if (error)
165 return error;
166
167 return 0;
168}
169
170/*
171 * Allocate a port resource with the given resource id.
172 */
173int
174cm_alloc_port(dev, rid, size)
175 device_t dev;
176 int rid;
177 int size;
178{
179 struct cm_softc *sc = device_get_softc(dev);
180 struct resource *res;
181
182 res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
183 0ul, ~0ul, size, RF_ACTIVE);
184 if (res) {
185 sc->port_rid = rid;
186 sc->port_res = res;
187 sc->port_used = size;
188 return (0);
189 } else {
190 return (ENOENT);
191 }
192}
193
194/*
195 * Allocate a memory resource with the given resource id.
196 */
197int
198cm_alloc_memory(dev, rid, size)
199 device_t dev;
200 int rid;
201 int size;
202{
203 struct cm_softc *sc = device_get_softc(dev);
204 struct resource *res;
205
206 res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
207 0ul, ~0ul, size, RF_ACTIVE);
208 if (res) {
209 sc->mem_rid = rid;
210 sc->mem_res = res;
211 sc->mem_used = size;
212 return (0);
213 } else {
214 return (ENOENT);
215 }
216}
217
218/*
219 * Allocate an irq resource with the given resource id.
220 */
221int
222cm_alloc_irq(dev, rid)
223 device_t dev;
224 int rid;
225{
226 struct cm_softc *sc = device_get_softc(dev);
227 struct resource *res;
228
4e6d744d 229 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
984263bc
MD
230 if (res) {
231 sc->irq_rid = rid;
232 sc->irq_res = res;
233 return (0);
234 } else {
235 return (ENOENT);
236 }
237}
238
239/*
240 * Release all resources
241 */
242void
243cm_release_resources(dev)
244 device_t dev;
245{
246 struct cm_softc *sc = device_get_softc(dev);
247
248 if (sc->port_res) {
249 bus_deactivate_resource(dev, SYS_RES_IOPORT,
250 sc->port_rid, sc->port_res);
251 bus_release_resource(dev, SYS_RES_IOPORT,
252 sc->port_rid, sc->port_res);
253 sc->port_res = 0;
254 }
255 if (sc->mem_res) {
256 bus_deactivate_resource(dev, SYS_RES_MEMORY,
257 sc->mem_rid, sc->mem_res);
258 bus_release_resource(dev, SYS_RES_MEMORY,
259 sc->mem_rid, sc->mem_res);
260 sc->mem_res = 0;
261 }
262 if (sc->irq_res) {
263 bus_deactivate_resource(dev, SYS_RES_IRQ,
264 sc->irq_rid, sc->irq_res);
265 bus_release_resource(dev, SYS_RES_IRQ,
266 sc->irq_rid, sc->irq_res);
267 sc->irq_res = 0;
268 }
269}
270
271int
3e4a09e7
MD
272cm_attach(dev)
273 device_t dev;
984263bc 274{
3e4a09e7 275 struct cm_softc *sc = device_get_softc(dev);
984263bc 276 struct ifnet *ifp = &sc->sc_arccom.ac_if;
984263bc
MD
277 u_int8_t linkaddress;
278
984263bc
MD
279 /*
280 * read the arcnet address from the board
281 */
282
283 GETREG(CMRESET);
284 do {
285 DELAY(200);
286 } while (!(GETREG(CMSTAT) & CM_POR));
287
288 linkaddress = GETMEM(CMMACOFF);
289
290 /* clear the int mask... */
291
292 sc->sc_intmask = 0;
293 PUTREG(CMSTAT, 0);
294
295 PUTREG(CMCMD, CM_CONF(CONF_LONG));
296 PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
297 sc->sc_recontime = sc->sc_reconcount = 0;
298
3f9026f2 299 callout_init(&sc->sc_recon_ch);
984263bc
MD
300
301 /*
302 * set interface to stopped condition (reset)
303 */
304 cm_stop(sc);
305
3e4a09e7 306 ifp->if_softc = sc;
b7c760e6 307 if_initname(ifp, device_get_name(dev), device_get_unit(dev));
3e4a09e7
MD
308 ifp->if_start = cm_start;
309 ifp->if_ioctl = cm_ioctl;
310 ifp->if_watchdog = cm_watchdog;
311 ifp->if_init = cm_init;
3e4a09e7
MD
312 ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
313 ifp->if_timer = 0;
314 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
984263bc 315
78195a76 316 arc_ifattach(ifp, linkaddress, NULL);
984263bc
MD
317 return 0;
318}
319
320/*
321 * Initialize device
322 *
323 */
324void
325cm_init(xsc)
326 void *xsc;
327{
328 struct cm_softc *sc = (struct cm_softc *)xsc;
1fe12906 329 struct ifnet *ifp = &sc->sc_arccom.ac_if;
984263bc
MD
330
331 if ((ifp->if_flags & IFF_RUNNING) == 0) {
984263bc
MD
332 ifp->if_flags |= IFF_RUNNING;
333 cm_reset(sc);
334 cm_start(ifp);
984263bc
MD
335 }
336}
337
338/*
339 * Reset the interface...
340 *
341 * this assumes that it is called inside a critical section...
342 *
343 */
344void
345cm_reset(sc)
346 struct cm_softc *sc;
347{
348 struct ifnet *ifp;
349 int linkaddress;
350
351 ifp = &sc->sc_arccom.ac_if;
352
353#ifdef CM_DEBUG
b7c760e6 354 if_printf(ifp, "reset\n");
984263bc
MD
355#endif
356 /* stop and restart hardware */
357
358 GETREG(CMRESET);
359 do {
360 DELAY(200);
361 } while (!(GETREG(CMSTAT) & CM_POR));
362
363 linkaddress = GETMEM(CMMACOFF);
364
365#if defined(CM_DEBUG) && (CM_DEBUG > 2)
b7c760e6
SZ
366 if_printf(ifp, "reset: card reset, link addr = 0x%02x (%d)\n",
367 linkaddress, linkaddress);
984263bc
MD
368#endif
369
370 /* tell the routing level about the (possibly changed) link address */
371 arc_storelladdr(ifp, linkaddress);
372 arc_frag_init(ifp);
373
374 /* POR is NMI, but we need it below: */
375 sc->sc_intmask = CM_RECON|CM_POR;
376 PUTREG(CMSTAT, sc->sc_intmask);
377 PUTREG(CMCMD, CM_CONF(CONF_LONG));
378
379#ifdef CM_DEBUG
b7c760e6
SZ
380 if_printf(ifp, "reset: chip configured, status=0x%02x\n",
381 GETREG(CMSTAT));
984263bc
MD
382#endif
383 PUTREG(CMCMD, CM_CLR(CLR_POR|CLR_RECONFIG));
384
385#ifdef CM_DEBUG
b7c760e6 386 if_printf(ifp, "reset: bits cleared, status=0x%02x\n", GETREG(CMSTAT));
984263bc
MD
387#endif
388
389 sc->sc_reconcount_excessive = ARC_EXCESSIVE_RECONS;
390
391 /* start receiver */
392
393 sc->sc_intmask |= CM_RI;
394 sc->sc_rx_fillcount = 0;
395 sc->sc_rx_act = 2;
396
397 PUTREG(CMCMD, CM_RXBC(2));
398 PUTREG(CMSTAT, sc->sc_intmask);
399
400#ifdef CM_DEBUG
b7c760e6
SZ
401 if_printf(ifp, "reset: started receiver, status=0x%02x\n",
402 GETREG(CMSTAT));
984263bc
MD
403#endif
404
405 /* and init transmitter status */
406 sc->sc_tx_act = 0;
407 sc->sc_tx_fillcount = 0;
408
409 ifp->if_flags |= IFF_RUNNING;
410 ifp->if_flags &= ~IFF_OACTIVE;
411
412 cm_start(ifp);
413}
414
415/*
416 * Take interface offline
417 */
418void
419cm_stop(sc)
420 struct cm_softc *sc;
421{
3f9026f2
SZ
422 struct ifnet *ifp = &sc->sc_arccom.ac_if;
423
984263bc
MD
424 /* Stop the interrupts */
425 PUTREG(CMSTAT, 0);
426
427 /* Stop the interface */
428 GETREG(CMRESET);
429
430 /* Stop watchdog timer */
3f9026f2
SZ
431 ifp->if_timer = 0;
432
433 callout_stop(&sc->sc_recon_ch);
434 ifp->if_flags &= ~IFF_RUNNING;
984263bc
MD
435}
436
437/*
438 * Start output on interface. Get another datagram to send
439 * off the interface queue, and copy it to the
440 * interface becore starting the output
441 *
442 * this assumes that it is called inside a critical section...
443 * XXX hm... does it still?
444 *
445 */
446void
447cm_start(ifp)
448 struct ifnet *ifp;
449{
450 struct cm_softc *sc = ifp->if_softc;
451 struct mbuf *m,*mp;
452
1fe12906 453 int cm_ram_ptr, len, tlen, offset, buffer;
984263bc
MD
454#ifdef CMTIMINGS
455 u_long copystart, lencopy, perbyte;
456#endif
457
458#if defined(CM_DEBUG) && (CM_DEBUG > 3)
b7c760e6 459 if_printf(ifp, "start(%p)\n", ifp);
984263bc
MD
460#endif
461
78195a76 462 if ((ifp->if_flags & IFF_RUNNING) == 0)
1fe12906 463 return;
984263bc 464
78195a76 465 if (sc->sc_tx_fillcount >= 2)
984263bc 466 return;
984263bc
MD
467
468 m = arc_frag_next(ifp);
469 buffer = sc->sc_tx_act ^ 1;
470
984263bc
MD
471 if (m == 0)
472 return;
473
474#ifdef CM_DEBUG
475 if (m->m_len < ARC_HDRLEN)
476 m = m_pullup(m, ARC_HDRLEN);/* gcc does structure padding */
b7c760e6
SZ
477 if_printf(ifp, "start: filling %d from %d to %d type %d\n",
478 buffer, mtod(m, u_char *)[0],
984263bc
MD
479 mtod(m, u_char *)[1], mtod(m, u_char *)[2]);
480#else
481 if (m->m_len < 2)
482 m = m_pullup(m, 2);
483#endif
484 cm_ram_ptr = buffer * 512;
485
486 if (m == 0)
487 return;
488
489 /* write the addresses to RAM and throw them away */
490
491 /*
492 * Hardware does this: Yet Another Microsecond Saved.
493 * (btw, timing code says usually 2 microseconds)
494 * PUTMEM(cm_ram_ptr + 0, mtod(m, u_char *)[0]);
495 */
496
497 PUTMEM(cm_ram_ptr + 1, mtod(m, u_char *)[1]);
498 m_adj(m, 2);
499
500 /* get total length left at this point */
501 tlen = m->m_pkthdr.len;
502 if (tlen < ARC_MIN_FORBID_LEN) {
503 offset = 256 - tlen;
504 PUTMEM(cm_ram_ptr + 2, offset);
505 } else {
506 PUTMEM(cm_ram_ptr + 2, 0);
507 if (tlen <= ARC_MAX_FORBID_LEN)
508 offset = 255; /* !!! */
509 else {
510 if (tlen > ARC_MAX_LEN)
511 tlen = ARC_MAX_LEN;
512 offset = 512 - tlen;
513 }
514 PUTMEM(cm_ram_ptr + 3, offset);
515
516 }
517 cm_ram_ptr += offset;
518
519 /* lets loop through the mbuf chain */
520
521 for (mp = m; mp; mp = mp->m_next) {
522 if ((len = mp->m_len)) { /* YAMS */
523 bus_space_write_region_1(
524 rman_get_bustag(sc->mem_res),
525 rman_get_bushandle(sc->mem_res),
526 cm_ram_ptr, mtod(mp, caddr_t), len);
527
528 cm_ram_ptr += len;
529 }
530 }
531
532 sc->sc_broadcast[buffer] = (m->m_flags & M_BCAST) != 0;
533 sc->sc_retransmits[buffer] = (m->m_flags & M_BCAST) ? 1 : 5;
534
535 /* actually transmit the packet */
984263bc
MD
536
537 if (++sc->sc_tx_fillcount > 1) {
538 /*
539 * We are filled up to the rim. No more bufs for the moment,
540 * please.
541 */
542 ifp->if_flags |= IFF_OACTIVE;
543 } else {
544#ifdef CM_DEBUG
b7c760e6
SZ
545 if_printf(ifp, "start: starting transmitter on buffer %d\n",
546 buffer);
984263bc
MD
547#endif
548 /* Transmitter was off, start it */
549 sc->sc_tx_act = buffer;
550
551 /*
552 * We still can accept another buf, so don't:
553 * ifp->if_flags |= IFF_OACTIVE;
554 */
555 sc->sc_intmask |= CM_TA;
556 PUTREG(CMCMD, CM_TX(buffer));
557 PUTREG(CMSTAT, sc->sc_intmask);
558
559 sc->sc_arccom.ac_if.if_timer = ARCTIMEOUT;
560 }
984263bc
MD
561 m_freem(m);
562
563 /*
564 * After 10 times reading the docs, I realized
565 * that in the case the receiver NAKs the buffer request,
566 * the hardware retries till shutdown.
567 * This is integrated now in the code above.
568 */
569
570 return;
571}
572
573/*
574 * Arcnet interface receiver soft interrupt:
575 * get the stuff out of any filled buffer we find.
576 */
577void
578cm_srint(vsc)
579 void *vsc;
580{
581 struct cm_softc *sc = (struct cm_softc *)vsc;
1fe12906 582 int buffer, len, offset, type;
984263bc
MD
583 int cm_ram_ptr;
584 struct mbuf *m;
585 struct arc_header *ah;
1fe12906 586 struct ifnet *ifp = &sc->sc_arccom.ac_if;
984263bc 587
984263bc 588 buffer = sc->sc_rx_act ^ 1;
984263bc 589
984263bc
MD
590 /*
591 * Align so that IP packet will be longword aligned. Here we
592 * assume that m_data of new packet is longword aligned.
593 * When implementing PHDS, we might have to change it to 2,
594 * (2*sizeof(ulong) - CM_HDRNEWLEN)), packet type dependent.
595 */
596
597 cm_ram_ptr = buffer * 512;
598 offset = GETMEM(cm_ram_ptr + 2);
599 if (offset)
600 len = 256 - offset;
601 else {
602 offset = GETMEM(cm_ram_ptr + 3);
603 len = 512 - offset;
604 }
605
606 /*
7bc77c55
JH
607 * Allocate header mbuf.
608 *
609 * First +2 bytes for align fixup below.
610 * Second +2 bytes are for src/dst addresses.
984263bc 611 */
7bc77c55
JH
612 m = m_getl(len + 2 + 2, MB_DONTWAIT, MT_DATA, M_PKTHDR, NULL);
613 if (m == NULL) {
614 /*
615 * in case s.th. goes wrong with mem, drop it
616 * to make sure the receiver can be started again
617 * count it as input error (we dont have any other
618 * detectable)
619 */
984263bc
MD
620 ifp->if_ierrors++;
621 goto cleanup;
622 }
7bc77c55 623 m->m_pkthdr.rcvif = ifp;
984263bc
MD
624
625 type = GETMEM(cm_ram_ptr + offset);
626 m->m_data += 1 + arc_isphds(type);
627 /* mbuf filled with ARCnet addresses */
628 m->m_pkthdr.len = m->m_len = len + 2;
629
630 ah = mtod(m, struct arc_header *);
631 ah->arc_shost = GETMEM(cm_ram_ptr + 0);
632 ah->arc_dhost = GETMEM(cm_ram_ptr + 1);
633
634 bus_space_read_region_1(
635 rman_get_bustag(sc->mem_res), rman_get_bushandle(sc->mem_res),
636 cm_ram_ptr + offset, mtod(m, u_char *) + 2, len);
637
78195a76 638 ifp->if_input(ifp, m);
984263bc
MD
639
640 m = NULL;
641 ifp->if_ipackets++;
642
643cleanup:
644
645 if (m != NULL)
646 m_freem(m);
647
648 /* mark buffer as invalid by source id 0 */
649 PUTMEM(buffer << 9, 0);
1fe12906 650
984263bc
MD
651 if (--sc->sc_rx_fillcount == 2 - 1) {
652
653 /* was off, restart it on buffer just emptied */
654 sc->sc_rx_act = buffer;
655 sc->sc_intmask |= CM_RI;
656
657 /* this also clears the RI flag interupt: */
658 PUTREG(CMCMD, CM_RXBC(buffer));
659 PUTREG(CMSTAT, sc->sc_intmask);
660
661#ifdef CM_DEBUG
b7c760e6 662 if_printf(ifp, "srint: restarted rx on buf %d\n", buffer);
984263bc
MD
663#endif
664 }
984263bc
MD
665}
666
667__inline static void
668cm_tint(sc, isr)
669 struct cm_softc *sc;
670 int isr;
671{
672 struct ifnet *ifp;
673
674 int buffer;
675#ifdef CMTIMINGS
676 int clknow;
677#endif
678
679 ifp = &(sc->sc_arccom.ac_if);
680 buffer = sc->sc_tx_act;
681
682 /*
683 * retransmit code:
684 * Normal situtations first for fast path:
685 * If acknowledgement received ok or broadcast, we're ok.
686 * else if
687 */
688
689 if (isr & CM_TMA || sc->sc_broadcast[buffer])
690 sc->sc_arccom.ac_if.if_opackets++;
691#ifdef CMRETRANSMIT
692 else if (ifp->if_flags & IFF_LINK2 && ifp->if_timer > 0
693 && --sc->sc_retransmits[buffer] > 0) {
694 /* retransmit same buffer */
695 PUTREG(CMCMD, CM_TX(buffer));
696 return;
697 }
698#endif
699 else
700 ifp->if_oerrors++;
701
702
703 /* We know we can accept another buffer at this point. */
704 ifp->if_flags &= ~IFF_OACTIVE;
705
706 if (--sc->sc_tx_fillcount > 0) {
707
708 /*
709 * start tx on other buffer.
710 * This also clears the int flag
711 */
712 buffer ^= 1;
713 sc->sc_tx_act = buffer;
714
715 /*
716 * already given:
717 * sc->sc_intmask |= CM_TA;
718 * PUTREG(CMSTAT, sc->sc_intmask);
719 */
720 PUTREG(CMCMD, CM_TX(buffer));
721 /* init watchdog timer */
722 ifp->if_timer = ARCTIMEOUT;
723
724#if defined(CM_DEBUG) && (CM_DEBUG > 1)
b7c760e6
SZ
725 if_printf(ifp, "tint: starting tx on buffer %d, "
726 "status 0x%02x\n", buffer, GETREG(CMSTAT));
984263bc
MD
727#endif
728 } else {
729 /* have to disable TX interrupt */
730 sc->sc_intmask &= ~CM_TA;
731 PUTREG(CMSTAT, sc->sc_intmask);
732 /* ... and watchdog timer */
733 ifp->if_timer = 0;
734
735#ifdef CM_DEBUG
b7c760e6
SZ
736 if_printf(ifp, "tint: no more buffers to send, status 0x%02x\n",
737 GETREG(CMSTAT));
984263bc
MD
738#endif
739 }
740
984263bc
MD
741 /* call it directly */
742 cm_start(ifp);
984263bc
MD
743}
744
745/*
746 * Our interrupt routine
747 */
748void
749cmintr(arg)
750 void *arg;
751{
752 struct cm_softc *sc = arg;
753 struct ifnet *ifp = &sc->sc_arccom.ac_if;
754
755 u_char isr, maskedisr;
756 int buffer;
757 u_long newsec;
758
759 isr = GETREG(CMSTAT);
760 maskedisr = isr & sc->sc_intmask;
761 if (!maskedisr)
762 return;
763 do {
764
765#if defined(CM_DEBUG) && (CM_DEBUG > 1)
b7c760e6
SZ
766 if_printf(ifp, "intr: status 0x%02x, intmask 0x%02x\n",
767 isr, sc->sc_intmask);
984263bc
MD
768#endif
769
770 if (maskedisr & CM_POR) {
771 /*
772 * XXX We should never see this. Don't bother to store
773 * the address.
774 * sc->sc_arccom.ac_anaddr = GETMEM(CMMACOFF);
775 */
776 PUTREG(CMCMD, CM_CLR(CLR_POR));
777 log(LOG_WARNING,
3e4a09e7
MD
778 "%s: intr: got spurious power on reset int\n",
779 ifp->if_xname);
984263bc
MD
780 }
781
782 if (maskedisr & CM_RECON) {
783 /*
784 * we dont need to:
785 * PUTREG(CMCMD, CM_CONF(CONF_LONG));
786 */
787 PUTREG(CMCMD, CM_CLR(CLR_RECONFIG));
788 sc->sc_arccom.ac_if.if_collisions++;
789
790 /*
791 * If less than 2 seconds per reconfig:
792 * If ARC_EXCESSIVE_RECONFIGS
793 * since last burst, complain and set treshold for
794 * warnings to ARC_EXCESSIVE_RECONS_REWARN.
795 *
796 * This allows for, e.g., new stations on the cable, or
797 * cable switching as long as it is over after
798 * (normally) 16 seconds.
799 *
800 * XXX TODO: check timeout bits in status word and
801 * double time if necessary.
802 */
803
804 callout_stop(&sc->sc_recon_ch);
805 newsec = time_second;
806 if ((newsec - sc->sc_recontime <= 2) &&
807 (++sc->sc_reconcount == ARC_EXCESSIVE_RECONS)) {
808 log(LOG_WARNING,
3e4a09e7 809 "%s: excessive token losses, "
984263bc 810 "cable problem?\n",
3e4a09e7 811 ifp->if_xname);
984263bc
MD
812 }
813 sc->sc_recontime = newsec;
814 callout_reset(&sc->sc_recon_ch, 15 * hz,
815 cm_reconwatch, (void *)sc);
816 }
817
818 if (maskedisr & CM_RI) {
819#if defined(CM_DEBUG) && (CM_DEBUG > 1)
b7c760e6
SZ
820 if_printf(ifp, "intr: hard rint, act %d\n",
821 sc->sc_rx_act);
984263bc
MD
822#endif
823
824 buffer = sc->sc_rx_act;
825 /* look if buffer is marked invalid: */
826 if (GETMEM(buffer * 512) == 0) {
827 /*
828 * invalid marked buffer (or illegally
829 * configured sender)
830 */
831 log(LOG_WARNING,
3e4a09e7
MD
832 "%s: spurious RX interupt or sender 0 "
833 " (ignored)\n", ifp->if_xname);
984263bc
MD
834 /*
835 * restart receiver on same buffer.
836 * XXX maybe better reset interface?
837 */
838 PUTREG(CMCMD, CM_RXBC(buffer));
839 } else {
840 if (++sc->sc_rx_fillcount > 1) {
841 sc->sc_intmask &= ~CM_RI;
842 PUTREG(CMSTAT, sc->sc_intmask);
843 } else {
844 buffer ^= 1;
845 sc->sc_rx_act = buffer;
846
847 /*
848 * Start receiver on other receive
849 * buffer. This also clears the RI
850 * interupt flag.
851 */
852 PUTREG(CMCMD, CM_RXBC(buffer));
853 /* in RX intr, so mask is ok for RX */
854
855#ifdef CM_DEBUG
b7c760e6 856 if_printf(ifp, "strt rx for buf %d, "
984263bc 857 "stat 0x%02x\n",
984263bc
MD
858 sc->sc_rx_act, GETREG(CMSTAT));
859#endif
860 }
861
984263bc
MD
862 /* this one does the copy here */
863 cm_srint(sc);
984263bc
MD
864 }
865 }
866 if (maskedisr & CM_TA) {
867 cm_tint(sc, isr);
868 }
869 isr = GETREG(CMSTAT);
870 maskedisr = isr & sc->sc_intmask;
871 } while (maskedisr);
872#if defined(CM_DEBUG) && (CM_DEBUG > 1)
b7c760e6
SZ
873 if_printf(ifp, "intr (exit): status 0x%02x, intmask 0x%02x\n",
874 isr, sc->sc_intmask);
984263bc
MD
875#endif
876}
877
878void
879cm_reconwatch(arg)
880 void *arg;
881{
882 struct cm_softc *sc = arg;
883 struct ifnet *ifp = &sc->sc_arccom.ac_if;
884
78195a76 885 lwkt_serialize_enter(ifp->if_serializer);
984263bc
MD
886 if (sc->sc_reconcount >= ARC_EXCESSIVE_RECONS) {
887 sc->sc_reconcount = 0;
3e4a09e7
MD
888 log(LOG_WARNING, "%s: token valid again.\n",
889 ifp->if_xname);
984263bc
MD
890 }
891 sc->sc_reconcount = 0;
78195a76 892 lwkt_serialize_exit(ifp->if_serializer);
984263bc
MD
893}
894
895
896/*
897 * Process an ioctl request.
898 * This code needs some work - it looks pretty ugly.
899 */
900int
9974b71d 901cm_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr)
984263bc
MD
902{
903 struct cm_softc *sc;
904 struct ifaddr *ifa;
905 struct ifreq *ifr;
1fe12906 906 int error;
984263bc
MD
907
908 error = 0;
909 sc = ifp->if_softc;
910 ifa = (struct ifaddr *)data;
911 ifr = (struct ifreq *)data;
1fe12906 912
984263bc 913#if defined(CM_DEBUG) && (CM_DEBUG > 2)
b7c760e6 914 if_printf(ifp, "ioctl() called, cmd = 0x%lx\n", command);
984263bc
MD
915#endif
916
917 switch (command) {
984263bc
MD
918 case SIOCSIFFLAGS:
919 if ((ifp->if_flags & IFF_UP) == 0 &&
920 (ifp->if_flags & IFF_RUNNING) != 0) {
921 /*
922 * If interface is marked down and it is running,
923 * then stop it.
924 */
925 cm_stop(sc);
984263bc
MD
926 } else if ((ifp->if_flags & IFF_UP) != 0 &&
927 (ifp->if_flags & IFF_RUNNING) == 0) {
928 /*
929 * If interface is marked up and it is stopped, then
930 * start it.
931 */
932 cm_init(sc);
933 }
934 break;
935
936 default:
bf6831e8 937 error = arc_ioctl(ifp, command, data);
984263bc
MD
938 break;
939 }
984263bc
MD
940 return (error);
941}
942
943/*
944 * watchdog routine for transmitter.
945 *
946 * We need this, because else a receiver whose hardware is alive, but whose
947 * software has not enabled the Receiver, would make our hardware wait forever
948 * Discovered this after 20 times reading the docs.
949 *
950 * Only thing we do is disable transmitter. We'll get an transmit timeout,
951 * and the int handler will have to decide not to retransmit (in case
952 * retransmission is implemented).
984263bc
MD
953 */
954
955void
956cm_watchdog(ifp)
957 struct ifnet *ifp;
958{
959 struct cm_softc *sc = ifp->if_softc;
960
961 PUTREG(CMCMD, CM_TXDIS);
962 return;
963}