diff options
author | Michael Brown <mcb30@etherboot.org> | 2007-07-02 20:05:58 +0100 |
---|---|---|
committer | Michael Brown <mcb30@etherboot.org> | 2007-07-02 20:05:58 +0100 |
commit | 332614a382c5e435e61509d130872e143670df56 (patch) | |
tree | 416f2a207e7b4e6b223296d257fe254d8c2fcb73 /src/interface | |
parent | e42eba4af49729c5535512d8b83ea6d18a8e3e95 (diff) | |
download | ipxe-332614a382c5e435e61509d130872e143670df56.tar.gz |
Add untested support for UNDI transmit and receive.
Diffstat (limited to 'src/interface')
-rw-r--r-- | src/interface/pxe/pxe_undi.c | 219 |
1 files changed, 107 insertions, 112 deletions
diff --git a/src/interface/pxe/pxe_undi.c b/src/interface/pxe/pxe_undi.c index e108764c..9488d9f4 100644 --- a/src/interface/pxe/pxe_undi.c +++ b/src/interface/pxe/pxe_undi.c @@ -23,12 +23,18 @@ */ #include <stdint.h> +#include <stdio.h> #include <string.h> #include <byteswap.h> +#include <basemem_packet.h> #include <gpxe/netdevice.h> +#include <gpxe/iobuf.h> #include <gpxe/device.h> #include <gpxe/pci.h> #include <gpxe/if_ether.h> +#include <gpxe/ip.h> +#include <gpxe/arp.h> +#include <gpxe/rarp.h> #include <gpxe/shutdown.h> #include "pxe.h" @@ -128,61 +134,76 @@ PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) { */ PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT *undi_transmit ) { - struct s_PXENV_UNDI_TBD *tbd; - const char *dest; - unsigned int type; - unsigned int length; - const char *data; + struct s_PXENV_UNDI_TBD tbd; + struct DataBlk *datablk; + struct io_buffer *iobuf; + struct net_protocol *net_protocol; + char destaddr[MAX_LL_ADDR_LEN]; + const void *ll_dest; + size_t len; + unsigned int i; + int rc; DBG ( "PXENV_UNDI_TRANSMIT" ); -#if 0 - /* We support only the "immediate" portion of the TBD. Who - * knows what Intel's "engineers" were smoking when they came - * up with the array of transmit data blocks... - */ - tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD ); - if ( tbd->DataBlkCount > 0 ) { + /* Identify network-layer protocol */ + switch ( undi_transmit->Protocol ) { + case P_IP: net_protocol = &ipv4_protocol; break; + case P_ARP: net_protocol = &arp_protocol; break; + case P_RARP: net_protocol = &rarp_protocol; break; + case P_UNKNOWN: net_protocol = NULL; break; + default: undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; return PXENV_EXIT_FAILURE; } - data = SEGOFF16_TO_PTR ( tbd->Xmit ); - length = tbd->ImmedLength; - /* If destination is broadcast, we need to supply the MAC address */ - if ( undi_transmit->XmitFlag == XMT_BROADCAST ) { - dest = broadcast_mac; - } else { - dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr ); + /* Calculate total packet length */ + copy_from_real ( &tbd, undi_transmit->TBD.segment, + undi_transmit->TBD.offset, sizeof ( tbd ) ); + len = tbd.ImmedLength; + for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { + datablk = &tbd.DataBlock[i]; + len += datablk->TDDataLen; } - /* We can't properly support P_UNKNOWN without rewriting all - * the driver transmit() methods, so we cheat: if P_UNKNOWN is - * specified we rip the destination address and type out of - * the pre-assembled packet, then skip over the header. - */ - switch ( undi_transmit->Protocol ) { - case P_IP: type = ETH_P_IP; break; - case P_ARP: type = ETH_P_ARP; break; - case P_RARP: type = ETH_P_RARP; break; - case P_UNKNOWN: - media_header = (media_header_t*)data; - dest = media_header->dest; - type = ntohs ( media_header->nstype ); - data += ETH_HLEN; - length -= ETH_HLEN; - break; - default: - undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; + /* Allocate and fill I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES; return PXENV_EXIT_FAILURE; } + copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment, + tbd.Xmit.offset, tbd.ImmedLength ); + for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { + datablk = &tbd.DataBlock[i]; + copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ), + datablk->TDDataPtr.segment, + datablk->TDDataPtr.offset, + datablk->TDDataLen ); + } - /* Send the packet */ - eth_transmit ( dest, type, length, data ); -#endif - - undi_transmit->Status = PXENV_STATUS_SUCCESS; - return PXENV_EXIT_SUCCESS; + /* Transmit packet */ + if ( net_protocol == NULL ) { + /* Link-layer header already present */ + rc = netdev_tx ( pxe_netdev, iobuf ); + } else { + /* Calculate destination address */ + if ( undi_transmit->XmitFlag == XMT_DESTADDR ) { + copy_from_real ( destaddr, + undi_transmit->DestAddr.segment, + undi_transmit->DestAddr.offset, + pxe_netdev->ll_protocol->ll_addr_len ); + ll_dest = destaddr; + } else { + ll_dest = pxe_netdev->ll_protocol->ll_broadcast; + } + rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest ); + } + +#warning "TX completion?" + + undi_transmit->Status = PXENV_STATUS ( rc ); + return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE ); } /* PXENV_UNDI_SET_MCAST_ADDRESS @@ -405,16 +426,15 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO *undi_get_iface_info ) { DBG ( "PXENV_UNDI_GET_IFACE_INFO" ); -#if 0 /* Just hand back some info, doesn't really matter what it is. * Most PXE stacks seem to take this approach. */ - sprintf ( undi_get_iface_info->IfaceType, "Etherboot" ); + snprintf ( ( char * ) undi_get_iface_info->IfaceType, + sizeof ( undi_get_iface_info->IfaceType ), "Etherboot" ); undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */ undi_get_iface_info->ServiceFlags = 0; memset ( undi_get_iface_info->Reserved, 0, sizeof(undi_get_iface_info->Reserved) ); -#endif undi_get_iface_info->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; @@ -437,18 +457,11 @@ PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE * Status: working */ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { + struct io_buffer *iobuf; + size_t len; + DBG ( "PXENV_UNDI_ISR" ); -#if 0 - /* We can't call ENSURE_READY, because this could be being - * called as part of an interrupt service routine. Instead, - * we should simply die if we're not READY. - */ - if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) { - undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE; - return PXENV_EXIT_FAILURE; - } - /* Just in case some idiot actually looks at these fields when * we weren't meant to fill them in... */ @@ -460,18 +473,14 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { switch ( undi_isr->FuncFlag ) { case PXENV_UNDI_ISR_IN_START : - /* Is there a packet waiting? If so, disable - * interrupts on the NIC and return "it's ours". Do - * *not* necessarily acknowledge the interrupt; this - * can happen later when eth_poll(1) is called. As - * long as the interrupt is masked off so that it - * doesn't immediately retrigger the 8259A then all - * should be well. - */ DBG ( " START" ); - if ( eth_poll ( 0 ) ) { + + /* Call poll(). This should acknowledge the device + * interrupt and queue up any received packet. + */ + if ( netdev_poll ( pxe_netdev, -1U ) ) { + /* Packet waiting in queue */ DBG ( " OURS" ); - eth_irq ( DISABLE ); undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS; } else { DBG ( " NOT_OURS" ); @@ -479,62 +488,48 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { } break; case PXENV_UNDI_ISR_IN_PROCESS : - /* Call poll(), return packet. If no packet, return "done". - */ - DBG ( " PROCESS" ); - if ( eth_poll ( 1 ) ) { - DBG ( " RECEIVE %d", nic.packetlen ); - if ( nic.packetlen > sizeof(pxe_stack->packet) ) { - /* Should never happen */ - undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; - undi_isr->Status = - PXENV_STATUS_OUT_OF_RESOURCES; - return PXENV_EXIT_FAILURE; - } - undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; - undi_isr->BufferLength = nic.packetlen; - undi_isr->FrameLength = nic.packetlen; - undi_isr->FrameHeaderLength = ETH_HLEN; - memcpy ( pxe_stack->packet, nic.packet, nic.packetlen); - PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame ); - switch ( ntohs(media_header->nstype) ) { - case ETH_P_IP: undi_isr->ProtType = P_IP; break; - case ETH_P_ARP: undi_isr->ProtType = P_ARP; break; - case ETH_P_RARP: undi_isr->ProtType = P_RARP; break; - default : undi_isr->ProtType = P_UNKNOWN; - } - if ( memcmp ( media_header->dest, broadcast_mac, - sizeof(broadcast_mac) ) ) { - undi_isr->PktType = XMT_BROADCAST; - } else { - undi_isr->PktType = XMT_DESTADDR; - } + case PXENV_UNDI_ISR_IN_GET_NEXT : + DBG ( " PROCESS/GET_NEXT" ); + + /* Remove first packet from netdev RX queue */ + iobuf = netdev_rx_dequeue ( pxe_netdev ); + if ( ! iobuf ) { + /* No more packets remaining */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; break; - } else { - /* No break - fall through to IN_GET_NEXT */ } - case PXENV_UNDI_ISR_IN_GET_NEXT : - /* We only ever return one frame at a time */ - DBG ( " GET_NEXT DONE" ); - /* Re-enable interrupts */ - eth_irq ( ENABLE ); - /* Force an interrupt if there's a packet still - * waiting, since we only handle one packet per - * interrupt. - */ - if ( eth_poll ( 0 ) ) { - DBG ( " (RETRIGGER)" ); - eth_irq ( FORCE ); + + /* Copy packet to base memory buffer */ + len = iob_len ( iobuf ); + DBG ( " RECEIVE %zd", len ); + if ( len > sizeof ( basemem_packet ) ) { + /* Should never happen */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + undi_isr->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; } - undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + memcpy ( basemem_packet, iobuf->data, len ); + + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; + undi_isr->BufferLength = len; + undi_isr->FrameLength = len; + undi_isr->FrameHeaderLength = + pxe_netdev->ll_protocol->ll_header_len; + undi_isr->Frame.segment = rm_ds; + undi_isr->Frame.offset = + ( ( unsigned ) & __from_data16 ( basemem_packet ) ); + /* Probably ought to fill in packet type */ + undi_isr->ProtType = P_UNKNOWN; + undi_isr->PktType = XMT_DESTADDR; break; default : + DBG ( " INVALID(%04x)", undi_isr->FuncFlag ); + /* Should never happen */ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; return PXENV_EXIT_FAILURE; } -#endif undi_isr->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; |