Nut/OS  4.10.3
API Reference
lanc111.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2003-2006 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00034 /*
00035  * $Log$
00036  * Revision 1.9  2009/02/06 15:37:39  haraldkipp
00037  * Added stack space multiplier and addend. Adjusted stack space.
00038  *
00039  * Revision 1.8  2008/08/28 11:12:15  haraldkipp
00040  * Added interface flags, which will be required to implement Ethernet ioctl
00041  * functions.
00042  *
00043  * Revision 1.7  2008/08/11 06:59:17  haraldkipp
00044  * BSD types replaced by stdint types (feature request #1282721).
00045  *
00046  * Revision 1.6  2007/05/02 11:22:51  haraldkipp
00047  * Added multicast table entry.
00048  *
00049  * Revision 1.5  2006/10/08 16:48:08  haraldkipp
00050  * Documentation fixed
00051  *
00052  * Revision 1.4  2006/06/28 14:30:19  haraldkipp
00053  * Post to the event queue on overflow interrupts.
00054  * Transmit event queue removed, because no one is listening.
00055  *
00056  * Revision 1.3  2005/10/24 18:02:34  haraldkipp
00057  * Fixes for ATmega103.
00058  *
00059  * Revision 1.2  2005/08/02 17:46:45  haraldkipp
00060  * Major API documentation update.
00061  *
00062  * Revision 1.1  2005/07/26 18:02:40  haraldkipp
00063  * Moved from dev.
00064  *
00065  * Revision 1.13  2005/04/30 16:42:41  chaac
00066  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00067  * is defined in NutConf, it will make effect where it is used.
00068  *
00069  * Revision 1.12  2005/02/02 19:55:34  haraldkipp
00070  * If no Ethernet link was available on the LAN91C111, each outgoing packet
00071  * took 15 seconds and, even worse, the ouput routine doesn't return an error.
00072  * Now the first attempt to send a packet without Ethernet link will wait for
00073  * 5 seconds and subsequent attempts take 0.5 seconds only, always returning
00074  * an error.
00075  *
00076  * Revision 1.11  2005/01/24 21:11:49  freckle
00077  * renamed NutEventPostFromIRQ into NutEventPostFromIrq
00078  *
00079  * Revision 1.10  2005/01/22 19:24:11  haraldkipp
00080  * Changed AVR port configuration names from PORTx to AVRPORTx.
00081  *
00082  * Revision 1.9  2005/01/21 16:49:45  freckle
00083  * Seperated calls to NutEventPostAsync between Threads and IRQs
00084  *
00085  * Revision 1.8  2004/09/22 08:14:48  haraldkipp
00086  * Made configurable
00087  *
00088  * Revision 1.7  2004/03/08 11:14:17  haraldkipp
00089  * Added quick hack for fixed mode.
00090  *
00091  * Revision 1.6  2004/02/25 16:22:33  haraldkipp
00092  * Do not initialize MAC with all zeros
00093  *
00094  * Revision 1.5  2004/01/14 19:31:43  drsung
00095  * Speed improvement to NicWrite applied. Thanks to Kolja Waschk
00096  *
00097  * Revision 1.4  2003/11/06 09:26:50  haraldkipp
00098  * Removed silly line with hardcoded MAC, left over from testing
00099  *
00100  * Revision 1.3  2003/11/04 17:54:47  haraldkipp
00101  * PHY configuration timing changed again for reliable linking
00102  *
00103  * Revision 1.2  2003/11/03 17:12:53  haraldkipp
00104  * Allow linking with RTL8019 driver.
00105  * Links more reliable to 10 MBit networks now.
00106  * Reset MMU on allocation failures.
00107  * Some optimizations.
00108  *
00109  * Revision 1.1  2003/10/13 10:13:49  haraldkipp
00110  * First release
00111  *
00112  */
00113 
00114 #include <cfg/os.h>
00115 #include <cfg/arch/avr.h>
00116 
00117 #include <sys/atom.h>
00118 #include <sys/heap.h>
00119 #include <sys/thread.h>
00120 #include <sys/event.h>
00121 #include <sys/timer.h>
00122 #include <sys/confnet.h>
00123 
00124 #include <netinet/if_ether.h>
00125 #include <net/ether.h>
00126 #include <net/if_var.h>
00127 
00128 #include <dev/irqreg.h>
00129 #include <dev/lanc111.h>
00130 
00131 #include <stdlib.h>
00132 #include <string.h>
00133 
00134 #ifdef NUTDEBUG
00135 #include <stdio.h>
00136 #endif
00137 
00138 #ifndef NUT_THREAD_LANCRXSTACK
00139 #if defined(__GNUC__)
00140 /* avr-gcc size optimized code used 76 bytes.
00141    Sigh! 144 bytes are reported using avr-gcc 4.3.3 and Nut/OS 4.9.10. */
00142 #define NUT_THREAD_LANCRXSTACK  256
00143 #else
00144 /* icc-avr v7.19 used 200 bytes. */
00145 #define NUT_THREAD_LANCRXSTACK  384
00146 #endif
00147 #endif
00148 
00149 /*
00150  * Determine ports, which had not been explicitely configured.
00151  */
00152 #ifndef LANC111_BASE_ADDR
00153 #define LANC111_BASE_ADDR   0xC000
00154 #endif
00155 
00156 #ifndef LANC111_SIGNAL_IRQ
00157 #define LANC111_SIGNAL_IRQ  INT5
00158 #endif
00159 
00160 #ifdef LANC111_RESET_BIT
00161 
00162 #if (LANC111_RESET_AVRPORT == AVRPORTB)
00163 #define LANC111_RESET_PORT   PORTB
00164 #define LANC111_RESET_DDR    DDRB
00165 
00166 #elif (LANC111_RESET_AVRPORT == AVRPORTD)
00167 #define LANC111_RESET_PORT   PORTD
00168 #define LANC111_RESET_DDR    DDRD
00169 
00170 #elif (LANC111_RESET_AVRPORT == AVRPORTE)
00171 #define LANC111_RESET_PORT   PORTE
00172 #define LANC111_RESET_DDR    DDRE
00173 
00174 #elif (LANC111_RESET_AVRPORT == AVRPORTF)
00175 #define LANC111_RESET_PORT   PORTF
00176 #define LANC111_RESET_DDR    DDRF
00177 
00178 #endif                          /* LANC111_RESET_AVRPORT */
00179 
00180 #endif                          /* LANC111_RESET_BIT */
00181 
00182 /*
00183  * Determine interrupt settings.
00184  */
00185 #if (LANC111_SIGNAL_IRQ == INT0)
00186 #define LANC111_SIGNAL          sig_INTERRUPT0
00187 #define LANC111_SIGNAL_MODE()   sbi(EICRA, ISC00); sbi(EICRA, ISC01)
00188 
00189 #elif (LANC111_SIGNAL_IRQ == INT1)
00190 #define LANC111_SIGNAL          sig_INTERRUPT1
00191 #define LANC111_SIGNAL_MODE()   sbi(EICRA, ISC10); sbi(EICRA, ISC11)
00192 
00193 #elif (LANC111_SIGNAL_IRQ == INT2)
00194 #define LANC111_SIGNAL          sig_INTERRUPT2
00195 #define LANC111_SIGNAL_MODE()   sbi(EICRA, ISC20); sbi(EICRA, ISC21)
00196 
00197 #elif (LANC111_SIGNAL_IRQ == INT3)
00198 #define LANC111_SIGNAL          sig_INTERRUPT3
00199 #define LANC111_SIGNAL_MODE()   sbi(EICRA, ISC30); sbi(EICRA, ISC31)
00200 
00201 #elif (LANC111_SIGNAL_IRQ == INT4)
00202 #define LANC111_SIGNAL          sig_INTERRUPT4
00203 #define LANC111_SIGNAL_MODE()   sbi(EICR, ISC40); sbi(EICR, ISC41)
00204 
00205 #elif (LANC111_SIGNAL_IRQ == INT6)
00206 #define LANC111_SIGNAL          sig_INTERRUPT6
00207 #define LANC111_SIGNAL_MODE()   sbi(EICR, ISC60); sbi(EICR, ISC61)
00208 
00209 #elif (LANC111_SIGNAL_IRQ == INT7)
00210 #define LANC111_SIGNAL          sig_INTERRUPT7
00211 #define LANC111_SIGNAL_MODE()   sbi(EICR, ISC70); sbi(EICR, ISC71)
00212 
00213 #else
00214 #define LANC111_SIGNAL          sig_INTERRUPT5
00215 #define LANC111_SIGNAL_MODE()   sbi(EICR, ISC50); sbi(EICR, ISC51)
00216 
00217 #endif
00218 
00223 
00227 #define NIC_BSR         (LANC111_BASE_ADDR + 0x0E)
00228 
00232 #define NIC_TCR         (LANC111_BASE_ADDR + 0x00)
00233 
00234 #define TCR_SWFDUP      0x8000  
00235 #define TCR_EPH_LOOP    0x2000  
00236 #define TCR_STP_SQET    0x1000  
00237 #define TCR_FDUPLX      0x0800  
00238 #define TCR_MON_CSN     0x0400  
00239 #define TCR_NOCRC       0x0100  
00240 #define TCR_PAD_EN      0x0080  
00241 #define TCR_FORCOL      0x0004  
00242 #define TCR_LOOP        0x0002  
00243 #define TCR_TXENA       0x0001  
00249 #define NIC_EPHSR       (LANC111_BASE_ADDR + 0x02)
00250 
00254 #define NIC_RCR         (LANC111_BASE_ADDR + 0x04)
00255 
00256 #define RCR_SOFT_RST    0x8000  
00257 #define RCR_FILT_CAR    0x4000  
00258 #define RCR_ABORT_ENB   0x2000  
00259 #define RCR_STRIP_CRC   0x0200  
00260 #define RCR_RXEN        0x0100  
00261 #define RCR_ALMUL       0x0004  
00262 #define RCR_PRMS        0x0002  
00263 #define RCR_RX_ABORT    0x0001  
00268 #define NIC_ECR         (LANC111_BASE_ADDR + 0x06)
00269 
00273 #define NIC_MIR         (LANC111_BASE_ADDR + 0x08)
00274 
00278 #define NIC_RPCR        (LANC111_BASE_ADDR + 0x0A)
00279 
00280 #define RPCR_SPEED      0x2000  
00281 #define RPCR_DPLX       0x1000  
00282 #define RPCR_ANEG       0x0800  
00283 #define RPCR_LEDA_PAT   0x0000  
00284 #define RPCR_LEDB_PAT   0x0010  
00289 #define NIC_CR          (LANC111_BASE_ADDR + 0x00)
00290 
00291 #define CR_EPH_EN       0x8000  
00296 #define NIC_BAR         (LANC111_BASE_ADDR + 0x02)
00297 
00301 #define NIC_IAR         (LANC111_BASE_ADDR + 0x04)
00302 
00306 #define NIC_GPR         (LANC111_BASE_ADDR + 0x0A)
00307 
00311 #define NIC_CTR         (LANC111_BASE_ADDR + 0x0C)
00312 
00313 #define CTR_RCV_BAD     0x4000  
00314 #define CTR_AUTO_RELEASE 0x0800 
00319 #define NIC_MMUCR       (LANC111_BASE_ADDR + 0x00)
00320 
00321 #define MMUCR_BUSY      0x0001
00322 
00323 #define MMU_NOP         0
00324 #define MMU_ALO         (1<<5)
00325 #define MMU_RST         (2<<5)
00326 #define MMU_REM         (3<<5)
00327 #define MMU_TOP         (4<<5)
00328 #define MMU_PKT         (5<<5)
00329 #define MMU_ENQ         (6<<5)
00330 #define MMU_RTX         (7<<5)
00331 
00337 #define NIC_PNR         (LANC111_BASE_ADDR + 0x02)
00338 
00344 #define NIC_ARR         (LANC111_BASE_ADDR + 0x03)
00345 
00346 #define ARR_FAILED      0x80
00347 
00351 #define NIC_FIFO        (LANC111_BASE_ADDR + 0x04)
00352 
00356 #define NIC_PTR         (LANC111_BASE_ADDR + 0x06)
00357 
00358 #define PTR_RCV         0x8000  
00359 #define PTR_AUTO_INCR   0x4000  
00360 #define PTR_READ        0x2000  
00361 #define PTR_ETEN        0x1000  
00362 #define PTR_NOT_EMPTY   0x0800  
00367 #define NIC_DATA        (LANC111_BASE_ADDR + 0x08)
00368 
00372 #define NIC_IST         (LANC111_BASE_ADDR + 0x0C)
00373 
00377 #define NIC_ACK         (LANC111_BASE_ADDR + 0x0C)
00378 
00382 #define NIC_MSK         (LANC111_BASE_ADDR + 0x0D)
00383 
00384 #define INT_MD          0x80    
00385 #define INT_ERCV        0x40    
00386 #define INT_EPH         0x20    
00387 #define INT_RX_OVRN     0x10    
00388 #define INT_ALLOC       0x08    
00389 #define INT_TX_EMPTY    0x04    
00390 #define INT_TX          0x02    
00391 #define INT_RCV         0x01    
00396 #define NIC_MT          (LANC111_BASE_ADDR + 0x00)
00397 
00401 #define NIC_MGMT        (LANC111_BASE_ADDR + 0x08)
00402 
00403 #define MGMT_MDOE       0x08    
00404 #define MGMT_MCLK       0x04    
00405 #define MGMT_MDI        0x02    
00406 #define MGMT_MDO        0x01    
00411 #define NIC_REV         (LANC111_BASE_ADDR + 0x0A)
00412 
00416 #define NIC_ERCV        (LANC111_BASE_ADDR + 0x0C)
00417 
00421 #define NIC_PHYCR       0
00422 
00423 #define PHYCR_RST       0x8000  
00424 #define PHYCR_LPBK      0x4000  
00425 #define PHYCR_SPEED     0x2000  
00426 #define PHYCR_ANEG_EN   0x1000  
00427 #define PHYCR_PDN       0x0800  
00428 #define PHYCR_MII_DIS   0x0400  
00429 #define PHYCR_ANEG_RST  0x0200  
00430 #define PHYCR_DPLX      0x0100  
00431 #define PHYCR_COLST     0x0080  
00437 #define NIC_PHYSR       1
00438 
00439 #define PHYSR_CAP_T4    0x8000  
00440 #define PHYSR_CAP_TXF   0x4000  
00441 #define PHYSR_CAP_TXH   0x2000  
00442 #define PHYSR_CAP_TF    0x1000  
00443 #define PHYSR_CAP_TH    0x0800  
00444 #define PHYSR_CAP_SUPR  0x0040  
00445 #define PHYSR_ANEG_ACK  0x0020  
00446 #define PHYSR_REM_FLT   0x0010  
00447 #define PHYSR_CAP_ANEG  0x0008  
00448 #define PHYSR_LINK      0x0004  
00449 #define PHYSR_JAB       0x0002  
00450 #define PHYSR_EXREG     0x0001  
00456 #define NIC_PHYID1      2
00457 
00461 #define NIC_PHYID2      3
00462 
00466 #define NIC_PHYANAD     4
00467 
00468 #define PHYANAD_NP      0x8000  
00469 #define PHYANAD_ACK     0x4000  
00470 #define PHYANAD_RF      0x2000  
00471 #define PHYANAD_T4      0x0200  
00472 #define PHYANAD_TX_FDX  0x0100  
00473 #define PHYANAD_TX_HDX  0x0080  
00474 #define PHYANAD_10FDX   0x0040  
00475 #define PHYANAD_10_HDX  0x0020  
00476 #define PHYANAD_CSMA    0x0001  
00481 #define NIC_PHYANRC     5
00482 
00486 #define NIC_PHYCFR1     16
00487 
00491 #define NIC_PHYCFR2     17
00492 
00496 #define NIC_PHYSOR      18
00497 
00498 #define PHYSOR_INT      0x8000  
00499 #define PHYSOR_LNKFAIL  0x4000  
00500 #define PHYSOR_LOSSSYNC 0x2000  
00501 #define PHYSOR_CWRD     0x1000  
00502 #define PHYSOR_SSD      0x0800  
00503 #define PHYSOR_ESD      0x0400  
00504 #define PHYSOR_RPOL     0x0200  
00505 #define PHYSOR_JAB      0x0100  
00506 #define PHYSOR_SPDDET   0x0080  
00507 #define PHYSOR_DPLXDET  0x0040  
00512 #define NIC_PHYMSK      19
00513 
00514 #define PHYMSK_MINT     0x8000  
00515 #define PHYMSK_MLNKFAIL 0x4000  
00516 #define PHYMSK_MLOSSSYN 0x2000  
00517 #define PHYMSK_MCWRD    0x1000  
00518 #define PHYMSK_MSSD     0x0800  
00519 #define PHYMSK_MESD     0x0400  
00520 #define PHYMSK_MRPOL    0x0200  
00521 #define PHYMSK_MJAB     0x0100  
00522 #define PHYMSK_MSPDDT   0x0080  
00523 #define PHYMSK_MDPLDT   0x0040  
00527 #define MSBV(bit)       (1 << ((bit) - 8))
00528 
00529 #define nic_outlb(addr, val) (*(volatile uint8_t *)(addr) = (val))
00530 #define nic_outhb(addr, val) (*(volatile uint8_t *)((addr) + 1) = (val))
00531 #define nic_outwx(addr, val) (*(volatile uint16_t *)(addr) = (val))
00532 #define nic_outw(addr, val) { \
00533     *(volatile uint8_t *)(addr) = (uint8_t)(val); \
00534     *((volatile uint8_t *)(addr) + 1) = (uint8_t)((val) >> 8); \
00535 }
00536 
00537 #define nic_inlb(addr) (*(volatile uint8_t *)(addr))
00538 #define nic_inhb(addr) (*(volatile uint8_t *)((addr) + 1))
00539 #define nic_inw(addr) (*(volatile uint16_t *)(addr))
00540 
00541 #define nic_bs(bank)    nic_outlb(NIC_BSR, bank)
00542 
00547 struct _NICINFO {
00548     HANDLE volatile ni_rx_rdy;    
00549     uint16_t ni_tx_cnt;           
00550     uint32_t ni_rx_packets;       
00551     uint32_t ni_tx_packets;       
00552     uint32_t ni_interrupts;       
00553     uint32_t ni_overruns;         
00554     uint32_t ni_rx_frame_errors;  
00555     uint32_t ni_rx_crc_errors;    
00556     uint32_t ni_rx_missed_errors; 
00557     uint8_t ni_mar[8];            
00558 };
00559 
00563 typedef struct _NICINFO NICINFO;
00564 
00571 
00572 
00573 static HANDLE mutex;
00574 static HANDLE maq;
00575 
00586 static uint8_t NicPhyRegSelect(uint8_t reg, uint8_t we)
00587 {
00588     uint8_t rs;
00589     uint8_t msk;
00590     uint8_t i;
00591 
00592     nic_bs(3);
00593     rs = (nic_inlb(NIC_MGMT) & ~(MGMT_MCLK | MGMT_MDO)) | MGMT_MDOE;
00594 
00595     /* Send idle pattern. */
00596     for (i = 0; i < 33; i++) {
00597         nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00598         nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00599     }
00600 
00601     /* Send start sequence. */
00602     nic_outlb(NIC_MGMT, rs);
00603     nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00604     nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00605     nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00606 
00607     /* Write or read mode. */
00608     if (we) {
00609         nic_outlb(NIC_MGMT, rs);
00610         nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00611         nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00612         nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00613     } else {
00614         nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00615         nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00616         nic_outlb(NIC_MGMT, rs);
00617         nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00618     }
00619 
00620     /* Send PHY address. Zero is used for the internal PHY. */
00621     for (i = 0; i < 5; i++) {
00622         nic_outlb(NIC_MGMT, rs);
00623         nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00624     }
00625 
00626     /* Send PHY register number. */
00627     for (msk = 0x10; msk; msk >>= 1) {
00628         if (reg & msk) {
00629             nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00630             nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00631         } else {
00632             nic_outlb(NIC_MGMT, rs);
00633             nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00634         }
00635     }
00636     nic_outlb(NIC_MGMT, rs);
00637 
00638     return rs;
00639 }
00640 
00650 static uint16_t NicPhyRead(uint8_t reg)
00651 {
00652     uint16_t rc = 0;
00653     uint8_t rs;
00654     uint8_t i;
00655 
00656     /* Select register for reading. */
00657     rs = NicPhyRegSelect(reg, 0);
00658 
00659     /* Switch data direction. */
00660     rs &= ~MGMT_MDOE;
00661     nic_outlb(NIC_MGMT, rs);
00662     nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00663 
00664     /* Clock data in. */
00665     for (i = 0; i < 16; i++) {
00666         nic_outlb(NIC_MGMT, rs);
00667         nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00668         rc <<= 1;
00669         rc |= (nic_inlb(NIC_MGMT) & MGMT_MDI) != 0;
00670     }
00671 
00672     /* This will set the clock line to low. */
00673     nic_outlb(NIC_MGMT, rs);
00674 
00675     return rc;
00676 }
00677 
00686 static void NicPhyWrite(uint8_t reg, uint16_t val)
00687 {
00688     uint16_t msk;
00689     uint8_t rs;
00690 
00691     /* Select register for writing. */
00692     rs = NicPhyRegSelect(reg, 1);
00693 
00694     /* Switch data direction dummy. */
00695     nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00696     nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00697     nic_outlb(NIC_MGMT, rs);
00698     nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00699 
00700     /* Clock data out. */
00701     for (msk = 0x8000; msk; msk >>= 1) {
00702         if (val & msk) {
00703             nic_outlb(NIC_MGMT, rs | MGMT_MDO);
00704             nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK);
00705         } else {
00706             nic_outlb(NIC_MGMT, rs);
00707             nic_outlb(NIC_MGMT, rs | MGMT_MCLK);
00708         }
00709     }
00710 
00711     /* Set clock line low and output line int z-state. */
00712     nic_outlb(NIC_MGMT, rs & ~MGMT_MDOE);
00713 }
00714 
00720 static int NicPhyConfig(void)
00721 {
00722     uint16_t phy_sor;
00723     uint16_t phy_sr;
00724     uint16_t phy_to;
00725     uint16_t mode;
00726 
00727     /* 
00728      * Reset the PHY and wait until this self clearing bit
00729      * becomes zero. We sleep 63 ms before each poll and
00730      * give up after 3 retries. 
00731      */
00732     //printf("Reset PHY..");
00733     NicPhyWrite(NIC_PHYCR, PHYCR_RST);
00734     for (phy_to = 0;; phy_to++) {
00735         NutSleep(63);
00736         if ((NicPhyRead(NIC_PHYCR) & PHYCR_RST) == 0)
00737             break;
00738         if (phy_to > 3)
00739             return -1;
00740     }
00741     //printf("OK\n");
00742 
00743     /* Store PHY status output. */
00744     phy_sor = NicPhyRead(NIC_PHYSOR);
00745 
00746     /* Enable PHY interrupts. */
00747     NicPhyWrite(NIC_PHYMSK, PHYMSK_MLOSSSYN | PHYMSK_MCWRD | PHYMSK_MSSD |
00748                 PHYMSK_MESD | PHYMSK_MRPOL | PHYMSK_MJAB | PHYMSK_MSPDDT | PHYMSK_MDPLDT);
00749 
00750     /* Set RPC register. */
00751     mode = RPCR_ANEG | RPCR_LEDA_PAT | RPCR_LEDB_PAT;
00752     nic_bs(0);
00753     nic_outw(NIC_RPCR, mode);
00754 
00755 #ifdef NIC_FIXED
00756     /* Disable link. */
00757     phy_sr = NicPhyRead(NIC_PHYCFR1);
00758     NicPhyWrite(NIC_PHYCFR1, phy_sr | 0x8000);
00759     NutSleep(63);
00760 
00761     /* Set fixed capabilities. */
00762     NicPhyWrite(NIC_PHYCR, NIC_FIXED);
00763     nic_bs(0);
00764     nic_outw(NIC_RPCR, mode);
00765 
00766     /* Enable link. */
00767     phy_sr = NicPhyRead(NIC_PHYCFR1);
00768     NicPhyWrite(NIC_PHYCFR1, phy_sr & ~0x8000);
00769     phy_sr = NicPhyRead(NIC_PHYCFR1);
00770 
00771 #else
00772     /*
00773      * Advertise our capabilities, initiate auto negotiation
00774      * and wait until this has been completed.
00775      */
00776     //printf("Negotiate..");
00777     NicPhyWrite(NIC_PHYANAD, PHYANAD_TX_FDX | PHYANAD_TX_HDX | PHYANAD_10FDX | PHYANAD_10_HDX | PHYANAD_CSMA);
00778     NutSleep(63);
00779     for (phy_to = 0, phy_sr = 0;; phy_to++) {
00780         /* Give up after 10 seconds. */
00781         if (phy_to >= 1024)
00782             return -1;
00783         /* Restart auto negotiation every 4 seconds or on failures. */
00784         if ((phy_to & 127) == 0 /* || (phy_sr & PHYSR_REM_FLT) != 0 */ ) {
00785             NicPhyWrite(NIC_PHYCR, PHYCR_ANEG_EN | PHYCR_ANEG_RST);
00786             //printf("Restart..");
00787             NutSleep(63);
00788         }
00789         /* Check if we are done. */
00790         phy_sr = NicPhyRead(NIC_PHYSR);
00791         //printf("[SR %04X]", phy_sr);
00792         if (phy_sr & PHYSR_ANEG_ACK)
00793             break;
00794         NutSleep(63);
00795     }
00796     //printf("OK\n");
00797 #endif
00798 
00799     return 0;
00800 }
00801 
00812 static INLINE int NicMmuWait(uint16_t tmo)
00813 {
00814     while (tmo--) {
00815         if ((nic_inlb(NIC_MMUCR) & MMUCR_BUSY) == 0)
00816             break;
00817         NutDelay(1);
00818     }
00819     return tmo ? 0 : -1;
00820 }
00821 
00827 static int NicReset(void)
00828 {
00829 #ifdef LANC111_RESET_BIT
00830     sbi(LANC111_RESET_DDR, LANC111_RESET_BIT);
00831     sbi(LANC111_RESET_PORT, LANC111_RESET_BIT);
00832     NutDelay(WAIT100);
00833     cbi(LANC111_RESET_PORT, LANC111_RESET_BIT);
00834     NutDelay(WAIT250);
00835     NutDelay(WAIT250);
00836 #endif
00837 
00838     /* Disable all interrupts. */
00839     nic_outlb(NIC_MSK, 0);
00840 
00841     /* MAC and PHY software reset. */
00842     nic_bs(0);
00843     nic_outw(NIC_RCR, RCR_SOFT_RST);
00844 
00845     /* Enable Ethernet protocol handler. */
00846     nic_bs(1);
00847     nic_outw(NIC_CR, CR_EPH_EN);
00848 
00849     NutDelay(10);
00850 
00851     /* Disable transmit and receive. */
00852     nic_bs(0);
00853     nic_outw(NIC_RCR, 0);
00854     nic_outw(NIC_TCR, 0);
00855 
00856     /* Enable auto release. */
00857     nic_bs(1);
00858     nic_outw(NIC_CTR, CTR_AUTO_RELEASE);
00859 
00860     /* Reset MMU. */
00861     nic_bs(2);
00862     nic_outlb(NIC_MMUCR, MMU_RST);
00863     if (NicMmuWait(1000))
00864         return -1;
00865 
00866     return 0;
00867 }
00868 
00874 static void NicUpdateMCHardware(NICINFO * ni)
00875 {
00876     int i;
00877 
00878     /* Set multicast address register */
00879     nic_bs(3);
00880     for (i = 0; i < 7; i++) {
00881         nic_outlb(NIC_MT + i, ni->ni_mar[i]);
00882     }
00883 }
00884 
00885 /*
00886  * Fires up the network interface. NIC interrupts
00887  * should have been disabled when calling this
00888  * function.
00889  *
00890  * \param mac Six byte unique MAC address.
00891  */
00892 static int NicStart(CONST uint8_t * mac, NICINFO * ni)
00893 {
00894     uint8_t i;
00895 
00896     if (NicReset())
00897         return -1;
00898 
00899     /* Enable receiver. */
00900     nic_bs(3);
00901     nic_outlb(NIC_ERCV, 7);
00902     nic_bs(0);
00903     nic_outw(NIC_RCR, RCR_RXEN);
00904 
00905     /* Enable transmitter and padding. */
00906     nic_outw(NIC_TCR, TCR_PAD_EN | TCR_TXENA);
00907 
00908     /* Configure the PHY. */
00909     if (NicPhyConfig())
00910         return -1;
00911 
00912     /* Set MAC address. */
00913     //printf("Set MAC %02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
00914     nic_bs(1);
00915     for (i = 0; i < 6; i++)
00916         nic_outlb(NIC_IAR + i, mac[i]);
00917     //printf("OK\n");
00918 
00919     /* Set multicast address register */
00920     NicUpdateMCHardware(ni);
00921 
00922     /* Enable interrupts. */
00923     nic_bs(2);
00924     nic_outlb(NIC_MSK, INT_ERCV | INT_RCV | INT_RX_OVRN);
00925 
00926     return 0;
00927 }
00928 
00929 /*
00930  * NIC interrupt entry.
00931  */
00932 static void NicInterrupt(void *arg)
00933 {
00934     uint8_t isr;
00935     uint8_t imr;
00936     NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb;
00937 
00938     ni->ni_interrupts++;
00939 
00940     /* Read the interrupt mask and disable all interrupts. */
00941     nic_bs(2);
00942     imr = nic_inlb(NIC_MSK);
00943     nic_outlb(NIC_MSK, 0);
00944 
00945     /* Read the interrupt status and acknowledge all interrupts. */
00946     isr = nic_inlb(NIC_IST);
00947     //printf("\n!%02X-%02X ", isr, imr);
00948     isr &= imr;
00949 
00950     /*
00951      * If this is a transmit interrupt, then a packet has been sent. 
00952      * So we can clear the transmitter busy flag and wake up the 
00953      * transmitter thread.
00954      */
00955     if (isr & INT_TX_EMPTY) {
00956         nic_outlb(NIC_ACK, INT_TX_EMPTY);
00957         imr &= ~INT_TX_EMPTY;
00958     }
00959     /* Transmit error. */
00960     else if (isr & INT_TX) {
00961         /* re-enable transmit */
00962         nic_bs(0);
00963         nic_outw(NIC_TCR, nic_inlb(NIC_TCR) | TCR_TXENA);
00964         nic_bs(2);
00965         nic_outlb(NIC_ACK, INT_TX);
00966         /* kill the packet */
00967         nic_outlb(NIC_MMUCR, MMU_PKT);
00968     }
00969 
00970 
00971     /*
00972      * If this is a receive interrupt, then wake up the receiver 
00973      * thread.
00974      */
00975     if (isr & INT_RX_OVRN) {
00976         nic_outlb(NIC_ACK, INT_RX_OVRN);
00977         //nic_outlb(NIC_MMUCR, MMU_TOP);
00978         NutEventPostFromIrq(&ni->ni_rx_rdy);
00979     }
00980     if (isr & INT_ERCV) {
00981         nic_outlb(NIC_ACK, INT_ERCV);
00982         NutEventPostFromIrq(&ni->ni_rx_rdy);
00983     }
00984     if (isr & INT_RCV) {
00985         nic_outlb(NIC_ACK, INT_RCV);
00986         imr &= ~INT_RCV;
00987         NutEventPostFromIrq(&ni->ni_rx_rdy);
00988     }
00989 
00990     if (isr & INT_ALLOC) {
00991         imr &= ~INT_ALLOC;
00992         NutEventPostFromIrq(&maq);
00993     }
00994     //printf(" -%02X-%02X- ", nic_inlb(NIC_IST), inb(PINE) & 0x20);
00995     nic_outlb(NIC_MSK, imr);
00996 }
00997 
00998 /*
00999  * Write data block to the NIC.
01000  */
01001 static void NicWrite(uint8_t * buf, uint16_t len)
01002 {
01003     register uint16_t l = len - 1;
01004     register uint8_t ih = (uint16_t) l >> 8;
01005     register uint8_t il = (uint8_t) l;
01006 
01007     if (!len)
01008         return;
01009 
01010     do {
01011         do {
01012             nic_outlb(NIC_DATA, *buf++);
01013         } while (il-- != 0);
01014     } while (ih-- != 0);
01015 }
01016 
01017 /*
01018  * Read data block from the NIC.
01019  */
01020 static void NicRead(uint8_t * buf, uint16_t len)
01021 {
01022     register uint16_t l = len - 1;
01023     register uint8_t ih = (uint16_t) l >> 8;
01024     register uint8_t il = (uint8_t) l;
01025 
01026     if (!len)
01027         return;
01028 
01029     do {
01030         do {
01031             *buf++ = nic_inlb(NIC_DATA);
01032         } while (il-- != 0);
01033     } while (ih-- != 0);
01034 }
01035 
01046 static NETBUF *NicGetPacket(void)
01047 {
01048     NETBUF *nb = 0;
01049     //uint8_t *buf;
01050     uint16_t fsw;
01051     uint16_t fbc;
01052 
01053     /* Check the fifo empty bit. If it is set, then there is 
01054        nothing in the receiver fifo. */
01055     nic_bs(2);
01056     if (nic_inw(NIC_FIFO) & 0x8000) {
01057         return 0;
01058     }
01059 
01060     /* Inialize pointer register. */
01061     nic_outw(NIC_PTR, PTR_READ | PTR_RCV | PTR_AUTO_INCR);
01062     _NOP();
01063     _NOP();
01064     _NOP();
01065     _NOP();
01066 
01067     /* Read status word and byte count. */
01068     fsw = nic_inw(NIC_DATA);
01069     fbc = nic_inw(NIC_DATA);
01070     //printf("[SW=%04X,BC=%04X]", fsw, fbc);
01071 
01072     /* Check for frame errors. */
01073     if (fsw & 0xAC00) {
01074         nb = (NETBUF *) 0xFFFF;
01075     }
01076     /* Check the byte count. */
01077     else if (fbc < 66 || fbc > 1524) {
01078         nb = (NETBUF *) 0xFFFF;
01079     }
01080 
01081     else {
01082         /* 
01083          * Allocate a NETBUF. 
01084          * Hack alert: Rev A chips never set the odd frame indicator.
01085          */
01086         fbc -= 3;
01087         nb = NutNetBufAlloc(0, NBAF_DATALINK, fbc);
01088 
01089         /* Perform the read. */
01090         if (nb)
01091             NicRead(nb->nb_dl.vp, fbc);
01092     }
01093 
01094     /* Release the packet. */
01095     nic_outlb(NIC_MMUCR, MMU_TOP);
01096 
01097     return nb;
01098 }
01099 
01114 static int NicPutPacket(NETBUF * nb)
01115 {
01116     uint16_t sz;
01117     uint8_t odd = 0;
01118     uint8_t imsk;
01119 
01120     //printf("[P]");
01121     /*
01122      * Calculate the number of bytes to be send. Do not send packets 
01123      * larger than the Ethernet maximum transfer unit. The MTU
01124      * consist of 1500 data bytes plus the 14 byte Ethernet header
01125      * plus 4 bytes CRC. We check the data bytes only.
01126      */
01127     if ((sz = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz) > ETHERMTU)
01128         return -1;
01129 
01130     /* Disable all interrupts. */
01131     imsk = nic_inlb(NIC_MSK);
01132     nic_outlb(NIC_MSK, 0);
01133 
01134     /* Allocate packet buffer space. */
01135     nic_bs(2);
01136     nic_outlb(NIC_MMUCR, MMU_ALO);
01137     if (NicMmuWait(100))
01138         return -1;
01139 
01140     /* Enable interrupts including allocation success. */
01141     nic_outlb(NIC_MSK, imsk | INT_ALLOC);
01142 
01143     /* The MMU needs some time. Use it to calculate the byte count. */
01144     sz += nb->nb_dl.sz;
01145     sz += 6;
01146     if (sz & 1) {
01147         sz++;
01148         odd++;
01149     }
01150 
01151     /* Wait for allocation success. */
01152     while ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) {
01153         if (NutEventWait(&maq, 125)) {
01154             nic_outlb(NIC_MMUCR, MMU_RST);
01155             NicMmuWait(1000);
01156             nic_outlb(NIC_MMUCR, MMU_ALO);
01157             if (NicMmuWait(100) || (nic_inlb(NIC_IST) & INT_ALLOC) == 0) {
01158                 if (NutEventWait(&maq, 125)) {
01159                     return -1;
01160                 }
01161             }
01162         }
01163     }
01164 
01165     /* Disable interrupts. */
01166     imsk = nic_inlb(NIC_MSK);
01167     nic_outlb(NIC_MSK, 0);
01168 
01169 
01170     nic_outlb(NIC_PNR, nic_inhb(NIC_PNR));
01171 
01172     nic_outw(NIC_PTR, 0x4000);
01173 
01174     /* Transfer control word. */
01175     nic_outlb(NIC_DATA, 0);
01176     nic_outlb(NIC_DATA, 0);
01177 
01178     /* Transfer the byte count. */
01179     nic_outw(NIC_DATA, sz);
01180 
01181     /* Transfer the Ethernet frame. */
01182     NicWrite(nb->nb_dl.vp, nb->nb_dl.sz);
01183     NicWrite(nb->nb_nw.vp, nb->nb_nw.sz);
01184     NicWrite(nb->nb_tp.vp, nb->nb_tp.sz);
01185     NicWrite(nb->nb_ap.vp, nb->nb_ap.sz);
01186 
01187     if (odd)
01188         nic_outlb(NIC_DATA, 0);
01189 
01190     /* Transfer the control word. */
01191     nic_outw(NIC_DATA, 0);
01192 
01193     /* Enqueue packet. */
01194     if (NicMmuWait(100))
01195         return -1;
01196     nic_outlb(NIC_MMUCR, MMU_ENQ);
01197 
01198     /* Enable interrupts. */
01199     imsk |= INT_TX | INT_TX_EMPTY;
01200     nic_outlb(NIC_MSK, imsk);
01201 
01202     return 0;
01203 }
01204 
01205 
01210 THREAD(NicRxLanc, arg)
01211 {
01212     NUTDEVICE *dev;
01213     IFNET *ifn;
01214     NICINFO *ni;
01215     NETBUF *nb;
01216     uint8_t imsk;
01217 
01218     dev = arg;
01219     ifn = (IFNET *) dev->dev_icb;
01220     ni = (NICINFO *) dev->dev_dcb;
01221 
01222     /*
01223      * This is a temporary hack. Due to a change in initialization,
01224      * we may not have got a MAC address yet. Wait until a valid one
01225      * has been set.
01226      */
01227     while (!ETHER_IS_UNICAST(ifn->if_mac)) {
01228         NutSleep(10);
01229     }
01230 
01231     /*
01232      * Do not continue unless we managed to start the NIC. We are
01233      * trapped here if the Ethernet link cannot be established.
01234      * This happens, for example, if no Ethernet cable is plugged
01235      * in.
01236      */
01237     while (NicStart(ifn->if_mac, ni)) {
01238         NutSleep(1000);
01239     }
01240 
01241     LANC111_SIGNAL_MODE();
01242     sbi(EIMSK, LANC111_SIGNAL_IRQ);
01243 
01244     NutEventPost(&mutex);
01245 
01246     /* Run at high priority. */
01247     NutThreadSetPriority(9);
01248 
01249     for (;;) {
01250 
01251         /*
01252          * Wait for the arrival of new packets or
01253          * check the receiver every two second.
01254          */
01255         NutEventWait(&ni->ni_rx_rdy, 2000);
01256 
01257         /*
01258          * Fetch all packets from the NIC's internal
01259          * buffer and pass them to the registered handler.
01260          */
01261         imsk = nic_inlb(NIC_MSK);
01262         nic_outlb(NIC_MSK, 0);
01263         while ((nb = NicGetPacket()) != 0) {
01264             if (nb != (NETBUF *) 0xFFFF) {
01265                 ni->ni_rx_packets++;
01266                 (*ifn->if_recv) (dev, nb);
01267             }
01268         }
01269         nic_outlb(NIC_MSK, imsk | INT_RCV | INT_ERCV);
01270     }
01271 }
01272 
01283 int LancOutput(NUTDEVICE * dev, NETBUF * nb)
01284 {
01285     static uint32_t mx_wait = 5000;
01286     int rc = -1;
01287     NICINFO *ni;
01288 
01289     /*
01290      * After initialization we are waiting for a long time to give
01291      * the PHY a chance to establish an Ethernet link.
01292      */
01293     if (NutEventWait(&mutex, mx_wait) == 0) {
01294         ni = (NICINFO *) dev->dev_dcb;
01295 
01296         if (NicPutPacket(nb) == 0) {
01297             ni->ni_tx_packets++;
01298             rc = 0;
01299             /* Ethernet works. Set a long waiting time in case we
01300                temporarly lose the link next time. */
01301             mx_wait = 5000;
01302         }
01303         NutEventPost(&mutex);
01304     }
01305     /*
01306      * Probably no Ethernet link. Significantly reduce the waiting
01307      * time, so following transmission will soon return an error.
01308      */
01309     else {
01310         mx_wait = 500;
01311     }
01312     return rc;
01313 }
01314 
01332 int LancInit(NUTDEVICE * dev)
01333 {
01334     /* Disable NIC interrupt and clear NICINFO structure. */
01335     cbi(EIMSK, LANC111_SIGNAL_IRQ);
01336     memset(dev->dev_dcb, 0, sizeof(NICINFO));
01337 
01338     /* Register interrupt handler and enable interrupts. */
01339     if (NutRegisterIrqHandler(&LANC111_SIGNAL, NicInterrupt, dev))
01340         return -1;
01341 
01342     /*
01343      * Start the receiver thread.
01344      *
01345      * avr-gcc size optimized code used 76 bytes.
01346      */
01347     NutThreadCreate("rxi5", NicRxLanc, dev, (NUT_THREAD_LANCRXSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
01348 
01349     //NutSleep(500);
01350 
01351     return 0;
01352 }
01353 
01354 static int LancIOCtl(NUTDEVICE * dev, int req, void *conf)
01355 {
01356     int rc = 0;
01357     IFNET *nif = (IFNET *) dev->dev_icb;
01358     NICINFO *ni = (NICINFO *) dev->dev_dcb;
01359     uint32_t i;
01360     MCASTENTRY *mcast;
01361 
01362     uint8_t mac[6];
01363 
01364     switch (req) {
01365         /* Set interface address */
01366     case SIOCSIFADDR:
01367         /* Set interface hardware address. */
01368         memcpy(nif->if_mac, conf, sizeof(nif->if_mac));
01369         break;
01370 
01371         /* Add multicast address */
01372     case SIOCADDMULTI:
01373         mac[0] = 0x01;
01374         mac[1] = 0x00;
01375         mac[2] = 0x5E;
01376         mac[3] = ((uint8_t *) conf)[1] & 0x7f;
01377         mac[4] = ((uint8_t *) conf)[2];
01378         mac[5] = ((uint8_t *) conf)[3];
01379 
01380         mcast = malloc(sizeof(MCASTENTRY));
01381         if (mcast != NULL) {
01382             /* 
01383              * HACK ALERT (MF):
01384              * I do not know the correct algorithm. The algorithm
01385              * which works for the dm9000 does not work here.
01386              * Therefore set all bits to 1.
01387              */
01388             for (i = 0; i < 7; i++) {
01389                 ni->ni_mar[i] = 0xFF;
01390             }
01391 
01392             /* Add new mcast to the mcast list */
01393             memcpy(mcast->mca_ha, mac, 6);
01394             mcast->mca_ip = *((uint32_t *) conf);
01395             mcast->mca_next = nif->if_mcast;
01396             nif->if_mcast = mcast;
01397 
01398             /* Update the MC hardware */
01399             NicUpdateMCHardware(ni);
01400         } else {
01401             rc = -1;
01402         }
01403         break;
01404 
01405         /* Delete multicast address */
01406     case SIOCDELMULTI:
01407         /* Will be implemented later */
01408         rc = -1;
01409         break;
01410 
01411     default:
01412         rc = -1;
01413         break;
01414     }
01415 
01416     return rc;
01417 }
01418 
01419 static NICINFO dcb_eth0;
01420 
01426 static IFNET ifn_eth0 = {
01427     IFT_ETHER,                  
01428     0,                          
01429     {0, 0, 0, 0, 0, 0},         
01430     0,                          
01431     0,                          
01432     0,                          
01433     ETHERMTU,                   
01434     0,                          
01435     0,                          
01436     0,                          
01437     NutEtherInput,              
01438     LancOutput,                 
01439     NutEtherOutput,             
01440     0                           
01441 };
01442 
01452 NUTDEVICE devSmsc111 = {
01453     0,                          /* Pointer to next device. */
01454     {'e', 't', 'h', '0', 0, 0, 0, 0, 0},        /* Unique device name. */
01455     IFTYP_NET,                  /* Type of device. */
01456     0,                          /* Base address. */
01457     0,                          /* First interrupt number. */
01458     &ifn_eth0,                  /* Interface control block. */
01459     &dcb_eth0,                  /* Driver control block. */
01460     LancInit,                   /* Driver initialization routine. */
01461     LancIOCtl,                  /* Driver specific control function. */
01462     0,                          /* Read from device. */
01463     0,                          /* Write to device. */
01464     0,                          /* Write from program space data to device. */
01465     0,                          /* Open a device or file. */
01466     0,                          /* Close a device or file. */
01467     0                           /* Request file size. */
01468 };
01469