/* $NetBSD: le_poll.c,v 1.6 2018/03/08 03:12:02 mrg Exp $ */ /* * Copyright (c) 1993 Adam Glass * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Adam Glass. * 4. The name of the Author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sboot.h" #include "if_lereg.h" struct { struct lereg1 *sc_r1; /* LANCE registers */ struct lereg2 *sc_r2; /* RAM */ int next_rmd; int next_tmd; } le_softc; static void le_error(const char *, struct lereg1 *); static void le_reset(u_char *); static int le_poll(void *, int); static void le_error(const char *str, struct lereg1 *ler1) { /* ler1->ler1_rap = LE_CSRO done in caller */ if (ler1->ler1_rdp & LE_C0_BABL) { printf("le0: been babbling, found by '%s'\n", str); callrom(); } if (ler1->ler1_rdp & LE_C0_CERR) { ler1->ler1_rdp = LE_C0_CERR; } if (ler1->ler1_rdp & LE_C0_MISS) { ler1->ler1_rdp = LE_C0_MISS; } if (ler1->ler1_rdp & LE_C0_MERR) { printf("le0: memory error in '%s'\n", str); callrom(); } } static void le_reset(u_char *myea) { struct lereg1 *ler1 = le_softc.sc_r1; struct lereg2 *ler2 = le_softc.sc_r2; unsigned int a; int timo = 100000, stat = 0, i; ler1->ler1_rap = LE_CSR0; ler1->ler1_rdp = LE_C0_STOP; /* do nothing until we are finished */ memset(ler2, 0, sizeof(*ler2)); ler2->ler2_mode = LE_MODE_NORMAL; ler2->ler2_padr[0] = myea[1]; ler2->ler2_padr[1] = myea[0]; ler2->ler2_padr[2] = myea[3]; ler2->ler2_padr[3] = myea[2]; ler2->ler2_padr[4] = myea[5]; ler2->ler2_padr[5] = myea[4]; ler2->ler2_ladrf0 = 0; ler2->ler2_ladrf1 = 0; a = (u_int)ler2->ler2_rmd; ler2->ler2_rlen = LE_RLEN | (a >> 16); ler2->ler2_rdra = a & LE_ADDR_LOW_MASK; a = (u_int)ler2->ler2_tmd; ler2->ler2_tlen = LE_TLEN | (a >> 16); ler2->ler2_tdra = a & LE_ADDR_LOW_MASK; ler1->ler1_rap = LE_CSR1; a = (u_int)ler2; ler1->ler1_rdp = a & LE_ADDR_LOW_MASK; ler1->ler1_rap = LE_CSR2; ler1->ler1_rdp = a >> 16; for (i = 0; i < LERBUF; i++) { a = (u_int)&ler2->ler2_rbuf[i]; ler2->ler2_rmd[i].rmd0 = a & LE_ADDR_LOW_MASK; ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN; ler2->ler2_rmd[i].rmd1_hadr = a >> 16; ler2->ler2_rmd[i].rmd2 = -LEMTU; ler2->ler2_rmd[i].rmd3 = 0; } for (i = 0; i < LETBUF; i++) { a = (u_int)&ler2->ler2_tbuf[i]; ler2->ler2_tmd[i].tmd0 = a & LE_ADDR_LOW_MASK; ler2->ler2_tmd[i].tmd1_bits = 0; ler2->ler2_tmd[i].tmd1_hadr = a >> 16; ler2->ler2_tmd[i].tmd2 = 0; ler2->ler2_tmd[i].tmd3 = 0; } ler1->ler1_rap = LE_CSR3; ler1->ler1_rdp = LE_C3_BSWP; ler1->ler1_rap = LE_CSR0; ler1->ler1_rdp = LE_C0_INIT; do { if (--timo == 0) { printf("le0: init timeout, stat = 0x%x\n", stat); break; } stat = ler1->ler1_rdp; } while ((stat & LE_C0_IDON) == 0); ler1->ler1_rdp = LE_C0_IDON; le_softc.next_rmd = 0; le_softc.next_tmd = 0; ler1->ler1_rap = LE_CSR0; ler1->ler1_rdp = LE_C0_STRT; } static int le_poll(void *pkt, int len) { struct lereg1 *ler1 = le_softc.sc_r1; struct lereg2 *ler2 = le_softc.sc_r2; unsigned int a; int length; struct lermd *rmd; ler1->ler1_rap = LE_CSR0; if ((ler1->ler1_rdp & LE_C0_RINT) != 0) ler1->ler1_rdp = LE_C0_RINT; rmd = &ler2->ler2_rmd[le_softc.next_rmd]; if (rmd->rmd1_bits & LE_R1_OWN) { return 0; } if (ler1->ler1_rdp & LE_C0_ERR) le_error("le_poll", ler1); if (rmd->rmd1_bits & LE_R1_ERR) { printf("le0_poll: rmd status 0x%x\n", rmd->rmd1_bits); length = 0; goto cleanup; } if ((rmd->rmd1_bits & (LE_R1_STP|LE_R1_ENP)) != (LE_R1_STP|LE_R1_ENP)) { printf("le_poll: chained packet\n"); callrom(); } length = rmd->rmd3; if (length >= LEMTU) { length = 0; printf("csr0 when bad things happen: %x\n", ler1->ler1_rdp); callrom(); goto cleanup; } if (length == 0) goto cleanup; length -= 4; if (length > 0) memcpy(pkt, (char *)&ler2->ler2_rbuf[le_softc.next_rmd], length); cleanup: a = (u_int)&ler2->ler2_rbuf[le_softc.next_rmd]; rmd->rmd0 = a & LE_ADDR_LOW_MASK; rmd->rmd1_hadr = a >> 16; rmd->rmd2 = -LEMTU; le_softc.next_rmd = (le_softc.next_rmd == (LERBUF - 1)) ? 0 : (le_softc.next_rmd + 1); rmd->rmd1_bits = LE_R1_OWN; return length; } int le_put(u_char *pkt, size_t len) { struct lereg1 *ler1 = le_softc.sc_r1; struct lereg2 *ler2 = le_softc.sc_r2; struct letmd *tmd; int timo = 100000, stat = 0; unsigned int a; ler1->ler1_rap = LE_CSR0; if (ler1->ler1_rdp & LE_C0_ERR) le_error("le_put(way before xmit)", ler1); tmd = &ler2->ler2_tmd[le_softc.next_tmd]; while (tmd->tmd1_bits & LE_T1_OWN) { printf("le0: output buffer busy\n"); } memcpy((char *)ler2->ler2_tbuf[le_softc.next_tmd], pkt, len); if (len < 64) tmd->tmd2 = -64; else tmd->tmd2 = -len; tmd->tmd3 = 0; if (ler1->ler1_rdp & LE_C0_ERR) le_error("le_put(before xmit)", ler1); tmd->tmd1_bits = LE_T1_STP | LE_T1_ENP | LE_T1_OWN; a = (u_int)&ler2->ler2_tbuf[le_softc.next_tmd]; tmd->tmd0 = a & LE_ADDR_LOW_MASK; tmd->tmd1_hadr = a >> 16; ler1->ler1_rdp = LE_C0_TDMD; if (ler1->ler1_rdp & LE_C0_ERR) le_error("le_put(after xmit)", ler1); do { if (--timo == 0) { printf("le0: transmit timeout, stat = 0x%x\n", stat); if (ler1->ler1_rdp & LE_C0_ERR) le_error("le_put(timeout)", ler1); break; } stat = ler1->ler1_rdp; } while ((stat & LE_C0_TINT) == 0); ler1->ler1_rdp = LE_C0_TINT; if (ler1->ler1_rdp & LE_C0_ERR) { if ((ler1->ler1_rdp & (LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_MERR)) != LE_C0_CERR) { printf("le_put: xmit error, buf %d\n", le_softc.next_tmd); le_error("le_put(xmit error)", ler1); } le_softc.next_tmd = 0; #if 0 (le_softc.next_tmd == (LETBUF - 1)) ? 0 : le_softc.next_tmd + 1; #endif if (tmd->tmd1_bits & LE_T1_ERR) printf("le0: transmit error, error = 0x%x\n", tmd->tmd3); return -1; } return len; } int le_get(u_char *pkt, size_t len, u_long timeout) { int cc; int now, then; int stopat = time() + timeout; then = 0; cc = 0; while ((now = time()) < stopat && !cc) { cc = le_poll(pkt, len); if (then != now) { #ifdef LE_DEBUG printf("%d \r", stopat - now); #endif then = now; } if (cc && (pkt[0] != myea[0] || pkt[1] != myea[1] || pkt[2] != myea[2] || pkt[3] != myea[3] || pkt[4] != myea[4] || pkt[5] != myea[5])) { cc = 0; /* ignore broadcast / multicast */ #ifdef LE_DEBUG printf("reject (%d sec left)\n", stopat - now); #endif } } #ifdef LE_DEBUG printf("\n"); #endif return cc; } void le_init(void) { int *ea = (int *)LANCE_ADDR; u_long *eram = (u_long *)ERAM_ADDR; u_long e = *ea; if ((e & 0x2fffff00) == 0x2fffff00) { printf("ERROR: ethernet address not set! Use LSAD.\n"); callrom(); } myea[0] = 0x08; myea[1] = 0x00; myea[2] = 0x3e; e = e >> 8; myea[5] = e & 0xff; e = e >> 8; myea[4] = e & 0xff; e = e >> 8; myea[3] = e; printf("le0: ethernet address: %x:%x:%x:%x:%x:%x\n", myea[0], myea[1], myea[2], myea[3], myea[4], myea[5]); memset(&le_softc, 0, sizeof(le_softc)); le_softc.sc_r1 = (struct lereg1 *)LANCE_REG_ADDR; le_softc.sc_r2 = (struct lereg2 *)(*eram - (1024*1024)); le_reset(myea); } void le_end(void) { struct lereg1 *ler1 = le_softc.sc_r1; ler1->ler1_rap = LE_CSR0; ler1->ler1_rdp = LE_C0_STOP; }