;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; 3Com network driver for KolibriOS ;; ;; ;; ;; Ported to KolibriOS net-branch by hidnplayr (28/05/10) ;; ;; ;; ;; Thanks to: scrap metal recyclers, whom provide me with ;; ;; loads of hardware ;; ;; diamond: who makes me understand KolibriOS ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; 3C59X.INC ;; ;; ;; ;; Ethernet driver for Menuet OS ;; ;; ;; ;; Driver for 3Com fast etherlink 3c59x and ;; ;; etherlink XL 3c900 and 3c905 cards ;; ;; References: ;; ;; www.3Com.com - data sheets ;; ;; DP83840A.pdf - ethernet physical layer ;; ;; 3c59x.c - linux driver ;; ;; ethernet driver template by Mike Hibbett ;; ;; ;; ;; Credits ;; ;; Mike Hibbett, ;; ;; who kindly supplied me with a 3Com905C-TX-M card ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Copyright (c) 2004, Endre Kozma ;; 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. The name of the author may not be used to endorse or promote products ;; derived from this software without specific prior written permission. ;; ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. ;; ;; History ;; ======= ;; $Log: 3C59X.INC,v $ ;; Revision 1.3 2004/07/11 12:21:12 kozma ;; Support of vortex chips (3c59x) added. ;; Support of 3c920 and 3c982 added. ;; Corrections. ;; ;; Revision 1.2 2004/06/12 19:40:20 kozma ;; Function e3c59x_set_available_media added in order to set ;; the default media in case auto detection finds no valid link. ;; Incorrect mii check removed (3c900 Cyclone works now). ;; Cleanups. ;; ;; Revision 1.1 2004/06/12 18:27:15 kozma ;; Initial revision ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; format MS COFF API_VERSION equ 0x01000100 DRIVER_VERSION equ 5 MAX_DEVICES equ 16 FORCE_FD equ 0 ; forcing full duplex mode makes sense at some cards and link types PROMISCIOUS equ 0 ; enables promiscous mode DEBUG equ 1 __DEBUG__ equ 1 __DEBUG_LEVEL__ equ 1 include 'proc32.inc' include 'imports.inc' include 'fdo.inc' include 'netdrv.inc' public START public service_proc public version struc DPD { ; Download Packet Descriptor .next_ptr dd ? .frame_start_hdr dd ? .frag_addr dd ? ; for packet data .frag_len dd ? ; for packet data .realaddr dd ? .size = 32 } virtual at 0 dpd DPD end virtual struc UPD { ; Upload Packet Descriptor .next_ptr dd ? .pkt_status dd ? .frag_addr dd ? .frag_len dd ? ; for packet data .realaddr dd ? .size = 32 } virtual at 0 upd UPD end virtual ; Ethernet frame symbols ETH_ALEN equ 6 ETH_HLEN equ (2*ETH_ALEN+2) ETH_ZLEN equ 60 ; 60 + 4bytes auto payload for ; mininmum 64bytes frame length ; Registers REG_POWER_MGMT_CTRL equ 0x7c REG_UP_LIST_PTR equ 0x38 REG_UP_PKT_STATUS equ 0x30 REG_TX_FREE_THRESH equ 0x2f REG_DN_LIST_PTR equ 0x24 REG_DMA_CTRL equ 0x20 REG_TX_STATUS equ 0x1b REG_RX_STATUS equ 0x18 REG_TX_DATA equ 0x10 ; Common window registers REG_INT_STATUS equ 0xe REG_COMMAND equ 0xe ; Register window 7 REG_MASTER_STATUS equ 0xc REG_POWER_MGMT_EVENT equ 0xc REG_MASTER_LEN equ 0x6 REG_VLAN_ETHER_TYPE equ 0x4 REG_VLAN_MASK equ 0x0 REG_MASTER_ADDRESS equ 0x0 ; Register window 6 REG_BYTES_XMITTED_OK equ 0xc REG_BYTES_RCVD_OK equ 0xa REG_UPPER_FRAMES_OK equ 0x9 REG_FRAMES_DEFERRED equ 0x8 REG_FRAMES_RCVD_OK equ 0x7 REG_FRAMES_XMITTED_OK equ 0x6 REG_RX_OVERRUNS equ 0x5 REG_LATE_COLLISIONS equ 0x4 REG_SINGLE_COLLISIONS equ 0x3 REG_MULTIPLE_COLLISIONS equ 0x2 REG_SQE_ERRORS equ 0x1 REG_CARRIER_LOST equ 0x0 ; Register window 5 REG_INDICATION_ENABLE equ 0xc REG_INTERRUPT_ENABLE equ 0xa REG_TX_RECLAIM_THRESH equ 0x9 REG_RX_FILTER equ 0x8 REG_RX_EARLY_THRESH equ 0x6 REG_TX_START_THRESH equ 0x0 ; Register window 4 REG_UPPER_BYTES_OK equ 0xe REG_BAD_SSD equ 0xc REG_MEDIA_STATUS equ 0xa REG_PHYSICAL_MGMT equ 0x8 REG_NETWORK_DIAGNOSTIC equ 0x6 REG_FIFO_DIAGNOSTIC equ 0x4 REG_VCO_DIAGNOSTIC equ 0x2 ; may not supported ; Bits in register window 4 BIT_AUTOSELECT equ 24 ; Register window 3 REG_TX_FREE equ 0xc REG_RX_FREE equ 0xa REG_MEDIA_OPTIONS equ 0x8 REG_MAC_CONTROL equ 0x6 REG_MAX_PKT_SIZE equ 0x4 REG_INTERNAL_CONFIG equ 0x0 ; Register window 2 REG_RESET_OPTIONS equ 0xc REG_STATION_MASK_HI equ 0xa REG_STATION_MASK_MID equ 0x8 REG_STATION_MASK_LO equ 0x6 REG_STATION_ADDRESS_HI equ 0x4 REG_STATION_ADDRESS_MID equ 0x2 REG_STATION_ADDRESS_LO equ 0x0 ; Register window 1 REG_TRIGGER_BITS equ 0xc REG_SOS_BITS equ 0xa REG_WAKE_ON_TIMER equ 0x8 REG_SMB_RXBYTES equ 0x7 REG_SMB_DIAG equ 0x5 REG_SMB_ARB equ 0x4 REG_SMB_STATUS equ 0x2 REG_SMB_ADDRESS equ 0x1 REG_SMB_FIFO_DATA equ 0x0 ; Register window 0 REG_EEPROM_DATA equ 0xc REG_EEPROM_COMMAND equ 0xa REG_BIOS_ROM_DATA equ 0x8 REG_BIOS_ROM_ADDR equ 0x4 ; Physical management bits BIT_MGMT_DIR equ 2 ; drive with the data written in mgmtData BIT_MGMT_DATA equ 1 ; MII management data bit BIT_MGMT_CLK equ 0 ; MII management clock ; MII commands MII_CMD_MASK equ (1111b shl 10) MII_CMD_READ equ (0110b shl 10) MII_CMD_WRITE equ (0101b shl 10) ; MII registers REG_MII_BMCR equ 0 ; basic mode control register REG_MII_BMSR equ 1 ; basic mode status register REG_MII_ANAR equ 4 ; auto negotiation advertisement register REG_MII_ANLPAR equ 5 ; auto negotiation link partner ability register REG_MII_ANER equ 6 ; auto negotiation expansion register ; MII bits BIT_MII_AUTONEG_COMPLETE equ 5 ; auto-negotiation complete BIT_MII_PREAMBLE_SUPPRESSION equ 6 ; eeprom bits and commands EEPROM_CMD_READ equ 0x80 EEPROM_BIT_BUSY equ 15 ; eeprom registers EEPROM_REG_OEM_NODE_ADDR equ 0xa EEPROM_REG_CAPABILITIES equ 0x10 ; Commands for command register SELECT_REGISTER_WINDOW equ (1 shl 11) IS_VORTEX equ 0x1 IS_BOOMERANG equ 0x2 IS_CYCLONE equ 0x4 IS_TORNADO equ 0x8 EEPROM_8BIT equ 0x10 HAS_PWR_CTRL equ 0x20 HAS_MII equ 0x40 HAS_NWAY equ 0x80 HAS_CB_FNS equ 0x100 INVERT_MII_PWR equ 0x200 INVERT_LED_PWR equ 0x400 MAX_COLLISION_RESET equ 0x800 EEPROM_OFFSET equ 0x1000 HAS_HWCKSM equ 0x2000 EXTRA_PREAMBLE equ 0x4000 ; Status IntLatch equ 0x0001 HostError equ 0x0002 TxComplete equ 0x0004 TxAvailable equ 0x0008 RxComplete equ 0x0010 RxEarly equ 0x0020 IntReq equ 0x0040 StatsFull equ 0x0080 DMADone equ 0x0100 DownComplete equ 0x0200 UpComplete equ 0x0400 DMAInProgress equ 0x0800 ; 1 shl 11 (DMA controller is still busy) CmdInProgress equ 0x1000 ; 1 shl 12 (EL3_CMD is still busy) S_5_INTS equ HostError + RxEarly + UpComplete + DownComplete ;+ TxComplete + RxComplete + TxAvailable ; Commands TotalReset equ 0 shl 11 SelectWindow equ 1 shl 11 StartCoax equ 2 shl 11 RxDisable equ 3 shl 11 RxEnable equ 4 shl 11 RxReset equ 5 shl 11 UpStall equ 6 shl 11 UpUnstall equ (6 shl 11)+1 DownStall equ (6 shl 11)+2 DownUnstall equ (6 shl 11)+3 RxDiscard equ 8 shl 11 TxEnable equ 9 shl 11 TxDisable equ 10 shl 11 TxReset equ 11 shl 11 FakeIntr equ 12 shl 11 AckIntr equ 13 shl 11 SetIntrEnb equ 14 shl 11 SetStatusEnb equ 15 shl 11 SetRxFilter equ 16 shl 11 SetRxThreshold equ 17 shl 11 SetTxThreshold equ 18 shl 11 SetTxStart equ 19 shl 11 StartDMAUp equ 20 shl 11 StartDMADown equ (20 shl 11)+1 StatsEnable equ 21 shl 11 StatsDisable equ 22 shl 11 StopCoax equ 23 shl 11 SetFilterBit equ 25 shl 11 ; Rx mode bits RxStation equ 1 RxMulticast equ 2 RxBroadcast equ 4 RxProm equ 8 ; RX/TX buffers sizes MAX_ETH_PKT_SIZE equ 1536 ; max packet size NUM_RX_DESC equ 4 ; a power of 2 number NUM_TX_DESC equ 4 ; a power of 2 number MAX_ETH_FRAME_SIZE equ 1520 ; size of ethernet frame + bytes alignment virtual at ebx device: ETH_DEVICE .dpd_buffer rd (dpd.size*NUM_TX_DESC)/4 .upd_buffer rd (upd.size*NUM_RX_DESC)/4 .curr_upd dd ? .prev_dpd dd ? .io_addr dd ? .pci_bus db ? .pci_dev db ? .irq_line db ? .prev_tx_frame dd ? .ver_id db ? .full_bus_master db ? .has_hwcksm db ? .preamble db ? .dn_list_ptr_cleared db ? .size = $ - device end virtual section '.flat' code readable align 16 ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; proc START ;; ;; ;; ;; (standard driver proc) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 proc START stdcall, state:dword cmp [state], 1 jne .exit .entry: DEBUGF 1,"Loading 3com network driver\n" stdcall RegService, my_service, service_proc ret .fail: .exit: xor eax, eax ret endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; proc SERVICE_PROC ;; ;; ;; ;; (standard driver proc) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 proc service_proc stdcall, ioctl:dword mov edx, [ioctl] mov eax, [IOCTL.io_code] ;------------------------------------------------------ cmp eax, 0 ;SRV_GETVERSION jne @F cmp [IOCTL.out_size], 4 jl .fail mov eax, [IOCTL.output] mov [eax], dword API_VERSION xor eax, eax ret ;------------------------------------------------------ @@: cmp eax, 1 ;SRV_HOOK jne .fail cmp [IOCTL.inp_size], 3 ; Data input must be at least 3 bytes jl .fail mov eax, [IOCTL.input] cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given jne .fail ; other types of this hardware dont exist ; check if the device is already listed mov ecx, [VORTEX_DEVICES] test ecx, ecx jz .maybeboomerang mov esi, VORTEX_LIST mov eax, [IOCTL.input] ; get the pci bus and device numbers mov ax , [eax+1] ; .nextdevice: mov ebx, [esi] cmp ax , word [device.pci_bus] ; compare with pci and device num in device list (notice the usage of word instead of byte) je .find_devicenum ; Device is already loaded, let's find it's device number add esi, 4 loop .nextdevice .maybeboomerang: mov ecx, [BOOMERANG_DEVICES] test ecx, ecx jz .firstdevice mov esi, BOOMERANG_LIST mov eax, [IOCTL.input] ; get the pci bus and device numbers mov ax , [eax+1] ; .nextdevice2: mov ebx, [esi] cmp ax , word [device.pci_bus] ; compare with pci and device num in device list (notice the usage of word instead of byte) je .find_devicenum ; Device is already loaded, let's find it's device number add esi, 4 loop .nextdevice2 ; This device doesnt have its own eth_device structure yet, lets create one .firstdevice: mov ecx, [BOOMERANG_DEVICES] add ecx, [VORTEX_DEVICES] cmp ecx, MAX_DEVICES ; First check if the driver can handle one more card jge .fail allocate_and_clear ebx, device.size, .fail ; Allocate the buffer for device structure ; Fill in the direct call addresses into the struct mov [device.reset], reset mov [device.transmit], null_op mov [device.get_MAC], read_mac mov [device.set_MAC], write_mac mov [device.unload], null_op mov [device.name], my_service ; save the pci bus and device numbers mov eax, [IOCTL.input] mov cl , [eax+1] mov [device.pci_bus], cl mov cl , [eax+2] mov [device.pci_dev], cl ; Now, it's time to find the base io addres of the PCI device find_io [device.pci_bus], [device.pci_dev], [device.io_addr] ; We've found the io address, find IRQ now find_irq [device.pci_bus], [device.pci_dev], [device.irq_line] DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\ [device.pci_dev]:1,[device.pci_bus]:1,[device.irq_line]:1,[device.io_addr]:4 ; Ok, the eth_device structure is ready, let's probe the device call probe ; this function will output in eax test eax, eax jnz .err ; If an error occured, exit movzx ecx, [device.ver_id] test word [hw_versions+2+ecx*4], IS_VORTEX jz .not_vortex mov eax, [VORTEX_DEVICES] ; Add the device structure to our device list mov [VORTEX_LIST+4*eax], ebx ; (IRQ handler uses this list to find device) inc [VORTEX_DEVICES] ; .register: mov [device.type], NET_TYPE_ETH call NetRegDev cmp eax, -1 je .destroy call start_device ret .not_vortex: mov eax, [BOOMERANG_DEVICES] ; Add the device structure to our device list mov [BOOMERANG_LIST+4*eax], ebx ; (IRQ handler uses this list to find device) inc [BOOMERANG_DEVICES] jmp .register ; If the device was already loaded, find the device number and return it in eax .find_devicenum: DEBUGF 1,"Trying to find device number of already registered device\n" call NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx ; into a device number in edi mov eax, edi ; Application wants it in eax instead DEBUGF 1,"Kernel says: %u\n", eax ret ; If an error occured, remove all allocated data and exit (returning -1 in eax) .destroy: ; todo: reset device into virgin state .err: stdcall KernelFree, ebx .fail: or eax, -1 ret ;------------------------------------------------------ endp ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; ;; ;; ;; Actual Hardware dependent code starts here ;; ;; ;; ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; ;*************************************************************************** ; Function ; probe ; Description ; Searches for an ethernet card, enables it and clears the rx buffer ; Destroyed registers ; eax, ebx, ecx, edx, edi, esi ; ;*************************************************************************** align 4 probe: DEBUGF 1,"Probing 3com card\n" make_bus_master [device.pci_bus], [device.pci_dev] ; wake up the card call wake_up movzx ecx, [device.pci_bus] movzx edx, [device.pci_dev] stdcall PciRead32, ecx ,edx ,0 ; get device/vendor id DEBUGF 1,"Vendor id: 0x%x\n", ax cmp ax , 0x10B7 jne .notfound shr eax, 16 DEBUGF 1,"Vendor ok!, device id: 0x%x\n", ax ; get chip version mov ecx, HW_VERSIONS_SIZE/4-1 .loop: cmp ax , [hw_versions+ecx*4] jz .found loop .loop DEBUGF 1,"ecx: %u\n", ecx .notfound: DEBUGF 1,"Device id not found in list!\n" or eax, -1 ret .found: mov esi, [hw_str+ecx*4] DEBUGF 1,"Hardware type: %s\n", esi mov [device.name], esi mov [device.ver_id], cl test word [hw_versions+2+ecx*4], HAS_HWCKSM setnz [device.has_hwcksm] ; set pci latency for vortex cards test word [hw_versions+2+ecx*4], IS_VORTEX jz .not_vortex mov eax, 11111000b ; 248 = max latency movzx ecx, [device.pci_bus] movzx edx, [device.pci_dev] stdcall PciWrite32, ecx, edx, PCI_REG_LATENCY, eax .not_vortex: ; set RX/TX functions mov ax, EEPROM_REG_CAPABILITIES call read_eeprom test al, 100000b ; full bus master? setnz [device.full_bus_master] jnz .boomerang_func mov [device.transmit], vortex_transmit DEBUGF 1,"Device is a vortex type\n" DEBUGF 1,"I'm sorry but vortex code hasnt been tested yet\n" DEBUGF 1,"Please contact me on hidnplayr@kolibrios.org\n" DEBUGF 1,"If you can help me finish it!\n" or eax, -1 ret jmp @f .boomerang_func: ; full bus master, so use boomerang functions mov [device.transmit], boomerang_transmit DEBUGF 1,"Device is a boomerang type\n" @@: call read_mac_eeprom test byte [device.full_bus_master], 0xff jz .set_preamble ; switch to register window 2 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+2 out dx, ax ; activate xcvr by setting some magic bits set_io REG_RESET_OPTIONS in ax, dx and ax, not 0x4010 movzx ecx, [device.ver_id] test word [ecx*4+hw_versions+2], INVERT_LED_PWR jz @f or al, 0x10 @@: test word [ecx*4+hw_versions+2], INVERT_MII_PWR jz @f or ah, 0x40 @@: out dx, ax .set_preamble: ; use preamble as default mov byte [device.preamble], 1 ; enable preamble call global_reset ;-------------------------- ; RESET align 4 reset: movzx eax, [device.irq_line] DEBUGF 1,"Attaching int handler to irq %x\n",eax:1 movzx ecx, [device.ver_id] test word [hw_versions+2+ecx*4], IS_VORTEX jz .not_vortex mov esi, int_vortex jmp .reg_int .not_vortex: mov esi, int_boomerang .reg_int: stdcall AttachIntHandler, eax, esi, dword 0 test eax, eax jnz @f DEBUGF 1,"\nCould not attach int handler!\n" ; or eax, -1 ; ret @@: set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW + 0 out dx, ax mov ax, StopCoax out dx, ax ; stop transceiver mov ax, SELECT_REGISTER_WINDOW + 4 out dx, ax ; disable UTP set_io REG_MEDIA_STATUS mov ax, 0x0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW + 0 out dx, ax set_io REG_FIFO_DIAGNOSTIC mov ax, 0 out dx, ax ; disable card mov ax, 1 out dx, ax ; enable card call write_mac ;<<<<<<<<<<<<<< set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW + 1 out dx, ax mov ecx, 32 set_io 0x0b .loop: in al, dx loop .loop ; Get rid of stary ints set_io REG_COMMAND mov ax, AckIntr + 0xff out dx, ax mov ax, SetStatusEnb + S_5_INTS out dx, ax mov ax, SetIntrEnb + S_5_INTS out dx, ax call set_rx_mode call set_active_port ;>>>>>>>>>> call create_rx_ring call rx_reset call tx_reset ;>>>>>>>>>>>>>>>>>> set_io 0 set_io REG_COMMAND mov ax, RxEnable out dx, ax mov ax, TxEnable out dx, ax set_io REG_COMMAND mov ax, SetRxThreshold + 208 out dx, ax mov ax, SetTxThreshold + 60 ;16 ; recommended by the manual :) out dx, ax mov ax, SELECT_REGISTER_WINDOW + 1 out dx, ax xor eax, eax ; clear packet/byte counters lea edi, [device.bytes_tx] mov ecx, 6 rep stosd ; Set the mtu, kernel will be able to send now mov [device.mtu], 1514 ret align 4 start_device: DEBUGF 1,"Starting the device\n" set_io 0 set_io REG_COMMAND mov ax, SetTxThreshold + 60 ;2047 ; recommended by the manual :) out dx, ax call check_tx_status set_io 0 set_io REG_COMMAND ; switch to register window 4 mov ax, SELECT_REGISTER_WINDOW+4 out dx, ax ; wait for linkDetect set_io REG_MEDIA_STATUS mov ecx, 20 ; wait for max 2s .link_detect_loop: mov esi, 100 call Sleep ; 100 ms in ax, dx test ah, 1000b ; linkDetect jnz @f loop .link_detect_loop DEBUGF 1,"Link detect timed-out!\n" @@: ; print link type xor eax, eax bsr ax, word [device.mode] jz @f sub ax, 4 @@: mov esi, [link_str+eax*4] DEBUGF 1,"Established Link type: %s\n", esi ; enable interrupts set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW + 1 out dx, ax mov ax, AckIntr + 0xff out dx, ax mov ax, SetStatusEnb + S_5_INTS out dx, ax mov ax, SetIntrEnb + S_5_INTS out dx, ax ret align 4 set_rx_mode: DEBUGF 1,"Setting RX mode\n" set_io 0 set_io REG_COMMAND if defined PROMISCIOUS mov ax, SetRxFilter + RxStation + RxMulticast + RxBroadcast + RxProm else if defined ALLMULTI mov ax, SetRxFilter + RxStation + RxMulticast + RxBroadcast else mov ax, SetRxFilter + RxStation + RxBroadcast end if out dx, ax ret ;*************************************************************************** ; Function ; global_reset ; Description ; resets the device ; Parameters: ; ebp - io_addr ; Return value: ; Destroyed registers ; ax, ecx, edx, esi ; ;***************************************************************************1 align 4 global_reset: DEBUGF 1,"Global reset..\n" ; GlobalReset set_io 0 set_io REG_COMMAND xor eax, eax ; or al, 0x14 out dx, ax ; wait for GlobalReset to complete mov ecx, 64000 .loop: in ax , dx test ah , 10000b ; check CmdInProgress loopz .loop DEBUGF 1,"Waiting for nic to boot..\n" ; wait for 2 seconds for NIC to boot mov esi, 2000 call Sleep ; 2 seconds DEBUGF 1,"Ok!\n" ret ;*************************************************************************** ; Function ; tx_reset ; Description ; resets and enables transmitter engine ; ;*************************************************************************** align 4 tx_reset: DEBUGF 1,"tx reset\n" ; TxReset set_io 0 set_io REG_COMMAND mov ax, TxReset out dx, ax ; Wait for TxReset to complete mov ecx, 200000 .tx_reset_loop: in ax, dx test ah, 10000b ; check CmdInProgress jz .tx_set_prev dec ecx jnz .tx_reset_loop .tx_set_prev: ; init last_dpd lea eax, [device.dpd_buffer + (NUM_TX_DESC-1)*dpd.size] mov [device.prev_dpd], eax .tx_enable: ret ;*************************************************************************** ; Function ; rx_reset ; Description ; resets and enables receiver engine ; ;*************************************************************************** align 4 rx_reset: DEBUGF 1,"rx reset\n" set_io 0 set_io REG_COMMAND mov ax, RxReset or 0x4 out dx, ax ; wait for RxReset to complete mov ecx, 200000 .loop: in ax, dx test ah, 10000b ; check CmdInProgress jz .done dec ecx jnz .loop .done: lea eax, [device.upd_buffer] mov [device.curr_upd], eax GetRealAddr set_io 0 set_io REG_UP_LIST_PTR out dx, eax .rx_enable: ret align 4 create_rx_ring: ; create upd ring lea eax, [device.upd_buffer] GetRealAddr mov edi, eax ; real addr of first descr lea esi, [device.upd_buffer] ; ptr to first descr lea edx, [device.upd_buffer + (NUM_RX_DESC-1)*upd.size] ; ptr to last descr mov ecx, NUM_RX_DESC .upd_loop: mov [edx + upd.next_ptr], edi push ecx edx stdcall KernelAlloc, MAX_ETH_FRAME_SIZE pop edx ecx mov [esi + upd.realaddr], eax call GetPgAddr mov [esi + upd.frag_addr], eax and [esi + upd.pkt_status], 0 mov [esi + upd.frag_len], MAX_ETH_FRAME_SIZE or (1 shl 31) DEBUGF 1,"UPD: lin=%x phys=%x len=%x next ptr=%x\n", [esi+upd.realaddr]:8, [esi+upd.frag_addr]:8, [esi+upd.frag_len]:8, edi DEBUGF 1,"UPD: cur=%x prev=%x\n", esi, edx mov edx, esi add esi, upd.size add edi, upd.size dec ecx jnz .upd_loop ret ;--------------------------------------------------------------------------- ; Function ; try_link_detect ; Description ; try_link_detect checks if link exists ; Parameters ; ebx = device structure ; Return value ; al - 0 ; no link detected ; al - 1 ; link detected ; Destroyed registers ; eax, ebx, ecx, edx, edi, esi ; ;--------------------------------------------------------------------------- align 4 try_link_detect: DEBUGF 1,"trying to detect link\n" ; create self-directed packet stdcall KernelAlloc, 20 ; create a buffer for the self-directed packet test eax, eax jz .fail pushd 20 ; Packet parameters for device.transmit push eax ; mov edi, eax lea esi, [device.mac] movsw movsd sub esi, 6 movsw movsd mov ax , 0x0608 stosw ; download self-directed packet call [device.transmit] ; switch to register window 4 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+4 out dx, ax ; See if we have received the packet by now.. cmp [device.packets_rx], 0 jnz .link_detected ; switch to register window 4 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+4 out dx, ax ; read linkbeatdetect set_io REG_MEDIA_STATUS in ax, dx test ah, 1000b ; test linkBeatDetect jnz .link_detected xor al, al jmp .finish .link_detected: DEBUGF 1,"link detected!\n" setb al .finish: test al, al jz @f or byte [device.mode+1], 100b @@: ret .fail: ret ;*************************************************************************** ; Function ; try_phy ; Description ; try_phy checks the auto-negotiation function ; in the PHY at PHY index. It can also be extended to ; include link detection for non-IEEE 802.3u ; auto-negotiation devices, for instance the BCM5000. ; TODO: BCM5000 ; Parameters ; ah - PHY index ; ebx - device stucture ; Return value ; al - 0 link is auto-negotiated ; al - 1 no link is auto-negotiated ; Destroyed registers ; eax, ebx, ecx, edx, esi ; ;*************************************************************************** align 4 try_phy: DEBUGF 1,"PHY=%u\n", ah DEBUGF 1,"Detecting if device is auto-negotiation capable\n" mov al, REG_MII_BMCR push eax call mdio_read ; returns with window #4 or ah , 0x80 ; software reset mov esi, eax mov eax, dword [esp] call mdio_write ; returns with window #4 ; wait for reset to complete mov esi, 2000 stdcall Sleep ; 2s mov eax, [esp] call mdio_read ; returns with window #4 test ah , 0x80 jnz .fail1 mov eax, [esp] ; wait for a while after reset mov esi, 20 stdcall Sleep ; 20ms mov eax, [esp] mov al , REG_MII_BMSR call mdio_read ; returns with window #4 test al , 1 ; extended capability supported? jz .fail2 ; auto-neg capable? test al , 1000b jz .fail2 ; not auto-negotiation capable DEBUGF 1,"Device is auto-negotiation capable\n" ; auto-neg complete? test al , 100000b jnz .auto_neg_ok DEBUGF 1,"Restarting auto-negotiation\n" ; restart auto-negotiation mov eax, [esp] mov al , REG_MII_ANAR push eax call mdio_read ; returns with window #4 or ax , 1111b shl 5; advertise only 10base-T and 100base-TX mov esi, eax pop eax call mdio_write ; returns with window #4 mov eax, [esp] call mdio_read ; returns with window #4 mov esi, eax or bh , 10010b ; restart auto-negotiation mov eax, [esp] call mdio_write ; returns with window #4 mov esi, 4000 stdcall Sleep ; 4 seconds mov eax, [esp] mov al , REG_MII_BMSR call mdio_read ; returns with window #4 test al , 100000b ; auto-neg complete? jnz .auto_neg_ok jmp .fail3 .auto_neg_ok: DEBUGF 1,"Auto-negotiation complete\n" ; compare advertisement and link partner ability registers mov eax, [esp] mov al , REG_MII_ANAR call mdio_read ; returns with window #4 xchg eax, [esp] mov al , REG_MII_ANLPAR call mdio_read ; returns with window #4 pop esi and eax, esi and eax, 1111100000b push eax mov word[device.mode+2], ax ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax , SELECT_REGISTER_WINDOW+3 out dx , ax ; set full-duplex mode set_io REG_MAC_CONTROL in ax , dx and ax , not 0x120 ; clear full duplex and flow control pop esi test esi, 1010b shl 5; check for full-duplex jz .half_duplex or ax , 0x120 ; set full duplex and flow control .half_duplex: DEBUGF 1,"Using half-duplex\n" out dx , ax mov al , 1 ret .fail1: DEBUGF 1,"reset failed!\n" pop eax xor al, al ret .fail2: DEBUGF 1,"This device is not auto-negotiation capable!\n" pop eax xor al, al ret .fail3: DEBUGF 1,"auto-negotiation reset failed!\n" pop eax xor al, al ret ;*************************************************************************** ; Function ; try_mii ; Description ; try_MII checks the on-chip auto-negotiation logic ; or an off-chip MII PHY, depending upon what is set in ; xcvrSelect by the caller. ; It exits when it finds the first device with a good link. ; Parameters ; ebp - io_addr ; Return value ; al - 0 ; al - 1 ; Destroyed registers ; eax, ebx, ecx, edx, esi ; ;*************************************************************************** align 4 try_mii: DEBUGF 1,"trying to find MII PHY\n" ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+3 out dx, ax set_io REG_INTERNAL_CONFIG in eax, dx and eax, (1111b shl 20) cmp eax, (1000b shl 20) ; is auto-negotiation set? jne .mii_device DEBUGF 1,"auto-negotiation is set\n" ; switch to register window 4 set_io REG_COMMAND mov ax , SELECT_REGISTER_WINDOW+4 out dx , ax ; PHY==24 is the on-chip auto-negotiation logic ; it supports only 10base-T and 100base-TX mov ah , 24 call try_phy test al , al jz .fail_finish mov cl , 24 jmp .check_preamble .mii_device: cmp eax, (0110b shl 20) jne .fail_finish set_io 0 set_io REG_COMMAND mov ax , SELECT_REGISTER_WINDOW+4 out dx , ax set_io REG_PHYSICAL_MGMT in ax , dx and al , (1 shl BIT_MGMT_DIR) or (1 shl BIT_MGMT_DATA) cmp al , (1 shl BIT_MGMT_DATA) je .search_for_phy xor al , al ret .search_for_phy: ; search for PHY mov cx , 31 .search_phy_loop: DEBUGF 1,"Searching the PHY\n" cmp cx , 24 je .next_phy mov ah , cl ; ah = phy mov al , REG_MII_BMCR ; al = Basic Mode Status Register push cx call mdio_read pop cx test ax , ax jz .next_phy cmp ax , 0xffff je .next_phy mov ah , cl ; ah = phy push cx call try_phy pop cx test al , al jnz .check_preamble .next_phy: loopw .search_phy_loop .fail_finish: xor al, al ret ; epilog .check_preamble: DEBUGF 1,"Using PHY: %u\nChecking PreAmble\n", cl push eax ; eax contains the return value of try_phy ; check hard coded preamble forcing movzx eax, [device.ver_id] test word [eax*4+hw_versions+2], EXTRA_PREAMBLE setnz [device.preamble] ; force preamble jnz .finish ; check mii for preamble suppression mov ah, cl mov al, REG_MII_BMSR call mdio_read test al, 1000000b ; preamble suppression? setz [device.preamble] ; no .finish: pop eax ret ;*************************************************************************** ; Function ; test_packet ; Description ; try_loopback try a loopback packet for 10BASE2 or AUI port ; Parameters ; ebx = device structure ; ;*************************************************************************** align 4 test_packet: DEBUGF 1,"sending test packet\n" ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+3 out dx, ax ; set fullDuplexEnable in MacControl register set_io REG_MAC_CONTROL in ax, dx or ax, 0x120 out dx, ax ; switch to register window 5 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+5 out dx, ax ; set RxFilter to enable individual address matches mov ax, (10000b shl 11) set_io REG_RX_FILTER in al, dx or al, 1 set_io REG_COMMAND out dx, ax ; issue RxEnable and TxEnable call rx_reset call tx_reset ; create self-directed packet stdcall KernelAlloc, 20 ; create a buffer for the self-directed packet test eax, eax jz .fail pushd 20 ; Packet parameters for device.transmit push eax ; mov edi, eax lea esi, [device.mac] movsw movsd sub esi, 6 movsw movsd mov ax , 0x0608 stosw ; download self-directed packet call [device.transmit] ; wait for 2s mov esi, 2000 call Sleep ; check if self-directed packet is received mov eax, [device.packets_rx] test eax, eax jnz .finish ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+3 out dx, ax ; clear fullDuplexEnable in MacControl register set_io REG_MAC_CONTROL in ax , dx and ax , not 0x120 out dx , ax .fail: xor eax, eax .finish: ret ;*************************************************************************** ; Function ; try_loopback ; Description ; tries a loopback packet for 10BASE2 or AUI port ; Parameters ; al - 0: 10Mbps AUI connector ; 1: 10BASE-2 ; ebp - io_addr ; Return value ; al - 0 ; al - 1 ; Destroyed registers ; eax, ebx, ecx, edx, edi, esi ; ;*************************************************************************** align 4 try_loopback: DEBUGF 1,"trying loopback\n" push eax ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+3 out dx, ax mov eax, [esp] mov cl, al inc cl shl cl, 3 or byte [device.mode+1], cl test al, al ; aui or coax? jz .complete_loopback ; enable 100BASE-2 DC-DC converter mov ax, (10b shl 11) ; EnableDcConverter out dx, ax .complete_loopback: mov cx, 2 ; give a port 3 chances to complete a loopback .next_try: push ecx call test_packet pop ecx test eax, eax loopzw .next_try .finish: xchg eax, [esp] test al, al jz .aui_finish ; issue DisableDcConverter command set_io 0 set_io REG_COMMAND mov ax, (10111b shl 11) out dx, ax .aui_finish: pop eax ; al contains the result of operation test al, al jnz @f and byte [device.mode+1], not 11000b @@: ret ;*************************************************************************** ; Function ; set_active_port ; Description ; It selects the media port (transceiver) to be used ; Return value: ; Destroyed registers ; eax, ebx, ecx, edx, edi, esi ; ;*************************************************************************** align 4 set_active_port: DEBUGF 1,"Trying to find the active port\n" ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW + 3 out dx, ax set_io REG_INTERNAL_CONFIG in eax, dx test eax, (1 shl 24) ; check if autoselect enable jz .set_first_available_media ; check 100BASE-TX and 10BASE-T set_io REG_MEDIA_OPTIONS in ax, dx test al, 1010b ; check whether 100BASE-TX or 10BASE-T available jz .mii_device ; they are not available ; set auto-negotiation set_io REG_INTERNAL_CONFIG in eax, dx and eax, not (1111b shl 20) or eax, (1000b shl 20) out dx, eax call try_mii test al, al jz .mii_device DEBUGF 1,"Using auto negotiation\n" ret .mii_device: ; switch to register window 3 set_io 0 ; check for off-chip mii device set_io REG_MEDIA_OPTIONS in ax, dx test al, 1000000b ; check miiDevice jz .base_fx set_io REG_INTERNAL_CONFIG in eax, dx and eax, not (1111b shl 20) or eax, (0110b shl 20) ; set MIIDevice out dx, eax call try_mii test al, al jz .base_fx DEBUGF 1,"Using off-chip mii device\n" ret .base_fx: ; switch to register window 3 set_io 0 ; check for 100BASE-FX set_io REG_MEDIA_OPTIONS in ax, dx ; read media option register test al, 100b ; check 100BASE-FX jz .aui_enable set_io REG_INTERNAL_CONFIG in eax, dx and eax, not (1111b shl 20) or eax, (0101b shl 20) ; set 100base-FX out dx, eax call try_link_detect test al, al jz .aui_enable DEBUGF 1,"Using 100Base-FX\n" ret .aui_enable: ; switch to register window 3 set_io 0 ; check for 10Mbps AUI connector set_io REG_MEDIA_OPTIONS in ax, dx ; read media option register test al, 100000b ; check 10Mbps AUI connector jz .coax_available set_io REG_INTERNAL_CONFIG in eax, dx and eax, not (1111b shl 20) or eax, (0001b shl 20) ; set 10Mbps AUI connector out dx, eax xor al, al ; try 10Mbps AUI connector call try_loopback test al, al jz .coax_available DEBUGF 1,"Using 10Mbps aui\n" ret .coax_available: ; switch to register window 3 set_io 0 ; check for coaxial 10BASE-2 port set_io REG_MEDIA_OPTIONS in ax, dx ; read media option register test al, 10000b ; check 10BASE-2 jz .set_first_available_media set_io REG_INTERNAL_CONFIG in eax, dx and eax, not (1111b shl 20) or eax, (0011b shl 20) ; set 10BASE-2 out dx, eax mov al, 1 call try_loopback test al, al jz .set_first_available_media DEBUGF 1,"Using 10BASE-2 port\n" ret .set_first_available_media: DEBUGF 1,"Using the first available media\n" ;*************************************************************************** ; Function ; set_available_media ; Description ; sets the first available media ; Parameters ; ebx - ptr to device struct ; Return value ; al - 0 ; al - 1 ; Destroyed registers ; eax, edx ; ;*************************************************************************** align 4 set_available_media: DEBUGF 1,"Setting the available media\n" ; switch to register window 3 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+3 out dx, ax set_io REG_MEDIA_OPTIONS in ax, dx DEBUGF 1,"available media:%x\n", al mov cl, al set_io REG_INTERNAL_CONFIG in eax, dx and eax, not (1111b shl 20) ; these bits hold the 'transceiver select' value test cl, 10b ; baseTXAvailable jz @f DEBUGF 1,"base TX is available\n" or eax, (100b shl 20) if defined FORCE_FD mov word [device.mode], (1 shl 8) else mov word [device.mode], (1 shl 7) end if jmp .set_media @@: test cl, 100b ; baseFXAvailable jz @f DEBUGF 1,"base FX is available\n" or eax, (101b shl 20) mov word [device.mode], (1 shl 10) jmp .set_media @@: test cl, 1000000b ; miiDevice jz @f DEBUGF 1,"mii-device is available\n" or eax, (0110b shl 20) mov word [device.mode], (1 shl 13) jmp .set_media @@: test cl, 1000b ; 10bTAvailable jz @f DEBUGF 1,"10base-T is available\n" .set_default: if FORCE_FD mov word [device.mode], (1 shl 6) else mov word [device.mode], (1 shl 5) end if jmp .set_media @@: test cl, 10000b ; coaxAvailable jz @f DEBUGF 1,"coax is available\n" push eax set_io REG_COMMAND mov ax, (10b shl 11) ; EnableDcConverter out dx, ax pop eax or eax, (11b shl 20) mov word [device.mode], (1 shl 12) jmp .set_media @@: test cl, 10000b ; auiAvailable jz .set_default DEBUGF 1,"AUI is available\n" or eax, (1 shl 20) mov word [device.mode], (1 shl 11) .set_media: set_io 0 set_io REG_INTERNAL_CONFIG out dx, eax if FORCE_FD DEBUGF 1,"Forcing full duplex\n" set_io REG_MAC_CONTROL in ax, dx or ax, 0x120 out dx, ax end if mov al, 1 ret ;*************************************************************************** ; Function ; wake_up ; Description ; set the power state to D0 ; ;*************************************************************************** align 4 wake_up: DEBUGF 1,"Waking up NIC: " ; wake up - we directly do it by programming PCI ; check if the device is power management capable movzx ecx, [device.pci_bus] movzx edx, [device.pci_dev] stdcall PciRead32, ecx, edx, PCI_REG_STATUS test al, 10000b ; is there "new capabilities" linked list? jz .device_awake ; search for power management register stdcall PciRead16, ecx, edx, PCI_REG_CAP_PTR cmp al, 0x3f jbe .device_awake ; traverse the list movzx esi, al .pm_loop: stdcall PciRead32, ecx, edx, esi cmp al , 1 je .set_pm_state movzx esi, ah test ah , ah jnz .pm_loop jmp .device_awake ; waku up the device if necessary .set_pm_state: add esi, PCI_REG_PM_CTRL stdcall PciRead32, ecx, edx, esi test al, 3 jz .device_awake and al, not 11b ; set state to D0 stdcall PciWrite32, ecx, edx, esi, eax .device_awake: DEBUGF 1,"Device is awake\n" ret ;*************************************************************************** ; Function ; write_eeprom ; Description ; reads eeprom ; Note : the caller must switch to the register window 0 ; before calling this function ; Parameters: ; ax - register to be read (only the first 63 words can be read) ; cx - value to be read into the register ; Return value: ; ax - word read ; Destroyed registers ; ax, ebx, edx ; ;*************************************************************************** ; align 4 ;write_eeprom: ; mov edx, [io_addr] ; add edx, REG_EEPROM_COMMAND ; cmp ah, 11b ; ja .finish ; address may have a value of maximal 1023 ; shl ax, 2 ; shr al, 2 ; push eax ;; wait for busy ; mov ebx, 0xffff ;@@: ; in ax, dx ; test ah, 0x80 ; jz .write_enable ; dec ebx ; jns @r ;; write enable ;.write_enable: ; xor eax, eax ; mov eax, (11b shl 4) ; out dx, ax ;; wait for busy ; mov ebx, 0xffff ;@@: ; in ax, dx ; test ah, 0x80 ; jz .erase_loop ; dec ebx ; jns @r ;.erase_loop: ; pop eax ; push eax ; or ax, (11b shl 6) ; erase register ; out dx, ax ; mov ebx, 0xffff ;@@: ; in ax, dx ; test ah, 0x80 ; jz .write_reg ; dec ebx ; jns @r ;.write_reg: ; add edx, REG_EEPROM_DATA-REG_EEPROM_COMMAND ; mov eax, ecx ; out dx, ax ;; write enable ; add edx, REG_EEPROM_COMMAND-REG_EEPROM_DATA ; xor eax, eax ; mov eax, (11b shl 4) ; out dx, ax ; wait for busy ; mov ebx, 0xffff ;@@: ; in ax, dx ; test ah, 0x80 ; jz .issue_write_reg ; dec ebx ; jns @r ;.issue_write_reg: ; pop eax ; or ax, 01b shl 6 ; out dx, ax ;.finish: ; ret ;*************************************************************************** ; Function ; read_eeprom ; Description ; reads eeprom ; Parameters: ; ax - register to be read (only the first 63 words can be read) ; ebx = driver structure ; Return value: ; ax - word read ; Destroyed registers ; ax, ebx, edx ; ;*************************************************************************** align 4 read_eeprom: DEBUGF 1,"Reading from eeprom.. " push eax ; switch to register window 0 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+0 out dx, ax pop eax and ax, 111111b ; take only the first 6 bits into account movzx esi, [device.ver_id] test word [esi*4+hw_versions+2], EEPROM_8BIT jz @f add ax, 0x230 ; hardware constant jmp .read @@: add ax, EEPROM_CMD_READ test word [esi*4+hw_versions+2], EEPROM_OFFSET jz .read add ax, 0x30 .read: set_io REG_EEPROM_COMMAND out dx, ax mov ecx, 0xffff ; duration of about 162 us ;-) .wait_for_reading: in ax, dx test ah, 0x80 ; check bit eepromBusy jz .read_data loop .wait_for_reading .read_data: set_io REG_EEPROM_DATA in ax, dx DEBUGF 1,"ok!\n" ret ;*************************************************************************** ; Function ; mdio_sync ; Description ; initial synchronization ; Parameters ; ebp - io_addr ; Return value ; Destroyed registers ; ax, edx, cl ; ;*************************************************************************** align 4 mdio_sync: DEBUGF 1,"syncing mdio\n" ; switch to register window 4 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+4 out dx, ax cmp [device.preamble], 0 je .no_preamble ; send 32 logic ones set_io REG_PHYSICAL_MGMT mov ecx, 31 .loop: mov ax, (1 shl BIT_MGMT_DATA) or (1 shl BIT_MGMT_DIR) out dx, ax in ax, dx ; delay mov ax, (1 shl BIT_MGMT_DATA) or (1 shl BIT_MGMT_DIR) or (1 shl BIT_MGMT_CLK) out dx, ax in ax, dx ; delay loop .loop .no_preamble: ret ;*************************************************************************** ; Function ; mdio_read ; Description ; read MII register ; see page 16 in D83840A.pdf ; Parameters ; ah - PHY addr ; al - register addr ; ebx = device structure ; Return value ; ax - register read ; ;*************************************************************************** align 4 mdio_read: DEBUGF 1,"Reading MII registers\n" push eax call mdio_sync ; returns with window #4 pop eax set_io 0 set_io REG_PHYSICAL_MGMT shl al, 3 shr ax, 3 and ax, not MII_CMD_MASK or ax, MII_CMD_READ mov esi, eax mov ecx, 13 .cmd_loop: mov ax, (1 shl BIT_MGMT_DIR) ; write mii bt esi, ecx jnc .zero_bit or al, (1 shl BIT_MGMT_DATA) .zero_bit: out dx, ax push ax in ax, dx ; delay pop ax or al, (1 shl BIT_MGMT_CLK) ; write out dx, ax in ax, dx ; delay loop .cmd_loop ; read data (18 bits with the two transition bits) mov ecx, 17 xor esi, esi .read_loop: shl esi, 1 xor eax, eax ; read comand out dx, ax in ax, dx ; delay in ax, dx test al, (1 shl BIT_MGMT_DATA) jz .dont_set inc esi .dont_set: mov ax, (1 shl BIT_MGMT_CLK) out dx, ax in ax, dx ; delay loop .read_loop mov eax, esi ret ;*************************************************************************** ; Function ; mdio_write ; Description ; write MII register ; see page 16 in D83840A.pdf ; Parameters ; ah - PHY addr ; al - register addr ; si - word to be written ; Return value ; ax - register read ; ;*************************************************************************** align 4 mdio_write: DEBUGF 1,"Writing MII registers\n" push eax call mdio_sync pop eax set_io 0 set_io REG_PHYSICAL_MGMT shl al, 3 shr ax, 3 and ax, not MII_CMD_MASK or ax, MII_CMD_WRITE shl eax, 2 or eax, 10b ; transition bits shl eax, 16 mov ax, si mov esi, eax mov ecx, 31 .cmd_loop: mov ax, (1 shl BIT_MGMT_DIR) ; write mii bt esi, ecx jnc @f or al, (1 shl BIT_MGMT_DATA) @@: out dx, ax push eax in ax, dx ; delay pop eax or al, (1 shl BIT_MGMT_CLK) ; write out dx, ax in ax, dx ; delay loop .cmd_loop ret ;*************************************************************************** ; Function ; check_tx_status ; Description ; Checks TxStatus queue. ; Return value ; al - 0 no error was found ; al - 1 error was found TxReset was needed ; Destroyed registers ; eax, ecx, edx, ebp ; ;*************************************************************************** align 4 check_tx_status: DEBUGF 1,"Checking TX status\n" ; clear TxStatus queue set_io 0 set_io REG_TX_STATUS mov ecx, 31 ; max number of queue entries .tx_status_loop: in al, dx test al, al jz .finish ; no error test al, 0x3f jnz .error .no_error_found: ; clear current TxStatus entry which advances the next one xor al, al out dx, al loop .tx_status_loop .finish: ret .error: call tx_reset ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Transmit (vortex) ;; ;; ;; ;; In: buffer pointer in [esp+4] ;; ;; size of buffer in [esp+8] ;; ;; pointer to device structure in ebx ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 vortex_transmit: DEBUGF 1,"Sending packet (vortex)\n" cmp dword [esp+8], MAX_ETH_FRAME_SIZE ja .finish ; packet is too long call check_tx_status ; switch to register window 7 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+7 out dx, ax ; check for master operation in progress set_io REG_MASTER_STATUS in ax, dx test ah, 0x80 jnz .finish ; no DMA for sending ; program frame address to be sent set_io REG_MASTER_ADDRESS mov eax, [esp+4] call GetPgAddr out dx, eax ; program frame length set_io REG_MASTER_LEN mov eax, [esp+8] ;;; and eax, not 3 out dx, ax ; start DMA Down set_io REG_COMMAND mov ax, (10100b shl 11) + 1 ; StartDMADown out dx, ax .finish: call KernelFree add esp, 4 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Transmit (boomerang) ;; ;; ;; ;; In: buffer pointer in [esp+4] ;; ;; size of buffer in [esp+8] ;; ;; pointer to device structure in ebx ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 boomerang_transmit: DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n",[esp+4],[esp+8] mov eax, [esp+4] DEBUGF 1,"To: %x-%x-%x-%x-%x-%x From: %x-%x-%x-%x-%x-%x Type:%x%x\n",\ [eax+00]:2,[eax+01]:2,[eax+02]:2,[eax+03]:2,[eax+04]:2,[eax+05]:2,\ [eax+06]:2,[eax+07]:2,[eax+08]:2,[eax+09]:2,[eax+10]:2,[eax+11]:2,\ [eax+13]:2,[eax+12]:2 cmp dword [esp+8], MAX_ETH_FRAME_SIZE jg .fail call check_tx_status ; calculate descriptor address mov esi, [device.prev_dpd] DEBUGF 1,"Previous DPD: %x\n", esi add esi, dpd.size lea ecx, [device.dpd_buffer + (NUM_TX_DESC)*dpd.size] cmp esi, ecx jl @f lea esi, [device.dpd_buffer] ; Wrap if needed @@: DEBUGF 1,"Found a free DPD: %x\n", esi ; check DnListPtr set_io 0 set_io REG_DN_LIST_PTR in eax, dx ; mark if Dn_List_Ptr is cleared test eax, eax setz [device.dn_list_ptr_cleared] ; finish if no more free descriptor is available - FIXME! ; cmp eax, esi ; jz .finish ; update statistics inc [device.packets_tx] mov ecx, [esp+8] ; buffer size add dword [device.bytes_tx], ecx adc dword [device.bytes_tx + 4], 0 ; program DPD and [esi+dpd.next_ptr], 0 mov eax, [esp+4] ; Tx buffer address mov [esi+dpd.realaddr], eax call GetPgAddr mov [esi+dpd.frag_addr], eax mov ecx, [esp+8] ; packet size or ecx, 0x80000000 ; last fragment mov [esi+dpd.frag_len], ecx mov ecx, [esp+8] ; packet size ; or ecx, 0x8000 ; transmission complete notification or ecx, 1 shl 31 ; test byte [device.has_hwcksm], 0xff ; jz @f ; or ecx, (1 shl 26) ; set AddTcpChecksum ;@@: mov [esi+dpd.frame_start_hdr], ecx DEBUGF 1,"DPD: lin=%x phys=%x len=%x start hdr=%x\n", [esi+dpd.realaddr]:8, [esi+dpd.frag_addr]:8, [esi+dpd.frag_len]:8, [esi+dpd.frame_start_hdr]:8 ; calculate physical address of dpd mov eax, esi GetRealAddr cmp [device.dn_list_ptr_cleared], 0 jz .add_to_list ; write Dn_List_Ptr DEBUGF 1,"DPD phys addr=%x\n", eax set_io 0 set_io REG_DN_LIST_PTR out dx, eax jmp .finish .add_to_list: DEBUGF 1,"Adding To list\n" push eax ; DnStall set_io 0 set_io REG_COMMAND mov ax, ((110b shl 11)+2) out dx, ax ; wait for DnStall to complete DEBUGF 1,"Waiting for DnStall\n" mov ecx, 6000 .wait_for_stall: in ax, dx ; read REG_INT_STATUS test ah, 10000b jz .dnstall_ok dec ecx jnz .wait_for_stall .dnstall_ok: DEBUGF 1,"DnStall ok!\n" mov ecx, [device.prev_dpd] mov [ecx+dpd.next_ptr], eax set_io 0 set_io REG_DN_LIST_PTR in eax, dx test eax, eax pop eax jnz .dnunstall ; if Dn_List_Ptr has been cleared fill it up DEBUGF 1,"DnList Ptr has been cleared\n" out dx, eax .dnunstall: ; DnUnStall set_io 0 set_io REG_COMMAND mov ax, ((110b shl 11)+3) out dx, ax .finish: mov [device.prev_dpd], esi xor eax, eax ret 8 .fail: stdcall KernelFree, [esp+4] ret 8 ;--------------------------------- ; Write MAC align 4 write_mac: DEBUGF 1,"Writing mac\n" set_io 0 set_io REG_COMMAND ; switch to register window 2 mov ax, SELECT_REGISTER_WINDOW+2 out dx, ax ; write MAC addres back into the station address registers set_io REG_STATION_ADDRESS_LO lea esi, [device.mac] outsw inc dx inc dx outsw inc dx inc dx outsw ;---------------------------- ; Read MAC align 4 read_mac: set_io 0 set_io REG_COMMAND ; switch to register window 2 mov ax, SELECT_REGISTER_WINDOW+2 out dx, ax ; write MAC addres back into the station address registers set_io REG_STATION_ADDRESS_LO lea edi, [device.mac] insw inc dx inc dx insw inc dx inc dx insw DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2 ret ;------------------------------------ ; Read MAC from eeprom align 4 read_mac_eeprom: ; Tested - ok DEBUGF 1,"Reading mac from eeprom\n" ; read MAC from eeprom mov ecx, 3 .mac_loop: lea ax, [EEPROM_REG_OEM_NODE_ADDR+ecx-1] push ecx call read_eeprom pop ecx xchg ah, al ; htons mov word [device.mac+ecx*2-2], ax loop .mac_loop DEBUGF 1,"%x-%x-%x-%x-%x-%x\n",[device.mac]:2,[device.mac+1]:2,[device.mac+2]:2,[device.mac+3]:2,[device.mac+4]:2,[device.mac+5]:2 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Vortex Interrupt handler ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 int_vortex: DEBUGF 1,"vortex IRQ %x ",eax:2 ; find pointer of device wich made IRQ occur mov esi, VORTEX_LIST mov ecx, [VORTEX_DEVICES] test ecx, ecx jz .fail .nextdevice: mov ebx, dword [esi] set_io 0 set_io REG_INT_STATUS in ax, dx ;; and ax, INT_MASK jnz .got_it add esi, 4 test ax , ax jnz .got_it loop .nextdevice .fail: ret .got_it: DEBUGF 1,"Device: %x Status: %x ",ebx,eax:4 test ax, RxComplete jz .noRX set_io 0 .rx_status_loop: ; examine RxStatus set_io REG_RX_STATUS in ax, dx test ax, ax jz .finish test ah, 0x80 ; rxIncomplete jnz .finish test ah, 0x40 jz .check_length ; discard the top frame received advancing the next one set_io REG_COMMAND mov ax, (01000b shl 11) out dx, ax jmp .rx_status_loop .check_length: and eax, 0x1fff cmp eax, MAX_ETH_PKT_SIZE ja .discard_frame ; frame is too long discard it .check_dma: mov ecx, eax ; switch to register window 7 set_io 0 set_io REG_COMMAND mov ax, SELECT_REGISTER_WINDOW+7 out dx, ax ; check for master operation in progress set_io REG_MASTER_STATUS in ax, dx test ah, 0x80 jnz .finish .read_frame: ; program buffer address to read in push ecx stdcall KernelAlloc, MAX_ETH_FRAME_SIZE pop ecx test eax, eax jz .finish push .discard_frame push ecx push eax ; zero_to_dma eax set_io REG_MASTER_ADDRESS out dx, eax ; program frame length set_io REG_MASTER_LEN mov ax, 1560 out dx, ax ; start DMA Up set_io REG_COMMAND mov ax, (10100b shl 11) ; StartDMAUp out dx, ax ; check for master operation in progress set_io REG_MASTER_STATUS ; TODO: use timeout and reset after timeout expired .dma_loop: in ax, dx test ah, 0x80 jnz .dma_loop ; registrate the received packet to kernel jmp EthReceiver ; discard the top frame received .discard_frame: set_io 0 set_io REG_COMMAND mov ax, (01000b shl 11) out dx, ax .finish: .noRX: test ax, DMADone jz .noDMA push ax set_io 0 set_io 12 in ax, dx test ax, 0x1000 jz .nodmaclear mov ax, 0x1000 out dx, ax .nodmaclear: pop ax DEBUGF 1, "DMA Done!\n", cx .noDMA: .ACK: set_io 0 set_io REG_COMMAND mov ax, AckIntr + IntReq + IntLatch out dx, ax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Boomerang Interrupt handler ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 int_boomerang: DEBUGF 1,"\nIRQ %x Boomerang\n",eax:2 ; find pointer of device wich made IRQ occur mov esi, BOOMERANG_LIST mov ecx, [BOOMERANG_DEVICES] test ecx, ecx jz .fail .nextdevice: mov ebx, dword[esi] set_io 0 set_io REG_INT_STATUS in ax, dx test ax, IntLatch jnz .got_it add esi, 4 test ax , ax jnz .got_it dec ecx jnz .nextdevice .fail: DEBUGF 1,"Failed!\n" ret .got_it: DEBUGF 1,"Device: %x Status: %x ", ebx, eax push ax ; disable all INTS set_io REG_COMMAND mov ax, SetIntrEnb out dx, ax ;-------------------------------------------------------------------------- test word[esp], UpComplete jz .noRX push ebx .receive: DEBUGF 1,"UpComplete\n" ; check if packet is uploaded mov esi, [device.curr_upd] test byte [esi+upd.pkt_status+1], 0x80 ; upPktComplete jz .finish DEBUGF 1, "Current upd: %x\n", esi ; packet is uploaded check for any error .check_error: test byte [esi+upd.pkt_status+1], 0x40 ; upError jz .copy_packet_length DEBUGF 1,"Error in packet\n" and [esi+upd.pkt_status], 0 ; mark packet as read jmp .finish .copy_packet_length: mov ecx, [esi+upd.pkt_status] and ecx, 0x1fff ; cmp ecx, MAX_ETH_PKT_SIZE ; jbe .copy_packet ; and [esi+upd.pkt_status], 0 ; jmp .finish ; .copy_packet: DEBUGF 1, "Received %u bytes in buffer %x\n", ecx, [esi+upd.realaddr]:8 push dword .loop ;.finish push ecx push [esi+upd.realaddr] ; update statistics inc [device.packets_rx] add dword [device.bytes_rx], ecx adc dword [device.bytes_rx + 4], 0 ; update UPD (Alloc new buffer for next packet) stdcall KernelAlloc, MAX_ETH_FRAME_SIZE mov [esi + upd.realaddr], eax GetRealAddr mov [esi + upd.frag_addr], eax and [esi + upd.pkt_status], 0 mov [esi + upd.frag_len], MAX_ETH_FRAME_SIZE or (1 shl 31) ; Update UPD pointer add esi, upd.size lea ecx, [device.upd_buffer+(NUM_RX_DESC)*upd.size] cmp esi, ecx jl @f lea esi, [device.upd_buffer] @@: mov [device.curr_upd], esi DEBUGF 1, "Next upd: %x\n", esi jmp EthReceiver .loop: mov ebx, [esp] jmp .receive .finish: pop ebx ; check if the NIC is in the upStall state set_io 0 set_io REG_UP_PKT_STATUS in eax, dx test ah, 0x20 ; UpStalled jz .noUpUnStall DEBUGF 1, "upUnStalling\n" ; issue upUnStall command set_io REG_COMMAND mov ax, ((11b shl 12)+1) ; upUnStall out dx, ax ;;;; FIXME: make upunstall work .noUpUnStall: .noRX: test word[esp], DownComplete jz .noTX DEBUGF 1, "Downcomplete!\n" mov ecx, NUM_TX_DESC lea esi, [device.dpd_buffer] .txloop: test [esi+dpd.frame_start_hdr], 1 shl 31 jz .maybenext and [esi+dpd.frame_start_hdr], 0 push ecx stdcall KernelFree, [esi+dpd.realaddr] pop ecx .maybenext: add esi, dpd.size dec ecx jnz .txloop .noTX: pop ax set_io 0 set_io REG_COMMAND or ax, AckIntr out dx, ax set_io REG_INT_STATUS in ax, dx test ax, S_5_INTS jnz .got_it ;re-enable ints set_io REG_COMMAND mov ax, SetIntrEnb + S_5_INTS out dx, ax ret ; End of code align 4 ; Place all initialised data here macro strtbl name, [string] { common label name dword forward local label dd label forward label db string, 0 } VORTEX_DEVICES dd 0 BOOMERANG_DEVICES dd 0 version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF) my_service db '3C59X',0 ; max 16 chars include zero strtbl link_str, \ "No valid link type detected", \ "10BASE-T half duplex", \ "10BASE-T full-duplex", \ "100BASE-TX half duplex", \ "100BASE-TX full duplex", \ "100BASE-T4", \ "100BASE-FX", \ "10Mbps AUI", \ "10Mbps COAX (BNC)", \ "miiDevice - not supported" strtbl hw_str, \ "3c590 Vortex 10Mbps", \ "3c592 EISA 10Mbps Demon/Vortex", \ "3c597 EISA Fast Demon/Vortex", \ "3c595 Vortex 100baseTx", \ "3c595 Vortex 100baseT4", \ "3c595 Vortex 100base-MII", \ "3c900 Boomerang 10baseT", \ "3c900 Boomerang 10Mbps Combo", \ "3c900 Cyclone 10Mbps TPO", \ "3c900 Cyclone 10Mbps Combo", \ "3c900 Cyclone 10Mbps TPC", \ "3c900B-FL Cyclone 10base-FL", \ "3c905 Boomerang 100baseTx", \ "3c905 Boomerang 100baseT4", \ "3c905B Cyclone 100baseTx", \ "3c905B Cyclone 10/100/BNC", \ "3c905B-FX Cyclone 100baseFx", \ "3c905C Tornado", \ "3c980 Cyclone", \ "3c982 Dual Port Server Cyclone", \ "3cSOHO100-TX Hurricane", \ "3c555 Laptop Hurricane", \ "3c556 Laptop Tornado", \ "3c556B Laptop Hurricane", \ "3c575 [Megahertz] 10/100 LAN CardBus", \ "3c575 Boomerang CardBus", \ "3CCFE575BT Cyclone CardBus", \ "3CCFE575CT Tornado CardBus", \ "3CCFE656 Cyclone CardBus", \ "3CCFEM656B Cyclone+Winmodem CardBus", \ "3CXFEM656C Tornado+Winmodem CardBus", \ "3c450 HomePNA Tornado", \ "3c920 Tornado", \ "3c982 Hydra Dual Port A", \ "3c982 Hydra Dual Port B", \ "3c905B-T4", \ "3c920B-EMB-WNM Tornado" align 4 hw_versions: dw 0x5900, IS_VORTEX ; 3c590 Vortex 10Mbps dw 0x5920, IS_VORTEX ; 3c592 EISA 10Mbps Demon/Vortex dw 0x5970, IS_VORTEX ; 3c597 EISA Fast Demon/Vortex dw 0x5950, IS_VORTEX ; 3c595 Vortex 100baseTx dw 0x5951, IS_VORTEX ; 3c595 Vortex 100baseT4 dw 0x5952, IS_VORTEX ; 3c595 Vortex 100base-MII dw 0x9000, IS_BOOMERANG ; 3c900 Boomerang 10baseT dw 0x9001, IS_BOOMERANG ; 3c900 Boomerang 10Mbps Combo dw 0x9004, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c900 Cyclone 10Mbps TPO dw 0x9005, IS_CYCLONE or HAS_HWCKSM ; 3c900 Cyclone 10Mbps Combo dw 0x9006, IS_CYCLONE or HAS_HWCKSM ; 3c900 Cyclone 10Mbps TPC dw 0x900A, IS_CYCLONE or HAS_HWCKSM ; 3c900B-FL Cyclone 10base-FL dw 0x9050, IS_BOOMERANG or HAS_MII ; 3c905 Boomerang 100baseTx dw 0x9051, IS_BOOMERANG or HAS_MII ; 3c905 Boomerang 100baseT4 dw 0x9055, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM or EXTRA_PREAMBLE ; 3c905B Cyclone 100baseTx dw 0x9058, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c905B Cyclone 10/100/BNC dw 0x905A, IS_CYCLONE or HAS_HWCKSM ; 3c905B-FX Cyclone 100baseFx dw 0x9200, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c905C Tornado dw 0x9800, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3c980 Cyclone dw 0x9805, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c982 Dual Port Server Cyclone dw 0x7646, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM ; 3cSOHO100-TX Hurricane dw 0x5055, IS_CYCLONE or EEPROM_8BIT or HAS_HWCKSM ; 3c555 Laptop Hurricane dw 0x6055, IS_TORNADO or HAS_NWAY or EEPROM_8BIT or HAS_CB_FNS or INVERT_MII_PWR or HAS_HWCKSM ; 3c556 Laptop Tornado dw 0x6056, IS_TORNADO or HAS_NWAY or EEPROM_OFFSET or HAS_CB_FNS or INVERT_MII_PWR or HAS_HWCKSM ; 3c556B Laptop Hurricane dw 0x5b57, IS_BOOMERANG or HAS_MII or EEPROM_8BIT ; 3c575 [Megahertz] 10/100 LAN CardBus dw 0x5057, IS_BOOMERANG or HAS_MII or EEPROM_8BIT ; 3c575 Boomerang CardBus dw 0x5157, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFE575BT Cyclone CardBus dw 0x5257, IS_TORNADO or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or MAX_COLLISION_RESET or HAS_HWCKSM ; 3CCFE575CT Tornado CardBus dw 0x6560, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFE656 Cyclone CardBus dw 0x6562, IS_CYCLONE or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or INVERT_LED_PWR or HAS_HWCKSM ; 3CCFEM656B Cyclone+Winmodem CardBus dw 0x6564, IS_TORNADO or HAS_NWAY or HAS_CB_FNS or EEPROM_8BIT or INVERT_MII_PWR or MAX_COLLISION_RESET or HAS_HWCKSM ; 3CXFEM656C Tornado+Winmodem CardBus dw 0x4500, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c450 HomePNA Tornado dw 0x9201, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c920 Tornado dw 0x1201, IS_TORNADO or HAS_HWCKSM or HAS_NWAY ; 3c982 Hydra Dual Port A dw 0x1202, IS_TORNADO or HAS_HWCKSM or HAS_NWAY ; 3c982 Hydra Dual Port B dw 0x9056, IS_CYCLONE or HAS_NWAY or HAS_HWCKSM or EXTRA_PREAMBLE ; 3c905B-T4 dw 0x9210, IS_TORNADO or HAS_NWAY or HAS_HWCKSM ; 3c920B-EMB-WNM Tornado HW_VERSIONS_SIZE = $ - hw_versions include_debug_strings ; All data wich FDO uses will be included here section '.data' data readable writable align 16 ; place all uninitialized data place here VORTEX_LIST rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling BOOMERANG_LIST rd MAX_DEVICES