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