|  | /**************************************************************************** | 
|  | * drivers/net/ftmac100.c | 
|  | * | 
|  | * Licensed to the Apache Software Foundation (ASF) under one or more | 
|  | * contributor license agreements.  See the NOTICE file distributed with | 
|  | * this work for additional information regarding copyright ownership.  The | 
|  | * ASF licenses this file to you under the Apache License, Version 2.0 (the | 
|  | * "License"); you may not use this file except in compliance with the | 
|  | * License.  You may obtain a copy of the License at | 
|  | * | 
|  | *   http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the | 
|  | * License for the specific language governing permissions and limitations | 
|  | * under the License. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Included Files | 
|  | ****************************************************************************/ | 
|  |  | 
|  | #include <nuttx/config.h> | 
|  | #if defined(CONFIG_NET) && defined(CONFIG_NET_FTMAC100) | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdbool.h> | 
|  | #include <time.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  | #include <assert.h> | 
|  | #include <debug.h> | 
|  |  | 
|  | #include <arpa/inet.h> | 
|  |  | 
|  | #include <nuttx/crc32.h> | 
|  | #include <nuttx/arch.h> | 
|  | #include <nuttx/irq.h> | 
|  | #include <nuttx/kmalloc.h> | 
|  | #include <nuttx/wdog.h> | 
|  | #include <nuttx/wqueue.h> | 
|  | #include <nuttx/net/ip.h> | 
|  | #include <nuttx/net/netdev.h> | 
|  | #include <nuttx/net/ftmac100.h> | 
|  |  | 
|  | #ifdef CONFIG_NET_PKT | 
|  | #  include <nuttx/net/pkt.h> | 
|  | #endif | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Pre-processor Definitions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /* If processing is not done at the interrupt level, then work queue support | 
|  | * is required. | 
|  | */ | 
|  |  | 
|  | #if !defined(CONFIG_SCHED_WORKQUEUE) | 
|  | #  error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE) | 
|  | #endif | 
|  |  | 
|  | /* The low priority work queue is preferred.  If it is not enabled, LPWORK | 
|  | * will be the same as HPWORK. | 
|  | * | 
|  | * NOTE:  However, the network should NEVER run on the high priority work | 
|  | * queue!  That queue is intended only to service short back end interrupt | 
|  | * processing that never suspends.  Suspending the high priority work queue | 
|  | * may bring the system to its knees! | 
|  | */ | 
|  |  | 
|  | #define FTMAWORK LPWORK | 
|  |  | 
|  | /* CONFIG_FTMAC100_NINTERFACES determines the number of physical interfaces | 
|  | * that will be supported. | 
|  | */ | 
|  |  | 
|  | #ifndef CONFIG_FTMAC100_NINTERFACES | 
|  | #  define CONFIG_FTMAC100_NINTERFACES 1 | 
|  | #endif | 
|  |  | 
|  | /* TX timeout = 1 minute */ | 
|  |  | 
|  | #define FTMAC100_TXTIMEOUT (60*CLK_TCK) | 
|  |  | 
|  | /* Packet buffer size */ | 
|  |  | 
|  | #define PKTBUF_SIZE (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE) | 
|  |  | 
|  | /* This is a helper pointer for accessing the contents of the Ethernet | 
|  | * header. | 
|  | */ | 
|  |  | 
|  | #define BUF ((FAR struct eth_hdr_s *)priv->ft_dev.d_buf) | 
|  |  | 
|  | /* RX/TX buffer alignment */ | 
|  |  | 
|  | #define MAX_PKT_SIZE  1536 | 
|  | #define RX_BUF_SIZE   2044 | 
|  |  | 
|  | #define ETH_ZLEN 60 | 
|  |  | 
|  | #if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) | 
|  | #  define MACCR_ENABLE_ALL (FTMAC100_MACCR_XMT_EN  | \ | 
|  | FTMAC100_MACCR_RCV_EN  | \ | 
|  | FTMAC100_MACCR_XDMA_EN | \ | 
|  | FTMAC100_MACCR_RDMA_EN | \ | 
|  | FTMAC100_MACCR_CRC_APD | \ | 
|  | FTMAC100_MACCR_FULLDUP | \ | 
|  | FTMAC100_MACCR_RX_RUNT | \ | 
|  | FTMAC100_MACCR_HT_MULTI_EN | \ | 
|  | FTMAC100_MACCR_RX_BROADPKT) | 
|  | #else | 
|  | #  define MACCR_ENABLE_ALL (FTMAC100_MACCR_XMT_EN  | \ | 
|  | FTMAC100_MACCR_RCV_EN  | \ | 
|  | FTMAC100_MACCR_XDMA_EN | \ | 
|  | FTMAC100_MACCR_RDMA_EN | \ | 
|  | FTMAC100_MACCR_CRC_APD | \ | 
|  | FTMAC100_MACCR_FULLDUP | \ | 
|  | FTMAC100_MACCR_RX_RUNT | \ | 
|  | FTMAC100_MACCR_RX_BROADPKT) | 
|  | #endif | 
|  |  | 
|  | #define MACCR_DISABLE_ALL 0 | 
|  |  | 
|  | #define INT_MASK_ALL_ENABLED (FTMAC100_INT_RPKT_FINISH | \ | 
|  | FTMAC100_INT_NORXBUF | \ | 
|  | FTMAC100_INT_XPKT_OK | \ | 
|  | FTMAC100_INT_XPKT_LOST | \ | 
|  | FTMAC100_INT_RPKT_LOST | \ | 
|  | FTMAC100_INT_AHB_ERR | \ | 
|  | FTMAC100_INT_PHYSTS_CHG) | 
|  |  | 
|  | #define INT_MASK_ALL_DISABLED 0 | 
|  |  | 
|  | #define putreg32(v, x) (*(volatile uint32_t*)(x) = (v)) | 
|  | #define getreg32(x) (*(uint32_t *)(x)) | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Types | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /* The ftmac100_driver_s encapsulates all state information for a single | 
|  | * hardware interface | 
|  | */ | 
|  |  | 
|  | struct ftmac100_driver_s | 
|  | { | 
|  | struct ftmac100_txdes_s txdes[CONFIG_FTMAC100_TX_DESC]; | 
|  | struct ftmac100_rxdes_s rxdes[CONFIG_FTMAC100_RX_DESC]; | 
|  | int rx_pointer; | 
|  | int tx_pointer; | 
|  | int tx_clean_pointer; | 
|  | int tx_pending; | 
|  | uint32_t iobase; | 
|  |  | 
|  | /* NuttX net data */ | 
|  |  | 
|  | bool ft_bifup;               /* true:ifup false:ifdown */ | 
|  | struct wdog_s ft_txtimeout;  /* TX timeout timer */ | 
|  | unsigned int status;         /* Last ISR status */ | 
|  | struct work_s ft_irqwork;    /* For deferring work to the work queue */ | 
|  | struct work_s ft_pollwork;   /* For deferring work to the work queue */ | 
|  |  | 
|  | /* This holds the information visible to the NuttX network */ | 
|  |  | 
|  | struct net_driver_s ft_dev;  /* Interface understood by the network */ | 
|  | }; | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Data | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /* A single packet buffer is used */ | 
|  |  | 
|  | static uint16_t g_pktbuf[CONFIG_FTMAC100_NINTERFACES] | 
|  | [(PKTBUF_SIZE + 1) / 2]; | 
|  |  | 
|  | /* Driver state structure. */ | 
|  |  | 
|  | static struct ftmac100_driver_s g_ftmac100[CONFIG_FTMAC100_NINTERFACES] | 
|  | aligned_data(16); | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Function Prototypes | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /* Common TX logic */ | 
|  |  | 
|  | static int  ftmac100_transmit(FAR struct ftmac100_driver_s *priv); | 
|  | static int  ftmac100_txpoll(struct net_driver_s *dev); | 
|  |  | 
|  | /* Interrupt handling */ | 
|  |  | 
|  | static void ftmac100_reset(FAR struct ftmac100_driver_s *priv); | 
|  | static void ftmac100_receive(FAR struct ftmac100_driver_s *priv); | 
|  | static void ftmac100_txdone(FAR struct ftmac100_driver_s *priv); | 
|  |  | 
|  | static void ftmac100_interrupt_work(FAR void *arg); | 
|  | static int  ftmac100_interrupt(int irq, FAR void *context, FAR void *arg); | 
|  |  | 
|  | /* Watchdog timer expirations */ | 
|  |  | 
|  | static void ftmac100_txtimeout_work(FAR void *arg); | 
|  | static void ftmac100_txtimeout_expiry(wdparm_t arg); | 
|  |  | 
|  | /* NuttX callback functions */ | 
|  |  | 
|  | static int ftmac100_ifup(FAR struct net_driver_s *dev); | 
|  | static int ftmac100_ifdown(FAR struct net_driver_s *dev); | 
|  |  | 
|  | static void ftmac100_txavail_work(FAR void *arg); | 
|  | static int ftmac100_txavail(FAR struct net_driver_s *dev); | 
|  |  | 
|  | #if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) | 
|  | static int ftmac100_addmac(FAR struct net_driver_s *dev, | 
|  | FAR const uint8_t *mac); | 
|  | #ifdef CONFIG_NET_MCASTGROUP | 
|  | static int ftmac100_rmmac(FAR struct net_driver_s *dev, | 
|  | FAR const uint8_t *mac); | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Private Functions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_transmit | 
|  | * | 
|  | * Description: | 
|  | *   Start hardware transmission.  Called either from the txdone interrupt | 
|  | *   handling or from watchdog based polling. | 
|  | * | 
|  | * Input Parameters: | 
|  | *   priv - Reference to the driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   OK on success; a negated errno on failure | 
|  | * | 
|  | * Assumptions: | 
|  | *   May or may not be called from an interrupt handler.  In either case, | 
|  | *   global interrupts are disabled, either explicitly or indirectly through | 
|  | *   interrupt handling logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static FAR struct ftmac100_rxdes_s * | 
|  | ftmac100_current_rxdes(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | return &priv->rxdes[priv->rx_pointer]; | 
|  | } | 
|  |  | 
|  | static FAR struct ftmac100_txdes_s * | 
|  | ftmac100_current_txdes(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | return &priv->txdes[priv->tx_pointer]; | 
|  | } | 
|  |  | 
|  | static FAR struct ftmac100_txdes_s * | 
|  | ftmac100_current_clean_txdes(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | return &priv->txdes[priv->tx_clean_pointer]; | 
|  | } | 
|  |  | 
|  | static int ftmac100_transmit(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | FAR struct ftmac100_txdes_s *txdes; | 
|  | int len = priv->ft_dev.d_len; | 
|  |  | 
|  | txdes = ftmac100_current_txdes(priv); | 
|  |  | 
|  | /* Verify that the hardware is ready to send another packet.  If we get | 
|  | * here, then we are committed to sending a packet; Higher level logic | 
|  | * must have assured that there is no transmission in progress. | 
|  | */ | 
|  |  | 
|  | len = len < ETH_ZLEN ? ETH_ZLEN : len; | 
|  |  | 
|  | /* Send the packet: address=priv->ft_dev.d_buf, length=priv->ft_dev.d_len */ | 
|  |  | 
|  | txdes->txdes2  = (unsigned int)priv->ft_dev.d_buf; | 
|  | txdes->txdes1 &= FTMAC100_TXDES1_EDOTR; | 
|  | txdes->txdes1 |= (FTMAC100_TXDES1_FTS | | 
|  | FTMAC100_TXDES1_LTS | | 
|  | FTMAC100_TXDES1_TXIC | | 
|  | FTMAC100_TXDES1_TXBUF_SIZE(len)); | 
|  | txdes->txdes0 |= FTMAC100_TXDES0_TXDMA_OWN; | 
|  |  | 
|  | ninfo("ftmac100_transmit[%x]: copy %08x to %08x %04x\n", | 
|  | priv->tx_pointer, priv->ft_dev.d_buf, txdes->txdes2, len); | 
|  |  | 
|  | priv->tx_pointer = (priv->tx_pointer + 1) & (CONFIG_FTMAC100_TX_DESC - 1); | 
|  | priv->tx_pending++; | 
|  |  | 
|  | /* Enable Tx polling */ | 
|  |  | 
|  | /* FIXME: enable interrupts */ | 
|  |  | 
|  | putreg32(1, &iobase->txpd); | 
|  |  | 
|  | /* Setup the TX timeout watchdog (perhaps restarting the timer) */ | 
|  |  | 
|  | wd_start(&priv->ft_txtimeout, FTMAC100_TXTIMEOUT, | 
|  | ftmac100_txtimeout_expiry, (wdparm_t)priv); | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_txpoll | 
|  | * | 
|  | * Description: | 
|  | *   The transmitter is available, check if the network has any outgoing | 
|  | *   packets ready to send.  This is a callback from devif_poll(). | 
|  | *   devif_poll() may be called: | 
|  | * | 
|  | *   1. When the preceding TX packet send is complete, | 
|  | *   2. When the preceding TX packet send timesout and the interface is | 
|  | *      reset | 
|  | *   3. During normal TX polling | 
|  | * | 
|  | * Input Parameters: | 
|  | *   dev - Reference to the NuttX driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   OK on success; a negated errno on failure | 
|  | * | 
|  | * Assumptions: | 
|  | *   May or may not be called from an interrupt handler.  In either case, | 
|  | *   global interrupts are disabled, either explicitly or indirectly through | 
|  | *   interrupt handling logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static int ftmac100_txpoll(struct net_driver_s *dev) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)dev->d_private; | 
|  |  | 
|  | /* Send the packet */ | 
|  |  | 
|  | ftmac100_transmit(priv); | 
|  |  | 
|  | /* If zero is returned, the polling will continue until all connections | 
|  | * have been examined. | 
|  | */ | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_reset | 
|  | * | 
|  | * Description: | 
|  | *   Do the HW reset | 
|  | * | 
|  | * Input Parameters: | 
|  | *   priv - Reference to the NuttX driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Global interrupts are disabled by interrupt handling logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_reset(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  |  | 
|  | ninfo("%s(): iobase=%p\n", __func__, iobase); | 
|  |  | 
|  | putreg32 (FTMAC100_MACCR_SW_RST, &iobase->maccr); | 
|  |  | 
|  | while (getreg32 (&iobase->maccr) & FTMAC100_MACCR_SW_RST) | 
|  | ; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_init | 
|  | * | 
|  | * Description: | 
|  | *   Perform HW initialization | 
|  | * | 
|  | * Input Parameters: | 
|  | *   priv - Reference to the NuttX driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Global interrupts are disabled by interrupt handling logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_init(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | FAR struct ftmac100_txdes_s *txdes = priv->txdes; | 
|  | FAR struct ftmac100_rxdes_s *rxdes = priv->rxdes; | 
|  | FAR unsigned char *kmem; | 
|  | int i; | 
|  |  | 
|  | nerr ("%s()\n", __func__); | 
|  |  | 
|  | /* Disable all interrupts */ | 
|  |  | 
|  | putreg32(0, &iobase->imr); | 
|  |  | 
|  | /* Initialize descriptors */ | 
|  |  | 
|  | priv->rx_pointer = 0; | 
|  | priv->tx_pointer = 0; | 
|  | priv->tx_clean_pointer = 0; | 
|  | priv->tx_pending = 0; | 
|  |  | 
|  | rxdes[CONFIG_FTMAC100_RX_DESC - 1].rxdes1 = FTMAC100_RXDES1_EDORR; | 
|  |  | 
|  | kmem = kmm_memalign(RX_BUF_SIZE, CONFIG_FTMAC100_RX_DESC * RX_BUF_SIZE); | 
|  |  | 
|  | ninfo("KMEM=%08x\n", kmem); | 
|  |  | 
|  | for (i = 0; i < CONFIG_FTMAC100_RX_DESC; i++) | 
|  | { | 
|  | /* RXBUF_BADR */ | 
|  |  | 
|  | rxdes[i].rxdes0 = FTMAC100_RXDES0_RXDMA_OWN; | 
|  | rxdes[i].rxdes1 |= FTMAC100_RXDES1_RXBUF_SIZE(RX_BUF_SIZE); | 
|  | rxdes[i].rxdes2 = (unsigned int)(kmem + i * RX_BUF_SIZE); | 
|  | rxdes[i].rxdes3 = (unsigned int)(rxdes + i + 1); /* Next ring */ | 
|  | } | 
|  |  | 
|  | rxdes[CONFIG_FTMAC100_RX_DESC - 1].rxdes3 = (unsigned int)rxdes; /* Next ring */ | 
|  |  | 
|  | for (i = 0; i < CONFIG_FTMAC100_TX_DESC; i++) | 
|  | { | 
|  | /* TXBUF_BADR */ | 
|  |  | 
|  | txdes[i].txdes0 = 0; | 
|  | txdes[i].txdes1 = 0; | 
|  | txdes[i].txdes2 = 0; | 
|  | txdes[i].txdes3 = 0; | 
|  | } | 
|  |  | 
|  | txdes[CONFIG_FTMAC100_TX_DESC - 1].txdes1 = FTMAC100_TXDES1_EDOTR; | 
|  |  | 
|  | /* transmit ring */ | 
|  |  | 
|  | ninfo("priv=%08x txdes=%08x rxdes=%08x\n", priv, txdes, rxdes); | 
|  |  | 
|  | putreg32 ((unsigned int)txdes, &iobase->txr_badr); | 
|  |  | 
|  | /* receive ring */ | 
|  |  | 
|  | putreg32 ((unsigned int)rxdes, &iobase->rxr_badr); | 
|  |  | 
|  | #if 0 | 
|  | /* Set RXINT_THR and TXINT_THR */ | 
|  |  | 
|  | putreg32(FTMAC100_ITC_RXINT_THR(1) | FTMAC100_ITC_TXINT_THR(1), | 
|  | &iobase->itc); | 
|  | #endif | 
|  |  | 
|  | /* Poll receive descriptor automatically */ | 
|  |  | 
|  | putreg32 (FTMAC100_APTC_RXPOLL_CNT(1), &iobase->aptc); | 
|  |  | 
|  | /* Set DMA burst length */ | 
|  |  | 
|  | putreg32 (FTMAC100_DBLAC_RXFIFO_LTHR(2) | | 
|  | FTMAC100_DBLAC_RXFIFO_HTHR(6) | | 
|  | FTMAC100_DBLAC_RX_THR_EN, &iobase->dblac); | 
|  |  | 
|  | #if 0 | 
|  | putreg32 (getreg32(&iobase->fcr) | 0x1, &iobase->fcr); | 
|  | putreg32 (getreg32(&iobase->bpr) | 0x1, &iobase->bpr); | 
|  | #endif | 
|  |  | 
|  | /* enable transmitter, receiver */ | 
|  |  | 
|  | putreg32 (MACCR_ENABLE_ALL, &iobase->maccr); | 
|  |  | 
|  | /* enable Rx, Tx interrupts */ | 
|  |  | 
|  | putreg32 (INT_MASK_ALL_ENABLED, &iobase->imr); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_mdio_read | 
|  | * | 
|  | * Description: | 
|  | *   Read MII registers | 
|  | * | 
|  | * Input Parameters: | 
|  | *   iobase - Pointer to the driver's registers base | 
|  | *      reg - MII register number | 
|  | * | 
|  | * Returned Value: | 
|  | *   Register value | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static uint32_t ftmac100_mdio_read(FAR struct ftmac100_register_s *iobase, | 
|  | int reg) | 
|  | { | 
|  | int i; | 
|  | uint32_t phycr = FTMAC100_PHYCR_PHYAD(1)   | | 
|  | FTMAC100_PHYCR_REGAD(reg) | | 
|  | FTMAC100_PHYCR_MIIRD; | 
|  |  | 
|  | putreg32(phycr, &iobase->phycr); | 
|  |  | 
|  | for (i = 0; i < 10; i++) | 
|  | { | 
|  | phycr = getreg32(&iobase->phycr); | 
|  | ninfo("%02x %d phycr=%08x\n", reg, i, phycr); | 
|  |  | 
|  | if ((phycr & FTMAC100_PHYCR_MIIRD) == 0) | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return phycr & 0xffff; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_set_mac | 
|  | * | 
|  | * Description: | 
|  | *   Set the MAC address | 
|  | * | 
|  | * Input Parameters: | 
|  | *   priv - Reference to the NuttX driver state structure | 
|  | *    mac - Six bytes MAC address | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_set_mac(FAR struct ftmac100_driver_s *priv, | 
|  | FAR const unsigned char *mac) | 
|  | { | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | unsigned int maddr = mac[0] << 8 | mac[1]; | 
|  | unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; | 
|  |  | 
|  | ninfo("%s(%x %x)\n", __func__, maddr, laddr); | 
|  |  | 
|  | putreg32(maddr, &iobase->mac_madr); | 
|  | putreg32(laddr, &iobase->mac_ladr); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_receive | 
|  | * | 
|  | * Description: | 
|  | *   An interrupt was received indicating the availability of a new RX packet | 
|  | * | 
|  | * Input Parameters: | 
|  | *   priv - Reference to the driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Global interrupts are disabled by interrupt handling logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_receive(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | FAR struct ftmac100_rxdes_s *rxdes; | 
|  | FAR uint8_t *data; | 
|  | uint32_t len; | 
|  | int found; | 
|  |  | 
|  | do | 
|  | { | 
|  | found = false; | 
|  | rxdes = ftmac100_current_rxdes(priv); | 
|  |  | 
|  | while (!(rxdes->rxdes0 & FTMAC100_RXDES0_RXDMA_OWN)) | 
|  | { | 
|  | if (rxdes->rxdes0 & FTMAC100_RXDES0_FRS) | 
|  | { | 
|  | found = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Clear status bits */ | 
|  |  | 
|  | rxdes->rxdes0 = FTMAC100_RXDES0_RXDMA_OWN; | 
|  |  | 
|  | priv->rx_pointer = (priv->rx_pointer + 1) & | 
|  | (CONFIG_FTMAC100_RX_DESC - 1); | 
|  | rxdes = ftmac100_current_rxdes(priv); | 
|  | } | 
|  |  | 
|  | if (!found) | 
|  | { | 
|  | ninfo("\nNOT FOUND\nCurrent RX %d rxdes0=%08x\n", | 
|  | priv->rx_pointer, rxdes->rxdes0); | 
|  | return; | 
|  | } | 
|  |  | 
|  | len = FTMAC100_RXDES0_RFL(rxdes->rxdes0); | 
|  | data = (uint8_t *)rxdes->rxdes2; | 
|  |  | 
|  | ninfo ("RX buffer %d (%08x), %x received (%d)\n", | 
|  | priv->rx_pointer, data, len, | 
|  | (rxdes->rxdes0 & FTMAC100_RXDES0_LRS)); | 
|  |  | 
|  | /* Copy the data data from the hardware to priv->ft_dev.d_buf.  Set | 
|  | * amount of data in priv->ft_dev.d_len | 
|  | */ | 
|  |  | 
|  | memcpy(priv->ft_dev.d_buf, data, len); | 
|  | priv->ft_dev.d_len = len; | 
|  |  | 
|  | #ifdef CONFIG_NET_PKT | 
|  | /* When packet sockets are enabled, feed the frame into the packet | 
|  | * tap. | 
|  | */ | 
|  |  | 
|  | pkt_input(&priv->ft_dev); | 
|  | #endif | 
|  |  | 
|  | /* We only accept IP packets of the configured type and ARP packets */ | 
|  |  | 
|  | #ifdef CONFIG_NET_IPv4 | 
|  | if (BUF->type == HTONS(ETHTYPE_IP)) | 
|  | { | 
|  | ninfo("IPv4 frame\n"); | 
|  |  | 
|  | /* Receive an IPv4 packet from the network device */ | 
|  |  | 
|  | ipv4_input(&priv->ft_dev); | 
|  |  | 
|  | /* If the above function invocation resulted in data that should be | 
|  | * sent out on the network, the field  d_len will set to a value | 
|  | * > 0. | 
|  | */ | 
|  |  | 
|  | if (priv->ft_dev.d_len > 0) | 
|  | { | 
|  | /* And send the packet */ | 
|  |  | 
|  | ftmac100_transmit(priv); | 
|  | } | 
|  | } | 
|  | else | 
|  | #endif | 
|  | #ifdef CONFIG_NET_IPv6 | 
|  | if (BUF->type == HTONS(ETHTYPE_IP6)) | 
|  | { | 
|  | ninfo("IPv6 frame\n"); | 
|  |  | 
|  | /* Give the IPv6 packet to the network layer */ | 
|  |  | 
|  | ipv6_input(&priv->ft_dev); | 
|  |  | 
|  | /* If the above function invocation resulted in data that should be | 
|  | * sent out on the network, the field  d_len will set to a value | 
|  | * > 0. | 
|  | */ | 
|  |  | 
|  | if (priv->ft_dev.d_len > 0) | 
|  | { | 
|  | /* And send the packet */ | 
|  |  | 
|  | ftmac100_transmit(priv); | 
|  | } | 
|  | } | 
|  | else | 
|  | #endif | 
|  | #ifdef CONFIG_NET_ARP | 
|  | if (BUF->type == HTONS(ETHTYPE_ARP)) | 
|  | { | 
|  | arp_input(&priv->ft_dev); | 
|  |  | 
|  | /* If the above function invocation resulted in data that should be | 
|  | * sent out on the network, the field  d_len will set to a value | 
|  | * > 0. | 
|  | */ | 
|  |  | 
|  | if (priv->ft_dev.d_len > 0) | 
|  | { | 
|  | ftmac100_transmit(priv); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | priv->rx_pointer = (priv->rx_pointer + 1) & | 
|  | (CONFIG_FTMAC100_RX_DESC - 1); | 
|  |  | 
|  | rxdes->rxdes1 &= FTMAC100_RXDES1_EDORR; | 
|  | rxdes->rxdes1 |= FTMAC100_RXDES1_RXBUF_SIZE(RX_BUF_SIZE); | 
|  | rxdes->rxdes0 |= FTMAC100_RXDES0_RXDMA_OWN; | 
|  | } | 
|  | while (true); /* While there are more packets to be processed */ | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_txdone | 
|  | * | 
|  | * Description: | 
|  | *   An interrupt was received indicating that the last TX packet(s) is done | 
|  | * | 
|  | * Input Parameters: | 
|  | *   priv - Reference to the driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Global interrupts are disabled by the watchdog logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_txdone(FAR struct ftmac100_driver_s *priv) | 
|  | { | 
|  | FAR struct ftmac100_txdes_s *txdes; | 
|  |  | 
|  | /* Check if a Tx was pending */ | 
|  |  | 
|  | while (priv->tx_pending) | 
|  | { | 
|  | txdes = ftmac100_current_clean_txdes(priv); | 
|  |  | 
|  | /* txdes owned by dma */ | 
|  |  | 
|  | if (txdes->txdes0 & FTMAC100_TXDES0_TXDMA_OWN) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* TODO: check for excessive and late collisions */ | 
|  |  | 
|  | /* txdes reset */ | 
|  |  | 
|  | txdes->txdes0 = 0; | 
|  | txdes->txdes1 &= FTMAC100_TXDES1_EDOTR; | 
|  | txdes->txdes2 = 0; | 
|  | txdes->txdes3 = 0; | 
|  |  | 
|  | priv->tx_clean_pointer = (priv->tx_clean_pointer + 1) & | 
|  | (CONFIG_FTMAC100_TX_DESC - 1); | 
|  | priv->tx_pending--; | 
|  | } | 
|  |  | 
|  | /* If no further xmits are pending, then cancel the TX timeout and | 
|  | * disable further Tx interrupts. | 
|  | */ | 
|  |  | 
|  | ninfo("txpending=%d\n", priv->tx_pending); | 
|  |  | 
|  | /* Cancel the TX timeout */ | 
|  |  | 
|  | wd_cancel(&priv->ft_txtimeout); | 
|  |  | 
|  | /* Then poll the network for new XMIT data */ | 
|  |  | 
|  | devif_poll(&priv->ft_dev, ftmac100_txpoll); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_interrupt_work | 
|  | * | 
|  | * Description: | 
|  | *   Perform interrupt related work from the worker thread | 
|  | * | 
|  | * Input Parameters: | 
|  | *   arg - The argument passed when work_queue() was called. | 
|  | * | 
|  | * Returned Value: | 
|  | *   OK on success | 
|  | * | 
|  | * Assumptions: | 
|  | *   Ethernet interrupts are disabled | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_interrupt_work(FAR void *arg) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)arg; | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | unsigned int status; | 
|  | unsigned int phycr; | 
|  |  | 
|  | /* Process pending Ethernet interrupts */ | 
|  |  | 
|  | net_lock(); | 
|  | status = priv->status; | 
|  |  | 
|  | ninfo("status=%08x(%08x) BASE=%p ISR=%p PHYCR=%p\n", | 
|  | status, getreg32(&iobase->isr), iobase, &iobase->isr, | 
|  | &iobase->phycr); | 
|  |  | 
|  | if (!status) | 
|  | { | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* Handle interrupts according to status bit settings */ | 
|  |  | 
|  | /* Check if we received an incoming packet, if so, call | 
|  | * ftmac100_receive(). | 
|  | */ | 
|  |  | 
|  | if (status & FTMAC100_INT_RPKT_SAV) | 
|  | { | 
|  | putreg32(1, &iobase->rxpd); | 
|  | } | 
|  |  | 
|  | if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) | 
|  | { | 
|  | ftmac100_receive(priv); | 
|  | } | 
|  |  | 
|  | /* Check if a packet transmission just completed.  If so, call | 
|  | * ftmac100_txdone().  This may disable further Tx interrupts if there are | 
|  | * no pending transmissions. | 
|  | */ | 
|  |  | 
|  | if (status & (FTMAC100_INT_XPKT_OK)) | 
|  | { | 
|  | ninfo("\n\nTXDONE\n\n"); | 
|  | ftmac100_txdone(priv); | 
|  | } | 
|  |  | 
|  | if (status & FTMAC100_INT_PHYSTS_CHG) | 
|  | { | 
|  | /* PHY link status change */ | 
|  |  | 
|  | phycr = ftmac100_mdio_read(iobase, 1); | 
|  | if (phycr & 0x04) | 
|  | { | 
|  | priv->ft_bifup = true; | 
|  | } | 
|  | else | 
|  | { | 
|  | priv->ft_bifup = false; | 
|  | } | 
|  |  | 
|  | ninfo("Link: %s\n", priv->ft_bifup ? "UP" : "DOWN"); | 
|  | ftmac100_mdio_read(iobase, 5); | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | #define REG(x) (*(volatile uint32_t *)(x)) | 
|  | ninfo("\n=============================================================\n"); | 
|  | ninfo("TM CNTL=%08x INTRS=%08x MASK=%08x LOAD=%08x COUNT=%08x M1=%08x\n", | 
|  | REG(0x98400030), REG(0x98400034), REG(0x98400038), REG(0x98400004), | 
|  | REG(0x98400000), REG(0x98400008)); | 
|  | ninfo("IRQ STATUS=%08x MASK=%08x MODE=%08x LEVEL=%08x\n", | 
|  | REG(0x98800014), REG(0x98800004), REG(0x9880000c), REG(0x98800010)); | 
|  | ninfo("FIQ STATUS=%08x MASK=%08x MODE=%08x LEVEL=%08x\n", REG(0x98800034), | 
|  | REG(0x98800024), REG(0x9880002c), REG(0x98800020)); | 
|  | ninfo("=============================================================\n"); | 
|  | #endif | 
|  |  | 
|  | out: | 
|  | putreg32 (INT_MASK_ALL_ENABLED, &iobase->imr); | 
|  |  | 
|  | ninfo("ISR-done\n"); | 
|  | net_unlock(); | 
|  |  | 
|  | /* Re-enable Ethernet interrupts */ | 
|  |  | 
|  | up_enable_irq(CONFIG_FTMAC100_IRQ); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_interrupt | 
|  | * | 
|  | * Description: | 
|  | *   Hardware interrupt handler | 
|  | * | 
|  | * Input Parameters: | 
|  | *   irq     - Number of the IRQ that generated the interrupt | 
|  | *   context - Interrupt register state save info (architecture-specific) | 
|  | * | 
|  | * Returned Value: | 
|  | *   OK on success | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static int ftmac100_interrupt(int irq, FAR void *context, FAR void *arg) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = &g_ftmac100[0]; | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  |  | 
|  | /* Disable further Ethernet interrupts.  Because Ethernet interrupts are | 
|  | * also disabled if the TX timeout event occurs, there can be no race | 
|  | * condition here. | 
|  | */ | 
|  |  | 
|  | priv->status = getreg32(&iobase->isr); | 
|  |  | 
|  | up_disable_irq(CONFIG_FTMAC100_IRQ); | 
|  |  | 
|  | putreg32 (INT_MASK_ALL_DISABLED, &iobase->imr); | 
|  |  | 
|  | /* TODO: Determine if a TX transfer just completed */ | 
|  |  | 
|  | ninfo("===> status=%08x\n", priv->status); | 
|  |  | 
|  | if (priv->status & (FTMAC100_INT_XPKT_OK)) | 
|  | { | 
|  | /* If a TX transfer just completed, then cancel the TX timeout so | 
|  | * there will be do race condition between any subsequent timeout | 
|  | * expiration and the deferred interrupt processing. | 
|  | */ | 
|  |  | 
|  | ninfo("\n\nTXDONE 0\n\n"); | 
|  | wd_cancel(&priv->ft_txtimeout); | 
|  | } | 
|  |  | 
|  | /* Schedule to perform the interrupt processing on the worker thread. */ | 
|  |  | 
|  | work_queue(FTMAWORK, &priv->ft_irqwork, ftmac100_interrupt_work, | 
|  | priv, 0); | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_txtimeout_work | 
|  | * | 
|  | * Description: | 
|  | *   Perform TX timeout related work from the worker thread | 
|  | * | 
|  | * Input Parameters: | 
|  | *   arg - The argument passed when work_queue() as called. | 
|  | * | 
|  | * Returned Value: | 
|  | *   OK on success | 
|  | * | 
|  | * Assumptions: | 
|  | *   Ethernet interrupts are disabled | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_txtimeout_work(FAR void *arg) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = (FAR struct ftmac100_driver_s *)arg; | 
|  |  | 
|  | ninfo("TXTIMEOUT\n"); | 
|  |  | 
|  | /* Process pending Ethernet interrupts */ | 
|  |  | 
|  | net_lock(); | 
|  |  | 
|  | /* Then poll the network for new XMIT data */ | 
|  |  | 
|  | devif_poll(&priv->ft_dev, ftmac100_txpoll); | 
|  | net_unlock(); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_txtimeout_expiry | 
|  | * | 
|  | * Description: | 
|  | *   Our TX watchdog timed out.  Called from the timer interrupt handler. | 
|  | *   The last TX never completed.  Reset the hardware and start again. | 
|  | * | 
|  | * Input Parameters: | 
|  | *   arg  - The argument | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Global interrupts are disabled by the watchdog logic. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_txtimeout_expiry(wdparm_t arg) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = (FAR struct ftmac100_driver_s *)arg; | 
|  |  | 
|  | /* Disable further Ethernet interrupts.  This will prevent some race | 
|  | * conditions with interrupt work.  There is still a potential race | 
|  | * condition with interrupt work that is already queued and in progress. | 
|  | */ | 
|  |  | 
|  | up_disable_irq(CONFIG_FTMAC100_IRQ); | 
|  |  | 
|  | /* Schedule to perform the TX timeout processing on the worker thread. */ | 
|  |  | 
|  | work_queue(FTMAWORK, &priv->ft_irqwork, ftmac100_txtimeout_work, priv, 0); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_ifup | 
|  | * | 
|  | * Description: | 
|  | *   NuttX Callback: Bring up the Ethernet interface when an IP address is | 
|  | *   provided | 
|  | * | 
|  | * Input Parameters: | 
|  | *   dev - Reference to the NuttX driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static int ftmac100_ifup(struct net_driver_s *dev) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)dev->d_private; | 
|  |  | 
|  | #ifdef CONFIG_NET_IPv4 | 
|  | ninfo("Bringing up: %u.%u.%u.%u\n", | 
|  | ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr), | 
|  | ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr)); | 
|  | #endif | 
|  | #ifdef CONFIG_NET_IPv6 | 
|  | ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | 
|  | dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], | 
|  | dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], | 
|  | dev->d_ipv6addr[6], dev->d_ipv6addr[7]); | 
|  | #endif | 
|  |  | 
|  | /* Initialize PHYs, the Ethernet interface, and setup up Ethernet | 
|  | * interrupts. | 
|  | */ | 
|  |  | 
|  | ftmac100_init(priv); | 
|  |  | 
|  | /* Instantiate the MAC address from | 
|  | * priv->ft_dev.d_mac.ether.ether_addr_octet | 
|  | */ | 
|  |  | 
|  | ftmac100_set_mac(priv, priv->ft_dev.d_mac.ether.ether_addr_octet); | 
|  |  | 
|  | /* Enable the Ethernet interrupt */ | 
|  |  | 
|  | priv->ft_bifup = true; | 
|  | up_enable_irq(CONFIG_FTMAC100_IRQ); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_ifdown | 
|  | * | 
|  | * Description: | 
|  | *   NuttX Callback: Stop the interface. | 
|  | * | 
|  | * Input Parameters: | 
|  | *   dev - Reference to the NuttX driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static int ftmac100_ifdown(struct net_driver_s *dev) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)dev->d_private; | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | irqstate_t flags; | 
|  |  | 
|  | /* Disable the Ethernet interrupt */ | 
|  |  | 
|  | flags = enter_critical_section(); | 
|  | up_disable_irq(CONFIG_FTMAC100_IRQ); | 
|  |  | 
|  | /* Cancel the TX timeout timers */ | 
|  |  | 
|  | wd_cancel(&priv->ft_txtimeout); | 
|  |  | 
|  | /* Put the EMAC in its reset, non-operational state.  This should be | 
|  | * a known configuration that will guarantee the ftmac100_ifup() always | 
|  | * successfully brings the interface back up. | 
|  | */ | 
|  |  | 
|  | putreg32 (0, &iobase->maccr); | 
|  |  | 
|  | /* Mark the device "down" */ | 
|  |  | 
|  | priv->ft_bifup = false; | 
|  | leave_critical_section(flags); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_txavail_work | 
|  | * | 
|  | * Description: | 
|  | *   Perform an out-of-cycle poll on the worker thread. | 
|  | * | 
|  | * Input Parameters: | 
|  | *   arg - Reference to the NuttX driver state structure (cast to void*) | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Called on the higher priority worker thread. | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static void ftmac100_txavail_work(FAR void *arg) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = (FAR struct ftmac100_driver_s *)arg; | 
|  |  | 
|  | /* Perform the poll */ | 
|  |  | 
|  | net_lock(); | 
|  |  | 
|  | /* Ignore the notification if the interface is not yet up */ | 
|  |  | 
|  | if (priv->ft_bifup) | 
|  | { | 
|  | /* Check if there is room in the hardware to hold another outgoing | 
|  | * packet. | 
|  | */ | 
|  |  | 
|  | /* If so, then poll the network for new XMIT data */ | 
|  |  | 
|  | devif_poll(&priv->ft_dev, ftmac100_txpoll); | 
|  | } | 
|  |  | 
|  | net_unlock(); | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_txavail | 
|  | * | 
|  | * Description: | 
|  | *   Driver callback invoked when new TX data is available.  This is a | 
|  | *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX | 
|  | *   latency. | 
|  | * | 
|  | * Input Parameters: | 
|  | *   dev - Reference to the NuttX driver state structure | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | *   Called in normal user mode | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | static int ftmac100_txavail(struct net_driver_s *dev) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)dev->d_private; | 
|  |  | 
|  | /* Is our single work structure available?  It may not be if there are | 
|  | * pending interrupt actions and we will have to ignore the Tx | 
|  | * availability action. | 
|  | */ | 
|  |  | 
|  | if (work_available(&priv->ft_pollwork)) | 
|  | { | 
|  | /* Schedule to serialize the poll on the worker thread. */ | 
|  |  | 
|  | work_queue(FTMAWORK, &priv->ft_pollwork, ftmac100_txavail_work, | 
|  | priv, 0); | 
|  | } | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_addmac | 
|  | * | 
|  | * Description: | 
|  | *   NuttX Callback: Add the specified MAC address to the hardware multicast | 
|  | *   address filtering | 
|  | * | 
|  | * Input Parameters: | 
|  | *   dev  - Reference to the NuttX driver state structure | 
|  | *   mac  - The MAC address to be added | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | #if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) | 
|  | static int ftmac100_addmac(struct net_driver_s *dev, FAR const uint8_t *mac) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)dev->d_private; | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | uint32_t hash_value; | 
|  | uint32_t hash_reg; | 
|  | uint32_t hash_bit; | 
|  | uint32_t mta; | 
|  |  | 
|  | /* Calculate Ethernet CRC32 for MAC */ | 
|  |  | 
|  | hash_value = crc32part(mac, 6, ~0L); | 
|  |  | 
|  | /* The HASH Table  is a register array of 2 32-bit registers. | 
|  | * It is treated like an array of 64 bits.  We want to set | 
|  | * bit BitArray[hash_value]. So we figure out what register | 
|  | * the bit is in, read it, OR in the new bit, then write | 
|  | * back the new value.  The register is determined by the | 
|  | * upper 7 bits of the hash value and the bit within that | 
|  | * register are determined by the lower 5 bits of the value. | 
|  | */ | 
|  |  | 
|  | hash_reg = (hash_value >> 31) & 0x1; | 
|  | hash_bit = (hash_value >> 26) & 0x1f; | 
|  |  | 
|  | /* Add the MAC address to the hardware multicast routing table */ | 
|  |  | 
|  | mta = getreg32(&iobase->maht0 + hash_reg); | 
|  |  | 
|  | mta |= (1 << hash_bit); | 
|  |  | 
|  | putreg32(mta, &iobase->maht0 + hash_reg); | 
|  |  | 
|  | return OK; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_rmmac | 
|  | * | 
|  | * Description: | 
|  | *   NuttX Callback: Remove the specified MAC address from the hardware | 
|  | *   multicast address filtering | 
|  | * | 
|  | * Input Parameters: | 
|  | *   dev  - Reference to the NuttX driver state structure | 
|  | *   mac  - The MAC address to be removed | 
|  | * | 
|  | * Returned Value: | 
|  | *   None | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | #ifdef CONFIG_NET_MCASTGROUP | 
|  | static int ftmac100_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac) | 
|  | { | 
|  | FAR struct ftmac100_driver_s *priv = | 
|  | (FAR struct ftmac100_driver_s *)dev->d_private; | 
|  | FAR struct ftmac100_register_s *iobase = | 
|  | (FAR struct ftmac100_register_s *)priv->iobase; | 
|  | uint32_t hash_value; | 
|  | uint32_t hash_reg; | 
|  | uint32_t hash_bit; | 
|  | uint32_t mta; | 
|  |  | 
|  | /* Calculate Ethernet CRC32 for MAC */ | 
|  |  | 
|  | hash_value = crc32part(mac, 6, ~0L); | 
|  |  | 
|  | hash_reg = (hash_value >> 31) & 0x1; | 
|  | hash_bit = (hash_value >> 26) & 0x1f; | 
|  |  | 
|  | /* Remove the MAC address to the hardware multicast routing table */ | 
|  |  | 
|  | mta = getreg32(&iobase->maht0 + hash_reg); | 
|  |  | 
|  | mta &= ~(1 << hash_bit); | 
|  |  | 
|  | putreg32(mta, &iobase->maht0 + hash_reg); | 
|  |  | 
|  | return OK; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Public Functions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | * Name: ftmac100_initialize | 
|  | * | 
|  | * Description: | 
|  | *   Initialize the Ethernet controller and driver | 
|  | * | 
|  | * Input Parameters: | 
|  | *   intf - In the case where there are multiple EMACs, this value | 
|  | *          identifies which EMAC is to be initialized. | 
|  | * | 
|  | * Returned Value: | 
|  | *   OK on success; Negated errno on failure. | 
|  | * | 
|  | * Assumptions: | 
|  | * | 
|  | ****************************************************************************/ | 
|  |  | 
|  | int ftmac100_initialize(int intf) | 
|  | { | 
|  | struct ftmac100_driver_s *priv; | 
|  |  | 
|  | /* Get the interface structure associated with this interface number. */ | 
|  |  | 
|  | DEBUGASSERT(intf < CONFIG_FTMAC100_NINTERFACES); | 
|  | priv = &g_ftmac100[intf]; | 
|  |  | 
|  | /* Attach the IRQ to the driver */ | 
|  |  | 
|  | if (irq_attach(CONFIG_FTMAC100_IRQ, ftmac100_interrupt, NULL)) | 
|  | { | 
|  | /* We could not attach the ISR to the interrupt */ | 
|  |  | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | /* Initialize the driver structure */ | 
|  |  | 
|  | memset(priv, 0, sizeof(struct ftmac100_driver_s)); | 
|  | priv->ft_dev.d_buf     = (FAR uint8_t *)g_pktbuf[intf]; /* Single packet buffer */ | 
|  | priv->ft_dev.d_ifup    = ftmac100_ifup;                 /* I/F up (new IP address) callback */ | 
|  | priv->ft_dev.d_ifdown  = ftmac100_ifdown;               /* I/F down callback */ | 
|  | priv->ft_dev.d_txavail = ftmac100_txavail;              /* New TX data callback */ | 
|  | #ifdef CONFIG_NET_MCASTGROUP | 
|  | priv->ft_dev.d_addmac  = ftmac100_addmac;               /* Add multicast MAC address */ | 
|  | priv->ft_dev.d_rmmac   = ftmac100_rmmac;                /* Remove multicast MAC address */ | 
|  | #endif | 
|  | priv->ft_dev.d_private = g_ftmac100;                    /* Used to recover private state from dev */ | 
|  | priv->iobase           = CONFIG_FTMAC100_BASE; | 
|  |  | 
|  | /* Put the interface in the down state.  This usually amounts to resetting | 
|  | * the device and/or calling ftmac100_ifdown(). | 
|  | */ | 
|  |  | 
|  | ftmac100_reset(priv); | 
|  |  | 
|  | /* Read the MAC address from the hardware into | 
|  | * priv->ft_dev.d_mac.ether.ether_addr_octet | 
|  | */ | 
|  |  | 
|  | memcpy(priv->ft_dev.d_mac.ether.ether_addr_octet, | 
|  | (FAR void *)(CONFIG_FTMAC100_MAC0_ENV_ADDR), 6); | 
|  |  | 
|  | /* Register the device with the OS so that socket IOCTLs can be performed */ | 
|  |  | 
|  | netdev_register(&priv->ft_dev, NET_LL_ETHERNET); | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_NET && CONFIG_NET_FTMAC100 */ |