📄 smc91111.c
字号:
/* * $Id: smc91111.c,v 1.1.2.1 2005/10/05 19:29:23 joel Exp $ */#include <rtems.h>/* * This driver currently only supports architectures with the old style * exception processing. The following checks try to keep this * from being compiled on systems which can't support this driver. * * NOTE: As of 28 September 2005, this has only been tested on the SPARC, * so that is all it is enabled for. */#if defined(__sparc__) #define SMC91111_SUPPORTED#endif#if defined(SMC91111_SUPPORTED)#include <bsp.h>#include <stdlib.h>#include <stdio.h>#include <stdarg.h>#include <rtems/error.h>#include <rtems/rtems_bsdnet.h>#include <sys/param.h>#include <sys/mbuf.h>#include <sys/socket.h>#include <sys/sockio.h>#include <net/if.h>#include <netinet/in.h>#include <netinet/if_ether.h>#define SMC91111_INTERRUPT_EVENT RTEMS_EVENT_1 /* RTEMS event used by interrupt handler to signal driver tasks. This must not be any of the events used by the network task synchronization. */#define SMC91111_START_TRANSMIT_EVENT RTEMS_EVENT_2 /* RTEMS event used to start transmit/receive daemon. This must not be the same as INTERRUPT_EVENT. */#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3 /* event to send when tx buffers become available *//* Set to perms of: 0 disables all debug output 1 for process debug output 2 for added data IO output: get_reg, put_reg 4 for packet allocation/free output 8 for only startup status, so we can tell we're installed OK 16 dump phy read/write 32 precise register dump 64 dump packets*//*#define DEBUG (-1)*//*#define DEBUG (-1 & ~(16))*/#define DEBUG (0) #include "smc91111config.h"#include <libchip/smc91111.h>struct lan91cxx_priv_data smc91111;int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd);static cyg_uint16 lan91cxx_read_phy(struct lan91cxx_priv_data *cpd, cyg_uint8 phyaddr, cyg_uint8 phyreg);static void lan91cxx_write_phy(struct lan91cxx_priv_data *cpd, cyg_uint8 phyaddr, cyg_uint8 phyreg, cyg_uint16 value);static void lan91cxx_start(struct ifnet *ifp);static void smc91111_start(struct ifnet *ifp);static int smc_probe(struct lan91cxx_priv_data *cpd);static void smc91111_stop(struct lan91cxx_priv_data *cpd);static void smc91111_init(void *arg);static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd);#if 0static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd);static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd);#endif#define min(l,r) ((l) < (r) ? (l) : (r))#define max(l,r) ((l) > (r) ? (l) : (r))/* \ ------------- Interrupt ------------- \ */rtems_isr lan91cxx_interrupt_handler(rtems_vector_number v){ struct lan91cxx_priv_data *cpd = &smc91111; unsigned short irq, event; unsigned short oldbase; unsigned short oldpointer; INCR_STAT(cpd, interrupts); DEBUG_FUNCTION(); HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), oldbase); oldpointer = get_reg(cpd, LAN91CXX_POINTER); /* Get the (unmasked) requests */ irq = get_reg(cpd, LAN91CXX_INTERRUPT); event = irq & (irq >> 8) & 0xff; if (0 == event) return; /*put_reg(cpd, LAN91CXX_INTERRUPT, irq ); *//* ack interrupts */ if (event & LAN91CXX_INTERRUPT_ERCV_INT) { db_printf("Early receive interrupt"); } else if (event & LAN91CXX_INTERRUPT_EPH_INT) { db_printf("ethernet protocol handler failures"); } else if (event & LAN91CXX_INTERRUPT_RX_OVRN_INT) { db_printf("receive overrun"); } else if (event & LAN91CXX_INTERRUPT_ALLOC_INT) { db_printf("allocation interrupt"); } else { if (event & LAN91CXX_INTERRUPT_TX_SET) { DEBUG_puts("#tx error\n"); db_printf("#*tx irq\n"); lan91cxx_finish_sent(cpd); put_reg(cpd, LAN91CXX_INTERRUPT, (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT); /*rtems_event_send (cpd->txDaemonTid, SMC91111_INTERRUPT_EVENT); */ /*put_reg(cpd, LAN91CXX_INTERRUPT, (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT); */ /*rtems_event_send (cpd->txDaemonTid, SMC91111_TX_WAIT_EVENT); */ } if (event & LAN91CXX_INTERRUPT_RCV_INT) { db_printf("#*rx irq\n"); rtems_event_send(cpd->rxDaemonTid, SMC91111_INTERRUPT_EVENT); } if (event & ~(LAN91CXX_INTERRUPT_TX_SET | LAN91CXX_INTERRUPT_RCV_INT)) db_printf("Unknown interrupt\n"); } db_printf("out %s\n", __FUNCTION__); put_reg(cpd, LAN91CXX_POINTER, oldpointer); HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), oldbase);}/* \ ------------- Rx receive ------------- \ */ /**//* This function is called as a result of the "readpacket()" call.*//* Its job is to actually fetch data for a packet from the hardware once*//* memory buffers have been allocated for the packet. Note that the buffers*//* may come in pieces, using a mbuf list. */static void lan91cxx_recv(struct lan91cxx_priv_data *cpd, struct mbuf *m){ struct ifnet *ifp = &cpd->arpcom.ac_if; struct ether_header *eh; short mlen = 0, plen; char *start; rxd_t *data = NULL, val, lp; struct mbuf *n; lp = 0; dbg_prefix = "<"; DEBUG_FUNCTION(); INCR_STAT(cpd, rx_deliver); /* ############ read packet ############ */ put_reg(cpd, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | LAN91CXX_POINTER_AUTO_INCR)); val = get_data(cpd); /* packet length (minus header/footer) */#ifdef LAN91CXX_32BIT_RX val = CYG_LE32_TO_CPU(val); plen = (val >> 16) - 6;#else val = CYG_LE16_TO_CPU(val); plen = get_data(cpd); plen = CYG_LE16_TO_CPU(plen) - 6;#endif if (LAN91CXX_RX_STATUS_IS_ODD(cpd, val)) plen++; for (n = m; n; n = n->m_next) {#ifdef LAN91CXX_32BIT_RX if (mlen == 2) {#if DEBUG & 64 db_printf("Appending to last apacket\n");#endif val = get_data(cpd); *((unsigned short *)data) = (val >> 16) & 0xffff; plen -= 2; data = (rxd_t *) n->m_data; start = (char *)data; mlen = n->m_len; if ((data) && (mlen > 1)) { *((unsigned short *)data)++ = (val & 0xffff); plen -= 2; mlen -= 2; } } else { data = (rxd_t *) n->m_data; start = (char *)data; mlen = n->m_len; }#else data = (rxd_t *) n->m_data; start = (char *)data; mlen = n->m_len;#endif db1_printf("<[packet : mlen 0x%x, plen 0x%x]\n", mlen, plen); if (data) { while (mlen >= sizeof(*data)) {#ifdef LAN91CXX_32BIT_RX val = get_data(cpd); *((unsigned short *)data)++ = (val >> 16) & 0xffff; *((unsigned short *)data)++ = (val & 0xffff);#else *data++ = get_data(cpd);#endif mlen -= sizeof(*data); plen -= sizeof(*data); } } else { /* must actively discard ie. read it from the chip anyway. */ while (mlen >= sizeof(*data)) { (void)get_data(cpd); mlen -= sizeof(*data); plen -= sizeof(*data); } }#if DEBUG & 64 lp = 0; while (((int)start) < ((int)data)) { unsigned char a = *(start++); unsigned char b = *(start++); db64_printf("%02x %02x ", a, b); lp += 2; if (lp >= 16) { db64_printf("\n"); lp = 0; } } db64_printf(" \n");#endif } val = get_data(cpd); /* Read control word (and potential data) unconditionally */#ifdef LAN91CXX_32BIT_RX if (plen & 2) { if (data) { *((unsigned short *)data)++ = (val >> 16) & 0xffff; val <<= 16; } } if (plen & 1) *(unsigned char *)data = val >> 24;#else val = CYG_LE16_TO_CPU(val); cp = (unsigned char *)data; CYG_ASSERT(val & LAN91CXX_CONTROLBYTE_RX, "Controlbyte is not for Rx"); CYG_ASSERT((1 == mlen) == (0 != LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)), "Controlbyte does not match"); if (data && (1 == mlen) && LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)) { cval = val & 0x00ff; /* last byte contains data */ *cp = cval; }#endif val = get_reg(cpd, LAN91CXX_FIFO_PORTS); if (0x8000 & val) { /* Then the Rx FIFO is empty */ db4_printf ("<+Rx packet NOT freed, stat is %x (expected %x)\n", val, cpd->rxpacket); } else { db4_printf("<+Rx packet freed %x (expected %x)\n", 0xff & (val >> 8), cpd->rxpacket); } CYG_ASSERT((0xff & (val >> 8)) == cpd->rxpacket, "Unexpected rx packet"); /* ############ free packet ############ */ /* Free packet */ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame); dbg_prefix = ""; /* Remove the mac header. This is different from the NetBSD stack. */ eh = mtod(m, struct ether_header *); m->m_data += sizeof(struct ether_header); m->m_len -= sizeof(struct ether_header); m->m_pkthdr.len -= sizeof(struct ether_header); ether_input(ifp, eh, m);}/* allocate mbuf chain */static struct mbuf *smc91111_allocmbufchain(int totlen, struct ifnet *ifp){ struct mbuf *m, *m0, *newm; int len; MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 == 0) return (0); m0->m_pkthdr.rcvif = ifp; m0->m_pkthdr.len = totlen; len = MHLEN; m = m0; /* This loop goes through and allocates mbufs for all the data we will be copying in. */ while (totlen > 0) { if (totlen >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) goto bad; len = MCLBYTES; } if (m == m0) { caddr_t newdata = (caddr_t) ALIGN(m->m_data + sizeof(struct ether_header)) - sizeof(struct ether_header); len -= newdata - m->m_data; m->m_data = newdata; } m->m_len = len = min(totlen, len); totlen -= len; if (totlen > 0) { MGET(newm, M_DONTWAIT, MT_DATA); if (newm == 0) goto bad; len = MLEN; m = m->m_next = newm; } } return (m0); bad: m_freem(m0); return (0);}static int readpacket(struct lan91cxx_priv_data *cpd){ struct mbuf *m; unsigned short stat, complen; struct ifnet *ifp = &cpd->arpcom.ac_if;#ifdef LAN91CXX_32BIT_RX cyg_uint32 val;#endif DEBUG_FUNCTION(); /* ############ read packet nr ############ */ stat = get_reg(cpd, LAN91CXX_FIFO_PORTS); db1_printf("+LAN91CXX_FIFO_PORTS: 0x%04x\n", stat); if (0x8000 & stat) { /* Then the Rx FIFO is empty */ db4_printf("!RxEvent with empty fifo\n"); return 0; } INCR_STAT(cpd, rx_count); db4_printf("+Rx packet allocated %x (previous %x)\n", 0xff & (stat >> 8), cpd->rxpacket); /* There is an Rx Packet ready */ cpd->rxpacket = 0xff & (stat >> 8); /* ############ read packet header ############ */ /* Read status and (word) length */ put_reg(cpd, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | LAN91CXX_POINTER_AUTO_INCR | 0x0000));#ifdef LAN91CXX_32BIT_RX val = get_data(cpd); val = CYG_LE32_TO_CPU(val); stat = val & 0xffff; complen = ((val >> 16) & 0xffff) - 6; /* minus header/footer words */#else stat = get_data(cpd); stat = CYG_LE16_TO_CPU(stat); complen = get_data(cpd); complen = CYG_LE16_TO_CPU(len) - 6; /* minus header/footer words */#endif#ifdef KEEP_STATISTICS if (stat & LAN91CXX_RX_STATUS_ALIGNERR) INCR_STAT(cpd, rx_align_errors); /*if ( stat & LAN91CXX_RX_STATUS_BCAST ) INCR_STAT( ); */ if (stat & LAN91CXX_RX_STATUS_BADCRC) INCR_STAT(cpd, rx_crc_errors); if (stat & LAN91CXX_RX_STATUS_TOOLONG) INCR_STAT(cpd, rx_too_long_frames); if (stat & LAN91CXX_RX_STATUS_TOOSHORT) INCR_STAT(cpd, rx_short_frames); /*if ( stat & LAN91CXX_RX_STATUS_MCAST ) INCR_STAT( ); */#endif /* KEEP_STATISTICS */ if ((stat & LAN91CXX_RX_STATUS_BAD) == 0) { INCR_STAT(cpd, rx_good); /* Then it's OK */ if (LAN91CXX_RX_STATUS_IS_ODD(cpd, stat)) complen++;#if DEBUG & 1 db_printf("good rx - stat: 0x%04x, len: 0x%04x\n", stat, complen);#endif /* Check for bogusly short packets; can happen in promisc mode: */ /* Asserted against and checked by upper layer driver. */ if (complen > sizeof(struct ether_header)) { /* then it is acceptable; offer the data to the network stack */ complen = ((complen + 3) & ~3); m = smc91111_allocmbufchain(complen, ifp); { struct mbuf *n = m; db_printf("mbuf-chain:"); while (n) { db_printf("[%x:%x]", (unsigned int)(n-> m_data), (unsigned int)(n->m_len)); n = n->m_next; } db_printf("\n"); } if (m) { /* fetch packet data into mbuf chain */ lan91cxx_recv(cpd, m); return 1; } } /*(sc->funs->eth_drv->recv)(sc, len); */ } /* Not OK for one reason or another... */ db1_printf("!bad rx: stat: 0x%04x, len: 0x%04x\n", stat, complen); /* ############ free packet ############ */ /* Free packet */ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame); return 1;}static void smc91111_rxDaemon(void *arg){ struct lan91cxx_priv_data *cpd = arg; rtems_event_set events; DEBUG_FUNCTION(); for (;;) { rtems_bsdnet_event_receive(INTERRUPT_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events); /* read until read fifo is empty */ while (!(get_reg(cpd, LAN91CXX_FIFO_PORTS) & 0x8000)) { readpacket(cpd); } }}/* \ ------------- Tx send ------------- \ */static void sendpacket(struct ifnet *ifp, struct mbuf *m){ struct lan91cxx_priv_data *cpd = ifp->if_softc; int i, len, plen, tcr; struct mbuf *n = m; unsigned short *sdata = NULL; unsigned short ints, control; cyg_uint16 packet, status; dbg_prefix = ">"; DEBUG_FUNCTION(); cpd->txbusy = 1; /* Worry about the TX engine stopping. */ tcr = get_reg(cpd, LAN91CXX_TCR); if (0 == (LAN91CXX_TCR_TXENA & tcr)) { db1_printf("> ENGINE RESTART: tcr %x\n", tcr); tcr |= LAN91CXX_TCR_TXENA; put_reg(cpd, LAN91CXX_TCR, tcr); } /* ############ packet allocation ############ */ /* Find packet length */ plen = 0; while (n) { plen += n->m_len; n = n->m_next; } /* Alloc new TX packet */ do { put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_alloc_for_tx | ((plen >> 8) & 0x07)); i = 1024 * 1024; do { status = get_reg(cpd, LAN91CXX_INTERRUPT); } while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT) && (--i > 0)); if (i) packet = get_reg(cpd, LAN91CXX_PNR); else packet = 0xffff; db1_printf(">+allocated packet %04x\n", packet); packet = packet >> 8; if (packet & 0x80) { /* Hm.. Isn't this a dead end? */ db1_printf("Allocation failed! Retrying...\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -