/* * Fires up the network interface. NIC interrupts * should have been disabled when calling this * function. * * \param mac Six byte unique MAC address. */ static int NicStart(phantom_device_t * dev, CONST uint8_t * mac) { uint8_t i; if (NicReset(dev)) return -1; /* Enable receiver. */ nic_bs(3); nic_outlb(NIC_ERCV, 7); nic_bs(0); nic_outw(NIC_RCR, RCR_RXEN); /* Enable transmitter and padding. */ nic_outw(NIC_TCR, TCR_PAD_EN | TCR_TXENA); /* Configure the PHY. */ if (NicPhyConfig(dev)) return -1; /* Set MAC address. */ //printf("Set MAC %02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); nic_bs(1); for (i = 0; i < 6; i++) nic_outlb(NIC_IAR + i, mac[i]); //printf("OK\n"); /* Enable interrupts. */ nic_bs(2); nic_outlb(NIC_MSK, INT_ERCV | INT_RCV | INT_RX_OVRN); return 0; }
/*! * \brief Initialize the NIC. * * \return 0 on success, -1 otherwise. */ int EtherInit(void) { u_char i; /* NIC reset. */ if (NicReset()) { return -1; } /* Enable receiver. */ nic_bs(3); nic_outlb(NIC_ERCV, 7); nic_bs(0); nic_outw(NIC_RCR, RCR_RXEN); /* Enable transmitter and padding. */ nic_outw(NIC_TCR, TCR_PAD_EN | TCR_TXENA); /* Configure the PHY. */ if (NicPhyConfig()) { return -1; } /* Set MAC address. */ nic_bs(1); for (i = 0; i < 6; i++) { nic_outlb(NIC_IAR + i, confnet.cdn_mac[i]); } return 0; }
/*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */ int NicReset(void) { /* Disable all interrupts. */ nic_outlb(NIC_MSK, 0); /* MAC and PHY software reset. */ nic_bs(0); nic_outw(NIC_RCR, RCR_SOFT_RST); /* Enable Ethernet protocol handler. */ nic_bs(1); nic_outw(NIC_CR, CR_EPH_EN); Delay(1); /* Disable transmit and receive. */ nic_bs(0); nic_outw(NIC_RCR, 0); nic_outw(NIC_TCR, 0); /* Enable auto release. */ nic_bs(1); nic_outw(NIC_CTR, CTR_AUTO_RELEASE); /* Reset MMU. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_RST); if (NicMmuWait(1000)) return -1; return 0; }
/*! * \brief Configure the internal PHY. * * Reset the PHY and initiate auto-negotiation. */ int NicPhyConfig(void) { u_short phy_sor; u_short phy_sr; u_short phy_to; u_short mode; /* * Reset the PHY and wait until this self clearing bit * becomes zero. We sleep 63 ms before each poll and * give up after 3 retries. */ NicPhyWrite(NIC_PHYCR, PHYCR_RST); for (phy_to = 0;; phy_to++) { Delay(1); if ((NicPhyRead(NIC_PHYCR) & PHYCR_RST) == 0) break; if (phy_to > 3) { return -1; } } Delay(1); /* Store PHY status output. */ phy_sor = NicPhyRead(NIC_PHYSOR); /* Enable PHY interrupts. */ NicPhyWrite(NIC_PHYMSK, PHYMSK_MLOSSSYN | PHYMSK_MCWRD | PHYMSK_MSSD | PHYMSK_MESD | PHYMSK_MRPOL | PHYMSK_MJAB | PHYMSK_MSPDDT | PHYMSK_MDPLDT); /* Set RPC register. */ mode = RPCR_ANEG | RPCR_LEDA_PAT | RPCR_LEDB_PAT; nic_bs(0); nic_outw(NIC_RPCR, mode); /* * Advertise our capabilities, initiate auto negotiation * and wait until this has been completed. */ NicPhyWrite(NIC_PHYANAD, PHYANAD_TX_FDX | PHYANAD_TX_HDX | PHYANAD_10FDX | PHYANAD_10_HDX | PHYANAD_CSMA); for (phy_to = 0, phy_sr = 0;; phy_to++) { /* Give up after long time wait. */ if (phy_to >= 1024) { return -1; } /* Restart auto negotiation every 4 seconds or on failures. */ if ((phy_to & 127) == 0 /* || (phy_sr & PHYSR_REM_FLT) != 0 */ ) { NicPhyWrite(NIC_PHYCR, PHYCR_ANEG_EN | PHYCR_ANEG_RST); Delay(2); } /* Check if link status detected. */ phy_sr = NicPhyRead(NIC_PHYSR); if (phy_sr & PHYSR_ANEG_ACK) break; Delay(3); } return 0; }
/*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */ static int NicReset(phantom_device_t * dev) { #ifdef LANC111_RESET_BIT sbi(LANC111_RESET_DDR, LANC111_RESET_BIT); sbi(LANC111_RESET_PORT, LANC111_RESET_BIT); NutDelay(WAIT100); cbi(LANC111_RESET_PORT, LANC111_RESET_BIT); NutDelay(WAIT250); NutDelay(WAIT250); #endif /* Disable all interrupts. */ nic_outlb(NIC_MSK, 0); /* MAC and PHY software reset. */ nic_bs(0); nic_outw(NIC_RCR, RCR_SOFT_RST); /* Enable Ethernet protocol handler. */ nic_bs(1); nic_outw(NIC_CR, CR_EPH_EN); NutDelay(10); /* Disable transmit and receive. */ nic_bs(0); nic_outw(NIC_RCR, 0); nic_outw(NIC_TCR, 0); /* Enable auto release. */ nic_bs(1); nic_outw(NIC_CTR, CTR_AUTO_RELEASE); /* Reset MMU. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_RST); if (NicMmuWait(dev,1000)) return -1; return 0; }
/*! * \brief Receive an Ethernet frame. * * \param tms Return with timeout after the specified * number of waiting loops. On a 14 Mhz ATmega * this value represents approximately the number * of milliseconds to wait. * * \return The number of bytes received, 0 on timeout * or -1 in case of a failure. */ int EtherInput(u_short type, u_short tms) { register u_short fsw; register u_char *buf; u_short fbc; /* Check the fifo empty bit. If it is set, then there is nothing in the receiver fifo. */ nic_bs(2); while (tms--) { if ((nic_inw(NIC_FIFO) & 0x8000) == 0) { break; } MicroDelay(1000); } if (nic_inw(NIC_FIFO) & 0x8000) { return 0; } /* Inialize pointer register. */ nic_outw(NIC_PTR, PTR_READ | PTR_RCV | PTR_AUTO_INCR); MicroDelay(1); /* Read status word and byte count. */ fsw = nic_inw(NIC_DATA); fbc = nic_inw(NIC_DATA) - 3; /* Check for frame errors. */ if ((fsw & 0xAC00) == 0) { DEBUG("\nRx("); DEBUGUSHORT(fbc); DEBUG(")"); if(fbc > sizeof(rframe)) { fbc = sizeof(rframe); } /* Perform the read. */ buf = (u_char *) & rframe; fsw = fbc; while (fsw--) { *buf++ = nic_inlb(NIC_DATA); } } else { fbc = -1; } /* Release the packet. */ nic_outlb(NIC_MMUCR, MMU_TOP); return fbc; }
/*! * \brief Load a packet into the nic's transmit ring buffer. * * Interupts must have been disabled when calling this function. * * \param nb Network buffer structure containing the packet to be sent. * The structure must have been allocated by a previous * call NutNetBufAlloc(). This routine will automatically * release the buffer in case of an error. * * \return 0 on success, -1 in case of any errors. Errors * will automatically release the network buffer * structure. */ static int NicPutPacket(NETBUF * nb) { u_int16_t sz; uint8_t odd = 0; uint8_t imsk; //printf("[P]"); /* * Calculate the number of bytes to be send. Do not send packets * larger than the Ethernet maximum transfer unit. The MTU * consist of 1500 data bytes plus the 14 byte Ethernet header * plus 4 bytes CRC. We check the data bytes only. */ if ((sz = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz) > ETH_DATA_LEN) return -1; /* Disable all interrupts. */ imsk = nic_inlb(NIC_MSK); nic_outlb(NIC_MSK, 0); /* Allocate packet buffer space. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100)) return -1; /* Enable interrupts including allocation success. */ nic_outlb(NIC_MSK, imsk | INT_ALLOC); /* The MMU needs some time. Use it to calculate the byte count. */ sz += nb->nb_dl.sz; sz += 6; if (sz & 1) { sz++; odd++; } /* Wait for allocation success. */ while ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) { if (NutEventWait(&maq, 125)) { nic_outlb(NIC_MMUCR, MMU_RST); NicMmuWait(1000); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100) || (nic_inlb(NIC_IST) & INT_ALLOC) == 0) { if (NutEventWait(&maq, 125)) { return -1; } } } } /* Disable interrupts. */ imsk = nic_inlb(NIC_MSK); nic_outlb(NIC_MSK, 0); nic_outlb(NIC_PNR, nic_inhb(NIC_PNR)); nic_outw(NIC_PTR, 0x4000); /* Transfer control word. */ nic_outlb(NIC_DATA, 0); nic_outlb(NIC_DATA, 0); /* Transfer the byte count. */ nic_outw(NIC_DATA, sz); /* Transfer the Ethernet frame. */ NicWrite(nb->nb_dl.vp, nb->nb_dl.sz); NicWrite(nb->nb_nw.vp, nb->nb_nw.sz); NicWrite(nb->nb_tp.vp, nb->nb_tp.sz); NicWrite(nb->nb_ap.vp, nb->nb_ap.sz); if (odd) nic_outlb(NIC_DATA, 0); /* Transfer the control word. */ nic_outw(NIC_DATA, 0); /* Enqueue packet. */ if (NicMmuWait(100)) return -1; nic_outlb(NIC_MMUCR, MMU_ENQ); /* Enable interrupts. */ imsk |= INT_TX | INT_TX_EMPTY; nic_outlb(NIC_MSK, imsk); return 0; }
/*! * \brief Fetch the next packet out of the receive ring buffer. * * Nic interrupts must be disabled when calling this funtion. * */ static int NicGetPacket( struct phantom_device *dev, void *buf, int len ) { //NETBUF *nb = 0; //uint8_t *buf; u_int16_t f_status_word; u_int16_t fbytecount; /* Check the fifo empty bit. If it is set, then there is nothing in the receiver fifo. */ nic_bs(2); //if (nic_inw(NIC_FIFO) & 0x8000) // return 0; // TODO cond/sem timed wait while(nic_inw(NIC_FIFO) & 0x8000) hal_sleep_msec(100); /* Inialize pointer register. */ nic_outw(NIC_PTR, PTR_READ | PTR_RCV | PTR_AUTO_INCR); _NOP(); _NOP(); _NOP(); _NOP(); /* Read status word and byte count. */ f_status_word = nic_inw(NIC_DATA); fbytecount = nic_inw(NIC_DATA); //printf("[SW=%04X,BC=%04X]", f_status_word, fbytecount); int ret = ERR_IO_ERROR; /* Check for frame errors. */ if (f_status_word & 0xAC00) { //nb = (NETBUF *) 0xFFFF; ret = ERR_IO_ERROR; } /* Check the byte count. */ else if (fbytecount < 66 || fbytecount > 1524) { //nb = (NETBUF *) 0xFFFF; ret = ERR_IO_ERROR; } else { /* * Allocate a NETBUF. * Hack alert: Rev A chips never set the odd frame indicator. */ fbytecount -= 3; if( len < fbytecount ) ret = ERR_VFS_INSUFFICIENT_BUF; else { NicRead( dev, buf, fbytecount); ret = fbytecount; } } /* Release the packet. */ nic_outlb(NIC_MMUCR, MMU_TOP); return ret; }
/* * NIC interrupt entry. */ static void NicInterrupt(void *arg) { phantom_device_t * dev = arg; uint8_t isr; uint8_t imr; lanc111_nic_t *ni = (lanc111_nic_t *) ((NUTDEVICE *) arg)->dev_dcb; ni->ni_interrupts++; /* Read the interrupt mask and disable all interrupts. */ nic_bs(2); imr = nic_inlb(NIC_MSK); nic_outlb(NIC_MSK, 0); /* Read the interrupt status and acknowledge all interrupts. */ isr = nic_inlb(NIC_IST); //printf("\n!%02X-%02X ", isr, imr); isr &= imr; /* * If this is a transmit interrupt, then a packet has been sent. * So we can clear the transmitter busy flag and wake up the * transmitter thread. */ if (isr & INT_TX_EMPTY) { nic_outlb(NIC_ACK, INT_TX_EMPTY); imr &= ~INT_TX_EMPTY; } /* Transmit error. */ else if (isr & INT_TX) { /* re-enable transmit */ nic_bs(0); nic_outw(NIC_TCR, nic_inlb(NIC_TCR) | TCR_TXENA); nic_bs(2); nic_outlb(NIC_ACK, INT_TX); /* kill the packet */ nic_outlb(NIC_MMUCR, MMU_PKT); } /* * If this is a receive interrupt, then wake up the receiver * thread. */ if (isr & INT_RX_OVRN) { nic_outlb(NIC_ACK, INT_RX_OVRN); //nic_outlb(NIC_MMUCR, MMU_TOP); NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & INT_ERCV) { nic_outlb(NIC_ACK, INT_ERCV); NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & INT_RCV) { nic_outlb(NIC_ACK, INT_RCV); imr &= ~INT_RCV; NutEventPostFromIrq(&ni->ni_rx_rdy); } if (isr & INT_ALLOC) { imr &= ~INT_ALLOC; NutEventPostFromIrq(&maq); } //printf(" -%02X-%02X- ", nic_inlb(NIC_IST), inb(PINE) & 0x20); nic_outlb(NIC_MSK, imr); }
/*! * \brief Configure the internal PHY. * * Reset the PHY and initiate auto-negotiation. */ static int NicPhyConfig(phantom_device_t * dev) { u_int16_t phy_sor; u_int16_t phy_sr; u_int16_t phy_to; u_int16_t mode; /* * Reset the PHY and wait until this self clearing bit * becomes zero. We sleep 63 ms before each poll and * give up after 3 retries. */ //printf("Reset PHY.."); NicPhyWrite(dev, NIC_PHYCR, PHYCR_RST); for (phy_to = 0;; phy_to++) { NutSleep(63); if ((NicPhyRead(dev, NIC_PHYCR) & PHYCR_RST) == 0) break; if (phy_to > 3) return -1; } //printf("OK\n"); /* Store PHY status output. */ phy_sor = NicPhyRead(dev, NIC_PHYSOR); /* Enable PHY interrupts. */ NicPhyWrite(dev, NIC_PHYMSK, PHYMSK_MLOSSSYN | PHYMSK_MCWRD | PHYMSK_MSSD | PHYMSK_MESD | PHYMSK_MRPOL | PHYMSK_MJAB | PHYMSK_MSPDDT | PHYMSK_MDPLDT); /* Set RPC register. */ mode = RPCR_ANEG | RPCR_LEDA_PAT | RPCR_LEDB_PAT; nic_bs(0); nic_outw(NIC_RPCR, mode); #ifdef NIC_FIXED /* Disable link. */ phy_sr = NicPhyRead(NIC_PHYCFR1); NicPhyWrite(NIC_PHYCFR1, phy_sr | 0x8000); NutSleep(63); /* Set fixed capabilities. */ NicPhyWrite(NIC_PHYCR, NIC_FIXED); nic_bs(0); nic_outw(NIC_RPCR, mode); /* Enable link. */ phy_sr = NicPhyRead(NIC_PHYCFR1); NicPhyWrite(NIC_PHYCFR1, phy_sr & ~0x8000); phy_sr = NicPhyRead(NIC_PHYCFR1); #else /* * Advertise our capabilities, initiate auto negotiation * and wait until this has been completed. */ //printf("Negotiate.."); NicPhyWrite(dev, NIC_PHYANAD, PHYANAD_TX_FDX | PHYANAD_TX_HDX | PHYANAD_10FDX | PHYANAD_10_HDX | PHYANAD_CSMA); NutSleep(63); for (phy_to = 0, phy_sr = 0;; phy_to++) { /* Give up after 10 seconds. */ if (phy_to >= 1024) return -1; /* Restart auto negotiation every 4 seconds or on failures. */ if ((phy_to & 127) == 0 /* || (phy_sr & PHYSR_REM_FLT) != 0 */ ) { NicPhyWrite(dev, NIC_PHYCR, PHYCR_ANEG_EN | PHYCR_ANEG_RST); //printf("Restart.."); NutSleep(63); } /* Check if we are done. */ phy_sr = NicPhyRead(dev, NIC_PHYSR); //printf("[SR %04X]", phy_sr); if (phy_sr & PHYSR_ANEG_ACK) break; NutSleep(63); } //printf("OK\n"); #endif return 0; }
/*! * \brief Send an Ethernet frame. * * \param dmac Destination MAC address. * \param type Frame type. * \param len Frame size. * * \return 0 on success, -1 otherwise. */ int EtherOutput(const u_char * dmac, u_short type, u_short len) { ETHERHDR *eh; u_char *cp; /* * Set the Ethernet header. */ if (type == ETHERTYPE_ARP) { cp = (u_char *) & arpframe; } else { cp = (u_char *) & sframe; } eh = (ETHERHDR *)cp; memcpy_(eh->ether_shost, confnet.cdn_mac, 6); memcpy_(eh->ether_dhost, dmac, 6); eh->ether_type = type; /* * The total packet length includes * - status word (2 bytes) * - byte count (2 bytes) * - destination address (6 bytes) * - source address (6 bytes) * - Ethernet type (2 bytes) * - data bytes (variable) * - control word (2 bytes) * Thus we add 20 to the number of data bytes. We didn't * manage to get an odd number of bytes transmitted, so * add another byte. */ if((len += 20) & 1) { len++; } DEBUG(" Tx("); DEBUGUSHORT(len); DEBUG(")"); /* Allocate transmit packet buffer space. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100)) { return -1; } /* * An allocation error might appear when incoming packets occupy * all the buffer. Reset the MMU to release all memory. This is * very drastic, but OK for our sequential boot loader. */ if ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) { DEBUG("[MMURST]"); nic_outlb(NIC_MMUCR, MMU_RST); NicMmuWait(1000); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100) || (nic_inlb(NIC_IST) & INT_ALLOC) == 0) { return -1; } } /* * Read the number of the allcocated packet from the allocation * result register and write it to the packet number register. */ nic_outlb(NIC_PNR, nic_inhb(NIC_PNR)); /* * Initially set the pointer register address to 2 and enable * auto increment. The first two bytes will be used by the CSMA * to store the status word upon transmit completion. */ nic_outw(NIC_PTR, PTR_AUTO_INCR | 2); /* * Transfer the byte count and the data bytes. */ nic_outw(NIC_DATA, len); while (len--) { nic_outlb(NIC_DATA, *cp); cp++; } /* * Transfer the control word. As stated above, we never succeeded * in sending an odd number of bytes. */ nic_outw(NIC_DATA, 0); /* Enqueue packet. */ NicMmuWait(100); nic_outlb(NIC_MMUCR, MMU_ENQ); return 0; }