aboutsummaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
authorMichael Brown <mcb30@etherboot.org>2007-07-02 20:05:58 +0100
committerMichael Brown <mcb30@etherboot.org>2007-07-02 20:05:58 +0100
commit332614a382c5e435e61509d130872e143670df56 (patch)
tree416f2a207e7b4e6b223296d257fe254d8c2fcb73 /src/interface
parente42eba4af49729c5535512d8b83ea6d18a8e3e95 (diff)
downloadipxe-332614a382c5e435e61509d130872e143670df56.tar.gz
Add untested support for UNDI transmit and receive.
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/pxe/pxe_undi.c219
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;