/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1997 Apple Computer, Inc. * * ethernet driver for mace on-board ethernet * * HISTORY * * Dieter Siegmund (dieter@next.com) Thu Feb 27 18:25:33 PST 1997 * - ripped off code from MK/LINUX, turned it into a polled-mode * driver for the PCI (8500) class machines * * Dieter Siegmund (dieter@next.com) Fri Mar 21 12:41:29 PST 1997 * - reworked to support a BSD-style interface, and to support kdb polled * interface and interrupt-driven interface concurrently * * Justin Walker (justin@apple.com) Tue May 20 10:29:29 PDT 1997 * - Added multicast support * * Dieter Siegmund (dieter@next.com) Thu May 29 15:02:29 PDT 1997 * - fixed problem with sending arp packets for ip address 0.0.0.0 * - use kdp_register_send_receive() instead of defining * en_send_pkt/en_recv_pkt routines to avoid name space * collisions with IOEthernetDebugger and allow these routines to be * overridden by a driverkit-style driver * * Dieter Siegmund (dieter@apple.com) Tue Jun 24 18:29:15 PDT 1997 * - don't let the adapter auto-strip 802.3 receive frames, it messes * up the frame size logic * * Dieter Siegmund (dieter@apple.com) Tue Aug 5 16:24:52 PDT 1997 * - handle multicast address deletion correctly */ #ifdef MACE_DEBUG /* * Caveat: MACE_DEBUG delimits some code that is getting kind of * stale. Before blindly turning on MACE_DEBUG for your * testing, take a look at the code enabled by it to check * that it is reasonably sane. */ #endif #include #include #define RECEIVE_INT DBDMA_INT_ALWAYS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "if_en.h" #include "mace.h" extern int kdp_flag; #if NBPFILTER > 0 #include #endif static void polled_send_pkt(char * data, int len); static void polled_receive_pkt(char *data, int *len, int timeout_ms); void mace_dbdma_rx_intr(int unit, void *, void *); void mace_dbdma_tx_intr(int, void *, void *); void mace_pci_intr(int, void *); void mace_service_queue(struct ifnet * ifp); #ifdef MACE_DEBUG static int mace_watchdog(); #endif static __inline__ vm_offset_t KVTOPHYS(vm_offset_t v) { return (v); } typedef int (*funcptr)(char *, int, void *); #ifdef MACE_DEBUG static int macAddrsEqual(unsigned char * one, unsigned char * two) { int i; for (i = 0; i < NUM_EN_ADDR_BYTES; i++) if (*one++ != *two++) return 0; return 1; } #endif static __inline__ int isprint(unsigned char c) { return (c >= 0x20 && c <= 0x7e); } static void printEtherHeader(enet_addr_t * dh, enet_addr_t * sh, u_short etype) { u_char * dhost = dh->ether_addr_octet; u_char * shost = sh->ether_addr_octet; printf("Dst: %x:%x:%x:%x:%x:%x Src: %x:%x:%x:%x:%x:%x Type: 0x%x\n", dhost[0], dhost[1], dhost[2], dhost[3], dhost[4], dhost[5], shost[0], shost[1], shost[2], shost[3], shost[4], shost[5], etype); } static void printData(u_char * data_p, int n_bytes) { #define CHARS_PER_LINE 16 char line_buf[CHARS_PER_LINE + 1]; int line_pos; int offset; for (line_pos = 0, offset = 0; offset < n_bytes; offset++, data_p++) { if (line_pos == 0) { printf("%04d ", offset); } line_buf[line_pos] = isprint(*data_p) ? *data_p : '.'; printf(" %02x", *data_p); line_pos++; if (line_pos == CHARS_PER_LINE) { line_buf[CHARS_PER_LINE] = '\0'; printf(" %s\n", line_buf); line_pos = 0; } } if (line_pos) { /* need to finish up the line */ for (; line_pos < CHARS_PER_LINE; line_pos++) { printf(" "); line_buf[line_pos] = ' '; } line_buf[CHARS_PER_LINE] = '\0'; printf(" %s\n", line_buf); } } static void printEtherPacket(enet_addr_t * dhost, enet_addr_t * shost, u_short type, u_char * data_p, int n_bytes) { printEtherHeader(dhost, shost, type); printData(data_p, n_bytes); } void printContiguousEtherPacket(u_char * data_p, int n_bytes) { printEtherPacket((enet_addr_t *)data_p, (enet_addr_t *)(data_p + NUM_EN_ADDR_BYTES), *((u_short *)(data_p + (NUM_EN_ADDR_BYTES * 2))), data_p, n_bytes); } mace_t mace; #define MACE_DMA_AREA_SIZE (ETHER_RX_NUM_DBDMA_BUFS * ETHERNET_BUF_SIZE + PG_SIZE) static unsigned long mace_rx_dma_area[(MACE_DMA_AREA_SIZE + sizeof(long))/sizeof(long)]; static unsigned long mace_tx_dma_area[(ETHERNET_BUF_SIZE + PG_SIZE + sizeof(long))/sizeof(long)]; /* * mace_get_hwid * * This function computes the Ethernet Hardware address * from PROM. (Its best not to ask how this is done.) */ unsigned char mace_swapbits(unsigned char bits) { unsigned char mask = 0x1, i, newbits = 0; for (i = 0x80; i; mask <<= 1, i >>=1) { if (bits & mask) newbits |= i; } return newbits; } void mace_get_hwid(unsigned char *hwid_addr, mace_t * m) { int i; for (i = 0; i < NUM_EN_ADDR_BYTES; i++, hwid_addr += 16) { m->macaddr[i] = mace_swapbits(*hwid_addr); } } /* * mace_reset * * Reset the board.. */ void mace_reset() { dbdma_reset(DBDMA_ETHERNET_RV); dbdma_reset(DBDMA_ETHERNET_TX); } /* * mace_geteh: * * This function gets the ethernet address (array of 6 unsigned * bytes) from the MACE board registers. * */ void mace_geteh(char *ep) { int i; unsigned char ep_temp; mace.ereg->iac = IAC_PHYADDR; eieio(); for (i = 0; i < ETHER_ADD_SIZE; i++) { ep_temp = mace.ereg->padr; eieio(); *ep++ = ep_temp; } } /* * mace_seteh: * * This function sets the ethernet address (array of 6 unsigned * bytes) on the MACE board. */ static void mace_seteh(char *ep) { int i; unsigned char status; if (mace.chip_id != MACE_REVISION_A2) { mace.ereg->iac = IAC_ADDRCHG|IAC_PHYADDR; eieio(); while ((status = mace.ereg->iac)) { if ((status & IAC_ADDRCHG) == 0) { eieio(); break; } eieio(); } } else { /* start to load the address.. */ mace.ereg->iac = IAC_PHYADDR; eieio(); } for (i = 0; i < NUM_EN_ADDR_BYTES; i++) { mace.ereg->padr = *(ep+i); eieio(); } return; } /* * mace_setup_dbdma * * Setup various dbdma pointers. */ void mace_setup_dbdma() { mace_t * m = &mace; int i; dbdma_command_t * d; vm_offset_t address; dbdma_regmap_t * regmap; #define ALIGN_MASK 0xfffffffcUL if (m->rv_dma_area == 0) { m->rv_dma_area = (unsigned char *) ((((unsigned long)mace_rx_dma_area) + 3) & ALIGN_MASK); m->rv_dma = dbdma_alloc(ETHER_RX_NUM_DBDMA_BUFS + 2); m->tx_dma = dbdma_alloc(TX_NUM_DBDMA); m->tx_dma_area = (unsigned char *) ((((unsigned long)mace_tx_dma_area) + 3) & ALIGN_MASK); } /* set up a ring of buffers */ d = m->rv_dma; for (i = 0; i < ETHER_RX_NUM_DBDMA_BUFS; i++, d++) { address = (vm_offset_t) KVTOPHYS((vm_offset_t)&m->rv_dma_area[i*ETHERNET_BUF_SIZE]); DBDMA_BUILD(d, DBDMA_CMD_IN_LAST, 0, ETHERNET_BUF_SIZE, address, RECEIVE_INT, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); } /* stop when we hit the end of the list */ DBDMA_BUILD(d, DBDMA_CMD_STOP, 0, 0, 0, RECEIVE_INT, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); d++; /* branch to command at "address" ie. element 0 of the "array" */ DBDMA_BUILD(d, DBDMA_CMD_NOP, 0, 0, 0, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); address = (vm_offset_t) KVTOPHYS((vm_offset_t)m->rv_dma); dbdma_st4_endian(&d->d_cmddep, address); m->rv_head = 0; m->rv_tail = ETHER_RX_NUM_DBDMA_BUFS; /* always contains DBDMA_CMD_STOP */ /* stop/init/restart dma channel */ dbdma_reset(DBDMA_ETHERNET_RV); dbdma_reset(DBDMA_ETHERNET_TX); /* Set the wait value.. */ regmap = DBDMA_REGMAP(DBDMA_ETHERNET_RV); dbdma_st4_endian(®map->d_wait, DBDMA_SET_CNTRL(0x00)); /* Set the tx wait value */ regmap = DBDMA_REGMAP(DBDMA_ETHERNET_TX); dbdma_st4_endian(®map->d_wait, DBDMA_SET_CNTRL(0x20)); flush_cache_v((vm_offset_t)m->rv_dma, sizeof(dbdma_command_t) * (ETHER_RX_NUM_DBDMA_BUFS + 2)); /* start receiving */ dbdma_start(DBDMA_ETHERNET_RV, m->rv_dma); } #ifdef MACE_DEBUG static unsigned char testBuffer[PG_SIZE * 4]; static unsigned char testMsg[] = "mace ethernet interface test"; static void send_test_packet() { unsigned char * tp; bzero(testBuffer, sizeof(testBuffer)); tp = testBuffer; /* send self-addressed packet */ bcopy(&mace.macaddr[0], tp, NUM_EN_ADDR_BYTES); tp += NUM_EN_ADDR_BYTES; bcopy(&mace.macaddr[0], tp, NUM_EN_ADDR_BYTES); tp += NUM_EN_ADDR_BYTES; *tp++ = 0; *tp++ = 0; bcopy(testMsg, tp, sizeof(testMsg)); polled_send_pkt(testBuffer, 80); return; } #endif /* * Function: init_mace * * Purpose: * Called early on, initializes the adapter and readies it for * kdb kernel debugging. */ void init_mace() { unsigned char status; mace_t * m = &mace; struct mace_board * ereg; int mpc = 0; /* * Only use in-kernel driver for early debugging (bootargs: kdp=1 or kdp=3) */ if ( (kdp_flag & 1) == 0 ) { return; } bzero(&mace, sizeof(mace)); /* get the ethernet registers' mapped address */ ereg = m->ereg = (struct mace_board *) POWERMAC_IO(PCI_ETHERNET_BASE_PHYS); mace_get_hwid((unsigned char *)POWERMAC_IO(PCI_ETHERNET_ADDR_PHYS), m); /* Reset the board & AMIC.. */ mace_reset(); /* grab the MACE chip rev */ m->chip_id = (ereg->chipid2 << 8 | ereg->chipid1); /* don't auto-strip for 802.3 */ m->ereg->rcvfc &= ~(RCVFC_ASTRPRCV); /* set the ethernet address */ mace_seteh(mace.macaddr); { unsigned char macaddr[NUM_EN_ADDR_BYTES]; mace_geteh(macaddr); printf("mace ethernet [%02x:%02x:%02x:%02x:%02x:%02x]\n", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); } /* Now clear the Multicast filter */ if (m->chip_id != MACE_REVISION_A2) { ereg->iac = IAC_ADDRCHG|IAC_LOGADDR; eieio(); while ((status = ereg->iac)) { if ((status & IAC_ADDRCHG) == 0) break; eieio(); } eieio(); } else { ereg->iac = IAC_LOGADDR; eieio(); } { int i; for (i=0; i < 8; i++) { ereg->ladrf = 0; eieio(); } } /* register interrupt routines */ mace_setup_dbdma(); /* Start the chip... */ m->ereg->maccc = MACCC_ENXMT|MACCC_ENRCV; eieio(); { volatile char ch = mace.ereg->ir; eieio(); } delay(500); /* paranoia */ mace.ereg->imr = 0xfe; eieio(); /* register our debugger routines */ kdp_register_send_receive((kdp_send_t)polled_send_pkt, (kdp_receive_t)polled_receive_pkt); #if 0 printf("Testing 1 2 3\n"); send_test_packet(); printf("Testing 1 2 3\n"); send_test_packet(); printf("Testing 1 2 3\n"); send_test_packet(); do { static unsigned char buf[ETHERNET_BUF_SIZE]; int len; int nmpc = mace.ereg->mpc; eieio(); if (nmpc > mpc) { mpc = nmpc; printf("mpc %d\n", mpc); } polled_receive_pkt(buf, &len, 100); if (len > 0) { printf("rx %d\n", len); printContiguousEtherPacket(buf, len); } } while(1); #endif return; } #ifdef MACE_DEBUG static void txstatus(char * msg) { volatile dbdma_regmap_t * dmap = DBDMA_REGMAP(DBDMA_ETHERNET_TX); volatile unsigned long status; volatile unsigned long intr; volatile unsigned long branch; volatile unsigned long wait; status = dbdma_ld4_endian(&dmap->d_status); eieio(); intr = dbdma_ld4_endian(&dmap->d_intselect); eieio(); branch = dbdma_ld4_endian(&dmap->d_branch); eieio(); wait = dbdma_ld4_endian(&dmap->d_wait); eieio(); printf("(%s s=0x%x i=0x%x b=0x%x w=0x%x)", msg, status, intr, branch, wait); return; } #endif static void tx_dbdma(char * data, int len) { unsigned long count; dbdma_command_t * d; unsigned long page; d = mace.tx_dma; page = ((unsigned long) data) & PG_MASK; if ((page + len) <= PG_SIZE) { /* one piece dma */ DBDMA_BUILD(d, DBDMA_CMD_OUT_LAST, DBDMA_KEY_STREAM0, len, (vm_offset_t) KVTOPHYS((vm_offset_t) data), DBDMA_INT_NEVER, DBDMA_WAIT_IF_FALSE, DBDMA_BRANCH_NEVER); } else { /* two piece dma */ count = PG_SIZE - page; DBDMA_BUILD(d, DBDMA_CMD_OUT_MORE, DBDMA_KEY_STREAM0, count, (vm_offset_t)KVTOPHYS((vm_offset_t) data), DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); d++; DBDMA_BUILD(d, DBDMA_CMD_OUT_LAST, DBDMA_KEY_STREAM0, len - count, (vm_offset_t) KVTOPHYS((vm_offset_t)((unsigned char *)data + count)), DBDMA_INT_NEVER, DBDMA_WAIT_IF_FALSE, DBDMA_BRANCH_NEVER); } d++; DBDMA_BUILD(d, DBDMA_CMD_LOAD_QUAD, DBDMA_KEY_SYSTEM, 1, KVTOPHYS((vm_offset_t) &mace.ereg->xmtfs),DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); d++; DBDMA_BUILD(d, DBDMA_CMD_LOAD_QUAD, DBDMA_KEY_SYSTEM, 1, KVTOPHYS((vm_offset_t) &mace.ereg->ir), DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); d++; DBDMA_BUILD(d, DBDMA_CMD_STOP, 0, 0, 0, 0, 0, 0); flush_cache_v((vm_offset_t)mace.tx_dma, sizeof(dbdma_command_t) * TX_NUM_DBDMA); dbdma_start(DBDMA_ETHERNET_TX, mace.tx_dma); return; } static void waitForDBDMADone(char * msg) { { /* wait for tx dma completion */ volatile dbdma_regmap_t * dmap = DBDMA_REGMAP(DBDMA_ETHERNET_TX); int i; volatile unsigned long val; i = 0; do { val = dbdma_ld4_endian(&dmap->d_status); eieio(); delay(50); i++; } while ((i < 100000) && (val & DBDMA_CNTRL_ACTIVE)); if (i == 100000) printf("mace(%s): tx_dbdma poll timed out 0x%x", msg, val); } } void mace_service_queue(struct ifnet * ifp) { unsigned char * buf_p; struct mbuf * m; struct mbuf * mp; int len; if (mace.tx_busy) { /* transmit in progress? */ return; } IF_DEQUEUE(&(ifp->if_snd), m); if (m == 0) { return; } len = m->m_pkthdr.len; if (len > ETHERMAXPACKET) { printf("mace_start: packet too big (%d), dropping\n", len); m_freem(m); return; } buf_p = mace.tx_dma_area; if (m->m_nextpkt) { printf("mace: sending more than one mbuf\n"); } for (mp = m; mp; mp = mp->m_next) { if (mp->m_len == 0) continue; bcopy(mtod(mp, caddr_t), buf_p, min(mp->m_len, len)); len -= mp->m_len; buf_p += mp->m_len; } m_freem(m); #if NBPFILTER > 0 if (ifp->if_bpf) BPF_TAP(ifp->if_bpf, mace.tx_dma_area, m->m_pkthdr.len); #endif #if 0 printf("tx packet %d\n", m->m_pkthdr.len); printContiguousEtherPacket(mace.tx_dma_area, m->m_pkthdr.len); #endif /* fill in the dbdma records and kick off the dma */ tx_dbdma(mace.tx_dma_area, m->m_pkthdr.len); mace.tx_busy = 1; return; } #ifdef MACE_DEBUG static int mace_watchdog() { struct ifnet * ifp = &mace.en_arpcom.ac_if; int s; mace.txwatchdog++; s = splnet(); if (mace.rxintr == 0) { printf("rx is hung up\n"); rx_intr(); } mace.rxintr = 0; #if 0 if (mace.txintr == 0 && ifp->if_snd.ifq_head) { if (mace.tx_busy) dbdma_stop(DBDMA_ETHERNET_TX); mace.tx_busy = 0; mace_service_queue(ifp); } mace.txintr = 0; #endif timeout(mace_watchdog, 0, 10*hz); /* just in case we drop an interrupt */ return (0); } #endif /* MACE_DEBUG */ static int mace_start(struct ifnet * ifp) { // int i = mace.tx_busy; // printf("mace_start %s\n", mace.tx_busy ? "(txBusy)" : ""); mace_service_queue(ifp); // if (mace.tx_busy && !i) // printf("(txStarted)\n"); return 0; } int mace_recv_pkt(funcptr pktfunc, void * p) { vm_offset_t address; struct mace_board * board; long bytes; int done = 0; int doContinue = 0; mace_t * m; unsigned long resid; unsigned short status; int tail; m = &mace; board = m->ereg; /* remember where the tail was */ tail = m->rv_tail; for (done = 0; (done == 0) && (m->rv_head != tail);) { dbdma_command_t * dmaHead; dmaHead = &m->rv_dma[m->rv_head]; resid = dbdma_ld4_endian(&dmaHead->d_status_resid); status = (resid >> 16); bytes = resid & 0xffff; bytes = ETHERNET_BUF_SIZE - bytes - 8; /* strip off FCS/CRC */ if ((status & DBDMA_ETHERNET_EOP) == 0) { /* no packets are ready yet */ break; } doContinue = 1; /* if the packet is good, pass it up */ if (bytes >= (ETHER_MIN_PACKET - 4)) { char * dmaPacket; dmaPacket = &m->rv_dma_area[m->rv_head * ETHERNET_BUF_SIZE]; done = (*pktfunc)(dmaPacket, bytes, p); } /* mark the head as the new tail in the dma channel command list */ DBDMA_BUILD(dmaHead, DBDMA_CMD_STOP, 0, 0, 0, RECEIVE_INT, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); flush_cache_v((vm_offset_t)dmaHead, sizeof(*dmaHead)); eieio(); /* make the tail an available dma'able entry */ { dbdma_command_t * dmaTail; dmaTail = &m->rv_dma[m->rv_tail]; address = KVTOPHYS((vm_offset_t) &m->rv_dma_area[m->rv_tail*ETHERNET_BUF_SIZE]); // this command is live so write it carefully DBDMA_ST4_ENDIAN(&dmaTail->d_address, address); dmaTail->d_status_resid = 0; dmaTail->d_cmddep = 0; eieio(); DBDMA_ST4_ENDIAN(&dmaTail->d_cmd_count, ((DBDMA_CMD_IN_LAST) << 28) | ((0) << 24) | ((RECEIVE_INT) << 20) | ((DBDMA_BRANCH_NEVER) << 18) | ((DBDMA_WAIT_NEVER) << 16) | (ETHERNET_BUF_SIZE)); eieio(); flush_cache_v((vm_offset_t)dmaTail, sizeof(*dmaTail)); } /* head becomes the tail */ m->rv_tail = m->rv_head; /* advance the head */ m->rv_head++; if (m->rv_head == (ETHER_RX_NUM_DBDMA_BUFS + 1)) m->rv_head = 0; } if (doContinue) { sync(); dbdma_continue(DBDMA_ETHERNET_RV); } return (done); } /* kdb handle buffer routines */ struct kdbCopy { int * len; char * data; }; static int kdb_copy(char * pktBuf, int len, void * p) { struct kdbCopy * cp = (struct kdbCopy *)p; bcopy(pktBuf, cp->data, len); *cp->len = len; return (1); /* signal that we're done */ } /* kdb debugger routines */ static void polled_send_pkt(char * data, int len) { waitForDBDMADone("mace: polled_send_pkt start"); tx_dbdma(data, len); waitForDBDMADone("mace: polled_send_pkt end"); return; } static void polled_receive_pkt(char *data, int *len, int timeout_ms) { struct kdbCopy cp; cp.len = len; cp.data = data; timeout_ms *= 1000; *len = 0; while (mace_recv_pkt(kdb_copy, (void *)&cp) == 0) { if (timeout_ms <= 0) break; delay(50); timeout_ms -= 50; } return; } /* Bump to force ethernet data to be 4-byte aligned * (since the ethernet header is 14 bytes, and the 802.3 header is * 22 = 14+8 bytes). This assumes that m_data is word-aligned * (which it is). */ #define ETHER_DATA_ALIGN 2 /* * Function: rxpkt * * Purpose: * Called from within mace_recv_pkt to deal with a packet of data. * rxpkt() allocates an mbuf(+cluser) and passes it up to the stacks. * Returns: * 0 if the packet was copied to an mbuf, 1 otherwise */ static int rxpkt(char * data, int len, void * p) { struct ether_header * eh_p = (struct ether_header *)data; struct ifnet * ifp = &mace.en_arpcom.ac_if; struct mbuf * m; int interesting; mace.rxintr++; /* mcast, bcast -- we're interested in either */ interesting = eh_p->ether_dhost[0] & 1; #if NBPFILTER > 0 /* * Check if there's a bpf filter listening on this interface. * If so, hand off the raw packet to bpf_tap(). */ if (ifp->if_bpf) { BPF_TAP(ifp->if_bpf, data, len); /* * Keep the packet if it's a broadcast or has our * physical ethernet address (or if we support * multicast and it's one). */ if ((interesting == 0) && bcmp(eh_p->ether_dhost, mace.macaddr, sizeof(eh_p->ether_dhost)) != 0) { return (1); } } #endif /* * We "know" a full-sized packet fits in one cluster. Set up the * packet header, and if the length is sufficient, attempt to allocate * a cluster. If that fails, fall back to the old way (m_devget()). * Here, we take the simple approach of cluster vs. single mbuf. */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == 0) { #ifdef MACE_DEBUG printf("mget failed\n"); #endif return (1); } if (len > (MHLEN - ETHER_DATA_ALIGN)) { MCLGET(m, M_DONTWAIT); if (m->m_flags&M_EXT) /* MCLGET succeeded */ { m->m_data += ETHER_DATA_ALIGN; bcopy(data, mtod(m, caddr_t), (unsigned)len); } else { #ifdef MACE_DEBUG printf("no clusters\n"); #endif m_free(m); m = (struct mbuf *)m_devget(data, len, 0, ifp, 0); if (m == 0) return (1); } } else { m->m_data += ETHER_DATA_ALIGN; bcopy(data, mtod(m, caddr_t), (unsigned)len); } /* * Current code up the line assumes that the media header's been * stripped, but we'd like to preserve it, just in case someone * wants to peek. */ m->m_pkthdr.len = len; m->m_len = len; m->m_pkthdr.rcvif = ifp; m->m_data += sizeof(*eh_p); m->m_len -= sizeof (*eh_p); m->m_pkthdr.len -= sizeof(*eh_p); ether_input(ifp, eh_p, m); return (0); } static void rx_intr() { mace_recv_pkt(rxpkt, 0); } void mace_dbdma_rx_intr(int unit, void *ignored, void * arp) { if (!mace.ready) return; thread_call_func((thread_call_func_t)rx_intr, 0, TRUE); } int mace_ioctl(struct ifnet * ifp,u_long cmd, caddr_t data) { struct arpcom * ar; unsigned error = 0; struct ifaddr * ifa = (struct ifaddr *)data; struct ifreq * ifr = (struct ifreq *)data; struct sockaddr_in * sin; sin = (struct sockaddr_in *)(&((struct ifreq *)data)->ifr_addr); ar = (struct arpcom *)ifp; switch (cmd) { case SIOCAUTOADDR: error = in_bootp(ifp, sin, &mace.en_arpcom.ac_enaddr); break; case SIOCSIFADDR: #if NeXT ifp->if_flags |= (IFF_UP | IFF_RUNNING); #else ifp->if_flags |= IFF_UP; #endif switch (ifa->ifa_addr->sa_family) { case AF_INET: /* * See if another station has *our* IP address. * i.e.: There is an address conflict! If a * conflict exists, a message is sent to the * console. */ if (IA_SIN(ifa)->sin_addr.s_addr != 0) { /* don't bother for 0.0.0.0 */ ar->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas(ar, &IA_SIN(ifa)->sin_addr); } break; default: break; } break; case SIOCSIFFLAGS: /* * If interface is marked down and it is running, then stop it */ if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_flags & IFF_RUNNING) != 0) { /* * If interface is marked down and it is running, then * stop it. */ ifp->if_flags &= ~IFF_RUNNING; } else if ((ifp->if_flags & IFF_UP) != 0 && (ifp->if_flags & IFF_RUNNING) == 0) { /* * If interface is marked up and it is stopped, then * start it. */ ifp->if_flags |= IFF_RUNNING; } /* * If the state of the promiscuous bit changes, the * interface must be reset to effect the change. */ if (((ifp->if_flags ^ mace.promisc) & IFF_PROMISC) && (ifp->if_flags & IFF_RUNNING)) { mace.promisc = ifp->if_flags & IFF_PROMISC; mace_sync_promisc(ifp); } break; case SIOCADDMULTI: if ((error = ether_addmulti(ifr, ar)) == ENETRESET) { if ((error = mace_addmulti(ifr, ar)) != 0) { error = 0; mace_sync_mcast(ifp); } } break; case SIOCDELMULTI: { struct ether_addr enaddr[2]; /* [0] - addrlo, [1] - addrhi */ if ((error = ether_delmulti(ifr, ar, enaddr)) == ENETRESET) { if ((error = mace_delmulti(ifr, ar, enaddr)) != 0) { error = 0; mace_sync_mcast(ifp); } } } break; default: error = EINVAL; break; } return (error); } void mace_init() { struct ifnet * ifp = &mace.en_arpcom.ac_if; /* * Only use in-kernel driver for early debugging (bootargs: kdp=1|3) */ if ( (kdp_flag & 1) == 0 ) { return; } mace.tx_busy = 0; mace.txintr = 0; mace.promisc = 0; bzero((caddr_t)ifp, sizeof(struct ifnet)); bcopy(&mace.macaddr, &mace.en_arpcom.ac_enaddr, NUM_EN_ADDR_BYTES); ifp->if_name = "en"; ifp->if_unit = 0; ifp->if_private = 0; ifp->if_ioctl = mace_ioctl; ifp->if_start = mace_start; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; #if NBPFILTER > 0 bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); #endif if_attach(ifp); ether_ifattach(ifp); mace.rxintr = 0; /* wire in the interrupt routines */ pmac_register_int(PMAC_DMA_ETHERNET_RX, SPLNET, mace_dbdma_rx_intr, 0); pmac_register_int(PMAC_DMA_ETHERNET_TX, SPLNET, mace_dbdma_tx_intr, 0); // pmac_register_int(PMAC_DEV_ETHERNET, SPLNET, mace_pci_intr); mace.ready = 1; #ifdef MACE_DEBUG timeout(mace_watchdog, 0, 10*hz); /* just in case we drop an interrupt */ #endif return; } /* * mace_pci_intr * * Service MACE interrupt */ void mace_pci_intr(int device, void *ssp) { unsigned char ir, retry, frame, packet, length; ir = mace.ereg->ir; eieio(); /* Clear Interrupt */ packet = mace.ereg->mpc; eieio(); length = mace.ereg->rntpc; eieio(); printf("(txI)"); if (ir & IR_XMTINT) { retry = mace.ereg->xmtrc; eieio(); /* Grab transmit retry count */ frame = mace.ereg->xmtfs; eieio(); // if (mace.ready) // mace_dbdma_tx_intr(device, ssp); } return; } static void tx_intr() { mace.txintr++; mace.tx_busy = 0; mace_service_queue(&mace.en_arpcom.ac_if); } /* * mace_dbdma_tx_intr * * DBDMA interrupt routine */ void mace_dbdma_tx_intr(int unit, void *ignored, void * arg) { if (!mace.ready) return; thread_call_func((thread_call_func_t)tx_intr, 0, TRUE); return; }