$Revision$ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; PCNET32.INC ;; ;; ;; ;; Ethernet driver for Menuet OS ;; ;; ;; ;; Version 1.0 31 July 2004 ;; ;; ;; ;; This driver is based on the PCNet32 driver from ;; ;; the etherboot 5.0.6 project. The copyright statement is ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; ;; Version 2, June 1991 ;; ;; ;; ;; remaining parts Copyright 2004 Jarek Pelczar, ;; ;; jpelczar@interia.pl ;; ;; ;; ;; See file COPYING for details ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;macro PutStr X ;{ ; local .__xyz1 ; local .__xyz2 ; push esi ; mov esi,.__xyz1 ; call sys_msg_board_str ; push eax ; mov eax,1 ; call delay_hs ; pop eax ; jmp .__xyz2 ;.__xyz1: ; db X ; db 13,10,0 ;.__xyz2: ; pop esi ;} PCNET32_PORT_AUI equ 0x00 PCNET32_PORT_10BT equ 0x01 PCNET32_PORT_GPSI equ 0x02 PCNET32_PORT_MII equ 0x03 PCNET32_PORT_PORTSEL equ 0x03 PCNET32_PORT_ASEL equ 0x04 PCNET32_PORT_100 equ 0x40 PCNET32_PORT_FD equ 0x80 PCNET32_DMA_MASK equ 0xffffffff PCNET32_LOG_TX_BUFFERS equ 1 PCNET32_LOG_RX_BUFFERS equ 2 PCNET32_TX_RING_SIZE equ (1 shl PCNET32_LOG_TX_BUFFERS) PCNET32_TX_RING_MOD_MASK equ (PCNET32_TX_RING_SIZE-1) PCNET32_TX_RING_LEN_BITS equ 0 PCNET32_RX_RING_SIZE equ (1 shl PCNET32_LOG_RX_BUFFERS) PCNET32_RX_RING_MOD_MASK equ (PCNET32_RX_RING_SIZE-1) PCNET32_RX_RING_LEN_BITS equ (PCNET32_LOG_RX_BUFFERS shl 4) PCNET32_PKT_BUF_SZ equ 1544 PCNET32_PKT_BUF_SZ_NEG equ 0xf9f8 pcnet32_txb equ (eth_data_start) pcnet32_rxb equ ((pcnet32_txb+(PCNET32_PKT_BUF_SZ*PCNET32_TX_RING_SIZE)+0xf) and 0xfffffff0) pcnet32_tx_ring equ ((pcnet32_rxb+(PCNET32_PKT_BUF_SZ*PCNET32_RX_RING_SIZE)+0xf) and 0xfffffff0) pcnet32_rx_ring equ ((pcnet32_tx_ring+(16*PCNET32_TX_RING_SIZE)+0xf) and 0xfffffff0) virtual at ((pcnet32_rx_ring+(16*PCNET32_RX_RING_SIZE)+0xf) and 0xfffffff0) pcnet32_private: .mode dw ? .tlen_rlen dw ? .phys_addr db ?,?,?,?,?,? .reserved dw ? .filter dd ?,? .rx_ring dd ? .tx_ring dd ? .cur_rx dd ? .cur_tx dd ? .dirty_rx dd ? .dirty_tx dd ? .tx_full db ? .options dd ? .full_duplex db ? .chip_version dd ? .mii db ? .ltint db ? .dxsuflo db ? .fset db ? .fdx db ? end virtual virtual at 0 pcnet32_rx_head: .base dd ? .buf_length dw ? .status dw ? .msg_length dd ? .reserved dd ? end virtual virtual at 0 pcnet32_tx_head: .base dd ? .length dw ? .status dw ? .misc dd ? .reserved dd ? end virtual uglobal pcnet32_access: .read_csr dd ? .write_csr dd ? .read_bcr dd ? .write_bcr dd ? .read_rap dd ? .write_rap dd ? .reset dd ? endg iglobal pcnet32_options_mapping: dd PCNET32_PORT_ASEL ; 0 Auto-select dd PCNET32_PORT_AUI ; 1 BNC/AUI dd PCNET32_PORT_AUI ; 2 AUI/BNC dd PCNET32_PORT_ASEL ; 3 not supported dd PCNET32_PORT_10BT or PCNET32_PORT_FD ; 4 10baseT-FD dd PCNET32_PORT_ASEL ; 5 not supported dd PCNET32_PORT_ASEL ; 6 not supported dd PCNET32_PORT_ASEL ; 7 not supported dd PCNET32_PORT_ASEL ; 8 not supported dd PCNET32_PORT_MII ; 9 MII 10baseT dd PCNET32_PORT_MII or PCNET32_PORT_FD ; 10 MII 10baseT-FD dd PCNET32_PORT_MII ; 11 MII (autosel) dd PCNET32_PORT_10BT ; 12 10BaseT dd PCNET32_PORT_MII or PCNET32_PORT_100 ; 13 MII 100BaseTx dd PCNET32_PORT_MII or PCNET32_PORT_100 or PCNET32_PORT_FD ; 14 MII 100BaseTx-FD dd PCNET32_PORT_ASEL ; 15 not supported endg PCNET32_WIO_RDP equ 0x10 PCNET32_WIO_RAP equ 0x12 PCNET32_WIO_RESET equ 0x14 PCNET32_WIO_BDP equ 0x16 PCNET32_DWIO_RDP equ 0x10 PCNET32_DWIO_RAP equ 0x14 PCNET32_DWIO_RESET equ 0x18 PCNET32_DWIO_BDP equ 0x1C PCNET32_TOTAL_SIZE equ 0x20 ; ebx - index ; return: ; eax - data pcnet32_wio_read_csr: push edx lea edx,[ebp+PCNET32_WIO_RAP] mov ax,bx out dx,ax lea edx,[ebp+PCNET32_WIO_RDP] in ax,dx and eax,0xffff pop edx ret ; eax - data ; ebx - index pcnet32_wio_write_csr: push edx lea edx,[ebp+PCNET32_WIO_RAP] xchg eax,ebx out dx,ax xchg eax,ebx lea edx,[ebp+PCNET32_WIO_RDP] out dx,ax pop edx ret ; ebx - index ; return: ; eax - data pcnet32_wio_read_bcr: push edx lea edx,[ebp+PCNET32_WIO_RAP] mov ax,bx out dx,ax lea edx,[ebp+PCNET32_WIO_BDP] in ax,dx and eax,0xffff pop edx ret ; eax - data ; ebx - index pcnet32_wio_write_bcr: push edx lea edx,[ebp+PCNET32_WIO_RAP] xchg eax,ebx out dx,ax xchg eax,ebx lea edx,[ebp+PCNET32_WIO_BDP] out dx,ax pop edx ret pcnet32_wio_read_rap: push edx lea edx,[ebp+PCNET32_WIO_RAP] in ax,dx and eax,0xffff pop edx ret ; eax - val pcnet32_wio_write_rap: push edx lea edx,[ebp+PCNET32_WIO_RAP] out dx,ax pop edx ret pcnet32_wio_reset: push edx push eax lea edx,[ebp+PCNET32_WIO_RESET] in ax,dx pop eax pop edx ret pcnet32_wio_check: push edx mov ax,88 lea edx,[ebp+PCNET32_WIO_RAP] out dx,ax nop nop in ax,dx cmp ax,88 sete al pop edx ret iglobal pcnet32_wio: dd pcnet32_wio_read_csr dd pcnet32_wio_write_csr dd pcnet32_wio_read_bcr dd pcnet32_wio_write_bcr dd pcnet32_wio_read_rap dd pcnet32_wio_write_rap dd pcnet32_wio_reset endg ; ebx - index ; return: ; eax - data pcnet32_dwio_read_csr: push edx lea edx,[ebp+PCNET32_DWIO_RAP] mov ebx,eax out dx,eax lea edx,[ebp+PCNET32_DWIO_RDP] in eax,dx and eax,0xffff pop edx ret ; ebx - index ; eax - data pcnet32_dwio_write_csr: push edx lea edx,[ebp+PCNET32_DWIO_RAP] xchg eax,ebx out dx,eax lea edx,[ebp+PCNET32_DWIO_RDP] xchg eax,ebx out dx,eax pop edx ret ; ebx - index ; return: ; eax - data pcnet32_dwio_read_bcr: push edx lea edx,[ebp+PCNET32_DWIO_RAP] mov ebx,eax out dx,eax lea edx,[ebp+PCNET32_DWIO_BDP] in eax,dx and eax,0xffff pop edx ret ; ebx - index ; eax - data pcnet32_dwio_write_bcr: push edx lea edx,[ebp+PCNET32_DWIO_RAP] xchg eax,ebx out dx,eax lea edx,[ebp+PCNET32_DWIO_BDP] xchg eax,ebx out dx,eax pop edx ret pcnet32_dwio_read_rap: push edx lea edx,[ebp+PCNET32_DWIO_RAP] in eax,dx and eax,0xffff pop edx ret ; eax - val pcnet32_dwio_write_rap: push edx lea edx,[ebp+PCNET32_DWIO_RAP] out dx,eax pop edx ret pcnet32_dwio_reset: push edx push eax lea edx,[ebp+PCNET32_DWIO_RESET] in eax,dx pop eax pop edx ret pcnet32_dwio_check: push edx lea edx,[PCNET32_DWIO_RAP] mov eax,88 out dx,eax nop nop in eax,dx and eax,0xffff cmp eax,88 sete al pop edx ret iglobal pcnet32_dwio: dd pcnet32_dwio_read_csr dd pcnet32_dwio_write_csr dd pcnet32_dwio_read_bcr dd pcnet32_dwio_write_bcr dd pcnet32_dwio_read_rap dd pcnet32_dwio_write_rap dd pcnet32_dwio_reset endg pcnet32_init_ring: mov [pcnet32_private.tx_full],0 mov [pcnet32_private.cur_rx],0 mov [pcnet32_private.cur_tx],0 mov [pcnet32_private.dirty_rx],0 mov [pcnet32_private.dirty_tx],0 mov edi,pcnet32_rx_ring mov ecx,PCNET32_RX_RING_SIZE mov ebx,pcnet32_rxb .rx_init: mov [edi+pcnet32_rx_head.base],ebx mov [edi+pcnet32_rx_head.buf_length],word PCNET32_PKT_BUF_SZ_NEG mov [edi+pcnet32_rx_head.status],word 0x8000 add ebx,PCNET32_PKT_BUF_SZ ; inc ebx add edi,16 loop .rx_init mov edi,pcnet32_tx_ring mov ecx,PCNET32_TX_RING_SIZE .tx_init: mov [edi+pcnet32_tx_head.base],dword 0 mov [edi+pcnet32_tx_head.status],word 0 add edi,16 loop .tx_init mov [pcnet32_private.tlen_rlen],(PCNET32_TX_RING_LEN_BITS or PCNET32_RX_RING_LEN_BITS) mov esi,node_addr mov edi,pcnet32_private.phys_addr cld movsd movsw mov dword [pcnet32_private.rx_ring],pcnet32_rx_ring mov dword [pcnet32_private.tx_ring],pcnet32_tx_ring ret pcnet32_reset: ; Reset PCNET32 mov ebp,[io_addr] call dword [pcnet32_access.reset] ; set 32bit mode mov ebx,20 mov eax,2 call dword [pcnet32_access.write_bcr] ; set/reset autoselect bit mov ebx,2 call dword [pcnet32_access.read_bcr] and eax,not 2 test [pcnet32_private.options],PCNET32_PORT_ASEL jz .L1 or eax,2 .L1: call dword [pcnet32_access.write_bcr] ; Handle full duplex setting cmp byte [pcnet32_private.full_duplex],0 je .L2 mov ebx,9 call dword [pcnet32_access.read_bcr] and eax,not 3 test [pcnet32_private.options],PCNET32_PORT_FD jz .L3 or eax,1 cmp [pcnet32_private.options],PCNET32_PORT_FD or PCNET32_PORT_AUI jne .L4 or eax,2 jmp .L4 .L3: test [pcnet32_private.options],PCNET32_PORT_ASEL jz .L4 cmp [pcnet32_private.chip_version],0x2627 jne .L4 or eax,3 .L4: mov ebx,9 call dword [pcnet32_access.write_bcr] .L2: ; set/reset GPSI bit mov ebx,124 call dword [pcnet32_access.read_csr] mov ecx,[pcnet32_private.options] and ecx,PCNET32_PORT_PORTSEL cmp ecx,PCNET32_PORT_GPSI jne .L5 or eax,0x10 .L5: call dword [pcnet32_access.write_csr] cmp [pcnet32_private.mii],0 je .L6 test [pcnet32_private.options],PCNET32_PORT_ASEL jnz .L6 mov ebx,32 call dword [pcnet32_access.read_bcr] and eax,not 0x38 test [pcnet32_private.options],PCNET32_PORT_FD jz .L7 or eax,0x10 .L7: test [pcnet32_private.options],PCNET32_PORT_100 jz .L8 or eax,0x08 .L8: call dword [pcnet32_access.write_bcr] jmp .L9 .L6: test [pcnet32_private.options],PCNET32_PORT_ASEL jz .L9 mov ebx,32 ; PutStr "ASEL, enable auto-negotiation" call dword [pcnet32_access.read_bcr] and eax,not 0x98 or eax,0x20 call dword [pcnet32_access.write_bcr] .L9: cmp [pcnet32_private.ltint],0 je .L10 mov ebx,5 call dword [pcnet32_access.read_csr] or eax,(1 shl 14) call dword [pcnet32_access.write_csr] .L10: mov eax,[pcnet32_private.options] and eax,PCNET32_PORT_PORTSEL shl eax,7 mov [pcnet32_private.mode],ax mov [pcnet32_private.filter],dword 0xffffffff mov [pcnet32_private.filter+4],dword 0xffffffff call pcnet32_init_ring mov ebx,1 mov eax,pcnet32_private and eax,0xffff call dword [pcnet32_access.write_csr] mov eax,pcnet32_private mov ebx,2 shr eax,16 call dword [pcnet32_access.write_csr] mov ebx,4 mov eax,0x0915 call dword [pcnet32_access.write_csr] mov ebx,0 mov eax,1 call dword [pcnet32_access.write_csr] mov ecx,100 .L11: xor ebx,ebx call dword [pcnet32_access.read_csr] test ax,0x100 jnz .L12 loop .L11 .L12: ; PutStr "hardware reset" xor ebx,ebx mov eax,0x0002 call dword [pcnet32_access.write_csr] xor ebx,ebx call dword [pcnet32_access.read_csr] ; PutStr "PCNET reset complete" ret pcnet32_adjust_pci_device: ;*******Get current setting************************ mov al, 2 ;read a word mov bh, [pci_dev] mov ah, [pci_bus] mov bl, 0x04 ;from command Register call pci_read_reg ;******see if its already set as bus master******** mov bx, ax and bx,5 cmp bx,5 je pcnet32_adjust_pci_device_Latency ;******Make card a bus master******* mov cx, ax ;value to write mov bh, [pci_dev] mov al, 2 ;write a word or cx,5 mov ah, [pci_bus] mov bl, 0x04 ;to command register call pci_write_reg ;******Check latency setting*********** pcnet32_adjust_pci_device_Latency: ;*******Get current latency setting************************ ; mov al, 1 ;read a byte ; mov bh, [pci_dev] ; mov ah, [pci_bus] ; mov bl, 0x0D ;from Lantency Timer Register ; call pci_read_reg ;******see if its aat least 64 clocks******** ; cmp ax,64 ; jge pcnet32_adjust_pci_device_Done ;******Set latency to 32 clocks******* ; mov cx, 64 ;value to write ; mov bh, [pci_dev] ; mov al, 1 ;write a byte ; mov ah, [pci_bus] ; mov bl, 0x0D ;to Lantency Timer Register ; call pci_write_reg ;******Check latency setting*********** pcnet32_adjust_pci_device_Done: ret pcnet32_probe: mov ebp,[io_addr] call pcnet32_wio_reset xor ebx,ebx call pcnet32_wio_read_csr cmp eax,4 jne .try_dwio call pcnet32_wio_check and al,al jz .try_dwio ; PutStr "Using WIO" mov esi,pcnet32_wio jmp .L1 .try_dwio: call pcnet32_dwio_reset xor ebx,ebx call pcnet32_dwio_read_csr cmp eax,4 jne .no_dev call pcnet32_dwio_check and al,al jz .no_dev ; PutStr "Using DWIO" mov esi,pcnet32_dwio jmp .L1 .no_dev: ; PutStr "PCNET32 not found" ret .L1: mov edi,pcnet32_access mov ecx,7 cld rep movsd mov ebx,88 call dword [pcnet32_access.read_csr] mov ecx,eax mov ebx,89 call dword [pcnet32_access.read_csr] shl eax,16 or eax,ecx mov ecx,eax and ecx,0xfff cmp ecx,3 jne .no_dev shr eax,12 and eax,0xffff mov [pcnet32_private.chip_version],eax ; PutStr "PCNET32 chip version OK" mov [pcnet32_private.fdx],0 mov [pcnet32_private.mii],0 mov [pcnet32_private.fset],0 mov [pcnet32_private.dxsuflo],0 mov [pcnet32_private.ltint],0 mov eax,[pcnet32_private.chip_version] cmp eax,0x2420 je .L2 cmp eax,0x2430 je .L3 cmp eax,0x2621 je .L4 cmp eax,0x2623 je .L5 cmp eax,0x2624 je .L6 cmp eax,0x2625 je .L7 cmp eax,0x2626 je .L8 cmp eax,0x2627 je .L9 ; PutStr "Invalid chip rev" jmp .no_dev .L2: ; PutStr "PCnet/PCI 79C970" jmp .L10 .L3: ; PutStr "PCnet/PCI 79C970" jmp .L10 .L4: ; PutStr "PCnet/PCI II 79C970A" mov [pcnet32_private.fdx],1 jmp .L10 .L5: ; PutStr "PCnet/FAST 79C971" mov [pcnet32_private.fdx],1 mov [pcnet32_private.mii],1 mov [pcnet32_private.fset],1 mov [pcnet32_private.ltint],1 jmp .L10 .L6: ; PutStr "PCnet/FAST+ 79C972" mov [pcnet32_private.fdx],1 mov [pcnet32_private.mii],1 mov [pcnet32_private.fset],1 jmp .L10 .L7: ; PutStr "PCnet/FAST III 79C973" mov [pcnet32_private.fdx],1 mov [pcnet32_private.mii],1 jmp .L10 .L8: ; PutStr "PCnet/Home 79C978" mov [pcnet32_private.fdx],1 mov ebx,49 call dword [pcnet32_access.read_bcr] call dword [pcnet32_access.write_bcr] jmp .L10 .L9: ; PutStr "PCnet/FAST III 79C975" mov [pcnet32_private.fdx],1 mov [pcnet32_private.mii],1 .L10: cmp [pcnet32_private.fset],1 jne .L11 mov ebx,18 call dword [pcnet32_access.read_bcr] or eax,0x800 call dword [pcnet32_access.write_bcr] mov ebx,80 call dword [pcnet32_access.read_csr] and eax,0xc00 or eax,0xc00 call dword [pcnet32_access.write_csr] mov [pcnet32_private.dxsuflo],1 mov [pcnet32_private.ltint],1 .L11: ; read MAC mov edi,node_addr mov edx,ebp mov ecx,6 .Lmac: in al,dx stosb inc edx loop .Lmac ; PutStr "MAC read" call pcnet32_adjust_pci_device ; PutStr "PCI done" mov eax,PCNET32_PORT_ASEL mov [pcnet32_private.options],eax mov [pcnet32_private.mode],word 0x0003 mov [pcnet32_private.tlen_rlen],word (PCNET32_TX_RING_LEN_BITS or PCNET32_RX_RING_LEN_BITS) mov esi,node_addr mov edi,pcnet32_private.phys_addr cld movsd movsw mov [pcnet32_private.filter],dword 0 mov [pcnet32_private.filter+4],dword 0 mov dword [pcnet32_private.rx_ring],pcnet32_rx_ring mov dword [pcnet32_private.tx_ring],pcnet32_tx_ring ; PutStr "Switching to 32" mov ebx,20 mov eax,2 call dword [pcnet32_access.write_bcr] mov ebx,1 mov eax,(pcnet32_private and 0xffff) call dword [pcnet32_access.write_csr] mov ebx,2 mov eax,(pcnet32_private shr 16) and 0xffff call dword [pcnet32_access.write_csr] mov ebx,0 mov eax,1 call dword [pcnet32_access.write_csr] mov esi,1 call delay_ms call pcnet32_reset mov eax, [pci_data] mov [eth_status], eax ret pcnet32_poll: xor eax,eax mov [eth_rx_data_len],ax mov eax,[pcnet32_private.cur_rx] and eax,PCNET32_RX_RING_MOD_MASK mov ebx,eax imul esi,eax,PCNET32_PKT_BUF_SZ add esi,pcnet32_rxb shl ebx,4 add ebx,pcnet32_rx_ring mov cx,[ebx+pcnet32_rx_head.status] test cx,0x8000 jnz .L1 cmp ch,3 jne .L1 ; PutStr "PCNETRX" mov ecx,[ebx+pcnet32_rx_head.msg_length] and ecx,0xfff sub ecx,4 mov [eth_rx_data_len],cx push ecx shr ecx,2 mov edi,Ether_buffer cld rep movsd pop ecx and ecx,3 rep movsb mov [ebx+pcnet32_rx_head.buf_length],word PCNET32_PKT_BUF_SZ_NEG or [ebx+pcnet32_rx_head.status],word 0x8000 inc [pcnet32_private.cur_rx] .L1: ret ; Pointer to 48 bit destination address in edi ; Type of packet in bx ; size of packet in ecx ; pointer to packet data in esi pcnet32_xmit: push edi push esi push ebx push ecx ; PutStr "PCNETTX" mov esi,edi mov edi,[pcnet32_private.cur_tx] imul edi,PCNET32_PKT_BUF_SZ add edi,pcnet32_txb ; edi=ptxb mov eax,edi cld ; copy MAC movsd movsw mov esi,node_addr cld movsd movsw mov [edi],bx add edi,2 mov esi,[esp+8] mov ecx,[esp] push ecx shr ecx,2 cld rep movsd pop ecx and ecx,3 rep movsb ; mov ecx,[esp] ; add ecx,14 ; ETH_HLEN ; xor eax,eax ; pad to min length (60=ETH_ZLEN) ; cmp ecx,60 ; jae .L1 ; sub ecx,60 ; cld ; rep stosb ;.L1: mov edi,pcnet32_tx_ring+0 ; entry=0 mov ecx,[esp] add ecx,14 cmp cx,60 jae .L1 mov cx,60 .L1: neg cx mov [edi+pcnet32_tx_head.length],cx mov [edi+pcnet32_tx_head.misc],dword 0 mov [edi+pcnet32_tx_head.base],eax mov [edi+pcnet32_tx_head.status],word 0x8300 ; trigger an immediate send poll mov ebx,0 mov eax,0x0008 ; 0x0048 mov ebp,[io_addr] call dword [pcnet32_access.write_csr] mov dword [pcnet32_private.cur_tx],0 ; wait for TX to complete mov ecx,[timer_ticks];[0xfdf0] add ecx,100 .L2: mov ax,[edi+pcnet32_tx_head.status] test ax,0x8000 jz .L3 cmp ecx,[timer_ticks];[0xfdf0] jb .L4 mov esi,10 call delay_ms jnz .L2 .L4: ; PutStr "PCNET: Send timeout" .L3: mov dword [edi+pcnet32_tx_head.base],0 pop ecx pop ebx pop esi pop edi ret