8 * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
27 #include <gpxe/netdevice.h>
28 #include <gpxe/device.h>
30 #include <gpxe/isapnp.h>
31 #include <gpxe/if_ether.h>
38 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
39 DBG ( "PXENV_UNDI_STARTUP" );
41 undi_startup->Status = PXENV_STATUS_SUCCESS;
42 return PXENV_EXIT_SUCCESS;
49 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
50 DBG ( "PXENV_UNDI_CLEANUP" );
52 undi_cleanup->Status = PXENV_STATUS_SUCCESS;
53 return PXENV_EXIT_SUCCESS;
56 /* PXENV_UNDI_INITIALIZE
60 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
62 DBG ( "PXENV_UNDI_INITIALIZE" );
64 undi_initialize->Status = PXENV_STATUS_SUCCESS;
65 return PXENV_EXIT_SUCCESS;
68 /* PXENV_UNDI_RESET_ADAPTER
72 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
73 *undi_reset_adapter ) {
74 DBG ( "PXENV_UNDI_RESET_ADAPTER" );
76 undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
77 return PXENV_EXIT_SUCCESS;
80 /* PXENV_UNDI_SHUTDOWN
84 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
86 DBG ( "PXENV_UNDI_SHUTDOWN" );
88 undi_shutdown->Status = PXENV_STATUS_SUCCESS;
89 return PXENV_EXIT_SUCCESS;
96 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
97 DBG ( "PXENV_UNDI_OPEN" );
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.
107 undi_open->Status = PXENV_STATUS_SUCCESS;
108 return PXENV_EXIT_SUCCESS;
115 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
116 DBG ( "PXENV_UNDI_CLOSE" );
118 undi_close->Status = PXENV_STATUS_SUCCESS;
119 return PXENV_EXIT_SUCCESS;
122 /* PXENV_UNDI_TRANSMIT
126 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
128 struct s_PXENV_UNDI_TBD *tbd;
134 DBG ( "PXENV_UNDI_TRANSMIT" );
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...
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;
146 data = SEGOFF16_TO_PTR ( tbd->Xmit );
147 length = tbd->ImmedLength;
149 /* If destination is broadcast, we need to supply the MAC address */
150 if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
151 dest = broadcast_mac;
153 dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
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.
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;
166 media_header = (media_header_t*)data;
167 dest = media_header->dest;
168 type = ntohs ( media_header->nstype );
173 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
174 return PXENV_EXIT_FAILURE;
177 /* Send the packet */
178 eth_transmit ( dest, type, length, data );
181 undi_transmit->Status = PXENV_STATUS_SUCCESS;
182 return PXENV_EXIT_SUCCESS;
185 /* PXENV_UNDI_SET_MCAST_ADDRESS
187 * Status: stub (no PXE multicast support)
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" );
194 undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
195 return PXENV_EXIT_FAILURE;
198 /* PXENV_UNDI_SET_STATION_ADDRESS
200 * Status: working (deliberately incomplete)
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" );
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.
214 if ( memcmp ( nic.node_addr,
215 &undi_set_station_address->StationAddress,
217 undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
218 return PXENV_EXIT_SUCCESS;
222 undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
223 return PXENV_EXIT_FAILURE;
226 /* PXENV_UNDI_SET_PACKET_FILTER
228 * Status: won't implement (would require driver API changes for no
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" );
236 undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
237 return PXENV_EXIT_FAILURE;
240 /* PXENV_UNDI_GET_INFORMATION
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;
251 DBG ( "PXENV_UNDI_GET_INFORMATION" );
253 switch ( dev->desc.bus_type ) {
255 struct pci_device *pci =
256 container_of ( dev, struct pci_device, dev );
258 ioaddr = pci->ioaddr;
261 case BUS_TYPE_ISAPNP: {
262 struct isapnp_device *isapnp =
263 container_of ( dev, struct isapnp_device, dev );
265 ioaddr = isapnp->ioaddr;
266 irqno = isapnp->irqno;
269 undi_get_information->Status = PXENV_STATUS_FAILURE;
270 return PXENV_EXIT_FAILURE;
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.
283 memcpy ( &undi_get_information->CurrentNodeAddress,
285 sizeof ( undi_get_information->CurrentNodeAddress ) );
286 memcpy ( &undi_get_information->PermNodeAddress,
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.
294 undi_get_information->RxBufCt = 1;
295 undi_get_information->TxBufCt = 1;
297 undi_get_information->Status = PXENV_STATUS_SUCCESS;
298 return PXENV_EXIT_SUCCESS;
301 /* PXENV_UNDI_GET_STATISTICS
303 * Status: won't implement (would require driver API changes for no
306 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
307 *undi_get_statistics ) {
308 DBG ( "PXENV_UNDI_GET_STATISTICS" );
310 undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
311 return PXENV_EXIT_FAILURE;
314 /* PXENV_UNDI_CLEAR_STATISTICS
316 * Status: won't implement (would require driver API changes for no
319 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
320 *undi_clear_statistics ) {
321 DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
323 undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
324 return PXENV_EXIT_FAILURE;
327 /* PXENV_UNDI_INITIATE_DIAGS
329 * Status: won't implement (would require driver API changes for no
332 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
333 *undi_initiate_diags ) {
334 DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
336 undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
337 return PXENV_EXIT_FAILURE;
340 /* PXENV_UNDI_FORCE_INTERRUPT
344 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
345 *undi_force_interrupt ) {
346 DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
352 undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
353 return PXENV_EXIT_SUCCESS;
356 /* PXENV_UNDI_GET_MCAST_ADDRESS
358 * Status: stub (no PXE multicast support)
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" );
365 undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
366 return PXENV_EXIT_FAILURE;
369 /* PXENV_UNDI_GET_NIC_TYPE
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;
377 DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
379 memset ( &undi_get_nic_type->info, 0,
380 sizeof ( undi_get_nic_type->info ) );
382 switch ( dev->desc.bus_type ) {
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;
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.
398 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
399 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
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;
406 undi_get_nic_type->NicType = PnP_NIC;
407 info->EISA_Dev_ID = ( ( isapnp->vendor_id << 16 ) |
409 info->CardSelNum = isapnp->csn;
410 /* Cheat: remaining fields are probably unnecessary,
411 * and would require adding extra code to isapnp.c.
415 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
416 return PXENV_EXIT_FAILURE;
419 undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
420 return PXENV_EXIT_SUCCESS;
423 /* PXENV_UNDI_GET_IFACE_INFO
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" );
432 /* Just hand back some info, doesn't really matter what it is.
433 * Most PXE stacks seem to take this approach.
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) );
442 undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
443 return PXENV_EXIT_SUCCESS;
446 /* PXENV_UNDI_GET_STATE
450 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
452 DBG ( "PXENV_UNDI_GET_STATE" );
454 undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
455 return PXENV_EXIT_FAILURE;
462 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
463 DBG ( "PXENV_UNDI_ISR" );
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.
470 if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
471 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
472 return PXENV_EXIT_FAILURE;
475 /* Just in case some idiot actually looks at these fields when
476 * we weren't meant to fill them in...
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;
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
495 if ( eth_poll ( 0 ) ) {
498 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
501 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
504 case PXENV_UNDI_ISR_IN_PROCESS :
505 /* Call poll(), return packet. If no packet, return "done".
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;
514 PXENV_STATUS_OUT_OF_RESOURCES;
515 return PXENV_EXIT_FAILURE;
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;
529 if ( memcmp ( media_header->dest, broadcast_mac,
530 sizeof(broadcast_mac) ) ) {
531 undi_isr->PktType = XMT_BROADCAST;
533 undi_isr->PktType = XMT_DESTADDR;
537 /* No break - fall through to IN_GET_NEXT */
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 */
544 /* Force an interrupt if there's a packet still
545 * waiting, since we only handle one packet per
548 if ( eth_poll ( 0 ) ) {
549 DBG ( " (RETRIGGER)" );
552 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
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;
562 undi_isr->Status = PXENV_STATUS_SUCCESS;
563 return PXENV_EXIT_SUCCESS;