6d30d1b67fd1c3dd327fb4d518e13f350ffdfdba
[people/pcmattman/gpxe.git] / src / interface / pxe / pxe_undi.c
1 /** @file
2  *
3  * PXE UNDI API
4  *
5  */
6
7 /*
8  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #include "pxe.h"
26
27 /* PXENV_UNDI_STARTUP
28  *
29  * Status: working
30  */
31 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
32         DBG ( "PXENV_UNDI_STARTUP" );
33
34         undi_startup->Status = PXENV_STATUS_SUCCESS;
35         return PXENV_EXIT_SUCCESS;
36 }
37
38 /* PXENV_UNDI_CLEANUP
39  *
40  * Status: working
41  */
42 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
43         DBG ( "PXENV_UNDI_CLEANUP" );
44
45         undi_cleanup->Status = PXENV_STATUS_SUCCESS;
46         return PXENV_EXIT_SUCCESS;
47 }
48
49 /* PXENV_UNDI_INITIALIZE
50  *
51  * Status: working
52  */
53 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
54                                      *undi_initialize ) {
55         DBG ( "PXENV_UNDI_INITIALIZE" );
56
57         undi_initialize->Status = PXENV_STATUS_SUCCESS;
58         return PXENV_EXIT_SUCCESS;
59 }
60
61 /* PXENV_UNDI_RESET_ADAPTER
62  *
63  * Status: working
64  */
65 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
66                                         *undi_reset_adapter ) {
67         DBG ( "PXENV_UNDI_RESET_ADAPTER" );
68
69         undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
70         return PXENV_EXIT_SUCCESS;
71 }
72
73 /* PXENV_UNDI_SHUTDOWN
74  *
75  * Status: working
76  */
77 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
78                                    *undi_shutdown ) {
79         DBG ( "PXENV_UNDI_SHUTDOWN" );
80
81         undi_shutdown->Status = PXENV_STATUS_SUCCESS;
82         return PXENV_EXIT_SUCCESS;
83 }
84
85 /* PXENV_UNDI_OPEN
86  *
87  * Status: working
88  */
89 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
90         DBG ( "PXENV_UNDI_OPEN" );
91
92 #if 0
93         /* PXESPEC: This is where we choose to enable interrupts.
94          * Can't actually find where we're meant to in the PXE spec,
95          * but this should work.
96          */
97         eth_irq ( ENABLE );
98 #endif
99
100         undi_open->Status = PXENV_STATUS_SUCCESS;
101         return PXENV_EXIT_SUCCESS;
102 }
103
104 /* PXENV_UNDI_CLOSE
105  *
106  * Status: working
107  */
108 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
109         DBG ( "PXENV_UNDI_CLOSE" );
110
111         undi_close->Status = PXENV_STATUS_SUCCESS;
112         return PXENV_EXIT_SUCCESS;
113 }
114
115 /* PXENV_UNDI_TRANSMIT
116  *
117  * Status: working
118  */
119 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
120                                    *undi_transmit ) {
121         struct s_PXENV_UNDI_TBD *tbd;
122         const char *dest;
123         unsigned int type;
124         unsigned int length;
125         const char *data;
126
127         DBG ( "PXENV_UNDI_TRANSMIT" );
128
129 #if 0
130         /* We support only the "immediate" portion of the TBD.  Who
131          * knows what Intel's "engineers" were smoking when they came
132          * up with the array of transmit data blocks...
133          */
134         tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
135         if ( tbd->DataBlkCount > 0 ) {
136                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
137                 return PXENV_EXIT_FAILURE;
138         }
139         data = SEGOFF16_TO_PTR ( tbd->Xmit );
140         length = tbd->ImmedLength;
141
142         /* If destination is broadcast, we need to supply the MAC address */
143         if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
144                 dest = broadcast_mac;
145         } else {
146                 dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
147         }
148
149         /* We can't properly support P_UNKNOWN without rewriting all
150          * the driver transmit() methods, so we cheat: if P_UNKNOWN is
151          * specified we rip the destination address and type out of
152          * the pre-assembled packet, then skip over the header.
153          */
154         switch ( undi_transmit->Protocol ) {
155         case P_IP:      type = ETH_P_IP;        break;
156         case P_ARP:     type = ETH_P_ARP;       break;
157         case P_RARP:    type = ETH_P_RARP;      break;
158         case P_UNKNOWN:
159                 media_header = (media_header_t*)data;
160                 dest = media_header->dest;
161                 type = ntohs ( media_header->nstype );
162                 data += ETH_HLEN;
163                 length -= ETH_HLEN;
164                 break;
165         default:
166                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
167                 return PXENV_EXIT_FAILURE;
168         }
169
170         /* Send the packet */
171         eth_transmit ( dest, type, length, data );
172 #endif
173         
174         undi_transmit->Status = PXENV_STATUS_SUCCESS;
175         return PXENV_EXIT_SUCCESS;
176 }
177
178 /* PXENV_UNDI_SET_MCAST_ADDRESS
179  *
180  * Status: stub (no PXE multicast support)
181  */
182 PXENV_EXIT_t
183 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
184                                *undi_set_mcast_address ) {
185         DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
186
187         undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
188         return PXENV_EXIT_FAILURE;
189 }
190
191 /* PXENV_UNDI_SET_STATION_ADDRESS
192  *
193  * Status: working (deliberately incomplete)
194  */
195 PXENV_EXIT_t 
196 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
197                                  *undi_set_station_address ) {
198         DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
199
200 #if 0
201         /* We don't offer a facility to set the MAC address; this
202          * would require adding extra code to all the Etherboot
203          * drivers, for very little benefit.  If we're setting it to
204          * the current value anyway then return success, otherwise
205          * return UNSUPPORTED.
206          */
207         if ( memcmp ( nic.node_addr,
208                       &undi_set_station_address->StationAddress,
209                       ETH_ALEN ) == 0 ) {
210                 undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
211                 return PXENV_EXIT_SUCCESS;
212         }
213 #endif
214
215         undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
216         return PXENV_EXIT_FAILURE;
217 }
218
219 /* PXENV_UNDI_SET_PACKET_FILTER
220  *
221  * Status: won't implement (would require driver API changes for no
222  * real benefit)
223  */
224 PXENV_EXIT_t
225 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
226                                *undi_set_packet_filter ) {
227         DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
228
229         undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
230         return PXENV_EXIT_FAILURE;
231 }
232
233 /* PXENV_UNDI_GET_INFORMATION
234  *
235  * Status: working
236  */
237 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
238                                           *undi_get_information ) {
239         DBG ( "PXENV_UNDI_GET_INFORMATION" );
240
241 #if 0
242         undi_get_information->BaseIo = nic.ioaddr;
243         undi_get_information->IntNumber = nic.irqno;
244         /* Cheat: assume all cards can cope with this */
245         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
246         /* Cheat: we only ever have Ethernet cards */
247         undi_get_information->HwType = ETHER_TYPE;
248         undi_get_information->HwAddrLen = ETH_ALEN;
249         /* Cheat: assume card is always configured with its permanent
250          * node address.  This is a valid assumption within Etherboot
251          * at the time of writing.
252          */
253         memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
254                  ETH_ALEN );
255         memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
256                  ETH_ALEN );
257         undi_get_information->ROMAddress = 0;
258                 /* nic.rom_info->rom_segment; */
259         /* We only provide the ability to receive or transmit a single
260          * packet at a time.  This is a bootloader, not an OS.
261          */
262         undi_get_information->RxBufCt = 1;
263         undi_get_information->TxBufCt = 1;
264 #endif
265
266         undi_get_information->Status = PXENV_STATUS_SUCCESS;
267         return PXENV_EXIT_SUCCESS;
268 }
269
270 /* PXENV_UNDI_GET_STATISTICS
271  *
272  * Status: won't implement (would require driver API changes for no
273  * real benefit)
274  */
275 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
276                                          *undi_get_statistics ) {
277         DBG ( "PXENV_UNDI_GET_STATISTICS" );
278
279         undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
280         return PXENV_EXIT_FAILURE;
281 }
282
283 /* PXENV_UNDI_CLEAR_STATISTICS
284  *
285  * Status: won't implement (would require driver API changes for no
286  * real benefit)
287  */
288 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
289                                            *undi_clear_statistics ) {
290         DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
291
292         undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
293         return PXENV_EXIT_FAILURE;
294 }
295
296 /* PXENV_UNDI_INITIATE_DIAGS
297  *
298  * Status: won't implement (would require driver API changes for no
299  * real benefit)
300  */
301 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
302                                          *undi_initiate_diags ) {
303         DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
304
305         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
306         return PXENV_EXIT_FAILURE;
307 }
308
309 /* PXENV_UNDI_FORCE_INTERRUPT
310  *
311  * Status: working
312  */
313 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
314                                           *undi_force_interrupt ) {
315         DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
316
317 #if 0
318         eth_irq ( FORCE );
319 #endif
320
321         undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
322         return PXENV_EXIT_SUCCESS;
323 }
324
325 /* PXENV_UNDI_GET_MCAST_ADDRESS
326  *
327  * Status: stub (no PXE multicast support)
328  */
329 PXENV_EXIT_t
330 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
331                                *undi_get_mcast_address ) {
332         DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
333
334         undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
335         return PXENV_EXIT_FAILURE;
336 }
337
338 /* PXENV_UNDI_GET_NIC_TYPE
339  *
340  * Status: working
341  */
342 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
343                                        *undi_get_nic_type ) {
344         DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
345
346 #warning "device probing mechanism has changed completely"
347 #if 0
348         struct dev *dev = &dev; 
349         if ( dev->to_probe == PROBE_PCI ) {
350                 struct pci_device *pci = &dev->state.pci.dev;
351
352                 undi_get_nic_type->NicType = PCI_NIC;
353                 undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
354                 undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
355                 undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
356                 undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
357                 undi_get_nic_type->info.pci.BusDevFunc =
358                         ( pci->bus << 8 ) | pci->devfn;
359                 /* Cheat: these fields are probably unnecessary, and
360                  * would require adding extra code to pci.c.
361                  */
362                 undi_get_nic_type->info.pci.Prog_Intf = 0;
363                 undi_get_nic_type->info.pci.Rev = 0;
364                 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
365                 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
366         } else if ( dev->to_probe == PROBE_ISA ) {
367                 /* const struct isa_driver *isa = dev->state.isa.driver; */
368
369                 undi_get_nic_type->NicType = PnP_NIC;
370                 /* Don't think anything fills these fields in, and
371                  * probably no-one will ever be interested in them.
372                  */
373                 undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
374                 undi_get_nic_type->info.pnp.Base_Class = 0;
375                 undi_get_nic_type->info.pnp.Sub_Class = 0;
376                 undi_get_nic_type->info.pnp.Prog_Intf = 0;
377                 undi_get_nic_type->info.pnp.CardSelNum = 0;
378         } else {
379                 /* PXESPEC: There doesn't seem to be an "unknown type"
380                  * defined.
381                  */
382                 undi_get_nic_type->NicType = 0;
383         }
384         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
385         return PXENV_EXIT_SUCCESS;
386
387 #endif
388 }
389
390 /* PXENV_UNDI_GET_IFACE_INFO
391  *
392  * Status: working
393  */
394 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
395                                          *undi_get_iface_info ) {
396         DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
397
398 #if 0
399         /* Just hand back some info, doesn't really matter what it is.
400          * Most PXE stacks seem to take this approach.
401          */
402         sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
403         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
404         undi_get_iface_info->ServiceFlags = 0;
405         memset ( undi_get_iface_info->Reserved, 0,
406                  sizeof(undi_get_iface_info->Reserved) );
407 #endif
408
409         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
410         return PXENV_EXIT_SUCCESS;
411 }
412
413 /* PXENV_UNDI_GET_STATE
414  *
415  * Status: impossible
416  */
417 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
418                                     *undi_get_state ) {
419         DBG ( "PXENV_UNDI_GET_STATE" );
420
421         undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
422         return PXENV_EXIT_FAILURE;
423 };
424
425 /* PXENV_UNDI_ISR
426  *
427  * Status: working
428  */
429 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
430         DBG ( "PXENV_UNDI_ISR" );
431
432 #if 0
433         /* We can't call ENSURE_READY, because this could be being
434          * called as part of an interrupt service routine.  Instead,
435          * we should simply die if we're not READY.
436          */
437         if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
438                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
439                 return PXENV_EXIT_FAILURE;
440         }
441         
442         /* Just in case some idiot actually looks at these fields when
443          * we weren't meant to fill them in...
444          */
445         undi_isr->BufferLength = 0;
446         undi_isr->FrameLength = 0;
447         undi_isr->FrameHeaderLength = 0;
448         undi_isr->ProtType = 0;
449         undi_isr->PktType = 0;
450
451         switch ( undi_isr->FuncFlag ) {
452         case PXENV_UNDI_ISR_IN_START :
453                 /* Is there a packet waiting?  If so, disable
454                  * interrupts on the NIC and return "it's ours".  Do
455                  * *not* necessarily acknowledge the interrupt; this
456                  * can happen later when eth_poll(1) is called.  As
457                  * long as the interrupt is masked off so that it
458                  * doesn't immediately retrigger the 8259A then all
459                  * should be well.
460                  */
461                 DBG ( " START" );
462                 if ( eth_poll ( 0 ) ) {
463                         DBG ( " OURS" );
464                         eth_irq ( DISABLE );
465                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
466                 } else {
467                         DBG ( " NOT_OURS" );
468                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
469                 }
470                 break;
471         case PXENV_UNDI_ISR_IN_PROCESS :
472                 /* Call poll(), return packet.  If no packet, return "done".
473                  */
474                 DBG ( " PROCESS" );
475                 if ( eth_poll ( 1 ) ) {
476                         DBG ( " RECEIVE %d", nic.packetlen );
477                         if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
478                                 /* Should never happen */
479                                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
480                                 undi_isr->Status =
481                                         PXENV_STATUS_OUT_OF_RESOURCES;
482                                 return PXENV_EXIT_FAILURE;
483                         }
484                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
485                         undi_isr->BufferLength = nic.packetlen;
486                         undi_isr->FrameLength = nic.packetlen;
487                         undi_isr->FrameHeaderLength = ETH_HLEN;
488                         memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
489                         PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
490                         switch ( ntohs(media_header->nstype) ) {
491                         case ETH_P_IP:  undi_isr->ProtType = P_IP;      break;
492                         case ETH_P_ARP: undi_isr->ProtType = P_ARP;     break;
493                         case ETH_P_RARP: undi_isr->ProtType = P_RARP;   break;
494                         default :       undi_isr->ProtType = P_UNKNOWN;
495                         }
496                         if ( memcmp ( media_header->dest, broadcast_mac,
497                                       sizeof(broadcast_mac) ) ) {
498                                 undi_isr->PktType = XMT_BROADCAST;
499                         } else {
500                                 undi_isr->PktType = XMT_DESTADDR;
501                         }
502                         break;
503                 } else {
504                         /* No break - fall through to IN_GET_NEXT */
505                 }
506         case PXENV_UNDI_ISR_IN_GET_NEXT :
507                 /* We only ever return one frame at a time */
508                 DBG ( " GET_NEXT DONE" );
509                 /* Re-enable interrupts */
510                 eth_irq ( ENABLE );
511                 /* Force an interrupt if there's a packet still
512                  * waiting, since we only handle one packet per
513                  * interrupt.
514                  */
515                 if ( eth_poll ( 0 ) ) {
516                         DBG ( " (RETRIGGER)" );
517                         eth_irq ( FORCE );
518                 }
519                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
520                 break;
521         default :
522                 /* Should never happen */
523                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
524                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
525                 return PXENV_EXIT_FAILURE;
526         }
527 #endif
528
529         undi_isr->Status = PXENV_STATUS_SUCCESS;
530         return PXENV_EXIT_SUCCESS;
531 }