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>
26 #include <gpxe/iobuf.h>
27 #include <gpxe/netdevice.h>
28 #include <gpxe/if_ether.h>
29 #include <gpxe/ethernet.h>
36 * UNDI network device driver
42 /** Assigned IRQ number */
44 /** Currently processing ISR */
46 /** Bug workarounds */
51 * @defgroup undi_hacks UNDI workarounds
55 /** Work around Etherboot 5.4 bugs */
56 #define UNDI_HACK_EB54 0x0001
60 static void undinet_close ( struct net_device *netdev );
62 /*****************************************************************************
66 *****************************************************************************
72 * @v function API call number
73 * @ret name API call name
75 static inline __attribute__ (( always_inline )) const char *
76 undinet_function_name ( unsigned int function ) {
78 case PXENV_START_UNDI:
79 return "PXENV_START_UNDI";
81 return "PXENV_STOP_UNDI";
82 case PXENV_UNDI_STARTUP:
83 return "PXENV_UNDI_STARTUP";
84 case PXENV_UNDI_CLEANUP:
85 return "PXENV_UNDI_CLEANUP";
86 case PXENV_UNDI_INITIALIZE:
87 return "PXENV_UNDI_INITIALIZE";
88 case PXENV_UNDI_RESET_ADAPTER:
89 return "PXENV_UNDI_RESET_ADAPTER";
90 case PXENV_UNDI_SHUTDOWN:
91 return "PXENV_UNDI_SHUTDOWN";
93 return "PXENV_UNDI_OPEN";
94 case PXENV_UNDI_CLOSE:
95 return "PXENV_UNDI_CLOSE";
96 case PXENV_UNDI_TRANSMIT:
97 return "PXENV_UNDI_TRANSMIT";
98 case PXENV_UNDI_SET_MCAST_ADDRESS:
99 return "PXENV_UNDI_SET_MCAST_ADDRESS";
100 case PXENV_UNDI_SET_STATION_ADDRESS:
101 return "PXENV_UNDI_SET_STATION_ADDRESS";
102 case PXENV_UNDI_SET_PACKET_FILTER:
103 return "PXENV_UNDI_SET_PACKET_FILTER";
104 case PXENV_UNDI_GET_INFORMATION:
105 return "PXENV_UNDI_GET_INFORMATION";
106 case PXENV_UNDI_GET_STATISTICS:
107 return "PXENV_UNDI_GET_STATISTICS";
108 case PXENV_UNDI_CLEAR_STATISTICS:
109 return "PXENV_UNDI_CLEAR_STATISTICS";
110 case PXENV_UNDI_INITIATE_DIAGS:
111 return "PXENV_UNDI_INITIATE_DIAGS";
112 case PXENV_UNDI_FORCE_INTERRUPT:
113 return "PXENV_UNDI_FORCE_INTERRUPT";
114 case PXENV_UNDI_GET_MCAST_ADDRESS:
115 return "PXENV_UNDI_GET_MCAST_ADDRESS";
116 case PXENV_UNDI_GET_NIC_TYPE:
117 return "PXENV_UNDI_GET_NIC_TYPE";
118 case PXENV_UNDI_GET_IFACE_INFO:
119 return "PXENV_UNDI_GET_IFACE_INFO";
121 * Duplicate case value; this is a bug in the PXE specification.
123 * case PXENV_UNDI_GET_STATE:
124 * return "PXENV_UNDI_GET_STATE";
127 return "PXENV_UNDI_ISR";
129 return "UNKNOWN API CALL";
134 * UNDI parameter block
136 * Used as the paramter block for all UNDI API calls. Resides in base
139 static union u_PXENV_ANY __bss16 ( undinet_params );
140 #define undinet_params __use_data16 ( undinet_params )
144 * Used as the indirection vector for all UNDI API calls. Resides in
147 SEGOFF16_t __bss16 ( undinet_entry_point );
148 #define undinet_entry_point __use_data16 ( undinet_entry_point )
151 * Issue UNDI API call
153 * @v undinic UNDI NIC
154 * @v function API call number
155 * @v params UNDI parameter block
156 * @v params_len Length of UNDI parameter block
157 * @ret rc Return status code
159 static int undinet_call ( struct undi_nic *undinic, unsigned int function,
160 void *params, size_t params_len ) {
162 int discard_b, discard_D;
165 /* Copy parameter block and entry point */
166 assert ( params_len <= sizeof ( undinet_params ) );
167 memcpy ( &undinet_params, params, params_len );
169 /* Call real-mode entry point. This calling convention will
170 * work with both the !PXE and the PXENV+ entry points.
172 __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
176 "addw $6, %%sp\n\t" )
177 : "=a" ( exit ), "=b" ( discard_b ),
179 : "p" ( &__from_data16 ( undinet_entry_point )),
181 "D" ( &__from_data16 ( undinet_params ) )
182 : "ecx", "edx", "esi", "ebp" );
184 /* UNDI API calls may rudely change the status of A20 and not
185 * bother to restore it afterwards. Intel is known to be
188 * Note that we will return to this point even if A20 gets
189 * screwed up by the UNDI driver, because Etherboot always
190 * resides in an even megabyte of RAM.
194 /* Determine return status code based on PXENV_EXIT and
197 if ( exit == PXENV_EXIT_SUCCESS ) {
200 rc = -undinet_params.Status;
201 /* Paranoia; don't return success for the combination
202 * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS
208 /* If anything goes wrong, print as much debug information as
209 * it's possible to give.
212 SEGOFF16_t rm_params = {
214 .offset = (intptr_t) &__from_data16 ( undinet_params ),
217 DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
218 undinet_function_name ( function ), strerror ( rc ) );
219 DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
220 "%#02x, entry point at %04x:%04x\n", undinic,
221 rm_params.segment, rm_params.offset, params_len,
222 undinet_entry_point.segment,
223 undinet_entry_point.offset );
224 DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
225 DBGC_HDA ( undinic, rm_params, params, params_len );
226 DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
227 DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
230 /* Copy parameter block back */
231 memcpy ( params, &undinet_params, params_len );
236 /*****************************************************************************
238 * UNDI interrupt service routine
240 *****************************************************************************
244 * UNDI interrupt service routine
246 * The UNDI ISR increments a counter (@c trigger_count) and exits.
248 extern void undiisr ( void );
251 uint8_t __data16 ( undiisr_irq );
252 #define undiisr_irq __use_data16 ( undiisr_irq )
254 /** IRQ chain vector */
255 struct segoff __data16 ( undiisr_next_handler );
256 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
258 /** IRQ trigger count */
259 volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
260 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
262 /** Last observed trigger count */
263 static unsigned int last_trigger_count = 0;
266 * Hook UNDI interrupt service routine
270 static void undinet_hook_isr ( unsigned int irq ) {
272 assert ( irq <= IRQ_MAX );
273 assert ( undiisr_irq == 0 );
276 hook_bios_interrupt ( IRQ_INT ( irq ),
277 ( ( unsigned int ) undiisr ),
278 &undiisr_next_handler );
282 * Unhook UNDI interrupt service routine
286 static void undinet_unhook_isr ( unsigned int irq ) {
288 assert ( irq <= IRQ_MAX );
290 unhook_bios_interrupt ( IRQ_INT ( irq ),
291 ( ( unsigned int ) undiisr ),
292 &undiisr_next_handler );
297 * Test to see if UNDI ISR has been triggered
299 * @ret triggered ISR has been triggered since last check
301 static int undinet_isr_triggered ( void ) {
302 unsigned int this_trigger_count;
304 /* Read trigger_count. Do this only once; it is volatile */
305 this_trigger_count = undiisr_trigger_count;
307 if ( this_trigger_count == last_trigger_count ) {
312 last_trigger_count = this_trigger_count;
317 /*****************************************************************************
319 * UNDI network device interface
321 *****************************************************************************
324 /** UNDI transmit buffer descriptor */
325 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
326 #define undinet_tbd __use_data16 ( undinet_tbd )
331 * @v netdev Network device
332 * @v iobuf I/O buffer
333 * @ret rc Return status code
335 static int undinet_transmit ( struct net_device *netdev,
336 struct io_buffer *iobuf ) {
337 struct undi_nic *undinic = netdev->priv;
338 struct s_PXENV_UNDI_TRANSMIT undi_transmit;
339 size_t len = iob_len ( iobuf );
342 /* Technically, we ought to make sure that the previous
343 * transmission has completed before we re-use the buffer.
344 * However, many PXE stacks (including at least some Intel PXE
345 * stacks and Etherboot 5.4) fail to generate TX completions.
346 * In practice this won't be a problem, since our TX datapath
347 * has a very low packet volume and we can get away with
348 * assuming that a TX will be complete by the time we want to
349 * transmit the next packet.
352 /* Copy packet to UNDI I/O buffer */
353 if ( len > sizeof ( basemem_packet ) )
354 len = sizeof ( basemem_packet );
355 memcpy ( &basemem_packet, iobuf->data, len );
357 /* Create PXENV_UNDI_TRANSMIT data structure */
358 memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
359 undi_transmit.DestAddr.segment = rm_ds;
360 undi_transmit.DestAddr.offset
361 = ( ( unsigned ) & __from_data16 ( undinet_tbd ) );
362 undi_transmit.TBD.segment = rm_ds;
363 undi_transmit.TBD.offset
364 = ( ( unsigned ) & __from_data16 ( undinet_tbd ) );
366 /* Create PXENV_UNDI_TBD data structure */
367 undinet_tbd.ImmedLength = len;
368 undinet_tbd.Xmit.segment = rm_ds;
369 undinet_tbd.Xmit.offset
370 = ( ( unsigned ) & __from_data16 ( basemem_packet ) );
372 /* Issue PXE API call */
373 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT,
375 sizeof ( undi_transmit ) ) ) != 0 )
378 /* Free I/O buffer */
379 netdev_tx_complete ( netdev, iobuf );
386 * Poll for received packets
388 * @v netdev Network device
390 * Fun, fun, fun. UNDI drivers don't use polling; they use
391 * interrupts. We therefore cheat and pretend that an interrupt has
392 * occurred every time undinet_poll() is called. This isn't too much
393 * of a hack; PCI devices share IRQs and so the first thing that a
394 * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
395 * not the UNDI NIC generated the interrupt; there is no harm done by
396 * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
397 * handling them any more rapidly than the usual rate of
398 * undinet_poll() being called even if we did implement a full ISR.
399 * So it should work. Ha!
401 * Addendum (21/10/03). Some cards don't play nicely with this trick,
402 * so instead of doing it the easy way we have to go to all the hassle
403 * of installing a genuine interrupt service routine and dealing with
404 * the wonderful 8259 Programmable Interrupt Controller. Joy.
406 * Addendum (10/07/07). When doing things such as iSCSI boot, in
407 * which we have to co-operate with a running OS, we can't get away
408 * with the "ISR-just-increments-a-counter-and-returns" trick at all,
409 * because it involves tying up the PIC for far too long, and other
410 * interrupt-dependent components (e.g. local disks) start breaking.
411 * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
412 * from within interrupt context in order to deassert the device
413 * interrupt, and sends EOI if applicable.
415 static void undinet_poll ( struct net_device *netdev ) {
416 struct undi_nic *undinic = netdev->priv;
417 struct s_PXENV_UNDI_ISR undi_isr;
418 struct io_buffer *iobuf = NULL;
424 if ( ! undinic->isr_processing ) {
425 /* Do nothing unless ISR has been triggered */
426 if ( ! undinet_isr_triggered() ) {
427 /* Allow interrupt to occur */
428 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
435 /* Start ISR processing */
436 undinic->isr_processing = 1;
437 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
439 /* Continue ISR processing */
440 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
443 /* Run through the ISR loop */
445 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
446 sizeof ( undi_isr ) ) ) != 0 )
448 switch ( undi_isr.FuncFlag ) {
449 case PXENV_UNDI_ISR_OUT_TRANSMIT:
450 /* We don't care about transmit completions */
452 case PXENV_UNDI_ISR_OUT_RECEIVE:
453 /* Packet fragment received */
454 len = undi_isr.FrameLength;
455 frag_len = undi_isr.BufferLength;
456 if ( ( len == 0 ) || ( len < frag_len ) ) {
457 /* Don't laugh. VMWare does it. */
458 DBGC ( undinic, "UNDINIC %p reported insane "
459 "fragment (%zd of %zd bytes)\n",
460 undinic, frag_len, len );
461 netdev_rx_err ( netdev, NULL, -EINVAL );
465 iobuf = alloc_iob ( len );
467 DBGC ( undinic, "UNDINIC %p could not "
468 "allocate %zd bytes for RX buffer\n",
470 /* Fragment will be dropped */
471 netdev_rx_err ( netdev, NULL, -ENOMEM );
474 max_frag_len = iob_tailroom ( iobuf );
475 if ( frag_len > max_frag_len ) {
476 DBGC ( undinic, "UNDINIC %p fragment too big "
477 "(%zd+%zd does not fit into %zd)\n",
478 undinic, iob_len ( iobuf ), frag_len,
479 ( iob_len ( iobuf ) + max_frag_len ) );
480 frag_len = max_frag_len;
482 copy_from_real ( iob_put ( iobuf, frag_len ),
483 undi_isr.Frame.segment,
484 undi_isr.Frame.offset, frag_len );
485 if ( iob_len ( iobuf ) == len ) {
486 /* Whole packet received; deliver it */
487 netdev_rx ( netdev, iobuf );
489 /* Etherboot 5.4 fails to return all packets
490 * under mild load; pretend it retriggered.
492 if ( undinic->hacks & UNDI_HACK_EB54 )
493 --last_trigger_count;
496 case PXENV_UNDI_ISR_OUT_DONE:
497 /* Processing complete */
498 undinic->isr_processing = 0;
501 /* Should never happen. VMWare does it routinely. */
502 DBGC ( undinic, "UNDINIC %p ISR returned invalid "
503 "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
504 undinic->isr_processing = 0;
507 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
512 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
513 "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
514 ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
515 netdev_rx_err ( netdev, iobuf, -EINVAL );
522 * @v netdev Net device
523 * @ret rc Return status code
525 static int undinet_open ( struct net_device *netdev ) {
526 struct undi_nic *undinic = netdev->priv;
527 struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
528 struct s_PXENV_UNDI_OPEN undi_open;
531 /* Hook interrupt service routine and enable interrupt */
532 undinet_hook_isr ( undinic->irq );
533 enable_irq ( undinic->irq );
534 send_eoi ( undinic->irq );
536 /* Set station address. Required for some PXE stacks; will
537 * spuriously fail on others. Ignore failures. We only ever
538 * use it to set the MAC address to the card's permanent value
541 memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
542 sizeof ( undi_set_address.StationAddress ) );
543 undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
544 &undi_set_address, sizeof ( undi_set_address ) );
547 memset ( &undi_open, 0, sizeof ( undi_open ) );
548 undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
549 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
550 sizeof ( undi_open ) ) ) != 0 )
553 DBGC ( undinic, "UNDINIC %p opened\n", undinic );
557 undinet_close ( netdev );
564 * @v netdev Net device
566 static void undinet_close ( struct net_device *netdev ) {
567 struct undi_nic *undinic = netdev->priv;
568 struct s_PXENV_UNDI_ISR undi_isr;
569 struct s_PXENV_UNDI_CLOSE undi_close;
572 /* Ensure ISR has exited cleanly */
573 while ( undinic->isr_processing ) {
574 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
575 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
576 sizeof ( undi_isr ) ) ) != 0 )
578 switch ( undi_isr.FuncFlag ) {
579 case PXENV_UNDI_ISR_OUT_TRANSMIT:
580 case PXENV_UNDI_ISR_OUT_RECEIVE:
581 /* Continue draining */
584 /* Stop processing */
585 undinic->isr_processing = 0;
591 undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
592 sizeof ( undi_close ) );
594 /* Disable interrupt and unhook ISR */
595 disable_irq ( undinic->irq );
596 undinet_unhook_isr ( undinic->irq );
598 DBGC ( undinic, "UNDINIC %p closed\n", undinic );
602 * Enable/disable interrupts
604 * @v netdev Net device
605 * @v enable Interrupts should be enabled
607 static void undinet_irq ( struct net_device *netdev, int enable ) {
608 struct undi_nic *undinic = netdev->priv;
610 /* Cannot support interrupts yet */
611 DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
612 undinic, ( enable ? "enable" : "disable" ) );
615 /** UNDI network device operations */
616 static struct net_device_operations undinet_operations = {
617 .open = undinet_open,
618 .close = undinet_close,
619 .transmit = undinet_transmit,
620 .poll = undinet_poll,
627 * @v undi UNDI device
628 * @ret rc Return status code
630 int undinet_probe ( struct undi_device *undi ) {
631 struct net_device *netdev;
632 struct undi_nic *undinic;
633 struct s_PXENV_START_UNDI start_undi;
634 struct s_PXENV_UNDI_STARTUP undi_startup;
635 struct s_PXENV_UNDI_INITIALIZE undi_initialize;
636 struct s_PXENV_UNDI_GET_INFORMATION undi_info;
637 struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
638 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
639 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
640 struct s_PXENV_STOP_UNDI stop_undi;
643 /* Allocate net device */
644 netdev = alloc_etherdev ( sizeof ( *undinic ) );
647 netdev_init ( netdev, &undinet_operations );
648 undinic = netdev->priv;
649 undi_set_drvdata ( undi, netdev );
650 netdev->dev = &undi->dev;
651 memset ( undinic, 0, sizeof ( *undinic ) );
652 undinet_entry_point = undi->entry;
653 DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
655 /* Hook in UNDI stack */
656 if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
657 memset ( &start_undi, 0, sizeof ( start_undi ) );
658 start_undi.AX = undi->pci_busdevfn;
659 start_undi.BX = undi->isapnp_csn;
660 start_undi.DX = undi->isapnp_read_port;
661 start_undi.ES = BIOS_SEG;
662 start_undi.DI = find_pnp_bios();
663 if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
665 sizeof ( start_undi ) ) ) != 0 )
668 undi->flags |= UNDI_FL_STARTED;
670 /* Bring up UNDI stack */
671 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
672 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP, &undi_startup,
673 sizeof ( undi_startup ) ) ) != 0 )
674 goto err_undi_startup;
675 memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
676 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
678 sizeof ( undi_initialize ) ) ) != 0 )
679 goto err_undi_initialize;
681 /* Get device information */
682 memset ( &undi_info, 0, sizeof ( undi_info ) );
683 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
684 &undi_info, sizeof ( undi_info ) ) ) != 0 )
685 goto err_undi_get_information;
686 memcpy ( netdev->ll_addr, undi_info.PermNodeAddress, ETH_ALEN );
687 undinic->irq = undi_info.IntNumber;
688 if ( undinic->irq > IRQ_MAX ) {
689 DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
690 undinic, undinic->irq );
693 DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
694 undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq );
696 /* Get interface information */
697 memset ( &undi_iface, 0, sizeof ( undi_iface ) );
698 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
700 sizeof ( undi_iface ) ) ) != 0 )
701 goto err_undi_get_iface_info;
702 DBGC ( undinic, "UNDINIC %p has type %s and link speed %ld\n",
703 undinic, undi_iface.IfaceType, undi_iface.LinkSpeed );
704 if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
705 sizeof ( undi_iface.IfaceType ) ) == 0 ) {
706 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
708 undinic->hacks |= UNDI_HACK_EB54;
711 /* Register network device */
712 if ( ( rc = register_netdev ( netdev ) ) != 0 )
715 DBGC ( undinic, "UNDINIC %p added\n", undinic );
719 err_undi_get_iface_info:
721 err_undi_get_information:
723 /* Shut down UNDI stack */
724 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
725 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
726 sizeof ( undi_shutdown ) );
727 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
728 undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
729 sizeof ( undi_cleanup ) );
731 /* Unhook UNDI stack */
732 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
733 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
734 sizeof ( stop_undi ) );
736 netdev_nullify ( netdev );
737 netdev_put ( netdev );
738 undi_set_drvdata ( undi, NULL );
745 * @v undi UNDI device
747 void undinet_remove ( struct undi_device *undi ) {
748 struct net_device *netdev = undi_get_drvdata ( undi );
749 struct undi_nic *undinic = netdev->priv;
750 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
751 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
752 struct s_PXENV_STOP_UNDI stop_undi;
754 /* Unregister net device */
755 unregister_netdev ( netdev );
757 /* Shut down UNDI stack */
758 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
759 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
760 sizeof ( undi_shutdown ) );
761 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
762 undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
763 sizeof ( undi_cleanup ) );
765 /* Unhook UNDI stack */
766 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
767 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
768 sizeof ( stop_undi ) );
769 undi->flags &= ~UNDI_FL_STARTED;
771 /* Clear entry point */
772 memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
774 /* Free network device */
775 netdev_nullify ( netdev );
776 netdev_put ( netdev );
778 DBGC ( undinic, "UNDINIC %p removed\n", undinic );