Fasm: Fixed a bug when building programs with the length of the included file name...
[kolibrios.git] / drivers / ethernet / i8254x.asm
blob9f7d3e4ab8c9d59fd88a82a5b0d8c46090916e9e
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; ;;
3 ;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;;
4 ;; Distributed under terms of the GNU General Public License ;;
5 ;; ;;
6 ;; i8254x driver for KolibriOS ;;
7 ;; ;;
8 ;; based on i8254x.asm from baremetal os ;;
9 ;; ;;
10 ;; Written by hidnplayr (hidnplayr@gmail.com) ;;
11 ;; ;;
12 ;; GNU GENERAL PUBLIC LICENSE ;;
13 ;; Version 2, June 1991 ;;
14 ;; ;;
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
17 format PE DLL native
18 entry START
20 CURRENT_API = 0x0200
21 COMPATIBLE_API = 0x0100
22 API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API
24 MAX_DEVICES = 16
26 __DEBUG__ = 1
27 __DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors only
29 MAX_PKT_SIZE = 1514 ; Maximum packet size
31 RX_RING_SIZE = 64 ; Must be a power of 2, and minimum 8
32 TX_RING_SIZE = 64 ; Must be a power of 2, and minimum 8
34 section '.flat' readable writable executable
36 include '../proc32.inc'
37 include '../struct.inc'
38 include '../macros.inc'
39 include '../fdo.inc'
40 include '../netdrv.inc'
42 ; Register list
43 REG_CTRL = 0x0000 ; Control Register
44 REG_STATUS = 0x0008 ; Device Status Register
45 REG_CTRLEXT = 0x0018 ; Extended Control Register
46 REG_MDIC = 0x0020 ; MDI Control Register
47 REG_FCAL = 0x0028 ; Flow Control Address Low
48 REG_FCAH = 0x002C ; Flow Control Address High
49 REG_FCT = 0x0030 ; Flow Control Type
50 REG_VET = 0x0038 ; VLAN Ether Type
51 REG_ICR = 0x00C0 ; Interrupt Cause Read
52 REG_ITR = 0x00C4 ; Interrupt Throttling Register
53 REG_ICS = 0x00C8 ; Interrupt Cause Set Register
54 REG_IMS = 0x00D0 ; Interrupt Mask Set/Read Register
55 REG_IMC = 0x00D8 ; Interrupt Mask Clear Register
56 REG_RCTL = 0x0100 ; Receive Control Register
57 REG_FCTTV = 0x0170 ; Flow Control Transmit Timer Value
58 REG_TXCW = 0x0178 ; Transmit Configuration Word
59 REG_RXCW = 0x0180 ; Receive Configuration Word
60 REG_TCTL = 0x0400 ; Transmit Control Register
61 REG_TIPG = 0x0410 ; Transmit Inter Packet Gap
63 REG_LEDCTL = 0x0E00 ; LED Control
64 REG_PBA = 0x1000 ; Packet Buffer Allocation
66 REG_RDBAL = 0x2800 ; RX Descriptor Base Address Low
67 REG_RDBAH = 0x2804 ; RX Descriptor Base Address High
68 REG_RDLEN = 0x2808 ; RX Descriptor Length
69 REG_RDH = 0x2810 ; RX Descriptor Head
70 REG_RDT = 0x2818 ; RX Descriptor Tail
71 REG_RDTR = 0x2820 ; RX Delay Timer Register
72 REG_RXDCTL = 0x3828 ; RX Descriptor Control
73 REG_RADV = 0x282C ; RX Int. Absolute Delay Timer
74 REG_RSRPD = 0x2C00 ; RX Small Packet Detect Interrupt
76 REG_TXDMAC = 0x3000 ; TX DMA Control
77 REG_TDBAL = 0x3800 ; TX Descriptor Base Address Low
78 REG_TDBAH = 0x3804 ; TX Descriptor Base Address High
79 REG_TDLEN = 0x3808 ; TX Descriptor Length
80 REG_TDH = 0x3810 ; TX Descriptor Head
81 REG_TDT = 0x3818 ; TX Descriptor Tail
82 REG_TIDV = 0x3820 ; TX Interrupt Delay Value
83 REG_TXDCTL = 0x3828 ; TX Descriptor Control
84 REG_TADV = 0x382C ; TX Absolute Interrupt Delay Value
85 REG_TSPMT = 0x3830 ; TCP Segmentation Pad & Min Threshold
87 REG_RXCSUM = 0x5000 ; RX Checksum Control
89 ; Register list for i8254x
90 I82542_REG_RDTR = 0x0108 ; RX Delay Timer Register
91 I82542_REG_RDBAL = 0x0110 ; RX Descriptor Base Address Low
92 I82542_REG_RDBAH = 0x0114 ; RX Descriptor Base Address High
93 I82542_REG_RDLEN = 0x0118 ; RX Descriptor Length
94 I82542_REG_RDH = 0x0120 ; RDH for i82542
95 I82542_REG_RDT = 0x0128 ; RDT for i82542
96 I82542_REG_TDBAL = 0x0420 ; TX Descriptor Base Address Low
97 I82542_REG_TDBAH = 0x0424 ; TX Descriptor Base Address Low
98 I82542_REG_TDLEN = 0x0428 ; TX Descriptor Length
99 I82542_REG_TDH = 0x0430 ; TDH for i82542
100 I82542_REG_TDT = 0x0438 ; TDT for i82542
102 ; CTRL - Control Register (0x0000)
103 CTRL_FD = 0x00000001 ; Full Duplex
104 CTRL_LRST = 0x00000008 ; Link Reset
105 CTRL_ASDE = 0x00000020 ; Auto-speed detection
106 CTRL_SLU = 0x00000040 ; Set Link Up
107 CTRL_ILOS = 0x00000080 ; Invert Loss of Signal
108 CTRL_SPEED_MASK = 0x00000300 ; Speed selection
109 CTRL_SPEED_SHIFT = 8
110 CTRL_FRCSPD = 0x00000800 ; Force Speed
111 CTRL_FRCDPLX = 0x00001000 ; Force Duplex
112 CTRL_SDP0_DATA = 0x00040000 ; SDP0 data
113 CTRL_SDP1_DATA = 0x00080000 ; SDP1 data
114 CTRL_SDP0_IODIR = 0x00400000 ; SDP0 direction
115 CTRL_SDP1_IODIR = 0x00800000 ; SDP1 direction
116 CTRL_RST = 0x04000000 ; Device Reset
117 CTRL_RFCE = 0x08000000 ; RX Flow Ctrl Enable
118 CTRL_TFCE = 0x10000000 ; TX Flow Ctrl Enable
119 CTRL_VME = 0x40000000 ; VLAN Mode Enable
120 CTRL_PHY_RST = 0x80000000 ; PHY reset
122 ; STATUS - Device Status Register (0x0008)
123 STATUS_FD = 0x00000001 ; Full Duplex
124 STATUS_LU = 0x00000002 ; Link Up
125 STATUS_TXOFF = 0x00000010 ; Transmit paused
126 STATUS_TBIMODE = 0x00000020 ; TBI Mode
127 STATUS_SPEED_MASK = 0x000000C0 ; Link Speed setting
128 STATUS_SPEED_SHIFT = 6
129 STATUS_ASDV_MASK = 0x00000300 ; Auto Speed Detection
130 STATUS_ASDV_SHIFT = 8
131 STATUS_PCI66 = 0x00000800 ; PCI bus speed
132 STATUS_BUS64 = 0x00001000 ; PCI bus width
133 STATUS_PCIX_MODE = 0x00002000 ; PCI-X mode
134 STATUS_PCIXSPD_MASK = 0x0000C000 ; PCI-X speed
135 STATUS_PCIXSPD_SHIFT = 14
137 ; CTRL_EXT - Extended Device Control Register (0x0018)
138 CTRLEXT_PHY_INT = 0x00000020 ; PHY interrupt
139 CTRLEXT_SDP6_DATA = 0x00000040 ; SDP6 data
140 CTRLEXT_SDP7_DATA = 0x00000080 ; SDP7 data
141 CTRLEXT_SDP6_IODIR = 0x00000400 ; SDP6 direction
142 CTRLEXT_SDP7_IODIR = 0x00000800 ; SDP7 direction
143 CTRLEXT_ASDCHK = 0x00001000 ; Auto-Speed Detect Chk
144 CTRLEXT_EE_RST = 0x00002000 ; EEPROM reset
145 CTRLEXT_SPD_BYPS = 0x00008000 ; Speed Select Bypass
146 CTRLEXT_RO_DIS = 0x00020000 ; Relaxed Ordering Dis.
147 CTRLEXT_LNKMOD_MASK = 0x00C00000 ; Link Mode
148 CTRLEXT_LNKMOD_SHIFT = 22
150 ; MDIC - MDI Control Register (0x0020)
151 MDIC_DATA_MASK = 0x0000FFFF ; Data
152 MDIC_REG_MASK = 0x001F0000 ; PHY Register
153 MDIC_REG_SHIFT = 16
154 MDIC_PHY_MASK = 0x03E00000 ; PHY Address
155 MDIC_PHY_SHIFT = 21
156 MDIC_OP_MASK = 0x0C000000 ; Opcode
157 MDIC_OP_SHIFT = 26
158 MDIC_R = 0x10000000 ; Ready
159 MDIC_I = 0x20000000 ; Interrupt Enable
160 MDIC_E = 0x40000000 ; Error
162 ; ICR - Interrupt Cause Read (0x00c0)
163 ICR_TXDW = 0x00000001 ; TX Desc Written back
164 ICR_TXQE = 0x00000002 ; TX Queue Empty
165 ICR_LSC = 0x00000004 ; Link Status Change
166 ICR_RXSEQ = 0x00000008 ; RX Sence Error
167 ICR_RXDMT0 = 0x00000010 ; RX Desc min threshold reached
168 ICR_RXO = 0x00000040 ; RX Overrun
169 ICR_RXT0 = 0x00000080 ; RX Timer Interrupt
170 ICR_MDAC = 0x00000200 ; MDIO Access Complete
171 ICR_RXCFG = 0x00000400
172 ICR_PHY_INT = 0x00001000 ; PHY Interrupt
173 ICR_GPI_SDP6 = 0x00002000 ; GPI on SDP6
174 ICR_GPI_SDP7 = 0x00004000 ; GPI on SDP7
175 ICR_TXD_LOW = 0x00008000 ; TX Desc low threshold hit
176 ICR_SRPD = 0x00010000 ; Small RX packet detected
178 ; RCTL - Receive Control Register (0x0100)
179 RCTL_EN = 0x00000002 ; Receiver Enable
180 RCTL_SBP = 0x00000004 ; Store Bad Packets
181 RCTL_UPE = 0x00000008 ; Unicast Promiscuous Enabled
182 RCTL_MPE = 0x00000010 ; Xcast Promiscuous Enabled
183 RCTL_LPE = 0x00000020 ; Long Packet Reception Enable
184 RCTL_LBM_MASK = 0x000000C0 ; Loopback Mode
185 RCTL_LBM_SHIFT = 6
186 RCTL_RDMTS_MASK = 0x00000300 ; RX Desc Min Threshold Size
187 RCTL_RDMTS_SHIFT = 8
188 RCTL_MO_MASK = 0x00003000 ; Multicast Offset
189 RCTL_MO_SHIFT = 12
190 RCTL_BAM = 0x00008000 ; Broadcast Accept Mode
191 RCTL_BSIZE_MASK = 0x00030000 ; RX Buffer Size
192 RCTL_BSIZE_SHIFT = 16
193 RCTL_VFE = 0x00040000 ; VLAN Filter Enable
194 RCTL_CFIEN = 0x00080000 ; CFI Enable
195 RCTL_CFI = 0x00100000 ; Canonical Form Indicator Bit
196 RCTL_DPF = 0x00400000 ; Discard Pause Frames
197 RCTL_PMCF = 0x00800000 ; Pass MAC Control Frames
198 RCTL_BSEX = 0x02000000 ; Buffer Size Extension
199 RCTL_SECRC = 0x04000000 ; Strip Ethernet CRC
201 ; TCTL - Transmit Control Register (0x0400)
202 TCTL_EN = 0x00000002 ; Transmit Enable
203 TCTL_PSP = 0x00000008 ; Pad short packets
204 TCTL_SWXOFF = 0x00400000 ; Software XOFF Transmission
206 ; PBA - Packet Buffer Allocation (0x1000)
207 PBA_RXA_MASK = 0x0000FFFF ; RX Packet Buffer
208 PBA_RXA_SHIFT = 0
209 PBA_TXA_MASK = 0xFFFF0000 ; TX Packet Buffer
210 PBA_TXA_SHIFT = 16
212 ; Flow Control Type
213 FCT_TYPE_DEFAULT = 0x8808
217 ; === TX Descriptor ===
219 struct TDESC
220 addr_l dd ?
221 addr_h dd ?
222 length_cso_cmd dd ? ; 16 bits length + 8 bits cso + 8 bits cmd
223 status dd ? ; status, checksum start field, special
224 ends
226 ; TX Packet Length (word 2)
227 TXDESC_LEN_MASK = 0x0000ffff
229 ; TX Descriptor CMD field (word 2)
230 TXDESC_IDE = 0x80000000 ; Interrupt Delay Enable
231 TXDESC_VLE = 0x40000000 ; VLAN Packet Enable
232 TXDESC_DEXT = 0x20000000 ; Extension
233 TXDESC_RPS = 0x10000000 ; Report Packet Sent
234 TXDESC_RS = 0x08000000 ; Report Status
235 TXDESC_IC = 0x04000000 ; Insert Checksum
236 TXDESC_IFCS = 0x02000000 ; Insert FCS
237 TXDESC_EOP = 0x01000000 ; End Of Packet
239 ; TX Descriptor STA field (word 3)
240 TXDESC_TU = 0x00000008 ; Transmit Underrun
241 TXDESC_LC = 0x00000004 ; Late Collision
242 TXDESC_EC = 0x00000002 ; Excess Collisions
243 TXDESC_DD = 0x00000001 ; Descriptor Done
247 ; === RX Descriptor ===
249 struct RDESC
250 addr_l dd ?
251 addr_h dd ?
252 status_l dd ?
253 status_h dd ?
254 ends
256 ; RX Packet Length (word 2)
257 RXDESC_LEN_MASK = 0x0000ffff
259 ; RX Descriptor STA field (word 3)
260 RXDESC_PIF = 0x00000080 ; Passed In-exact Filter
261 RXDESC_IPCS = 0x00000040 ; IP cksum calculated
262 RXDESC_TCPCS = 0x00000020 ; TCP cksum calculated
263 RXDESC_VP = 0x00000008 ; Packet is 802.1Q
264 RXDESC_IXSM = 0x00000004 ; Ignore cksum indication
265 RXDESC_EOP = 0x00000002 ; End Of Packet
266 RXDESC_DD = 0x00000001 ; Descriptor Done
268 struct device ETH_DEVICE
270 mmio_addr dd ?
271 pci_bus dd ?
272 pci_dev dd ?
273 irq_line db ?
275 cur_rx dd ?
276 cur_tx dd ?
277 last_tx dd ?
279 rb 0x100 - ($ and 0xff) ; align 256
280 rx_desc rb RX_RING_SIZE*sizeof.RDESC*2
282 rb 0x100 - ($ and 0xff) ; align 256
283 tx_desc rb TX_RING_SIZE*sizeof.TDESC*2
285 ends
287 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
288 ;; ;;
289 ;; proc START ;;
290 ;; ;;
291 ;; (standard driver proc) ;;
292 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
294 proc START c, reason:dword, cmdline:dword
296 cmp [reason], DRV_ENTRY
297 jne .fail
299 DEBUGF 1,"Loading driver\n"
300 invoke RegService, my_service, service_proc
303 .fail:
304 xor eax, eax
307 endp
310 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
311 ;; ;;
312 ;; proc SERVICE_PROC ;;
313 ;; ;;
314 ;; (standard driver proc) ;;
315 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
317 align 4
318 proc service_proc stdcall, ioctl:dword
320 mov edx, [ioctl]
321 mov eax, [edx + IOCTL.io_code]
323 ;------------------------------------------------------
325 cmp eax, 0 ;SRV_GETVERSION
326 jne @F
328 cmp [edx + IOCTL.out_size], 4
329 jb .fail
330 mov eax, [edx + IOCTL.output]
331 mov dword[eax], API_VERSION
333 xor eax, eax
336 ;------------------------------------------------------
338 cmp eax, 1 ;SRV_HOOK
339 jne .fail
341 cmp [edx + IOCTL.inp_size], 3 ; Data input must be at least 3 bytes
342 jb .fail
344 mov eax, [edx + IOCTL.input]
345 cmp byte[eax], 1 ; 1 means device number and bus number (pci) are given
346 jne .fail ; other types arent supported for this card yet
348 ; check if the device is already listed
350 mov esi, device_list
351 mov ecx, [devices]
352 test ecx, ecx
353 jz .firstdevice
355 ; mov eax, [edx + IOCTL.input] ; get the pci bus and device numbers
356 mov ax, [eax+1] ;
357 .nextdevice:
358 mov ebx, [esi]
359 cmp al, byte[ebx + device.pci_bus]
360 jne .next
361 cmp ah, byte[ebx + device.pci_dev]
362 je .find_devicenum ; Device is already loaded, let's find it's device number
363 .next:
364 add esi, 4
365 loop .nextdevice
368 ; This device doesnt have its own eth_device structure yet, lets create one
369 .firstdevice:
370 cmp [devices], MAX_DEVICES ; First check if the driver can handle one more card
371 jae .fail
373 allocate_and_clear ebx, sizeof.device, .fail ; Allocate the buffer for device structure
375 ; Fill in the direct call addresses into the struct
377 mov [ebx + device.reset], reset
378 mov [ebx + device.transmit], transmit
379 mov [ebx + device.unload], unload
380 mov [ebx + device.name], my_service
382 ; save the pci bus and device numbers
384 mov eax, [edx + IOCTL.input]
385 movzx ecx, byte[eax+1]
386 mov [ebx + device.pci_bus], ecx
387 movzx ecx, byte[eax+2]
388 mov [ebx + device.pci_dev], ecx
390 ; Now, it's time to find the base mmio addres of the PCI device
392 stdcall PCI_find_mmio, [ebx + device.pci_bus], [ebx + device.pci_dev] ; returns in eax
393 test eax, eax
394 jz .destroy
396 ; Create virtual mapping of the physical memory
398 invoke MapIoMem, eax, 10000h, PG_SW+PG_NOCACHE
399 mov [ebx + device.mmio_addr], eax
401 ; We've found the mmio address, find IRQ now
403 invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.interrupt_line
404 mov [ebx + device.irq_line], al
406 DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\
407 [ebx + device.pci_dev]:1,[ebx + device.pci_bus]:1,[ebx + device.irq_line]:1,[ebx + device.mmio_addr]:8
409 ; Ok, the eth_device structure is ready, let's probe the device
410 call probe ; this function will output in eax
411 test eax, eax
412 jnz .err ; If an error occured, exit
414 mov eax, [devices] ; Add the device structure to our device list
415 mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device)
416 inc [devices] ;
418 call start_i8254x
419 test eax, eax
420 jnz .destroy
422 mov [ebx + device.type], NET_TYPE_ETH
423 invoke NetRegDev
424 cmp eax, -1
425 je .destroy
429 ; If the device was already loaded, find the device number and return it in eax
431 .find_devicenum:
432 DEBUGF 1,"Trying to find device number of already registered device\n"
433 invoke NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx
434 ; into a device number in edi
435 mov eax, edi ; Application wants it in eax instead
436 DEBUGF 1,"Kernel says: %u\n", eax
439 ; If an error occured, remove all allocated data and exit (returning -1 in eax)
441 .destroy:
442 ; todo: reset device into virgin state
444 .err:
445 invoke KernelFree, ebx
447 .fail:
448 DEBUGF 2,"Loading driver failed\n"
449 or eax, -1
452 ;------------------------------------------------------
453 endp
456 ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
457 ;; ;;
458 ;; Actual Hardware dependent code starts here ;;
459 ;; ;;
460 ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;;
463 align 4
464 unload:
466 DEBUGF 1,"Unload\n"
468 ; TODO: (in this particular order)
470 ; - Stop the device
471 ; - Detach int handler
472 ; - Remove device from local list (device_list)
473 ; - call unregister function in kernel
474 ; - Remove all allocated structures and buffers the card used
476 or eax, -1
481 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
483 ;; probe: enables the device (if it really is I8254X)
485 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
486 align 4
487 probe:
489 DEBUGF 1,"Probe\n"
491 ; Make the device a bus master
492 invoke PciRead32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command
493 or al, PCI_CMD_MASTER
494 invoke PciWrite32, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax
496 ; TODO: validate the device
498 call read_mac
500 movzx eax, [ebx + device.irq_line]
501 DEBUGF 1,"Attaching int handler to irq %x\n", eax:1
502 invoke AttachIntHandler, eax, int_handler, ebx
503 test eax, eax
504 jnz @f
505 DEBUGF 2,"Could not attach int handler!\n"
506 or eax, -1
511 reset_dontstart:
512 DEBUGF 1,"Reset\n"
514 mov esi, [ebx + device.mmio_addr]
516 or dword[esi + REG_CTRL], CTRL_RST ; reset device
517 .loop:
518 push esi
519 xor esi, esi
520 inc esi
521 invoke Sleep
522 pop esi
523 test dword[esi + REG_CTRL], CTRL_RST
524 jnz .loop
526 mov dword[esi + REG_IMC], 0xffffffff ; Disable all interrupt causes
527 mov eax, dword [esi + REG_ICR] ; Clear any pending interrupts
528 mov dword[esi + REG_ITR], 0 ; Disable interrupt throttling logic
530 mov dword[esi + REG_PBA], 0x00000004 ; PBA: set the RX buffer size to 4KB (TX buffer is calculated as 64-RX buffer)
531 mov dword[esi + REG_RDTR], 0 ; RDTR: set no delay
533 mov dword[esi + REG_TXCW], 0x00008060 ; TXCW: TxConfigWord (Half/Full duplex, Next Page Reqest)
535 mov eax, [esi + REG_CTRL]
536 or eax, 1 shl 6 + 1 shl 5
537 and eax, not (1 shl 3 + 1 shl 7 + 1 shl 30 + 1 shl 31)
538 mov dword [esi + REG_CTRL], eax ; CTRL: clear LRST, set SLU and ASDE, clear RSTPHY, VME, and ILOS
540 lea edi, [esi + 0x5200] ; MTA: reset
541 mov eax, 0xffffffff
542 stosd
543 stosd
544 stosd
545 stosd
547 call init_rx
548 test eax, eax
549 jnz .fail
550 call init_tx
552 xor eax, eax
553 .fail:
558 align 4
559 init_rx:
561 lea edi, [ebx + device.rx_desc]
562 mov ecx, RX_RING_SIZE
563 .loop:
564 push ecx
565 push edi
566 invoke NetAlloc, MAX_PKT_SIZE+NET_BUFF.data
567 test eax, eax
568 jz .out_of_mem
569 DEBUGF 1,"RX buffer: 0x%x\n", eax
570 pop edi
571 mov dword[edi + RX_RING_SIZE*sizeof.RDESC], eax
572 push edi
573 invoke GetPhysAddr
574 pop edi
575 add eax, NET_BUFF.data
576 mov [edi + RDESC.addr_l], eax
577 mov [edi + RDESC.addr_h], 0
578 mov [edi + RDESC.status_l], 0
579 mov [edi + RDESC.status_h], 0
580 add edi, sizeof.RDESC
581 pop ecx
582 dec ecx
583 jnz .loop
585 mov [ebx + device.cur_rx], 0
587 lea eax, [ebx + device.rx_desc]
588 invoke GetPhysAddr
589 mov dword[esi + REG_RDBAL], eax ; Receive Descriptor Base Address Low
590 mov dword[esi + REG_RDBAH], 0 ; Receive Descriptor Base Address High
591 mov dword[esi + REG_RDLEN], RX_RING_SIZE*sizeof.RDESC ; Receive Descriptor Length
592 mov dword[esi + REG_RDH], 0 ; Receive Descriptor Head
593 mov dword[esi + REG_RDT], RX_RING_SIZE-1 ; Receive Descriptor Tail
594 mov dword[esi + REG_RCTL], RCTL_SBP or RCTL_BAM or RCTL_SECRC or RCTL_UPE or RCTL_MPE
595 ; Store Bad Packets, Broadcast Accept Mode, Strip Ethernet CRC from incoming packet, Promiscuous mode
596 xor eax, eax ; success!
599 .out_of_mem:
600 DEBUGF 2,"Out of memory!\n"
601 pop edi ecx
602 or eax, -1 ; error!
607 align 4
608 init_tx:
610 lea edi, [ebx + device.tx_desc]
611 mov ecx, TX_RING_SIZE
612 .loop:
613 mov [edi + TDESC.addr_l], eax
614 mov [edi + TDESC.addr_h], 0
615 mov [edi + TDESC.length_cso_cmd], 0
616 mov [edi + TDESC.status], 0
617 add edi, sizeof.TDESC
618 dec ecx
619 jnz .loop
621 mov [ebx + device.cur_tx], 0
622 mov [ebx + device.last_tx], 0
624 lea eax, [ebx + device.tx_desc]
625 invoke GetPhysAddr
626 mov dword[esi + REG_TDBAL], eax ; Transmit Descriptor Base Address Low
627 mov dword[esi + REG_TDBAH], 0 ; Transmit Descriptor Base Address High
628 mov dword[esi + REG_TDLEN], RX_RING_SIZE*sizeof.TDESC ; Transmit Descriptor Length
629 mov dword[esi + REG_TDH], 0 ; Transmit Descriptor Head
630 mov dword[esi + REG_TDT], 0 ; Transmit Descriptor Tail
631 mov dword[esi + REG_TCTL], 0x010400fa ; Enabled, Pad Short Packets, 15 retrys, 64-byte COLD, Re-transmit on Late Collision
632 mov dword[esi + REG_TIPG], 0x0060200A ; IPGT 10, IPGR1 8, IPGR2 6
637 align 4
638 reset:
639 call reset_dontstart
640 test eax, eax
641 je start_i8254x
645 align 4
646 start_i8254x:
648 mov esi, [ebx + device.mmio_addr]
649 or dword[esi + REG_RCTL], RCTL_EN ; Enable the receiver
651 xor eax, eax
652 mov [esi + REG_RDTR], eax ; Clear the Receive Delay Timer Register
653 mov [esi + REG_RADV], eax ; Clear the Receive Interrupt Absolute Delay Timer
654 mov [esi + REG_RSRPD], eax ; Clear the Receive Small Packet Detect Interrupt
656 mov dword[esi + REG_IMS], 0x1F6DC ; Enable interrupt types
657 mov eax, [esi + REG_ICR] ; Clear pending interrupts
659 mov [ebx + device.mtu], 1514
660 call link_status
662 xor eax, eax
668 align 4
669 read_mac:
671 DEBUGF 1,"Read MAC\n"
673 mov esi, [ebx + device.mmio_addr]
675 mov eax, [esi+0x5400] ; RAL
676 test eax, eax
677 jz .try_eeprom
679 mov dword[ebx + device.mac], eax
680 mov eax, [esi+0x5404] ; RAH
681 mov word[ebx + device.mac+4], ax
683 jmp .mac_ok
685 .try_eeprom:
686 mov dword[esi+0x14], 0x00000001
687 mov eax, [esi+0x14]
688 shr eax, 16
689 mov word[ebx + device.mac], ax
691 mov dword[esi+0x14], 0x00000101
692 mov eax, [esi+0x14]
693 shr eax, 16
694 mov word[ebx + device.mac+2], ax
696 mov dword[esi+0x14], 0x00000201
697 mov eax, [esi+0x14]
698 shr eax, 16
699 mov word[ebx + device.mac+4], ax
701 .mac_ok:
702 DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\
703 [ebx + device.mac+0]:2,[ebx + device.mac+1]:2,[ebx + device.mac+2]:2,\
704 [ebx + device.mac+3]:2,[ebx + device.mac+4]:2,[ebx + device.mac+5]:2
709 link_status:
711 DEBUGF 1,"Verifying link status\n"
713 xor ecx, ecx ; ETH_LINK_DOWN
714 mov esi, [ebx + device.mmio_addr]
715 mov eax, [esi + REG_STATUS]
716 test eax, STATUS_LU
717 jz .ok
719 test eax, STATUS_FD
720 jz @f
721 or cl, ETH_LINK_FULL_DUPLEX
723 shr eax, STATUS_SPEED_SHIFT
724 and al, 3
725 test al, al
726 jnz @f
727 or cl, ETH_LINK_SPEED_10M
728 jmp .ok
730 cmp al, 1
731 jne @f
732 or cl, ETH_LINK_SPEED_100M
733 jmp .ok
735 or cl, ETH_LINK_SPEED_1G
736 ; jmp .ok
738 .ok:
739 mov [ebx + device.state], ecx
740 invoke NetLinkChanged
744 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
745 ;; ;;
746 ;; Transmit ;;
747 ;; ;;
748 ;; In: ebx = pointer to device structure ;;
749 ;; Out: eax = 0 on success ;;
750 ;; ;;
751 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
752 align 16
753 proc transmit stdcall bufferptr
755 spin_lock_irqsave
757 mov esi, [bufferptr]
758 DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [esi + NET_BUFF.length]
759 lea eax, [esi + NET_BUFF.data]
760 DEBUGF 1,"To: %x-%x-%x-%x-%x-%x From: %x-%x-%x-%x-%x-%x Type:%x%x\n",\
761 [eax+00]:2,[eax+01]:2,[eax+02]:2,[eax+03]:2,[eax+04]:2,[eax+05]:2,\
762 [eax+06]:2,[eax+07]:2,[eax+08]:2,[eax+09]:2,[eax+10]:2,[eax+11]:2,\
763 [eax+13]:2,[eax+12]:2
765 cmp [esi + NET_BUFF.length], 1514
766 ja .fail
767 cmp [esi + NET_BUFF.length], 60
768 jb .fail
770 ; Program the descriptor (use legacy mode)
771 mov edi, [ebx + device.cur_tx]
772 DEBUGF 1, "Using TX desc: %u\n", edi
773 shl edi, 4 ; edi = edi * sizeof.TDESC
774 lea edi, [ebx + device.tx_desc + edi]
775 mov eax, esi
776 mov dword[edi + TX_RING_SIZE*sizeof.TDESC], eax ; Store the data location (for driver)
777 add eax, [eax + NET_BUFF.offset]
778 invoke GetPhysAddr
779 mov [edi + TDESC.addr_l], eax ; Data location (for hardware)
780 mov [edi + TDESC.addr_h], 0
782 mov ecx, [esi + NET_BUFF.length]
783 or ecx, TXDESC_EOP + TXDESC_IFCS + TXDESC_RS
784 mov [edi + TDESC.length_cso_cmd], ecx
785 mov [edi + TDESC.status], 0
787 ; Tell i8254x wich descriptor(s) we programmed, by moving the tail
788 mov edi, [ebx + device.mmio_addr]
789 mov eax, [ebx + device.cur_tx]
790 inc eax
791 and eax, TX_RING_SIZE-1
792 mov [ebx + device.cur_tx], eax
793 mov dword[edi + REG_TDT], eax ; TDT - Transmit Descriptor Tail
795 ; Update stats
796 inc [ebx + device.packets_tx]
797 mov eax, [esi + NET_BUFF.length]
798 add dword[ebx + device.bytes_tx], eax
799 adc dword[ebx + device.bytes_tx + 4], 0
801 call clean_tx
803 spin_unlock_irqrestore
804 xor eax, eax
807 .fail:
808 call clean_tx
810 DEBUGF 2,"Send failed\n"
811 invoke NetFree, [bufferptr]
812 spin_unlock_irqrestore
813 or eax, -1
816 endp
819 ;;;;;;;;;;;;;;;;;;;;;;;
820 ;; ;;
821 ;; Interrupt handler ;;
822 ;; ;;
823 ;;;;;;;;;;;;;;;;;;;;;;;
824 align 16
825 int_handler:
827 push ebx esi edi
829 mov ebx, [esp+4*4]
830 DEBUGF 1,"INT for 0x%x\n", ebx
832 ; TODO? if we are paranoid, we can check that the value from ebx is present in the current device_list
834 mov edi, [ebx + device.mmio_addr]
835 mov eax, [edi + REG_ICR]
836 cmp eax, 0xffffffff ; if so, hardware is no longer present
837 je .nothing ;
838 test eax, eax
839 jz .nothing
841 DEBUGF 1,"Status: %x\n", eax
843 ;---------
844 ; RX done?
846 test eax, ICR_RXDMT0 + ICR_RXT0
847 jz .no_rx
849 push eax ebx
850 .retaddr:
851 pop ebx eax
852 ; Get last descriptor addr
853 mov esi, [ebx + device.cur_rx]
854 shl esi, 4 ; esi = esi * sizeof.RDESC
855 lea esi, [ebx + device.rx_desc + esi]
856 cmp byte[esi + RDESC.status_h], 0 ; Check status field
857 je .no_rx
859 push eax ebx
860 push .retaddr
861 movzx ecx, word[esi + 8] ; Get the packet length
862 DEBUGF 1,"got %u bytes\n", ecx
863 mov eax, [esi + RX_RING_SIZE*sizeof.RDESC] ; Get packet pointer
864 push eax
865 mov [eax + NET_BUFF.length], ecx
866 mov [eax + NET_BUFF.device], ebx
867 mov [eax + NET_BUFF.offset], NET_BUFF.data
869 ; Update stats
870 add dword[ebx + device.bytes_rx], ecx
871 adc dword[ebx + device.bytes_rx + 4], 0
872 inc [ebx + device.packets_rx]
874 ; Allocate new descriptor
875 push esi
876 invoke NetAlloc, MAX_PKT_SIZE+NET_BUFF.data
877 pop esi
878 test eax, eax
879 jz .out_of_mem
880 mov dword[esi + RX_RING_SIZE*sizeof.RDESC], eax
881 invoke GetPhysAddr
882 add eax, NET_BUFF.data
883 mov [esi + RDESC.addr_l], eax
884 mov [esi + RDESC.status_l], 0
885 mov [esi + RDESC.status_h], 0
887 ; Move the receive descriptor tail
888 mov esi, [ebx + device.mmio_addr]
889 mov eax, [ebx + device.cur_rx]
890 mov [esi + REG_RDT], eax
892 ; Move to next rx desc
893 inc [ebx + device.cur_rx]
894 and [ebx + device.cur_rx], RX_RING_SIZE-1
896 jmp [EthInput]
898 .out_of_mem:
899 DEBUGF 2,"Out of memory!\n"
901 ; Move to next rx desc
902 inc [ebx + device.cur_rx]
903 and [ebx + device.cur_rx], RX_RING_SIZE-1
905 jmp [EthInput]
906 .no_rx:
908 ;--------------
909 ; Link Changed?
911 test eax, ICR_LSC
912 jz .no_link
914 DEBUGF 2,"Link Changed\n"
916 call link_status
918 .no_link:
920 ;---------------
921 ; Transmit done?
923 test eax, ICR_TXDW
924 jz .no_tx
926 DEBUGF 1,"Transmit done\n"
928 ; call clean_tx
930 .no_tx:
932 pop edi esi ebx
933 xor eax, eax
934 inc eax
937 .nothing:
938 pop edi esi ebx
939 xor eax, eax
945 clean_tx:
947 .txdesc_loop:
948 mov edi, [ebx + device.last_tx]
949 shl edi, 4 ; edi = edi * sizeof.TDESC
950 lea edi, [ebx + device.tx_desc + edi]
951 test [edi + TDESC.status], TXDESC_DD ; Descriptor done?
952 jz .no_tx
953 cmp dword[edi + TX_RING_SIZE*sizeof.TDESC], 0
954 je .no_tx
956 DEBUGF 1,"Cleaning up TX desc: 0x%x\n", edi
958 push ebx
959 push dword[edi + TX_RING_SIZE*sizeof.TDESC]
960 mov dword[edi + TX_RING_SIZE*sizeof.TDESC], 0
961 invoke NetFree
962 pop ebx
964 inc [ebx + device.last_tx]
965 and [ebx + device.last_tx], TX_RING_SIZE-1
966 jmp .txdesc_loop
968 .no_tx:
975 ; End of code
977 data fixups
978 end data
980 include '../peimport.inc'
982 include_debug_strings
983 my_service db 'I8254X', 0 ; max 16 chars include zero
985 align 4
986 devices dd 0
987 device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling