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 );
97 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
98 DBG ( "PXENV_UNDI_STARTUP" );
100 undi_startup->Status = PXENV_STATUS_SUCCESS;
101 return PXENV_EXIT_SUCCESS;
104 /* PXENV_UNDI_CLEANUP
108 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
109 DBG ( "PXENV_UNDI_CLEANUP" );
113 undi_cleanup->Status = PXENV_STATUS_SUCCESS;
114 return PXENV_EXIT_SUCCESS;
117 /* PXENV_UNDI_INITIALIZE
121 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
123 DBG ( "PXENV_UNDI_INITIALIZE" );
125 undi_initialize->Status = PXENV_STATUS_SUCCESS;
126 return PXENV_EXIT_SUCCESS;
129 /* PXENV_UNDI_RESET_ADAPTER
133 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
134 *undi_reset_adapter ) {
137 DBG ( "PXENV_UNDI_RESET_ADAPTER" );
140 if ( ( rc = pxe_netdev_open() ) != 0 ) {
141 undi_reset_adapter->Status = PXENV_STATUS ( rc );
142 return PXENV_EXIT_FAILURE;
145 undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
146 return PXENV_EXIT_SUCCESS;
149 /* PXENV_UNDI_SHUTDOWN
153 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
155 DBG ( "PXENV_UNDI_SHUTDOWN" );
159 undi_shutdown->Status = PXENV_STATUS_SUCCESS;
160 return PXENV_EXIT_SUCCESS;
167 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
170 DBG ( "PXENV_UNDI_OPEN" );
172 if ( ( rc = pxe_netdev_open() ) != 0 ) {
173 undi_open->Status = PXENV_STATUS ( rc );
174 return PXENV_EXIT_FAILURE;
177 undi_open->Status = PXENV_STATUS_SUCCESS;
178 return PXENV_EXIT_SUCCESS;
185 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
186 DBG ( "PXENV_UNDI_CLOSE" );
190 undi_close->Status = PXENV_STATUS_SUCCESS;
191 return PXENV_EXIT_SUCCESS;
194 /* PXENV_UNDI_TRANSMIT
198 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
200 struct s_PXENV_UNDI_TBD tbd;
201 struct DataBlk *datablk;
202 struct io_buffer *iobuf;
203 struct net_protocol *net_protocol;
204 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
205 char destaddr[MAX_LL_ADDR_LEN];
207 size_t ll_hlen = ll_protocol->ll_header_len;
212 DBG ( "PXENV_UNDI_TRANSMIT" );
214 /* Identify network-layer protocol */
215 switch ( undi_transmit->Protocol ) {
216 case P_IP: net_protocol = &ipv4_protocol; break;
217 case P_ARP: net_protocol = &arp_protocol; break;
218 case P_RARP: net_protocol = &rarp_protocol; break;
224 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
225 return PXENV_EXIT_FAILURE;
227 DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
229 /* Calculate total packet length */
230 copy_from_real ( &tbd, undi_transmit->TBD.segment,
231 undi_transmit->TBD.offset, sizeof ( tbd ) );
232 len = tbd.ImmedLength;
233 DBG ( " %d", tbd.ImmedLength );
234 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
235 datablk = &tbd.DataBlock[i];
236 len += datablk->TDDataLen;
237 DBG ( "+%d", datablk->TDDataLen );
240 /* Allocate and fill I/O buffer */
241 iobuf = alloc_iob ( ll_hlen + len );
243 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
244 return PXENV_EXIT_FAILURE;
246 iob_reserve ( iobuf, ll_hlen );
247 copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
248 tbd.Xmit.offset, tbd.ImmedLength );
249 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
250 datablk = &tbd.DataBlock[i];
251 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
252 datablk->TDDataPtr.segment,
253 datablk->TDDataPtr.offset,
254 datablk->TDDataLen );
257 /* Add link-layer header, if required to do so */
258 if ( net_protocol != NULL ) {
260 /* Calculate destination address */
261 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
262 copy_from_real ( destaddr,
263 undi_transmit->DestAddr.segment,
264 undi_transmit->DestAddr.offset,
265 ll_protocol->ll_addr_len );
269 ll_dest = ll_protocol->ll_broadcast;
272 /* Add link-layer header */
273 if ( ( rc = ll_protocol->push ( iobuf, ll_dest,
275 net_protocol->net_proto ))!=0){
277 undi_transmit->Status = PXENV_STATUS ( rc );
278 return PXENV_EXIT_FAILURE;
282 /* Transmit packet */
283 if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
284 undi_transmit->Status = PXENV_STATUS ( rc );
285 return PXENV_EXIT_FAILURE;
288 /* Flag transmission as in-progress */
291 undi_transmit->Status = PXENV_STATUS_SUCCESS;
292 return PXENV_EXIT_SUCCESS;
295 /* PXENV_UNDI_SET_MCAST_ADDRESS
297 * Status: stub (no PXE multicast support)
300 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
301 *undi_set_mcast_address ) {
302 DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
304 undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
305 return PXENV_EXIT_FAILURE;
308 /* PXENV_UNDI_SET_STATION_ADDRESS
313 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
314 *undi_set_station_address ) {
316 DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
318 /* If adapter is open, the change will have no effect; return
321 if ( pxe_netdev->state & NETDEV_OPEN ) {
322 undi_set_station_address->Status =
323 PXENV_STATUS_UNDI_INVALID_STATE;
324 return PXENV_EXIT_FAILURE;
327 /* Update MAC address */
328 memcpy ( pxe_netdev->ll_addr,
329 &undi_set_station_address->StationAddress,
330 pxe_netdev->ll_protocol->ll_addr_len );
332 undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
333 return PXENV_EXIT_SUCCESS;
336 /* PXENV_UNDI_SET_PACKET_FILTER
338 * Status: won't implement (would require driver API changes for no
342 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
343 *undi_set_packet_filter ) {
344 DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
346 undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
347 return PXENV_EXIT_FAILURE;
350 /* PXENV_UNDI_GET_INFORMATION
354 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
355 *undi_get_information ) {
356 struct device *dev = pxe_netdev->dev;
357 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
359 DBG ( "PXENV_UNDI_GET_INFORMATION" );
361 undi_get_information->BaseIo = dev->desc.ioaddr;
362 undi_get_information->IntNumber = dev->desc.irq;
363 /* Cheat: assume all cards can cope with this */
364 undi_get_information->MaxTranUnit = ETH_MAX_MTU;
365 undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
366 undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
367 /* Cheat: assume card is always configured with its permanent
368 * node address. This is a valid assumption within Etherboot
369 * at the time of writing.
371 memcpy ( &undi_get_information->CurrentNodeAddress,
373 sizeof ( undi_get_information->CurrentNodeAddress ) );
374 memcpy ( &undi_get_information->PermNodeAddress,
376 sizeof ( undi_get_information->PermNodeAddress ) );
377 undi_get_information->ROMAddress = 0;
378 /* nic.rom_info->rom_segment; */
379 /* We only provide the ability to receive or transmit a single
380 * packet at a time. This is a bootloader, not an OS.
382 undi_get_information->RxBufCt = 1;
383 undi_get_information->TxBufCt = 1;
385 undi_get_information->Status = PXENV_STATUS_SUCCESS;
386 return PXENV_EXIT_SUCCESS;
389 /* PXENV_UNDI_GET_STATISTICS
393 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
394 *undi_get_statistics ) {
395 DBG ( "PXENV_UNDI_GET_STATISTICS" );
397 undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
398 undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
399 undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
400 undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
402 undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
403 return PXENV_EXIT_SUCCESS;
406 /* PXENV_UNDI_CLEAR_STATISTICS
410 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
411 *undi_clear_statistics ) {
412 DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
414 memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
415 memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
417 undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
418 return PXENV_EXIT_SUCCESS;
421 /* PXENV_UNDI_INITIATE_DIAGS
423 * Status: won't implement (would require driver API changes for no
426 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
427 *undi_initiate_diags ) {
428 DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
430 undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
431 return PXENV_EXIT_FAILURE;
434 /* PXENV_UNDI_FORCE_INTERRUPT
436 * Status: won't implement (would require driver API changes for no
437 * perceptible benefit)
439 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
440 *undi_force_interrupt ) {
441 DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
443 undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
444 return PXENV_EXIT_FAILURE;
447 /* PXENV_UNDI_GET_MCAST_ADDRESS
449 * Status: stub (no PXE multicast support)
452 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
453 *undi_get_mcast_address ) {
454 DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
456 undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
457 return PXENV_EXIT_FAILURE;
460 /* PXENV_UNDI_GET_NIC_TYPE
464 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
465 *undi_get_nic_type ) {
466 struct device *dev = pxe_netdev->dev;
468 DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
470 memset ( &undi_get_nic_type->info, 0,
471 sizeof ( undi_get_nic_type->info ) );
473 switch ( dev->desc.bus_type ) {
475 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
477 undi_get_nic_type->NicType = PCI_NIC;
478 info->Vendor_ID = dev->desc.vendor;
479 info->Dev_ID = dev->desc.device;
480 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
481 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
482 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
483 info->BusDevFunc = dev->desc.location;
484 /* Cheat: remaining fields are probably unnecessary,
485 * and would require adding extra code to pci.c.
487 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
488 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
490 case BUS_TYPE_ISAPNP: {
491 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
493 undi_get_nic_type->NicType = PnP_NIC;
494 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
496 info->CardSelNum = dev->desc.location;
497 /* Cheat: remaining fields are probably unnecessary,
498 * and would require adding extra code to isapnp.c.
502 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
503 return PXENV_EXIT_FAILURE;
506 undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
507 return PXENV_EXIT_SUCCESS;
510 /* PXENV_UNDI_GET_IFACE_INFO
514 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
515 *undi_get_iface_info ) {
516 DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
518 /* Just hand back some info, doesn't really matter what it is.
519 * Most PXE stacks seem to take this approach.
521 snprintf ( ( char * ) undi_get_iface_info->IfaceType,
522 sizeof ( undi_get_iface_info->IfaceType ), "gPXE" );
523 undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
524 undi_get_iface_info->ServiceFlags = 0;
525 memset ( undi_get_iface_info->Reserved, 0,
526 sizeof(undi_get_iface_info->Reserved) );
528 undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
529 return PXENV_EXIT_SUCCESS;
532 /* PXENV_UNDI_GET_STATE
536 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
538 DBG ( "PXENV_UNDI_GET_STATE" );
540 undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
541 return PXENV_EXIT_FAILURE;
548 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
549 struct io_buffer *iobuf;
551 struct ll_protocol *ll_protocol;
553 const void *ll_source;
556 struct net_protocol *net_protocol;
557 unsigned int prottype;
560 DBG ( "PXENV_UNDI_ISR" );
562 /* Just in case some idiot actually looks at these fields when
563 * we weren't meant to fill them in...
565 undi_isr->BufferLength = 0;
566 undi_isr->FrameLength = 0;
567 undi_isr->FrameHeaderLength = 0;
568 undi_isr->ProtType = 0;
569 undi_isr->PktType = 0;
571 switch ( undi_isr->FuncFlag ) {
572 case PXENV_UNDI_ISR_IN_START :
575 /* Call poll(). This should acknowledge the device
576 * interrupt and queue up any received packet.
578 netdev_poll ( pxe_netdev );
580 /* Disable interrupts to avoid interrupt storm */
581 netdev_irq ( pxe_netdev, 0 );
583 /* Always say it was ours for the sake of simplicity */
584 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
586 case PXENV_UNDI_ISR_IN_PROCESS :
589 case PXENV_UNDI_ISR_IN_GET_NEXT :
592 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
593 * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
594 * they just sit in a tight polling loop merrily
595 * violating the PXE spec with repeated calls to
596 * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to
597 * cope with these out-of-spec clients.
599 netdev_poll ( pxe_netdev );
601 /* If we have not yet marked a TX as complete, and the
602 * netdev TX queue is empty, report the TX completion.
604 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
607 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
611 /* Remove first packet from netdev RX queue */
612 iobuf = netdev_rx_dequeue ( pxe_netdev );
615 /* No more packets remaining */
616 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
617 /* Re-enable interrupts */
618 netdev_irq ( pxe_netdev, 1 );
622 /* Copy packet to base memory buffer */
623 len = iob_len ( iobuf );
624 DBG ( " RX %zd", len );
625 if ( len > sizeof ( basemem_packet ) ) {
626 /* Should never happen */
627 len = sizeof ( basemem_packet );
629 memcpy ( basemem_packet, iobuf->data, len );
631 /* Strip link-layer header */
632 ll_protocol = pxe_netdev->ll_protocol;
633 if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest, &ll_source,
634 &net_proto ) ) != 0 ) {
635 /* Assume unknown net_proto and no ll_source */
639 ll_hlen = ( len - iob_len ( iobuf ) );
641 /* Determine network-layer protocol */
642 switch ( net_proto ) {
643 case htons ( ETH_P_IP ):
644 net_protocol = &ipv4_protocol;
647 case htons ( ETH_P_ARP ):
648 net_protocol = &arp_protocol;
651 case htons ( ETH_P_RARP ):
652 net_protocol = &rarp_protocol;
657 prottype = P_UNKNOWN;
660 DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
662 /* Fill in UNDI_ISR structure */
663 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
664 undi_isr->BufferLength = len;
665 undi_isr->FrameLength = len;
666 undi_isr->FrameHeaderLength = ll_hlen;
667 undi_isr->Frame.segment = rm_ds;
668 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
669 undi_isr->ProtType = prottype;
670 undi_isr->PktType = XMT_DESTADDR;
676 DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
678 /* Should never happen */
679 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
680 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
681 return PXENV_EXIT_FAILURE;
684 undi_isr->Status = PXENV_STATUS_SUCCESS;
685 return PXENV_EXIT_SUCCESS;