[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
ICL EtherTeam 16i NIC support for OpenBSD
>Synopsis: if_fe.c does not compile and does not support ICL cards
>Severity: non-critical
>Priority: medium
>Category: i386
>Class: change-request
>Release: OpenBSD 3.0
>Environment:
System : OpenBSD 3.0
Architecture: OpenBSD.i386
Machine : i386
>Description:
The fe network card driver contains a few bugs which prevent it
to compile correctly. Also, it does not support ICL EtherTeam
16i network cards (which use the Fujitsu MB86965 chip).
First of all, it redefines the values of ETHER_MAX_LEN and
ETHER_MIN_LEN to values without the CRC and uses this.
In netinet/if_ether.h, these values are #defined with CRC
included. I removed the offending #define's from
dev/isa/if_fe.c and changed every instance of ETHER_MAX_LEN
in if_fe.c to (ETHER_MAX_LEN - ETHER_CRC_LEN)
(same for ETHER_MIN_LEN).
Also, if_fe.c uses a bogus ETHER_HDR_SIZE, this should be
ETHER_HDR_LEN (as defined in if_ether.h), all instances changed.
Also, some function prototypes were wrong or missing (e.g.
fe_rint and fe_droppacket).
Then I added support for the ICL EtherTeam 16i ISA network
cards.
I had to change fe_read_eeprom() so it would support
reading EEPROMs of different sizes, because the ICL card's
EEPROM is 128 bytes (#define FE_EEPROM_SIZE_ICL 128 in
dev/ic/mb8696reg.h).
The fe_probe_ati() routine already detected the card as an
unknown type (RE2000) but didn't have the needed support.
That's why I added it there.
I also added the necessary valid_Ether_p() and fe_init_icl()
routines.
>How-To-Repeat:
Add a line like this to the kernel configuration:
fe0 at isa? port 0x2a0 irq 9
The kernel compilation will issue some warnings.
>Fix:
Attached is a patch to the OpenBSD 3.0 kernel source
tree. Most of the code I added comes from the FreeBSD
driver.
diff -ru sys/dev/ic/mb86960reg.h mytree/dev/ic/mb86960reg.h
--- sys/dev/ic/mb86960reg.h Wed Oct 31 17:00:22 2001
+++ mytree/dev/ic/mb86960reg.h Wed Oct 31 16:58:59 2001
@@ -304,6 +304,14 @@
/* Number of bytes in an EEPROM accessible through 86965. */
#define FE_EEPROM_SIZE 32
+/* Datasheet for 86965 explicitly states that it only supports serial
+ * EEPROM with 16 words (32 bytes) capacity. (I.e., 93C06.) However,
+ * ones with 64 words (128 bytes) are available in the market, namely
+ * 93C46, and are also fully compatible with 86965. It is known that
+ * some boards (e.g., ICL) actually have 93C46 on them and use extra
+ * storage to keep various config info. */
+#define FE_EEPROM_SIZE_ICL 128
+
/* Offset for JLI config; automatically copied into BMPR19 at startup. */
#define FE_EEPROM_CONF 0
diff -ru sys/dev/isa/if_fe.c mytree/dev/isa/if_fe.c
--- sys/dev/isa/if_fe.c Wed Oct 31 17:00:22 2001
+++ mytree/dev/isa/if_fe.c Wed Oct 31 16:53:07 2001
@@ -155,6 +155,9 @@
/* PCMCIA by Fujitsu. */
FE_TYPE_MBH10302,
FE_TYPE_MBH10304,
+
+ /* ICL EtherTeam 16i cards */
+ FE_TYPE_ETH16I,
};
/*
@@ -213,14 +216,23 @@
void fe_init_mbh __P((struct fe_softc *));
int fe_get_packet __P((struct fe_softc *, int));
void fe_stop __P((struct fe_softc *));
-void fe_tint __P((/*struct fe_softc *, u_char*/));
-void fe_rint __P((/*struct fe_softc *, u_char*/));
+void fe_tint __P((struct fe_softc *, u_char));
+void fe_rint __P((struct fe_softc *, u_char));
static inline
void fe_xmit __P((struct fe_softc *));
void fe_write_mbufs __P((struct fe_softc *, struct mbuf *));
void fe_getmcaf __P((struct arpcom *, u_char *));
void fe_setmode __P((struct fe_softc *));
void fe_loadmar __P((struct fe_softc *));
+
+void fe_read_eeprom __P((struct fe_softc *, u_char*, u_char));
+static inline
+void fe_droppacket __P((struct fe_softc *));
+
+static inline
+int valid_Ether_p __P((u_char const * addr, unsigned vendor));
+void fe_init_icl __P((struct fe_softc *));
+
#if FE_DEBUG >= 3
void fe_dump __P((int, struct fe_softc *));
#endif
@@ -233,10 +245,6 @@
NULL, "fe", DV_IFNET
};
-/* Ethernet constants. To be defined in if_ehter.h? FIXME. */
-#define ETHER_MIN_LEN 60 /* with header, without CRC. */
-#define ETHER_MAX_LEN 1514 /* with header, without CRC. */
-
/*
* Fe driver specific constants which relate to 86960/86965.
*/
@@ -351,9 +359,10 @@
}
void
-fe_read_eeprom(sc, data)
+fe_read_eeprom(sc, data, size)
struct fe_softc *sc;
u_char *data;
+ u_char size;
{
int iobase = sc->sc_iobase;
int bmpr16 = iobase + FE_BMPR16;
@@ -361,7 +370,7 @@
u_char n, val, bit;
/* Read bytes from EEPROM; two bytes per an iterration. */
- for (n = 0; n < FE_EEPROM_SIZE / 2; n++) {
+ for (n = 0; n < size / 2; n++) {
/* Reset the EEPROM interface. */
outb(bmpr16, 0x00);
outb(bmpr17, 0x00);
@@ -400,12 +409,16 @@
#if FE_DEBUG >= 3
/* Report what we got. */
- data -= FE_EEPROM_SIZE;
+ /* This will only output the first 32 bytes of the
+ EEPROM. Change it if debugging for cards with
+ larger EEPROM (e.g. ICL's 128 byte EEPROM) */
+ data -= size;
log(LOG_INFO, "%s: EEPROM at %04x:"
" %02x%02x%02x%02x %02x%02x%02x%02x -"
" %02x%02x%02x%02x %02x%02x%02x%02x -"
" %02x%02x%02x%02x %02x%02x%02x%02x -"
" %02x%02x%02x%02x %02x%02x%02x%02x\n",
+
sc->sc_dev.dv_xname, iobase,
data[ 0], data[ 1], data[ 2], data[ 3],
data[ 4], data[ 5], data[ 6], data[ 7],
@@ -414,10 +427,51 @@
data[16], data[17], data[18], data[19],
data[20], data[21], data[22], data[23],
data[24], data[25], data[26], data[27],
- data[28], data[29], data[30], data[31]);
+ data[28], data[29], data[30], data[31]
+);
+#endif
+}
+
+static inline
+int
+valid_Ether_p (u_char const * addr, unsigned vendor)
+{
+#ifdef FE_DEBUG
+ printf("fe?: validating %6D against %06x\n", addr, ":", vendor);
#endif
+
+ /* All zero is not allowed as a vendor code. */
+ if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0) return 0;
+
+ switch (vendor) {
+ case 0x000000:
+ /* Legal Ethernet address (stored in ROM) must have
+ its Group and Local bits cleared. */
+ if ((addr[0] & 0x03) != 0) return 0;
+ break;
+ case 0x020000:
+ /* Same as above, but a local address is allowed in
+ this context. */
+ if ((addr[0] & 0x01) != 0) return 0;
+ break;
+ default:
+ /* Make sure the vendor part matches if one is given. */
+ if ( addr[0] != ((vendor >> 16) & 0xFF)
+ || addr[1] != ((vendor >> 8) & 0xFF)
+ || addr[2] != ((vendor ) & 0xFF)) return 0;
+ break;
+ }
+
+ /* Host part must not be all-zeros nor all-ones. */
+ if (addr[3] == 0xFF && addr[4] == 0xFF && addr[5] == 0xFF) return 0;
+ if (addr[3] == 0x00 && addr[4] == 0x00 && addr[5] == 0x00) return 0;
+
+ /* Given addr looks like an Ethernet address. */
+ return 1;
}
+
+
/*
* Hardware (vendor) specific probe routines.
*/
@@ -596,6 +650,11 @@
/*
* Probe and initialization for Allied-Telesis AT1700/RE2000 series.
+ * It happens to detect the ICL EtherTeam 16i cards too, so let's add
+ * that support here. Maybe a "fe_probe_jli" would be a better name
+ * now, as they all use the chip's JLI mode and those cards are very
+ * alike (the FreeBSD driver is like this, but has sub-probe routines
+ * for the different cards.
*/
int
fe_probe_ati(sc, ia)
@@ -604,17 +663,18 @@
{
int i, n;
int iobase = sc->sc_iobase;
- u_char eeprom[FE_EEPROM_SIZE];
+ u_char eeprom[FE_EEPROM_SIZE_ICL];
u_char save16, save17;
int irq;
static int const iomap[8] =
{ 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300 };
- static int const irqmap[4][4] = {
+ static int const irqmap[5][4] = {
{ 3, 4, 5, 9 },
{ 10, 11, 12, 15 },
{ 3, 11, 5, 15 },
{ 10, 11, 14, 15 },
+ { 9, 10, 5, 15 }, /* This is for ICL EtherTeam 16i */
};
static struct fe_simple_probe_struct const probe_table[] = {
{ FE_DLCR2, 0x70, 0x00 },
@@ -677,7 +737,7 @@
* at this stage, but I cannot test the presense of the chip
* any further without reading EEPROM. FIXME.
*/
- fe_read_eeprom(sc, eeprom);
+ fe_read_eeprom(sc, eeprom, FE_EEPROM_SIZE );
/* Make sure the EEPROM is turned off. */
outb(iobase + FE_BMPR16, 0);
@@ -713,33 +773,118 @@
break;
}
+ if (sc->type == FE_TYPE_RE2000) { /* if unknown, probe for ICL */
+ u_char d6;
+ int i;
+
+ /* ICL has bigger EEPROM, so read _all_ of it now. */
+ fe_read_eeprom(sc, eeprom, FE_EEPROM_SIZE_ICL);
+
+ /* Make sure the EEPROM contains ICL bit pattern. */
+ for (i = 24; i < 39; i++) {
+ if (eeprom[i] != 0x20 && (eeprom[i] & 0xF0) != 0x30)
+ goto no_icl;
+ }
+ for (i = 112; i < 122; i++) {
+ if (eeprom[i] != 0x20 && (eeprom[i] & 0xF0) != 0x30)
+ goto no_icl;
+ }
+
+ /* Make sure the EEPROM contains ICL's permanent station
+ address. If it isn't, probably this board is not an
+ ICL's. */
+ if (!valid_Ether_p(eeprom+122, 0x00004B)) goto no_icl;
+
+ /* Check if the "configured" Ethernet address in the EEPROM is
+ * valid. Use it if it is, or use the
+ * "permanent" address instead. */
+ if (valid_Ether_p(eeprom+4, 0x020000)) {
+ /* The configured address is valid. Use it. */
+ bcopy(eeprom+4, sc->sc_enaddr, ETHER_ADDR_LEN);
+ } else {
+ /* configured address invalid. Use permanent. */
+ bcopy(eeprom+122, sc->sc_enaddr, ETHER_ADDR_LEN);
+ }
+
+ /* Determine model */
+ switch (eeprom[0x5E]) {
+ case 0:
+ sc->typestr = "EtherTeam16i/COMBO";
+ break;
+ case 1:
+ sc->typestr = "EtherTeam16i/TP";
+ break;
+ case 2:
+ sc->typestr = "EtherTeam16i/ErgoPro";
+ break;
+ case 4:
+ sc->typestr = "EtherTeam16i/DUO";
+ break;
+ default:
+ sc->typestr = "EtherTeam16i";
+ printf(
+ "%s: unknown model code %02x for EtherTeam16i\n",
+ sc->sc_dev.dv_xname, eeprom[0x5E]);
+ break;
+ }
+ sc->type = FE_TYPE_ETH16I;
+
+ /* ICL has "fat" models. We have to program 86965 to properly
+ reflect the hardware. */
+ d6 = sc->proto_dlcr6 & ~(FE_D6_BUFSIZ | FE_D6_BBW);
+ switch ((eeprom[0x61] << 8) | eeprom[0x60]) {
+ case 0x2008:
+ d6 |= FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+ break;
+
+ case 0x4010:
+ d6 |= FE_D6_BUFSIZ_64KB | FE_D6_BBW_WORD
+ | FE_D6_TXBSIZ_2x4KB | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+ break;
+ default:
+ /* We can't support it, since we don't
+ know which bits to set in DLCR6. */
+ printf("%s: unknown SRAM config for ICL\n",
+ sc->sc_dev.dv_xname);
+ goto no_icl;
+ }
+ sc->proto_dlcr6 = d6;
+
+ /* Use default media from EEPROM */
+ sc->proto_bmpr13 = eeprom[0x29];
+ }
+no_icl:
/*
* Try to determine IRQ settings.
* Different models use different ranges of IRQs.
*/
n = (inb(iobase + FE_BMPR19) & FE_B19_IRQ) >> FE_B19_IRQ_SHIFT;
- switch (eeprom[FE_ATI_EEP_REVISION] & 0xf0) {
- case 0x30:
- irq = irqmap[3][n];
- break;
- case 0x10:
- case 0x50:
- irq = irqmap[2][n];
- break;
- case 0x40:
- case 0x60:
- if (eeprom[FE_ATI_EEP_MAGIC] & 0x04) {
- irq = irqmap[1][n];
+ if(sc->type != FE_TYPE_ETH16I) {
+ switch (eeprom[FE_ATI_EEP_REVISION] & 0xf0) {
+ case 0x30:
+ irq = irqmap[3][n];
+ break;
+ case 0x10:
+ case 0x50:
+ irq = irqmap[2][n];
+ break;
+ case 0x40:
+ case 0x60:
+ if (eeprom[FE_ATI_EEP_MAGIC] & 0x04) {
+ irq = irqmap[1][n];
+ break;
+ }
+ default:
+ irq = irqmap[0][n];
break;
}
- default:
- irq = irqmap[0][n];
- break;
- }
-
+ } else irq = irqmap[4][n]; /* For ICL cards */
+
if (ia->ia_irq != IRQUNK) {
if (ia->ia_irq != irq) {
- printf("%s: irq mismatch; kernel configured %d != board configured %d\n",
+ printf(
+ "%s: irq mismatch; kernel configured %d != board configured %d\n",
sc->sc_dev.dv_xname, ia->ia_irq, irq);
return (0);
}
@@ -751,42 +896,48 @@
*/
/* Get our station address from EEPROM. */
- bcopy(eeprom + FE_ATI_EEP_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN);
+ if (sc->type != FE_TYPE_ETH16I) {
+ bcopy(eeprom + FE_ATI_EEP_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN);
- /* Make sure we got a valid station address. */
- if ((sc->sc_enaddr[0] & 0x03) != 0x00
- || (sc->sc_enaddr[0] == 0x00
- && sc->sc_enaddr[1] == 0x00
- && sc->sc_enaddr[2] == 0x00))
- goto fail;
+ /* Make sure we got a valid station address. */
+ if ((sc->sc_enaddr[0] & 0x03) != 0x00
+ || (sc->sc_enaddr[0] == 0x00
+ && sc->sc_enaddr[1] == 0x00
+ && sc->sc_enaddr[2] == 0x00))
+ goto fail;
- /* Should find all register prototypes here. FIXME. */
- sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */
- sc->proto_dlcr5 = 0;
- sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC;
-#if 0 /* XXXX Should we use this? */
- sc->proto_bmpr13 = eeprom[FE_ATI_EEP_MEDIA];
+#if 0 /* XXXX Should we use this? */
+ sc->proto_bmpr13 = eeprom[FE_ATI_EEP_MEDIA];
#else
- sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO;
+ sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO;
#endif
- /*
- * Program the 86965 as follows:
- * SRAM: 32KB, 100ns, byte-wide access.
- * Transmission buffer: 4KB x 2.
- * System bus interface: 16 bits.
- * We cannot change these values but TXBSIZE, because they
- * are hard-wired on the board. Modifying TXBSIZE will affect
- * the driver performance.
- */
- sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
- | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+ /*
+ * Program the 86965 as follows:
+ * SRAM: 32KB, 100ns, byte-wide access.
+ * Transmission buffer: 4KB x 2.
+ * System bus interface: 16 bits.
+ * We cannot change these values but TXBSIZE, because they
+ * are hard-wired on the board. Modifying TXBSIZE will affect
+ * the driver performance.
+ */
+ sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+
+
+ } /* end if(sc->type==FE_TYPE_ETH16I) */
+
+ /* Should find all register prototypes here. FIXME. */
+ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */
+ sc->proto_dlcr5 = 0;
+ sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC;
+
#if FE_DEBUG >= 3
- log(LOG_INFO, "%s: ATI found\n", sc->sc_dev.dv_xname);
- fe_dump(LOG_INFO, sc);
+ log(LOG_INFO, "%s: ATI found\n", sc->sc_dev.dv_xname);
+ fe_dump(LOG_INFO, sc);
#endif
-
+
/* Initialize 86965. */
outb(iobase + FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE);
delay(200);
@@ -814,6 +965,17 @@
return (0);
}
+void
+fe_init_icl(sc) /* ICL init hook */
+ struct fe_softc *sc;
+{
+ /* "Reset" by writing into a magic location. */
+ DELAY(200);
+ outb( sc->sc_iobase + 0x1E, inb(sc->sc_iobase + 0x1E) );
+ DELAY(300);
+}
+
+
/*
* Probe and initialization for Fujitsu MBH10302 PCMCIA Ethernet interface.
*/
@@ -1278,6 +1440,16 @@
outb(sc->sc_iobase + FE_DLCR2, FE_TMASK);
outb(sc->sc_iobase + FE_DLCR3, FE_RMASK);
+ /* The ICL board requires a bit to be set in DLCR4.
+ This needs to be done here, just before enabling the DLC,
+ so I couldn't put it in fe_init_icl(). */
+ if (sc->type == FE_TYPE_ETH16I) {
+ if((sc->proto_bmpr13)==1) /* if media = AUI */
+ outb(sc->sc_iobase + FE_DLCR4, sc->proto_dlcr4 | FE_D4_CNTRL);
+ else
+ outb(sc->sc_iobase + FE_DLCR4, sc->proto_dlcr4 & ~FE_D4_CNTRL);
+ }
+
/* Enable transmitter and receiver. */
delay(200);
outb(sc->sc_iobase + FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_ENABLE);
@@ -1479,7 +1651,7 @@
* (i.e., minimum packet sized) packets rapidly. An 8KB
* buffer can hold 130 blocks of 62 bytes long...
*/
- if (sc->txb_free < ETHER_MAX_LEN + FE_DATA_LEN_LEN) {
+ if (sc->txb_free < (ETHER_MAX_LEN - ETHER_CRC_LEN) + FE_DATA_LEN_LEN) {
/* No room. */
goto indicate_active;
}
@@ -1762,12 +1934,12 @@
*
* Is this statement true? FIXME.
*/
- if (len > ETHER_MAX_LEN || len < ETHER_HDR_SIZE) {
+ if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN) || len < ETHER_HDR_LEN) {
#if FE_DEBUG >= 2
log(LOG_WARNING,
"%s: received a %s packet? (%u bytes)\n",
sc->sc_dev.dv_xname,
- len < ETHER_HDR_SIZE ? "partial" : "big", len);
+ len < ETHER_HDR_LEN ? "partial" : "big", len);
#endif
ifp->if_ierrors++;
fe_droppacket(sc);
@@ -1781,7 +1953,7 @@
* if it carries data for upper layer.
*/
#if FE_DEBUG >= 2
- if (len < ETHER_MIN_LEN) {
+ if (len < (ETHER_MIN_LEN - ETHER_CRC_LEN)) {
log(LOG_WARNING,
"%s: received a short packet? (%u bytes)\n",
sc->sc_dev.dv_xname, len);
@@ -2085,7 +2257,9 @@
struct mbuf *m;
{
int bmpr8 = sc->sc_iobase + FE_BMPR8;
+#if FE_DEBUG >= 2
struct mbuf *mp;
+#endif
u_char *data;
u_short savebyte; /* WARNING: Architecture dependent! */
int totlen, len, wantbyte;
@@ -2123,10 +2297,10 @@
* it should be a bug of upper layer. We just ignore it.
* ... Partial (too short) packets, neither.
*/
- if (totlen > ETHER_MAX_LEN || totlen < ETHER_HDR_SIZE) {
+ if (totlen > (ETHER_MAX_LEN - ETHER_CRC_LEN) || totlen < ETHER_HDR_LEN) {
log(LOG_ERR, "%s: got a %s packet (%u bytes) to send\n",
sc->sc_dev.dv_xname,
- totlen < ETHER_HDR_SIZE ? "partial" : "big", totlen);
+ totlen < ETHER_HDR_LEN ? "partial" : "big", totlen);
sc->sc_arpcom.ac_if.if_oerrors++;
return;
}
@@ -2140,20 +2314,20 @@
* packet in the transmission buffer, we can skip the
* padding process. It may gain performance slightly. FIXME.
*/
- outw(bmpr8, max(totlen, ETHER_MIN_LEN));
+ outw(bmpr8, max(totlen, (ETHER_MIN_LEN - ETHER_CRC_LEN)));
/*
* Update buffer status now.
* Truncate the length up to an even number, since we use outw().
*/
totlen = (totlen + 1) & ~1;
- sc->txb_free -= FE_DATA_LEN_LEN + max(totlen, ETHER_MIN_LEN);
+ sc->txb_free -= FE_DATA_LEN_LEN + max(totlen, (ETHER_MIN_LEN - ETHER_CRC_LEN));
sc->txb_count++;
#if FE_DELAYED_PADDING
/* Postpone the packet padding if necessary. */
- if (totlen < ETHER_MIN_LEN)
- sc->txb_padding = ETHER_MIN_LEN - totlen;
+ if (totlen < (ETHER_MIN_LEN - ETHER_CRC_LEN))
+ sc->txb_padding = (ETHER_MIN_LEN - ETHER_CRC_LEN) - totlen;
#endif
/*
@@ -2200,7 +2374,7 @@
/*
* Pad the packet to the minimum length if necessary.
*/
- len = (ETHER_MIN_LEN >> 1) - (totlen >> 1);
+ len = ((ETHER_MIN_LEN - ETHER_CRC_LEN) >> 1) - (totlen >> 1);
while (--len >= 0)
outw(bmpr8, 0);
#endif