/*
 **************************************************************************
 *
 * Network driver interface for netboot bootrom
 *
 * Module:  pktinit.S86
 * Purpose: Load and initialize the packet 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: pktinit.S86,v 1.9 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 "pktdrv.i86"

.file __FILE__
.line __LINE__



/*
 **************************************************************************
 *
 * Miscelleneous equates:
 *
 */
#define	MIN_INT		0x20		/* lowest usable packet driver interrupt */
#define	MAX_INT		0x80		/* highest usable packet driver interrupt */
#define	DRIVER_OFS	3		/* offset of ID string */




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

	.global	irqnum
	.global	pktvect


	.lcomm	irqnum,2		# hardware IRQ number
	.lcomm	pktvect,4		# packet driver software int vector




/*
 **************************************************************************
 *
 * String data:
 *
 */
	_rodata

/*
 * ID string at the beginning of the packet driver code:
 */

pktdrv_id:	.ascii	"PKT DRVR"
pktdrv_end_id:




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

	.global	drv_load		# define entry points

/*
 * External variables in code space
 */

	.extern	drvflg

/*
 * External routines of the DOS simulator
 */

	.extern	initdos
	.extern	cleandos
	.extern	loadprog
	.extern	runprog

/*
 * Network driver interface library external routines
 */

	.extern	sethwinfo
	.extern	initpci
	.extern	cleanpci

/*
 * General library external routines
 */

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



/*
 **************************************************************************
 *
 * Load and initialize the packet 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, EBX, ECX, EDX, 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

/*
 * 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.
 * Note that these DOS simulator 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,word ptr es:[si]	# get number of DOS programs
	add	si,2
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

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

	call	intrestore		# restore all interrupt vectors
	jc	8f

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

/*
 * Determine the packet driver interrupt. If one has been found, save
 * it for later reference and remove it from the local IDT buffer. This
 * way it doesnt get set again.
 */

	cld
	mov	bx,MIN_INT * 4		# start looking at first interrupt
3:	mov	si,bx
	les	di,[bp + si]		# get interrupt vector
	add	di,DRIVER_OFS		# compute address of ID string
	mov	si,offset pktdrv_id
	mov	cx,offset pktdrv_end_id - offset pktdrv_id
	repe	cmpsb			# compare both strings
	je	4f			# found it
	add	bx,4			# advance to next interrupt vector
	cmp	bx,MAX_INT * 4		# at the end of the list?
	jb	3b
	jmp	8f

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

4:	mov	si,bx
	xor	eax,eax
	xchg	eax,[bp + si]		# get new vector and set it to 0
	mov	[pktvect],eax		# in interrupt buffer
	shr	bx,2			# compute interrupt number

/*
 * Now try to find the hardware interrupt in the local interrupt buffer. This
 * will also clear the interrupt from the IDT buffer. 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.
 */

	push	bx
	call	chkhwint		# find hardware interrupt
	pop	bx
	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	6f
5:	mov	[irqnum],cx		# save hardware IRQ number

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

6:	push	bx
	call	intchanged		# identify permanent interrupts
	mov	si,offset fndmsg
	call	prnstr
	pop	ax
	call	prnbyt			# print packet driver software interrupt
	mov	si,offset irqmsg
	call	prnstr
	movzx32	eax,word 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




/*
 **************************************************************************
 *
 * Messages:
 */
	_rodata


/*
 * Message to tell user about packet driver interrupt:
 */

fndmsg:	.byte	0x0D,0x0A
	.asciz	"Found packet driver at int "
irqmsg:	.asciz	", irq "




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

	.end

