undi.c revision d65680efa46fa49e8bf14e67b29b782510ff934c
/**************************************************************************
UNDI NIC driver for Etherboot
This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights
reserved.
$Id$
***************************************************************************/
/*
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
/*
* Sun GPL Disclaimer: For the avoidance of doubt, except that if any license choice
* other than GPL or LGPL is available it will apply instead, Sun elects to use only
* the General Public License version 2 (GPLv2) at this time for any software where
* a choice of GPL license versions is made available with the language indicating
* that GPLv2 or any later version may be used, or where a choice of which version
* of the GPL is applied is otherwise unspecified.
*/
#ifdef PCBIOS
/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include "pci.h"
/* UNDI and PXE defines. Includes pxe.h. */
#include "undi.h"
/* 8259 PIC defines */
#include "pic8259.h"
/* Real-mode calls */
#include "realmode.h"
/* E820 map mangler */
#include "hidemem.h"
/* NIC specific static variables go here */
.undi_rom_id = NULL,
.base_mem_data = NULL,
.driver_code = NULL,
.driver_code_size = 0,
.driver_data = NULL,
.driver_data_size = 0,
.xmit_buffer = NULL,
.prestarted = 0,
.started = 0,
.initialized = 0,
.opened = 0,
};
/* Function prototypes */
static int allocate_base_mem_data ( void );
static int free_base_mem_data ( void );
static int eb_pxenv_undi_shutdown ( void );
static int eb_pxenv_stop_undi ( void );
static int undi_unload_base_code ( void );
static int undi_full_shutdown ( void );
/* Trivial/nontrivial IRQ handler selection */
#ifdef UNDI_NONTRIVIAL_IRQ
static void nontrivial_irq_handler ( void );
static void nontrivial_irq_handler_end ( void );
#else
#endif /* UNDI_NONTRIVIAL_IRQ */
/* Size of variable-length data in base_mem_data */
/**************************************************************************
* Utility functions
**************************************************************************/
/* Checksum a block.
*/
uint16_t i = 0;
for ( i = 0; i < size; i++ ) {
}
return sum;
}
/* Print the status of a !PXE structure
*/
static void pxe_dump ( void ) {
printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx "
"BD %hx:%hx BC %hx:%hx\n",
}
*/
static int allocate_base_mem_data ( void ) {
/* Allocate space in base memory.
* Initialise pointers to base memory structures.
*/
allot_base_memory ( sizeof(undi_base_mem_data_t) +
printf ( "Failed to allocate base memory\n" );
return 0;
}
}
return 1;
}
static int free_base_mem_data ( void ) {
sizeof(undi_base_mem_data_t) +
copy_undi_irq_handler ( NULL, 0 );
}
return 1;
}
int target;
int index;
int bit;
}
}
int shoot_this_target = 0;
int shoot_last_target = 0;
int start_target = 0;
int target;
if ( shoot_this_target && !shoot_last_target ) {
} else if ( shoot_last_target && !shoot_this_target ) {
range_size );
}
}
}
/* Debug macros
*/
#ifdef TRACE_UNDI
#else
#define DBG(...)
#endif
"SUCCESS" : \
"FAILURE" : "UNKNOWN" ) )
/**************************************************************************
* Base memory scanning functions
**************************************************************************/
/* Locate the $PnP structure indicating a PnP BIOS.
*/
static int hunt_pnp_bios ( void ) {
printf ( "Hunting for PnP BIOS..." );
while ( off > 0 ) {
off -= 16;
printf ( "invalid checksum\n..." );
continue;
}
printf ( "ok\n" );
return 1;
}
}
printf ( "none found\n" );
return 0;
}
/* Locate the !PXE structure indicating a loaded UNDI driver.
*/
static int hunt_pixie ( void ) {
printf ( "Hunting for pixies..." );
while ( ptr > 0x10000 ) {
ptr -= 16;
printf ( "invalid checksum\n..." );
continue;
}
if ( ptr < get_free_base_memory() ) {
printf ( "in free base memory!\n\n"
"WARNING: a valid !PXE structure was "
"found in an area of memory marked "
"as free!\n\n" );
pxe_dump();
printf ( "\nIgnoring and continuing, but this "
"may cause problems later!\n\n" );
continue;
}
printf ( "ok\n" );
pxe_dump();
printf ( "Resetting pixie...\n" );
pxe_dump();
return 1;
}
}
printf ( "none found\n" );
ptr = 0;
return 0;
}
/* Locate PCI PnP ROMs.
*/
static int hunt_rom ( void ) {
/* If we are not a PCI device, we cannot search for a ROM that
* matches us (?)
*/
return 0;
printf ( "Hunting for ROMs..." );
while ( ptr > 0x0c0000 ) {
ptr -= 0x800;
printf ( "not a PCI ROM\n..." );
continue;
}
printf ( "invalid PCI signature\n..." );
continue;
}
pcir_header->device_id );
printf ( "not me (%hx:%hx)\n...",
continue;
}
printf ( "not a PnP ROM\n..." );
continue;
}
printf ( "invalid $PnP signature\n..." );
continue;
}
printf ( "invalid PnP checksum\n..." );
continue;
}
printf ( "ok\nROM contains %s by %s\n",
return 1;
}
}
printf ( "none found\n" );
ptr = 0;
return 0;
}
/* Locate ROMs containing UNDI drivers.
*/
static int hunt_undi_rom ( void ) {
while ( hunt_rom() ) {
printf ( "Not a PXE ROM\n" );
continue;
}
printf ( "Invalid UNDI signature\n" );
continue;
}
printf ( "Invalid checksum\n" );
continue;
}
printf ( "Located UNDI ROM supporting revision %d.%d.%d\n",
return 1;
}
return 0;
}
/**************************************************************************
* Low-level UNDI API call wrappers
**************************************************************************/
/* Make a real-mode UNDI API call to the UNDI routine at
* routine_seg:routine_off, passing in three uint16 parameters on the
* real-mode stack.
*/
struct {
};
"popw %di\n\t" /* %es:di = routine */
"popw %es\n\t"
"pushw %cs\n\t" /* set up return address */
"call 1f\n\t1:popw %bx\n\t"
"leaw (2f-1b)(%bx), %ax\n\t"
"pushw %ax\n\t"
"pushw %es\n\t" /* routine address to stack */
"pushw %di\n\t"
"lret\n\t" /* calculated lcall */
"\n2:\n\t" /* continuation point */
);
/* Parameters are left on stack: set out_stack = in_stack */
/* UNDI API calls may rudely change the status of A20 and not
* bother to restore it afterwards. Intel is known to be
* guilty of this.
*
* Note that we will return to this point even if A20 gets
* screwed up by the UNDI driver, because Etherboot always
* resides in an even megabyte of RAM.
*/
gateA20_set();
return ret;
}
/* Make a real-mode call to the UNDI loader routine at
* routine_seg:routine_off, passing in the seg:off address of a
* pxenv_structure on the real-mode stack.
*/
static int undi_call_loader ( void ) {
/* Hide Etherboot around the loader, so that the PXE stack
* doesn't trash our memory areas
*/
0 /* Unused for UNDI loader API */ );
if ( !unhide_etherboot() ) {
printf ( "FATAL: corrupt INT15\n" );
return 0;
}
/* Return 1 for success, to be consistent with other routines */
printf ( "UNDI loader call failed with status %#hx\n",
return 0;
}
/* Make a real-mode UNDI API call, passing in the opcode and the
* seg:off address of a pxenv_structure on the real-mode stack.
*
* Two versions: undi_call() will automatically report any failure
* codes, undi_call_silent() will not.
*/
/* Return 1 for success, to be consistent with other routines */
}
printf ( "UNDI API call %#hx failed with status %#hx\n",
return 0;
}
#ifdef UNDI_NONTRIVIAL_IRQ
/* IRQ handler that actually calls PXENV_UNDI_ISR. It's probably
* better to use the trivial IRQ handler, since this seems to work for
* just about all known NICs and doesn't involve making a PXE API call
* in interrupt context.
*
* This routine is mainly used for testing the Etherboot PXE stack's
* ability to be called in interrupt context. It is not compiled in
* by default.
*
* This code has fewer safety checks than those in the
* trivial_irq_handler routines. These are omitted because this code
* is not intended for mainstream use.
*/
static int copy_nontrivial_irq_handler ( void *target,
/* Will be installed on a paragraph boundary, so access variables
* using %cs:(xxx-irqstart)
*/
"\n\t"
"irqstart:\n\t"
/* Fields here must match those in undi_irq_handler_t */
"chain_to:\t.word 0,0\n\t"
"irq_chain:\t.byte 0,0,0,0\n\t"
"entry:\t.word 0,0\n\t"
"count_all:\t.word 0\n\t"
"count_ours:\t.word 0\n\t"
"undi_isr:\n\t"
"undi_isr_Status:\t.word 0\n\t"
"undi_isr_FuncFlag:\t.word 0\n\t"
"undi_isr_others:\t.word 0,0,0,0,0,0\n\t"
"handler:\n\t"
/* Assume that PXE stack will corrupt everything */
"pushal\n\t"
"push %ds\n\t"
"push %es\n\t"
"push %fs\n\t"
"push %gs\n\t"
/* Set DS == CS */
"pushw %cs\n\t"
"popw %ds\n\t"
/* Set up parameters for call */
#ifdef VBOX
#else /* !VBOX */
#endif /* !VBOX */
"pushw %cs\n\t"
"popw %es\n\t"
"movw $(undi_isr-irqstart), %di\n\t"
#ifdef VBOX
#else /* !VBOX */
#endif /* !VBOX */
"pushw %es\n\t" /* Registers for PXENV+, stack for !PXE */
"pushw %di\n\t"
"pushw %bx\n\t"
/* Make PXE API call */
"lcall *%ds:(entry-irqstart)\n\t"
"addw $6, %sp\n\t"
/* Set DS == CS */
"pushw %cs\n\t"
"popw %ds\n\t"
/* Check return status to see if it's one of our interrupts */
#ifdef VBOX
#else /* !VBOX */
#endif /* !VBOX */
"jne 1f\n\t"
#ifdef VBOX
#else /* !VBOX */
#endif /* !VBOX */
"jne 1f\n\t"
/* Increment count_ours if so */
"incw %ds:(count_ours-irqstart)\n\t"
"1:\n\t"
/* Increment count_all anyway */
"incw %ds:(count_all-irqstart)\n\t"
/* Restore registers and return */
"popw %gs\n\t"
"popw %fs\n\t"
"popw %es\n\t"
"popw %ds\n\t"
"popal\n\t"
"\n\t"
/* Chain to acknowledge the interrupt */
"cmpb $0, %cs:(irq_chain-irqstart)\n\t"
"jz 2f\n\t"
"ljmp %cs:(chain_to-irqstart)\n\t"
"2:\n\t"
"\n\t"
"iret\n\t"
"\n\t"
);
/* Copy handler */
return 1;
}
printf ( "WARNING: using non-trivial IRQ handler [EXPERIMENTAL]\n" );
/*
* This code is deliberately quick and dirty. The whole
* nontrivial IRQ stuff is only present in order to test out
* calling our PXE stack in interrupt context. Do NOT use
* this in production code.
*/
disable_irq ( irq );
handler->count_ours = 0;
enable_irq ( irq );
return 1;
}
return 1;
}
int triggered = ( nontrivial_irq_this_trigger_count -
return triggered ? 1 : 0;
}
printf ( "IRQ %d triggered %d times (%d of which were ours)\n",
}
#endif /* UNDI_NONTRIVIAL_IRQ */
/**************************************************************************
* High-level UNDI API call wrappers
**************************************************************************/
/* Install the UNDI driver from a located UNDI ROM.
*/
static int undi_loader ( void ) {
printf ( "ERROR: attempted to call loader of an ISA ROM?\n" );
return 0;
}
/* AX contains PCI bus:devfn (PCI specification) */
/* BX and DX set to 0xffff for non-ISAPnP devices
* (BIOS boot specification)
*/
/* ES:DI points to PnP BIOS' $PnP structure
* (BIOS boot specification)
*/
} else {
/* Set to a NULL pointer and hope that we don't need it */
}
/* Allocate space for UNDI driver's code and data segments */
printf ( "Could not allocate %d bytes for UNDI code segment\n",
return 0;
}
printf ( "Could not allocate %d bytes for UNDI code segment\n",
return 0;
}
printf ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n",
/* Do the API call to install the loader */
if ( ! undi_call_loader () ) return 0;
printf ( "UNDI driver created a pixie at %hx:%hx...",
printf ( "invalid signature\n" );
return 0;
}
printf ( "invalid checksum\n" );
return 0;
}
printf ( "ok\n" );
pxe_dump();
return 1;
}
/* Start the UNDI driver.
*/
static int eb_pxenv_start_undi ( void ) {
int success = 0;
/* AX contains PCI bus:devfn (PCI specification) */
/* BX and DX set to 0xffff for non-ISAPnP devices
* (BIOS boot specification)
*/
/* ES:DI points to PnP BIOS' $PnP structure
* (BIOS boot specification)
*/
} else {
/* Set to a NULL pointer and hope that we don't need it */
}
DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n",
return success;
}
static int eb_pxenv_undi_startup ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_STARTUP => (void)\n" );
return success;
}
static int eb_pxenv_undi_cleanup ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_CLEANUP => (void)\n" );
return success;
}
static int eb_pxenv_undi_initialize ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" );
return success;
}
static int eb_pxenv_undi_shutdown ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" );
if ( success ) {
undi.initialized = 0;
}
return success;
}
static int eb_pxenv_undi_open ( void ) {
int success = 0;
/* Multicast support not yet implemented */
DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx "
"MCastAddrCount=%hx\n",
return success;
}
static int eb_pxenv_undi_close ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_CLOSE => (void)\n" );
return success;
}
static int eb_pxenv_undi_transmit_packet ( void ) {
int success = 0;
/* XMitFlag selects unicast / broadcast */
sizeof(broadcast) ) == 0 ) {
} else {
}
/* Zero reserved dwords */
/* Segment:offset pointer to DestAddr in base memory */
/* Segment:offset pointer to TBD in base memory */
/* Use only the "immediate" part of the TBD */
DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n"
"... DestAddr=%hx:%hx TBD=%hx:%hx ...\n",
DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n",
DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n",
return success;
}
static int eb_pxenv_undi_set_station_address ( void ) {
/* This will spuriously fail on some cards. Ignore failures.
* We only ever use it to set the MAC address to the card's
* permanent value anyway, so it's a useless call (although we
* make it because PXE spec says we should).
*/
DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => "
"StationAddress=%!\n",
DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n",
return 1;
}
static int eb_pxenv_undi_get_information ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" );
DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s "
"BaseIO=%hx IntNumber=%hx ...\n"
"... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n"
"... CurrentNodeAddress=%! PermNodeAddress=%! ...\n"
"... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n",
return success;
}
static int eb_pxenv_undi_get_iface_info ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" );
DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n"
"... LinkSpeed=%x ServiceFlags=%x\n",
return success;
}
static int eb_pxenv_undi_isr ( void ) {
int success = 0;
DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n",
DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n"
"... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx "
"ProtType=%hhx ...\n... PktType=%hhx\n",
return success;
}
static int eb_pxenv_stop_undi ( void ) {
int success = 0;
DBG ( "PXENV_STOP_UNDI => (void)\n" );
return success;
}
static int eb_pxenv_unload_stack ( void ) {
int success = 0;
DBG ( "PXENV_UNLOAD_STACK => (void)\n" );
DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n",
"base-code is ready to be removed" :
"the size of free base memory has been changed" :
"the NIC interrupt vector has been changed" :
"UNEXPECTED STATUS CODE" ) ) ) );
return success;
}
static int eb_pxenv_stop_base ( void ) {
int success = 0;
DBG ( "PXENV_STOP_BASE => (void)\n" );
return success;
}
/* Unload UNDI base code (if any present) and free memory.
*/
static int undi_unload_base_code ( void ) {
/* Since we never start the base code, the only time we should
* reach this is if we were loaded via PXE. There are many
* different and conflicting versions of the "correct" way to
* unload the PXE base code, several of which appear within
* the PXE specification itself. This one seems to work for
* our purposes.
*
* We always call PXENV_STOP_BASE and PXENV_UNLOAD_STACK even
* if the !PXE structure indicates that no base code is
* present. We do this for the case that there is a
* base-code-less UNDI driver loaded that has hooked some
* interrupts. If the base code really is absent, then these
* calls will fail, we will ignore the failure, and our
* subsequent memory-freeing code is robust enough to handle
* whatever's thrown at it.
*/
{
printf ( "Could not free memory allocated to PXE base code: "
"possible memory leak\n" );
return 0;
}
/* Free data structures. Forget what the PXE specification
* says about how to calculate the new size of base memory;
* basemem.c takes care of all that for us. Note that we also
* have to free the stack (even though PXE spec doesn't say
* anything about it) because nothing else is going to do so.
*
* Structures will almost certainly not be kB-aligned and
* there's a reasonable chance that the UNDI code or data
* portions will lie in the same kB as the base code. Since
* forget_base_memory works only in 1kB increments, this means
* we have to do some arcane trickery.
*/
/* Don't shoot any bits of the UNDI driver code or data */
shoot_targets ( &lineup );
/* Free and reallocate our own base memory data structures, to
* allow the freed base-code blocks to be fully released.
*/
if ( ! allocate_base_mem_data() ) {
printf ( "FATAL: memory unaccountably lost\n" );
while ( 1 ) {};
}
return 1;
}
/* UNDI full initialization
*
* This calls all the various UNDI initialization routines in sequence.
*/
static int undi_full_startup ( void ) {
if ( ! eb_pxenv_start_undi() ) return 0;
if ( ! eb_pxenv_undi_startup() ) return 0;
if ( ! eb_pxenv_undi_initialize() ) return 0;
if ( ! eb_pxenv_undi_get_information() ) return 0;
return 0;
}
if ( ! eb_pxenv_undi_set_station_address() ) return 0;
if ( ! eb_pxenv_undi_open() ) return 0;
return 1;
}
/* UNDI full shutdown
*
* This calls all the various UNDI shutdown routines in sequence and
* also frees any memory that it can.
*/
static int undi_full_shutdown ( void ) {
/* In case we didn't allocate the driver's memory in the first
* place, try to grab the code and data segments and sizes
* from the !PXE structure.
*/
0 );
}
0 );
}
/* Ignore errors and continue in the hope of shutting
* down anyway
*/
/* We may get spurious UNDI API errors at this
* point. If startup() succeeded but
* initialize() failed then according to the
* spec, we should call shutdown(). However,
* some NICS will fail with a status code
* 0x006a (INVALID_STATE).
*/
}
}
if ( undi.prestarted ) {
/* Success OR Failure indicates that memory
* can be freed. Any other status code means
* that it can't.
*/
printf ("Could not free memory allocated to "
"UNDI driver: possible memory leak\n");
return 0;
}
}
}
/* Free memory allocated to UNDI driver */
/* Clear contents in order to eliminate !PXE and PXENV
* signatures to prevent spurious detection via base
* memory scan.
*/
undi.driver_code_size = 0;
}
undi.driver_data_size = 0;
}
/* !PXE structure now gone; memory freed */
return 1;
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
{
/* Fun, fun, fun. UNDI drivers don't use polling; they use
* interrupts. We therefore cheat and pretend that an
* interrupt has occurred every time undi_poll() is called.
* This isn't too much of a hack; PCI devices share IRQs and
* so the first thing that a proper ISR should do is call
* PXENV_UNDI_ISR to determine whether or not the UNDI NIC
* generated the interrupt; there is no harm done by spurious
* calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
* handling them any more rapidly than the usual rate of
* undi_poll() being called even if we did implement a full
* ISR. So it should work. Ha!
*
* Addendum (21/10/03). Some cards don't play nicely with
* this trick, so instead of doing it the easy way we have to
* go to all the hassle of installing a genuine interrupt
* service routine and dealing with the wonderful 8259
* Programmable Interrupt Controller. Joy.
*/
/* See if a hardware interrupt has occurred since the last poll().
*/
/* Given the frailty of PXE stacks, it's probably not safe to
* risk calling PXENV_UNDI_ISR with
* FuncFlag=PXENV_UNDI_ISR_START twice for the same interrupt,
* so we cheat slightly and assume that there is something
* ready to retrieve as long as an interrupt has occurred.
*/
if ( ! retrieve ) return 1;
#ifdef UNDI_NONTRIVIAL_IRQ
/* With the nontrivial IRQ handler, we have already called
* PXENV_UNDI_ISR with PXENV_UNDI_ISR_IN_START and determined
* that it is one of ours.
*/
#else
/* Ask the UNDI driver if this is "our" interrupt.
*/
if ( ! eb_pxenv_undi_isr() ) return 0;
/* "Not our interrupt" translates to "no packet ready
* to read".
*/
/* FIXME: Technically, we shouldn't be the one sending
* EOI. However, since our IRQ handlers don't yet
* support chaining, nothing else gets the chance to.
* One nice side-effect of doing this is that it means
* we can cheat and claim the timer interrupt as our
* NIC interrupt; it will be inefficient but will
* work.
*/
return 0;
}
#endif
/* At this stage, the device should have cleared its interrupt
* line so we can send EOI to the 8259.
*/
/* We might have received a packet, or this might be a
* "transmit completed" interrupt. Zero nic->packetlen,
* increment whenever we receive a bit of a packet, test
* nic->packetlen when we're done to see whether or not we
* actually received anything.
*/
if ( ! eb_pxenv_undi_isr() ) return 0;
/* We really don't care about transmission complete
* interrupts.
*/
break;
case PXENV_UNDI_ISR_OUT_BUSY:
/* This should never happen.
*/
printf ( "UNDI ISR thinks it's being re-entered!\n"
"Aborting receive\n" );
return 0;
/* Copy data to receive buffer */
break;
default:
printf ( "UNDI ISR returned bizzare status code %d\n",
}
if ( ! eb_pxenv_undi_isr() ) return 0;
}
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void undi_transmit(
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
/* Copy destination to buffer in base memory */
/* Translate packet type to UNDI packet type */
switch ( t ) {
default: printf ( "Unknown packet type %hx\n", t );
return;
}
/* Store packet length in TBD */
/* Check to see if data to be transmitted is currently in base
* memory. If not, allocate temporary storage in base memory
* and copy it there.
*/
if ( SEGMENT( p ) <= 0xffff ) {
} else {
}
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
}
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
/* Locate an UNDI driver by first scanning through base memory for an
* installed driver and then by scanning for UNDI ROMs and attempting
* to install their drivers.
*/
static int hunt_pixies_and_undi_roms ( void ) {
if ( hunt_type == HUNT_FOR_PIXIES ) {
if ( hunt_pixie() ) {
return 1;
}
}
while ( hunt_undi_rom() ) {
if ( undi_loader() ) {
return 1;
}
undi_full_shutdown(); /* Free any allocated memory */
}
return 0;
}
/* The actual Etherboot probe routine.
*/
{
/* Zero out global undi structure */
/* Store PCI parameters; we will need them to initialize the
* UNDI driver later. If not a PCI device, leave as 0.
*/
if ( pci ) {
}
/* Find the BIOS' $PnP structure */
if ( ! hunt_pnp_bios() ) {
/* Not all PXE stacks actually insist on a PnP BIOS.
* In particular, an Etherboot PXE stack will work
* just fine without one.
*
* We used to make this a fatal error, but now we just
* warn and continue. Note that this is necessary in
* order to be able to debug the Etherboot PXE stack
* under Bochs, since Bochs' BIOS is non-PnP.
*/
printf ( "WARNING: No PnP BIOS found\n" );
}
/* Allocate base memory data structures */
if ( ! allocate_base_mem_data() ) return 0;
/* Search thoroughly for UNDI drivers */
for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
/* Try to initialise UNDI driver */
printf ( "Initializing UNDI driver. Please wait...\n" );
if ( ! undi_full_startup() ) {
printf ( "Cable not connected (code %#hx)\n",
}
continue;
}
/* Basic information: MAC, IO addr, IRQ */
if ( ! eb_pxenv_undi_get_information() ) continue;
printf ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
/* Fill out MAC address in nic structure */
ETH_ALEN );
/* More diagnostic information including link speed */
if ( ! eb_pxenv_undi_get_iface_info() ) continue;
printf ( "NDIS type %s interface at %d Mbps\n",
return 1;
}
return 0;
}
unsigned short *probe_addrs __unused ) {
}
/* UNDI driver states that it is suitable for any PCI NIC (i.e. any
* PCI device of class PCI_CLASS_NETWORK_ETHERNET). If there are any
* obscure UNDI NICs that have the incorrect PCI class, add them to
* this list.
*/
/* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */
};
.type = NIC_DRIVER,
.name = "UNDI",
.probe = undi_probe,
};
.type = NIC_DRIVER,
.name = "UNDI",
.probe = undi_isa_probe,
.ioaddrs = 0,
};
#endif /* PCBIOS */