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.
31 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
32 DBG ( "PXENV_UNDI_STARTUP" );
34 undi_startup->Status = PXENV_STATUS_SUCCESS;
35 return PXENV_EXIT_SUCCESS;
42 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
43 DBG ( "PXENV_UNDI_CLEANUP" );
45 undi_cleanup->Status = PXENV_STATUS_SUCCESS;
46 return PXENV_EXIT_SUCCESS;
49 /* PXENV_UNDI_INITIALIZE
53 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
55 DBG ( "PXENV_UNDI_INITIALIZE" );
57 undi_initialize->Status = PXENV_STATUS_SUCCESS;
58 return PXENV_EXIT_SUCCESS;
61 /* PXENV_UNDI_RESET_ADAPTER
65 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
66 *undi_reset_adapter ) {
67 DBG ( "PXENV_UNDI_RESET_ADAPTER" );
69 undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
70 return PXENV_EXIT_SUCCESS;
73 /* PXENV_UNDI_SHUTDOWN
77 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
79 DBG ( "PXENV_UNDI_SHUTDOWN" );
81 undi_shutdown->Status = PXENV_STATUS_SUCCESS;
82 return PXENV_EXIT_SUCCESS;
89 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
90 DBG ( "PXENV_UNDI_OPEN" );
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.
100 undi_open->Status = PXENV_STATUS_SUCCESS;
101 return PXENV_EXIT_SUCCESS;
108 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
109 DBG ( "PXENV_UNDI_CLOSE" );
111 undi_close->Status = PXENV_STATUS_SUCCESS;
112 return PXENV_EXIT_SUCCESS;
115 /* PXENV_UNDI_TRANSMIT
119 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
121 struct s_PXENV_UNDI_TBD *tbd;
127 DBG ( "PXENV_UNDI_TRANSMIT" );
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...
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;
139 data = SEGOFF16_TO_PTR ( tbd->Xmit );
140 length = tbd->ImmedLength;
142 /* If destination is broadcast, we need to supply the MAC address */
143 if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
144 dest = broadcast_mac;
146 dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
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.
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;
159 media_header = (media_header_t*)data;
160 dest = media_header->dest;
161 type = ntohs ( media_header->nstype );
166 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
167 return PXENV_EXIT_FAILURE;
170 /* Send the packet */
171 eth_transmit ( dest, type, length, data );
174 undi_transmit->Status = PXENV_STATUS_SUCCESS;
175 return PXENV_EXIT_SUCCESS;
178 /* PXENV_UNDI_SET_MCAST_ADDRESS
180 * Status: stub (no PXE multicast support)
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" );
187 undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
188 return PXENV_EXIT_FAILURE;
191 /* PXENV_UNDI_SET_STATION_ADDRESS
193 * Status: working (deliberately incomplete)
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" );
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.
207 if ( memcmp ( nic.node_addr,
208 &undi_set_station_address->StationAddress,
210 undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
211 return PXENV_EXIT_SUCCESS;
215 undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
216 return PXENV_EXIT_FAILURE;
219 /* PXENV_UNDI_SET_PACKET_FILTER
221 * Status: won't implement (would require driver API changes for no
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" );
229 undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
230 return PXENV_EXIT_FAILURE;
233 /* PXENV_UNDI_GET_INFORMATION
237 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
238 *undi_get_information ) {
239 DBG ( "PXENV_UNDI_GET_INFORMATION" );
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.
253 memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
255 memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
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.
262 undi_get_information->RxBufCt = 1;
263 undi_get_information->TxBufCt = 1;
266 undi_get_information->Status = PXENV_STATUS_SUCCESS;
267 return PXENV_EXIT_SUCCESS;
270 /* PXENV_UNDI_GET_STATISTICS
272 * Status: won't implement (would require driver API changes for no
275 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
276 *undi_get_statistics ) {
277 DBG ( "PXENV_UNDI_GET_STATISTICS" );
279 undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
280 return PXENV_EXIT_FAILURE;
283 /* PXENV_UNDI_CLEAR_STATISTICS
285 * Status: won't implement (would require driver API changes for no
288 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
289 *undi_clear_statistics ) {
290 DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
292 undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
293 return PXENV_EXIT_FAILURE;
296 /* PXENV_UNDI_INITIATE_DIAGS
298 * Status: won't implement (would require driver API changes for no
301 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
302 *undi_initiate_diags ) {
303 DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
305 undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
306 return PXENV_EXIT_FAILURE;
309 /* PXENV_UNDI_FORCE_INTERRUPT
313 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
314 *undi_force_interrupt ) {
315 DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
321 undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
322 return PXENV_EXIT_SUCCESS;
325 /* PXENV_UNDI_GET_MCAST_ADDRESS
327 * Status: stub (no PXE multicast support)
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" );
334 undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
335 return PXENV_EXIT_FAILURE;
338 /* PXENV_UNDI_GET_NIC_TYPE
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" );
346 #warning "device probing mechanism has changed completely"
348 struct dev *dev = &dev;
349 if ( dev->to_probe == PROBE_PCI ) {
350 struct pci_device *pci = &dev->state.pci.dev;
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.
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; */
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.
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;
379 /* PXESPEC: There doesn't seem to be an "unknown type"
382 undi_get_nic_type->NicType = 0;
384 undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
385 return PXENV_EXIT_SUCCESS;
390 /* PXENV_UNDI_GET_IFACE_INFO
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" );
399 /* Just hand back some info, doesn't really matter what it is.
400 * Most PXE stacks seem to take this approach.
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) );
409 undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
410 return PXENV_EXIT_SUCCESS;
413 /* PXENV_UNDI_GET_STATE
417 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
419 DBG ( "PXENV_UNDI_GET_STATE" );
421 undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
422 return PXENV_EXIT_FAILURE;
429 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
430 DBG ( "PXENV_UNDI_ISR" );
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.
437 if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
438 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
439 return PXENV_EXIT_FAILURE;
442 /* Just in case some idiot actually looks at these fields when
443 * we weren't meant to fill them in...
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;
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
462 if ( eth_poll ( 0 ) ) {
465 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
468 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
471 case PXENV_UNDI_ISR_IN_PROCESS :
472 /* Call poll(), return packet. If no packet, return "done".
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;
481 PXENV_STATUS_OUT_OF_RESOURCES;
482 return PXENV_EXIT_FAILURE;
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;
496 if ( memcmp ( media_header->dest, broadcast_mac,
497 sizeof(broadcast_mac) ) ) {
498 undi_isr->PktType = XMT_BROADCAST;
500 undi_isr->PktType = XMT_DESTADDR;
504 /* No break - fall through to IN_GET_NEXT */
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 */
511 /* Force an interrupt if there's a packet still
512 * waiting, since we only handle one packet per
515 if ( eth_poll ( 0 ) ) {
516 DBG ( " (RETRIGGER)" );
519 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
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;
529 undi_isr->Status = PXENV_STATUS_SUCCESS;
530 return PXENV_EXIT_SUCCESS;