/*
 **************************************************************************
 *
 * Network driver interface for netboot bootrom
 *
 * Module:  ndis.S86
 * Purpose: NDIS driver to UNDI interface
 * Entries: drv_entry, mac_bind
 *
 **************************************************************************
 *
 * 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: ndis.S86,v 1.13 2007/01/06 18:30:49 gkminix Exp $
 *
 */


/*
 **************************************************************************
 *
 * Include assembler macros:
 *
 */
#include <common.i86>
#include <romconfig.i86>
#include <system/bootrom.i86>
#include <net/ethernet.i86>
#include <net/in.i86>
#include <pxe/general.i86>
#include <pxe/undi.i86>
#include "ndis.i86"

.file __FILE__
.line __LINE__



/*
 **************************************************************************
 *
 * Definition of service flags. See NDIS-2.0 specification for further
 * explanation.
 *
 */
#ifdef NEEDMCAST
#define	FLAGS_MCAST	0b0000000000000010
#else
#define	FLAGS_MCAST	0b0000000000000000
#endif

#ifdef NEEDSETADDR
#define	FLAGS_SETADDR	0b0000000000010000
#else
#define	FLAGS_SETADDR	0b0000000000000000
#endif

#define	FLAGS_STANDARD	0b0001110000001001
#define	FLAGS_BITS	0b0000110000000000
#define	FLAGS_MASK	(FLAGS_STANDARD + FLAGS_MCAST + FLAGS_SETADDR)



/*
 **************************************************************************
 *
 * Values for initialization flag:
 *
 */
#define	FLAG_NOTSTART	0			/* UNDI driver not started */
#define	FLAG_NOTINIT	UNDI_STATE_STARTED	/* device not initialized */
#define	FLAG_INIT	UNDI_STATE_INITIALIZED	/* device is initialized */
#define	FLAG_OPEN	UNDI_STATE_OPENED	/* device has been opened */



/*
 **************************************************************************
 *
 * Number of seconds to wait for a status indication
 */
#define IND_WAIT	10		/* wait max. 10 seconds */



/*
 **************************************************************************
 *
 * Transmit retry values:
 *
 */
#define	XMT_RETRY	10		/* retry 10 times */
#define	XMT_WAIT	5		/* wait 50 ms between retries */



/*
 **************************************************************************
 *
 * Multicast hardware address (first three bytes):
 *
 */
#define	HW_MCAST_1	0x01
#define	HW_MCAST_2	0x00
#define	HW_MCAST_3	0x5E




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

	.extern	mac_cct			# far ptr to MAC CCT
	.extern	irqnum			# hardware IRQ number
	.extern	protflag		# protected mode flag
	.extern	devinfo			# device info structure

	.lcomm	MacDS,2			# MAC data segment
	.lcomm	GenRequest,4		# MAC entry point for general requests
	.lcomm	SysRequest,4		# MAC entry point for system requests
	.lcomm	TransmitChain,4		# MAC entry point to send a packet
	.lcomm	TransferData,4		# MAC entry point for data copy
	.lcomm	IndicationOn,4		# MAC entry point to control indica-
	.lcomm	IndicationOff,4		# taions (upcalls) from NDIS

	.lcomm	reqconflg,2		# general request confirmation flag
	.lcomm	txconflg,2		# transmit request confirmation flag
	.lcomm	indstatus,2		# last status indication
	.lcomm	macflags,2		# MAC flags
	.lcomm	txbuf,TXBUF_MINSIZE	# transmit buffer descriptor
	.lcomm	rdbuf,RDBUF_MINSIZE	# receive buffer descriptor
	.lcomm	readsize,2		# number of bytes in receive buffer
	.lcomm	writebuf,ETH_FRAME_MAX	# send buffer
	.lcomm	readbuf,ETH_FRAME_MAX	# receive buffer
	.lcomm	initflag,1		# flag to indicate if initialized
	.lcomm	isrflag,1		# flag set when inside ISR handler
	.lcomm	intcount,1		# number of interrupts received
#if MASKINT == 1
	.lcomm	intdisable,1		# disable interrupt processing
#endif
	.lcomm	rcvmode,2		# current receive mode




/*
 **************************************************************************
 *
 * Data segment:
 *
 */
	.data

/*
 * Common Characteristics Table for bootrom interface according to NDIS
 * version 2.0.1.
 */

drv_cct:
	.word	CCT_SIZE		# length of structure
	.byte	NDIS_MAJOR_VER		# major version of NDIS protocol
	.byte	NDIS_MINOR_VER		# minor version of NDIS protocol
	.word	0			# reserved
	.byte	VER_MAJOR		# major version of module
	.byte	VER_MINOR		# minor version of module
	.long	MODFUNC_LOWER_BIND	# module function flags
	.ascii	"NETBOOT$"		# module name
	.byte	0,0,0,0,0,0,0,0		# pad name up to 16 bytes
	.byte	PROT_TRANSPORT		# protocol level at upper boundary
	.byte	TYPE_PRIVATE		# protocol type at upper boundary
	.byte	PROT_MAC		# protocol level at lower boundary
	.byte	TYPE_MAC		# protocol type at lower boundary
	.word	MODID_PROT		# module ID of protocol
	.word	0			# module data segment (set by init)
	.word	SystemRequest		# far ptr to system request dispatch
	.word	0			# entry point
	.long	0			# far ptr to protocol SSC (not used)
	.long	0			# far ptr to protocol SSS (not used)
	.long	0			# far ptr to protocol UDT (not used)
	.long	drv_ldt			# far ptr to protocol lower dispatch
	.long	0,0			# reserved


/*
 * Protocol lower dispatch table according to NDIS version 2.0.1 specifi-
 * cation.
 */

drv_ldt:
	.word	drv_cct			# far back ptr to CCT
	.word	0
	.long	PROT_IF_LSAP_LLC	# protocol flags
	.long	RequestConfirm		# far ptr to RequestConfirm
	.long	TransmitConfirm		# far ptr to TransmitConfirm
	.long	ReceiveLookahead	# far ptr to ReceiveLookahead
	.long	IndicationComplete	# far ptr to IndicationComplete
	.long	ReceiveChain		# far ptr to ReceiveChain
	.long	StatusIndication	# far ptr to StatusIndication




/*
 **************************************************************************
 *
 * Constant data:
 *
 */
	_rodata


/*
 * Name of network protocol supported by this driver interface
 */

prtnam:	.asciz	"DIX+802.3"		# bootrom requires standard ethernet
prtend:




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

	.global	drv_entry		# define entry points
	.global	mac_bind

/*
 * External routines for interrupt handling
 */

	.extern	intstart
	.extern	intstop
	.extern	intset
	.extern	intreset
	.extern	dohwint
	.extern	endhwint

/*
 * External routines in general network driver interface library
 */

	.extern	wait10ms
	.extern	switchreal
	.extern	switchprot

/*
 * External routines in general library
 */

	.extern	set_timeout
	.extern	chk_timeout



/*
 **************************************************************************
 *
 * Bind us to the MAC. This will be called by the general network driver
 * initialization routine. We cant do it at PXE_UNDI_STARTUP because
 * some MACs set the hardware interrupt after binding, which is too late.
 * Input:  none
 * Output: Carry flag set if error
 * Registers changed: AX, BX
 *
 */
mac_bind:

/*
 * First we have to set all the pointers in the drivers CCT and LDT.
 */

	push	es
	mov	word ptr cct_module_ds[drv_cct],ds
	mov	word ptr cct_sysreq[drv_cct + 2],cs
	mov	word ptr cct_ldt[drv_cct + 2],ds
	mov	word ptr protldt_cct[drv_ldt + 2],ds
	mov	word ptr protldt_req_confirm[drv_ldt + 2],cs
	mov	word ptr protldt_tx_confirm[drv_ldt + 2],cs
	mov	word ptr protldt_rx_lah[drv_ldt + 2],cs
	mov	word ptr protldt_ind_complete[drv_ldt + 2],cs
	mov	word ptr protldt_rx_chain[drv_ldt + 2],cs
	mov	word ptr protldt_status[drv_ldt + 2],cs

/*
 * Check that the MAC registered with our protocol manager
 */

	les	bx,[mac_cct]
	mov	ax,es
	or	ax,bx
	jz	8f

/*
 * Now call MAC to request a bind to our module
 */

	push	ds			# push far pointer to protocol CCT
	push	offset drv_cct
	push	ds			# push far pointer to MAC CCT pointer
	push	offset mac_cct
	push	0			# push pad word
	push	NDIS_BIND		# push bind opcode
	push	word ptr es:cct_module_ds[bx]
	lcall	es:cct_sysreq[bx]	# call MAC to bind to us
	or	ax,ax
	jz	9f
8:	stc
9:	pop	es
	ret



/*
 **************************************************************************
 *
 * PXE API entry point. Since we get called by the general network driver
 * interface API entry which already checked if the function number is for
 * us, we dont have to do that again here. We can safely assume that the
 * function number is always correct. Also, the general network driver
 * interface API already saved all registers for us.
 * Input:  ES:DI  -  pointer to parameter structure
 *         BX     -  function number
 * Output: AX     -  error code
 * Registers changed: EAX, BX, CX, DX, SI, DI, BP, ES
 *
 */
drv_entry:

	test	byte ptr [protflag],0xFF
	jz	1f			# check protected mode flag
	call	switchreal		# switch to real mode
	jnc	1f
	mov	ax,PXENV_STATUS_PMODE	# not available in protected mode
	ret

1:	shl	bx,1			# compute offset into jump table
	call	cs:jmptab[bx]		# call function handler

	test	byte ptr [protflag],0xFF
	jz	2f
	call	switchprot		# switch back to protected mode
2:	ret



/*
 **************************************************************************
 *
 * Function table
 *
 */
jmptab:	.word	pxe_start_undi		# start UNDI interface
	.word	pxe_startup		# startup driver
	.word	pxe_cleanup		# cleanup driver
	.word	pxe_initialize		# initialize driver
	.word	pxe_reset_nic		# reset network interface
	.word	pxe_shutdown		# shutdown driver
	.word	pxe_open		# open network driver
	.word	pxe_close		# close network driver
	.word	pxe_transmit		# transmit a packet
#ifdef NEEDMCAST
	.word	pxe_set_multicast	# set multicast address
#else
	.word	pxe_unsupported		# set multicast address
#endif
#ifdef NEEDSETADDR
	.word	pxe_set_addr		# set station address
#else
	.word	pxe_unsupported		# set station address
#endif
	.word	pxe_set_filter		# set packet filter
	.word	pxe_get_info		# get network driver information
#ifdef NEEDSTAT
	.word	pxe_get_stat		# get network statistics
	.word	pxe_clear_stat		# clear statistics
#else
	.word	pxe_unsupported		# get network statistics
	.word	pxe_unsupported		# clear statistics
#endif
	.word	pxe_unsupported		# initiate diagnostics
	.word	pxe_force_int		# force receive interrupt
#ifdef NEEDMCAST
	.word	pxe_get_mcast_addr	# get multicast address
#else
	.word	pxe_unsupported		# get multicast address
#endif
	.word	pxe_get_nic_type	# get network interface driver type
	.word	pxe_get_iface_info	# get interface info
	.word	pxe_isr			# handle hardware interrupt
	.word	pxe_stop_undi		# stop UNDI interface
	.word	pxe_get_state		# get UNDI state



/*
 **************************************************************************
 *
 * Stub for unsupported functions
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX
 *
 */
pxe_unsupported:

	mov	ax,PXENV_STATUS_UNSUPPORTED
	ret



/*
 **************************************************************************
 *
 * START_UNDI. This just saves the BIOS parameters passed to the bootrom
 * and sets all interrupts required by the driver.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI
 *
 */
pxe_start_undi:

	call	intstart		# set interrupt vectors
	xor	ax,ax
	ret



/*
 **************************************************************************
 *
 * STOP_UNDI. This restores the interrupt vectors.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI, DI, ES
 *
 */
pxe_stop_undi:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_NOTINIT
	ja	9f
	call	closendis		# close NDIS driver interface
	call	intstop			# restore interrupt vectors
	mov	ax,PXENV_STATUS_KEEP_UNDI
	jc	9f
	mov	byte ptr [initflag],FLAG_NOTSTART
	xor	ax,ax
9:	ret



/*
 **************************************************************************
 *
 * UNDI_STARTUP. Startup the NDIS driver interface.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI, DI, ES
 *
 */
pxe_startup:

	push	es
	push	di
	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_NOTSTART
	jne	9f

/*
 * Check that the MAC uses standard ethernet headers
 */

	les	bx,[mac_cct]
	les	bx,es:cct_ssc[bx]
	lea	di,macssc_type_name[bx]
	mov	si,offset prtnam
	mov	cx,offset prtend - offset prtnam
	repe	cmpsb				# compare MAC type with required type
	je	1f
8:	mov	ax,PXENV_STATUS_UNDI_NIC_INIT	# invalid NDIS driver
	jmp	9f

/*
 * Now check that the MAC uses correct parameters
 */

1:	cmp	word ptr es:macssc_frame_size[bx],ETH_FRAME_MAX
	jb	8b			# check MTU
	cmp	word ptr es:macssc_addr_len[bx],ETH_ALEN
	jne	8b			# check MAC address size
	mov	ax,es:macssc_sf_1[bx]
	mov	[macflags],ax		# save MAC flags

#ifdef NEEDMCAST
	test	ax,MAC_SF1_MULTICAST
	jz	8b			# check for multicast capability
#endif
#ifdef NEEDSETADDR
	test	ax,MAC_SF1_STAT_ADDR
	jz	8b			# check for settable station address
#endif

/*
 * We are now bound to the MAC, so we can save all MAC entry points into
 * static variables. Unfortunately, this is necessary as per the NDIS
 * specification, but it just wastes precious memory space.
 */

	les	bx,[mac_cct]		# get pointer to MAC CCT
	mov	ax,es:cct_module_ds[bx]
	mov	[MacDS],ax		# save MAC data segment
	mov	eax,es:cct_sysreq[bx]
	mov	[SysRequest],eax

	les	bx,es:cct_udt[bx]	# get pointer to MAC UDT
	mov	eax,es:macudt_request[bx]
	mov	[GenRequest],eax
	mov	eax,es:macudt_tx_chain[bx]
	mov	[TransmitChain],eax
	mov	eax,es:macudt_tx_data[bx]
	mov	[TransferData],eax
	mov	eax,es:macudt_ind_on[bx]
	mov	[IndicationOn],eax
	mov	eax,es:macudt_ind_off[bx]
	mov	[IndicationOff],eax
	mov	byte ptr [initflag],FLAG_NOTINIT
	xor	ax,ax
9:	pop	di
	pop	es
	ret



/*
 **************************************************************************
 *
 * UNDI_CLEANUP. This will terminate the NDIS driver. Unfortunately NDIS
 * drivers do not allow getting removed from memory. Therefore we simply
 * close everything, unbind and then restore all interrupt vectors.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: (E)AX, BX, CX, DX, SI
 *
 */
pxe_cleanup:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_NOTINIT
	ja	9f
	call	closendis		# first close all open connections
	call	intset			# restore interrupts for NDIS driver
	mov	ax,PXENV_STATUS_KEEP_UNDI
	jc	8f
	push	ds
	push	offset drv_cct
	push	0
	push	0
	push	NDIS_UNBIND
	push	[MacDS]
	lcall	[SysRequest]		# call MAC to unbind the protocol
	xor	ax,ax
8:	push	ax
	call	intreset		# restore interrupts
	pop	ax
	mov	byte ptr [initflag],FLAG_NOTSTART
9:	ret



/*
 **************************************************************************
 *
 * Initialize the NDIS driver interface.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX
 *
 */
pxe_initialize:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_NOTINIT
	jne	9f

	xor	dx,dx
	mov	[rcvmode],dx
	call	setrcvmode		# disable receiver
	mov	ax,PXENV_STATUS_UNDI_PHY_INIT
	jc	9f
	mov	byte ptr [initflag],FLAG_INIT
	xor	ax,ax			# return without error
9:	ret



/*
 **************************************************************************
 *
 * Shutdown the NDIS driver interface.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX
 *
 */
pxe_shutdown:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_NOTINIT
	jbe	9f
	mov	byte ptr [initflag],FLAG_NOTINIT
	xor	dx,dx
	mov	[rcvmode],dx
	call	setrcvmode		# disable receiver
	call	closendis		# close adapter
	mov	byte ptr [initflag],FLAG_NOTINIT
	xor	ax,ax
9:	ret



/*
 **************************************************************************
 *
 * Reset the NDIS driver and reinitialize the interface.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI
 *
 */
pxe_reset_nic:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_NOTINIT
	jbe	9f
	mov	byte ptr [initflag],FLAG_NOTINIT

	call	resetndis		# reset the NDIS driver interface
	mov	ax,PXENV_STATUS_UNDI_PHY_INIT
	jc	9f

	mov	dx,[rcvmode]
	call	setrcvmode		# restore receive mode

#ifdef NEEDMCAST
	lea	si,o_ur_mcastbuf[di]
	call	setmcast
	mov	ax,PXENV_STATUS_UNDI_MCAST
	jc	9f			# reset the multicast address list
#endif

	mov	byte ptr [initflag],FLAG_OPEN
8:	xor	ax,ax			# return without error
9:	ret



/*
 **************************************************************************
 *
 * Open the network driver interface.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI
 *
 */
pxe_open:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_INIT
	jne	9f

	call	openndis		# open MAC interface
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f

	mov	dx,word ptr es:o_uo_pktfilter[di]
	cmp	dx,[rcvmode]
	je	1f
	mov	[rcvmode],dx
	call	setrcvmode		# set receive mode packet filter
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f

1:
#ifdef NEEDMCAST
	lea	si,o_uo_mcastbuf[di]
	call	setmcast		# set multicast address list
	mov	ax,PXENV_STATUS_UNDI_MCAST
	jc	9f
#endif

	mov	byte ptr [initflag],FLAG_OPEN
	xor	ax,ax
9:	ret



/*
 **************************************************************************
 *
 * Close the network driver interface.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX
 *
 */
pxe_close:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	9f

	call	closendis		# close MAC
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f

	mov	byte ptr [initflag],FLAG_INIT
	xor	ax,ax			# return without error
9:	ret



/*
 **************************************************************************
 *
 * Transmit a packet
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI, DI, BP, ES
 *
 */
pxe_transmit:

	push	es
	push	di
	push	bp
	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	9f

/*
 * According to the PXE specification it should never happen that the transmit
 * function gets called while an interrupt hasn't been processed completely.
 * However, with the netboot base code, this is indeed possible. How should we
 * handle this case? Just bailing out will terminate the boot process, so it's
 * not a real option. Actually, this problem has to be solved within the base
 * code, and not here. But we try a solution: just re-enable the hardware
 * interrupts to avoid any lockup.
 */

#if MASKINT == 1

	pushf
	cli
	mov	byte ptr [intdisable],1
	cmp	byte ptr [intcount],0
	je	2f			# check if interrupts are pending
	cmp	byte ptr [isrflag],0
	je	1f			# if currently processing any
	popf				# interrupts it is impossible to
	jmp	8f			# send anything

1:	call	endhwint		# if any interrupt is currently
	mov	byte ptr [intcount],0	# pending, reenable interrupt
2:	popf				# processing

#endif

	cld
	push	ds
	push	es
	mov	ax,ds			# DS:BX  -  ptr to parameter structure
	mov	es,ax			# ES:DI  -  ptr to send buffer
	mov	bx,di
	mov	di,offset writebuf
	pop	ds
	xor	bp,bp			# BP  -  total size of send buffer

/*
 * First setup the MAC level header. If the protocol value in the parameter
 * structure is 0, the upper layer has already filled in the MAC header.
 * Otherwise, if the transmit flag is 0, we have to fill in the broadcast
 * address for the destination address. Otherwise the hardware destination
 * address can be found in the protocol structure.
 */

	mov	dl,byte ptr [bx + o_ut_protocol]
	or	dl,dl
	jz	4f

	mov	cx,ETH_ALEN / 2
	cmp	byte ptr o_ut_xmtflag[bx],UNDI_XMT_DESTADDR
	je	1f
	mov	ax,0xFFFF		# set destination address to broadcast
	rep	stosw
	jmp	2f

1:	push	ds
	lds	si,o_ut_dest_addr[bx]	# copy destination address into send
	rep	movsw			# buffer
	pop	ds

2:	push	ds
	lds	si,es:[mac_cct]
	lds	si,cct_ssc[si]
#ifdef NEEDSETADDR
	lea	si,macssc_cur_addr[si]
#else
	lea	si,macssc_perm_addr[si]
#endif
	mov	cx,ETH_ALEN / 2
	rep	movsw			# copy source address into send buffer
	pop	ds

	mov	ax,ETHERTYPE_IP
	cmp	dl,UNDI_PROT_IP	# determine type of packet
	je	3f
	mov	ax,ETHERTYPE_ARP
	cmp	dl,UNDI_PROT_ARP
	je	3f
	mov	ax,ETHERTYPE_RARP
3:	xchg	al,ah			# write packet type into MAC header
	stosw
	add	bp,ETH_HLEN		# adjust buffer size to MAC header size

/*
 * The parameter structure contains a couple of buffers. One of them is the
 * so called immediate buffer. Copy it into the send buffer.
 */

4:	lds	bx,o_ut_tbd[bx]
	mov	cx,word ptr o_tbd_xmtlength[bx]
	jcxz	5f
	add	bp,cx
	cmp	bp,ETH_FRAME_MAX
	ja	1f
	push	ds
	lds	si,o_tbd_xmtbuffer[bx]
	shr	cx,1
	rep	movsw
	jnc	0f
	movsb
0:	pop	ds

/*
 * Now copy all the different transmit buffers into the send buffer.
 */

5:	mov	dx,o_tbd_blocks[bx]
	or	dx,dx
	jz	2f

	lea	bx,o_tbd_datablock[bx]
6:	mov	cx,o_tbd_tdlength[bx]
	jcxz	7f
	add	bp,cx
	cmp	bp,ETH_FRAME_MAX
	ja	1f
	push	ds
	lds	si,o_tbd_tdaddr[bx]
	shr	cx,1
	rep	movsw
	jnc	0f
	movsb
0:	pop	ds
7:	add	bx,datablk_size		# continue with next buffer
	dec	dx
	jnz	6b
	jmp	2f

/*
 * Handle transmit errors
 */

1:	pop	ds
	jmp	8f

/*
 * We can now fillup the send buffer to the minimum size.
 */

2:	cmp	bp,ETH_FRAME_MIN
	jae	3f
	mov	cx,ETH_FRAME_MIN
	sub	cx,bp
	xor	al,al			# pad send buffer to minimum size
	rep	stosb
	mov	bp,ETH_FRAME_MIN
3:	pop	ds
	mov	si,offset writebuf

/*
 * Send the buffer using the MAC. For this, we have to first setup the
 * frame buffer descriptor. This looks almost exactly like the UNDI
 * send buffer structure. Unfortunately, because we have to insert the
 * source and destination addresses, we cant simply pass the UNDI values
 * to the NDIS driver, but have to use a temporary buffer and fill it using
 * the code above.
 */

	mov	word ptr [txconflg],NDISERR_QUEUED
	mov	word ptr txbuf_immed_len[txbuf],0
	mov	word ptr txbuf_data_count[txbuf],1
	mov	byte ptr txbuf_ptr_type[txbuf + txbuf_data_bufs],TXBUF_PTR_PHYSICAL
	mov	word ptr txbuf_data_len[txbuf + txbuf_data_bufs],bp
	mov	word ptr txbuf_data_ptr[txbuf + txbuf_data_bufs + 0],si
	mov	word ptr txbuf_data_ptr[txbuf + txbuf_data_bufs + 2],ds

	mov	cx,XMT_RETRY
4:	push	cx
	push	MODID_PROT		# push module ID for protocol
	push	cs			# push request handle
	push	ds			# push far ptr to txbuf descriptor
	push	offset txbuf
	push	[MacDS]			# push MAC data segment
	lcall	[TransmitChain]		# call MAC to transmit the buffer
	sti				# sti required due to sume buggy MACs
	pop	cx
5:	cmp	ax,NDISERR_TRANSMIT	# check if transmit error
	jne	6f			# if we cant send the packet, retry it
	mov	bx,XMT_WAIT		# a couple of times, as the sender might
	call	wait10ms		# just be busy right now
	loop	4b
	jmp	8f

6:	cmp	ax,NDISERR_QUEUED	# poll until request processed
	jne	7f			# this is necessary because the UNDI
	mov	ax,[txconflg]		# specification requires the transmit
	jmp	5b			# to block until the request finished

7:	or	ax,ax
#if MASKINT == 1
	jz	0f			# return on error
#else
	jz	9f			# return on error
#endif
8:	mov	ax,PXENV_STATUS_UNDI_TRANSMIT
#if MASKINT == 1
0:	mov	byte ptr [intdisable],0
#endif

9:	pop	bp
	pop	di
	pop	es
	ret



#ifdef NEEDMCAST
/*
 **************************************************************************
 *
 * Set multicast address list
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI
 *
 */
pxe_set_multicast:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	9f
	lea	si,o_usma_mcastbuf[di]
	call	setmcast		# set multicast address list
	mov	ax,PXENV_STATUS_UNDI_MCAST
	jc	9f
	xor	ax,ax
9:	ret
#endif



#ifdef NEEDSETADDR
/*
 **************************************************************************
 *
 * Set station address
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX, SI
 *
 */
pxe_set_addr:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_INIT
	jne	9f			# is UNDI initialized already?
	lea	si,o_ussa_addr[di]
	call	setaddr
	mov	ax,PXENV_STATUS_UNDI_INV_HWADDR
	jc	9f
	xor	ax,ax
9:	ret
#endif



/*
 **************************************************************************
 *
 * Set packet filter
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX
 *
 */
pxe_set_filter:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	9f
	mov	dx,es:o_uo_pktfilter[di]
	cmp	dx,[rcvmode]
	je	8f
	mov	[rcvmode],dx
	call	setrcvmode
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f
8:	xor	ax,ax
9:	ret



/*
 **************************************************************************
 *
 * Get information about network driver
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, SI
 *
 */
pxe_get_info:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_INIT
	jb	9f

	cld
	push	di
	push	ds
	add	di,_I(o_ugi_intnumber)
	mov	ax,[irqnum]		# get interrupt IRQ number
	stosw
	lds	bx,[mac_cct]
	lds	bx,cct_ssc[bx]
	mov	ax,macssc_frame_size[bx]
	sub	ax,ETH_HLEN		# get MTU without MAC header
	stosw
	mov	ax,UNDI_TYPE_ETHER	# get ethernet hardware type
	stosw
	mov	ax,ETH_ALEN		# get hardware address length
	stosw

#ifdef NEEDSETADDR
	lea	si,macssc_cur_addr[bx]
#else
	lea	si,macssc_perm_addr[bx]
#endif
	mov	cx,ETH_ALEN / 2		# copy current hardware address
	rep	movsw
	add	di,UNDI_ADDR_LEN - ETH_ALEN
	lea	si,macssc_perm_addr[bx]
	mov	cx,ETH_ALEN / 2		# copy default hardware address
	rep	movsw
	pop	ds
	pop	di

	mov	ax,1			# save size of send/receive queues
	mov	es:o_ugi_rxbufcnt[di],ax
	mov	es:o_ugi_txbufcnt[di],ax
	xor	ax,ax
9:	ret



#ifdef NEEDSTAT
/*
 **************************************************************************
 *
 * Read network driver statistics
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: (E)AX, DX
 *
 */
pxe_get_stat:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_INIT
	jne	9f

	call	setstat			# update statistics
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f

	push	ds
	lds	bx,[mac_cct]
	lds	bx,cct_sss[bx]		# get pointer to network statistics
	cli				# interrupts have to be disabled
	mov	eax,macsss_tx_frames[bx]
	sub	eax,macsss_tx_hardware[bx]
	sub	eax,macsss_tx_timeout[bx]
	mov	es:o_ugs_xmtgood[di],eax
	mov	eax,macsss_rx_frames[bx]
	sub	eax,macsss_rx_error[bx]
	mov	es:o_ugs_rcvgood[di],eax
	mov	eax,macsss_rx_discarded[bx]
	mov	es:o_ugs_rcvreserr[di],eax
	mov	eax,macsss_rx_crc_frames[bx]
	mov	es:o_ugs_rcvcrcerr[di],eax
	sti
	xor	ax,ax			# return without error
9:	ret
#endif



#ifdef NEEDSTAT
/*
 **************************************************************************
 *
 * Clear network driver statistics
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: (E)AX
 *
 */
pxe_clear_stat:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_INIT
	jne	9f
	call	clrstat			# clear statistics
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f
	xor	ax,ax
9:	ret
#endif



/*
 **************************************************************************
 *
 * Force a hardware interrupt.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: (E)AX, BX, CX, DX
 *
 */
pxe_force_int:

	mov	ax,PXENV_STATUS_UNDI_INVALID_STATE
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	9f
	call	forceint
	mov	ax,PXENV_STATUS_FAILURE
	jc	9f
	xor	ax,ax
9:	ret



#ifdef NEEDMCAST
/*
 **************************************************************************
 *
 * Determine hardware multicast address fro IP multicast address.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, CX, SI
 *
 */
pxe_get_mcast_addr:

	lea	bx,o_ugma_inetaddr[di]
	mov	al,es:[bx]
	and	al,IP_CLASS_D_MASK	# check that the IP address really is
	cmp	al,IP_CLASS_D_VAL	# a multicast address
	mov	ax,PXENV_STATUS_UNDI_INV_HWADDR
	jne	9f

	cld
	push	di
	lea	di,o_ugma_hwaddr[di]
	mov	ax,HW_MCAST_2 * 256 + HW_MCAST_1
	stosw
	mov	al,HW_MCAST_3		# set hardware multicast for ethernet
	stosb				# devices
	mov	al,es:[bx + 1]
	and	al,0x7F			# only 23 bits of the IP address will
	stosb				# be copied
	mov	ax,es:[bx + 2]
	stosw
	pop	di
	xor	ax,ax			# return without error
9:	ret
#endif



/*
 **************************************************************************
 *
 * Return NIC type information.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, CX, SI
 *
 */
pxe_get_nic_type:

	cld
	push	di
	mov	si,offset devinfo
	mov	cx,getnic_size		# just copy the hardware info structure
	rep	movsb			# into destination parameter structure
	pop	di
	xor	ax,ax
	ret



/*
 **************************************************************************
 *
 * Return interface info.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: (E)AX, BX, CX, SI
 *
 */
pxe_get_iface_info:

	cld
	push	di
	push	ds
	lds	bx,[mac_cct]
	lds	bx,cct_ssc[bx]
	lea	di,o_ugii_ifacetype[di]
	lea	si,macssc_type_name[bx]
	mov	cx,o_ugii_ifacelen
	rep	movsb
	mov	eax,macssc_link_speed[bx]
	stosd
	mov	eax,macssc_sf_1[bx]
	or	eax,FLAGS_BITS
	and	eax,FLAGS_MASK
	stosd
	xor	ax,ax
	pop	ds
	pop	di
	ret



/*
 **************************************************************************
 *
 * Handle hardware interrupt. Note that the low-level handling has already
 * been done by the PXE entry dispatcher.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX, BX
 *
 */
pxe_isr:

/*
 * Dont check for the initflag status. This will be done in the receiver
 * routine. Otherwise, we would block the interrupt system on the NIC by
 * not calling the NDIS driver hardware interrupt handler.
 */

	mov	bx,es:o_uisr_funcflag[di]
	cmp	bx,UNDI_ISR_IN_START
	je	1f
	pushf					# we have to disable interrupts
	cli					# here
	cmp	bx,UNDI_ISR_IN_PROCESS
	je	3f
	test	byte ptr [isrflag],0xFF		# IN_GET_NEXT is only valid
	jz	2f				# if following a IN_PROCESS
	cmp	bx,UNDI_ISR_IN_GET_NEXT		# we only have one rx buffer so
	je	5f				# terminate ISR on IN_GET_NEXT
2:	popf
	mov	ax,PXENV_STATUS_FAILURE
	jmp	9f

/*
 * Handle function UNDI_ISR_IN_START. It will let the NDIS driver handle
 * the interrupt.
 */

1:	call	dohwint
	jc	4f

#if MASKINT == 1
	test	byte ptr [intdisable],0xFF
	jz	6f
	call	endhwint
	jmp	4f
#endif

6:	inc	byte ptr [intcount]
	mov	ax,UNDI_ISR_OUT_OURS
	jmp	8f

4:	mov	ax,UNDI_ISR_OUT_NOT_OURS
	jmp	8f

/*
 * Handle function UNDI_ISR_IN_PROCESS. It returns the address of the receive
 * buffer to the kernel.
 */

3:	mov	al,1
	xchg	al,[isrflag]		# check that we are not inside our
	or	al,al			# own handler
	mov	ax,UNDI_ISR_OUT_BUSY
	jnz	7f
	mov	ax,[readsize]
	or	ax,ax			# check that we have something to
	jz	5f			# receive
	mov	word ptr es:o_uisr_buflength[di],ax
	mov	word ptr es:o_uisr_framelength[di],ax
	mov	word ptr es:o_uisr_headerlength[di],ETH_HLEN
	mov	bx,offset readbuf
	mov	word ptr es:o_uisr_frame[di + 0],bx
	mov	word ptr es:o_uisr_frame[di + 2],ds
	call	getaddrtype
	mov	byte ptr es:o_uisr_pkttype[di],al
	mov	ax,[bx + ETH_ALEN * 2]
	xchg	al,ah
	mov	bx,UNDI_PROT_IP
	cmp	ax,ETHERTYPE_IP
	je	4f
	mov	bx,UNDI_PROT_ARP
	cmp	ax,ETHERTYPE_ARP
	je	4f
	mov	bx,UNDI_PROT_RARP
	cmp	ax,ETHERTYPE_RARP
	je	4f
	mov	bx,UNDI_PROT_OTHERS
4:	mov	es:o_uisr_prottype[di],bx
	mov	ax,UNDI_ISR_OUT_RECEIVE
	jmp	7f

/*
 * Terminate hardware interrupt processing.
 */

5:	cmp	byte ptr [intcount],0
	je	6f
	call	endhwint
6:	mov	word ptr [readsize],0
	mov	byte ptr [isrflag],0
	mov	byte ptr [intcount],0
	mov	ax,UNDI_ISR_OUT_DONE	# terminate ISR handling
7:	popf
8:	mov	es:o_uisr_funcflag[di],ax
	xor	ax,ax
9:	ret



/*
 **************************************************************************
 *
 * Return UNDI state.
 * Input:  ES:DI  -  pointer to parameter structure
 * Output: AX     -  error code
 * Registers changed: AX
 *
 */
pxe_get_state:

	mov	al,[initflag]
	mov	es:o_ugs_state[di],al
	xor	ax,ax
	ret



/*
 **************************************************************************
 *
 * Determine type of destination address
 * Input:  DS:BX  -  pointer to MAC header hardware address
 * Output: AX     -  type of hardware address (UNDI_PKT_*)
 * Registers changed: AX
 *
 */
getaddrtype:

/*
 * Check if the first bit of the hardware address is zero. If it is, the
 * address has to be directed.
 */

	mov	ax,UNDI_PKT_DIRECTED
	test	byte ptr [bx],0x01
	jz	9f

/*
 * Now check if we have a broadcast address.
 */

#if ETH_ALEN == 6
	mov	ax,[bx + 0]
	and	ax,[bx + 2]
	and	ax,[bx + 4]
	inc	ax
#else
	push	cx
	mov	cx,ETH_ALEN
	mov	al,0xFF
1:	and	al,[bx]
	inc	bx
	loop	1b
	pop	cx
	sub	bx,ETH_ALEN
	inc	al
#endif
	mov	ax,UNDI_PKT_BROADCAST
	jz	9f

/*
 * None of the conditions above applied, so we assume a multicast packet.
 */

#ifdef NEEDMCAST
	mov	ax,UNDI_PKT_MULTICAST
#else
	mov	ax,UNDI_PKT_DIRECTED
#endif
9:	ret




/*
 **************************************************************************
 *
 *		Handle MAC general requests
 *
 **************************************************************************
 *
 * Reset NDIS driver interface. This will not change the packet filter,
 * station address or open/close state of the MAC.
 * Input:  none
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
resetndis:

	test	word ptr [macflags],MAC_SF1_RESET_MAC
	jz	9f
	mov	word ptr [indstatus],NDIS_START_RESET
	mov	ax,NDIS_RESET_MAC
	xor	bx,bx
	xor	cx,cx
	xor	dx,dx
	call	dogenreq		# do reset request
	or	ax,ax
	jz	1f
	stc
	jmp	9f

/*
 * Wait for the reset request to terminate
 */

1:	mov	bx,offset indstatus
	mov	dx,NDIS_END_RESET
	call	waitflag
9:	ret



/*
 **************************************************************************
 *
 * Open NDIS driver interface.
 * Input:  none
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
openndis:

	test	word ptr [macflags],MAC_SF1_OPEN_CLOSE
	jz	9f
	mov	ax,NDIS_OPEN_ADAPTER
open1:	xor	bx,bx
open2:	xor	cx,cx
	xor	dx,dx
	call	dogenreq		# do general request
	or	ax,ax
	jz	open9
open8:	stc
open9:	ret



/*
 **************************************************************************
 *
 * Close NDIS driver interface.
 * Input:  none
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
closendis:

	test	word ptr [macflags],MAC_SF1_OPEN_CLOSE
	jz	open9
	mov	ax,NDIS_CLOSE_ADAPTER
	jmp	open1



/*
 **************************************************************************
 *
 * Force a hardware interrupt.
 * Input:  none
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
forceint:

	test	word ptr [macflags],MAC_SF1_INTERRUPT
	jz	open8
	mov	ax,NDIS_REQUEST_INT
	jmp	open1



/*
 **************************************************************************
 *
 * Set receive mode. UNDI filter values are the same as for NDIS.
 * Input:  DX  -  UNDI packet filter value
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
setrcvmode:

	mov	ax,NDIS_SET_PKT_FLT
	mov	bx,dx
	jmp	open2



#ifdef NEEDSTAT
/*
 **************************************************************************
 *
 * Update network statistics
 * Input:  None
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
setstat:

	mov	ax,NDIS_UPDATE_STATS
	jmp	open1
#endif



#ifdef NEEDSTAT
/*
 **************************************************************************
 *
 * Clear network statistics
 * Input:  None
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
clrstat:

	mov	ax,NDIS_CLEAR_STATS
	jmp	open1
#endif



/*
 **************************************************************************
 *
 * Let the MAC generate a hardware interrupt.
 * Input:  none
 * Output: Carry flag set if error or unsupported
 * Registers changed: AX, BX, CX, DX
 *
 */
doint:

	test	word ptr [macflags],MAC_SF1_INTERRUPT
	jz	open8
	mov	ax,NDIS_REQUEST_INT
	jmp	open1



#ifdef NEEDMCAST
/*
 **************************************************************************
 *
 * Set multicast addresses from UNDI address list.
 * Input:  ES:SI  -  pointer to UNDI multicast address structure
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX, SI
 *
 */
setmcast:

/*
 * First we have to delete the current multicast address list.
 */

	push	es
	les	bx,[mac_cct]
	les	bx,es:cct_ssc[bx]
	les	bx,es:macssc_mcast_addr[bx]
	mov	cx,[bx + 2]
	lea	bx,[bx + 4]
	jcxz	3f
1:	push	bx
	push	cx
	mov	ax,NDIS_DEL_MULT_ADDR
	mov	dx,bx
	mov	cx,es			# delete each multicast address
	xor	bx,bx			# successively
	call	dogenreq
	pop	cx
	pop	bx
	add	bx,NDIS_HWADDR_SIZE
	loop	1b
3:	pop	es

/*
 * Now we can set all multicast addresses.
 */

	mov	cx,es:[si]
	lea	si,[si + 2]
	jcxz	7f
2:	push	cx
	mov	ax,NDIS_ADD_MULT_ADDR
	mov	dx,si
	mov	cx,es
	xor	bx,bx			# set each multicast address
	call	dogenreq		# successively
	pop	cx
	or	ax,ax
	jnz	8f
	add	si,UNDI_ADDR_LEN
	loop	2b
7:	clc
	jmp	9f

8:	stc
9:	ret
#endif



#ifdef NEEDSETADDR
/*
 **************************************************************************
 *
 * Set hardware station address.
 * Input:  ES:SI  -  pointer to new hardware station address
 * Output: Carry flag set if error
 * Registers changed: AX, BX, CX, DX
 *
 */
setaddr:

	push	es
	mov	ax,NDIS_SET_STA_ADDR
	xor	bx,bx
	mov	cx,es
	mov	dx,si
	call	dogenreq
	pop	es
	or	ax,ax
	jz	9f
	stc
9:	ret
#endif



/*
 **************************************************************************
 *
 * Wait for a flag to change
 * Input:  BX  -  offset of flag word
 *         DX  -  required flag value
 * Output: Carry flag set if timeout exceeded
 * Registers changed: AX
 */
waitflag:

	mov	ax,IND_WAIT
	call	set_timeout
1:	cmp	[bx],dx
	je	9f
	call	chk_timeout
	jnc	1b
9:	ret



/*
 **************************************************************************
 *
 * Handle general requests to the MAC driver.
 * Input:  AX     -  request opcode
 *         BX     -  request word parameter
 *         CX:DX  -  request dword parameter
 * Output: AX     -  error code
 * Registers changed: AX, BX, CX, DX
 *
 */
dogenreq:

	mov	word ptr [reqconflg],NDISERR_QUEUED
	push	es
	push	MODID_PROT		# push module ID
	push	cs			# push unique request handle
	push	bx			# push request word parameter
	push	cx			# push dword parameter
	push	dx
	push	ax			# push opcode
	push	[MacDS]			# push MAC data segment
	lcall	[GenRequest]		# call MAC with general request

	sti				# needed because of some MAC bugs
1:	cmp	ax,NDISERR_QUEUED	# poll until request processed
	jne	9f
	mov	ax,[reqconflg]
	jmp	1b
9:	pop	es
	ret




/*
 **************************************************************************
 *
 *			MAC upcalls
 *
 **************************************************************************
 *
 * Handle RequestConfirm. It gets called when an asynchronous general
 * request has been finished processing.
 * Input:  1. Arg  -  protocol data segment
 *         2. Arg  -  request opcode
 *         3. Arg  -  status
 *         4. Arg  -  unique request handle (our code segment)
 *         5. Arg  -  MAC ID
 *         6. Arg  -  protocol ID
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
RequestConfirm:

#define prot_ds		6
#define req_opcode	8
#define status		10
#define request_handle	12
#define mac_id		14
#define prot_id		16

	enter	0,0
	push	ds
	push	bx
	mov	ds,[bp + prot_ds]	# get driver data segment
	mov	ax,NDISERR_PARAM
	mov	bx,cs
	cmp	word ptr [bp + request_handle],bx
	jne	9f			# check unique request handle
	cmp	word ptr [bp + mac_id],MODID_MAC
	jne	9f			# check MAC module ID
	cmp	word ptr [bp + prot_id],MODID_PROT
	jne	9f			# check protocol module ID
	mov	bx,[bp + status]
	mov	[reqconflg],bx		# save request status
	xor	ax,ax
9:	pop	bx
	pop	ds
	leave
	lret	12

#undef prot_ds
#undef req_opcode
#undef status
#undef request_handle
#undef mac_id
#undef prot_id



/*
 **************************************************************************
 *
 * Handle TransmitConfirm. It gets called when an asynchronous transmit
 * request has been finished processing.
 * Input:  1. Arg  -  protocol data segment
 *         2. Arg  -  status
 *         3. Arg  -  unique request handle (our code segment)
 *         4. Arg  -  MAC ID
 *         5. Arg  -  protocol ID
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
TransmitConfirm:

#define prot_ds		6
#define status		8
#define request_handle	10
#define mac_id		12
#define prot_id		14

	enter	0,0
	push	ds
	push	bx
	mov	ds,[bp + prot_ds]	# get driver data segment
	mov	ax,NDISERR_PARAM
	mov	bx,cs
	cmp	word ptr [bp + request_handle],bx
	jne	9f			# check unique request handle
	cmp	word ptr [bp + mac_id],MODID_MAC
	jne	9f			# check MAC module ID
	cmp	word ptr [bp + prot_id],MODID_PROT
	jne	9f			# check protocol module ID
	mov	bx,[bp + status]
	mov	[txconflg],bx		# save request status
	xor	ax,ax
9:	pop	bx
	pop	ds
	leave
	lret	10

#undef prot_ds
#undef status
#undef request_handle
#undef mac_id
#undef prot_id



/*
 **************************************************************************
 *
 * Handle StatusIndication
 * Input:  1.    Arg  -  protocol data segment
 *         2.    Arg  -  status indication opcode
 *         3./4. Arg  -  far pointer to indicate flag
 *         5.    Arg  -  word parameter
 *         6.    Arg  -  MAC ID
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
StatusIndication:

#define prot_ds		6
#define opcode		8
#define ind_flag	10
#define word_param	14
#define mac_id		16

	enter	0,0
	push	ds
	mov	ds,[bp + prot_ds]	# get driver data segment
	cmp	word ptr [bp + mac_id],MODID_MAC
	jne	9f			# check MAC module ID
	mov	ax,[bp + opcode]
	mov	[indstatus],ax		# save indication opcode
9:	xor	ax,ax			# always return success
	pop	ds
	leave
	lret	12

#undef prot_ds
#undef opcode
#undef ind_flag
#undef word_param
#undef mac_id



/*
 **************************************************************************
 *
 * Handle IndicationComplete
 * Input:  1. Arg  -  protocol data segment
 *         2. Arg  -  MAC ID
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
IndicationComplete:

#define prot_ds		6
#define mac_id		8

	enter	0,0
	push	ds
	mov	ds,[bp + prot_ds]	# get driver data segment
	cmp	word ptr [bp + mac_id],MODID_MAC
	jne	9f			# check MAC module ID

/*
 * In case we previously had a StartReset indication, but no EndReset
 * indication (an error in the NDIS 1.0.1 specification), set the
 * EndReset indication now.
 */

	pushf
	cli
	cmp	word ptr [indstatus],NDIS_START_RESET
	jne	1f
	mov	word ptr [indstatus],NDIS_END_RESET
1:	popf
9:	xor	ax,ax			# always return success
	pop	ds
	leave
	lret	4

#undef prot_ds
#undef mac_id



/*
 **************************************************************************
 *
 * Handle ReceiveLookahead.
 * Input:  1.    Arg  -  protocol data segment
 *         2./3. Arg  -  far ptr to indicate flag
 *         4./5. Arg  -  far ptr to lookahead buffer
 *         6.    Arg  -  number of bytes in lookahead buffer
 *         7.    Arg  -  total size of frame
 *         8.    Arg  -  MAC ID
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
ReceiveLookahead:

#define prot_ds		6
#define ind_flag	8
#define buffer		12
#define buf_size	16
#define frame_size	18
#define mac_id		20

	enter	0,0
	push	ds
	push	es
	push	cx
	mov	ds,[bp + prot_ds]	# get driver data segment
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	8f
	cmp	word ptr [bp + mac_id],MODID_MAC
	jne	8f			# check MAC module ID
	mov	cx,[bp + frame_size]	# get length of received frame
	or	cx,cx
	jnz	1f
	mov	cx,ETH_FRAME_MAX
1:	cmp	cx,ETH_FRAME_MAX	# check that its not too large
	ja	8f
	mov	ax,[readsize]		# buffer free? if not, discard
	or	ax,ax			# the current frame
	jnz	8f

/*
 * We accept all packets, so copy the received packet into our own
 * receive buffer. For TransferData we need to setup a receive buffer
 * descriptor.
 */

	mov	word ptr rdbuf_data_count[rdbuf],1
	mov	byte ptr rdbuf_ptr_type[rdbuf + rdbuf_data_bufs],RDBUF_PTR_PHYSICAL
	mov	word ptr rdbuf_data_len[rdbuf + rdbuf_data_bufs],cx
	mov	word ptr rdbuf_data_ptr[rdbuf + rdbuf_data_bufs + 0],offset readbuf
	mov	word ptr rdbuf_data_ptr[rdbuf + rdbuf_data_bufs + 2],ds

	push	ds
	push	offset readsize		# push far ptr to receive size value
	push	0			# push offset into received frame
	push	ds
	push	offset rdbuf		# push far ptr to rdbuf descriptor
	push	[MacDS]			# push MAC data segment
	lcall	[TransferData]		# ask MAC to copy the frame
	or	ax,ax
	jz	9f

7:	mov	word ptr [readsize],0
8:	mov	ax,NDISERR_REJECT	# reject frame if copy error
9:	pop	cx
	pop	es
	pop	ds
	leave
	lret	16

#undef prot_ds
#undef ind_flag
#undef buffer
#undef buf_size
#undef frame_size
#undef mac_id



/*
 **************************************************************************
 *
 * Handle ReceiveChain
 * Input:  1.    Arg  -  protocol data segment
 *         2./3. Arg  -  far ptr to indicate flag
 *         4./5. Arg  -  far ptr to receive buffer descriptor
 *         6.    Arg  -  unique handle for this request
 *         7.    Arg  -  total size of frame
 *         8.    Arg  -  MAC ID
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
ReceiveChain:

#define prot_ds		6
#define ind_flag	8
#define buffer		12
#define handle		16
#define frame_size	18
#define mac_id		20

	enter	0,0
	push	ds
	push	bx
	push	cx
	push	si
	push	di
	mov	ds,[bp + prot_ds]	# get driver data segment
	cmp	byte ptr [initflag],FLAG_OPEN
	jne	8f
	cmp	word ptr [bp + mac_id],MODID_MAC
	jne	8f			# check MAC module ID
	mov	cx,[bp + frame_size]	# get length of received frame
	cmp	cx,ETH_FRAME_MAX	# check that its not too large
	ja	8f
	mov	ax,[readsize]		# buffer free? if not, discard
	or	ax,ax			# the current frame
	jnz	8f

/*
 * We accept all frames, so copy the received frame into our local receive
 * buffer.
 */

	cld
	push	ds
	push	es
	mov	ax,ds
	mov	es,ax			# set ES:DI to local receive buffer
	mov	di,offset readbuf
	lds	bx,[bp + buffer]	# set DS:BX to recieve buffer descriptor
	mov	cx,rxbuf_data_count[bx]
	lea	bx,rxbuf_data_bufs[bx]
1:	push	ds
	push	cx
	mov	cx,rxbuf_data_len[bx]
	lds	si,rxbuf_data_ptr[bx]
	shr	cx,1
	rep	movsw			# copy buffer into local receive buffer
	jnc	2f
	movsb
2:	pop	cx
	pop	ds
	add	bx,RXBUF_SIZE		# proceed with next buffer
	loop	1b
	pop	es
	pop	ds
	sub	di,offset readbuf
	mov	[readsize],di		# save size of received data
	xor	ax,ax
	jmp	9f

8:	mov	ax,NDISERR_REJECT
9:	pop	di
	pop	si
	pop	cx
	pop	bx
	pop	ds
	leave
	lret	16

#undef prot_ds
#undef ind_flag
#undef buffer
#undef handle
#undef frame_size
#undef mac_id



/*
 **************************************************************************
 *
 * Handle SystemRequest. We should never be called with a system request,
 * so this is just an error return.
 * Input:  1. Arg  -  request dependant double word parameter 0
 *         2. Arg  -  request dependant double word parameter 1
 *         3. Arg  -  request dependant word parameter 0
 *         4. Arg  -  opcode
 *         5. Arg  -  our own data segment
 * Output: AX      -  error code
 * Registers changed: AX
 *
 */
SystemRequest:

	mov	ax,NDISERR_FUNCTION
	lret	14




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

	.end

