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.
25 FILE_LICENCE ( GPL2_OR_LATER );
31 #include <basemem_packet.h>
32 #include <gpxe/netdevice.h>
33 #include <gpxe/iobuf.h>
34 #include <gpxe/device.h>
36 #include <gpxe/if_ether.h>
39 #include <gpxe/rarp.h>
43 * Count of outstanding transmitted packets
45 * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
46 * decremented each time that PXENV_UNDI_ISR is called with the TX
47 * queue empty, stopping when the count reaches zero. This allows us
48 * to provide a pessimistic approximation of TX completion events to
49 * the PXE NBP simply by monitoring the netdev's TX queue.
51 static int undi_tx_count = 0;
53 struct net_device *pxe_netdev = NULL;
56 * Set network device as current PXE network device
58 * @v netdev Network device, or NULL
60 void pxe_set_netdev ( struct net_device *netdev ) {
62 netdev_put ( pxe_netdev );
65 pxe_netdev = netdev_get ( netdev );
69 * Open PXE network device
71 * @ret rc Return status code
73 static int pxe_netdev_open ( void ) {
76 if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
79 netdev_irq ( pxe_netdev, 1 );
84 * Close PXE network device
87 static void pxe_netdev_close ( void ) {
88 netdev_irq ( pxe_netdev, 0 );
89 netdev_close ( pxe_netdev );
94 * Dump multicast address list
96 * @v mcast PXE multicast address list
98 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
99 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
102 for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
103 DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) );
107 /* PXENV_UNDI_STARTUP
111 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
112 DBG ( "PXENV_UNDI_STARTUP\n" );
114 undi_startup->Status = PXENV_STATUS_SUCCESS;
115 return PXENV_EXIT_SUCCESS;
118 /* PXENV_UNDI_CLEANUP
122 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
123 DBG ( "PXENV_UNDI_CLEANUP\n" );
127 undi_cleanup->Status = PXENV_STATUS_SUCCESS;
128 return PXENV_EXIT_SUCCESS;
131 /* PXENV_UNDI_INITIALIZE
135 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
137 DBG ( "PXENV_UNDI_INITIALIZE protocolini %08x\n",
138 undi_initialize->ProtocolIni );
140 undi_initialize->Status = PXENV_STATUS_SUCCESS;
141 return PXENV_EXIT_SUCCESS;
144 /* PXENV_UNDI_RESET_ADAPTER
148 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
149 *undi_reset_adapter ) {
152 DBG ( "PXENV_UNDI_RESET_ADAPTER" );
153 pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
157 if ( ( rc = pxe_netdev_open() ) != 0 ) {
158 DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n",
159 pxe_netdev->name, strerror ( rc ) );
160 undi_reset_adapter->Status = PXENV_STATUS ( rc );
161 return PXENV_EXIT_FAILURE;
164 undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
165 return PXENV_EXIT_SUCCESS;
168 /* PXENV_UNDI_SHUTDOWN
172 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
174 DBG ( "PXENV_UNDI_SHUTDOWN\n" );
178 undi_shutdown->Status = PXENV_STATUS_SUCCESS;
179 return PXENV_EXIT_SUCCESS;
186 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
189 DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x",
190 undi_open->OpenFlag, undi_open->PktFilter );
191 pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
194 if ( ( rc = pxe_netdev_open() ) != 0 ) {
195 DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n",
196 pxe_netdev->name, strerror ( rc ) );
197 undi_open->Status = PXENV_STATUS ( rc );
198 return PXENV_EXIT_FAILURE;
201 undi_open->Status = PXENV_STATUS_SUCCESS;
202 return PXENV_EXIT_SUCCESS;
209 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
210 DBG ( "PXENV_UNDI_CLOSE\n" );
214 undi_close->Status = PXENV_STATUS_SUCCESS;
215 return PXENV_EXIT_SUCCESS;
218 /* PXENV_UNDI_TRANSMIT
222 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
224 struct s_PXENV_UNDI_TBD tbd;
225 struct DataBlk *datablk;
226 struct io_buffer *iobuf;
227 struct net_protocol *net_protocol;
228 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
229 char destaddr[MAX_LL_ADDR_LEN];
231 size_t ll_hlen = ll_protocol->ll_header_len;
236 DBG2 ( "PXENV_UNDI_TRANSMIT" );
238 /* Forcibly enable interrupts at this point, to work around
239 * callers that never call PXENV_UNDI_OPEN before attempting
240 * to use the UNDI API.
242 netdev_irq ( pxe_netdev, 1 );
244 /* Identify network-layer protocol */
245 switch ( undi_transmit->Protocol ) {
246 case P_IP: net_protocol = &ipv4_protocol; break;
247 case P_ARP: net_protocol = &arp_protocol; break;
248 case P_RARP: net_protocol = &rarp_protocol; break;
254 DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol );
255 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
256 return PXENV_EXIT_FAILURE;
258 DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
260 /* Calculate total packet length */
261 copy_from_real ( &tbd, undi_transmit->TBD.segment,
262 undi_transmit->TBD.offset, sizeof ( tbd ) );
263 len = tbd.ImmedLength;
264 DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
266 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
267 datablk = &tbd.DataBlock[i];
268 len += datablk->TDDataLen;
269 DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment,
270 datablk->TDDataPtr.offset, datablk->TDDataLen );
273 /* Allocate and fill I/O buffer */
274 iobuf = alloc_iob ( ll_hlen + len );
276 DBG2 ( " could not allocate iobuf\n" );
277 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
278 return PXENV_EXIT_FAILURE;
280 iob_reserve ( iobuf, ll_hlen );
281 copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
282 tbd.Xmit.offset, tbd.ImmedLength );
283 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
284 datablk = &tbd.DataBlock[i];
285 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
286 datablk->TDDataPtr.segment,
287 datablk->TDDataPtr.offset,
288 datablk->TDDataLen );
291 /* Add link-layer header, if required to do so */
292 if ( net_protocol != NULL ) {
294 /* Calculate destination address */
295 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
296 copy_from_real ( destaddr,
297 undi_transmit->DestAddr.segment,
298 undi_transmit->DestAddr.offset,
299 ll_protocol->ll_addr_len );
301 DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) );
303 ll_dest = pxe_netdev->ll_broadcast;
307 /* Add link-layer header */
308 if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
310 net_protocol->net_proto ))!=0){
311 DBG2 ( " could not add link-layer header: %s\n",
314 undi_transmit->Status = PXENV_STATUS ( rc );
315 return PXENV_EXIT_FAILURE;
319 /* Flag transmission as in-progress. Do this before starting
320 * to transmit the packet, because the ISR may trigger before
321 * we return from netdev_tx().
325 /* Transmit packet */
327 if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
328 DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n",
331 undi_transmit->Status = PXENV_STATUS ( rc );
332 return PXENV_EXIT_FAILURE;
335 undi_transmit->Status = PXENV_STATUS_SUCCESS;
336 return PXENV_EXIT_SUCCESS;
339 /* PXENV_UNDI_SET_MCAST_ADDRESS
341 * Status: working (for NICs that support receive-all-multicast)
344 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
345 *undi_set_mcast_address ) {
346 DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
347 pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
350 undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
351 return PXENV_EXIT_SUCCESS;
354 /* PXENV_UNDI_SET_STATION_ADDRESS
359 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
360 *undi_set_station_address ) {
361 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
363 DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s",
364 ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
366 /* If adapter is open, the change will have no effect; return
369 if ( pxe_netdev->state & NETDEV_OPEN ) {
370 DBG ( " failed: netdev is open\n" );
371 undi_set_station_address->Status =
372 PXENV_STATUS_UNDI_INVALID_STATE;
373 return PXENV_EXIT_FAILURE;
376 /* Update MAC address */
377 memcpy ( pxe_netdev->ll_addr,
378 &undi_set_station_address->StationAddress,
379 ll_protocol->ll_addr_len );
382 undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
383 return PXENV_EXIT_SUCCESS;
386 /* PXENV_UNDI_SET_PACKET_FILTER
388 * Status: won't implement (would require driver API changes for no
392 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
393 *undi_set_packet_filter ) {
395 DBG ( "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
396 undi_set_packet_filter->filter );
398 /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
399 * driver refuses to load. (We ignore the filter value in the
400 * PXENV_UNDI_OPEN call anyway.)
402 undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
404 return PXENV_EXIT_SUCCESS;
407 /* PXENV_UNDI_GET_INFORMATION
411 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
412 *undi_get_information ) {
413 struct device *dev = pxe_netdev->dev;
414 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
416 DBG ( "PXENV_UNDI_GET_INFORMATION" );
418 undi_get_information->BaseIo = dev->desc.ioaddr;
419 undi_get_information->IntNumber = dev->desc.irq;
420 /* Cheat: assume all cards can cope with this */
421 undi_get_information->MaxTranUnit = ETH_MAX_MTU;
422 undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
423 undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
424 /* Cheat: assume card is always configured with its permanent
425 * node address. This is a valid assumption within Etherboot
426 * at the time of writing.
428 memcpy ( &undi_get_information->CurrentNodeAddress,
430 sizeof ( undi_get_information->CurrentNodeAddress ) );
431 memcpy ( &undi_get_information->PermNodeAddress,
433 sizeof ( undi_get_information->PermNodeAddress ) );
434 undi_get_information->ROMAddress = 0;
435 /* nic.rom_info->rom_segment; */
436 /* We only provide the ability to receive or transmit a single
437 * packet at a time. This is a bootloader, not an OS.
439 undi_get_information->RxBufCt = 1;
440 undi_get_information->TxBufCt = 1;
442 DBG ( " io %04x irq %d mtu %d %s %s\n",
443 undi_get_information->BaseIo, undi_get_information->IntNumber,
444 undi_get_information->MaxTranUnit, ll_protocol->name,
445 ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
446 undi_get_information->Status = PXENV_STATUS_SUCCESS;
447 return PXENV_EXIT_SUCCESS;
450 /* PXENV_UNDI_GET_STATISTICS
454 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
455 *undi_get_statistics ) {
456 DBG ( "PXENV_UNDI_GET_STATISTICS" );
458 undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
459 undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
460 undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
461 undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
463 DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n",
464 undi_get_statistics->XmtGoodFrames,
465 undi_get_statistics->RcvGoodFrames,
466 undi_get_statistics->RcvCRCErrors,
467 undi_get_statistics->RcvResourceErrors );
468 undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
469 return PXENV_EXIT_SUCCESS;
472 /* PXENV_UNDI_CLEAR_STATISTICS
476 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
477 *undi_clear_statistics ) {
478 DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" );
480 memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
481 memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
483 undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
484 return PXENV_EXIT_SUCCESS;
487 /* PXENV_UNDI_INITIATE_DIAGS
489 * Status: won't implement (would require driver API changes for no
492 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
493 *undi_initiate_diags ) {
494 DBG ( "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
496 undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
497 return PXENV_EXIT_FAILURE;
500 /* PXENV_UNDI_FORCE_INTERRUPT
502 * Status: won't implement (would require driver API changes for no
503 * perceptible benefit)
505 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
506 *undi_force_interrupt ) {
507 DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
509 undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
510 return PXENV_EXIT_FAILURE;
513 /* PXENV_UNDI_GET_MCAST_ADDRESS
518 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
519 *undi_get_mcast_address ) {
520 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
521 struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
524 DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) );
526 if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
527 undi_get_mcast_address->MediaAddr ))!=0){
528 DBG ( " failed: %s\n", strerror ( rc ) );
529 undi_get_mcast_address->Status = PXENV_STATUS ( rc );
530 return PXENV_EXIT_FAILURE;
533 ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
535 undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
536 return PXENV_EXIT_SUCCESS;
539 /* PXENV_UNDI_GET_NIC_TYPE
543 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
544 *undi_get_nic_type ) {
545 struct device *dev = pxe_netdev->dev;
547 DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
549 memset ( &undi_get_nic_type->info, 0,
550 sizeof ( undi_get_nic_type->info ) );
552 switch ( dev->desc.bus_type ) {
554 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
556 undi_get_nic_type->NicType = PCI_NIC;
557 info->Vendor_ID = dev->desc.vendor;
558 info->Dev_ID = dev->desc.device;
559 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
560 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
561 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
562 info->BusDevFunc = dev->desc.location;
563 /* Cheat: remaining fields are probably unnecessary,
564 * and would require adding extra code to pci.c.
566 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
567 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
568 DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x "
569 "rev %02x\n", PCI_BUS ( info->BusDevFunc ),
570 PCI_SLOT ( info->BusDevFunc ),
571 PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
572 info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
573 info->Base_Class, info->Sub_Class, info->Prog_Intf,
576 case BUS_TYPE_ISAPNP: {
577 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
579 undi_get_nic_type->NicType = PnP_NIC;
580 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
582 info->CardSelNum = dev->desc.location;
583 /* Cheat: remaining fields are probably unnecessary,
584 * and would require adding extra code to isapnp.c.
586 DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n",
587 info->CardSelNum, info->EISA_Dev_ID,
588 info->Base_Class, info->Sub_Class, info->Prog_Intf );
591 DBG ( " failed: unknown bus type\n" );
592 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
593 return PXENV_EXIT_FAILURE;
596 undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
597 return PXENV_EXIT_SUCCESS;
600 /* PXENV_UNDI_GET_IFACE_INFO
604 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
605 *undi_get_iface_info ) {
606 DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
608 /* Just hand back some info, doesn't really matter what it is.
609 * Most PXE stacks seem to take this approach.
611 snprintf ( ( char * ) undi_get_iface_info->IfaceType,
612 sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
613 undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
614 undi_get_iface_info->ServiceFlags =
615 ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
616 SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
617 SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ );
618 memset ( undi_get_iface_info->Reserved, 0,
619 sizeof(undi_get_iface_info->Reserved) );
621 DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType,
622 undi_get_iface_info->LinkSpeed,
623 undi_get_iface_info->ServiceFlags );
624 undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
625 return PXENV_EXIT_SUCCESS;
628 /* PXENV_UNDI_GET_STATE
632 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
634 DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" );
636 undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
637 return PXENV_EXIT_FAILURE;
644 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
645 struct io_buffer *iobuf;
647 struct ll_protocol *ll_protocol;
649 const void *ll_source;
652 struct net_protocol *net_protocol;
653 unsigned int prottype;
656 /* Use coloured debug, since UNDI ISR messages are likely to
657 * be interspersed amongst other UNDI messages.
659 DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
661 /* Just in case some idiot actually looks at these fields when
662 * we weren't meant to fill them in...
664 undi_isr->BufferLength = 0;
665 undi_isr->FrameLength = 0;
666 undi_isr->FrameHeaderLength = 0;
667 undi_isr->ProtType = 0;
668 undi_isr->PktType = 0;
670 switch ( undi_isr->FuncFlag ) {
671 case PXENV_UNDI_ISR_IN_START :
672 DBGC2 ( &pxenv_undi_isr, " START" );
674 /* Call poll(). This should acknowledge the device
675 * interrupt and queue up any received packet.
677 netdev_poll ( pxe_netdev );
679 /* Disable interrupts to avoid interrupt storm */
680 netdev_irq ( pxe_netdev, 0 );
682 /* Always say it was ours for the sake of simplicity */
683 DBGC2 ( &pxenv_undi_isr, " OURS" );
684 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
686 case PXENV_UNDI_ISR_IN_PROCESS :
687 case PXENV_UNDI_ISR_IN_GET_NEXT :
688 DBGC2 ( &pxenv_undi_isr, " %s",
689 ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
690 "PROCESS" : "GET_NEXT" ) );
692 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
693 * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
694 * they just sit in a tight polling loop merrily
695 * violating the PXE spec with repeated calls to
696 * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to
697 * cope with these out-of-spec clients.
699 netdev_poll ( pxe_netdev );
701 /* If we have not yet marked a TX as complete, and the
702 * netdev TX queue is empty, report the TX completion.
704 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
705 DBGC2 ( &pxenv_undi_isr, " TXC" );
707 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
711 /* Remove first packet from netdev RX queue */
712 iobuf = netdev_rx_dequeue ( pxe_netdev );
714 DBGC2 ( &pxenv_undi_isr, " DONE" );
715 /* No more packets remaining */
716 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
717 /* Re-enable interrupts */
718 netdev_irq ( pxe_netdev, 1 );
722 /* Copy packet to base memory buffer */
723 len = iob_len ( iobuf );
724 DBGC2 ( &pxenv_undi_isr, " RX" );
725 if ( len > sizeof ( basemem_packet ) ) {
726 /* Should never happen */
727 DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
728 len = sizeof ( basemem_packet );
730 memcpy ( basemem_packet, iobuf->data, len );
732 /* Strip link-layer header */
733 ll_protocol = pxe_netdev->ll_protocol;
734 if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
735 &ll_source, &net_proto )) !=0){
736 /* Assume unknown net_proto and no ll_source */
740 ll_hlen = ( len - iob_len ( iobuf ) );
742 /* Determine network-layer protocol */
743 switch ( net_proto ) {
744 case htons ( ETH_P_IP ):
745 net_protocol = &ipv4_protocol;
748 case htons ( ETH_P_ARP ):
749 net_protocol = &arp_protocol;
752 case htons ( ETH_P_RARP ):
753 net_protocol = &rarp_protocol;
758 prottype = P_UNKNOWN;
762 /* Fill in UNDI_ISR structure */
763 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
764 undi_isr->BufferLength = len;
765 undi_isr->FrameLength = len;
766 undi_isr->FrameHeaderLength = ll_hlen;
767 undi_isr->Frame.segment = rm_ds;
768 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
769 undi_isr->ProtType = prottype;
770 undi_isr->PktType = XMT_DESTADDR;
771 DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
772 undi_isr->Frame.segment, undi_isr->Frame.offset,
773 undi_isr->BufferLength, undi_isr->FrameLength,
774 ( net_protocol ? net_protocol->name : "RAW" ),
775 undi_isr->FrameHeaderLength );
781 DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
782 undi_isr->FuncFlag );
784 /* Should never happen */
785 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
786 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
787 return PXENV_EXIT_FAILURE;
790 DBGC2 ( &pxenv_undi_isr, "\n" );
791 undi_isr->Status = PXENV_STATUS_SUCCESS;
792 return PXENV_EXIT_SUCCESS;