|
@@ -23,12 +23,18 @@
|
23
|
23
|
*/
|
24
|
24
|
|
25
|
25
|
#include <stdint.h>
|
|
26
|
+#include <stdio.h>
|
26
|
27
|
#include <string.h>
|
27
|
28
|
#include <byteswap.h>
|
|
29
|
+#include <basemem_packet.h>
|
28
|
30
|
#include <gpxe/netdevice.h>
|
|
31
|
+#include <gpxe/iobuf.h>
|
29
|
32
|
#include <gpxe/device.h>
|
30
|
33
|
#include <gpxe/pci.h>
|
31
|
34
|
#include <gpxe/if_ether.h>
|
|
35
|
+#include <gpxe/ip.h>
|
|
36
|
+#include <gpxe/arp.h>
|
|
37
|
+#include <gpxe/rarp.h>
|
32
|
38
|
#include <gpxe/shutdown.h>
|
33
|
39
|
#include "pxe.h"
|
34
|
40
|
|
|
@@ -128,61 +134,76 @@ PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
|
128
|
134
|
*/
|
129
|
135
|
PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
|
130
|
136
|
*undi_transmit ) {
|
131
|
|
- struct s_PXENV_UNDI_TBD *tbd;
|
132
|
|
- const char *dest;
|
133
|
|
- unsigned int type;
|
134
|
|
- unsigned int length;
|
135
|
|
- const char *data;
|
|
137
|
+ struct s_PXENV_UNDI_TBD tbd;
|
|
138
|
+ struct DataBlk *datablk;
|
|
139
|
+ struct io_buffer *iobuf;
|
|
140
|
+ struct net_protocol *net_protocol;
|
|
141
|
+ char destaddr[MAX_LL_ADDR_LEN];
|
|
142
|
+ const void *ll_dest;
|
|
143
|
+ size_t len;
|
|
144
|
+ unsigned int i;
|
|
145
|
+ int rc;
|
136
|
146
|
|
137
|
147
|
DBG ( "PXENV_UNDI_TRANSMIT" );
|
138
|
148
|
|
139
|
|
-#if 0
|
140
|
|
- /* We support only the "immediate" portion of the TBD. Who
|
141
|
|
- * knows what Intel's "engineers" were smoking when they came
|
142
|
|
- * up with the array of transmit data blocks...
|
143
|
|
- */
|
144
|
|
- tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
|
145
|
|
- if ( tbd->DataBlkCount > 0 ) {
|
|
149
|
+ /* Identify network-layer protocol */
|
|
150
|
+ switch ( undi_transmit->Protocol ) {
|
|
151
|
+ case P_IP: net_protocol = &ipv4_protocol; break;
|
|
152
|
+ case P_ARP: net_protocol = &arp_protocol; break;
|
|
153
|
+ case P_RARP: net_protocol = &rarp_protocol; break;
|
|
154
|
+ case P_UNKNOWN: net_protocol = NULL; break;
|
|
155
|
+ default:
|
146
|
156
|
undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
147
|
157
|
return PXENV_EXIT_FAILURE;
|
148
|
158
|
}
|
149
|
|
- data = SEGOFF16_TO_PTR ( tbd->Xmit );
|
150
|
|
- length = tbd->ImmedLength;
|
151
|
159
|
|
152
|
|
- /* If destination is broadcast, we need to supply the MAC address */
|
153
|
|
- if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
|
154
|
|
- dest = broadcast_mac;
|
155
|
|
- } else {
|
156
|
|
- dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
|
|
160
|
+ /* Calculate total packet length */
|
|
161
|
+ copy_from_real ( &tbd, undi_transmit->TBD.segment,
|
|
162
|
+ undi_transmit->TBD.offset, sizeof ( tbd ) );
|
|
163
|
+ len = tbd.ImmedLength;
|
|
164
|
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
|
|
165
|
+ datablk = &tbd.DataBlock[i];
|
|
166
|
+ len += datablk->TDDataLen;
|
157
|
167
|
}
|
158
|
168
|
|
159
|
|
- /* We can't properly support P_UNKNOWN without rewriting all
|
160
|
|
- * the driver transmit() methods, so we cheat: if P_UNKNOWN is
|
161
|
|
- * specified we rip the destination address and type out of
|
162
|
|
- * the pre-assembled packet, then skip over the header.
|
163
|
|
- */
|
164
|
|
- switch ( undi_transmit->Protocol ) {
|
165
|
|
- case P_IP: type = ETH_P_IP; break;
|
166
|
|
- case P_ARP: type = ETH_P_ARP; break;
|
167
|
|
- case P_RARP: type = ETH_P_RARP; break;
|
168
|
|
- case P_UNKNOWN:
|
169
|
|
- media_header = (media_header_t*)data;
|
170
|
|
- dest = media_header->dest;
|
171
|
|
- type = ntohs ( media_header->nstype );
|
172
|
|
- data += ETH_HLEN;
|
173
|
|
- length -= ETH_HLEN;
|
174
|
|
- break;
|
175
|
|
- default:
|
176
|
|
- undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
|
169
|
+ /* Allocate and fill I/O buffer */
|
|
170
|
+ iobuf = alloc_iob ( len );
|
|
171
|
+ if ( ! iobuf ) {
|
|
172
|
+ undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
|
177
|
173
|
return PXENV_EXIT_FAILURE;
|
178
|
174
|
}
|
|
175
|
+ copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
|
|
176
|
+ tbd.Xmit.offset, tbd.ImmedLength );
|
|
177
|
+ for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
|
|
178
|
+ datablk = &tbd.DataBlock[i];
|
|
179
|
+ copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
|
|
180
|
+ datablk->TDDataPtr.segment,
|
|
181
|
+ datablk->TDDataPtr.offset,
|
|
182
|
+ datablk->TDDataLen );
|
|
183
|
+ }
|
179
|
184
|
|
180
|
|
- /* Send the packet */
|
181
|
|
- eth_transmit ( dest, type, length, data );
|
182
|
|
-#endif
|
183
|
|
-
|
184
|
|
- undi_transmit->Status = PXENV_STATUS_SUCCESS;
|
185
|
|
- return PXENV_EXIT_SUCCESS;
|
|
185
|
+ /* Transmit packet */
|
|
186
|
+ if ( net_protocol == NULL ) {
|
|
187
|
+ /* Link-layer header already present */
|
|
188
|
+ rc = netdev_tx ( pxe_netdev, iobuf );
|
|
189
|
+ } else {
|
|
190
|
+ /* Calculate destination address */
|
|
191
|
+ if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
|
|
192
|
+ copy_from_real ( destaddr,
|
|
193
|
+ undi_transmit->DestAddr.segment,
|
|
194
|
+ undi_transmit->DestAddr.offset,
|
|
195
|
+ pxe_netdev->ll_protocol->ll_addr_len );
|
|
196
|
+ ll_dest = destaddr;
|
|
197
|
+ } else {
|
|
198
|
+ ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
|
|
199
|
+ }
|
|
200
|
+ rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest );
|
|
201
|
+ }
|
|
202
|
+
|
|
203
|
+#warning "TX completion?"
|
|
204
|
+
|
|
205
|
+ undi_transmit->Status = PXENV_STATUS ( rc );
|
|
206
|
+ return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE );
|
186
|
207
|
}
|
187
|
208
|
|
188
|
209
|
/* PXENV_UNDI_SET_MCAST_ADDRESS
|
|
@@ -405,16 +426,15 @@ PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
|
405
|
426
|
*undi_get_iface_info ) {
|
406
|
427
|
DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
|
407
|
428
|
|
408
|
|
-#if 0
|
409
|
429
|
/* Just hand back some info, doesn't really matter what it is.
|
410
|
430
|
* Most PXE stacks seem to take this approach.
|
411
|
431
|
*/
|
412
|
|
- sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
|
|
432
|
+ snprintf ( ( char * ) undi_get_iface_info->IfaceType,
|
|
433
|
+ sizeof ( undi_get_iface_info->IfaceType ), "Etherboot" );
|
413
|
434
|
undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
|
414
|
435
|
undi_get_iface_info->ServiceFlags = 0;
|
415
|
436
|
memset ( undi_get_iface_info->Reserved, 0,
|
416
|
437
|
sizeof(undi_get_iface_info->Reserved) );
|
417
|
|
-#endif
|
418
|
438
|
|
419
|
439
|
undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
|
420
|
440
|
return PXENV_EXIT_SUCCESS;
|
|
@@ -437,18 +457,11 @@ PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
|
437
|
457
|
* Status: working
|
438
|
458
|
*/
|
439
|
459
|
PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
|
|
460
|
+ struct io_buffer *iobuf;
|
|
461
|
+ size_t len;
|
|
462
|
+
|
440
|
463
|
DBG ( "PXENV_UNDI_ISR" );
|
441
|
464
|
|
442
|
|
-#if 0
|
443
|
|
- /* We can't call ENSURE_READY, because this could be being
|
444
|
|
- * called as part of an interrupt service routine. Instead,
|
445
|
|
- * we should simply die if we're not READY.
|
446
|
|
- */
|
447
|
|
- if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
|
448
|
|
- undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
|
449
|
|
- return PXENV_EXIT_FAILURE;
|
450
|
|
- }
|
451
|
|
-
|
452
|
465
|
/* Just in case some idiot actually looks at these fields when
|
453
|
466
|
* we weren't meant to fill them in...
|
454
|
467
|
*/
|
|
@@ -460,18 +473,14 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
|
460
|
473
|
|
461
|
474
|
switch ( undi_isr->FuncFlag ) {
|
462
|
475
|
case PXENV_UNDI_ISR_IN_START :
|
463
|
|
- /* Is there a packet waiting? If so, disable
|
464
|
|
- * interrupts on the NIC and return "it's ours". Do
|
465
|
|
- * *not* necessarily acknowledge the interrupt; this
|
466
|
|
- * can happen later when eth_poll(1) is called. As
|
467
|
|
- * long as the interrupt is masked off so that it
|
468
|
|
- * doesn't immediately retrigger the 8259A then all
|
469
|
|
- * should be well.
|
470
|
|
- */
|
471
|
476
|
DBG ( " START" );
|
472
|
|
- if ( eth_poll ( 0 ) ) {
|
|
477
|
+
|
|
478
|
+ /* Call poll(). This should acknowledge the device
|
|
479
|
+ * interrupt and queue up any received packet.
|
|
480
|
+ */
|
|
481
|
+ if ( netdev_poll ( pxe_netdev, -1U ) ) {
|
|
482
|
+ /* Packet waiting in queue */
|
473
|
483
|
DBG ( " OURS" );
|
474
|
|
- eth_irq ( DISABLE );
|
475
|
484
|
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
|
476
|
485
|
} else {
|
477
|
486
|
DBG ( " NOT_OURS" );
|
|
@@ -479,62 +488,48 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
|
479
|
488
|
}
|
480
|
489
|
break;
|
481
|
490
|
case PXENV_UNDI_ISR_IN_PROCESS :
|
482
|
|
- /* Call poll(), return packet. If no packet, return "done".
|
483
|
|
- */
|
484
|
|
- DBG ( " PROCESS" );
|
485
|
|
- if ( eth_poll ( 1 ) ) {
|
486
|
|
- DBG ( " RECEIVE %d", nic.packetlen );
|
487
|
|
- if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
|
488
|
|
- /* Should never happen */
|
489
|
|
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
490
|
|
- undi_isr->Status =
|
491
|
|
- PXENV_STATUS_OUT_OF_RESOURCES;
|
492
|
|
- return PXENV_EXIT_FAILURE;
|
493
|
|
- }
|
494
|
|
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
|
495
|
|
- undi_isr->BufferLength = nic.packetlen;
|
496
|
|
- undi_isr->FrameLength = nic.packetlen;
|
497
|
|
- undi_isr->FrameHeaderLength = ETH_HLEN;
|
498
|
|
- memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
|
499
|
|
- PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
|
500
|
|
- switch ( ntohs(media_header->nstype) ) {
|
501
|
|
- case ETH_P_IP: undi_isr->ProtType = P_IP; break;
|
502
|
|
- case ETH_P_ARP: undi_isr->ProtType = P_ARP; break;
|
503
|
|
- case ETH_P_RARP: undi_isr->ProtType = P_RARP; break;
|
504
|
|
- default : undi_isr->ProtType = P_UNKNOWN;
|
505
|
|
- }
|
506
|
|
- if ( memcmp ( media_header->dest, broadcast_mac,
|
507
|
|
- sizeof(broadcast_mac) ) ) {
|
508
|
|
- undi_isr->PktType = XMT_BROADCAST;
|
509
|
|
- } else {
|
510
|
|
- undi_isr->PktType = XMT_DESTADDR;
|
511
|
|
- }
|
|
491
|
+ case PXENV_UNDI_ISR_IN_GET_NEXT :
|
|
492
|
+ DBG ( " PROCESS/GET_NEXT" );
|
|
493
|
+
|
|
494
|
+ /* Remove first packet from netdev RX queue */
|
|
495
|
+ iobuf = netdev_rx_dequeue ( pxe_netdev );
|
|
496
|
+ if ( ! iobuf ) {
|
|
497
|
+ /* No more packets remaining */
|
|
498
|
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
512
|
499
|
break;
|
513
|
|
- } else {
|
514
|
|
- /* No break - fall through to IN_GET_NEXT */
|
515
|
500
|
}
|
516
|
|
- case PXENV_UNDI_ISR_IN_GET_NEXT :
|
517
|
|
- /* We only ever return one frame at a time */
|
518
|
|
- DBG ( " GET_NEXT DONE" );
|
519
|
|
- /* Re-enable interrupts */
|
520
|
|
- eth_irq ( ENABLE );
|
521
|
|
- /* Force an interrupt if there's a packet still
|
522
|
|
- * waiting, since we only handle one packet per
|
523
|
|
- * interrupt.
|
524
|
|
- */
|
525
|
|
- if ( eth_poll ( 0 ) ) {
|
526
|
|
- DBG ( " (RETRIGGER)" );
|
527
|
|
- eth_irq ( FORCE );
|
|
501
|
+
|
|
502
|
+ /* Copy packet to base memory buffer */
|
|
503
|
+ len = iob_len ( iobuf );
|
|
504
|
+ DBG ( " RECEIVE %zd", len );
|
|
505
|
+ if ( len > sizeof ( basemem_packet ) ) {
|
|
506
|
+ /* Should never happen */
|
|
507
|
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
|
508
|
+ undi_isr->Status = PXENV_STATUS_OUT_OF_RESOURCES;
|
|
509
|
+ return PXENV_EXIT_FAILURE;
|
528
|
510
|
}
|
529
|
|
- undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
|
511
|
+ memcpy ( basemem_packet, iobuf->data, len );
|
|
512
|
+
|
|
513
|
+ undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
|
|
514
|
+ undi_isr->BufferLength = len;
|
|
515
|
+ undi_isr->FrameLength = len;
|
|
516
|
+ undi_isr->FrameHeaderLength =
|
|
517
|
+ pxe_netdev->ll_protocol->ll_header_len;
|
|
518
|
+ undi_isr->Frame.segment = rm_ds;
|
|
519
|
+ undi_isr->Frame.offset =
|
|
520
|
+ ( ( unsigned ) & __from_data16 ( basemem_packet ) );
|
|
521
|
+ /* Probably ought to fill in packet type */
|
|
522
|
+ undi_isr->ProtType = P_UNKNOWN;
|
|
523
|
+ undi_isr->PktType = XMT_DESTADDR;
|
530
|
524
|
break;
|
531
|
525
|
default :
|
|
526
|
+ DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
|
|
527
|
+
|
532
|
528
|
/* Should never happen */
|
533
|
529
|
undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
|
534
|
530
|
undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
|
535
|
531
|
return PXENV_EXIT_FAILURE;
|
536
|
532
|
}
|
537
|
|
-#endif
|
538
|
533
|
|
539
|
534
|
undi_isr->Status = PXENV_STATUS_SUCCESS;
|
540
|
535
|
return PXENV_EXIT_SUCCESS;
|