;History:833,1

	include	chip.inc

	include	defs.asm
	include	options.inc

if	CHIP EQ INTEL_82596
	include	82596.inc
else
	include	82557.inc
endif

	include	pci.inc

cgroup	group code, _text, init

code	segment para public 'code'
	assume	cs:cgroup, ds:cgroup

	even

extrn	sys_features : byte
extrn	is_186	: byte		;=0 if 808[68], =1 if 80[123]86.
extrn	is_pci	: byte		;=0 if PCI, =1 if PCI
extrn	decout	: near
extrn	int_no 			: byte
extrn	int_num : word
extrn	mem_map_io	: word
extrn	base_addr : word
extrn	send_pkt : near
extrn	set_ether : near
extrn	do_config : near
extrn	config_b8 : byte
extrn	config_b15 : byte
extrn	reset_chip : near
extrn	wait_27ms : near
NOWARN
extrn	wait : near
WARN
extrn	do_timeout : near
extrn	set_timeout : near

; debug
extrn	scp_buff : byte
extrn	iscp_buff : byte
extrn	temp_buffer : byte

extrn	issue_cmd : near
extrn	wait_scb_non_active : near
extrn	tx_buff_no : word
extrn	buff_ptrs : word
extrn	cmd_buff1 : byte
extrn	cmd_buff2 : byte
extrn	start_rfd : byte
extrn	start_rbd : byte
extrn	start_rx_buff : byte
extrn	first_rfd : word
extrn	last_rfd : word
extrn	first_rbd : word
extrn	last_rbd : word
extrn	enable_board_int : near

if	CHIP EQ INTEL_82596
	extrn	scb_buff	: word
endif

;any code after this will not be kept after initialization. Buffers
;used by the program, if any, are allocated from the memory between
;end_resident and end_free_mem.
	public end_resident,end_free_mem
end_resident	label	byte
end_free_mem	label	byte

	extrn	get_hex: near
	include	getea.asm
	include	messages.inc
	
cnvrt_to_phys_add	dw	?	; Address of subroutine to convert to physical add
	even
virtual_mem_struct:
		dd      0		;- 0 Region Size
		dd		0		;- 4 Region Offset
		dw		0		;- 8 Region Segment
		dw		0		;- A Buffer ID
		dw		0		;- C Low Physical Address
		dw		0		;- E High Physical Address
	
io_addr		dw	0,0		; I/O address specified on the command line
no_prom		db	0	; <> 0 if ethernet address specified on the command line

multiple_cards	db	0			; Set if more than one 'matching' card is found
found_one		db	0			; = 1 if one card is found
pci_bus_dev_no	dw	?			; Storage for PCI bus / device number
temp_storage	dw	0, 0
temp_connector	db	AUTO_CONNECTOR
connector		db	?


	ALIGN	16
self_test_buff	dd	0, -1		; Results of the self-test test placed here

;debug
;	ALIGN	16
;temp_buffer	db	80 dup (0)		; TX buffer for media detect

if	CHIP EQ INTEL_82596
; debug
;	ALIGN	16
;scp_buff	db	(type SCP_STRUC + 7)      dup (0)
;	ALIGN	16
;iscp_buff	db	(type ISCP_STRUC + 7)     dup (0)
else
eeprom_buff	dw	40h DUP (0)
endif

	extrn	set_recv_isr: near

;enter with si -> argument string, di -> wword to store.
;if there is no number, don't change the number.
	extrn	get_number: near

;enter with dx -> argument string, di -> wword to print.
	extrn	print_number: near

;-> the Ethernet address of the card.
	extrn	rom_address: byte

;print the character in al.
	extrn	chrout: near

;print a crlf
	extrn	crlf: near

; Enter with si -> argument string.
; Skip spaces and tabs.  Exit with si -> first non-blank char.
	extrn	skip_blanks: near

	public	parse_args
parse_args:
; Exit with nc if all went well, cy otherwise.
	mov		di, offset cgroup:int_no
	call	get_number
	mov		di, offset cgroup:io_addr
	call	get_number
	mov		di, offset cgroup:mem_map_io
	call	get_number
	call	skip_blanks
	cmp		al, CR			; Does an Ethernet address follow?
	je		parse_args_1	; No.
	inc		no_prom			; We don't have an Ethernet PROM.
; Enter with ds:si -> Ethernet address to parse, es:di -> place to put it.
	movseg	es,ds
	mov		di, offset cgroup:rom_address
	call	get_eaddr
parse_args_1:
	clc
	ret

	public	print_parameters
print_parameters:
;echo our command-line parameters
	mov		di, offset cgroup:int_no
	mov		dx, offset cgroup:int_no_name
	call	print_number
	mov		di, offset cgroup:io_addr
	mov		dx, offset cgroup:io_addr_name
	call	print_number
	ret

	public	etopen
etopen:
; Initialize the driver.
; Fill in rom_address with the assigned address of the board.
	assume	ds:code
	cmp		is_pci, 1
	je		have_pci_bus
	mov 	dx, offset cgroup:need_pci_msg	; A PCI bus is required
	stc
	ret

have_pci_bus:
	.386					; Must have >= 386
	call	check_virtual_mem_services
	call	etopen_chip
	jc		short exit_etopen
	mov		al, int_no		; Get board's interrupt vector
	add		al, 8
	cmp		al, 8+8			; Is it a slave 8259 interrupt?
	jb		short set_int_num		; No.
	add		al, 70h - 8 - 8	; Map it to the real interrupt.
set_int_num:
	xor		ah, ah			; Clear high byte
	mov		int_num, ax		; Set parameter_list int num.
	call	setup_receive	; Setup receive buffers & start reception
	call	set_recv_isr
	call	enable_board_int
	clc
exit_etopen:
	ret


check_virtual_mem_services	PROC	NEAR
	; Check to see if virtual DMA service is being provided.
	; Bit 5 of the Bios Flags at 40:7Bh is set if virtual services present
	; Sets the appropriate routine to use for conversion to physical addresses
    mov     ax, 40h
	mov		es, ax
	test	BYTE PTR es:[007bh], 20h
	jz		short no_virtual_services_found
	print 	virtual_services	; Announce virtual DMA services present
	mov		cnvrt_to_phys_add, offset cgroup:virtual_address_convert
	jmp		short exit_check_virtual_mem_services
no_virtual_services_found:
	print 	no_virtual_services	; Announce no virtual DMA services
	mov		cnvrt_to_phys_add, offset cgroup:linear_address_convert
exit_check_virtual_mem_services:
	ret
check_virtual_mem_services	ENDP


linear_address_convert	PROC	NEAR
	; Address to be converted is in es:si
	; Converted physical address is returned in eax
	xor		eax, eax
	mov		ax, es
	shl		eax, 4
	movzx	esi, si			; Make certain that top part of SI is clear
	add		eax, esi
	ret
linear_address_convert	ENDP


virtual_address_convert	PROC	NEAR
	mov     di, offset cgroup:virtual_mem_struct
	; Lock the memory, which in turn returns the physical address in the DMA descriptor
; debug
	mov     word ptr [di], 200h
	mov     word ptr [di+2], 0
	mov     word ptr [di+4], si
	mov     word ptr [di+6], 0
	mov     [di+8], es

    mov     ax, 8103h		; Lock DMA region
    mov     dx, 0ch			; No alloc, no remap
    push    ds
    pop     es
    int     4Bh
	jc		short exit_virtual_address_convert
    test	[di+0ch], 0FF000000h
	jz		short below_16_meg
;debug
	jmp		short below_16_meg
    mov     ax, 8104h		; Unlock DMA region
    mov     dx, 0			; No copy
    int     4Bh
	print	above_16_meg_msg
	stc

below_16_meg:
; debug
	jmp		short no_unlock


	; Now unlock the DMA region
    mov     ax, 8104h		; Unlock DMA region
    mov     dx, 0			; No copy
    int     4Bh
no_unlock:
    mov     eax, [di+0ch]
	clc
exit_virtual_address_convert:
	ret
virtual_address_convert	ENDP


etopen_chip	PROC	NEAR
	call	find_base			; Go and find the chip
	jc		short exit_etopen_chip
	call	reset_chip
	call	test_chip
	jc		short exit_etopen_chip
if	CHIP EQ INTEL_82557
	call	dump_eeprom			; Copy eeprom contents to RAM
	call	chksum_eeprom		; Validate eeprom contents
else
	call	init_plx_chip		; Initialize the pci interface chip
    call    init_scp			; Initialize the SCP, ISCP, SCB
	jc		short exit_etopen_chip
scp_int_ok:
	call	read_ia_pci			; Get Ethernet address from PCI Config space
endif
	jnc		short configure

	cmp		no_prom, 0			; Did they specify everything we need?
	je		short etopen_failed	; No - exit
address_specified:
	print	assumed_msg			; Yes, tell them we're assuming they're right.
configure:
	call	setup_cmd_buffs		; Setup buffer for commands
	call	config_chip			; Configure chip
	jc		short exit_etopen_chip
	call	do_select_media
if	CHIP EQ INTEL_82557
; debug
;	call	set_data_bit_rate
	cmp		connector, TPE
	clc
	jne		short exit_etopen_chip
	call	do_fdx_hdx_negotiation
endif
exit_etopen_chip:
	ret
etopen_failed:
	stc							; Give up.
	ret
etopen_chip	ENDP

test_chip	PROC	NEAR
	print	self_test_msg			; Announce the start of the self test
	mov		ax, cs
	mov		es, ax
	mov		si, offset cgroup:self_test_buff
	call	cnvrt_to_phys_add
	jnc		short cnvrt_ok
	print	problem_in_phys_add
	stc
	jmp		short exit_test_chip
cnvrt_ok:
	or		al, SELF_TEST_CMD
if	CHIP EQ INTEL_82596
	LOAD_PORT	PLXP_PORT_OFFSET
else
	LOAD_PORT	PORT
endif
	out		dx, eax
	STALL							; Hold things up - briefly
	mov		ax, 1					; Wait, if necessary, for about 27 mili-
	call	set_timeout				;    seconds (5 should be enough)
check_test_buff:
	cmp		self_test_buff, 0h	; Test both DWORDS in the buffer
	je		short not_passed		; Need to pass both tests
	cmp		self_test_buff+4, 0h
	jne		short not_passed
	print	self_test_ok_msg
	clc
	jmp		short exit_test_chip
not_passed:
	call	do_timeout				; Any more time left ?
	jnz		check_test_buff			; Continue to wait if so
	print	self_test_no_msg		; No - announce failure
	stc
exit_test_chip:
	ret
test_chip	ENDP


if	CHIP EQ INTEL_82557
get_eeprom_data PROC NEAR
					; Called with :
					; bx = starting offset address in the EEPROM
					; cx = number of bytes to read
					; ds:di = buffer to place the data
					; si is use for temporary storage

	LOAD_PORT	EEPROM_REG	; Set the eeprom port
get_next_eeprom_byte:
	mov		al, EECS		; EEPROM chip select
	out		dx, al			; Enable the eeprom

;get_next_eeprom_byte:
	push	cx				; Number of bytes left to read
	push	bx				; Address of next byte in eeprom

; First select eeprom for reading
eeprom_ok:
	mov		al, EEDI OR EECS; Set a 1 in the data bit
	write_eeprom_bit		; Write a 1
	write_eeprom_bit		; Write a 1
	mov		al, EECS	; Set a 0 in the data bit
	write_eeprom_bit		; Write a 1

	pop		bx		; Next eeprom address bits
	inc		bx
	push	bx			; Store eeprom address bits
	dec		bx

	ror		bl,1		; Align the address bits so that the MSB
	ror		bl,1		; is at the EEDI position
	ror		bl,1
	mov		cx, 06h		; Number of bits in each address is 6
next_add_bit:
	mov		bh, bl		; Save a copy of the address bits
	and		bl, EEDI	; Clear all bits except EEDI
	and		al, NOT EEDI	; Clear the EDIT bit
	or		al, bl		; EDDI bit in al is now set as required
	write_eeprom_bit		; Write the address bit
	jz		eeprom_address_done;  no need to do all 6 bits
	mov		bl, bh		; Retrive the address bits	
	rol		bl, 1		; Rotate the address bits
	loop	next_add_bit		; Write out all 6 bits

eeprom_address_done:			; Now ready to read the EEPROM data
	xor		si, si		; Byte will be assembled in si
	mov		cx, 10h		; Number of bits to fetch
get_next_bit:
	read_eeprom_bit			; Value of eeprom bit returned in bl
	and		bx, EEDO	; Clear all but the eeprom data output bit
	or		si, bx		; Add the bit to the assembled byte
	rol		si, 1		; Advance byte ready for next bit
	loop	get_next_bit		; Continue until all 16 bits have been read

	mov		cl, 4h		; Final rotate for SI gets the word aligned
	ror		si, cl		;  correctly

	mov		[di], si	; Store the word
	add		di, 2		; Advance the buffer pointer

	pop		bx		; Next EEPROM address to read from
	pop		cx		; Remaining bytes to read
	dec		cx		; Two bytes are read each time
	dec		cx		; So cx is decremented by two
	mov		al, 0		; Disable the EEPROM
	out		dx, al		; Perform the operation
	jcxz	no_more			; If zero then we have finished
	jmp		get_next_eeprom_byte
no_more:
	mov		al, 0		; Disable the EEPROM
	out		dx, al		; Perform the operation
	ret
get_eeprom_data ENDP

dump_eeprom	PROC	NEAR
	push	bx
	push	cx
	push	di

	xor		bx, bx					; Offset into eeprom for start of address
	mov		cx, CHKSUM_LEN * 2		; Number of bytes to read
	mov		di, offset cgroup:eeprom_buff	; Where to write the data
	call	get_eeprom_data			; Get the ethernet address

	pop		di
	pop		cx
	pop		bx
	ret
dump_eeprom	ENDP

chksum_eeprom	PROC	NEAR
	mov		si, offset cgroup:eeprom_buff	; offset of data
	mov		cx, CHKSUM_LEN		; words to chksum
	xor		bx, bx			; Seed chksum buffer so that final sum is zero
chksum_loop:
	lodsw					; get a word
	add		bx, ax			; sum it
	loop	chksum_loop

	cmp		bx, CHKSUM_VAL	; Does it sum to correct signature?
	je		chksum_ok
	print	eeprom_chksum_bad
	stc						; set flag for chksum bad
	jmp		short exit_chksum_eeprom

chksum_ok:
	cmp		no_prom, 0			; User specified ethernet address
	jne		use_user_address 	; Yes - no need to read the eeprom address

	; Copy over the ethernet address
	mov		ax, ds				; ES needs to be correct
	mov		es, ax
	mov		cx, EADDR_LEN/2		; Number of word to move/byte swap
	mov		si, offset cgroup:eeprom_buff
	mov		di, offset cgroup:rom_address	; Where to write ethernet address
	rep		movsw

use_user_address:
	clc
exit_chksum_eeprom:
	ret
chksum_eeprom	ENDP

else
; Routines for the 82596
init_plx_chip	PROC	NEAR
;- Program UserPins 0-3 in Write Mode. RCVDIS should default to 1.
	LOAD_PORT	PLXP_USER_PINS
	mov     al, 0F0h
	or      al, BIT_2
	out     dx, al

	LOAD_PORT	PLXP_LAN_CONTROL
    in      ax, dx
    or      ax, SOFTWARE_RESET_BIT
    out     dx, ax
    and     ax, NOT SOFTWARE_RESET_BIT
    STALL
    STALL
    out     dx, ax
    STALL
    STALL

;- Enable LAN 0 interrupt and set it to LATCHED mode
	LOAD_PORT	PLXP_INTERRUPT_CONTROL
    in      ax, dx
    or      ax, LAN_0_INTERRUPT_ENABLE
    or      ax, LATCHED_INTWRITE_BIT
    out     dx, ax
    STALL
    STALL

;- Delay 100msec after PLX reset.
	mov		ax,0004h
	call	wait
    ret
init_plx_chip	ENDP


init_scp	PROC	NEAR
; Performs the alternate SCP and ISCP initialization for the 82596
	mov		ax, cs						; Set Up the Alternate SCP Address
	mov		es, ax
	mov		si, offset cgroup:scp_buff
	call	cnvrt_to_phys_add
	jc		error_init_scp
    or      al, ALT_SCP_CMD
	LOAD_PORT	PLXP_PORT_OFFSET
	out		dx, eax
	STALL							; Hold things up - briefly
	mov		ax, 2
	call	wait					; Hold things up for 50ms

; Initialize the SYSBUS byte in System Configuration Pointer (SCP)
; Internal Triggering Of Timers, Active High Edge-Triggered Interrupt
	mov		ax, SYSBUS_CSW OR SYSBUS_LOCK_DIS OR SYSBUS_32_MODE OR SYSBUS_BIT_0
	mov     scp_buff.scp_sysbus, ax
	mov		si, offset cgroup:iscp_buff
	call	cnvrt_to_phys_add
	jc		error_init_scp

    mov     scp_buff.scp_iscp_add, eax
	mov		iscp_buff.iscp_busy, ISCP_BUSY_MARK
    mov     si, 0
	call	cnvrt_to_phys_add
	jc		short error_init_scp

    lea     si, scb_buff
    mov     iscp_buff.iscp_scb_offset, si
    mov     iscp_buff.iscp_scb_lo_add, ax
    shr     eax, 16
    mov     iscp_buff.iscp_scb_hi_add, ax
    mov     scb_buff.scb_status, 0h
    C_ATTENTION						; Issue a channel attention to the 82596

	mov		ax, 1					; Wait, if necessary, for about 27 mili-
	call	set_timeout				;    seconds (10 should be enough)
iscp_busy_wait:
	test	iscp_buff.iscp_busy, ISCP_BUSY_MARK
	jz		short iscp_success
	call	do_timeout				; Any more time left ?
	jnz		short iscp_busy_wait	; Continue to wait if so
	jmp		short iscb_problem

iscp_success:
	mov		ax, scb_buff.scb_status		;- Acknowledge any interrupts
	and		ax, SCB_STATUS_MASK
	mov		scb_buff.scb_command, ax
    C_ATTENTION						; Issue a channel attention to the 82596
	mov		ax, 1					; Minimum time out value
	call	wait_scb_non_active
	jc		short iscb_problem

    mov     ax, DEFAULT_T_OFF		; Load the Bus Throttle values.
    mov     scb_buff.scb_off_timer, ax
    mov     ax, DEFAULT_T_ON
    mov     scb_buff.scb_on_timer, ax
    mov     scb_buff.scb_command, CU_LD_B_T_R
    C_ATTENTION						; Issue a channel attention to the 82596
	mov		ax, 1					; Minimum time out value
	call	wait_scb_non_active
	jnc		short exit_init_scp
iscb_problem:
	print	iscp_init_problem		; Inform the user
	stc

exit_init_scp:
    ret
error_init_scp:
	print	problem_in_phys_add
error_init_iscp:
	stc
	jmp		short	exit_init_scp

init_scp	ENDP


read_ia_pci	PROC	NEAR
	; For the 82596 the Ethernet address is obtained from PCI configuration
	cmp		no_prom, 0			; User specified ethernet address
	jne		short use_user_address 	; Yes - no need to read the PCI address

	mov		ah, PCI_FUNCTION_ID		; Get IO base
	mov		al, READ_PCI_CONFIG_DWORD
	mov		bx, pci_bus_dev_no		; Recover the bus / device number
    mov     di, PLXP_NODE_ADDR_REGISTER
	int		PCI_BIOS_INTERRUPT
	cmp		ah, PCI_SUCCESSFULL
	jne		short error_read_ia_pci
	mov		DWORD PTR rom_address, ecx
	mov		ah, PCI_FUNCTION_ID		; Get IO base
	mov		al, READ_PCI_CONFIG_WORD
    add     di, 4					; Advance to the next 4 bytes
	int		PCI_BIOS_INTERRUPT
	cmp		ah, PCI_SUCCESSFULL
	jne		short error_read_ia_pci
	mov		WORD PTR rom_address+4, cx
use_user_address:
	clc
exit_read_ia_pci:
	ret
error_read_ia_pci:
	stc
	jmp		short exit_read_ia_pci
read_ia_pci	ENDP

endif

setup_cmd_buffs	PROC	NEAR
; Setup buffer for commands
	mov		word ptr cmd_buff1, C_DONE_BIT	; Mark buffer as available
	mov		word ptr cmd_buff2, C_DONE_BIT	; Mark buffer as available
	mov		buff_ptrs[0], offset cgroup:cmd_buff1	; Settup buffer 'array'
	mov		buff_ptrs[2], offset cgroup:cmd_buff2
	mov		tx_buff_no, 0					; Set next buffer # to use
	ret
setup_cmd_buffs	ENDP

setup_receive	PROC	NEAR
	mov		cx, RBD_COUNT 			; Setup the receive buffer descriptors
	mov		ax, cs
	mov		es, ax
	mov		si, offset cgroup:start_rx_buff
	mov		bx, si					; Bx holds a copy of the virtual offset
	call	cnvrt_to_phys_add
	mov		esi, eax
	mov		di, offset cgroup:start_rbd
	mov		first_rbd, di
	mov		ax, di
next_rbd:
	mov		[di].rbd_status, 0
	add		ax, size rbd_struct				; Pointer to the next RBD
if	CHIP EQ INTEL_82596
	mov		[di].rbd_link, ax
else
	movzx	eax, ax
	mov		[di].rbd_link, eax
	mov		[di].rbd_filler2, 0
endif
	mov		[di].rbd_ptr, esi				; Store physical address
	mov		[di].rbd_size, SIZE_ONE_DATA_BUFF
	mov		[di].rbd_filler1, 0
	mov		[di].rbd_voffset, bx			; Store the virtual offset
	add		esi, SIZE_ONE_DATA_BUFF			; Physical address of next buffer
	add		bx, SIZE_ONE_DATA_BUFF			; Virtual offset of next buffer
	mov		di, ax							; Advance to the next RBD
	loop	next_rbd

	sub		di, size rbd_struct				; Return to previous RBD
	mov		last_rbd, di
	or		[di].rbd_size, EL_BIT			; Mark end of chain
	mov		[di].rbd_link, offset cgroup:start_rbd	; Wrap to start

	mov		cx, RFD_COUNT 				; Setup the receive buffer descriptors
	mov		di, offset cgroup:start_rfd
	mov		first_rfd, di
	mov		ax, di
next_rfd:
	mov		[di].rfd_status, 0
	mov		[di].rfd_eol, FLEXIBLE_MODE
	add		ax, size rfd_struct		 		; Pointer to the next RFD
if	CHIP EQ INTEL_82596
	mov		[di].rfd_link, ax
else
	movzx	eax, ax
	mov		[di].rfd_link, eax
endif
	mov		[di].rfd_ptr, -1
	mov		[di].rfd_cnt, 0
	mov		[di].rfd_size, 0
	mov		di, ax							; Advance to the next RFD
	loop	next_rfd

	sub		di, size rfd_struct				; Get back to last RFD
	mov		last_rfd, di
	mov		[di].rfd_eol, EL_BIT+FLEXIBLE_MODE	; Mark end of chain
	mov		[di].rfd_link, offset cgroup:start_rfd	; Wrap around to start

							; Setup the start rdf to point to the start rbd
	mov		[start_rfd].rfd_ptr, offset cgroup:start_rbd	

if	CHIP EQ INTEL_82596
	mov		scb_buff.scb_rfa_add, offset cgroup:start_rfd
	mov		scb_buff.scb_command, RU_START
	C_ATTENTION
	
;#	mov		ax, scb_buff.scb_status		;- Acknowledge any interrupts
;#	and		ax, SCB_STATUS_MASK
;#	mov		scb_buff.scb_command, ax
;#	C_ATTENTION						; Issue a channel attention to the 82596
else
; debug - setup receive code required for 82557
endif
	ret
setup_receive	ENDP

config_chip	PROC	NEAR
if	CHIP EQ INTEL_82557
	call	load_base_regs
	mov		dx, offset cgroup:load_base_msg
	jc		short exit_config_chip		; Exit if there is a problem

	test	WORD PTR eeprom_buff[PHY_DEV_REC_1], TEN_MB_ONLY
	jne		short no_mii_support

	; Set the two configuration bytes for the MII support
	mov		config_b8, MII_MODE
	mov		config_b15, 48h

no_mii_support:
endif
	call	do_config
	mov		dx, offset cgroup:config_msg
	jc		short exit_config_chip	; Exit if there is a problem

	mov		ax, 8					; Wait for the command
	call	wait
; Set the Individual address registers with the Ethernet address
	mov		si, offset cgroup:rom_address
	call	set_ether
;	mov		dx, offset cgroup:ether_add_msg
	jc		short exit_config_chip	; Exit if there is a problem
	clc
exit_config_chip:
	ret
config_chip	ENDP

if	CHIP EQ INTEL_82557
; debug	DEBUG - this routine needs physical addresses
load_base_regs	PROC	NEAR
	mov		ax, 1					; Use minimum time-out in the wait routine
	call	wait_scb_non_active		; Can't issue a command if chip is active
	jc		exit_load_base_regs		; Exit if there is a problem
	LOAD_PORT	SCB_GEN_PTR			; Load port address for SCB general pointer
	xor		eax, eax
	mov		ax, cs
	shl		eax, 4
	out		dx, eax					; Load the port with the segment address
	LOAD_PORT	SCB_CMD				; Load port address for SCB command
	mov		al, CU_LD_BASE			; Command to set the CU base register
	out		dx, al					; Issue the command

	mov		ax, 1					; Use minimum time-out in the wait routine
	call	wait_scb_non_active		; Can't issue a command if chip is active
	jc		short exit_load_base_regs	; Exit if there is a problem
	LOAD_PORT	SCB_GEN_PTR			; Load port address for SCB general pointer
	xor		eax, eax
	mov		ax, cs
	shl		eax, 4
	out		dx, eax					; Load the port with the segment address
	LOAD_PORT	SCB_CMD				; Load port address for SCB command
	mov		al, RU_LD_BASE			; Command to set the RU base register
	out		dx, al					; Issue the command
exit_load_base_regs:
	ret
load_base_regs	ENDP
endif

do_select_media	PROC NEAR
; First check to see if connector type has been specified
	cmp		temp_connector, TPE_CONNECTOR
	je		short test_TPE
	cmp		temp_connector, BNC_CONNECTOR
	je		short test_BNC
	cmp		temp_connector, AUI_CONNECTOR
	je		short test_AUI

	print	using_auto_msg		; Auto detecting media
	call	setup_test_packet

test_BNC:						; AUI failed, now try BNC
	mov		connector, BNC
if	CHIP EQ INTEL_82596
	LOAD_PORT	PLXP_USER_PINS
	in		al, dx
	and		al, NOT PLXP_USER_0 AND NOT PLXP_USER_1
	out		dx, al
else
; debug 82557 code required
endif
	mov		ax,0016h	; Delay about 600 msec for front_end setup
	call	wait
	cmp		temp_connector, BNC_CONNECTOR
	je		short using_bnc
	call	send_on_connector
	jc		short test_TPE
using_bnc:
	mov		dx, offset cgroup:using_10B_2_msg	; Setup message for BNC
	jmp		short announce_media

test_AUI:						; Try the AUI connector first
	mov		connector, AUI
if	CHIP EQ INTEL_82596
	LOAD_PORT	PLXP_USER_PINS
	in		al, dx
	or		al, PLXP_USER_1
	and		al, NOT PLXP_USER_0
	out		dx, al
else
; debug 82557 code required
endif
	mov		ax,0016h	; Delay about 600 msec for front_end setup
	call	wait
	cmp     temp_connector, AUI_CONNECTOR
	je		short using_aui
	call	send_on_connector
	jc		short test_bnc
using_aui:
	mov		dx, offset cgroup:using_AUI_msg	; Setup message for AUI
	jmp		short announce_media

test_TPE:									; BNC failed, now try TPE
	mov		connector, TPE
if	CHIP EQ INTEL_82596
	LOAD_PORT	PLXP_USER_PINS
	in		al, dx
	or		al, PLXP_USER_0 OR PLXP_USER_1
	out		dx, al
else
; debug 82557 code required
endif
	mov		ax,0016h	; Delay about 600 msec for front_end setup
	call	wait
	cmp		temp_connector, TPE_CONNECTOR
	je		short using_tpe
	call	send_on_connector
	jc		short default_aui
using_tpe:
	mov		dx, offset cgroup:using_10B_T_msg	; Setup message for twisted pair
	jmp		short announce_media

default_aui:
	mov		connector, AUI 			; At this point all connectors have failed
if	CHIP EQ INTEL_82596
	LOAD_PORT	PLXP_USER_PINS
	in		al, dx					; Use AUI as default connector
	or		al, PLXP_USER_1
	and		al, NOT PLXP_USER_0
	out		dx, al
else
; debug 82557 code required
endif
	mov		dx, offset cgroup:using_AUI_msg	; Setup message for AUI

announce_media:
	print_it
    ret
do_select_media	ENDP

send_on_connector	PROC	NEAR
	mov		[di].tx_status, 0h
if	CHIP EQ INTEL_82596
	mov		scb_buff.scb_cbl_add, di
else
; debug 82557 code required
endif
	call	issue_cmd

	mov		ax, 1					; Wait, if necessary, for about 27 msec
	call	set_timeout
wait_for_send:
	mov		ax, [di].tx_status
	test	ax, CB_STATUS_COMPLETE
	jnz		short send_complete
	call	do_timeout				; Any more time left ?
	jnz		short wait_for_send		; Continue to wait if so

if	CHIP EQ INTEL_82596
	mov		scb_buff.scb_command, CU_ABORT	; Abort the command
	C_ATTENTION
else
; debug 82557 code required
endif
error_send_on_connector:
	stc								; Indicate failure
    ret

send_complete:
    mov     ax, [di].tx_status		; Check if status bits are ok
    test    ax, CB_STATUS_OK
    jz		short error_send_on_connector
    test    ax, CB_TX_NO_CARRIER_SENSE	; Also need to check for carrier
    jnz     short error_send_on_connector
	clc								; Indicate success
	ret
send_on_connector	ENDP

setup_test_packet	PROC NEAR
	mov		cx, RUNT
	lea		di, temp_buffer		; Buffer to use for test packets
	push	di					; Save start address of buffer
	mov		eax, (EL_BIT + TRANSMIT)*10000h
	stosd						; Store the action command word + status word
	mov		ax, -1				; Set the address of next command to NULL
	stosw						; Store the link address - next command
	stosw						; TBD offset is also null
	mov		ax, RUNT OR TX_EOF_BIT	; Set length of data
	stosw
if	CHIP EQ INTEL_82596
	xor		ax, ax
else
	mov		ax, TX_THRESHOLD	; Set TX threshold
endif
	stosw
	mov		si, offset cgroup:rom_address	; Set destination address as us
	movsd
	movsw
	mov		si, offset cgroup:rom_address
	movsd
	movsw
    mov     ax, RUNT - EADDR_LEN*2 + 2 ; Set packet size (2 * address + size)
    stosw
	pop		di						; Recover start address of buffer
    ret
setup_test_packet	ENDP


if	CHIP EQ INTEL_82557
do_fdx_hdx_negotiation	PROC NEAR
;	LOAD_BANK_PORT	BANK2, FDX_HDX	; Get auto-negotiation status
	in		al, dx
	test	al, A_N_ENABLE			; Is auto-negotiation require ?
	jne		short need_negotiation		; Jump if so
	test	word ptr [eeprom_buff+1*2], EE_CONF_FDX	; Full duplex set in EEPROM
	je		no_negotiation			; Jump if not
	or		al, FDX_ENABLE			; Set full duplex bit
	out		dx, al					; Update register
	jmp		short full_duplex
need_negotiation:
	push	dx
	print 	negotiation_msg			; Announce that negotiation is in progress
	pop		dx
	mov		al, A_N_ENABLE+FDX_ENABLE
	out		dx, al
	mov		ax, 200					; Wait, if necessary, for about 5
	call	set_timeout				;    seconds (150 is too small)
get_link_status:
	in		al, dx					; Get status of the negotiation
	test	ax, NEG_STATUS			; Non-zero when the negotation is done
	jne		short negotiation_done		; Jump if so
	call	do_timeout				; Any more time left ?
	jnz		get_link_status			; Continue to wait if so
negotiation_problem:
	print	neg_problem_msg			; Otherwise announce negotiation problem
	jmp		short exit_do_fdx_neg

negotiation_done:
	; Intel specs state that a double read is necssary for this register
	in		al, dx					; Get status of the negotiation
	mov		ah, al					; Save a copy of al
	and		ah, NEG_STATUS			; Clear all but the negotiation status
	cmp		ah, NEG_STATUS_OK		; Check if negotiation completed correctly
	jne		short negotiation_problem		; Jump if negotiation problem

no_negotiation:
	test	al, FDX_ENABLE			; Is this a full duplex link ?
	je		short half_duplex				; Jump if not
full_duplex:
	print	full_duplex_msg			; Announce full duplex
	jmp		short exit_do_fdx_neg
half_duplex:
	print	half_duplex_msg			; Announce half duplex
exit_do_fdx_neg:
	ret
do_fdx_hdx_negotiation	ENDP
endif


	public	find_base
find_base	PROC	NEAR
	call	get_pci_info		; Go and search the card
	jc		short f_b_failed	; Jump if card not found
	mov		ax, base_addr		; Copy the base address for the card
	mov		io_addr, ax
	ret
f_b_failed:
	cmp		io_addr, 0h			; Has a base address been specified
	jz		short f_b_failed1	; No - then the scan failed
	mov		dx, offset cgroup:specified_failed
	jmp		short f_b_failed2
f_b_failed1:
	mov		dx, offset cgroup:scan_failed	; Message to announce later
f_b_failed2:
	stc							; Set carry and return
	ret
find_base	ENDP

get_pci_info	PROC	NEAR
	mov		si,	-1
try_next_index:
	inc		si						; Start out with index # = 0
	mov		ah, PCI_FUNCTION_ID
	mov		al, FIND_PCI_DEVICE
	mov		cx, DEVICE_ID			; Look for particular device
	mov		dx, VENDOR_ID
	int		PCI_BIOS_INTERRUPT
	cmp		ah, PCI_SUCCESSFULL
	jne		short exit_get_pci_info	; Not found - exit
	mov		ah, PCI_FUNCTION_ID		; Get IO base
	mov		al, READ_PCI_CONFIG_DWORD
	mov		di, 14h					; Base address register for IO info
	int		PCI_BIOS_INTERRUPT
	cmp		ah, PCI_SUCCESSFULL
	jne		short try_next_index
	mov		pci_bus_dev_no, bx		; Save bus / device number
	mov		bl, cl					; Check that this base register has IO info
	and		bl, 03h					
	cmp		bl, 01h					; Check the IO space indicator bits
	jne		short try_next_index	; No - should not happen
	and		cl, 0FCh				; Mask lower 2 bits of IO address

found_a_match:
	cmp		io_addr, 00h			; Has the base io address been specified
	je		short no_io_add_specified	; Jump if not, otherwise check to see if
	cmp		cx, io_addr				;   this is the specified address
	jne		short try_next_index	; No - continue the search
	call	get_irq
	jc		short try_next_index
	jmp		short exit_get_pci_info
no_io_add_specified:				; Everything has matched - card is found
	cmp		base_addr, 00h			; Value should initially be zero
	jz		short first_card_found	; Ok to proceed
	call	found_many				; Deal with multiple cards
	mov		found_one, 00h
	jmp		short try_next_index	; Continue the search
first_card_found:
	call	get_irq
	jc		short try_next_index	; Continue the search
	mov		found_one, 01h
	jmp		short try_next_index	; Continue the search
exit_get_pci_info:
	cmp		found_one, 01h			; Carry set if there is a problem
	ret
error_get_pci_info:
	stc
	jmp		short exit_get_pci_info
get_pci_info	ENDP

get_irq	PROC	NEAR
	mov		base_addr, cx
	mov		bx, pci_bus_dev_no		; Recover the bus / device number
	mov		ah, PCI_FUNCTION_ID		; Get IRQ number
	mov		al, READ_PCI_CONFIG_BYTE
	mov		di, 3ch					; PCI register for IRQ info
	int		PCI_BIOS_INTERRUPT
	cmp		ah, PCI_SUCCESSFULL
	je		short got_irq
	print	irq_problem_msg
	stc
	jmp		short exit_get_irq
got_irq:
	mov		int_no, cl
	clc
exit_get_irq:
	ret
get_irq	ENDP

found_many	PROC	NEAR
	; Called when multiple cards are found
	push	si
	push	cx					; Save IO address of current card
	cmp		multiple_cards, 0	; Have multiple cards been previously found
	jnz		short no_need_m_error		; Jump if so
	mov		multiple_cards, 1 	; Mark for next time
	print	more_than_one_msg	; Announce the multiple cards problem
	mov		ax, base_addr		; Display the IO address of the first card
	mov		di, offset cgroup:temp_storage
	mov		[di], ax
	mov		dx, offset cgroup:dummy_msg
	call	print_number
no_need_m_error:
	pop		ax					; Display the IO base address of current card
	mov		di, offset cgroup:temp_storage
	mov		[di], ax
	mov		dx, offset cgroup:dummy_msg
	call	print_number
	pop		si
	ret
found_many	ENDP


code	ends

_text	segment para public 'code'
_text	ends

init	segment para public 'code'
init	ends

	END
