;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; Broadcom NetXtreme 57xx driver for KolibriOS ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; ;; Version 2, June 1991 ;; ;; ;; ;; Broadcom's programmers's manual for the BCM57xx ;; ;; http://www.broadcom.com/collateral/pg/57XX-PG105-R.pdf ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; format PE DLL native entry START CURRENT_API = 0x0200 COMPATIBLE_API = 0x0100 API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API ; configureable area MAX_DEVICES = 16 __DEBUG__ = 1 __DEBUG_LEVEL__ = 2 TX_RING_SIZE = 128 ; Number of packets in send ring buffer RX_RING_SIZE = 128 ; Number of packets in receive ring buffer ; end configureable area section '.flat' readable writable executable include '../proc32.inc' include '../struct.inc' include '../macros.inc' include '../fdo.inc' include '../netdrv.inc' if (bsr TX_RING_SIZE)>(bsf TX_RING_SIZE) display 'TX_RING_SIZE must be a power of two' err end if if (bsr RX_RING_SIZE)>(bsf RX_RING_SIZE) display 'RX_RING_SIZE must be a power of two' err end if struct device ETH_DEVICE mmio_addr dd ? pci_bus dd ? pci_dev dd ? irq_line db ? cur_tx dd ? last_tx dd ? rb 0x100 - ($ and 0xff) ; align 256 rx_desc rd 256/8 rb 0x100 - ($ and 0xff) ; align 256 tx_desc rd 256/8 ends ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; proc START ;; ;; ;; ;; (standard driver proc) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; proc START c, reason:dword, cmdline:dword cmp [reason], DRV_ENTRY jne .fail DEBUGF 1,"Loading driver\n" invoke RegService, my_service, service_proc ret .fail: xor eax, eax ret endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; proc SERVICE_PROC ;; ;; ;; ;; (standard driver proc) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; proc service_proc stdcall, ioctl:dword mov edx, [ioctl] mov eax, [edx + IOCTL.io_code] ;------------------------------------------------------ cmp eax, 0 ;SRV_GETVERSION jne @F cmp [edx + IOCTL.out_size], 4 jb .fail mov eax, [edx + IOCTL.output] mov [eax], dword API_VERSION xor eax, eax ret ;------------------------------------------------------ @@: cmp eax, 1 ;SRV_HOOK jne .fail cmp [edx + IOCTL.inp_size], 3 ; Data input must be at least 3 bytes jb .fail mov eax, [edx + 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 esi, device_list mov ecx, [devices] test ecx, ecx jz .firstdevice ; mov eax, [edx + IOCTL.input] ; get the pci bus and device numbers mov ax, [eax+1] ; .nextdevice: mov ebx, [esi] cmp al, byte [ebx + device.pci_bus] jne .next cmp ah, byte [ebx + device.pci_dev] je .find_devicenum ; Device is already loaded, let's find it's device number .next: 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 jae .fail allocate_and_clear ebx, sizeof.device, .fail ; Allocate the buffer for device structure ; Fill in the direct call addresses into the struct mov [ebx + device.reset], reset mov [ebx + device.transmit], transmit mov [ebx + device.unload], unload mov [ebx + device.name], my_service ; save the pci bus and device numbers mov eax, [edx + IOCTL.input] movzx ecx, byte [eax+1] mov [ebx + device.pci_bus], ecx movzx ecx, byte [eax+2] mov [ebx + device.pci_dev], ecx ; Now, it's time to find the base mmio addres of the PCI device stdcall PCI_find_mmio, [ebx + device.pci_bus], [ebx + device.pci_dev] test eax, eax jz .destroy ; Create virtual mapping of the physical memory invoke MapIoMem, eax, 10000h, PG_SW+PG_NOCACHE mov [ebx + device.mmio_addr], eax ; We've found the mmio address, find IRQ now invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.interrupt_line mov [ebx + device.irq_line], al DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\ [ebx + device.pci_dev]:1,[ebx + device.pci_bus]:1,[ebx + device.irq_line]:1,[ebx + device.mmio_addr]:8 ; 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 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] ; mov [ebx + device.type], NET_TYPE_ETH invoke 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" invoke 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: invoke KernelFree, ebx .fail: or eax, -1 ret ;------------------------------------------------------ endp ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; ;; ;; ;; Actual Hardware dependent code starts here ;; ;; ;; ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; unload: ; TODO: (in this particular order) ; ; - Stop the device ; - Detach int handler ; - Remove device from local list (device_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 BCM57XX) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; probe: DEBUGF 1,"Probe\n" ; Make the device a bus master invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command or al, PCI_CMD_MASTER + PCI_CMD_MMIO + PCI_CMD_PIO invoke PciWrite32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax ; TODO: validate the device reset: DEBUGF 1,"Reset\n" movzx eax, [ebx + device.irq_line] DEBUGF 1,"Attaching int handler to irq %x\n", eax:1 invoke AttachIntHandler, eax, int_handler, ebx test eax, eax jnz @f DEBUGF 1,"\nCould not attach int handler!\n" ; or eax, -1 ; ret @@: call read_mac ; Set the mtu, kernel will be able to send now mov [ebx + device.mtu], 1514 ; Set link state to unknown mov [ebx + device.state], ETH_LINK_UNKNOWN ret align 4 read_mac: DEBUGF 1,"Read MAC\n" mov esi, [ebx + device.mmio_addr] lea edi, [ebx + device.mac] movsd movsw .mac_ok: DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\ [ebx + device.mac+0]:2,[ebx + device.mac+1]:2,[ebx + device.mac+2]:2,[ebx + device.mac+3]:2,[ebx + device.mac+4]:2,[ebx + device.mac+5]:2 ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Transmit ;; ;; ;; ;; In: pointer to device structure in ebx ;; ;; Out: eax = 0 on success ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; align 16 proc transmit stdcall bufferptr, buffersize spin_lock_irqsave DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [buffersize] mov eax, [bufferptr] 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 [buffersize], 1514 ja .error cmp [buffersize], 60 jb .error ; Program the descriptor ; test [something], STILL_BUSY? ; jnz .overrun ; TODO: Program the descriptor ; Update stats inc [ebx + device.packets_tx] mov eax, [buffersize] add dword[ebx + device.bytes_tx], eax adc dword[ebx + device.bytes_tx + 4], 0 DEBUGF 1,"Transmit OK\n" spin_unlock_irqrestore xor eax, eax ret .error: DEBUGF 2, "TX packet error\n" inc [ebx + device.packets_tx_err] invoke NetFree, [bufferptr] spin_unlock_irqrestore or eax, -1 ret .overrun: DEBUGF 2, "TX overrun\n" inc [ebx + device.packets_tx_ovr] invoke NetFree, [bufferptr] spin_unlock_irqrestore or eax, -1 ret endp ;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Interrupt handler ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;; align 16 int_handler: push ebx esi edi mov ebx, [esp+4*4] DEBUGF 1,"INT for 0x%x\n", ebx ; TODO? if we are paranoid, we can check that the value from ebx is present in the current device_list mov edi, [ebx + device.mmio_addr] ; mov eax, [edi + REG_ICR] test eax, eax jz .nothing DEBUGF 1,"Status: %x ", eax ; TODO: handle interrupts pop edi esi ebx xor eax, eax inc eax ret .nothing: pop edi esi ebx xor eax, eax ret ; End of code data fixups end data include '../peimport.inc' my_service db 'BCM57XX',0 ; max 16 chars include zero include_debug_strings ; All data wich FDO uses will be included here align 4 devices dd 0 device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling