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.
29 #include <basemem_packet.h>
30 #include <gpxe/netdevice.h>
31 #include <gpxe/iobuf.h>
32 #include <gpxe/device.h>
34 #include <gpxe/if_ether.h>
37 #include <gpxe/rarp.h>
41 * Count of outstanding transmitted packets
43 * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
44 * decremented each time that PXENV_UNDI_ISR is called with the TX
45 * queue empty, stopping when the count reaches zero. This allows us
46 * to provide a pessimistic approximation of TX completion events to
47 * the PXE NBP simply by monitoring the netdev's TX queue.
49 static int undi_tx_count = 0;
52 * Open PXE network device
54 * @ret rc Return status code
56 static int pxe_netdev_open ( void ) {
57 return netdev_open ( pxe_netdev );
61 * Close PXE network device
64 static void pxe_netdev_close ( void ) {
65 netdev_close ( pxe_netdev );
73 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
74 DBG ( "PXENV_UNDI_STARTUP" );
76 undi_startup->Status = PXENV_STATUS_SUCCESS;
77 return PXENV_EXIT_SUCCESS;
84 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
85 DBG ( "PXENV_UNDI_CLEANUP" );
89 undi_cleanup->Status = PXENV_STATUS_SUCCESS;
90 return PXENV_EXIT_SUCCESS;
93 /* PXENV_UNDI_INITIALIZE
97 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
99 DBG ( "PXENV_UNDI_INITIALIZE" );
101 undi_initialize->Status = PXENV_STATUS_SUCCESS;
102 return PXENV_EXIT_SUCCESS;
105 /* PXENV_UNDI_RESET_ADAPTER
109 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
110 *undi_reset_adapter ) {
113 DBG ( "PXENV_UNDI_RESET_ADAPTER" );
116 if ( ( rc = pxe_netdev_open() ) != 0 ) {
117 undi_reset_adapter->Status = PXENV_STATUS ( rc );
118 return PXENV_EXIT_FAILURE;
121 undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
122 return PXENV_EXIT_SUCCESS;
125 /* PXENV_UNDI_SHUTDOWN
129 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
131 DBG ( "PXENV_UNDI_SHUTDOWN" );
135 undi_shutdown->Status = PXENV_STATUS_SUCCESS;
136 return PXENV_EXIT_SUCCESS;
143 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
146 DBG ( "PXENV_UNDI_OPEN" );
148 if ( ( rc = pxe_netdev_open() ) != 0 ) {
149 undi_open->Status = PXENV_STATUS ( rc );
150 return PXENV_EXIT_FAILURE;
153 undi_open->Status = PXENV_STATUS_SUCCESS;
154 return PXENV_EXIT_SUCCESS;
161 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
162 DBG ( "PXENV_UNDI_CLOSE" );
166 undi_close->Status = PXENV_STATUS_SUCCESS;
167 return PXENV_EXIT_SUCCESS;
170 /* PXENV_UNDI_TRANSMIT
174 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
176 struct s_PXENV_UNDI_TBD tbd;
177 struct DataBlk *datablk;
178 struct io_buffer *iobuf;
179 struct net_protocol *net_protocol;
180 char destaddr[MAX_LL_ADDR_LEN];
182 size_t ll_hlen = pxe_netdev->ll_protocol->ll_header_len;
187 DBG ( "PXENV_UNDI_TRANSMIT" );
189 /* Identify network-layer protocol */
190 switch ( undi_transmit->Protocol ) {
191 case P_IP: net_protocol = &ipv4_protocol; break;
192 case P_ARP: net_protocol = &arp_protocol; break;
193 case P_RARP: net_protocol = &rarp_protocol; break;
199 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
200 return PXENV_EXIT_FAILURE;
202 DBG ( " %s", ( net_protocol ? net_protocol->name : "UNKNOWN" ) );
204 /* Calculate total packet length */
205 copy_from_real ( &tbd, undi_transmit->TBD.segment,
206 undi_transmit->TBD.offset, sizeof ( tbd ) );
207 len = tbd.ImmedLength;
208 DBG ( " %zd", tbd.ImmedLength );
209 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
210 datablk = &tbd.DataBlock[i];
211 len += datablk->TDDataLen;
212 DBG ( "+%zd", datablk->TDDataLen );
215 /* Allocate and fill I/O buffer */
216 iobuf = alloc_iob ( ll_hlen + len );
218 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
219 return PXENV_EXIT_FAILURE;
221 iob_reserve ( iobuf, ll_hlen );
222 copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
223 tbd.Xmit.offset, tbd.ImmedLength );
224 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
225 datablk = &tbd.DataBlock[i];
226 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
227 datablk->TDDataPtr.segment,
228 datablk->TDDataPtr.offset,
229 datablk->TDDataLen );
232 /* Transmit packet */
233 if ( net_protocol == NULL ) {
234 /* Link-layer header already present */
235 rc = netdev_tx ( pxe_netdev, iobuf );
237 /* Calculate destination address */
238 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
239 copy_from_real ( destaddr,
240 undi_transmit->DestAddr.segment,
241 undi_transmit->DestAddr.offset,
242 pxe_netdev->ll_protocol->ll_addr_len );
245 ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
247 rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest );
250 /* Flag transmission as in-progress */
253 undi_transmit->Status = PXENV_STATUS ( rc );
254 return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE );
257 /* PXENV_UNDI_SET_MCAST_ADDRESS
259 * Status: stub (no PXE multicast support)
262 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
263 *undi_set_mcast_address ) {
264 DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
266 undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
267 return PXENV_EXIT_FAILURE;
270 /* PXENV_UNDI_SET_STATION_ADDRESS
275 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
276 *undi_set_station_address ) {
278 DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
280 /* If adapter is open, the change will have no effect; return
283 if ( pxe_netdev->state & NETDEV_OPEN ) {
284 undi_set_station_address->Status =
285 PXENV_STATUS_UNDI_INVALID_STATE;
286 return PXENV_EXIT_FAILURE;
289 /* Update MAC address */
290 memcpy ( pxe_netdev->ll_addr,
291 &undi_set_station_address->StationAddress,
292 pxe_netdev->ll_protocol->ll_addr_len );
294 undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
295 return PXENV_EXIT_SUCCESS;
298 /* PXENV_UNDI_SET_PACKET_FILTER
300 * Status: won't implement (would require driver API changes for no
304 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
305 *undi_set_packet_filter ) {
306 DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
308 undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
309 return PXENV_EXIT_FAILURE;
312 /* PXENV_UNDI_GET_INFORMATION
316 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
317 *undi_get_information ) {
318 struct device *dev = pxe_netdev->dev;
319 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
321 DBG ( "PXENV_UNDI_GET_INFORMATION" );
323 undi_get_information->BaseIo = dev->desc.ioaddr;
324 undi_get_information->IntNumber = dev->desc.irq;
325 /* Cheat: assume all cards can cope with this */
326 undi_get_information->MaxTranUnit = ETH_MAX_MTU;
327 undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
328 undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
329 /* Cheat: assume card is always configured with its permanent
330 * node address. This is a valid assumption within Etherboot
331 * at the time of writing.
333 memcpy ( &undi_get_information->CurrentNodeAddress,
335 sizeof ( undi_get_information->CurrentNodeAddress ) );
336 memcpy ( &undi_get_information->PermNodeAddress,
338 sizeof ( undi_get_information->PermNodeAddress ) );
339 undi_get_information->ROMAddress = 0;
340 /* nic.rom_info->rom_segment; */
341 /* We only provide the ability to receive or transmit a single
342 * packet at a time. This is a bootloader, not an OS.
344 undi_get_information->RxBufCt = 1;
345 undi_get_information->TxBufCt = 1;
347 undi_get_information->Status = PXENV_STATUS_SUCCESS;
348 return PXENV_EXIT_SUCCESS;
351 /* PXENV_UNDI_GET_STATISTICS
355 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
356 *undi_get_statistics ) {
357 DBG ( "PXENV_UNDI_GET_STATISTICS" );
359 undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_count;
360 undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_count;
361 undi_get_statistics->RcvCRCErrors = 0;
362 undi_get_statistics->RcvResourceErrors = 0;
364 undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
365 return PXENV_EXIT_SUCCESS;
368 /* PXENV_UNDI_CLEAR_STATISTICS
372 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
373 *undi_clear_statistics ) {
374 DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
376 memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) );
378 undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
379 return PXENV_EXIT_SUCCESS;
382 /* PXENV_UNDI_INITIATE_DIAGS
384 * Status: won't implement (would require driver API changes for no
387 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
388 *undi_initiate_diags ) {
389 DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
391 undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
392 return PXENV_EXIT_FAILURE;
395 /* PXENV_UNDI_FORCE_INTERRUPT
399 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
400 *undi_force_interrupt ) {
401 DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
407 undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
408 return PXENV_EXIT_SUCCESS;
411 /* PXENV_UNDI_GET_MCAST_ADDRESS
413 * Status: stub (no PXE multicast support)
416 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
417 *undi_get_mcast_address ) {
418 DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
420 undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
421 return PXENV_EXIT_FAILURE;
424 /* PXENV_UNDI_GET_NIC_TYPE
428 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
429 *undi_get_nic_type ) {
430 struct device *dev = pxe_netdev->dev;
432 DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
434 memset ( &undi_get_nic_type->info, 0,
435 sizeof ( undi_get_nic_type->info ) );
437 switch ( dev->desc.bus_type ) {
439 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
441 undi_get_nic_type->NicType = PCI_NIC;
442 info->Vendor_ID = dev->desc.vendor;
443 info->Dev_ID = dev->desc.device;
444 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
445 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
446 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
447 info->BusDevFunc = dev->desc.location;
448 /* Cheat: remaining fields are probably unnecessary,
449 * and would require adding extra code to pci.c.
451 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
452 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
454 case BUS_TYPE_ISAPNP: {
455 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
457 undi_get_nic_type->NicType = PnP_NIC;
458 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
460 info->CardSelNum = dev->desc.location;
461 /* Cheat: remaining fields are probably unnecessary,
462 * and would require adding extra code to isapnp.c.
466 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
467 return PXENV_EXIT_FAILURE;
470 undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
471 return PXENV_EXIT_SUCCESS;
474 /* PXENV_UNDI_GET_IFACE_INFO
478 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
479 *undi_get_iface_info ) {
480 DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
482 /* Just hand back some info, doesn't really matter what it is.
483 * Most PXE stacks seem to take this approach.
485 snprintf ( ( char * ) undi_get_iface_info->IfaceType,
486 sizeof ( undi_get_iface_info->IfaceType ), "Etherboot" );
487 undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
488 undi_get_iface_info->ServiceFlags = 0;
489 memset ( undi_get_iface_info->Reserved, 0,
490 sizeof(undi_get_iface_info->Reserved) );
492 undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
493 return PXENV_EXIT_SUCCESS;
496 /* PXENV_UNDI_GET_STATE
500 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
502 DBG ( "PXENV_UNDI_GET_STATE" );
504 undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
505 return PXENV_EXIT_FAILURE;
512 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
513 struct io_buffer *iobuf;
516 DBG ( "PXENV_UNDI_ISR" );
518 /* Just in case some idiot actually looks at these fields when
519 * we weren't meant to fill them in...
521 undi_isr->BufferLength = 0;
522 undi_isr->FrameLength = 0;
523 undi_isr->FrameHeaderLength = 0;
524 undi_isr->ProtType = 0;
525 undi_isr->PktType = 0;
527 switch ( undi_isr->FuncFlag ) {
528 case PXENV_UNDI_ISR_IN_START :
531 /* Call poll(). This should acknowledge the device
532 * interrupt and queue up any received packet.
534 if ( netdev_poll ( pxe_netdev, -1U ) ) {
535 /* Packet waiting in queue */
537 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
540 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
543 case PXENV_UNDI_ISR_IN_PROCESS :
544 case PXENV_UNDI_ISR_IN_GET_NEXT :
545 DBG ( " PROCESS/GET_NEXT" );
547 /* If we have not yet marked a TX as complete, and the
548 * netdev TX queue is empty, report the TX completion.
550 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
552 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
556 /* Remove first packet from netdev RX queue */
557 iobuf = netdev_rx_dequeue ( pxe_netdev );
559 /* No more packets remaining */
560 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
564 /* Copy packet to base memory buffer */
565 len = iob_len ( iobuf );
566 DBG ( " RECEIVE %zd", len );
567 if ( len > sizeof ( basemem_packet ) ) {
568 /* Should never happen */
569 len = sizeof ( basemem_packet );
571 memcpy ( basemem_packet, iobuf->data, len );
573 /* Fill in UNDI_ISR structure */
574 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
575 undi_isr->BufferLength = len;
576 undi_isr->FrameLength = len;
577 undi_isr->FrameHeaderLength =
578 pxe_netdev->ll_protocol->ll_header_len;
579 undi_isr->Frame.segment = rm_ds;
580 undi_isr->Frame.offset =
581 ( ( unsigned ) & __from_data16 ( basemem_packet ) );
582 /* Probably ought to fill in packet type */
583 undi_isr->ProtType = P_UNKNOWN;
584 undi_isr->PktType = XMT_DESTADDR;
590 DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
592 /* Should never happen */
593 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
594 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
595 return PXENV_EXIT_FAILURE;
598 undi_isr->Status = PXENV_STATUS_SUCCESS;
599 return PXENV_EXIT_SUCCESS;