When an UNDI API call fails, print everything there is to know about it.
[people/holger/gpxe.git] / src / arch / i386 / drivers / net / undinet.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <string.h>
20 #include <pxe.h>
21 #include <realmode.h>
22 #include <pic8259.h>
23 #include <biosint.h>
24 #include <pnpbios.h>
25 #include <gpxe/pkbuff.h>
26 #include <gpxe/netdevice.h>
27 #include <gpxe/if_ether.h>
28 #include <gpxe/ethernet.h>
29 #include <undi.h>
30 #include <undinet.h>
31
32
33 /** @file
34  *
35  * UNDI network device driver
36  *
37  */
38
39 /** An UNDI NIC */
40 struct undi_nic {
41         /** Entry point */
42         SEGOFF16_t entry;
43         /** Assigned IRQ number */
44         unsigned int irq;
45         /** Currently processing ISR */
46         int isr_processing;
47 };
48
49 static void undinet_close ( struct net_device *netdev );
50
51 /*****************************************************************************
52  *
53  * UNDI API call
54  *
55  *****************************************************************************
56  */
57
58 /**
59  * Name UNDI API call
60  *
61  * @v function          API call number
62  * @ret name            API call name
63  */
64 static inline __attribute__ (( always_inline )) const char *
65 undinet_function_name ( unsigned int function ) {
66         switch ( function ) {
67         case PXENV_START_UNDI:
68                 return "PXENV_START_UNDI";
69         case PXENV_STOP_UNDI:
70                 return "PXENV_STOP_UNDI";
71         case PXENV_UNDI_STARTUP:
72                 return "PXENV_UNDI_STARTUP";
73         case PXENV_UNDI_CLEANUP:
74                 return "PXENV_UNDI_CLEANUP";
75         case PXENV_UNDI_INITIALIZE:
76                 return "PXENV_UNDI_INITIALIZE";
77         case PXENV_UNDI_RESET_ADAPTER:
78                 return "PXENV_UNDI_RESET_ADAPTER";
79         case PXENV_UNDI_SHUTDOWN:
80                 return "PXENV_UNDI_SHUTDOWN";
81         case PXENV_UNDI_OPEN:
82                 return "PXENV_UNDI_OPEN";
83         case PXENV_UNDI_CLOSE:
84                 return "PXENV_UNDI_CLOSE";
85         case PXENV_UNDI_TRANSMIT:
86                 return "PXENV_UNDI_TRANSMIT";
87         case PXENV_UNDI_SET_MCAST_ADDRESS:
88                 return "PXENV_UNDI_SET_MCAST_ADDRESS";
89         case PXENV_UNDI_SET_STATION_ADDRESS:
90                 return "PXENV_UNDI_SET_STATION_ADDRESS";
91         case PXENV_UNDI_SET_PACKET_FILTER:
92                 return "PXENV_UNDI_SET_PACKET_FILTER";
93         case PXENV_UNDI_GET_INFORMATION:
94                 return "PXENV_UNDI_GET_INFORMATION";
95         case PXENV_UNDI_GET_STATISTICS:
96                 return "PXENV_UNDI_GET_STATISTICS";
97         case PXENV_UNDI_CLEAR_STATISTICS:
98                 return "PXENV_UNDI_CLEAR_STATISTICS";
99         case PXENV_UNDI_INITIATE_DIAGS:
100                 return "PXENV_UNDI_INITIATE_DIAGS";
101         case PXENV_UNDI_FORCE_INTERRUPT:
102                 return "PXENV_UNDI_FORCE_INTERRUPT";
103         case PXENV_UNDI_GET_MCAST_ADDRESS:
104                 return "PXENV_UNDI_GET_MCAST_ADDRESS";
105         case PXENV_UNDI_GET_NIC_TYPE:
106                 return "PXENV_UNDI_GET_NIC_TYPE";
107         case PXENV_UNDI_GET_IFACE_INFO:
108                 return "PXENV_UNDI_GET_IFACE_INFO";
109         /*
110          * Duplicate case value; this is a bug in the PXE specification.
111          *
112          *      case PXENV_UNDI_GET_STATE:
113          *              return "PXENV_UNDI_GET_STATE";
114          */
115         case PXENV_UNDI_ISR:
116                 return "PXENV_UNDI_ISR";
117         default:
118                 return "UNKNOWN API CALL";
119         }
120 }
121
122 /**
123  * UNDI parameter block
124  *
125  * Used as the paramter block for all UNDI API calls.  Resides in base
126  * memory.
127  */
128 static union u_PXENV_ANY __data16 ( undinet_params );
129 #define undinet_params __use_data16 ( undinet_params )
130
131 /** UNDI entry point
132  *
133  * Used as the indirection vector for all UNDI API calls.  Resides in
134  * base memory.
135  */
136 static SEGOFF16_t __data16 ( undinet_entry_point );
137 #define undinet_entry_point __use_data16 ( undinet_entry_point )
138
139 /**
140  * Issue UNDI API call
141  *
142  * @v undinic           UNDI NIC
143  * @v function          API call number
144  * @v params            UNDI parameter block
145  * @v params_len        Length of UNDI parameter block
146  * @ret rc              Return status code
147  */
148 static int undinet_call ( struct undi_nic *undinic, unsigned int function,
149                           void *params, size_t params_len ) {
150         union u_PXENV_ANY *pxenv_any = params;
151         PXENV_EXIT_t exit;
152         int discard_b, discard_D;
153         int rc;
154
155         /* Copy parameter block and entry point */
156         assert ( params_len <= sizeof ( undinet_params ) );
157         memcpy ( &undinet_params, params, params_len );
158         undinet_entry_point = undinic->entry;
159
160         /* Call real-mode entry point.  This calling convention will
161          * work with both the !PXE and the PXENV+ entry points.
162          */
163         __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
164                                            "pushw %%di\n\t"
165                                            "pushw %%bx\n\t"
166                                            "lcall *%c3\n\t"
167                                            "addw $6, %%sp\n\t" )
168                                : "=a" ( exit ), "=b" ( discard_b ),
169                                  "=D" ( discard_D )
170                                : "p" ( &__from_data16 ( undinet_entry_point )),
171                                  "b" ( function ),
172                                  "D" ( &__from_data16 ( undinet_params ) )
173                                : "ecx", "edx", "esi", "ebp" );
174
175         /* UNDI API calls may rudely change the status of A20 and not
176          * bother to restore it afterwards.  Intel is known to be
177          * guilty of this.
178          *
179          * Note that we will return to this point even if A20 gets
180          * screwed up by the UNDI driver, because Etherboot always
181          * resides in an even megabyte of RAM.
182          */     
183         gateA20_set();
184
185         /* Determine return status code based on PXENV_EXIT and
186          * PXENV_STATUS
187          */
188         if ( exit == PXENV_EXIT_SUCCESS ) {
189                 rc = 0;
190         } else {
191                 rc = -pxenv_any->Status;
192                 /* Paranoia; don't return success for the combination
193                  * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS
194                  */
195                 if ( rc == 0 )
196                         rc = -EIO;
197         }
198
199         /* If anything goes wrong, print as much debug information as
200          * it's possible to give.
201          */
202         if ( rc != 0 ) {
203                 SEGOFF16_t rm_params = {
204                         .segment = rm_ds,
205                         .offset = (intptr_t) &__from_data16 ( undinet_params ),
206                 };
207
208                 DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
209                        undinet_function_name ( function ), strerror ( rc ) );
210                 DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
211                        "%#02x, entry point at %04x:%04x\n", undinic,
212                        rm_params.segment, rm_params.offset, params_len,
213                        undinic->entry.segment, undinic->entry.offset );
214                 DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
215                 DBGC_HDA ( undinic, rm_params, params, params_len );
216                 DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
217                 DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
218         }
219
220         /* Copy parameter block back */
221         memcpy ( params, &undinet_params, params_len );
222
223         return rc;
224 }
225
226 /*****************************************************************************
227  *
228  * UNDI interrupt service routine
229  *
230  *****************************************************************************
231  */
232
233 /**
234  * UNDI interrupt service routine
235  *
236  * The UNDI ISR simply increments a counter (@c trigger_count) and
237  * exits.
238  */
239 extern void undinet_isr ( void );
240
241 /** Dummy chain vector */
242 static struct segoff prev_handler[ IRQ_MAX + 1 ];
243
244 /** IRQ trigger count */
245 static volatile uint8_t __text16 ( trigger_count ) = 0;
246 #define trigger_count __use_text16 ( trigger_count )
247
248 /**
249  * Hook UNDI interrupt service routine
250  *
251  * @v irq               IRQ number
252  *
253  * The UNDI ISR specifically does @b not chain to the previous
254  * interrupt handler.  BIOSes seem to install somewhat perverse
255  * default interrupt handlers; some do nothing other than an iret (and
256  * so will cause a screaming interrupt if there really is another
257  * interrupting device) and some disable the interrupt at the PIC (and
258  * so will bring our own interrupts to a shuddering halt).
259  */
260 static void undinet_hook_isr ( unsigned int irq ) {
261
262         assert ( irq <= IRQ_MAX );
263
264         __asm__ __volatile__ ( TEXT16_CODE ( "\nundinet_isr:\n\t"
265                                              "incb %%cs:%c0\n\t"
266                                              "iret\n\t" )
267                                : : "p" ( & __from_text16 ( trigger_count ) ) );
268
269         hook_bios_interrupt ( IRQ_INT ( irq ),
270                               ( ( unsigned int ) undinet_isr ),
271                               &prev_handler[irq] );
272
273 }
274
275 /**
276  * Unhook UNDI interrupt service routine
277  *
278  * @v irq               IRQ number
279  */
280 static void undinet_unhook_isr ( unsigned int irq ) {
281
282         assert ( irq <= IRQ_MAX );
283
284         unhook_bios_interrupt ( IRQ_INT ( irq ),
285                                 ( ( unsigned int ) undinet_isr ),
286                                 &prev_handler[irq] );
287 }
288
289 /**
290  * Test to see if UNDI ISR has been triggered
291  *
292  * @ret triggered       ISR has been triggered since last check
293  */
294 static int undinet_isr_triggered ( void ) {
295         static unsigned int last_trigger_count = 0;
296         unsigned int this_trigger_count;
297
298         /* Read trigger_count.  Do this only once; it is volatile */
299         this_trigger_count = trigger_count;
300
301         if ( this_trigger_count == last_trigger_count ) {
302                 /* Not triggered */
303                 return 0;
304         } else {
305                 /* Triggered */
306                 last_trigger_count = this_trigger_count;
307                 return 1;
308         }
309 }
310
311 /*****************************************************************************
312  *
313  * UNDI network device interface
314  *
315  *****************************************************************************
316  */
317
318 /** Maximum length of a packet transmitted via the UNDI API */
319 #define UNDI_PKB_LEN 1514
320
321 /** UNDI packet buffer */
322 static char __data16_array ( undinet_pkb, [UNDI_PKB_LEN] );
323 #define undinet_pkb __use_data16 ( undinet_pkb )
324
325 /** UNDI transmit buffer descriptor */
326 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
327 #define undinet_tbd __use_data16 ( undinet_tbd )
328
329 /**
330  * Transmit packet
331  *
332  * @v netdev            Network device
333  * @v pkb               Packet buffer
334  * @ret rc              Return status code
335  */
336 static int undinet_transmit ( struct net_device *netdev,
337                               struct pk_buff *pkb ) {
338         struct undi_nic *undinic = netdev->priv;
339         struct s_PXENV_UNDI_TRANSMIT undi_transmit;
340         size_t len = pkb_len ( pkb );
341         int rc;
342
343         /* Copy packet to UNDI packet buffer */
344         if ( len > sizeof ( undinet_pkb ) )
345                 len = sizeof ( undinet_pkb );
346         memcpy ( &undinet_pkb, pkb->data, len );
347
348         /* Create PXENV_UNDI_TRANSMIT data structure */
349         memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
350         undi_transmit.DestAddr.segment = rm_ds;
351         undi_transmit.DestAddr.offset
352                 = ( ( unsigned ) & __from_data16 ( undinet_tbd ) );
353         undi_transmit.TBD.segment = rm_ds;
354         undi_transmit.TBD.offset
355                 = ( ( unsigned ) & __from_data16 ( undinet_tbd ) );
356
357         /* Create PXENV_UNDI_TBD data structure */
358         undinet_tbd.ImmedLength = len;
359         undinet_tbd.Xmit.segment = rm_ds;
360         undinet_tbd.Xmit.offset 
361                 = ( ( unsigned ) & __from_data16 ( undinet_pkb ) );
362
363         /* Issue PXE API call */
364         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT,
365                                    &undi_transmit,
366                                    sizeof ( undi_transmit ) ) ) != 0 )
367                 goto done;
368
369         /* Free packet buffer */
370         netdev_tx_complete ( netdev, pkb );
371
372  done:
373         return rc;
374 }
375
376 /** 
377  * Poll for received packets
378  *
379  * @v netdev            Network device
380  * @v rx_quota          Maximum number of packets to receive
381  *
382  * Fun, fun, fun.  UNDI drivers don't use polling; they use
383  * interrupts.  We therefore cheat and pretend that an interrupt has
384  * occurred every time undinet_poll() is called.  This isn't too much
385  * of a hack; PCI devices share IRQs and so the first thing that a
386  * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
387  * not the UNDI NIC generated the interrupt; there is no harm done by
388  * spurious calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
389  * handling them any more rapidly than the usual rate of
390  * undinet_poll() being called even if we did implement a full ISR.
391  * So it should work.  Ha!
392  *
393  * Addendum (21/10/03).  Some cards don't play nicely with this trick,
394  * so instead of doing it the easy way we have to go to all the hassle
395  * of installing a genuine interrupt service routine and dealing with
396  * the wonderful 8259 Programmable Interrupt Controller.  Joy.
397  */
398 static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) {
399         struct undi_nic *undinic = netdev->priv;
400         struct s_PXENV_UNDI_ISR undi_isr;
401         struct pk_buff *pkb = NULL;
402         size_t len;
403         size_t frag_len;
404         int rc;
405
406         if ( ! undinic->isr_processing ) {
407                 /* Do nothing unless ISR has been triggered */
408                 if ( ! undinet_isr_triggered() )
409                         return;
410
411                 /* See if this was our interrupt */
412                 memset ( &undi_isr, 0, sizeof ( undi_isr ) );
413                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
414                 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
415                                            sizeof ( undi_isr ) ) ) != 0 )
416                         return;
417
418                 /* Send EOI to the PIC.  In an ideal world, we'd do
419                  * this only for interrupts which the UNDI stack
420                  * reports as "ours".  However, since we don't (can't)
421                  * chain to the previous interrupt handler, we have to
422                  * acknowledge all interrupts.  See undinet_hook_isr()
423                  * for more background.
424                  */
425                 send_eoi ( undinic->irq );
426
427                 /* If this wasn't our interrupt, exit now */
428                 if ( undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_OURS )
429                         return;
430                 
431                 /* Start ISR processing */
432                 undinic->isr_processing = 1;
433                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
434         } else {
435                 /* Continue ISR processing */
436                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
437         }
438
439         /* Run through the ISR loop */
440         while ( rx_quota ) {
441                 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
442                                            sizeof ( undi_isr ) ) ) != 0 )
443                         break;
444                 switch ( undi_isr.FuncFlag ) {
445                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
446                         /* We don't care about transmit completions */
447                         break;
448                 case PXENV_UNDI_ISR_OUT_RECEIVE:
449                         /* Packet fragment received */
450                         len = undi_isr.FrameLength;
451                         frag_len = undi_isr.BufferLength;
452                         if ( ! pkb )
453                                 pkb = alloc_pkb ( len );
454                         if ( ! pkb ) {
455                                 DBGC ( undinic, "UNDINIC %p could not "
456                                        "allocate %zd bytes for RX buffer\n",
457                                        undinic, len );
458                                 /* Fragment will be dropped */
459                                 goto done;
460                         }
461                         if ( frag_len > pkb_tailroom ( pkb ) ) {
462                                 DBGC ( undinic, "UNDINIC %p fragment too "
463                                        "large\n", undinic );
464                                 frag_len = pkb_tailroom ( pkb );
465                         }
466                         copy_from_real ( pkb_put ( pkb, frag_len ),
467                                          undi_isr.Frame.segment,
468                                          undi_isr.Frame.offset, frag_len );
469                         if ( pkb_len ( pkb ) == len ) {
470                                 netdev_rx ( netdev, pkb );
471                                 pkb = NULL;
472                                 --rx_quota;
473                         }
474                         break;
475                 case PXENV_UNDI_ISR_OUT_DONE:
476                         /* Processing complete */
477                         undinic->isr_processing = 0;
478                         goto done;
479                 default:
480                         /* Should never happen */
481                         DBGC ( undinic, "UNDINIC %p ISR returned invalid "
482                                "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
483                         undinic->isr_processing = 0;
484                         goto done;
485                 }
486                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
487         }
488
489  done:
490         if ( pkb ) {
491                 DBGC ( undinic, "UNDINIC %p returned incomplete packet\n",
492                        undinic );
493                 netdev_rx ( netdev, pkb );
494         }
495 }
496
497 /**
498  * Open NIC
499  *
500  * @v netdev            Net device
501  * @ret rc              Return status code
502  */
503 static int undinet_open ( struct net_device *netdev ) {
504         struct undi_nic *undinic = netdev->priv;
505         struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
506         struct s_PXENV_UNDI_OPEN undi_open;
507         int rc;
508
509         /* Hook interrupt service routine and enable interrupt */
510         undinet_hook_isr ( undinic->irq );
511         enable_irq ( undinic->irq );
512         send_eoi ( undinic->irq );
513
514         /* Set station address.  Required for some PXE stacks; will
515          * spuriously fail on others.  Ignore failures.  We only ever
516          * use it to set the MAC address to the card's permanent value
517          * anyway.
518          */
519         memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
520                  sizeof ( undi_set_address.StationAddress ) );
521         undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
522                        &undi_set_address, sizeof ( undi_set_address ) );
523
524         /* Open NIC */
525         memset ( &undi_open, 0, sizeof ( undi_open ) );
526         undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
527         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
528                                    sizeof ( undi_open ) ) ) != 0 )
529                 goto err;
530
531         return 0;
532
533  err:
534         undinet_close ( netdev );
535         return rc;
536 }
537
538 /**
539  * Close NIC
540  *
541  * @v netdev            Net device
542  */
543 static void undinet_close ( struct net_device *netdev ) {
544         struct undi_nic *undinic = netdev->priv;
545         struct s_PXENV_UNDI_ISR undi_isr;
546         struct s_PXENV_UNDI_CLOSE undi_close;
547         int rc;
548
549         /* Ensure ISR has exited cleanly */
550         while ( undinic->isr_processing ) {
551                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
552                 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
553                                            sizeof ( undi_isr ) ) ) != 0 )
554                         break;
555                 switch ( undi_isr.FuncFlag ) {
556                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
557                 case PXENV_UNDI_ISR_OUT_RECEIVE:
558                         /* Continue draining */
559                         break;
560                 default:
561                         /* Stop processing */
562                         undinic->isr_processing = 0;
563                         break;
564                 }
565         }
566
567         /* Close NIC */
568         undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
569                        sizeof ( undi_close ) );
570
571         /* Disable interrupt and unhook ISR */
572         disable_irq ( undinic->irq );
573         undinet_unhook_isr ( undinic->irq );
574 }
575
576 /**
577  * Probe UNDI device
578  *
579  * @v undi              UNDI device
580  * @ret rc              Return status code
581  */
582 int undinet_probe ( struct undi_device *undi ) {
583         struct net_device *netdev;
584         struct undi_nic *undinic;
585         struct s_PXENV_START_UNDI start_undi;
586         struct s_PXENV_UNDI_STARTUP undi_startup;
587         struct s_PXENV_UNDI_INITIALIZE undi_initialize;
588         struct s_PXENV_UNDI_GET_INFORMATION undi_info;
589         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
590         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
591         struct s_PXENV_STOP_UNDI stop_undi;
592         int rc;
593
594         /* Allocate net device */
595         netdev = alloc_etherdev ( sizeof ( *undinic ) );
596         if ( ! netdev )
597                 return -ENOMEM;
598         undinic = netdev->priv;
599         undi_set_drvdata ( undi, netdev );
600         netdev->dev = &undi->dev;
601         memset ( undinic, 0, sizeof ( *undinic ) );
602         undinic->entry = undi->entry;
603         DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
604
605         /* Hook in UNDI stack */
606         memset ( &start_undi, 0, sizeof ( start_undi ) );
607         start_undi.AX = undi->pci_busdevfn;
608         start_undi.BX = undi->isapnp_csn;
609         start_undi.DX = undi->isapnp_read_port;
610         start_undi.ES = BIOS_SEG;
611         start_undi.DI = find_pnp_bios();
612         if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI, &start_undi,
613                                    sizeof ( start_undi ) ) ) != 0 )
614                 goto err_start_undi;
615
616         /* Bring up UNDI stack */
617         memset ( &undi_startup, 0, sizeof ( undi_startup ) );
618         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP, &undi_startup,
619                                    sizeof ( undi_startup ) ) ) != 0 )
620                 goto err_undi_startup;
621         memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
622         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE,
623                                    &undi_initialize,
624                                    sizeof ( undi_initialize ) ) ) != 0 )
625                 goto err_undi_initialize;
626
627         /* Get device information */
628         memset ( &undi_info, 0, sizeof ( undi_info ) );
629         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
630                                    &undi_info, sizeof ( undi_info ) ) ) != 0 )
631                 goto err_undi_get_information;
632         memcpy ( netdev->ll_addr, undi_info.PermNodeAddress, ETH_ALEN );
633         undinic->irq = undi_info.IntNumber;
634         if ( undinic->irq > IRQ_MAX ) {
635                 DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
636                        undinic, undinic->irq );
637                 goto err_bad_irq;
638         }
639         DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
640                undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq );
641
642         /* Point to NIC specific routines */
643         netdev->open     = undinet_open;
644         netdev->close    = undinet_close;
645         netdev->transmit = undinet_transmit;
646         netdev->poll     = undinet_poll;
647
648         /* Register network device */
649         if ( ( rc = register_netdev ( netdev ) ) != 0 )
650                 goto err_register;
651
652         return 0;
653
654  err_register:
655  err_bad_irq:
656  err_undi_get_information:
657  err_undi_initialize:
658         /* Shut down UNDI stack */
659         memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
660         undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
661                        sizeof ( undi_shutdown ) );
662         memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
663         undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
664                        sizeof ( undi_cleanup ) );
665  err_undi_startup:
666         /* Unhook UNDI stack */
667         memset ( &stop_undi, 0, sizeof ( stop_undi ) );
668         undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
669                        sizeof ( stop_undi ) );
670  err_start_undi:
671         free_netdev ( netdev );
672         undi_set_drvdata ( undi, NULL );
673         return rc;
674 }
675
676 /**
677  * Remove UNDI device
678  *
679  * @v undi              UNDI device
680  */
681 void undinet_remove ( struct undi_device *undi ) {
682         struct net_device *netdev = undi_get_drvdata ( undi );
683         struct undi_nic *undinic = netdev->priv;
684         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
685         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
686         struct s_PXENV_STOP_UNDI stop_undi;
687
688         /* Unregister net device */
689         unregister_netdev ( netdev );
690
691         /* Shut down UNDI stack */
692         memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
693         undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
694                        sizeof ( undi_shutdown ) );
695         memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
696         undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
697                        sizeof ( undi_cleanup ) );
698
699         /* Unhook UNDI stack */
700         memset ( &stop_undi, 0, sizeof ( stop_undi ) );
701         undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
702                        sizeof ( stop_undi ) );
703
704         /* Free network device */
705         free_netdev ( netdev );
706 }