2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <basemem_packet.h>
27 #include <gpxe/iobuf.h>
28 #include <gpxe/netdevice.h>
29 #include <gpxe/if_ether.h>
30 #include <gpxe/ethernet.h>
37 * UNDI network device driver
43 /** Assigned IRQ number */
45 /** Currently processing ISR */
47 /** Bug workarounds */
52 * @defgroup undi_hacks UNDI workarounds
56 /** Work around Etherboot 5.4 bugs */
57 #define UNDI_HACK_EB54 0x0001
61 static void undinet_close ( struct net_device *netdev );
63 /*****************************************************************************
67 *****************************************************************************
73 * @v function API call number
74 * @ret name API call name
76 static inline __attribute__ (( always_inline )) const char *
77 undinet_function_name ( unsigned int function ) {
79 case PXENV_START_UNDI:
80 return "PXENV_START_UNDI";
82 return "PXENV_STOP_UNDI";
83 case PXENV_UNDI_STARTUP:
84 return "PXENV_UNDI_STARTUP";
85 case PXENV_UNDI_CLEANUP:
86 return "PXENV_UNDI_CLEANUP";
87 case PXENV_UNDI_INITIALIZE:
88 return "PXENV_UNDI_INITIALIZE";
89 case PXENV_UNDI_RESET_ADAPTER:
90 return "PXENV_UNDI_RESET_ADAPTER";
91 case PXENV_UNDI_SHUTDOWN:
92 return "PXENV_UNDI_SHUTDOWN";
94 return "PXENV_UNDI_OPEN";
95 case PXENV_UNDI_CLOSE:
96 return "PXENV_UNDI_CLOSE";
97 case PXENV_UNDI_TRANSMIT:
98 return "PXENV_UNDI_TRANSMIT";
99 case PXENV_UNDI_SET_MCAST_ADDRESS:
100 return "PXENV_UNDI_SET_MCAST_ADDRESS";
101 case PXENV_UNDI_SET_STATION_ADDRESS:
102 return "PXENV_UNDI_SET_STATION_ADDRESS";
103 case PXENV_UNDI_SET_PACKET_FILTER:
104 return "PXENV_UNDI_SET_PACKET_FILTER";
105 case PXENV_UNDI_GET_INFORMATION:
106 return "PXENV_UNDI_GET_INFORMATION";
107 case PXENV_UNDI_GET_STATISTICS:
108 return "PXENV_UNDI_GET_STATISTICS";
109 case PXENV_UNDI_CLEAR_STATISTICS:
110 return "PXENV_UNDI_CLEAR_STATISTICS";
111 case PXENV_UNDI_INITIATE_DIAGS:
112 return "PXENV_UNDI_INITIATE_DIAGS";
113 case PXENV_UNDI_FORCE_INTERRUPT:
114 return "PXENV_UNDI_FORCE_INTERRUPT";
115 case PXENV_UNDI_GET_MCAST_ADDRESS:
116 return "PXENV_UNDI_GET_MCAST_ADDRESS";
117 case PXENV_UNDI_GET_NIC_TYPE:
118 return "PXENV_UNDI_GET_NIC_TYPE";
119 case PXENV_UNDI_GET_IFACE_INFO:
120 return "PXENV_UNDI_GET_IFACE_INFO";
122 * Duplicate case value; this is a bug in the PXE specification.
124 * case PXENV_UNDI_GET_STATE:
125 * return "PXENV_UNDI_GET_STATE";
128 return "PXENV_UNDI_ISR";
130 return "UNKNOWN API CALL";
135 * UNDI parameter block
137 * Used as the paramter block for all UNDI API calls. Resides in base
140 static union u_PXENV_ANY __bss16 ( undinet_params );
141 #define undinet_params __use_data16 ( undinet_params )
145 * Used as the indirection vector for all UNDI API calls. Resides in
148 SEGOFF16_t __bss16 ( undinet_entry_point );
149 #define undinet_entry_point __use_data16 ( undinet_entry_point )
152 * Issue UNDI API call
154 * @v undinic UNDI NIC
155 * @v function API call number
156 * @v params UNDI parameter block
157 * @v params_len Length of UNDI parameter block
158 * @ret rc Return status code
160 static int undinet_call ( struct undi_nic *undinic, unsigned int function,
161 void *params, size_t params_len ) {
163 int discard_b, discard_D;
166 /* Copy parameter block and entry point */
167 assert ( params_len <= sizeof ( undinet_params ) );
168 memcpy ( &undinet_params, params, params_len );
170 /* Call real-mode entry point. This calling convention will
171 * work with both the !PXE and the PXENV+ entry points.
173 __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
177 "addw $6, %%sp\n\t" )
178 : "=a" ( exit ), "=b" ( discard_b ),
180 : "p" ( __from_data16 ( &undinet_entry_point )),
182 "D" ( __from_data16 ( &undinet_params ) )
183 : "ecx", "edx", "esi", "ebp" );
185 /* UNDI API calls may rudely change the status of A20 and not
186 * bother to restore it afterwards. Intel is known to be
189 * Note that we will return to this point even if A20 gets
190 * screwed up by the UNDI driver, because Etherboot always
191 * resides in an even megabyte of RAM.
195 /* Determine return status code based on PXENV_EXIT and
198 if ( exit == PXENV_EXIT_SUCCESS ) {
201 rc = -undinet_params.Status;
202 /* Paranoia; don't return success for the combination
203 * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS
209 /* If anything goes wrong, print as much debug information as
210 * it's possible to give.
213 SEGOFF16_t rm_params = {
215 .offset = __from_data16 ( &undinet_params ),
218 DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
219 undinet_function_name ( function ), strerror ( rc ) );
220 DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
221 "%#02zx, entry point at %04x:%04x\n", undinic,
222 rm_params.segment, rm_params.offset, params_len,
223 undinet_entry_point.segment,
224 undinet_entry_point.offset );
225 DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
226 DBGC_HDA ( undinic, rm_params, params, params_len );
227 DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
228 DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
231 /* Copy parameter block back */
232 memcpy ( params, &undinet_params, params_len );
237 /*****************************************************************************
239 * UNDI interrupt service routine
241 *****************************************************************************
245 * UNDI interrupt service routine
247 * The UNDI ISR increments a counter (@c trigger_count) and exits.
249 extern void undiisr ( void );
252 uint8_t __data16 ( undiisr_irq );
253 #define undiisr_irq __use_data16 ( undiisr_irq )
255 /** IRQ chain vector */
256 struct segoff __data16 ( undiisr_next_handler );
257 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
259 /** IRQ trigger count */
260 volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
261 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
263 /** Last observed trigger count */
264 static unsigned int last_trigger_count = 0;
267 * Hook UNDI interrupt service routine
271 static void undinet_hook_isr ( unsigned int irq ) {
273 assert ( irq <= IRQ_MAX );
274 assert ( undiisr_irq == 0 );
277 hook_bios_interrupt ( IRQ_INT ( irq ),
278 ( ( unsigned int ) undiisr ),
279 &undiisr_next_handler );
283 * Unhook UNDI interrupt service routine
287 static void undinet_unhook_isr ( unsigned int irq ) {
289 assert ( irq <= IRQ_MAX );
291 unhook_bios_interrupt ( IRQ_INT ( irq ),
292 ( ( unsigned int ) undiisr ),
293 &undiisr_next_handler );
298 * Test to see if UNDI ISR has been triggered
300 * @ret triggered ISR has been triggered since last check
302 static int undinet_isr_triggered ( void ) {
303 unsigned int this_trigger_count;
305 /* Read trigger_count. Do this only once; it is volatile */
306 this_trigger_count = undiisr_trigger_count;
308 if ( this_trigger_count == last_trigger_count ) {
313 last_trigger_count = this_trigger_count;
318 /*****************************************************************************
320 * UNDI network device interface
322 *****************************************************************************
325 /** UNDI transmit buffer descriptor */
326 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
327 #define undinet_tbd __use_data16 ( undinet_tbd )
332 * @v netdev Network device
333 * @v iobuf I/O buffer
334 * @ret rc Return status code
336 static int undinet_transmit ( struct net_device *netdev,
337 struct io_buffer *iobuf ) {
338 struct undi_nic *undinic = netdev->priv;
339 struct s_PXENV_UNDI_TRANSMIT undi_transmit;
340 size_t len = iob_len ( iobuf );
343 /* Technically, we ought to make sure that the previous
344 * transmission has completed before we re-use the buffer.
345 * However, many PXE stacks (including at least some Intel PXE
346 * stacks and Etherboot 5.4) fail to generate TX completions.
347 * In practice this won't be a problem, since our TX datapath
348 * has a very low packet volume and we can get away with
349 * assuming that a TX will be complete by the time we want to
350 * transmit the next packet.
353 /* Copy packet to UNDI I/O buffer */
354 if ( len > sizeof ( basemem_packet ) )
355 len = sizeof ( basemem_packet );
356 memcpy ( &basemem_packet, iobuf->data, len );
358 /* Create PXENV_UNDI_TRANSMIT data structure */
359 memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
360 undi_transmit.DestAddr.segment = rm_ds;
361 undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd );
362 undi_transmit.TBD.segment = rm_ds;
363 undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
365 /* Create PXENV_UNDI_TBD data structure */
366 undinet_tbd.ImmedLength = len;
367 undinet_tbd.Xmit.segment = rm_ds;
368 undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
370 /* Issue PXE API call */
371 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT,
373 sizeof ( undi_transmit ) ) ) != 0 )
376 /* Free I/O buffer */
377 netdev_tx_complete ( netdev, iobuf );
384 * Poll for received packets
386 * @v netdev Network device
388 * Fun, fun, fun. UNDI drivers don't use polling; they use
389 * interrupts. We therefore cheat and pretend that an interrupt has
390 * occurred every time undinet_poll() is called. This isn't too much
391 * of a hack; PCI devices share IRQs and so the first thing that a
392 * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
393 * not the UNDI NIC generated the interrupt; there is no harm done by
394 * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
395 * handling them any more rapidly than the usual rate of
396 * undinet_poll() being called even if we did implement a full ISR.
397 * So it should work. Ha!
399 * Addendum (21/10/03). Some cards don't play nicely with this trick,
400 * so instead of doing it the easy way we have to go to all the hassle
401 * of installing a genuine interrupt service routine and dealing with
402 * the wonderful 8259 Programmable Interrupt Controller. Joy.
404 * Addendum (10/07/07). When doing things such as iSCSI boot, in
405 * which we have to co-operate with a running OS, we can't get away
406 * with the "ISR-just-increments-a-counter-and-returns" trick at all,
407 * because it involves tying up the PIC for far too long, and other
408 * interrupt-dependent components (e.g. local disks) start breaking.
409 * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
410 * from within interrupt context in order to deassert the device
411 * interrupt, and sends EOI if applicable.
413 static void undinet_poll ( struct net_device *netdev ) {
414 struct undi_nic *undinic = netdev->priv;
415 struct s_PXENV_UNDI_ISR undi_isr;
416 struct io_buffer *iobuf = NULL;
422 if ( ! undinic->isr_processing ) {
423 /* Do nothing unless ISR has been triggered */
424 if ( ! undinet_isr_triggered() ) {
425 /* Allow interrupt to occur */
426 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
433 /* Start ISR processing */
434 undinic->isr_processing = 1;
435 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
437 /* Continue ISR processing */
438 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
441 /* Run through the ISR loop */
443 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
444 sizeof ( undi_isr ) ) ) != 0 )
446 switch ( undi_isr.FuncFlag ) {
447 case PXENV_UNDI_ISR_OUT_TRANSMIT:
448 /* We don't care about transmit completions */
450 case PXENV_UNDI_ISR_OUT_RECEIVE:
451 /* Packet fragment received */
452 len = undi_isr.FrameLength;
453 frag_len = undi_isr.BufferLength;
454 if ( ( len == 0 ) || ( len < frag_len ) ) {
455 /* Don't laugh. VMWare does it. */
456 DBGC ( undinic, "UNDINIC %p reported insane "
457 "fragment (%zd of %zd bytes)\n",
458 undinic, frag_len, len );
459 netdev_rx_err ( netdev, NULL, -EINVAL );
463 iobuf = alloc_iob ( len );
465 DBGC ( undinic, "UNDINIC %p could not "
466 "allocate %zd bytes for RX buffer\n",
468 /* Fragment will be dropped */
469 netdev_rx_err ( netdev, NULL, -ENOMEM );
472 max_frag_len = iob_tailroom ( iobuf );
473 if ( frag_len > max_frag_len ) {
474 DBGC ( undinic, "UNDINIC %p fragment too big "
475 "(%zd+%zd does not fit into %zd)\n",
476 undinic, iob_len ( iobuf ), frag_len,
477 ( iob_len ( iobuf ) + max_frag_len ) );
478 frag_len = max_frag_len;
480 copy_from_real ( iob_put ( iobuf, frag_len ),
481 undi_isr.Frame.segment,
482 undi_isr.Frame.offset, frag_len );
483 if ( iob_len ( iobuf ) == len ) {
484 /* Whole packet received; deliver it */
485 netdev_rx ( netdev, iob_disown ( iobuf ) );
486 /* Etherboot 5.4 fails to return all packets
487 * under mild load; pretend it retriggered.
489 if ( undinic->hacks & UNDI_HACK_EB54 )
490 --last_trigger_count;
493 case PXENV_UNDI_ISR_OUT_DONE:
494 /* Processing complete */
495 undinic->isr_processing = 0;
498 /* Should never happen. VMWare does it routinely. */
499 DBGC ( undinic, "UNDINIC %p ISR returned invalid "
500 "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
501 undinic->isr_processing = 0;
504 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
509 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
510 "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
511 ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
512 netdev_rx_err ( netdev, iobuf, -EINVAL );
519 * @v netdev Net device
520 * @ret rc Return status code
522 static int undinet_open ( struct net_device *netdev ) {
523 struct undi_nic *undinic = netdev->priv;
524 struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
525 struct s_PXENV_UNDI_OPEN undi_open;
528 /* Hook interrupt service routine and enable interrupt */
529 undinet_hook_isr ( undinic->irq );
530 enable_irq ( undinic->irq );
531 send_eoi ( undinic->irq );
533 /* Set station address. Required for some PXE stacks; will
534 * spuriously fail on others. Ignore failures. We only ever
535 * use it to set the MAC address to the card's permanent value
538 memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
539 sizeof ( undi_set_address.StationAddress ) );
540 undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
541 &undi_set_address, sizeof ( undi_set_address ) );
543 /* Open NIC. We ask for promiscuous operation, since it's the
544 * only way to ask for all multicast addresses. On any
545 * switched network, it shouldn't really make a difference to
548 memset ( &undi_open, 0, sizeof ( undi_open ) );
549 undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
550 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
551 sizeof ( undi_open ) ) ) != 0 )
554 DBGC ( undinic, "UNDINIC %p opened\n", undinic );
558 undinet_close ( netdev );
565 * @v netdev Net device
567 static void undinet_close ( struct net_device *netdev ) {
568 struct undi_nic *undinic = netdev->priv;
569 struct s_PXENV_UNDI_ISR undi_isr;
570 struct s_PXENV_UNDI_CLOSE undi_close;
573 /* Ensure ISR has exited cleanly */
574 while ( undinic->isr_processing ) {
575 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
576 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
577 sizeof ( undi_isr ) ) ) != 0 )
579 switch ( undi_isr.FuncFlag ) {
580 case PXENV_UNDI_ISR_OUT_TRANSMIT:
581 case PXENV_UNDI_ISR_OUT_RECEIVE:
582 /* Continue draining */
585 /* Stop processing */
586 undinic->isr_processing = 0;
592 undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
593 sizeof ( undi_close ) );
595 /* Disable interrupt and unhook ISR */
596 disable_irq ( undinic->irq );
597 undinet_unhook_isr ( undinic->irq );
599 DBGC ( undinic, "UNDINIC %p closed\n", undinic );
603 * Enable/disable interrupts
605 * @v netdev Net device
606 * @v enable Interrupts should be enabled
608 static void undinet_irq ( struct net_device *netdev, int enable ) {
609 struct undi_nic *undinic = netdev->priv;
611 /* Cannot support interrupts yet */
612 DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
613 undinic, ( enable ? "enable" : "disable" ) );
616 /** UNDI network device operations */
617 static struct net_device_operations undinet_operations = {
618 .open = undinet_open,
619 .close = undinet_close,
620 .transmit = undinet_transmit,
621 .poll = undinet_poll,
628 * @v undi UNDI device
629 * @ret rc Return status code
631 int undinet_probe ( struct undi_device *undi ) {
632 struct net_device *netdev;
633 struct undi_nic *undinic;
634 struct s_PXENV_START_UNDI start_undi;
635 struct s_PXENV_UNDI_STARTUP undi_startup;
636 struct s_PXENV_UNDI_INITIALIZE undi_initialize;
637 struct s_PXENV_UNDI_GET_INFORMATION undi_info;
638 struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
639 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
640 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
641 struct s_PXENV_STOP_UNDI stop_undi;
644 /* Allocate net device */
645 netdev = alloc_etherdev ( sizeof ( *undinic ) );
648 netdev_init ( netdev, &undinet_operations );
649 undinic = netdev->priv;
650 undi_set_drvdata ( undi, netdev );
651 netdev->dev = &undi->dev;
652 memset ( undinic, 0, sizeof ( *undinic ) );
653 undinet_entry_point = undi->entry;
654 DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
656 /* Hook in UNDI stack */
657 if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
658 memset ( &start_undi, 0, sizeof ( start_undi ) );
659 start_undi.AX = undi->pci_busdevfn;
660 start_undi.BX = undi->isapnp_csn;
661 start_undi.DX = undi->isapnp_read_port;
662 start_undi.ES = BIOS_SEG;
663 start_undi.DI = find_pnp_bios();
664 if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
666 sizeof ( start_undi ) ) ) != 0 )
669 undi->flags |= UNDI_FL_STARTED;
671 /* Bring up UNDI stack */
672 if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
673 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
674 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
676 sizeof ( undi_startup ) ) ) != 0 )
677 goto err_undi_startup;
678 memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
679 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
681 sizeof ( undi_initialize ))) != 0 )
682 goto err_undi_initialize;
684 undi->flags |= UNDI_FL_INITIALIZED;
686 /* Get device information */
687 memset ( &undi_info, 0, sizeof ( undi_info ) );
688 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
689 &undi_info, sizeof ( undi_info ) ) ) != 0 )
690 goto err_undi_get_information;
691 memcpy ( netdev->ll_addr, undi_info.PermNodeAddress, ETH_ALEN );
692 undinic->irq = undi_info.IntNumber;
693 if ( undinic->irq > IRQ_MAX ) {
694 DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
695 undinic, undinic->irq );
698 DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
699 undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq );
701 /* Get interface information */
702 memset ( &undi_iface, 0, sizeof ( undi_iface ) );
703 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
705 sizeof ( undi_iface ) ) ) != 0 )
706 goto err_undi_get_iface_info;
707 DBGC ( undinic, "UNDINIC %p has type %s and link speed %d\n",
708 undinic, undi_iface.IfaceType, undi_iface.LinkSpeed );
709 if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
710 sizeof ( undi_iface.IfaceType ) ) == 0 ) {
711 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
713 undinic->hacks |= UNDI_HACK_EB54;
716 /* Mark as link up; we don't handle link state */
717 netdev_link_up ( netdev );
719 /* Register network device */
720 if ( ( rc = register_netdev ( netdev ) ) != 0 )
723 DBGC ( undinic, "UNDINIC %p added\n", undinic );
727 err_undi_get_iface_info:
729 err_undi_get_information:
731 /* Shut down UNDI stack */
732 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
733 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
734 sizeof ( undi_shutdown ) );
735 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
736 undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
737 sizeof ( undi_cleanup ) );
738 undi->flags &= ~UNDI_FL_INITIALIZED;
740 /* Unhook UNDI stack */
741 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
742 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
743 sizeof ( stop_undi ) );
744 undi->flags &= ~UNDI_FL_STARTED;
746 netdev_nullify ( netdev );
747 netdev_put ( netdev );
748 undi_set_drvdata ( undi, NULL );
755 * @v undi UNDI device
757 void undinet_remove ( struct undi_device *undi ) {
758 struct net_device *netdev = undi_get_drvdata ( undi );
759 struct undi_nic *undinic = netdev->priv;
760 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
761 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
762 struct s_PXENV_STOP_UNDI stop_undi;
764 /* Unregister net device */
765 unregister_netdev ( netdev );
767 /* If we are preparing for an OS boot, or if we cannot exit
768 * via the PXE stack, then shut down the PXE stack.
770 if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
772 /* Shut down UNDI stack */
773 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
774 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
775 sizeof ( undi_shutdown ) );
776 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
777 undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
778 sizeof ( undi_cleanup ) );
779 undi->flags &= ~UNDI_FL_INITIALIZED;
781 /* Unhook UNDI stack */
782 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
783 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
784 sizeof ( stop_undi ) );
785 undi->flags &= ~UNDI_FL_STARTED;
788 /* Clear entry point */
789 memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
791 /* Free network device */
792 netdev_nullify ( netdev );
793 netdev_put ( netdev );
795 DBGC ( undinic, "UNDINIC %p removed\n", undinic );