kolibrios/kernel/branches/net/drivers/i8255x.asm

1112 lines
26 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; i8255x (Intel eepro 100) driver for KolibriOS ;;
;; ;;
;; Written by hidnplayr@kolibrios.org ;;
;; ;;
;; GNU GENERAL PUBLIC LICENSE ;;
;; Version 2, June 1991 ;;
;; ;;
;; ;;
;; Good read about how to program this family of devices: ;;
;; http://www.intel.com/design/network/manuals/8255x_opensdm.htm ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
format MS COFF
API_VERSION = 0x01000100
DRIVER_VERSION = 5
MAX_DEVICES = 16
DEBUG = 1
__DEBUG__ = 1
__DEBUG_LEVEL__ = 1
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
.io_addr dd ?
.pci_bus db ?
.pci_dev db ?
.irq_line db ?
.rx_buffer dd ?
.tx_buffer dd ?
.ee_bus_width dd ?
rb 5+8 ;;;; align
rxfd:
.status dw ?
.command dw ?
.link dd ?
.rx_buf_addr dd ?
.count dw ?
.size dw ?
.packet dd ?
rb 12 ;;;;
txfd:
.status dw ?
.command dw ?
.link dd ?
.tx_desc_addr dd ?
.count dd ?
.tx_buf_addr0 dd ?
.tx_buf_size0 dd ?
.tx_buf_addr1 dd ?
.tx_buf_size1 dd ?
confcmd:
.status: dw ?
.command: dw ?
.link: dd ?
.data rb 64
lstats:
tx_good_frames dd ?
tx_coll16_errs dd ?
tx_late_colls dd ?
tx_underruns dd ?
tx_lost_carrier dd ?
tx_deferred dd ?
tx_one_colls dd ?
tx_multi_colls dd ?
tx_total_colls dd ?
rx_good_frames dd ?
rx_crc_errs dd ?
rx_align_errs dd ?
rx_resource_errs dd ?
rx_overrun_errs dd ?
rx_colls_errs dd ?
rx_runt_errs dd ?
device_size = $ - device
end virtual
; Serial EEPROM
EE_SK = 1 shl 16 ; serial clock
EE_CS = 1 shl 17 ; chip select
EE_DI = 1 shl 18 ; data in
EE_DO = 1 shl 19 ; data out
EE_READ = 110b
EE_WRITE = 101b
EE_ERASE = 111b
; The SCB accepts the following controls for the Tx and Rx units:
CU_START = 0x0010
CU_RESUME = 0x0020
CU_STATSADDR = 0x0040
CU_SHOWSTATS = 0x0050 ; Dump statistics counters.
CU_CMD_BASE = 0x0060 ; Base address to add to add CU commands.
CU_DUMPSTATS = 0x0070 ; Dump then reset stats counters.
RX_START = 0x0001
RX_RESUME = 0x0002
RX_ABORT = 0x0004
RX_ADDR_LOAD = 0x0006
RX_RESUMENR = 0x0007
INT_MASK = 0x0100
DRVR_INT = 0x0200 ; Driver generated interrupt
CmdIASetup = 0x0001
CmdConfigure = 0x0002
CmdTx = 0x0004 ;;;;
CmdTxFlex = 0x0008 ;;;
Cmdsuspend = 0x4000
reg_scb_status = 0
reg_scb_cmd = 2
reg_scb_ptr = 4
reg_port = 8
reg_eeprom_ctrl = 12
reg_eeprom = 14
reg_mdi_ctrl = 16
macro delay {
push eax
in eax, dx
in eax, dx
in eax, dx
in eax, dx
in eax, dx
in eax, dx
in eax, dx
in eax, dx
in eax, dx
in eax, dx
pop eax
}
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 i8255x 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 esi, device_list
mov ecx, [devices]
test ecx, ecx
jz .firstdevice
; 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
push edx
stdcall KernelAlloc, device_size
pop edx
test eax, eax
jz .fail
mov ebx, eax ; ebx is always used as a pointer to the structure (in driver, but also in kernel code)
; 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], MAC_write
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 2,"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.rx_buffer], (4096), .err
allocate_and_clear [device.tx_buffer], (4096), .err
; 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 [device.type], NET_TYPE_ETH
call NetRegDev
cmp eax, -1
je .err
ret
; If the device was already loaded, find the device number and return it in eax
.find_devicenum:
DEBUGF 2,"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 2,"Kernel says: %u\n", eax
ret
; If an error occured, remove all allocated data and exit (returning -1 in eax)
.err:
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 ;;
;; ;;
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
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
;
;-------------
align 4
probe:
DEBUGF 1,"Probing i8255x\n"
make_bus_master [device.pci_bus], [device.pci_dev]
;---------------------------
; First, identify the device
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, 0x8086
jne .notfound
shr eax, 16
DEBUGF 1,"Device_id=0x%x\n", ax
mov ecx, DEVICE_IDs
mov edi, device_id_list
repne scasw
jne .notfound
jmp .found
.notfound:
DEBUGF 1,"ERROR: Unsupported device!\n"
or eax, -1
ret
.found:
call ee_get_width
call MAC_read_eeprom
;;; TODO: detect phy
;----------
;
; Reset
;
;----------
align 4
reset:
;---------------
; reset the card
set_io 0
set_io reg_port
xor eax, eax ; Software Reset
out dx, eax
mov esi, 10
call Sleep ; Give the card time to warm up.
;---------------------------------
; Tell device where to store stats
lea eax, [lstats]
GetRealAddr
set_io reg_scb_ptr
out dx, eax
mov ax, INT_MASK + CU_STATSADDR
set_io reg_scb_cmd
out dx, ax
call cmd_wait
;-----------------
; Set CU base to 0
xor eax, eax
set_io reg_scb_ptr
out dx, eax
mov ax, INT_MASK + RX_ADDR_LOAD
set_io reg_scb_cmd
out dx, ax
call cmd_wait
;---------------------
; build rxfd structure
mov ax, 0x0001
mov [rxfd.status], ax
mov ax, 0x0000
mov [rxfd.command], ax
lea eax, [rxfd.status]
GetRealAddr
mov [rxfd.link], eax
lea eax, [device.rx_buffer]
GetRealAddr
mov [rxfd.rx_buf_addr], eax
xor ax, ax
mov [rxfd.count], ax
mov ax, 1528
mov [rxfd.size], ax
;-------------------------------
; Set ptr to first command block
set_io reg_scb_ptr
lea eax, [rxfd]
GetRealAddr
out dx, eax
set_io reg_scb_cmd
mov ax, INT_MASK + RX_START
out dx, ax
call cmd_wait
;-------------------
; start the receiver
mov [rxfd.status], 0
mov [rxfd.command], 0xc000
set_io reg_scb_ptr
lea eax, [rxfd]
GetRealAddr
out dx, eax
set_io reg_scb_cmd
mov ax, INT_MASK + RX_START
out dx, ax
call cmd_wait
;-----------------
; set CU base to 0
set_io reg_scb_ptr
xor eax, eax
out dx, eax
set_io reg_scb_cmd
mov ax, INT_MASK + CU_CMD_BASE
out dx, ax
call cmd_wait
;--------------------
; Set TX Base address
; First, set up confcmd values
mov [txfd.command], CmdIASetup
mov [txfd.status], 0
lea eax, [confcmd]
GetRealAddr
mov [txfd.link], eax
mov word [confcmd.command], Cmdsuspend + CmdConfigure
mov word [confcmd.status], 0
lea eax, [txfd]
GetRealAddr
mov [confcmd.link], eax
mov byte [confcmd.data + 1], 0x88 ; fifo of 8 each
mov byte [confcmd.data + 4], 0
mov byte [confcmd.data + 5], 0x80
mov byte [confcmd.data + 15], 0x48
mov byte [confcmd.data + 19], 0x80
mov byte [confcmd.data + 21], 0x05
; CU start
; lea eax, [txfd]
; GetRealAddr
set_io 0
set_io reg_scb_ptr
out dx, eax
mov ax, INT_MASK + CU_START
set_io reg_scb_cmd
out dx, ax
call cmd_wait
; wait for thing to start
; drp004:
;
; cmp [txfd.status], 0
; jz drp004
; Indicate that we have successfully reset the card
;;; enable interrupts
xor eax, eax
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], 1500
jg .error ; packet is too long
cmp dword [esp+8], 60
jl .error ; packet is too short
set_io 0
in ax, dx
and ax, 0xfc00
out dx, ax
mov [txfd.status], 0
mov [txfd.command], Cmdsuspend + CmdTx + CmdTxFlex
lea eax, [txfd]
GetRealAddr
mov [txfd.link], eax
mov [txfd.count], 0x02208000
lea eax, [txfd.tx_buf_addr0]
GetRealAddr
mov [txfd.tx_desc_addr], eax
mov eax, [esp+4]
mov [txfd.tx_buf_addr0], eax
mov eax, [esp+8]
mov [txfd.tx_buf_size0], eax
; Copy the buffer address and size in
mov [txfd.tx_buf_addr1], 0
mov [txfd.tx_buf_size1], 0
lea eax, [txfd]
GetRealAddr
set_io reg_scb_ptr
out dx, eax
mov ax, INT_MASK + CU_START
set_io reg_scb_cmd
out dx, ax
call cmd_wait
in ax, dx
.I8t_001:
cmp [txfd.status], 0
je .I8t_001
in ax, dx
.finish:
xor eax, eax
ret 8
.error:
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 .fail
.nextdevice:
mov ebx, dword [esi]
set_io 0
;; set_io REG_ISR
;; in ax , dx
;; out dx , ax ; send it back to ACK
add esi, 4
test ax , ax
jnz .got_it
dec ecx
jnz .nextdevice
ret ; If no device was found, abort (The irq was probably for a device, not registered to this driver)
.got_it:
;;; receive
cmp [rxfd.status], 0
je .nodata
mov [rxfd.status], 0
mov [rxfd.command], 0xc000
set_io reg_scb_ptr
lea eax, [rxfd.status]
GetRealAddr
out dx, eax
set_io reg_scb_cmd
mov ax, INT_MASK + RX_START
out dx, ax
call cmd_wait
movzx ecx, [rxfd.count]
and ecx, 0x3fff
stdcall KernelAlloc, ecx ; Allocate a buffer to put packet into
push ecx
push eax
lea esi, [device.rx_buffer]
.copy:
shr ecx, 1
jnc .nb
movsb
.nb:
shr ecx, 1
jnc .nw
movsw
.nw:
jz .nd
rep movsd
.nd:
jmp EthReceiver ; Send it to kernel
.nodata:
.fail:
ret
align 4
cmd_wait:
in al, dx
test al, al
jnz cmd_wait
ret
align 4
ee_read: ; esi = address to read
set_io 0
set_io reg_eeprom
;-----------------------------------------------------
; Prepend start bit + read opcode to the address field
; and shift it to the very left bits of esi
mov ecx, 32
sub ecx, [device.ee_bus_width]
shl esi, cl
or esi, EE_READ shl 28
mov ecx, [device.ee_bus_width]
add ecx, 3
;-----------------------
; Write this to the chip
.loop:
mov eax, EE_CS
shl esi, 1
jnc @f
or eax, EE_DI
@@:
out dx , eax
delay
or eax, EE_SK
out dx , eax
delay
loop .loop
;------------------------------
; Now read the data from eeprom
xor esi, esi
mov ecx, 16
.loop2:
mov eax, EE_CS + EE_SK
out dx , eax
delay
in eax, dx
test eax, EE_DO
jz @f
inc esi
@@:
shl esi, 1
mov eax, EE_CS
out dx , eax
delay
loop .loop2
;-----------------------
; de-activate the eeprom
xor eax, eax
out dx, eax
DEBUGF 1,"data=%x\n", esi
ret
align 4
ee_write: ; esi = address to write to, di = data
set_io 0
set_io reg_eeprom
;-----------------------------------------------------
; Prepend start bit + write opcode to the address field
; and shift it to the very left bits of esi
mov ecx, 32
sub ecx, [device.ee_bus_width]
shl esi, cl
or esi, EE_WRITE shl 28
mov ecx, [device.ee_bus_width]
add ecx, 3
;-----------------------
; Write this to the chip
.loop:
mov eax, EE_CS
shl esi, 1
jnc @f
or eax, EE_DI
@@:
out dx , eax
delay
or eax, EE_SK
out dx , eax
delay
loop .loop
;-----------------------------
; Now write the data to eeprom
mov ecx, 16
.loop2:
mov eax, EE_CS
shl di , 1
jnc @f
or eax, EE_DI
@@:
out dx , eax
delay
or eax, EE_SK
out dx , eax
delay
loop .loop2
;-----------------------
; de-activate the eeprom
xor eax, eax
out dx, eax
ret
align 4
ee_get_width:
set_io 0
set_io reg_eeprom
mov esi, EE_READ shl 28
xor ecx, ecx
.loop:
mov eax, EE_CS
out dx , eax
delay
or eax, EE_SK
out dx , eax
delay
inc ecx
in eax, dx
test eax, EE_DO
jnz .loop
mov [device.ee_bus_width], ecx
;------------------------------
; Now read the data from eeprom
mov ecx, 16
.loop2:
mov eax, EE_CS + EE_SK
out dx , eax
delay
mov eax, EE_CS
out dx , eax
delay
loop .loop2
;-----------------------
; de-activate the eeprom
xor eax, eax
out dx, eax
ret
; cx = phy addr
; dx = phy reg addr
; ax = data
align 4
mdio_read:
shl ecx, 21 ; PHY addr
shl edx, 16 ; PHY reg addr
mov eax, ecx
or eax, edx
or eax, 10b shl 26 ; read opcode
set_io 0
set_io reg_mdi_ctrl
out dx, eax
.wait:
delay
in eax, dx
test eax, 1 shl 28 ; ready bit
jz .wait
ret
; ax = data
; cx = phy addr
; dx = phy reg addr
; ax = data
align 4
mdio_write:
and eax, 0xffff
shl ecx, 21 ; PHY addr
shl edx, 16 ; PHY reg addr
or eax, ecx
or eax, edx
or eax, 01b shl 26 ; write opcode
set_io 0
set_io reg_mdi_ctrl
out dx, eax
.wait:
delay
in eax, dx
test eax, 1 shl 28 ; ready bit
jz .wait
ret
align 4
MAC_read_eeprom:
;;;;
ret
align 4
MAC_write:
;;;;
ret
; End of code
align 4 ; Place all initialised data here
devices dd 0 ; number of currently running devices
version dd (DRIVER_VERSION shl 16) or (API_VERSION and 0xFFFF)
my_service db 'i8255x',0 ; max 16 chars include zero
devicename db 'Intel Etherexpress pro/100',0
device_id_list:
dw 0x1029
dw 0x1030
dw 0x1031
dw 0x1032
dw 0x1033
dw 0x1034
dw 0x1038
dw 0x1039
dw 0x103A
dw 0x103B
dw 0x103C
dw 0x103D
dw 0x103E
dw 0x1050
dw 0x1051
dw 0x1052
dw 0x1053
dw 0x1054
dw 0x1055
dw 0x1056
dw 0x1057
dw 0x1059
dw 0x1064
dw 0x1065
dw 0x1066
dw 0x1067
dw 0x1068
dw 0x1069
dw 0x106A
dw 0x106B
dw 0x1091
dw 0x1092
dw 0x1093
dw 0x1094
dw 0x1095
dw 0x10fe
dw 0x1209
dw 0x1229
dw 0x2449
dw 0x2459
dw 0x245D
dw 0x27DC
DEVICE_IDs = ($ - device_id_list) / 2
mac_82557_D100_A = 0
mac_82557_D100_B = 1
mac_82557_D100_C = 2
mac_82558_D101_A4 = 4
mac_82558_D101_B0 = 5
mac_82559_D101M = 8
mac_82559_D101S = 9
mac_82550_D102 = 12
mac_82550_D102_C = 13
mac_82551_E = 14
mac_82551_F = 15
mac_82551_10 = 16
mac_unknown = 0xFF
phy_100a = 0x000003E0
phy_100c = 0x035002A8
phy_82555_tx = 0x015002A8
phy_nsc_tx = 0x5C002000
phy_82562_et = 0x033002A8
phy_82562_em = 0x032002A8
phy_82562_ek = 0x031002A8
phy_82562_eh = 0x017002A8
phy_82552_v = 0xd061004d
phy_unknown = 0xFFFFFFFF
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