;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; PCnet driver for KolibriOS ;; ;; ;; ;; By hidnplayr & clevermouse ;; ;; ;; ;; Based on the PCnet32 driver for MenuetOS, by Jarek Pelczar ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; ;; Version 2, June 1991 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; $Revision$ format MS COFF API_VERSION = 0x01000100 DEBUG = 1 __DEBUG__ = 1 __DEBUG_LEVEL__ = 1 MAX_DEVICES = 4 MAX_ETH_FRAME_SIZE = 1514 include 'proc32.inc' include 'imports.inc' include 'fdo.inc' include 'netdrv.inc' public START public service_proc public version virtual at ebx device: ETH_DEVICE ; device specific .private: .mode_ dw ? .tlen_rlen dw ? .phys_addr dp ? .reserved dw ? .filter dq ? .rx_ring_phys dd ? .tx_ring_phys dd ? .cur_rx db ? .cur_tx db ? .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 ? .rx_buffer dd ? .tx_buffer dd ? .io_addr dd ? .irq_line db ? .pci_bus db ? .pci_dev db ? .access_read_csr dd ? .access_write_csr dd ? .access_read_bcr dd ? .access_write_bcr dd ? .access_read_rap dd ? .access_write_rap dd ? .access_reset dd ? device_size = $ - device end virtual struc buf_head { .base dd ? .length dw ? .status dw ? .msg_length dw ? .misc dw ? .reserved dd ? .size: } virtual at 0 buf_head buf_head end virtual PCNET_PORT_AUI = 0x00 PCNET_PORT_10BT = 0x01 PCNET_PORT_GPSI = 0x02 PCNET_PORT_MII = 0x03 PCNET_PORT_PORTSEL = 0x03 PCNET_PORT_ASEL = 0x04 PCNET_PORT_100 = 0x40 PCNET_PORT_FD = 0x80 PCNET_DMA_MASK = 0xffffffff PCNET_LOG_TX_BUFFERS = 2 PCNET_LOG_RX_BUFFERS = 2 PCNET_TX_RING_SIZE = 4 PCNET_TX_RING_MOD_MASK = (PCNET_TX_RING_SIZE-1) PCNET_TX_RING_LEN_BITS = (PCNET_LOG_TX_BUFFERS shl 12) PCNET_RX_RING_SIZE = 4 PCNET_RX_RING_MOD_MASK = (PCNET_RX_RING_SIZE-1) PCNET_RX_RING_LEN_BITS = (PCNET_LOG_RX_BUFFERS shl 4) PCNET_PKT_BUF_SZ = 1544 PCNET_PKT_BUF_SZ_NEG = 0xf9f8 PCNET_WIO_RDP = 0x10 PCNET_WIO_RAP = 0x12 PCNET_WIO_RESET = 0x14 PCNET_WIO_BDP = 0x16 PCNET_DWIO_RDP = 0x10 PCNET_DWIO_RAP = 0x14 PCNET_DWIO_RESET = 0x18 PCNET_DWIO_BDP = 0x1C PCNET_TOTAL_SIZE = 0x20 ; CSR registers PCNET_CSR_CSR = 0x00 PCNET_CSR_IAB0 = 0x01 PCNET_CSR_IAB1 = 0x02 PCNET_CSR_IMR = 0x03 PCNET_CSR_TFEAT = 0x04 PCNET_CSR_EXTCTL1 = 0x05 PCNET_CSR_DTBLLEN = 0x06 PCNET_CSR_EXTCTL2 = 0x07 PCNET_CSR_MAR0 = 0x08 PCNET_CSR_MAR1 = 0x09 PCNET_CSR_MAR2 = 0x0A PCNET_CSR_MAR3 = 0x0B PCNET_CSR_PAR0 = 0x0C PCNET_CSR_PAR1 = 0x0D PCNET_CSR_PAR2 = 0x0E PCNET_CSR_MODE = 0x0F PCNET_CSR_RXADDR0 = 0x18 PCNET_CSR_RXADDR1 = 0x19 PCNET_CSR_TXADDR0 = 0x1E PCNET_CSR_TXADDR1 = 0x1F PCNET_CSR_TXPOLL = 0x2F PCNET_CSR_RXPOLL = 0x31 PCNET_CSR_RXRINGLEN = 0x4C PCNET_CSR_TXRINGLEN = 0x4E PCNET_CSR_DMACTL = 0x50 PCNET_CSR_BUSTIMER = 0x52 PCNET_CSR_MEMERRTIMEO = 0x64 PCNET_CSR_ONNOWMISC = 0x74 PCNET_CSR_ADVFEAT = 0x7A PCNET_CSR_MACCFG = 0x7D PCNET_CSR_CHIPID0 = 0x58 PCNET_CSR_CHIPID1 = 0x59 ; Control and Status Register (CSR0) PCNET_CSR_INIT = 1 shl 0 PCNET_CSR_START = 1 shl 1 PCNET_CSR_STOP = 1 shl 2 PCNET_CSR_TX = 1 shl 3 PCNET_CSR_TXON = 1 shl 4 PCNET_CSR_RXON = 1 shl 5 PCNET_CSR_INTEN = 1 shl 6 PCNET_CSR_INTR = 1 shl 7 PCNET_CSR_IDONE = 1 shl 8 PCNET_CSR_TINT = 1 shl 9 PCNET_CSR_RINT = 1 shl 10 PCNET_CSR_MERR = 1 shl 11 PCNET_CSR_MISS = 1 shl 12 PCNET_CSR_CERR = 1 shl 13 ; Interrupt masks and deferral control (CSR3) PCNET_IMR_BSWAP = 0x0004 PCNET_IMR_ENMBA = 0x0008 ; enable modified backoff alg PCNET_IMR_DXMT2PD = 0x0010 PCNET_IMR_LAPPEN = 0x0020 ; lookahead packet processing enb PCNET_IMR_DXSUFLO = 0x0040 ; disable TX stop on underflow PCNET_IMR_IDONE = 0x0100 PCNET_IMR_TINT = 0x0200 PCNET_IMR_RINT = 0x0400 PCNET_IMR_MERR = 0x0800 PCNET_IMR_MISS = 0x1000 PCNET_IMR = PCNET_IMR_TINT+PCNET_IMR_RINT+PCNET_IMR_IDONE+PCNET_IMR_MERR+PCNET_IMR_MISS ; Test and features control (CSR4) PCNET_TFEAT_TXSTRTMASK = 0x0004 PCNET_TFEAT_TXSTRT = 0x0008 PCNET_TFEAT_RXCCOFLOWM = 0x0010 ; Rx collision counter oflow PCNET_TFEAT_RXCCOFLOW = 0x0020 PCNET_TFEAT_UINT = 0x0040 PCNET_TFEAT_UINTREQ = 0x0080 PCNET_TFEAT_MISSOFLOWM = 0x0100 PCNET_TFEAT_MISSOFLOW = 0x0200 PCNET_TFEAT_STRIP_FCS = 0x0400 PCNET_TFEAT_PAD_TX = 0x0800 PCNET_TFEAT_TXDPOLL = 0x1000 PCNET_TFEAT_DMAPLUS = 0x4000 ; Extended control and interrupt 1 (CSR5) PCNET_EXTCTL1_SPND = 0x0001 ; suspend PCNET_EXTCTL1_MPMODE = 0x0002 ; magic packet mode PCNET_EXTCTL1_MPENB = 0x0004 ; magic packet enable PCNET_EXTCTL1_MPINTEN = 0x0008 ; magic packet interrupt enable PCNET_EXTCTL1_MPINT = 0x0010 ; magic packet interrupt PCNET_EXTCTL1_MPPLBA = 0x0020 ; magic packet phys. logical bcast PCNET_EXTCTL1_EXDEFEN = 0x0040 ; excessive deferral interrupt enb. PCNET_EXTCTL1_EXDEF = 0x0080 ; excessive deferral interrupt PCNET_EXTCTL1_SINTEN = 0x0400 ; system interrupt enable PCNET_EXTCTL1_SINT = 0x0800 ; system interrupt PCNET_EXTCTL1_LTINTEN = 0x4000 ; last TX interrupt enb PCNET_EXTCTL1_TXOKINTD = 0x8000 ; TX OK interrupt disable ; RX/TX descriptor len (CSR6) PCNET_DTBLLEN_RLEN = 0x0F00 PCNET_DTBLLEN_TLEN = 0xF000 ; Extended control and interrupt 2 (CSR7) PCNET_EXTCTL2_MIIPDTINTE = 0x0001 PCNET_EXTCTL2_MIIPDTINT = 0x0002 PCNET_EXTCTL2_MCCIINTE = 0x0004 PCNET_EXTCTL2_MCCIINT = 0x0008 PCNET_EXTCTL2_MCCINTE = 0x0010 PCNET_EXTCTL2_MCCINT = 0x0020 PCNET_EXTCTL2_MAPINTE = 0x0040 PCNET_EXTCTL2_MAPINT = 0x0080 PCNET_EXTCTL2_MREINTE = 0x0100 PCNET_EXTCTL2_MREINT = 0x0200 PCNET_EXTCTL2_STINTE = 0x0400 PCNET_EXTCTL2_STINT = 0x0800 PCNET_EXTCTL2_RXDPOLL = 0x1000 PCNET_EXTCTL2_RDMD = 0x2000 PCNET_EXTCTL2_RXFRTG = 0x4000 PCNET_EXTCTL2_FASTSPNDE = 0x8000 ; Mode (CSR15) PCNET_MODE_RXD = 0x0001 ; RX disable PCNET_MODE_TXD = 0x0002 ; TX disable PCNET_MODE_LOOP = 0x0004 ; loopback enable PCNET_MODE_TXCRCD = 0x0008 PCNET_MODE_FORCECOLL = 0x0010 PCNET_MODE_RETRYD = 0x0020 PCNET_MODE_INTLOOP = 0x0040 PCNET_MODE_PORTSEL = 0x0180 PCNET_MODE_RXVPAD = 0x2000 PCNET_MODE_RXNOBROAD = 0x4000 PCNET_MODE_PROMISC = 0x8000 ; BCR (Bus Control Registers) PCNET_BCR_MMRA = 0x00 ; Master Mode Read Active PCNET_BCR_MMW = 0x01 ; Master Mode Write Active PCNET_BCR_MISCCFG = 0x02 PCNET_BCR_LED0 = 0x04 PCNET_BCR_LED1 = 0x05 PCNET_BCR_LED2 = 0x06 PCNET_BCR_LED3 = 0x07 PCNET_BCR_DUPLEX = 0x09 PCNET_BCR_BUSCTL = 0x12 PCNET_BCR_EECTL = 0x13 PCNET_BCR_SSTYLE = 0x14 PCNET_BCR_PCILAT = 0x16 PCNET_BCR_PCISUBVENID = 0x17 PCNET_BCR_PCISUBSYSID = 0x18 PCNET_BCR_SRAMSIZE = 0x19 PCNET_BCR_SRAMBOUND = 0x1A PCNET_BCR_SRAMCTL = 0x1B PCNET_BCR_MIICTL = 0x20 PCNET_BCR_MIIADDR = 0x21 PCNET_BCR_MIIDATA = 0x22 PCNET_BCR_PCIVENID = 0x23 PCNET_BCR_PCIPCAP = 0x24 PCNET_BCR_DATA0 = 0x25 PCNET_BCR_DATA1 = 0x26 PCNET_BCR_DATA2 = 0x27 PCNET_BCR_DATA3 = 0x28 PCNET_BCR_DATA4 = 0x29 PCNET_BCR_DATA5 = 0x2A PCNET_BCR_DATA6 = 0x2B PCNET_BCR_DATA7 = 0x2C PCNET_BCR_ONNOWPAT0 = 0x2D PCNET_BCR_ONNOWPAT1 = 0x2E PCNET_BCR_ONNOWPAT2 = 0x2F PCNET_BCR_PHYSEL = 0x31 ; RX status register PCNET_RXSTAT_BPE = 0x0080 ; bus parity error PCNET_RXSTAT_ENP = 0x0100 ; end of packet PCNET_RXSTAT_STP = 0x0200 ; start of packet PCNET_RXSTAT_BUFF = 0x0400 ; buffer error PCNET_RXSTAT_CRC = 0x0800 ; CRC error PCNET_RXSTAT_OFLOW = 0x1000 ; rx overrun PCNET_RXSTAT_FRAM = 0x2000 ; framing error PCNET_RXSTAT_ERR = 0x4000 ; error summary PCNET_RXSTAT_OWN = 0x8000 ; TX status register PCNET_TXSTAT_TRC = 0x0000000F ; transmit retries PCNET_TXSTAT_RTRY = 0x04000000 ; retry PCNET_TXSTAT_LCAR = 0x08000000 ; lost carrier PCNET_TXSTAT_LCOL = 0x10000000 ; late collision PCNET_TXSTAT_EXDEF = 0x20000000 ; excessive deferrals PCNET_TXSTAT_UFLOW = 0x40000000 ; transmit underrun PCNET_TXSTAT_BUFF = 0x80000000 ; buffer error PCNET_TXCTL_OWN = 0x80000000 PCNET_TXCTL_ERR = 0x40000000 ; error summary PCNET_TXCTL_ADD_FCS = 0x20000000 ; add FCS to pkt PCNET_TXCTL_MORE_LTINT = 0x10000000 PCNET_TXCTL_ONE = 0x08000000 PCNET_TXCTL_DEF = 0x04000000 PCNET_TXCTL_STP = 0x02000000 PCNET_TXCTL_ENP = 0x01000000 PCNET_TXCTL_BPE = 0x00800000 PCNET_TXCTL_MBO = 0x0000F000 PCNET_TXCTL_BUFSZ = 0x00000FFF section '.flat' code readable align 16 ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; proc START ;; ;; ;; ;; (standard driver proc) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; proc START stdcall, state:dword cmp [state], 1 jne .exit .entry: DEBUGF 1,"Loading PCnet 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 arent supported for this card yet ; check if the device is already listed mov ecx, [devices] test ecx, ecx jz .firstdevice mov esi, device_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 ; This device doesnt have its own eth_device structure yet, lets create one .firstdevice: cmp [devices], MAX_DEVICES ; First check if the driver can handle one more card jge .fail allocate_and_clear ebx, device_size, .fail ; Fill in the direct call addresses into the struct mov [device.reset], reset mov [device.transmit], transmit mov [device.get_MAC], read_mac mov [device.set_MAC], write_mac mov [device.unload], unload 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 allocate_and_clear [device.tx_buffer], PCNET_RX_RING_SIZE * (PCNET_PKT_BUF_SZ + buf_head.size), .err allocate_and_clear [device.rx_buffer], PCNET_TX_RING_SIZE * (PCNET_PKT_BUF_SZ + buf_head.size), .err ; Ok, the eth_device structure is ready, let's probe the device ; Because initialization fires IRQ, IRQ handler must be aware of this device mov eax, [devices] ; Add the device structure to our device list mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device) inc [devices] ; call probe ; this function will output in eax test eax, eax jnz .destroy ; If an error occured, exit mov [device.type], NET_TYPE_ETH call NetRegDev cmp eax, -1 je .destroy ret ; 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 dec [devices] .err: DEBUGF 1,"Error, removing all data !\n" stdcall KernelFree, [device.rx_buffer] stdcall KernelFree, [device.tx_buffer] stdcall KernelFree, ebx .fail: or eax, -1 ret ;------------------------------------------------------ endp ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; ;; ;; ;; Actual Hardware dependent code starts here ;; ;; ;; ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; align 4 unload: ; TODO: (in this particular order) ; ; - Stop the device ; - Detach int handler ; - Remove device from local list (RTL8139_LIST) ; - call unregister function in kernel ; - Remove all allocated structures and buffers the card used or eax,-1 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; probe: enables the device (if it really is a PCnet device) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 probe: mov edx, [device.io_addr] call wio_reset xor ecx, ecx call wio_read_csr cmp eax, 4 jne .try_dwio ; Try Word I/O mov ax , 88 add edx, PCNET_WIO_RAP out dx , ax nop nop in ax , dx sub edx, PCNET_WIO_RAP cmp ax , 88 jne .try_dwio call switch_to_wio jmp .L1 .try_dwio: call dwio_reset xor ecx, ecx call dwio_read_csr cmp eax, 4 jne .no_dev ; Try Dword I/O add edx, PCNET_DWIO_RAP mov eax, 88 out dx , eax nop nop in eax, dx sub edx, PCNET_DWIO_RAP and eax, 0xffff cmp eax, 88 jne .no_dev call switch_to_dwio jmp .L1 .no_dev: DEBUGF 1,"PCnet device not found!\n" mov eax, 1 ret .L1: mov ecx, PCNET_CSR_CHIPID0 call [device.access_read_csr] mov esi, eax mov ecx, PCNET_CSR_CHIPID1 call [device.access_read_csr] shl eax, 16 or eax, esi mov ecx, eax and ecx, 0xfff cmp ecx, 3 jne .no_dev shr eax, 12 and eax, 0xffff mov [device.chip_version], eax DEBUGF 1,"chip version ok\n" mov [device.fdx], 0 mov [device.mii], 0 mov [device.fset], 0 mov [device.dxsuflo], 0 mov [device.ltint], 0 cmp eax, 0x2420 je .L2 cmp eax, 0x2430 je .L2 mov [device.fdx], 1 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 DEBUGF 1,"Invalid chip rev\n" jmp .no_dev .L2: mov [device.name], device_l2 jmp .L10 .L4: mov [device.name], device_l4 ; mov [device.fdx], 1 jmp .L10 .L5: mov [device.name], device_l5 ; mov [device.fdx], 1 mov [device.mii], 1 mov [device.fset], 1 mov [device.ltint], 1 jmp .L10 .L6: mov [device.name], device_l6 ; mov [device.fdx], 1 mov [device.mii], 1 mov [device.fset], 1 jmp .L10 .L7: mov [device.name], device_l7 ; mov [device.fdx], 1 mov [device.mii], 1 jmp .L10 .L8: mov [device.name], device_l8 ; mov [device.fdx], 1 mov ecx, PCNET_CSR_RXPOLL call dword [device.access_read_bcr] call dword [device.access_write_bcr] jmp .L10 .L9: mov [device.name], device_l9 ; mov [device.fdx], 1 mov [device.mii], 1 .L10: DEBUGF 1,"device name: %s\n",[device.name] cmp [device.fset], 1 jne .L11 mov ecx, PCNET_BCR_BUSCTL call [device.access_read_bcr] or eax, 0x800 call [device.access_write_bcr] mov ecx, PCNET_CSR_DMACTL call [device.access_read_csr] ; and eax, 0xc00 ; or eax, 0xc00 mov eax, 0xc00 call [device.access_write_csr] mov [device.dxsuflo],1 mov [device.ltint],1 .L11: make_bus_master [device.pci_bus], [device.pci_dev] mov eax, PCNET_PORT_ASEL mov [device.options], eax mov [device.mode_], word 0x0003 mov [device.tlen_rlen], word (PCNET_TX_RING_LEN_BITS or PCNET_RX_RING_LEN_BITS) mov dword [device.filter], 0 mov dword [device.filter+4], 0 mov eax, PCNET_IMR mov ecx, PCNET_CSR_IMR ; Write interrupt mask call [device.access_write_csr] align 4 reset: ; attach int handler movzx eax, [device.irq_line] DEBUGF 1,"Attaching int handler to irq %x\n",eax:1 stdcall AttachIntHandler, eax, int_handler, dword 0 test eax, eax jnz @f DEBUGF 1,"\nCould not attach int handler!\n" ; or eax, -1 ; ret @@: mov edx, [device.io_addr] call [device.access_reset] ; Switch pcnet32 to 32bit mode mov ecx, PCNET_BCR_SSTYLE mov eax, 2 call [device.access_write_bcr] ; set/reset autoselect bit mov ecx, PCNET_BCR_MISCCFG call [device.access_read_bcr] and eax,not 2 test [device.options], PCNET_PORT_ASEL jz .L1 or eax, 2 .L1: call [device.access_write_bcr] ; Handle full duplex setting cmp byte [device.full_duplex], 0 je .L2 mov ecx, PCNET_BCR_DUPLEX call [device.access_read_bcr] and eax, not 3 test [device.options], PCNET_PORT_FD jz .L3 or eax, 1 cmp [device.options], PCNET_PORT_FD or PCNET_PORT_AUI jne .L4 or eax, 2 jmp .L4 .L3: test [device.options], PCNET_PORT_ASEL jz .L4 cmp [device.chip_version], 0x2627 jne .L4 or eax, 3 .L4: mov ecx, PCNET_BCR_DUPLEX call [device.access_write_bcr] .L2: ; set/reset GPSI bit in test register mov ecx, 124 call [device.access_read_csr] mov ecx, [device.options] and ecx, PCNET_PORT_PORTSEL cmp ecx, PCNET_PORT_GPSI jne .L5 or eax, 0x10 .L5: call [device.access_write_csr] cmp [device.mii], 0 je .L6 test [device.options], PCNET_PORT_ASEL jnz .L6 mov ecx, PCNET_BCR_MIICTL call [device.access_read_bcr] and eax,not 0x38 test [device.options], PCNET_PORT_FD jz .L7 or eax, 0x10 .L7: test [device.options], PCNET_PORT_100 jz .L8 or eax, 0x08 .L8: call [device.access_write_bcr] jmp .L9 .L6: test [device.options], PCNET_PORT_ASEL jz .L9 mov ecx, PCNET_BCR_MIICTL DEBUGF 1,"ASEL, enable auto-negotiation\n" call [device.access_read_bcr] and eax, not 0x98 or eax, 0x20 call [device.access_write_bcr] .L9: cmp [device.ltint],0 je .L10 mov ecx,5 call [device.access_read_csr] or eax,(1 shl 14) call [device.access_write_csr] .L10: mov eax,[device.options] and eax,PCNET_PORT_PORTSEL shl eax,7 mov [device.mode_],ax mov dword [device.filter], -1 mov dword [device.filter+4], -1 call read_mac lea esi, [device.mac] lea edi, [device.phys_addr] movsd movsw call init_ring lea eax, [device.private] GetRealAddr push eax and eax, 0xffff mov ecx, 1 call [device.access_write_csr] pop eax shr eax,16 mov ecx,2 call [device.access_write_csr] mov ecx,4 mov eax,0x0915 call [device.access_write_csr] mov ecx,0 mov eax,1 call [device.access_write_csr] mov [device.tx_full],0 mov [device.cur_rx],0 mov [device.cur_tx],0 mov [device.dirty_rx],0 mov [device.dirty_tx],0 mov ecx,100 .L11: push ecx xor ecx,ecx call [device.access_read_csr] pop ecx test ax,0x100 jnz .L12 loop .L11 .L12: DEBUGF 1,"hardware reset\n" xor ecx, ecx mov eax, 0x0002 call [device.access_write_csr] xor ecx, ecx call [device.access_read_csr] xor ecx, ecx mov eax, PCNET_CSR_INTEN or PCNET_CSR_START call [device.access_write_csr] ; Set the mtu, kernel will be able to send now mov [device.mtu], 1514 DEBUGF 1,"PCNET reset complete\n" xor eax, eax ret align 4 init_ring: mov ecx, PCNET_RX_RING_SIZE mov edi, [device.rx_buffer] mov eax, edi GetRealAddr mov [device.rx_ring_phys], eax add eax, PCNET_RX_RING_SIZE * buf_head.size .rx_init: mov [edi + buf_head.base], eax mov [edi + buf_head.length], PCNET_PKT_BUF_SZ_NEG mov [edi + buf_head.status], 0x8000 and dword [edi + buf_head.msg_length], 0 and dword [edi + buf_head.reserved], 0 add eax, PCNET_PKT_BUF_SZ add edi, buf_head.size loop .rx_init mov ecx, PCNET_TX_RING_SIZE mov edi, [device.tx_buffer] mov eax, edi GetRealAddr mov [device.tx_ring_phys], eax add eax, PCNET_TX_RING_SIZE * buf_head.size .tx_init: mov [edi + buf_head.base], eax and dword [edi + buf_head.length], 0 and dword [edi + buf_head.msg_length], 0 and dword [edi + buf_head.reserved], 0 add eax, PCNET_PKT_BUF_SZ add edi, buf_head.size loop .tx_init mov [device.tlen_rlen], (PCNET_TX_RING_LEN_BITS or PCNET_RX_RING_LEN_BITS) ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Transmit ;; ;; ;; ;; In: buffer pointer in [esp+4] ;; ;; size of buffer in [esp+8] ;; ;; pointer to device structure in ebx ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 4 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], 1514 jg .nospace ; packet is too long cmp dword [esp+8], 60 jl .nospace ; packet is too short ; check descriptor movzx eax, [device.cur_tx] imul edi, eax, PCNET_PKT_BUF_SZ shl eax, 4 add eax, [device.tx_buffer] add edi, [device.tx_buffer] add edi, PCNET_TX_RING_SIZE * buf_head.size test byte [eax + buf_head.status + 1], 80h jnz .nospace ; descriptor is free, copy data mov esi, [esp+4] mov ecx, [esp+8] mov edx, ecx shr ecx, 2 and edx, 3 rep movsd mov ecx, edx rep movsb ; set length mov ecx, [esp+8] neg ecx mov [eax + buf_head.length], cx ; put to transfer queue mov [eax + buf_head.status], 0x8300 ; trigger an immediate send xor ecx, ecx ; CSR0 call [device.access_read_csr] or eax, PCNET_CSR_TX call [device.access_write_csr] ; get next descriptor 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, ... inc [device.cur_tx] and [device.cur_tx], 3 DEBUGF 2," - Packet Sent! " ; Update stats inc [device.packets_tx] mov eax, [esp+8] add dword [device.bytes_tx], eax adc dword [device.bytes_tx + 4], 0 .finish: DEBUGF 2," - Done!\n" stdcall KernelFree, [esp+4] xor eax, eax ret 8 .nospace: DEBUGF 1, 'ERROR: no free transmit descriptors\n' stdcall KernelFree, [esp+4] or eax, -1 ret 8 ;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Interrupt handler ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;; align 4 int_handler: ; DEBUGF 1,"IRQ %x ",eax:2 ; no, you cant replace 'eax:2' with 'al', this must be a bug in FDO ; find pointer of device wich made IRQ occur mov esi, device_list mov ecx, [devices] test ecx, ecx jz .abort .nextdevice: mov ebx, dword [esi] mov edx, [device.io_addr] ; get IRQ reason push ecx xor ecx, ecx ; CSR0 call [device.access_read_csr] pop ecx test al , al js .got_it add esi, 4 loop .nextdevice ret ; If no device was found, abort (The irq was probably for a device, not registered to this driver .got_it: ;------------------------------------------------------- ; Possible reasons: ; initialization done - ignore ; transmit done - ignore ; packet received - handle ; Clear ALL IRQ reasons. ; N.B. One who wants to handle more than one reason must be ready ; to two or more reasons in one IRQ. xor ecx, ecx call [device.access_write_csr] ; Received packet ok? test ax, PCNET_CSR_RINT jz @f push ebx .receiver_test_loop: pop ebx movzx eax, [device.cur_rx] ; and eax, PCNET_RX_RING_MOD_MASK mov edi, eax imul esi, eax, PCNET_PKT_BUF_SZ ; add esi, [device.rx_buffer] ; esi now points to rx buffer add esi, PCNET_RX_RING_SIZE * buf_head.size shl edi, 4 ; desc * 16 (16 is size of one ring entry) add edi, [device.rx_buffer] ; edi now points to current rx ring entry mov cx , [edi + buf_head.status] test cx , PCNET_RXSTAT_OWN ; If this bit is set, the controller OWN's the packet, if not, we do jnz .abort test cx , PCNET_RXSTAT_ENP jz .abort test cx , PCNET_RXSTAT_STP jz .abort movzx ecx, [edi + buf_head.msg_length] ; get packet length in ecx sub ecx, 4 ; push ecx stdcall KernelAlloc, ecx ; Allocate a buffer to put packet into pop ecx test eax, eax ; Test if we allocated succesfully jz .abort ; ; Update stats add dword [device.bytes_rx], ecx adc dword [device.bytes_rx + 4], 0 inc dword [device.packets_rx] push ebx push .receiver_test_loop ; push ecx ; for eth_receiver push eax ; xchg edi, eax push ecx shr ecx, 2 cld rep movsd pop ecx and ecx, 3 rep movsb ; mov word [eax + buf_head.length], PCNET_PKT_BUF_SZ_NEG mov word [eax + buf_head.status], PCNET_RXSTAT_OWN ; Set OWN bit back to 1 (controller may write to tx-buffer again now) inc [device.cur_rx] ; update descriptor and [device.cur_rx], 3 ; jmp EthReceiver ; Send the copied packet to kernel .abort: @@: ret ;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Write MAC address ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;; align 4 write_mac: ; in: mac pushed onto stack (as 3 words) DEBUGF 1,"Writing MAC: %x-%x-%x-%x-%x-%x",[esp+0]:2,[esp+1]:2,[esp+2]:2,[esp+3]:2,[esp+4]:2,[esp+5]:2 mov edx, [device.io_addr] add dx, 2 xor eax, eax mov ecx, PCNET_CSR_PAR0 @@: pop ax call [device.access_write_csr] DEBUGF 1,"." inc ecx cmp ecx, PCNET_CSR_PAR2 jl @r DEBUGF 1,"\n" ; Notice this procedure does not ret, but continues to read_mac instead. ;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Read MAC address ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;; align 4 read_mac: DEBUGF 1,"Reading MAC" mov edx, [device.io_addr] add dx, 6 @@: dec dx dec dx in ax, dx push ax DEBUGF 1,"." cmp edx, [device.io_addr] jg @r DEBUGF 1," %x-%x-%x-%x-%x-%x\n",[esp+0]:2,[esp+1]:2,[esp+2]:2,[esp+3]:2,[esp+4]:2,[esp+5]:2 lea edi, [device.mac] pop ax stosw pop ax stosw pop ax stosw ret align 4 switch_to_wio: DEBUGF 1,"Switching to 16-bit mode\n" mov [device.access_read_csr], wio_read_csr mov [device.access_write_csr], wio_write_csr mov [device.access_read_bcr], wio_read_bcr mov [device.access_write_bcr], wio_write_bcr mov [device.access_read_rap], wio_read_rap mov [device.access_write_rap], wio_write_rap mov [device.access_reset], wio_reset ret align 4 switch_to_dwio: DEBUGF 1,"Switching to 32-bit mode\n" mov [device.access_read_csr], dwio_read_csr mov [device.access_write_csr], dwio_write_csr mov [device.access_read_bcr], dwio_read_bcr mov [device.access_write_bcr], dwio_write_bcr mov [device.access_read_rap], dwio_read_rap mov [device.access_write_rap], dwio_write_rap mov [device.access_reset], dwio_reset ret ; ecx - index ; return: ; eax - data align 4 wio_read_csr: add edx, PCNET_WIO_RAP mov ax , cx out dx , ax add edx, PCNET_WIO_RDP - PCNET_WIO_RAP in ax , dx and eax, 0xffff sub edx, PCNET_WIO_RDP ret ; eax - data ; ecx - index align 4 wio_write_csr: add edx, PCNET_WIO_RAP xchg eax, ecx out dx , ax xchg eax, ecx add edx, PCNET_WIO_RDP - PCNET_WIO_RAP out dx , ax sub edx, PCNET_WIO_RDP ret ; ecx - index ; return: ; eax - data align 4 wio_read_bcr: add edx, PCNET_WIO_RAP mov ax , cx out dx , ax add edx, PCNET_WIO_BDP - PCNET_WIO_RAP in ax , dx and eax, 0xffff sub edx, PCNET_WIO_BDP ret ; eax - data ; ecx - index align 4 wio_write_bcr: add edx, PCNET_WIO_RAP xchg eax, ecx out dx , ax xchg eax, ecx add edx, PCNET_WIO_BDP - PCNET_WIO_RAP out dx , ax sub edx, PCNET_WIO_BDP ret align 4 wio_read_rap: add edx, PCNET_WIO_RAP in ax , dx and eax, 0xffff sub edx, PCNET_WIO_RAP ret ; eax - val align 4 wio_write_rap: add edx, PCNET_WIO_RAP out dx , ax sub edx, PCNET_WIO_RAP ret align 4 wio_reset: push eax add edx, PCNET_WIO_RESET in ax , dx pop eax sub edx, PCNET_WIO_RESET ret ; ecx - index ; return: ; eax - data align 4 dwio_read_csr: add edx, PCNET_DWIO_RAP mov eax, ecx out dx , eax add edx, PCNET_DWIO_RDP - PCNET_DWIO_RAP in eax, dx and eax, 0xffff sub edx, PCNET_DWIO_RDP ret ; ecx - index ; eax - data align 4 dwio_write_csr: add edx, PCNET_DWIO_RAP xchg eax, ecx out dx , eax add edx, PCNET_DWIO_RDP - PCNET_DWIO_RAP xchg eax, ecx out dx , eax sub edx, PCNET_DWIO_RDP ret ; ecx - index ; return: ; eax - data align 4 dwio_read_bcr: add edx, PCNET_DWIO_RAP mov eax, ecx out dx , eax add edx, PCNET_DWIO_BDP - PCNET_DWIO_RAP in eax, dx and eax, 0xffff sub edx, PCNET_DWIO_BDP ret ; ecx - index ; eax - data align 4 dwio_write_bcr: add edx, PCNET_DWIO_RAP xchg eax, ecx out dx , eax add edx, PCNET_DWIO_BDP - PCNET_DWIO_RAP xchg eax, ecx out dx , eax sub edx, PCNET_DWIO_BDP ret align 4 dwio_read_rap: add edx, PCNET_DWIO_RAP in eax, dx and eax, 0xffff sub edx, PCNET_DWIO_RAP ret ; eax - val align 4 dwio_write_rap: add edx, PCNET_DWIO_RAP out dx , eax sub edx, PCNET_DWIO_RAP ret align 4 dwio_reset: push eax add edx, PCNET_DWIO_RESET in eax, dx pop eax sub edx, PCNET_DWIO_RESET ret ; End of code align 4 ; Place all initialised data here devices dd 0 version dd (5 shl 16) or (API_VERSION and 0xFFFF) my_service db 'PCnet',0 ; max 16 chars include zero device_l2 db "PCnet/PCI 79C970",0 device_l4 db "PCnet/PCI II 79C970A",0 device_l5 db "PCnet/FAST 79C971",0 device_l6 db "PCnet/FAST+ 79C972",0 device_l7 db "PCnet/FAST III 79C973",0 device_l8 db "PCnet/Home 79C978",0 device_l9 db "PCnet/FAST III 79C975",0 options_mapping: dd PCNET_PORT_ASEL ; 0 Auto-select dd PCNET_PORT_AUI ; 1 BNC/AUI dd PCNET_PORT_AUI ; 2 AUI/BNC dd PCNET_PORT_ASEL ; 3 not supported dd PCNET_PORT_10BT or PCNET_PORT_FD ; 4 10baseT-FD dd PCNET_PORT_ASEL ; 5 not supported dd PCNET_PORT_ASEL ; 6 not supported dd PCNET_PORT_ASEL ; 7 not supported dd PCNET_PORT_ASEL ; 8 not supported dd PCNET_PORT_MII ; 9 MII 10baseT dd PCNET_PORT_MII or PCNET_PORT_FD ; 10 MII 10baseT-FD dd PCNET_PORT_MII ; 11 MII (autosel) dd PCNET_PORT_10BT ; 12 10BaseT dd PCNET_PORT_MII or PCNET_PORT_100 ; 13 MII 100BaseTx dd PCNET_PORT_MII or PCNET_PORT_100 or PCNET_PORT_FD ; 14 MII 100BaseTx-FD dd PCNET_PORT_ASEL ; 15 not supported 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 device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling