/*
 **************************************************************************
 *
 * Network driver interface for netboot bootrom
 *
 * Module:  ndisinit.S86
 * Purpose: Load and initialize the NDIS driver interface
 * Entries: drv_load
 *
 **************************************************************************
 *
 * Copyright (C) 1998-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: ndisinit.S86,v 1.11 2007/01/06 18:30:49 gkminix Exp $
 *
 */


/*
 **************************************************************************
 *
 * Include assembler macros:
 *
 */
#include <common.i86>
#include <romconfig.i86>
#include <system/bootrom.i86>
#include <system/i8259.i86>
#include <pxe/general.i86>
#include "ndis.i86"
#include "ndisdos.i86"

.file __FILE__
.line __LINE__



/*
 **************************************************************************
 *
 * BSS segment:
 *
 */
	.bss				# start BSS segment

	.extern	mac_cct
	.extern	curr_psp

	.global	irqnum

	.lcomm	irqnum,2		# hardware interrupt number




/*
 **************************************************************************
 *
 * Start code segment.
 *
 */
	.text

	.global	drv_load		# define entry points


/*
 * External variables in code space
 */

	.extern	drvflg

/*
 * External routines of the NDIS interface
 */

	.extern	mac_bind


/*
 * External routines of the protocol manager
 */

	.extern	pm_init


/*
 * External routines of the DOS simulator
 */

	.extern	initdos
	.extern	cleandos
	.extern	loadprog
	.extern	runprog
	.extern	dos4A
	.extern	dos50

/*
 * Network driver interface library external routines
 */

	.extern	sethwinfo
	.extern	initpci
	.extern	cleanpci

/*
 * General library external routines
 */

	.extern	prnchr
	.extern	prnstr
	.extern	prnnum
	.extern	prcrlf
	.extern	intsave
	.extern	intrestore
	.extern	intchanged
	.extern	chkhwint



/*
 **************************************************************************
 *
 * Load and initialize the NDIS driver interface. When an error
 * occurred, it will print an error message before returning.
 * Input:  ES:SI  -  pointer to network driver source
 *         FS:BX  -  pointer to UNDI loader structure
 *         AX     -  loader text segment
 *         CX     -  first segment following loader text segment
 *         DX     -  first usable paragraph for network driver
 * Output: AX     -  PXE error code
 * Registers changed: EAX, BX, CX, DX, SI, DI, ES
 *
 */
drv_load:

/*
 * Get space for the interrupt table copy on the stack. We can safely
 * assume that we have enough stack space here (required are 1024 bytes)
 * because the kernel loader already switched to a new stack which should
 * be large enough. When normally entering a routine C style, BP gets
 * the old stack pointer. Here it gets the new stack pointer. Therefore
 * we cant use the penter macro.
 */

	push	bp
	sub	sp,IDT_NUM * 4
	mov	bp,sp

/*
 * Determine hardware configuration
 */

	call	sethwinfo
	jc	8f

/*
 * Now initialize the protocol manager. The initialization routine gets
 * the first available segment of the data area in DX, and the pointer to
 * the PROTOCOL.INI image in ES:SI. It has to return the first free segment
 * in DX, and a pointer to the first byte after the PROTOCOL.INI image in
 * ES:SI.
 */

	call	pm_init			# initialize protocol manager
	jc	8f

/*
 * Save all interrupts and initialize the DOS simulator and PCI hardware
 * handler.
 */

	push	es
	push	si
	call	intsave			# save interrupts
	call	initdos			# initialize the DOS simulator
	call	initpci			# initialize the PCI interface
	pop	si
	pop	es
	jc	7f

/*
 * Scan through the list of DOS programs and run each in turn. The binary
 * patch program should have stored the number of DOS programs at the first
 * byte of the network driver source area. The first program has to be the
 * NDIS device driver, which will be initialized.
 * Note that these DOS simuator routines print a message in case of an
 * error, so we dont have to do it ourselves.
 * After all programs have been started, restore all interrupt vectors and
 * identify those which have been changed by the packet driver. These will
 * be set in the local IDT buffer. Then clean all DOS interrupts from this
 * buffer, as the DOS simulator should not be needed anymore.
 */

	mov	cx,es:[si]		# get number of DOS programs
	add	si,2
	push	cx
	call	loadprog		# load NDIS driver into DOS memory
	jc	2f
	call	startdrv		# startup driver
	jmp	2f

1:	push	cx
	call	loadprog		# load program into DOS memory
	jc	2f
	call	runprog			# and run it
2:	pop	cx
	jc	7f
	loop	1b			# scan through all programs

/*
 * Bind the NDIS driver. Unfortunately, some drivers only set the hardware
 * interrupt at bind time, not at initialization time. Since we need to
 * know the hardware interrupt now and cant allow the driver to set it
 * lateron, we have to do the bind now.
 */

	call	mac_bind
	jnc	4f
7:	call	intrestore		# in case of error restore all ints
8:	mov	ax,PXENV_STATUS_UNDI_INIT_DATA
	jmp	9f

/*
 * We can now restore all interrupts and cleanup the DOS and PCI interrupts.
 */

4:	call	intrestore		# restore all interrupt vectors
	jc	8b			# and save new vectors in IDT buffer

	call	cleandos		# cleanup DOS interrupts
	call	cleanpci		# cleanup PCI interrupts

/*
 * Now try to find the hardware interrupt in the local interrupt buffer. This
 * will also clear the interrupt from the IDT buffer. We have to use our own
 * hardware interrupt detection routine, because the corresponding field in
 * the MAC Service Specific Characteristics (SSC) table is only available
 * at NDIS version 2.0.1 and later. Most drivers still use the older 2.0
 * version, which doesnt define these hardware interrupt detection fields.
 * If we have a PCI network card, the PCI initialization routine has already
 * pre-set the IRQ number. For some network drivers we need to override the
 * interrupt number which we found by using this PCI interrupt number.
 */

	call	chkhwint		# find hardware interrupt
	jcxz	8b
	test	word ptr cs:[drvflg],DRVFLG_IRQPCI
	jz	5f
	mov	ax,[irqnum]		# check if hardware interrupt number
	or	ax,ax			# set by PCI initialization
	jnz	3f
5:	mov	[irqnum],cx		# save hardware IRQ number

/*
 * Finally set all those interrupt vectors which have to remain set while the
 * NDIS driver is loaded. Then tell the user that we found something.
 */

3:	call	intchanged		# identify permanent interrupts
	mov	si,offset fndmsg
	call	prnstr
	cld
	push	ds
	lds	si,[mac_cct]
	lea	si,cct_mod_name[si]	# print module name
5:	lodsb
	or	al,al
	jz	6f
	call	prnchr
	jmp	5b

6:	pop	ds
	mov	si,offset irqmsg
	call	prnstr
	movzx32	eax,byte ptr [irqnum]
	mov	dl,10
	call	prnnum			# print hardware IRQ number
	call	prcrlf
	xor	ax,ax			# return without error
9:	add	sp,IDT_NUM * 4		# restore stack
	pop	bp
	ret



/*
 **************************************************************************
 *
 * Call NDIS device initialization routine
 * Input:  AX  -  segment of new PSP
 *         BX  -  first segment of device driver
 * Output: Carry set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
startdrv:

	enter	DEV0_LENGTH,0

/*
 * Setup new PSP pointer so that the DOS simulator routines know what
 * program is currently running.
 */

	mov	dx,bx
	mov	bx,ax
	call	dos50			# set new PSP segment
	mov	si,bx

/*
 * Clear the device request header.
 */

	push	es
	push	ds
	mov	ax,ss
	mov	es,ax			# put pointer to request header into
	lea	di,[bp - DEV0_LENGTH]	# ES:BX
	mov	bx,di
	mov	cx,DEV0_LENGTH		# clear device request header
	xor	al,al
	cld
	rep	stosb

/*
 * Setup the device request header with some default values, including the
 * last usable segment. According to the specs this should only be necessary
 * before calling the device driver initialization routine. However, some
 * NDIS drivers already use the device strategy routine for initialization,
 * so the device request header has to be setup now.
 */

	mov	ax,si
	mov	cx,ax
	dec	ax
	mov	ds,ax
	add	ax,ds:[mcb_size]
	inc	ax
	mov	byte ptr devreq_length[bp - DEV0_LENGTH],DEV0_LENGTH
	mov	byte ptr devreq_command[bp - DEV0_LENGTH],DEV_INIT
	mov	word ptr dev0_memavail[bp - DEV0_LENGTH + 2],ax
	mov	word ptr dev0_cmdline[bp - DEV0_LENGTH + 0],offset psp_cmdl
	mov	word ptr dev0_cmdline[bp - DEV0_LENGTH + 2],cx
	mov	ds,dx
	push	cx

/*
 * Now call the device driver strategy routine with ES:BX pointing to the
 * properly setup device request header.
 */

	push	cs
	push	offset 1f		# push far return address
	push	ds			# push ptr to device strategy routine
	push	word ptr ds:[devhdr_strategy]
	lret				# call strategy routine

/*
 * Call the device initialization routine. The pointer to the device request
 * header has already been passed to the device driver, setup with the proper
 * operation code.
 */

1:	push	cs
	push	offset 2f		# push far return address
	push	ds			# push ptr to device strategy routine
	push	word ptr ds:[devhdr_interrupt]
	lret				# call device initialization routine

/*
 * Restore PSP pointer and check for driver return code.
 */

2:	pop	cx
	pop	ds
	xor	bx,bx
	call	dos50
	test	word ptr devreq_status[bp - DEV0_LENGTH],DEVERR_ERRFLAG
	jnz	8f

/*
 * Determine the last address of the network driver and adjust the memory
 * allocation accordingly.
 */

	mov	es,cx
	mov	bx,dev0_free[bp - DEV0_LENGTH + 0]
	add	bx,0x000F
	rcr	bx,1			# adjust last address to paragraph
	shr	bx,3			# boundary
	add	bx,dev0_free[bp - DEV0_LENGTH + 2]
	sub	bx,cx			# compute new size of driver memory
	jbe	8f			# block in paragraphs
	call	dos4A			# resize the memory block
	jnc	9f
8:	stc				# in case of error return with carry set
9:	pop	es
	leave
	ret




/*
 **************************************************************************
 *
 * Message strings:
 *
 */
	_rodata


/*
 * General mnessages to tell the user what we found
 */

fndmsg:	.byte	0x0D,0x0A
	.asciz	"Found NDIS driver "
irqmsg:	.asciz	", irq "




/*
 **************************************************************************
 *
 */

	.end

