| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342 |
- /*
- * Copyright (C) 2008 by egnite GmbH. All rights reserved.
- * Copyright (C) 2003-2005 by egnite Software GmbH. 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/
- *
- */
- /*
- * $Log$
- * Revision 1.12 2009/02/06 15:37:39 haraldkipp
- * Added stack space multiplier and addend. Adjusted stack space.
- *
- * Revision 1.11 2008/08/28 11:12:15 haraldkipp
- * Added interface flags, which will be required to implement Ethernet ioctl
- * functions.
- *
- * Revision 1.10 2008/08/11 06:59:07 haraldkipp
- * BSD types replaced by stdint types (feature request #1282721).
- *
- * Revision 1.9 2008/02/15 17:09:44 haraldkipp
- * Added support for the Elektor Internet Radio.
- *
- * Revision 1.8 2007/08/17 11:43:46 haraldkipp
- * Enable multicast.
- *
- * Revision 1.7 2007/05/24 07:26:44 haraldkipp
- * Added some delay befor reading the status of the received packet. Fixes
- * bug #1672527, thanks to Andreas Helmcke.
- *
- * Revision 1.6 2007/05/02 11:22:51 haraldkipp
- * Added multicast table entry.
- *
- * Revision 1.5 2006/06/28 17:10:15 haraldkipp
- * Include more general header file for ARM.
- *
- * Revision 1.4 2006/03/16 19:04:48 haraldkipp
- * Adding a short delay before reading the status word makes it work with
- * compiler optimization. On receiver overflow interrupts the chip is
- * declared insane. The output routine will no more enter NutEventWait()
- * on insane chips.
- *
- * Revision 1.3 2006/03/02 19:51:16 haraldkipp
- * Replaced GCC specific inline specifications with their portable
- * counterparts.
- *
- * Revision 1.2 2006/01/23 17:33:14 haraldkipp
- * Possible memory alignment problem may start network interface too early.
- *
- * Revision 1.1 2005/10/24 08:49:05 haraldkipp
- * Initial check in.
- *
- */
- #include <cfg/os.h>
- #include <arch/arm.h>
- #include <string.h>
- #include <stdlib.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>
- #include <netinet/if_ether.h>
- #include <net/ether.h>
- #include <net/if_var.h>
- #include <dev/irqreg.h>
- #include <dev/phy.h>
- #include <dev/dm9000e.h>
- /* WARNING: Variadic macros are C99 and may fail with C89 compilers. */
- #ifdef NUTDEBUG
- #include <stdio.h>
- #define DMPRINTF(args,...) printf(args,##__VA_ARGS__)
- #else
- #define DMPRINTF(args,...)
- #endif
- #ifndef NUT_THREAD_NICRXSTACK
- /* arm-elf-gcc size optimized code used 160 bytes. */
- #define NUT_THREAD_NICRXSTACK 384
- #endif
- /*
- * Determine ports, which had not been explicitely configured.
- */
- #if defined(ETHERNUT3)
- #ifndef NIC_BASE_ADDR
- #define NIC_BASE_ADDR 0x20000000
- #endif
- #ifndef NIC_SIGNAL_IRQ
- #define NIC_SIGNAL_IRQ INT1
- #endif
- #ifndef NIC_SIGNAL_PDR
- #define NIC_SIGNAL_PDR PIO_PDR
- #endif
- #ifndef NIC_SIGNAL_BIT
- #define NIC_SIGNAL_BIT 10
- #endif
- #elif defined(ELEKTOR_IR1)
- #ifndef NIC_BASE_ADDR
- #define NIC_BASE_ADDR 0x30000000
- #endif
- #ifndef NIC_SIGNAL_IRQ
- #define NIC_SIGNAL_IRQ INT0
- #endif
- #ifndef NIC_SIGNAL_PDR
- #define NIC_SIGNAL_PDR PIOB_PDR
- #endif
- #ifndef NIC_SIGNAL_XSR
- #define NIC_SIGNAL_XSR PIOB_ASR
- #endif
- #ifndef NIC_SIGNAL_BIT
- #define NIC_SIGNAL_BIT PB20_IRQ0_A
- #endif
- #endif
- #ifdef NIC_BASE_ADDR
- #ifndef NIC_DATA_ADDR
- #define NIC_DATA_ADDR (NIC_BASE_ADDR + 4)
- #endif
- #define INT0 0
- #define INT1 1
- #define INT2 2
- #define INT3 3
- #define INT4 4
- #define INT5 5
- #define INT6 6
- #define INT7 7
- #ifdef NIC_RESET_BIT
- #if (NIC_RESET_AVRPORT == AVRPORTB)
- #define NIC_RESET_PORT PORTB
- #define NIC_RESET_DDR DDRB
- #elif (NIC_RESET_AVRPORT == AVRPORTD)
- #define NIC_RESET_PORT PORTD
- #define NIC_RESET_DDR DDRD
- #elif (NIC_RESET_AVRPORT == AVRPORTE)
- #define NIC_RESET_PORT PORTE
- #define NIC_RESET_DDR DDRE
- #elif (NIC_RESET_AVRPORT == AVRPORTF)
- #define NIC_RESET_PORT PORTF
- #define NIC_RESET_DDR DDRF
- #endif /* NIC_RESET_AVRPORT */
- #endif /* NIC_RESET_BIT */
- /*
- * Determine interrupt settings.
- * DOES NOT WORK
- */
- #if (NIC_SIGNAL_IRQ == INT0)
- #define NIC_SIGNAL sig_INTERRUPT0
- #elif (NIC_SIGNAL_IRQ == INT2)
- #define NIC_SIGNAL sig_INTERRUPT2
- #elif (NIC_SIGNAL_IRQ == INT3)
- #define NIC_SIGNAL sig_INTERRUPT3
- #elif (NIC_SIGNAL_IRQ == INT4)
- #define NIC_SIGNAL sig_INTERRUPT4
- #elif (NIC_SIGNAL_IRQ == INT5)
- #define NIC_SIGNAL sig_INTERRUPT5
- #elif (NIC_SIGNAL_IRQ == INT6)
- #define NIC_SIGNAL sig_INTERRUPT6
- #elif (NIC_SIGNAL_IRQ == INT7)
- #define NIC_SIGNAL sig_INTERRUPT7
- #else
- #define NIC_SIGNAL sig_INTERRUPT1
- #endif
- /*!
- * \addtogroup xgDm9000eRegs
- */
- /*@{*/
- #define NIC_NCR 0x00 /* Network control register (0x00). */
- #define NIC_NCR_LBM 0x06 /* Loopback mode. */
- #define NIC_NCR_LBNORM 0x00 /* Normal mode. */
- #define NIC_NCR_LBMAC 0x02 /* MAC loopback. */
- #define NIC_NCR_LBPHY 0x04 /* PHY loopback. */
- #define NIC_NCR_RST 0x01 /* Software reset, auto clear. */
- #define NIC_NSR 0x01 /* Network status register (0x00). */
- #define NIC_NSR_SPEED 0x80
- #define NIC_NSR_LINKST 0x40
- #define NIC_NSR_WAKEST 0x20
- #define NIC_NSR_TX2END 0x08
- #define NIC_NSR_TX1END 0x04
- #define NIC_NSR_RXOV 0x02
- #define NIC_TCR 0x02 /* TX control register (0x00). */
- #define NIC_TCR_TXREQ 0x01 /* TX request */
- #define NIC_TSR1 0x03 /* TX status register I (0x00). */
- #define NIC_TSR2 0x04 /* TX status register II (0x00). */
- #define NIC_RCR 0x05 /* RX control register (0x00). */
- #define NIC_RCR_DIS_LONG 0x20 /* Discard long packets. */
- #define NIC_RCR_DIS_CRC 0x10 /* Discard CRC error packets. */
- #define NIC_RCR_ALL 0x08 /* Pass all multicast */
- #define NIC_RCR_PRMSC 0x02 /* Enable promiscuous mode. */
- #define NIC_RCR_RXEN 0x01 /* Enable receiver. */
- #define NIC_RSR 0x06 /* RX status register (0x00). */
- #define NIC_RSR_ERRORS 0xBF /* Error bit mask. */
- #define NIC_RSR_RF 0x80 /* Runt frame. */
- #define NIC_RSR_MF 0x40 /* Multicast frame. */
- #define NIC_RSR_LCS 0x20 /* Late collision. */
- #define NIC_RSR_RWTO 0x10 /* Receiver watchdog time out. */
- #define NIC_RSR_PLE 0x08 /* Physical layer error. */
- #define NIC_RSR_AE 0x04 /* Alignment error. */
- #define NIC_RSR_CE 0x02 /* CRC error. */
- #define NIC_RSR_FOE 0x01 /* FIFO overflow error. */
- #define NIC_ROCR 0x07 /* Receive overflow counter register (0x00). */
- #define NIC_BPTR 0x08 /* Back pressure threshold register (0x37). */
- #define NIC_FCTR 0x09 /* Flow control threshold register (0x38). */
- #define NIC_FCR 0x0A /* RX flow control register (0x00). */
- #define NIC_EPCR 0x0B /* EEPROM and PHY control register. */
- #define NIC_EPAR 0x0C /* EEPROM and PHY address register. */
- #define NIC_EPDRL 0x0D /* EEPROM and PHY low byte data register. */
- #define NIC_EPDRH 0x0E /* EEPROM and PHY high byte data register. */
- #define NIC_WCR 0x0F /* Wake up control register (0x00). */
- #define NIC_PAR 0x10 /* 6 byte physical address register. */
- #define NIC_MAR 0x16 /* 8 byte multicast address register. */
- #define NIC_GPCR 0x1E /* General purpose control register (?). */
- #define NIC_GPR 0x1F /* General purpose register (?). */
- #define NIC_TRPA 0x22 /* 2 byte TX SRAM read pointer address, low/high (0x0000). */
- #define NIC_RWPA 0x24 /* 2 byte RX SRAM write pointer address, low/high (0x0000). */
- #define NIC_VID 0x28 /* 2 byte vendor ID (0x0A46). */
- #define NIC_PID 0x2A /* 2 byte product ID (0x0900). */
- #define NIC_CHIPR 0x2C /* Chip revision (0x00). */
- #define NIC_SMCR 0x2F /* Special mode register (0x00). */
- #define NIC_MRCMDX 0xF0 /* Memory data read command w/o increment (?). */
- #define NIC_MRCMD 0xF2 /* Memory data read command with increment (?). */
- #define NIC_MRR 0xF4 /* 2 byte memory data read register, low/high (?). */
- #define NIC_MWCMDX 0xF6 /* Memory data write command register w/o increment (?). */
- #define NIC_MWCMD 0xF8 /* Memory data write command register with increment (?). */
- #define NIC_MWR 0xFA /* Memory data write command register with increment (?). */
- #define NIC_TXPL 0xFC /* 2 byte TX packet length register. (?). */
- #define NIC_ISR 0xFE /* Interrupt status register (0x00). */
- #define NIC_ISR_IOM 0xC0 /* I/O mode mask */
- #define NIC_ISR_M16 0x00 /* 16-bit I/O mode */
- #define NIC_ISR_M32 0x40 /* 32-bit I/O mode */
- #define NIC_ISR_M8 0x80 /* 8-bit I/O mode */
- #define NIC_ISR_ROOS 0x08 /* Receiver overflow counter interrupt. */
- #define NIC_ISR_ROS 0x04 /* Receiver overflow interrupt. */
- #define NIC_ISR_PTS 0x02 /* Transmitter interrupt. */
- #define NIC_ISR_PRS 0x01 /* Receiver interrupt. */
- #define NIC_IMR 0xFF /* Interrupt mask register (0x00). */
- #define NIC_IMR_PAR 0x80 /* Enable read/write pointer wrap around. */
- #define NIC_IMR_ROOM 0x08 /* Enable receiver overflow counter interrupts. */
- #define NIC_IMR_ROM 0x04 /* Enable receiver overflow interrupts. */
- #define NIC_IMR_PTM 0x02 /* Enable transmitter interrupts. */
- #define NIC_IMR_PRM 0x01 /* Enable receiver interrupts. */
- /*!
- * \brief Network interface controller information structure.
- */
- struct _NICINFO {
- #ifdef NUT_PERFMON
- 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_tx_queued; /*!< Number of packets in transmission queue. */
- volatile int ni_tx_quelen; /*!< Number of bytes in transmission queue not sent. */
- volatile int ni_insane; /*!< Set by error detection. */
- int ni_iomode; /*!< 8 or 16 bit access. 32 bit is not supported. */
- uint8_t ni_mar[8]; /*!< Multicast Address Register. */
- };
- /*!
- * \brief Network interface controller information type.
- */
- typedef struct _NICINFO NICINFO;
- /*@}*/
- /*!
- * \addtogroup xgNicDm9000e
- */
- /*@{*/
- /*
- * ether_crc32_le based on FreeBSD code from if_ethersubr.c
- */
- static const uint32_t crctab[] = {
- 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
- 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
- 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
- 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
- };
- static uint32_t ether_crc32_le(const uint8_t *buf, uint8_t len)
- {
- uint32_t crc;
- uint8_t i;
- crc = 0xffffffff; /* initial value */
- for (i = 0; i < len; i++) {
- crc ^= buf[i];
- crc = (crc >> 4) ^ crctab[crc & 0xf];
- crc = (crc >> 4) ^ crctab[crc & 0xf];
- }
- return (crc);
- }
- static INLINE void nic_outb(uint8_t reg, uint8_t val)
- {
- outb(NIC_BASE_ADDR, reg);
- outb(NIC_DATA_ADDR, val);
- }
- static INLINE uint8_t nic_inb(uint16_t reg)
- {
- outb(NIC_BASE_ADDR, reg);
- return inb(NIC_DATA_ADDR);
- }
- /*!
- * \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)
- {
- /* Select PHY register */
- nic_outb(NIC_EPAR, 0x40 | reg);
- /* PHY read command. */
- nic_outb(NIC_EPCR, 0x0C);
- NutDelay(1);
- nic_outb(NIC_EPCR, 0x00);
- /* Get data from PHY data register. */
- return ((uint16_t) nic_inb(NIC_EPDRH) << 8) | (uint16_t) nic_inb(NIC_EPDRL);
- }
- /*!
- * \brief Write value to PHY register.
- *
- * \note NIC interrupts must have been disabled before calling this routine.
- *
- * \param reg PHY register number.
- * \param val Value to write.
- */
- static void phy_outw(uint8_t reg, uint16_t val)
- {
- /* Select PHY register */
- nic_outb(NIC_EPAR, 0x40 | reg);
- /* Store value in PHY data register. */
- nic_outb(NIC_EPDRL, (uint8_t) (val));
- nic_outb(NIC_EPDRH, (uint8_t) (val >> 8));
- /* PHY write command. */
- nic_outb(NIC_EPCR, 0x0A);
- NutDelay(1);
- nic_outb(NIC_EPCR, 0x00);
- }
- static int NicPhyInit(void)
- {
- uint32_t phy = 1;
- /* Restart auto negotiation. */
- NutPhyCtl(PHY_CTL_AUTONEG_RE, &phy);
- nic_outb(NIC_GPCR, 1);
- nic_outb(NIC_GPR, 0);
- return 0;
- }
- /*!
- * \brief Reset the Ethernet controller.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int NicReset(void)
- {
- /* Hardware reset. */
- #ifdef undef_NIC_RESET_BIT
- sbi(NIC_RESET_DDR, NIC_RESET_BIT);
- sbi(NIC_RESET_PORT, NIC_RESET_BIT);
- NutDelay(WAIT100);
- cbi(NIC_RESET_PORT, NIC_RESET_BIT);
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- #else
- /* Software reset. */
- nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC);
- NutDelay(1);
- /* FIXME: Delay required. */
- #endif
- DMPRINTF("\n*DMRES*\n");
- return NicPhyInit();
- }
- /*
- * NIC interrupt entry.
- */
- static void NicInterrupt(void *arg)
- {
- uint_fast8_t isr;
- NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb;
- /* Read interrupt status and disable interrupts. */
- isr = nic_inb(NIC_ISR);
- DMPRINTF("*DMIRQ %02x:", isr);
- /* Receiver interrupt. */
- if (isr & NIC_ISR_PRS) {
- DMPRINTF("RX*");
- nic_outb(NIC_ISR, NIC_ISR_PRS);
- NutEventPostFromIrq(&ni->ni_rx_rdy);
- }
- /* Transmitter interrupt. */
- if (isr & NIC_ISR_PTS) {
- DMPRINTF("TX*");
- if (ni->ni_tx_queued) {
- if (ni->ni_tx_quelen) {
- /* Initiate transfer of a queued packet. */
- nic_outb(NIC_TXPL, (uint8_t) ni->ni_tx_quelen);
- nic_outb(NIC_TXPL + 1, (uint8_t) (ni->ni_tx_quelen >> 8));
- ni->ni_tx_quelen = 0;
- nic_outb(NIC_TCR, NIC_TCR_TXREQ);
- }
- ni->ni_tx_queued--;
- }
- nic_outb(NIC_ISR, NIC_ISR_PTS);
- NutEventPostFromIrq(&ni->ni_tx_rdy);
- }
- /* Receiver overflow interrupt. */
- if (isr & NIC_ISR_ROS) {
- DMPRINTF("OVI*");
- nic_outb(NIC_ISR, NIC_ISR_ROS);
- ni->ni_insane = 1;
- NutEventPostFromIrq(&ni->ni_rx_rdy);
- }
- /* Receiver overflow counter interrupt. */
- if (isr & NIC_ISR_ROOS) {
- DMPRINTF("OVC*");
- nic_outb(NIC_ISR, NIC_ISR_ROOS);
- NutEventPostFromIrq(&ni->ni_rx_rdy);
- }
- if( ni->ni_insane)
- DMPRINTF("INS\n");
- else
- DMPRINTF("\n");
- }
- /*!
- * \brief Write data block to the NIC.
- *
- * NIC interrupts must be disabled when calling this function.
- */
- static void NicWrite8(uint8_t * buf, uint16_t len)
- {
- while (len--) {
- outb(NIC_DATA_ADDR, *buf);
- buf++;
- }
- }
- /*!
- * \brief Write data block to the NIC.
- *
- * NIC interrupts must be disabled when calling this function.
- */
- static void NicWrite16(uint8_t * buf, uint16_t len)
- {
- uint16_t *wp = (uint16_t *) buf;
- len = (len + 1) / 2;
- while (len--) {
- outw(NIC_DATA_ADDR, *wp);
- wp++;
- }
- }
- /*!
- * \brief Read data block from the NIC.
- *
- * NIC interrupts must be disabled when calling this function.
- */
- static void NicRead8(uint8_t * buf, uint16_t len)
- {
- while (len--) {
- *buf++ = inb(NIC_DATA_ADDR);
- }
- }
- /*!
- * \brief Read data block from the NIC.
- *
- * NIC interrupts must be disabled when calling this function.
- */
- static void NicRead16(uint8_t * buf, uint16_t len)
- {
- uint16_t *wp = (uint16_t *) buf;
- len = (len + 1) / 2;
- while (len--) {
- *wp++ = inw(NIC_DATA_ADDR);
- }
- }
- /*!
- * \brief Fetch the next packet out of the receive ring buffer.
- *
- * \return Pointer to an allocated ::NETBUF. If there is no
- * no data available, then the function returns a
- * null pointer. If the NIC's buffer seems to be
- * corrupted, a pointer to 0xFFFF is returned.
- */
- static int NicGetPacket(NICINFO * ni, NETBUF ** nbp)
- {
- int rc = -1;
- uint16_t fsw;
- uint16_t fbc;
- *nbp = NULL;
- /* Disable NIC interrupts. */
- NutIrqDisable(&NIC_SIGNAL);
- /*
- * Read the status word w/o auto increment. If zero, no packet is
- * available. Otherwise it should be set to one. Any other value
- * indicates a weird chip crying for reset.
- */
- nic_inb(NIC_MRCMDX);
- /* Add some delay befor reading the status of the received packet. */
- _NOP(); _NOP(); _NOP(); _NOP();
- fsw = inb(NIC_DATA_ADDR);
- if (fsw > 1) {
- ni->ni_insane = 1;
- } else if (fsw) {
- /* Now read status word and byte count with auto increment. */
- outb(NIC_BASE_ADDR, NIC_MRCMD);
- if (ni->ni_iomode == NIC_ISR_M16) {
- fsw = inw(NIC_DATA_ADDR);
- _NOP(); _NOP(); _NOP(); _NOP();
- fbc = inw(NIC_DATA_ADDR);
- } else {
- fsw = inb(NIC_DATA_ADDR) + ((uint16_t) inb(NIC_DATA_ADDR) << 8);
- _NOP(); _NOP(); _NOP(); _NOP();
- fbc = inb(NIC_DATA_ADDR) + ((uint16_t) inb(NIC_DATA_ADDR) << 8);
- }
- /*
- * Receiving long packets is unexpected, because we disabled
- * this during initialization. Let's declare the chip insane.
- * Short packets will be handled by the caller.
- */
- if (fbc > 1536) {
- ni->ni_insane = 1;
- } else {
- /*
- * The high byte of the status word contains a copy of the
- * receiver status register.
- */
- fsw >>= 8;
- fsw &= NIC_RSR_ERRORS;
- #ifdef NUT_PERMON
- /* Update statistics. */
- if (fsw) {
- if (RxStatus & NIC_RSR_CE) {
- ni->ni_crc_errors++;
- } else if (RxStatus & NIC_RSR_FOE) {
- ni->ni_overruns++;
- } else {
- ni->ni_rx_missed_errors++;
- }
- } else {
- ni->ni_rx_packets++;
- }
- #endif
- /*
- * If we got an error packet or failed to allocated the
- * buffer, then silently discard the packet.
- */
- if (fsw || (*nbp = NutNetBufAlloc(0, NBAF_DATALINK, fbc - 4)) == NULL) {
- if (ni->ni_iomode == NIC_ISR_M16) {
- fbc = (fbc + 1) / 2;
- while (fbc--) {
- fsw = inw(NIC_DATA_ADDR);
- }
- } else {
- while (fbc--) {
- fsw = inb(NIC_DATA_ADDR);
- }
- }
- } else {
- if (ni->ni_iomode == NIC_ISR_M16) {
- /* Read packet data from 16 bit bus. */
- NicRead16((*nbp)->nb_dl.vp, (*nbp)->nb_dl.sz);
- /* Read packet CRC. */
- fsw = inw(NIC_DATA_ADDR);
- fsw = inw(NIC_DATA_ADDR);
- } else {
- /* Read packet data from 8 bit bus. */
- NicRead8((*nbp)->nb_dl.vp, (*nbp)->nb_dl.sz);
- /* Read packet CRC. */
- fsw = inb(NIC_DATA_ADDR);
- fsw = inb(NIC_DATA_ADDR);
- fsw = inb(NIC_DATA_ADDR);
- fsw = inb(NIC_DATA_ADDR);
- }
- /* Return success. */
- rc = 0;
- }
- }
- }
- /* Enable NIC interrupts if the chip is sane. */
- if (ni->ni_insane == 0) {
- NutIrqEnable(&NIC_SIGNAL);
- }
- 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 int NicPutPacket(NICINFO * ni, NETBUF * nb)
- {
- int rc = -1;
- uint16_t sz;
- /*
- * 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) > ETHERMTU) {
- return -1;
- }
- sz += nb->nb_dl.sz;
- if (sz & 1) {
- sz++;
- }
- /* Disable interrupts. */
- NutIrqDisable(&NIC_SIGNAL);
- /* TODO: Check for link. */
- if (ni->ni_insane == 0) {
- /* Enable data write. */
- outb(NIC_BASE_ADDR, NIC_MWCMD);
- /* Transfer the Ethernet frame. */
- if (ni->ni_iomode == NIC_ISR_M16) {
- NicWrite16(nb->nb_dl.vp, nb->nb_dl.sz);
- NicWrite16(nb->nb_nw.vp, nb->nb_nw.sz);
- NicWrite16(nb->nb_tp.vp, nb->nb_tp.sz);
- NicWrite16(nb->nb_ap.vp, nb->nb_ap.sz);
- } else {
- NicWrite8(nb->nb_dl.vp, nb->nb_dl.sz);
- NicWrite8(nb->nb_nw.vp, nb->nb_nw.sz);
- NicWrite8(nb->nb_tp.vp, nb->nb_tp.sz);
- NicWrite8(nb->nb_ap.vp, nb->nb_ap.sz);
- }
- /* If no packet is queued, start the transmission. */
- if (ni->ni_tx_queued == 0) {
- nic_outb(NIC_TXPL, (uint8_t) sz);
- nic_outb(NIC_TXPL + 1, (uint8_t) (sz >> 8));
- nic_outb(NIC_TCR, NIC_TCR_TXREQ);
- }
- /* ...otherwise mark this packet queued. */
- else {
- ni->ni_tx_quelen = sz;
- }
- ni->ni_tx_queued++;
- rc = 0;
- #ifdef NUT_PERFMON
- ni->ni_tx_packets++;
- #endif
- }
- /* Enable interrupts. */
- NutIrqEnable(&NIC_SIGNAL);
- /* If the controller buffer is filled with two packets, then
- wait for the first being sent out. */
- if (rc == 0 && ni->ni_tx_queued > 1) {
- NutEventWait(&ni->ni_tx_rdy, 500);
- }
- return rc;
- }
- /*!
- * \brief Update the multicast register.
- *
- * \param Network interface controller information.
- */
- static void NicUpdateMCHardware(NICINFO *ni)
- {
- int i;
- /* Enable broadcast receive. */
- ni->ni_mar[7] |= 0x80;
- /* Set multicast address register */
- for (i = 0; i < 7; i++) {
- nic_outb(NIC_MAR + i, ni->ni_mar[i]);
- }
- }
- /*!
- * \brief Fire up the network interface.
- *
- * NIC interrupts must be disabled when calling this function.
- *
- * \param mac Six byte unique MAC address.
- */
- static int NicStart(const uint8_t * mac, NICINFO * ni)
- {
- int i;
- int link_wait = 20;
- uint32_t phy;
- /* Power up the PHY. */
- nic_outb(NIC_GPR, 0);
- NutDelay(5);
- /* Software reset with MAC loopback. */
- nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC);
- NutDelay(5);
- nic_outb(NIC_NCR, NIC_NCR_RST | NIC_NCR_LBMAC);
- NutDelay(5);
- /*
- * PHY power down followed by PHY power up. This should activate
- * the auto sense link.
- */
- nic_outb(NIC_GPR, 1);
- nic_outb(NIC_GPR, 0);
- /* Set MAC address. */
- for (i = 0; i < 6; i++) {
- nic_outb(NIC_PAR + i, mac[i]);
- }
- /* Set multicast address register */
- NicUpdateMCHardware(ni);
- /* Clear interrupts. */
- nic_outb(NIC_ISR, NIC_ISR_ROOS | NIC_ISR_ROS | NIC_ISR_PTS | NIC_ISR_PRS);
- /* Enable late collision retries on the DM9000A. */
- if (nic_inb(NIC_CHIPR) == 0x19) {
- nic_outb(0x2D, 0x40);
- }
- /* Enable receiver. */
- nic_outb(NIC_RCR, NIC_RCR_DIS_LONG | NIC_RCR_DIS_CRC | NIC_RCR_RXEN | NIC_RCR_ALL);
- /* Restart autonegotiation */
- phy = 1;
- NutPhyCtl(PHY_CTL_AUTONEG_RE, &phy);
- /* Wait for auto negotiation completed and link established. */
- for (link_wait = 25;; link_wait--) {
- NutPhyCtl(PHY_GET_STATUS, &phy);
- if((phy & PHY_STATUS_HAS_LINK) && (phy & PHY_STATUS_AUTONEG_OK)) {
- break;
- }
- if (link_wait == 0) {
- return -1;
- }
- NutSleep(200);
- }
- /* Enable interrupts. */
- nic_outb(NIC_IMR, NIC_IMR_PAR | NIC_IMR_PTM | NIC_IMR_PRM);
- return 0;
- }
- /*! \fn NicRxLanc(void *arg)
- * \brief NIC receiver thread.
- *
- */
- THREAD(NicRxLanc, arg)
- {
- NUTDEVICE *dev;
- IFNET *ifn;
- NICINFO *ni;
- NETBUF *nb;
- dev = arg;
- ifn = (IFNET *) dev->dev_icb;
- ni = (NICINFO *) dev->dev_dcb;
- /*
- * 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.
- */
- while (NicStart(ifn->if_mac, ni)) {
- NutSleep(1000);
- }
- /* Initialize the access mutex. */
- NutEventPost(&ni->ni_mutex);
- /* Run at high priority. */
- NutThreadSetPriority(9);
- /* Enable interrupts. */
- #ifdef NIC_SIGNAL_XSR
- outr(NIC_SIGNAL_XSR, _BV(NIC_SIGNAL_BIT));
- #if defined(ELEKTOR_IR1)
- /* Ugly code alarm: Should be configurable. */
- outr(PMC_PCER, _BV(IRQ0_ID));
- #endif
- #endif
- outr(NIC_SIGNAL_PDR, _BV(NIC_SIGNAL_BIT));
- NutIrqEnable(&NIC_SIGNAL);
- #if defined(ELEKTOR_IR1)
- /* Ugly code alarm: Should be configurable. */
- NutIrqSetMode(&NIC_SIGNAL, NUT_IRQMODE_HIGHLEVEL);
- #endif
- for (;;) {
- /*
- * Wait for the arrival of new packets or poll the receiver
- * every two seconds.
- */
- NutEventWait(&ni->ni_rx_rdy, 2000);
- /*
- * Fetch all packets from the NIC's internal buffer and pass
- * them to the registered handler.
- */
- while (NicGetPacket(ni, &nb) == 0) {
- /* Discard short packets. */
- if (nb->nb_dl.sz < 60) {
- NutNetBufFree(nb);
- } else {
- (*ifn->if_recv) (dev, nb);
- }
- }
- /* We got a weird chip, try to restart it. */
- while (ni->ni_insane) {
- if (NicStart(ifn->if_mac, ni) == 0) {
- ni->ni_insane = 0;
- ni->ni_tx_queued = 0;
- ni->ni_tx_quelen = 0;
- NutIrqEnable(&NIC_SIGNAL);
- } else {
- NutSleep(1000);
- }
- }
- }
- }
- /*!
- * \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 DmOutput(NUTDEVICE * dev, NETBUF * nb)
- {
- static uint32_t mx_wait = 5000;
- int rc = -1;
- NICINFO *ni = (NICINFO *) dev->dev_dcb;
- /*
- * After initialization we are waiting for a long time to give
- * the PHY a chance to establish an Ethernet link.
- */
- while (rc) {
- if (ni->ni_insane) {
- break;
- }
- if (NutEventWait(&ni->ni_mutex, mx_wait)) {
- break;
- }
- /* Check for packet queue space. */
- if (ni->ni_tx_queued > 1) {
- if (NutEventWait(&ni->ni_tx_rdy, 500)) {
- /* No queue space. Release the lock and give up. */
- NutEventPost(&ni->ni_mutex);
- break;
- }
- } else if (NicPutPacket(ni, nb) == 0) {
- /* Ethernet works. Set a long waiting time in case we
- temporarly lose the link next time. */
- rc = 0;
- mx_wait = 5000;
- }
- 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;
- }
- return rc;
- }
- /*!
- * \brief Initialize Ethernet hardware.
- *
- * Resets the LAN91C111 Ethernet controller, initializes all required
- * hardware registers and starts a background thread for incoming
- * Ethernet traffic.
- *
- * Applications should do not directly call this function. It is
- * automatically executed during during device registration by
- * NutRegisterDevice().
- *
- * If the network configuration hasn't been set by the application
- * before registering the specified device, this function will
- * call NutNetLoadConfig() to get the MAC address.
- *
- * \param dev Identifies the device to initialize.
- */
- int DmInit(NUTDEVICE * dev)
- {
- uint32_t id;
- NICINFO *ni = (NICINFO *) dev->dev_dcb;
- #if defined(ELEKTOR_IR1)
- /* @@MF: "Power on reset" the Ethernet controller */
- outr(PIOC_SODR, _BV(PC17_NANDOE_B));
- outr(PIOC_PER, _BV(PC17_NANDOE_B));
- outr(PIOC_OER, _BV(PC17_NANDOE_B));
- outr(PIOC_CODR, _BV(PC17_NANDOE_B));
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- outr(PIOC_SODR, _BV(PC17_NANDOE_B));
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- NutDelay(WAIT250);
- /**************************************************/
- outr(PIOA_BSR, _BV(PA20_NCS2_B));
- outr(PIOA_PDR, _BV(PA20_NCS2_B));
- outr(PIOC_BSR, _BV(PC16_NWAIT_B) | _BV(PC21_NWR0_B) | _BV(PC22_NRD_B));
- outr(PIOC_PDR, _BV(PC16_NWAIT_B) | _BV(PC21_NWR0_B) | _BV(PC22_NRD_B));
- outr(SMC_CSR(2)
- , (1 << SMC_NWS_LSB)
- | SMC_WSEN
- | (2 << SMC_TDF_LSB)
- | SMC_BAT
- | SMC_DBW_16
- | (1 << SMC_RWSETUP_LSB)
- | (1 << SMC_RWHOLD_LSB)
- );
- #endif
- /* Probe chip by verifying the identifier registers. */
- id = (uint32_t) nic_inb(NIC_VID);
- id |= (uint32_t) nic_inb(NIC_VID + 1) << 8;
- id |= (uint32_t) nic_inb(NIC_PID) << 16;
- id |= (uint32_t) nic_inb(NIC_PID + 1) << 24;
- if (id != 0x90000A46) {
- return -1;
- }
- /* Register PHY */
- NutRegisterPhy( 1, phy_outw, phy_inw);
- /* Reset chip. */
- if (NicReset()) {
- return -1;
- }
- /* Clear NICINFO structure. */
- memset(ni, 0, sizeof(NICINFO));
- /* Determine bus mode. We do not support 32 bit access. */
- ni->ni_iomode = nic_inb(NIC_ISR) & NIC_ISR_IOM;
- if (ni->ni_iomode == NIC_ISR_M32) {
- return -1;
- }
- /* Register interrupt handler. */
- if (NutRegisterIrqHandler(&NIC_SIGNAL, NicInterrupt, dev)) {
- return -1;
- }
- /* Start the receiver thread. */
- if (NutThreadCreate("rxi1", NicRxLanc, dev,
- (NUT_THREAD_NICRXSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD) == NULL) {
- return -1;
- }
- return 0;
- }
- static int DmIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- int i;
- IFNET *nif = (IFNET *) dev->dev_icb;
- NICINFO *ni = (NICINFO *) dev->dev_dcb;
- uint32_t index;
- uint32_t ip_addr;
- MCASTENTRY *mcast;
- MCASTENTRY *mcast_prev;
- MCASTENTRY *mcast_next;
- uint8_t mac[6];
- switch (req) {
- /* Set interface address */
- case SIOCSIFADDR:
- /* Set interface hardware address. */
- memcpy(nif->if_mac, conf, sizeof(nif->if_mac));
- break;
- /* Add multicast address */
- case SIOCADDMULTI:
- ip_addr = *((uint32_t*)conf);
- /* Test if the address is still in the list */
- mcast = nif->if_mcast;
- while(mcast)
- {
- if (ip_addr == mcast->mca_ip)
- {
- /* The address is still in the list, do nothing */
- return -1;
- }
- mcast = mcast->mca_next;
- }
- /* Create MAC address */
- mac[0] = 0x01;
- mac[1] = 0x00;
- mac[2] = 0x5E;
- mac[3] = ((uint8_t*)conf)[1] & 0x7f;
- mac[4] = ((uint8_t*)conf)[2];
- mac[5] = ((uint8_t*)conf)[3];
- mcast = malloc(sizeof(MCASTENTRY));
- if (mcast != NULL) {
- /* Calculate MAR index, range 0...63 */
- index = ether_crc32_le(&mac[0], 6);
- index &= 0x3F;
- /* Set multicast bit */
- ni->ni_mar[index / 8] |= (1 << (index % 8));
- /* Add new mcast to the mcast list */
- memcpy(mcast->mca_ha, mac, 6);
- mcast->mca_ip = *((uint32_t*)conf);
- mcast->mca_next = nif->if_mcast;
- nif->if_mcast = mcast;
- /* Update the MC hardware */
- NicUpdateMCHardware(ni);
- }
- else {
- rc = -1;
- }
- break;
- /* Delete multicast address */
- case SIOCDELMULTI:
- ip_addr = *((uint32_t*)conf);
- /* Test if the address is still in the list */
- mcast = nif->if_mcast;
- mcast_prev = mcast;
- while(mcast)
- {
- if (ip_addr == mcast->mca_ip)
- {
- /* The address is in the list, leave the loop */
- break;
- }
- mcast_prev = mcast;
- mcast = mcast->mca_next;
- }
- if (NULL == mcast)
- {
- /* The address is not in the list */
- return -1;
- }
- /*
- * Remove the address from the list
- */
- mcast_next = mcast->mca_next;
- /* Check if the first element must be removed */
- if (nif->if_mcast == mcast)
- {
- /*
- * The element is the first one
- */
- /* The first element is now the "next" element */
- nif->if_mcast = mcast_next;
- free(mcast);
- } else {
- /*
- * The element is in the middle of the list
- */
- /* The next element of the previous is the "next" element */
- mcast_prev->mca_next = mcast_next;
- free(mcast);
- }
- /*
- * Clear the multicast filter, and rebuild it
- */
- /* Clear */
- for (i = 0; i < 7; i++) {
- ni->ni_mar[i] = 0;
- }
- /* Rebuild */
- mcast = nif->if_mcast;
- while(mcast)
- {
- /* Calculate MAR index, range 0...63 */
- index = ether_crc32_le(&mcast->mca_ha[0], 6);
- index &= 0x3F;
- /* Set multicast bit */
- ni->ni_mar[index / 8] |= (1 << (index % 8));
- mcast = mcast->mca_next;
- }
- /* Update the MC hardware */
- NicUpdateMCHardware(ni);
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- static NICINFO 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(). */
- DmOutput, /*!< \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 devDM9000E = {
- 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. */
- DmInit, /*!< \brief Driver initialization routine. */
- DmIOCtl, /*!< \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 */
- };
- #endif /* NIC_BASE_ADDR */
- /*@}*/
|