| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272 |
- /*
- * Copyright (C) 2012 by Ole Reinhardt (ole.reinhardt@embedded-it.de)
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*
- * \verbatim
- * $Id: $
- * \endverbatim
- */
- #include <stdio.h>
- #include <arch/cm3.h>
- #include <cfg/arch.h>
- #include <cfg/os.h>
- #include <cfg/dev.h>
- #include <cfg/arch/gpio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/atom.h>
- #include <sys/heap.h>
- #include <sys/thread.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/confnet.h>
- #if defined(MCU_LPC176x)
- #include <arch/cm3/nxp/lpc176x.h>
- #include <arch/cm3/nxp/lpc176x_clk.h>
- #include <arch/cm3/nxp/lpc176x_gpio.h>
- #elif defined(MCU_LPC177x_8x)
- #include <arch/cm3/nxp/lpc177x_8x.h>
- #include <arch/cm3/nxp/lpc177x_8x_clk.h>
- #include <arch/cm3/nxp/lpc177x_8x_gpio.h>
- #elif defined(MCU_LPC407x_8x)
- #include <arch/cm3/nxp/lpc407x_8x.h>
- #include <arch/cm3/nxp/lpc407x_8x_clk.h>
- #include <arch/cm3/nxp/lpc177x_8x_gpio.h>
- #else
- #warning "Unknown LPC familiy"
- #endif
- #include <netinet/if_ether.h>
- #include <net/ether.h>
- #include <net/if_var.h>
- #include <dev/gpio.h>
- #include <dev/irqreg.h>
- #include <dev/phy.h>
- #include <arch/cm3/nxp/lpc17xx_emac.h>
- /* WARNING: Variadic macros are C99 and may fail with C89 compilers. */
- #ifdef NUTDEBUG
- #include <stdio.h>
- #include <arpa/inet.h>
- #define EMPRINTF(args,...) printf(args,##__VA_ARGS__);
- #else
- #define EMPRINTF(args,...)
- #endif
- #define NUT_THREAD_NICLINKSTACK 512
- #ifndef NUT_THREAD_NICRXSTACK
- #define NUT_THREAD_NICRXSTACK 768
- #endif
- #ifndef EMAC_LINK_LOOPS
- #define EMAC_LINK_LOOPS 1000
- #endif
- /*!
- * \brief PHY address.
- *
- * Any other than 0 seems to create problems with Atmel's evaluation kits.
- */
- #ifndef NIC_PHY_ADDR
- #define NIC_PHY_ADDR 0
- #endif
- #ifndef TRUE
- #define TRUE 1
- #endif
- #ifndef FALSE
- #define FALSE 0
- #endif
- #define min(a,b) ((a>b)?b:a)
- /* MII Mgmt Configuration register - Clock divider setting */
- const uint8_t emac_clkdiv[] = { 4, 6, 8, 10, 14, 20, 28, 36, 40, 44, 48, 52, 56, 60, 64 };
- typedef struct _BufDescriptor {
- unsigned int addr;
- unsigned int stat;
- } BufDescriptor;
- /*!
- * \brief Network interface controller information structure.
- */
- struct _EMACINFO {
- #ifdef NUT_PERFMON
- #warning not yet implemented
- uint32_t ni_rx_packets; /*!< Number of packets received. */
- uint32_t ni_tx_packets; /*!< Number of packets sent. */
- uint32_t ni_overruns; /*!< Number of packet overruns. */
- uint32_t ni_rx_frame_errors; /*!< Number of frame errors. */
- uint32_t ni_rx_crc_errors; /*!< Number of CRC errors. */
- uint32_t ni_rx_missed_errors; /*!< Number of missed packets. */
- #endif
- HANDLE volatile ni_rx_rdy; /*!< Receiver event queue. */
- HANDLE volatile ni_tx_rdy; /*!< Transmitter event queue. */
- HANDLE ni_mutex; /*!< Access mutex semaphore. */
- volatile int ni_insane; /*!< Set by error detection. */
- };
- /*!
- * \brief Network interface controller information type.
- */
- typedef struct _EMACINFO EMACINFO;
- /*!
- * \addtogroup xgNutArchCm3Lpc17xxEmac
- */
- /*@{*/
- /*!
- * \brief Read contents of PHY register.
- *
- * \param reg PHY register number.
- *
- * \return Contents of the specified register.
- */
- static uint16_t phy_inw(uint8_t reg)
- {
- unsigned int tout;
- LPC_EMAC->MADR = (NIC_PHY_ADDR << 8) | reg;
- LPC_EMAC->MCMD = EMAC_MCMD_READ;
- /* Wait until operation completed */
- tout = 0;
- for (tout = 0; tout < EMAC_MII_RD_TOUT; tout++) {
- if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0) {
- break;
- }
- }
- LPC_EMAC->MCMD = 0;
- return (LPC_EMAC->MRDD);
- }
- /*!
- * \brief Write value to PHY register.
- *
- * \param reg PHY register number.
- * \param val Value to write.
- */
- static void phy_outw(uint8_t reg, uint16_t val)
- {
- unsigned int tout;
- LPC_EMAC->MADR = (NIC_PHY_ADDR << 8) | reg;
- LPC_EMAC->MWTD = val;
- /* Wait utill operation completed */
- tout = 0;
- for (tout = 0; tout < EMAC_MII_WR_TOUT; tout++) {
- if ((LPC_EMAC->MIND & EMAC_MIND_BUSY) == 0) {
- break;
- }
- }
- }
- /*!
- * \brief Set the emac duplex mode.
- *
- * \param full 1: full duplex, 0: half duplex
- *
- * \return none;
- */
- static void Lpc17xxEmacSetDuplex(int full)
- {
- if(full) {
- LPC_EMAC->MAC2 |= EMAC_MAC2_FULL_DUP;
- LPC_EMAC->Command |= EMAC_CR_FULL_DUP;
- LPC_EMAC->IPGT = EMAC_IPGT_FULL_DUP;
- } else {
- LPC_EMAC->IPGT = EMAC_IPGT_HALF_DUP;
- }
- }
- /*!
- * \brief Set the emac speed.
- *
- * \param full 1: 100MBit, 0: 10 MBit
- *
- * \return none;
- */
- static void Lpc17xxEmacSetSpeed(int enable_100mBit)
- {
- if(enable_100mBit) {
- LPC_EMAC->SUPP = EMAC_SUPP_SPEED;
- } else {
- LPC_EMAC->SUPP = 0;
- }
- }
- /*!
- * \brief Set the emac station address.
- *
- * \param mac MAC address (station address)
- *
- * \return none;
- */
- static void Lpc17xxEmacSetMACAddr(const uint8_t * mac)
- {
- /* Set the Ethernet MAC Address registers */
- LPC_EMAC->SA0 = ((uint32_t)mac[5] << 8) | (uint32_t)mac[4];
- LPC_EMAC->SA1 = ((uint32_t)mac[3] << 8) | (uint32_t)mac[2];
- LPC_EMAC->SA2 = ((uint32_t)mac[1] << 8) | (uint32_t)mac[0];
- }
- /*! \fn Lpc17xxEmacWaitLinkThread(void *arg)
- * \brief Wait for link establishment and configure the NIC
- * accordingly.
- */
- THREAD(Lpc17xxEmacWaitLinkThread, arg)
- {
- uint32_t phyval;
- int link_wait;
- for (;;) {
- /* Restart autonegotiation */
- phyval = 1;
- NutPhyCtl(PHY_CTL_AUTONEG_RE, &phyval);
- /* Wait for auto negotiation completed and link established. */
- for (link_wait = EMAC_LINK_LOOPS;; link_wait--) {
- phyval = 0;
- NutPhyCtl(PHY_GET_STATUS, &phyval);
- if((phyval & PHY_STATUS_HAS_LINK) && (phyval & PHY_STATUS_AUTONEG_OK)) {
- /* Check link state and configure EMAC accordingly */
- if (phyval & PHY_STATUS_100M) {
- Lpc17xxEmacSetSpeed(TRUE);
- EMPRINTF("EMAC: Got link: 100 MBit ");
- } else {
- Lpc17xxEmacSetSpeed(FALSE);
- EMPRINTF("Link: Got link: 10 MBit ");
- }
- if (phyval & PHY_STATUS_FULLDUPLEX) {
- Lpc17xxEmacSetDuplex(TRUE);
- EMPRINTF("Full Duplex\n");
- } else {
- Lpc17xxEmacSetDuplex(FALSE);
- EMPRINTF("Half Duplex\n");
- }
- NutThreadExit();
- break;
- }
- if (link_wait == 0) {
- EMPRINTF("NO LINK!\n");
- }
- NutSleep(10);
- }
- }
- }
- /*!
- * \brief Reset the Ethernet controller.
- *
- * \return 0 on success, -1 on error
- */
- static int Lpc17xxEmacReset(NUTDEVICE *dev)
- {
- int rc = 0;
- uint32_t phyval;
- int idx;
- int32_t tmp;
- IFNET *ifn = (IFNET *) dev->dev_icb;
- EMPRINTF("Lpc17xxEmacReset\n");
- /* Reset all EMAC internal modules */
- LPC_EMAC->MAC1 = EMAC_MAC1_RES_TX | EMAC_MAC1_RES_MCS_TX | EMAC_MAC1_RES_RX |
- EMAC_MAC1_RES_MCS_RX | EMAC_MAC1_SIM_RES | EMAC_MAC1_SOFT_RES;
- LPC_EMAC->Command = EMAC_CR_REG_RES | EMAC_CR_TX_RES | EMAC_CR_RX_RES | EMAC_CR_PASS_RUNT_FRM;
- /* A short delay after reset. */
- NutSleep(10);
- /* Initialize MAC control registers. */
- LPC_EMAC->MAC1 = EMAC_MAC1_PASS_ALL;
- LPC_EMAC->MAC2 = EMAC_MAC2_CRC_EN | EMAC_MAC2_PAD_EN;
- /* Set maximum frame size. */
- LPC_EMAC->MAXF = ifn->if_mtu + ETHER_HDR_LEN + ETHER_HDR_LEN;
- /* Find the clock that close to desired target clock */
- tmp = NutArchClockGet(NUT_HWCLK_CPU) / EMAC_MCFG_MII_MAXCLK;
- for (idx = 0; idx < sizeof (emac_clkdiv); idx++) {
- if (emac_clkdiv[idx] >= tmp) {
- break;
- }
- }
- if(idx >= sizeof (emac_clkdiv)) {
- return -1;
- }
- idx++;
- /* Write to MAC configuration register and reset */
- LPC_EMAC->MCFG = EMAC_MCFG_CLK_SEL(idx) | EMAC_MCFG_RES_MII;
- /* release reset */
- LPC_EMAC->MCFG &= ~(EMAC_MCFG_RES_MII);
- LPC_EMAC->CLRT = EMAC_CLRT_DEF;
- LPC_EMAC->IPGR = EMAC_IPGR_P2_DEF;
- #ifdef PHY_MODE_RMII
- /* Enable Reduced MII interface. */
- LPC_EMAC->Command = EMAC_CR_RMII | EMAC_CR_PASS_RUNT_FRM;
- #endif
- NutSleep(10);
- LPC_EMAC->SUPP = 0;
- /* Wait for PHY ready. */
- NutSleep(255);
- /* Register PHY
- PHY driver memory is only allocated one, but as NutRegisterPhy reads the
- PHY ID we need to have the (R)MII hardware initialised first
- */
- rc = NutRegisterPhy(1, phy_outw, phy_inw);
- #ifndef PHY_MODE_RMII
- /* Clear MII isolate. */
- phyval = 0;
- NutPhyCtl(PHY_CTL_ISOLATE, &phyval);
- #endif
- /* Restart autonegotiation */
- phyval = 1;
- NutPhyCtl(PHY_CTL_AUTONEG_RE, &phyval);
- if (NutThreadCreate("emaclink", Lpc17xxEmacWaitLinkThread, NULL,
- (NUT_THREAD_NICLINKSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD) == NULL) {
- EMPRINTF("Lpc17xxEmacReset() Could not create link establishment thread\n");
- rc = -1;
- }
- EMPRINTF("Lpc17xxEmacReset() DONE\n");
- return rc;
- }
- /*!
- * \brief Initialise the Emac RX descriptors.
- *
- * RX descriptors are located in the extra 16K internal SRAM of the CPU
- * and will be used for DMA operations.
- *
- * \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 none
- */
- static void Lpc17xxEmacRxDescriptorInit(void)
- {
- unsigned int i;
- for (i = 0; i < EMAC_NUM_RX_FRAG; i++) {
- RX_DESC_PACKET(i) = RX_BUF(i);
- RX_DESC_CTRL(i) = EMAC_RCTRL_INT | (EMAC_ETH_RX_FRAG_SIZE-1);
- RX_STAT_INFO(i) = 0;
- RX_STAT_HASHCRC(i) = 0;
- }
- /* Set EMAC Receive Descriptor Registers. */
- LPC_EMAC->RxDescriptor = RX_DESC_BASE;
- LPC_EMAC->RxStatus = RX_STAT_BASE;
- LPC_EMAC->RxDescriptorNumber = EMAC_NUM_RX_FRAG-1;
- /* Rx Descriptors Point to 0 */
- LPC_EMAC->RxConsumeIndex = 0;
- }
- /*!
- * \brief Initialise the Emac TX descriptors.
- *
- * RX descriptors are located in the extra 16K internal SRAM of the CPU
- * and will be used for DMA operations.
- *
- * \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 none
- */
- static void Lpc17xxEmacTxDescriptorInit (void)
- {
- unsigned int i;
- for (i = 0; i < EMAC_NUM_TX_FRAG; i++)
- {
- TX_DESC_PACKET(i) = TX_BUF(i);
- TX_DESC_CTRL(i) = 0;
- TX_STAT_INFO(i) = 0;
- }
- /* Set EMAC Transmit Descriptor Registers. */
- LPC_EMAC->TxDescriptor = TX_DESC_BASE;
- LPC_EMAC->TxStatus = TX_STAT_BASE;
- LPC_EMAC->TxDescriptorNumber = EMAC_NUM_TX_FRAG-1;
- /* Tx Descriptors Point to 0 */
- LPC_EMAC->TxProduceIndex = 0;
- }
- /*
- * NIC interrupt entry.
- */
- static void Lpc17xxEmacInterrupt(void *arg)
- {
- uint32_t isr;
- EMACINFO *ni = (EMACINFO *) ((NUTDEVICE *) arg)->dev_dcb;
- /* EMAC Ethernet Controller Interrupt function. */
- /* Get EMAC interrupt status */
- isr = (LPC_EMAC->IntStatus & LPC_EMAC->IntEnable);
- if (isr != 0) {
- /* Clear interrupt status */
- LPC_EMAC->IntClear = isr;
- if (isr & EMAC_INT_RX_OVERRUN) {
- /* An RX overrun error is fatal. We have to soft-reset the RX Datapath */
- LPC_EMAC->Command |= EMAC_CR_RX_RES;
- /* Anyway post the RX queue so the receiver thread can read the just received frames */
- NutEventPostFromIrq(&ni->ni_rx_rdy);
- } else
- if (isr & (EMAC_INT_RX_DONE | EMAC_INT_RX_ERR)) {
- LPC_EMAC->IntEnable &= ~(EMAC_INT_RX_ERR | EMAC_INT_RX_DONE);
- NutEventPostFromIrq(&ni->ni_rx_rdy);
- }
- if (isr & EMAC_INT_TX_UNDERRUN) {
- /* An TX underrun error is fatal. We have to soft-reset the TX Datapath */
- LPC_EMAC->Command |= EMAC_CR_TX_RES;
- /* Anyway post the TX queue so the transmitter can rwrite new frames */
- NutEventPostFromIrq(&ni->ni_tx_rdy);
- } else
- if (isr & (EMAC_INT_TX_DONE | EMAC_INT_TX_ERR)) {
- NutEventPostFromIrq(&ni->ni_tx_rdy);
- }
- }
- }
- /*!
- * \brief Fetch the next packet out of the receive buffers.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int Lpc17xxEmacGetPacket(NUTDEVICE *dev, NETBUF ** nbp)
- {
- int rc = -1;
- uint32_t idx;
- uint32_t produce_idx;
- uint32_t new_consume_idx;
- int32_t rxlen;
- register uint32_t status;
- EMACINFO *ni = (EMACINFO *) dev->dev_dcb;
- *nbp = NULL;
- produce_idx = LPC_EMAC->RxProduceIndex;
- idx = LPC_EMAC->RxConsumeIndex;
- rxlen = 0;
- if (idx != produce_idx) {
- int32_t size = 0;
- status = RX_STAT_INFO(idx);
- while (status && idx != produce_idx) {
- size += (status & EMAC_RINFO_SIZE) + 1;
- if (++idx == EMAC_NUM_RX_FRAG) {
- idx = 0;
- }
- new_consume_idx = idx;
- if (status & EMAC_RINFO_LAST_FLAG) {
- rxlen = size;
- break;
- }
- status = RX_STAT_INFO(idx);
- }
- }
- /* Check if there was any data received */
- if(rxlen > 0) {
- idx = LPC_EMAC->RxConsumeIndex;
- /* substract 4 bytes CTC. */
- rxlen -= 4;
- /* "status" represents the status flag of the last frame fragment */
- /* Check if the frame was correctly received */
- if((status & EMAC_RINFO_ERR_MASK) == 0) {
- /*
- * Receiving long packets is unexpected. Let's declare the
- * chip insane. Short packets will be handled by the caller.
- */
- if (rxlen < 0) {
- ni->ni_insane = 1;
- } else {
- *nbp = NutNetBufAlloc(0, NBAF_DATALINK + (2 & (NUTMEM_ALIGNMENT - 1)), (uint16_t)rxlen);
- if (*nbp != NULL) {
- uint8_t *bp = (uint8_t *) (* nbp)->nb_dl.vp;
- unsigned int len;
- idx = LPC_EMAC->RxConsumeIndex;
- while (rxlen) {
- if (rxlen > EMAC_ETH_RX_FRAG_SIZE) {
- len = EMAC_ETH_RX_FRAG_SIZE;
- } else {
- len = rxlen;
- }
- /* Copy received data from current frame fragment */
- memcpy(bp, (void*)RX_DESC_PACKET(idx), len);
- /* Clear status info and crc hash from current frame fragment */
- RX_STAT_INFO(idx) = 0;
- RX_STAT_HASHCRC(idx) = 0;
- if (++idx == EMAC_NUM_RX_FRAG) {
- idx = 0;
- }
- rxlen -= len;
- bp += len;
- }
- rc = 0;
- }
- }
- } else {
- /* Silently discard the frame from EMAC buffer, update the Rx consume index */
- }
- /* Release frame from EMAC buffer, update the Rx consume index */
- LPC_EMAC->RxConsumeIndex = new_consume_idx;
- }
- return rc;
- }
- /*!
- * \brief Load a packet into the nic's transmit ring buffer.
- *
- * \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 inline int Lpc17xxEmacPutPacket(NUTDEVICE *dev, NETBUF * nb)
- {
- int rc = -1;
- uint16_t sz;
- uint8_t *buf;
- uint32_t idx;
- IFNET *ifn = (IFNET *) dev->dev_icb;
- EMACINFO *ni = (EMACINFO *) dev->dev_dcb;
- /*
- * 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) > ifn->if_mtu) {
- return -1;
- }
- sz += nb->nb_dl.sz;
- /* Disable EMAC interrupts. */
- NutIrqDisable(&sig_EMAC);
- /* TODO: Check for link. */
- if (ni->ni_insane == 0) {
- int32_t txlen = sz;
- int32_t offs = 0;
- /* Allocate a descriptors for sending frame and get the coressponding
- buffer address NIC interrupts must be disabled.
- */
- idx = LPC_EMAC->TxProduceIndex;
- while (txlen > 0) {
- /* Get the current transmit fragment data pointer */
- buf = (uint8_t*)TX_DESC_PACKET(idx);
- if (txlen > EMAC_ETH_TX_FRAG_SIZE) {
- /* This is not the last fragment, it is completely filled */
- TX_DESC_CTRL(idx) = ((EMAC_ETH_TX_FRAG_SIZE - 1) & EMAC_TCTRL_SIZE) | EMAC_TCTRL_INT;
- } else {
- /* Round up the len to the next even number and mark this as the last fragment */
- TX_DESC_CTRL(idx) = ((txlen - 1) & EMAC_TCTRL_SIZE) | (EMAC_TCTRL_INT | EMAC_TCTRL_LAST);
- }
- if (txlen == sz) {
- /* This is the first fragment, copy the header bytes and then the application data */
- memcpy(buf, nb->nb_dl.vp, nb->nb_dl.sz);
- buf += nb->nb_dl.sz;
- txlen -= nb->nb_dl.sz;
- memcpy(buf, nb->nb_nw.vp, nb->nb_nw.sz);
- buf += nb->nb_nw.sz;
- txlen -= nb->nb_nw.sz;
- memcpy(buf, nb->nb_tp.vp, nb->nb_tp.sz);
- buf += nb->nb_tp.sz;
- txlen -= nb->nb_tp.sz;
- offs = min (EMAC_ETH_TX_FRAG_SIZE - nb->nb_dl.sz - nb->nb_nw.sz - nb->nb_tp.sz, nb->nb_ap.sz);
- memcpy(buf, nb->nb_ap.vp, offs);
- buf += offs;
- txlen -= offs;
- } else {
- register int len = min(EMAC_ETH_TX_FRAG_SIZE, nb->nb_ap.sz - offs);
- /* Continue copying the application data */
- memcpy(buf, (uint8_t *)nb->nb_ap.vp + offs, len);
- offs += len;
- txlen -= len;
- }
- /* Increment produce index counter, taking wrap around into account */
- if (++idx == LPC_EMAC->TxDescriptorNumber + 1) {
- idx = 0;
- }
- }
- /* Update tx produce index
- Increase the TxProduceIndex (after writting to the Transmit buffer
- to enable the Transmit buffer)
- */
- LPC_EMAC->TxProduceIndex = idx;
- rc = 0;
- #ifdef NUT_PERFMON
- ni->ni_tx_packets++;
- #endif
- }
- /* Enable EMAC interrupts. */
- NutIrqEnable(&sig_EMAC);
- return rc;
- }
- /*!
- * \brief Fire up the network interface.
- *
- * NIC interrupts must be disabled when calling this function.
- *
- * \param mac Six byte unique MAC address.
- */
- static int Lpc17xxEmacStart(NUTDEVICE *dev)
- {
- IFNET *ifn = (IFNET *) dev->dev_icb;
- /* Set local MAC address. */
- Lpc17xxEmacSetMACAddr(ifn->if_mac);
- /* Initialize the rx and tx buffer descriptors */
- Lpc17xxEmacRxDescriptorInit();
- Lpc17xxEmacTxDescriptorInit();
- /* Set Receive Filter register: enable broadcast and multicast */
- LPC_EMAC->RxFilterCtrl = EMAC_RFC_BCAST_EN | EMAC_RFC_PERFECT_EN;
- /* Reset all interrupts, Interrupts will be enabled in the RX-Thread */
- LPC_EMAC->IntClear = 0xFFFF;
- /* Enable Transmitter and receiver */
- LPC_EMAC->Command |= EMAC_CR_TX_EN | EMAC_CR_RX_EN;
- LPC_EMAC->MAC1 |= EMAC_MAC1_REC_EN;
- EMPRINTF("Lpc17xxEmacStart() DONE\n");
- return 0;
- }
- /*! \fn EmacRxThread(void *arg)
- * \brief NIC receiver thread.
- *
- */
- THREAD(Lpc17xxEmacRxThread, arg)
- {
- NUTDEVICE *dev = arg;
- IFNET *ifn = (IFNET *) dev->dev_icb;
- EMACINFO *ni = (EMACINFO *) dev->dev_dcb;
- NETBUF *nb;
- EMPRINTF("Lpc17xxEmacRxThread() INIT\n");
- /*
- * This is a temporary hack. Due to a change in initialization,
- * we may not have got a MAC address yet. Wait until a valid one
- * has been set.
- */
- while (!ETHER_IS_UNICAST(ifn->if_mac)) {
- NutSleep(10);
- }
- /*
- * Do not continue unless we managed to start the NIC. We are
- * trapped here if the Ethernet link cannot be established.
- * This happens, for example, if no Ethernet cable is plugged
- * in.
- */
- Lpc17xxEmacStart(dev);
- /* Initialize the access mutex. */
- NutEventPost(&ni->ni_mutex);
- /* Run at high priority. */
- NutThreadSetPriority(9);
- /* Enable receive and transmit interrupts. */
- LPC_EMAC->IntEnable |= EMAC_INT_RX_OVERRUN | EMAC_INT_RX_ERR | EMAC_INT_RX_DONE |
- EMAC_INT_TX_UNDERRUN | EMAC_INT_TX_ERR | EMAC_INT_TX_DONE;
- NutIrqEnable(&sig_EMAC);
- for (;;) {
- /*
- * Wait for the arrival of new packets or poll the receiver every
- * 2 seconds. The timeout can be modified if there are interrupt problems.
- */
- NutEventWait(&ni->ni_rx_rdy, 2000);
- /*
- * Fetch all packets from the NIC's internal buffer and pass
- * them to the registered handler.
- */
- while (Lpc17xxEmacGetPacket(dev, &nb) == 0) {
- /* Discard short packets. */
- if (nb->nb_dl.sz < 60) {
- NutNetBufFree(nb);
- } else {
- (*ifn->if_recv) (dev, nb);
- }
- }
- LPC_EMAC->IntEnable |= EMAC_INT_RX_OVERRUN | EMAC_INT_RX_ERR | EMAC_INT_RX_FIN | EMAC_INT_RX_DONE;
- /* We got a weird chip, try to restart it. */
- while (ni->ni_insane) {
- if (Lpc17xxEmacReset(dev)) {
- NutSleep(500);
- } else {
- Lpc17xxEmacStart(dev);
- ni->ni_insane = 0;
- NutIrqEnable(&sig_EMAC);
- }
- }
- }
- }
- /*!
- * \brief Send Ethernet packet.
- *
- * \param dev Identifies the device to use.
- * \param nb Network buffer structure containing the packet to be sent.
- * The structure must have been allocated by a previous
- * call NutNetBufAlloc().
- *
- * \return 0 on success, -1 in case of any errors.
- */
- int Lpc17xxEmacOutput(NUTDEVICE * dev, NETBUF * nb)
- {
- static uint32_t mx_wait = 5000;
- uint32_t consume_idx;
- uint32_t produce_idx;
- uint32_t num_fragments;
- int rc = -1;
- EMACINFO *ni = (EMACINFO *) dev->dev_dcb;
- size_t space;
- size_t pkg_size;
- /*
- * After initialization we are waiting for a long time to give
- * the PHY a chance to establish an Ethernet link.
- */
- while (rc) {
- /* Check if we stay in an error condition */
- if (ni->ni_insane) {
- break;
- }
- if (NutEventWait(&ni->ni_mutex, mx_wait)) {
- break;
- }
- /* Check for packet queue space. */
- consume_idx = LPC_EMAC->TxConsumeIndex;
- produce_idx = LPC_EMAC->TxProduceIndex;
- num_fragments = LPC_EMAC->TxDescriptorNumber + 1;
- /* empty */
- if (consume_idx == produce_idx) {
- space = num_fragments * EMAC_ETH_TX_FRAG_SIZE;
- } else
- /* full */
- if ((consume_idx == 0) && (produce_idx == num_fragments - 1)) {
- space = 0;
- } else
- /* Wrap-around */
- if (consume_idx == produce_idx + 1) {
- space = 0;
- } else {
- /* Calc free space */
- if (consume_idx > produce_idx) {
- space = consume_idx - produce_idx;
- } else {
- space = num_fragments - (produce_idx - consume_idx);
- }
- space *= EMAC_ETH_TX_FRAG_SIZE;
- }
- pkg_size = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz + nb->nb_dl.sz;
- if (space < pkg_size) {
- if (NutEventWait(&ni->ni_tx_rdy, 500)) {
- /* No queue space. Release the lock and give up. */
- NutEventPost(&ni->ni_mutex);
- break;
- }
- } else
- if (Lpc17xxEmacPutPacket(dev, nb) == 0) {
- /* Ethernet works. Set a long waiting time in case we
- temporarly lose the link next time. */
- rc = 0;
- }
- NutEventPost(&ni->ni_mutex);
- }
- /*
- * Probably no Ethernet link. Significantly reduce the waiting
- * time, so following transmission will soon return an error.
- */
- if (rc) {
- mx_wait = 500;
- } else {
- /* Ethernet works. Set a long waiting time in case we
- temporarily lose the link next time. */
- mx_wait = 5000;
- }
- return rc;
- }
- /*!
- * \brief Calculate CRC of a number of bytes (e.g. of an ethernet frame)
- *
- * \param data Pointer to the first byte of the data buffer
- * \param len length of the data
- *
- * \return Calculated CRC (32 bit)
- */
- static int32_t Lpc17xxEmacCalcCrc(uint8_t *data, int32_t len)
- {
- int i, j;
- char byte;
- int crc;
- int q0, q1, q2, q3;
- crc = 0xFFFFFFFF;
- for (i = 0; i < len; i++) {
- byte = *(data++);
- for (j = 0; j < 2; j++) {
- if (((crc >> 28) ^ (byte >> 3)) & 0x00000001) {
- q3 = 0x04C11DB7;
- } else {
- q3 = 0x00000000;
- }
- if (((crc >> 29) ^ (byte >> 2)) & 0x00000001) {
- q2 = 0x09823B6E;
- } else {
- q2 = 0x00000000;
- }
- if (((crc >> 30) ^ (byte >> 1)) & 0x00000001) {
- q1 = 0x130476DC;
- } else {
- q1 = 0x00000000;
- }
- if (((crc >> 31) ^ (byte >> 0)) & 0x00000001) {
- q0 = 0x2608EDB8;
- } else {
- q0 = 0x00000000;
- }
- crc = (crc << 4) ^ q3 ^ q2 ^ q1 ^ q0;
- byte >>= 4;
- }
- }
- return crc;
- }
- /*!
- * \brief Update the multicast hash.
- *
- * This function must be called after the multicast list changed.
- *
- * \param mclst Pointer to the first entry of the linked list of
- * multicast entries.
- */
- static void Lpc17xxEmacHashUpdate(MCASTENTRY *mclst)
- {
- uint32_t idx;
- uint32_t hash[2] = { 0, 0 };
- /* Determine the hash bit for each entry. */
- while (mclst) {
- /* Calc CRC of the hash entry. Bits 23-28 are used as hash index */
- idx = (Lpc17xxEmacCalcCrc(mclst->mca_ha, 6) >> 23) & 0x3F;
- /* Set the bit given by the 6 bit index. */
- hash[idx > 31] |= 1 << (idx & 31);
- mclst = mclst->mca_next;
- }
- /* Set result in the hash register. */
- LPC_EMAC->HashFilterL = hash[0];
- LPC_EMAC->HashFilterH = hash[1];
- /* Enable or disable multicast hash. */
- if (hash[0] || hash[1]) {
- LPC_EMAC->RxFilterCtrl |= EMAC_RFC_MCAST_HASH_EN;
- } else {
- LPC_EMAC->RxFilterCtrl &= ~EMAC_RFC_MCAST_HASH_EN;
- }
- }
- /*!
- * \brief Get multicast entry of a given IP address.
- *
- * \todo This function should be shared by all Ethernet drivers.
- *
- * \param ifn Pointer to the network interface structure.
- * \param ip IP address of the entry to retrieve.
- *
- * \return Pointer to the entry or NULL if none exists.
- */
- static MCASTENTRY *McastIpEntry(IFNET *ifn, uint32_t ip)
- {
- MCASTENTRY *mca = ifn->if_mcast;
- while (mca) {
- if (ip == mca->mca_ip) {
- break;
- }
- mca = mca->mca_next;
- }
- return mca;
- }
- /*!
- * \brief Add a given IP address to the multicast list.
- *
- * \todo This function should be shared by all Ethernet drivers.
- *
- * \param ifn Pointer to the network interface structure.
- * \param ip IP address of the new entry.
- *
- * \return Pointer to the requested entry, either a new one or
- * an already existing entry. In case of a failure, NULL
- * is returned.
- */
- static MCASTENTRY *McastAddEntry(IFNET *ifn, uint32_t ip)
- {
- MCASTENTRY *mca;
- mca = McastIpEntry(ifn, ip);
- if (mca == NULL) {
- mca = malloc(sizeof(MCASTENTRY));
- if (mca) {
- mca->mca_ip = ip;
- /* Set the IANA OUI. */
- mca->mca_ha[0] = 0x01;
- mca->mca_ha[1] = 0x00;
- mca->mca_ha[2] = 0x5e;
- /* Map the lower 23 bits of the IP address to the MAC address.
- Note that Nut/Net IP addresses are in network byte order. */
- mca->mca_ha[3] = (ip >> 8) & 0x7f;
- mca->mca_ha[4] = (ip >> 16) & 0xff;
- mca->mca_ha[5] = (ip >> 24) & 0xff;
- /* Add the new entry to the front of the list. */
- mca->mca_next = ifn->if_mcast;
- ifn->if_mcast = mca;
- /* Update the EMAC's multicast hash. */
- Lpc17xxEmacHashUpdate(mca);
- }
- }
- return mca;
- }
- /*!
- * \brief Remove multicast entry of a given IP address.
- *
- * \param ifn Pointer to the network interface structure.
- * \param ip IP address of the entry to remove.
- */
- static void McastDelEntry(IFNET *ifn, uint32_t ip)
- {
- MCASTENTRY *mca = ifn->if_mcast;
- MCASTENTRY **lnk = &ifn->if_mcast;
- while (mca) {
- if (mca->mca_ip == ip) {
- *lnk = mca->mca_next;
- free(mca);
- break;
- }
- lnk = &mca->mca_next;
- mca = *lnk;
- }
- /* Update the EMAC's multicast hash. */
- Lpc17xxEmacHashUpdate(mca);
- }
- /*!
- * \brief Perform Ethernet control functions.
- *
- * \param dev Identifies the device that receives the device-control
- * function.
- * \param req Requested control function. May be set to one of the
- * following constants:
- * - SIOCSIFADDR sets interface MAC address passed in buffer.
- * - SIOCGIFADDR copies current interface MAC address to buffer.
- * - SIOCADDMULTI adds multicast entry for IP in buffer.
- * - SIOCDELMULTI removes multicast entry of IP in buffer.
- * \param conf Points to a buffer that contains any data required for
- * the given control function or receives data from that
- * function.
- * \return 0 on success, -1 otherwise.
- *
- */
- static int Lpc17xxEmacIoCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- IFNET *ifn = (IFNET *) dev->dev_icb;
- uint32_t ip;
- switch (req) {
- case SIOCSIFADDR:
- /* Set interface hardware address. */
- memcpy(ifn->if_mac, conf, sizeof(ifn->if_mac));
- break;
- case SIOCGIFADDR:
- /* Get interface hardware address. */
- memcpy(conf, ifn->if_mac, sizeof(ifn->if_mac));
- break;
- case SIOCADDMULTI:
- /* Add multicast address. */
- memcpy(&ip, conf, sizeof(ip));
- if (McastAddEntry(ifn, ip) == NULL) {
- rc = -1;
- }
- break;
- case SIOCDELMULTI:
- /* Delete multicast address. */
- memcpy(&ip, conf, sizeof(ip));
- McastDelEntry(ifn, ip);
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- /*!
- * \brief Initialize Ethernet hardware.
- *
- * Applications should do not directly call this function. It is
- * automatically executed during during device registration by
- * NutRegisterDevice().
- *
- * \param dev Identifies the device to initialize.
- */
- int Lpc17xxEmacInit(NUTDEVICE * dev)
- {
- EMACINFO *ni = (EMACINFO *) dev->dev_dcb;
- EMPRINTF("Lpc17xxEmacInit()\n");
- SysCtlPeripheralClkEnable(CLKPWR_PCONP_PCENET);
- /* Configure P1 Ethernet pins for RMII interface. */
- /* on rev. 'A' and later, P1.6 should NOT be set. */
- GpioPinConfigSet(NUTGPIO_PORT1, 0, GPIO_CFG_PERIPHERAL1); /* ETH_TXD0 */
- GpioPinConfigSet(NUTGPIO_PORT1, 1, GPIO_CFG_PERIPHERAL1); /* ETH_TXD1 */
- GpioPinConfigSet(NUTGPIO_PORT1, 4, GPIO_CFG_PERIPHERAL1); /* ETH_TXEN */
- GpioPinConfigSet(NUTGPIO_PORT1, 8, GPIO_CFG_PERIPHERAL1); /* ETH_CRS */
- GpioPinConfigSet(NUTGPIO_PORT1, 9, GPIO_CFG_PERIPHERAL1); /* ETH_RXD0 */
- GpioPinConfigSet(NUTGPIO_PORT1, 10, GPIO_CFG_PERIPHERAL1); /* ETH_RXD1 */
- GpioPinConfigSet(NUTGPIO_PORT1, 14, GPIO_CFG_PERIPHERAL1); /* ETH_RXER */
- GpioPinConfigSet(NUTGPIO_PORT1, 15, GPIO_CFG_PERIPHERAL1); /* ETH_RXCLK */
- GpioPinConfigSet(NUTGPIO_PORT1, 16, GPIO_CFG_PERIPHERAL1); /* ETH_MDC */
- GpioPinConfigSet(NUTGPIO_PORT1, 17, GPIO_CFG_PERIPHERAL1); /* ETH_MDIO */
- #ifndef PHY_MODE_RMII
- /* Configure further I/O pins for MII interface */
- GpioPinConfigSet(NUTGPIO_PORT1, 2, GPIO_CFG_PERIPHERAL1); /* ETH_TXD2 */
- GpioPinConfigSet(NUTGPIO_PORT1, 3, GPIO_CFG_PERIPHERAL1); /* ETH_TXD3 */
- GpioPinConfigSet(NUTGPIO_PORT1, 5, GPIO_CFG_PERIPHERAL1); /* ETH_TXER */
- GpioPinConfigSet(NUTGPIO_PORT1, 6, GPIO_CFG_PERIPHERAL1); /* ETH_TXCLK */
- GpioPinConfigSet(NUTGPIO_PORT1, 7, GPIO_CFG_PERIPHERAL1); /* ETH_COL */
- GpioPinConfigSet(NUTGPIO_PORT1, 11, GPIO_CFG_PERIPHERAL1); /* ETH_RXD2 */
- GpioPinConfigSet(NUTGPIO_PORT1, 12, GPIO_CFG_PERIPHERAL1); /* ETH_RXD3 */
- GpioPinConfigSet(NUTGPIO_PORT1, 13, GPIO_CFG_PERIPHERAL1); /* ETH_RXDV */
- #endif
- /* Reset the controller. */
- if (Lpc17xxEmacReset(dev) == -1) {
- return -1;
- }
- /* Clear EMACINFO structure. */
- memset(ni, 0, sizeof(EMACINFO));
- /* Register interrupt handler. */
- if (NutRegisterIrqHandler(&sig_EMAC, Lpc17xxEmacInterrupt, dev)) {
- EMPRINTF("EMAC: Registering IRQ failed\n");
- return -1;
- }
- /* Start the receiver thread. */
- if (NutThreadCreate("emacrx", Lpc17xxEmacRxThread, dev,
- (NUT_THREAD_NICRXSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD) == NULL) {
- EMPRINTF("EMAC: Registering RX Thread failed\n");
- return -1;
- }
- EMPRINTF("Lpc17xxEmacInit() DONE\n");
- return 0;
- }
- static EMACINFO dcb_eth0;
- /*!
- * \brief Network interface information structure.
- *
- * Used to call.
- */
- static IFNET ifn_eth0 = {
- IFT_ETHER, /*!< \brief Interface type, if_type. */
- 0, /*!< \brief Interface flags, if_flags. */
- {0, 0, 0, 0, 0, 0}, /*!< \brief Hardware net address, if_mac. */
- 0, /*!< \brief IP address, if_local_ip. */
- 0, /*!< \brief Remote IP address for point to point, if_remote_ip. */
- 0, /*!< \brief IP network mask, if_mask. */
- ETHERMTU, /*!< \brief Maximum size of a transmission unit, if_mtu. */
- 0, /*!< \brief Packet identifier, if_pkt_id. */
- 0, /*!< \brief Linked list of arp entries, arpTable. */
- 0, /*!< \brief Linked list of multicast address entries, if_mcast. */
- NutEtherInput, /*!< \brief Routine to pass received data to, if_recv(). */
- Lpc17xxEmacOutput, /*!< \brief Driver output routine, if_send(). */
- NutEtherOutput, /*!< \brief Media output routine, if_output(). */
- NULL /*!< \brief Interface specific control function, if_ioctl(). */
- #ifdef NUT_PERFMON
- , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- #endif
- };
- /*!
- * \brief Device information structure.
- *
- * A pointer to this structure must be passed to NutRegisterDevice()
- * to bind this Ethernet device driver to the Nut/OS kernel.
- * An application may then call NutNetIfConfig() with the name \em eth0
- * of this driver to initialize the network interface.
- *
- */
- NUTDEVICE devLpc17xxEmac = {
- 0, /*!< \brief Pointer to next device. */
- {'e', 't', 'h', '0', 0, 0, 0, 0, 0}, /*!< \brief Unique device name. */
- IFTYP_NET, /*!< \brief Type of device. */
- 0, /*!< \brief Base address. */
- 0, /*!< \brief First interrupt number. */
- &ifn_eth0, /*!< \brief Interface control block. */
- &dcb_eth0, /*!< \brief Driver control block. */
- Lpc17xxEmacInit, /*!< \brief Driver initialization routine. */
- Lpc17xxEmacIoCtl, /*!< \brief Driver specific control function. */
- 0, /*!< \brief Read from device. */
- 0, /*!< \brief Write to device. */
- #ifdef __HARVARD_ARCH__
- 0, /*!< \brief Write from program space data to device. */
- #endif
- 0, /*!< \brief Open a device or file. */
- 0, /*!< \brief Close a device or file. */
- 0, /*!< \brief Request file size. */
- 0, /*!< \brief Select function, optional, not yet implemented */
- };
- /*@}*/
|