⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ne2000.c

📁 RTEMS (Real-Time Executive for Multiprocessor Systems) is a free open source real-time operating sys
💻 C
📖 第 1 页 / 共 2 页
字号:
/*  ne2k.c -- RTEMS NE2000 Ethernet driver. *  Written by Ian Lance Taylor, Zembu Labs. *  October, 1998. * *  The license and distribution terms for this file may be *  found in found in the file LICENSE in this distribution or at *  http://www.rtems.com/license/LICENSE. * *  $Id: ne2000.c,v 1.2.2.1 2003/09/04 18:44:09 joel Exp $ * *  Both the ne2000 and the wd80x3 are based on the National Semiconductor *  8390 chip, so there is a fair amount of overlap between the two *  drivers.  It would be possible in principle to combine some code into *  a separate set of subroutines called by both.  In fact, the drivers in *  both OpenBSD and Linux work this way.  I didn't bother, because for *  the relatively simple drivers used by RTEMS, the overlap is not *  especially large, and any reasonable use of subroutines would lead to *  slightly less efficient code. *  This ne2000 driver uses two transmit buffers.  While one packet is *  being transmitted over the Ethernet, RTEMS will upload another.  Since *  uploading a packet to the ne2000 is rather slow, I don't think there *  is any point to having more than two transmit buffers.  However, the *  code does make it possible, by changing NE_TX_BUFS, although that *  would of course reduce the number of receive buffers. *   *  I suspect that the wd80x3 driver would benefit slightly from copying *  the multiple transmit buffer code.  However, I have no way to test *  that. */#include <bsp.h>#include <wd80x3.h>#include <stdio.h>#include <assert.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>#include <irq.h>/* Define this to force byte-wide data transfers with the NIC. This   is needed for boards like the TS-1325 386EX PC, which support only   an 8-bit PC/104 bus.  Undefine this on a normal PC.*//* #define NE2000_BYTE_TRANSFERS *//* Define this to print debugging messages with printk.  *//* #define DEBUG_NE2000 *//* We expect to be able to read a complete packet into an mbuf.  */#if (MCLBYTES < 1520)# error "Driver must have MCLBYTES >= 1520"#endif/* The 8390 macro definitions in wd80x3.h expect RO to be defined.  */#define RO 0/* Minimum size of Ethernet packet.  */#define	ET_MINLEN 60/* The number of NE2000 devices supported by this driver.  */#define NNEDRIVER	1/* RTEMS event number used by the interrupt handler to signal the   driver task.  This must not be any of the events used by the   network task synchronization.  */#define INTERRUPT_EVENT RTEMS_EVENT_1/* RTEMS event number used to start the transmit daemon.  This must   not be the same as INTERRUPT_EVENT.  */#define START_TRANSMIT_EVENT RTEMS_EVENT_2/* Interrupts we want to handle from the device.  */#define NE_INTERRUPTS \  (MSK_PRX | MSK_PTX | MSK_RXE | MSK_TXE | MSK_OVW | MSK_CNT)/* The size of a page in device memory.  */#define NE_PAGE_SIZE (256)/* The first page address in device memory.  */#define NE_START_PAGE (0x40)/* The last page address, plus 1.  */#define NE_STOP_PAGE (0x80)/* The number of pages used for a single transmit buffer.  This is   1536 bytes, enough for a full size packet.  */#define NE_TX_PAGES (6)/* The number of transmit buffers.  We use two, so we can load one   packet while the other is being sent.  */#define NE_TX_BUFS (2)/* We use the first pages in memory as transmit buffers, and the   remaining ones as receive buffers.  */#define NE_FIRST_TX_PAGE (NE_START_PAGE)#define NE_FIRST_RX_PAGE (NE_FIRST_TX_PAGE + NE_TX_PAGES * NE_TX_BUFS)/* Data we store for each NE2000 device.  */struct ne_softc {  /* The bsdnet information structure.  */  struct arpcom arpcom;  /* The interrupt request number.  */  unsigned int irno;  /* The base IO port number.  */  unsigned int port;  /* Whether we accept broadcasts.  */  int accept_broadcasts;  /* The thread ID of the transmit task.   */  rtems_id tx_daemon_tid;  /* The thread ID of the receive task.  */  rtems_id rx_daemon_tid;  /* Whether we use byte-transfers with the device. */  rtems_boolean byte_transfers;  /* The number of memory buffers which the transmit daemon has loaded     with data to be sent, but which have not yet been completely     sent.  */  int inuse;  /* The index of the next available transmit memory buffer.  */  int nextavail;  /* The index of the next transmit buffer to send.  */  int nextsend;  /* Nonzero if the device is currently transmitting a packet.  */  int transmitting;  /* The length of the data stored in each transmit buffer.  */  int sendlen[NE_TX_BUFS];  /* Set if we have a packet overrun while receiving.  */  int overrun;  /* Set if we should resend after an overrun.  */  int resend;  /* Statistics.  */  struct {    /* Number of packets received.  */    unsigned long rx_packets;    /* Number of packets sent.  */    unsigned long tx_packets;    /* Number of interrupts.  */    unsigned long interrupts;    /* Number of receive acknowledgements.  */    unsigned long rx_acks;    /* Number of transmit acknowledgements.  */    unsigned long tx_acks;    /* Number of packet overruns.  */    unsigned long overruns;    /* Number of frame errors.  */    unsigned long rx_frame_errors;    /* Number of CRC errors.  */    unsigned long rx_crc_errors;    /* Number of missed packets.  */    unsigned long rx_missed_errors;  } stats;};/* The list of NE2000 devices on this system.  */static struct ne_softc ne_softc[NNEDRIVER];/* Find the NE2000 device which is attached at a particular interrupt   vector.  */static struct ne_softc *ne_device_for_irno (int irno){  int i;  for (i = 0; i < NNEDRIVER; ++i)    {      if (ne_softc[i].irno == irno	  && ne_softc[i].arpcom.ac_if.if_softc != NULL)	return &ne_softc[i];    }  return NULL;}/* Read data from an NE2000 device.  Read LEN bytes at ADDR, storing   them into P.  */static voidne_read_data (struct ne_softc *sc, int addr, int len, unsigned char *p){  unsigned int port = sc->port;  unsigned int dport = port + DATAPORT;  outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);  outport_byte (port + RBCR0, len);  outport_byte (port + RBCR1, len >> 8);  outport_byte (port + RSAR0, addr);  outport_byte (port + RSAR1, addr >> 8);  outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA);  if (sc->byte_transfers)    while (len > 0) {      unsigned char d;            inport_byte (dport, d);      *p++ = d;      len--;    }  else  /* word transfers */    while (len > 0) {      unsigned short d;            inport_word (dport, d);      *p++ = d;      *p++ = d >> 8;      len -= 2;    }  outport_byte (port + ISR, MSK_RDC);}/* Handle the current NE2000 status.  This is called when the device   signals an interrupt.  It is also called at other times while   NE2000 interrupts have been disabled.  */static voidne_check_status (struct ne_softc *sc){  unsigned int port = sc->port;  unsigned char status;  /* It seems that we need to use a loop here, because if the NE2000     signals an interrupt because packet transmission is complete, and     then receives a packet while interrupts are disabled, it seems to     sometimes fail to signal the interrupt for the received packet     when interrupts are reenabled.  (Based on the behaviour of the     Realtek 8019AS chip).  */  while (1) {    inport_byte (port + ISR, status);    if (status == 0)      break;#ifdef DEBUG_NE2000    printk ("NE2000 status 0x%x (8259 enabled: %s; mask: %x)\n", status,	    i8259s_cache & (1 << sc->irno) ? "no" : "yes",	    i8259s_cache);#endif    /* Check for incoming packet overwrite.  */    if (status & MSK_OVW) {      unsigned char status2;      ++sc->stats.overruns;      outport_byte (port + CMDR, MSK_PG0 | MSK_STP | MSK_RD2);      Wait_X_ms (2);      outport_byte (port + RBCR0, 0);      outport_byte (port + RBCR1, 0);      inport_byte (port + ISR, status2);      status |= status2 & (MSK_PTX | MSK_TXE);      outport_byte (port + TCR, MSK_LOOP);      outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);      sc->overrun = 1;      if ((status & (MSK_PTX | MSK_TXE)) == 0)	sc->resend = 1;      rtems_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT);    }    /* Check for transmitted packet.  The transmit daemon may now be       able to send another packet to the device.  */    if ((status & (MSK_PTX | MSK_TXE)) != 0) {      ++sc->stats.tx_acks;      outport_byte (port + ISR, status & (MSK_PTX | MSK_TXE));      --sc->inuse;      sc->transmitting = 0;      if (sc->inuse > 0 || (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) != 0)	rtems_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);    }    /* Check for received packet.  */    if ((status & (MSK_PRX | MSK_RXE)) != 0) {      ++sc->stats.rx_acks;      outport_byte (port + ISR, status & (MSK_PRX | MSK_RXE));      rtems_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT);    }    /* Check for counter change.  */    if ((status & MSK_CNT) != 0) {      unsigned char add;      inport_byte (port + CNTR0, add);      sc->stats.rx_frame_errors += add;      inport_byte (port + CNTR1, add);      sc->stats.rx_crc_errors += add;      inport_byte (port + CNTR2, add);      sc->stats.rx_missed_errors += add;      outport_byte (port + ISR, MSK_CNT);    }  }  outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);}/* Handle an NE2000 interrupt.  */static rtems_isrne_interrupt_handler (rtems_vector_number v){  struct ne_softc *sc;  sc = ne_device_for_irno (v);  if (sc == NULL)    return;  ++sc->stats.interrupts;  ne_check_status (sc);}/* Turn NE2000 interrupts on.  */static voidne_interrupt_on (const rtems_irq_connect_data *irq){  struct ne_softc *sc;#ifdef DEBUG_NE2000  printk ("ne_interrupt_on\n");#endif  sc = ne_device_for_irno (irq->name);  if (sc != NULL)    outport_byte (sc->port + IMR, NE_INTERRUPTS);}/* Turn NE2000 interrupts off.  See ne_interrupt_on.  */static voidne_interrupt_off (const rtems_irq_connect_data *irq){  struct ne_softc *sc;#ifdef DEBUG_NE2000  printk ("ne_interrupt_off\n");#endif  sc = ne_device_for_irno (irq->name);  if (sc != NULL)    outport_byte (sc->port + IMR, 0);}/* Return whether NE2000 interrupts are on.  */static intne_interrupt_is_on (const rtems_irq_connect_data *irq){  return BSP_irq_enabled_at_i8259s (irq->name);}/* Initialize the NE2000 hardware.  */static voidne_init_hardware (struct ne_softc *sc){  unsigned int port = sc->port;  int i;  rtems_irq_connect_data irq;#ifdef DEBUG_NE2000  printk ("ne_init_hardware\n");#endif  /* Initialize registers.  */  outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);  if (sc->byte_transfers) {    outport_byte (port + DCR, MSK_FT10 | MSK_BMS);  }  else {    outport_byte (port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);  }  outport_byte (port + RBCR0, 0);  outport_byte (port + RBCR1, 0);  outport_byte (port + RCR, MSK_MON);  outport_byte (port + TCR, MSK_LOOP);  outport_byte (port + IMR, 0);  outport_byte (port + ISR, 0xff);  outport_byte (port + PSTOP, NE_STOP_PAGE);  outport_byte (port + PSTART, NE_FIRST_RX_PAGE);  outport_byte (port + BNRY, NE_STOP_PAGE - 1);  /* Set the Ethernet hardware address.  */  outport_byte (port + CMDR, MSK_PG1 | MSK_RD2);  for (i = 0; i < ETHER_ADDR_LEN; ++i)    outport_byte (port + PAR + i, sc->arpcom.ac_enaddr[i]);#ifdef DEBUG_NE2000  printk ("Using ethernet address: ");  for (i = 0; i < ETHER_ADDR_LEN; ++i)    printk("%x ",sc->arpcom.ac_enaddr[i]);  printk ("\n");#endif  /* Clear the multicast address.  */  for (i = 0; i < MARsize; ++i)    outport_byte (port + MAR + i, 0);  outport_byte (port + CURR, NE_FIRST_RX_PAGE);  outport_byte (port + CMDR, MSK_PG0 | MSK_RD2);  /* Put the device on line.  */  outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);  /* Set up interrupts.  */  irq.name = sc->irno;  irq.hdl = (rtems_irq_hdl)ne_interrupt_handler;  irq.on = ne_interrupt_on;  irq.off = ne_interrupt_off;  irq.isOn = ne_interrupt_is_on;  if (! BSP_install_rtems_irq_handler (&irq))    rtems_panic ("Can't attach NE interrupt handler for irq %d.\n",		 sc->irno);  /* Prepare to receive packets.  */  outport_byte (port + TCR, 0);  outport_byte (port + RCR, (sc->accept_broadcasts ? MSK_AB : 0));}/* The NE2000 packet receive daemon.  This task is started when the   NE2000 driver is initialized.  */static voidne_rx_daemon (void *arg){  struct ne_softc *sc = (struct ne_softc *) arg;  struct ifnet *ifp = &sc->arpcom.ac_if;  unsigned int port = sc->port;  unsigned int dport = port + DATAPORT;  while (1) {    rtems_event_set events;    /* Wait for the interrupt handler to tell us that there is a       packet ready to receive.  */    rtems_bsdnet_event_receive (INTERRUPT_EVENT,				RTEMS_WAIT | RTEMS_EVENT_ANY,				RTEMS_NO_TIMEOUT,				&events);    /* Don't let the device interrupt us now.  */    outport_byte (port + IMR, 0);    while (1) {      unsigned char startpage, currpage;      unsigned short statnext, len;      int next;      struct mbuf *m;      unsigned char *p;      int startaddr;      int toend;      struct ether_header *eh;      inport_byte (port + BNRY, startpage);      outport_byte (port + CMDR, MSK_PG1 | MSK_RD2);      inport_byte (port + CURR, currpage);      outport_byte (port + CMDR, MSK_PG0 | MSK_RD2);      ++startpage;      if (startpage >= NE_STOP_PAGE)	startpage = NE_FIRST_RX_PAGE;      if (startpage == currpage)	break;#ifdef DEBUG_NE2000      printk ("ne_rx_daemon: start page %x; current page %x\n",	      startpage, currpage);#endif      /* Read the buffer header.  This is 1 byte receive status, 1         byte page of next buffer, 2 bytes length.  */      outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);      outport_byte (port + RBCR0, 4);      outport_byte (port + RBCR1, 0);      outport_byte (port + RSAR0, 0);      outport_byte (port + RSAR1, startpage);      outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA);      if (sc->byte_transfers) {	unsigned char data;	inport_byte (dport, data);  /* Throw away status  */	inport_byte (dport, data);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -