📄 undi.c
字号:
* * 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. */ memset ( &lineup, 0, sizeof(lineup) ); if ( SEGMENT(bc_code) != 0 ) assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT ); if ( SEGMENT(bc_data) != 0 ) assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT ); if ( SEGMENT(bc_stck) != 0 ) assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT ); /* Don't shoot any bits of the UNDI driver code or data */ assemble_firing_squad ( &lineup, VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0), undi.pxe->UNDICode.Seg_Size, DONTSHOOT ); assemble_firing_squad ( &lineup, VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0), undi.pxe->UNDIData.Seg_Size, DONTSHOOT ); shoot_targets ( &lineup ); undi.pxe->BC_Code.Seg_Addr = 0; undi.pxe->BC_Data.Seg_Addr = 0; undi.pxe->Stack.Seg_Addr = 0; /* Free and reallocate our own base memory data structures, to * allow the freed base-code blocks to be fully released. */ free_base_mem_data(); 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. */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; undi.irq = undi.pxs->undi_get_information.IntNumber; if ( ! install_trivial_irq_handler ( undi.irq ) ) { undi.irq = IRQ_NONE; return 0; } memmove ( &undi.pxs->undi_set_station_address.StationAddress, &undi.pxs->undi_get_information.PermNodeAddress, sizeof (undi.pxs->undi_set_station_address.StationAddress) ); 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. */int undi_full_shutdown ( void ) { if ( undi.pxe != NULL ) { /* 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. */ if ( undi.driver_code == NULL ) { undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0 ); undi.driver_code_size = undi.pxe->UNDICode.Seg_Size; } if ( undi.driver_data == NULL ) { undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0 ); undi.driver_data_size = undi.pxe->UNDIData.Seg_Size; } /* Ignore errors and continue in the hope of shutting * down anyway */ if ( undi.opened ) eb_pxenv_undi_close(); if ( undi.started ) { eb_pxenv_undi_cleanup(); /* 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). */ eb_pxenv_undi_shutdown(); } if ( undi.irq != IRQ_NONE ) { remove_trivial_irq_handler ( undi.irq ); undi.irq = IRQ_NONE; } undi_unload_base_code(); if ( undi.prestarted ) { eb_pxenv_stop_undi(); /* Success OR Failure indicates that memory * can be freed. Any other status code means * that it can't. */ if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) || ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) { printf ("Could not free memory allocated to " "UNDI driver: possible memory leak\n"); return 0; } } } /* Free memory allocated to UNDI driver */ if ( undi.driver_code != NULL ) { /* Clear contents in order to eliminate !PXE and PXENV * signatures to prevent spurious detection via base * memory scan. */ memset ( undi.driver_code, 0, undi.driver_code_size ); forget_base_memory ( undi.driver_code, undi.driver_code_size ); undi.driver_code = NULL; undi.driver_code_size = 0; } if ( undi.driver_data != NULL ) { forget_base_memory ( undi.driver_data, undi.driver_data_size ); undi.driver_data = NULL; undi.driver_data_size = 0; } /* !PXE structure now gone; memory freed */ undi.pxe = NULL; return 1;}/**************************************************************************POLL - Wait for a frame***************************************************************************/static int undi_poll(struct nic *nic){ /* 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(). */ if ( ! trivial_irq_triggered ( undi.irq ) ) return 0; /* Ask the UNDI driver if this is "our" interrupt. */ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START; if ( ! eb_pxenv_undi_isr() ) return 0; if ( undi.pxs->undi_isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS ) { /* "Not our interrupt" translates to "no packet ready * to read". */ return 0; } /* At this stage, the device should have cleared its interrupt * line so we can send EOI to the 8259. */ send_specific_eoi ( undi.irq ); /* 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. */ nic->packetlen = 0; undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; if ( ! eb_pxenv_undi_isr() ) return 0; while ( undi.pxs->undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE ) { switch ( undi.pxs->undi_isr.FuncFlag ) { case PXENV_UNDI_ISR_OUT_TRANSMIT: /* 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; case PXENV_UNDI_ISR_OUT_RECEIVE: /* Copy data to receive buffer */ memcpy ( nic->packet + nic->packetlen, VIRTUAL( undi.pxs->undi_isr.Frame.segment, undi.pxs->undi_isr.Frame.offset ), undi.pxs->undi_isr.BufferLength ); nic->packetlen += undi.pxs->undi_isr.BufferLength; break; default: printf ( "UNDI ISR returned bizzare status code %d\n", undi.pxs->undi_isr.FuncFlag ); } undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; if ( ! eb_pxenv_undi_isr() ) return 0; } return nic->packetlen > 0 ? 1 : 0;}/**************************************************************************TRANSMIT - Transmit a frame***************************************************************************/static void undi_transmit( struct nic *nic, const char *d, /* Destination */ unsigned int t, /* Type */ unsigned int s, /* size */ const char *p) /* Packet */{ /* Inhibit compiler warning about unused parameter nic */ if ( nic == NULL ) {}; /* Copy destination to buffer in base memory */ memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) ); /* Translate packet type to UNDI packet type */ switch ( t ) { case IP : undi.pxs->undi_transmit.Protocol = P_IP; break; case ARP: undi.pxs->undi_transmit.Protocol = P_ARP; break; case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break; default: printf ( "Unknown packet type %hx\n", t ); return; } /* Store packet length in TBD */ undi.xmit_data->tbd.ImmedLength = s; /* 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 ) { undi.xmit_data->tbd.Xmit.segment = SEGMENT( p ); undi.xmit_data->tbd.Xmit.offset = OFFSET( p ); } else { memcpy ( undi.xmit_buffer, p, s ); undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer ); undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer ); } eb_pxenv_undi_transmit_packet();}/**************************************************************************DISABLE - Turn off ethernet interface***************************************************************************/static void undi_disable(struct dev *dev){ /* Inhibit compiler warning about unused parameter dev */ if ( dev == NULL ) {}; undi_full_shutdown(); free_base_mem_data();}/**************************************************************************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. */int hunt_pixies_and_undi_roms ( void ) { static uint8_t hunt_type = HUNT_FOR_PIXIES; if ( hunt_type == HUNT_FOR_PIXIES ) { if ( hunt_pixie() ) { return 1; } } hunt_type = HUNT_FOR_UNDI_ROMS; while ( hunt_undi_rom() ) { if ( undi_loader() ) { return 1; } undi_full_shutdown(); /* Free any allocated memory */ } hunt_type = HUNT_FOR_PIXIES; return 0;}/* The actual Etherboot probe routine. */static int undi_probe(struct dev *dev, struct pci_device *pci){ struct nic *nic = (struct nic *)dev; /* Zero out global undi structure */ memset ( &undi, 0, sizeof(undi) ); /* Store PCI parameters; we will need them to initialize the UNDI * driver later. */ memcpy ( &undi.pci, pci, sizeof(undi.pci) ); /* Find the BIOS' $PnP structure */ if ( ! hunt_pnp_bios() ) { printf ( "No PnP BIOS found; aborting\n" ); return 0; } /* 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() ) { if ( undi.pxs->Status == PXENV_STATUS_UNDI_MEDIATEST_FAILED ) { printf ( "Cable not connected (code %#hx)\n", PXENV_STATUS_UNDI_MEDIATEST_FAILED ); } 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", undi.pxs->undi_get_information.BaseIo, undi.pxs->undi_get_information.IntNumber, undi.pxs->undi_get_information.CurrentNodeAddress ); /* Fill out MAC address in nic structure */ memcpy ( nic->node_addr, undi.pxs->undi_get_information.CurrentNodeAddress, 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", undi.pxs->undi_get_iface_info.IfaceType, undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 ); dev->disable = undi_disable; nic->poll = undi_poll; nic->transmit = undi_transmit; return 1; } undi_disable ( dev ); /* To free base memory structures */ return 0;}/* 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. */static struct pci_id undi_nics[] = { /* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */};static struct pci_driver undi_driver __pci_driver = { .type = NIC_DRIVER, .name = "UNDI", .probe = undi_probe, .ids = undi_nics, .id_count = sizeof(undi_nics)/sizeof(undi_nics[0]), .class = PCI_CLASS_NETWORK_ETHERNET,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -