|
@@ -97,29 +97,44 @@ static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) {
|
97
|
97
|
mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
|
98
|
98
|
}
|
99
|
99
|
|
|
100
|
+/**
|
|
101
|
+ * Flush transmit ring and receive queue
|
|
102
|
+ *
|
|
103
|
+ * @v snpdev SNP device
|
|
104
|
+ */
|
|
105
|
+static void efi_snp_flush ( struct efi_snp_device *snpdev ) {
|
|
106
|
+ struct io_buffer *iobuf;
|
|
107
|
+ struct io_buffer *tmp;
|
|
108
|
+
|
|
109
|
+ /* Reset transmit completion ring */
|
|
110
|
+ snpdev->tx_prod = 0;
|
|
111
|
+ snpdev->tx_cons = 0;
|
|
112
|
+
|
|
113
|
+ /* Discard any queued receive buffers */
|
|
114
|
+ list_for_each_entry_safe ( iobuf, tmp, &snpdev->rx, list ) {
|
|
115
|
+ list_del ( &iobuf->list );
|
|
116
|
+ free_iob ( iobuf );
|
|
117
|
+ }
|
|
118
|
+}
|
|
119
|
+
|
100
|
120
|
/**
|
101
|
121
|
* Poll net device and count received packets
|
102
|
122
|
*
|
103
|
123
|
* @v snpdev SNP device
|
104
|
124
|
*/
|
105
|
125
|
static void efi_snp_poll ( struct efi_snp_device *snpdev ) {
|
|
126
|
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
106
|
127
|
struct io_buffer *iobuf;
|
107
|
|
- unsigned int before = 0;
|
108
|
|
- unsigned int after = 0;
|
109
|
|
- unsigned int arrived;
|
110
|
128
|
|
111
|
|
- /* We have to report packet arrivals, and this is the easiest
|
112
|
|
- * way to fake it.
|
113
|
|
- */
|
114
|
|
- list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
|
115
|
|
- before++;
|
|
129
|
+ /* Poll network device */
|
116
|
130
|
netdev_poll ( snpdev->netdev );
|
117
|
|
- list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
|
118
|
|
- after++;
|
119
|
|
- arrived = ( after - before );
|
120
|
131
|
|
121
|
|
- snpdev->rx_count_interrupts += arrived;
|
122
|
|
- snpdev->rx_count_events += arrived;
|
|
132
|
+ /* Retrieve any received packets */
|
|
133
|
+ while ( ( iobuf = netdev_rx_dequeue ( snpdev->netdev ) ) ) {
|
|
134
|
+ list_add_tail ( &iobuf->list, &snpdev->rx );
|
|
135
|
+ snpdev->interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
|
|
136
|
+ bs->SignalEvent ( &snpdev->snp.WaitForPacket );
|
|
137
|
+ }
|
123
|
138
|
}
|
124
|
139
|
|
125
|
140
|
/**
|
|
@@ -221,6 +236,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
|
221
|
236
|
|
222
|
237
|
netdev_close ( snpdev->netdev );
|
223
|
238
|
efi_snp_set_state ( snpdev );
|
|
239
|
+ efi_snp_flush ( snpdev );
|
224
|
240
|
|
225
|
241
|
if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
|
226
|
242
|
DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
|
|
@@ -251,6 +267,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
|
251
|
267
|
|
252
|
268
|
netdev_close ( snpdev->netdev );
|
253
|
269
|
efi_snp_set_state ( snpdev );
|
|
270
|
+ efi_snp_flush ( snpdev );
|
254
|
271
|
|
255
|
272
|
return 0;
|
256
|
273
|
}
|
|
@@ -446,20 +463,22 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
|
446
|
463
|
*
|
447
|
464
|
* @v snp SNP interface
|
448
|
465
|
* @v interrupts Interrupt status, or NULL
|
449
|
|
- * @v txbufs Recycled transmit buffer address, or NULL
|
|
466
|
+ * @v txbuf Recycled transmit buffer address, or NULL
|
450
|
467
|
* @ret efirc EFI status code
|
451
|
468
|
*/
|
452
|
469
|
static EFI_STATUS EFIAPI
|
453
|
470
|
efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
|
454
|
|
- UINT32 *interrupts, VOID **txbufs ) {
|
|
471
|
+ UINT32 *interrupts, VOID **txbuf ) {
|
455
|
472
|
struct efi_snp_device *snpdev =
|
456
|
473
|
container_of ( snp, struct efi_snp_device, snp );
|
457
|
474
|
|
458
|
475
|
DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
|
459
|
476
|
|
460
|
477
|
/* Fail if net device is currently claimed for use by iPXE */
|
461
|
|
- if ( efi_snp_claimed )
|
|
478
|
+ if ( efi_snp_claimed ) {
|
|
479
|
+ DBGC2 ( snpdev, "\n" );
|
462
|
480
|
return EFI_NOT_READY;
|
|
481
|
+ }
|
463
|
482
|
|
464
|
483
|
/* Poll the network device */
|
465
|
484
|
efi_snp_poll ( snpdev );
|
|
@@ -468,47 +487,19 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
|
468
|
487
|
* to detect TX completions.
|
469
|
488
|
*/
|
470
|
489
|
if ( interrupts ) {
|
471
|
|
- *interrupts = 0;
|
472
|
|
- /* Report TX completions once queue is empty; this
|
473
|
|
- * avoids having to add hooks in the net device layer.
|
474
|
|
- */
|
475
|
|
- if ( snpdev->tx_count_interrupts &&
|
476
|
|
- list_empty ( &snpdev->netdev->tx_queue ) ) {
|
477
|
|
- *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
|
478
|
|
- snpdev->tx_count_interrupts--;
|
479
|
|
- }
|
480
|
|
- /* Report RX */
|
481
|
|
- if ( snpdev->rx_count_interrupts ) {
|
482
|
|
- *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
|
483
|
|
- snpdev->rx_count_interrupts--;
|
484
|
|
- }
|
|
490
|
+ *interrupts = snpdev->interrupts;
|
485
|
491
|
DBGC2 ( snpdev, " INTS:%02x", *interrupts );
|
|
492
|
+ snpdev->interrupts = 0;
|
486
|
493
|
}
|
487
|
494
|
|
488
|
|
- /* TX completions. It would be possible to design a more
|
489
|
|
- * idiotic scheme for this, but it would be a challenge.
|
490
|
|
- * According to the UEFI header file, txbufs will be filled in
|
491
|
|
- * with a list of "recycled transmit buffers" (i.e. completed
|
492
|
|
- * TX buffers). Observant readers may care to note that
|
493
|
|
- * *txbufs is a void pointer. Precisely how a list of
|
494
|
|
- * completed transmit buffers is meant to be represented as an
|
495
|
|
- * array of voids is left as an exercise for the reader.
|
496
|
|
- *
|
497
|
|
- * The only users of this interface (MnpDxe/MnpIo.c and
|
498
|
|
- * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until
|
499
|
|
- * seeing a non-NULL result return in txbufs. This is valid
|
500
|
|
- * provided that they do not ever attempt to transmit more
|
501
|
|
- * than one packet concurrently (and that TX never times out).
|
502
|
|
- */
|
503
|
|
- if ( txbufs ) {
|
504
|
|
- if ( snpdev->tx_count_txbufs &&
|
505
|
|
- list_empty ( &snpdev->netdev->tx_queue ) ) {
|
506
|
|
- *txbufs = "Which idiot designed this API?";
|
507
|
|
- snpdev->tx_count_txbufs--;
|
|
495
|
+ /* TX completions */
|
|
496
|
+ if ( txbuf ) {
|
|
497
|
+ if ( snpdev->tx_prod != snpdev->tx_cons ) {
|
|
498
|
+ *txbuf = snpdev->tx[snpdev->tx_cons++ % EFI_SNP_NUM_TX];
|
508
|
499
|
} else {
|
509
|
|
- *txbufs = NULL;
|
|
500
|
+ *txbuf = NULL;
|
510
|
501
|
}
|
511
|
|
- DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) );
|
|
502
|
+ DBGC2 ( snpdev, " TX:%p", *txbuf );
|
512
|
503
|
}
|
513
|
504
|
|
514
|
505
|
DBGC2 ( snpdev, "\n" );
|
|
@@ -537,6 +528,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
|
537
|
528
|
struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
|
538
|
529
|
struct io_buffer *iobuf;
|
539
|
530
|
size_t payload_len;
|
|
531
|
+ unsigned int tx_fill;
|
540
|
532
|
int rc;
|
541
|
533
|
|
542
|
534
|
DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
|
|
@@ -624,12 +616,27 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
|
624
|
616
|
goto err_tx;
|
625
|
617
|
}
|
626
|
618
|
|
627
|
|
- /* Record transmission as outstanding */
|
628
|
|
- snpdev->tx_count_interrupts++;
|
629
|
|
- snpdev->tx_count_txbufs++;
|
|
619
|
+ /* Record in transmit completion ring. If we run out of
|
|
620
|
+ * space, report the failure even though we have already
|
|
621
|
+ * transmitted the packet.
|
|
622
|
+ *
|
|
623
|
+ * This allows us to report completions only for packets for
|
|
624
|
+ * which we had reported successfully initiating transmission,
|
|
625
|
+ * while continuing to support clients that never poll for
|
|
626
|
+ * transmit completions.
|
|
627
|
+ */
|
|
628
|
+ tx_fill = ( snpdev->tx_prod - snpdev->tx_cons );
|
|
629
|
+ if ( tx_fill >= EFI_SNP_NUM_TX ) {
|
|
630
|
+ DBGC ( snpdev, "SNPDEV %p TX completion ring full\n", snpdev );
|
|
631
|
+ rc = -ENOBUFS;
|
|
632
|
+ goto err_ring_full;
|
|
633
|
+ }
|
|
634
|
+ snpdev->tx[ snpdev->tx_prod++ % EFI_SNP_NUM_TX ] = data;
|
|
635
|
+ snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
|
630
|
636
|
|
631
|
637
|
return 0;
|
632
|
638
|
|
|
639
|
+ err_ring_full:
|
633
|
640
|
err_tx:
|
634
|
641
|
err_ll_push:
|
635
|
642
|
free_iob ( iobuf );
|
|
@@ -676,12 +683,13 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
|
676
|
683
|
efi_snp_poll ( snpdev );
|
677
|
684
|
|
678
|
685
|
/* Dequeue a packet, if one is available */
|
679
|
|
- iobuf = netdev_rx_dequeue ( snpdev->netdev );
|
|
686
|
+ iobuf = list_first_entry ( &snpdev->rx, struct io_buffer, list );
|
680
|
687
|
if ( ! iobuf ) {
|
681
|
688
|
DBGC2 ( snpdev, "\n" );
|
682
|
689
|
rc = -EAGAIN;
|
683
|
690
|
goto out_no_packet;
|
684
|
691
|
}
|
|
692
|
+ list_del ( &iobuf->list );
|
685
|
693
|
DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) );
|
686
|
694
|
|
687
|
695
|
/* Return packet to caller */
|
|
@@ -721,9 +729,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
|
721
|
729
|
* @v event Event
|
722
|
730
|
* @v context Event context
|
723
|
731
|
*/
|
724
|
|
-static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
|
|
732
|
+static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused,
|
725
|
733
|
VOID *context ) {
|
726
|
|
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
727
|
734
|
struct efi_snp_device *snpdev = context;
|
728
|
735
|
|
729
|
736
|
DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
|
|
@@ -738,14 +745,6 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
|
738
|
745
|
|
739
|
746
|
/* Poll the network device */
|
740
|
747
|
efi_snp_poll ( snpdev );
|
741
|
|
-
|
742
|
|
- /* Fire event if packets have been received */
|
743
|
|
- if ( snpdev->rx_count_events != 0 ) {
|
744
|
|
- DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n",
|
745
|
|
- snpdev );
|
746
|
|
- bs->SignalEvent ( event );
|
747
|
|
- snpdev->rx_count_events--;
|
748
|
|
- }
|
749
|
748
|
}
|
750
|
749
|
|
751
|
750
|
/** SNP interface */
|
|
@@ -922,6 +921,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
|
922
|
921
|
}
|
923
|
922
|
snpdev->netdev = netdev_get ( netdev );
|
924
|
923
|
snpdev->efidev = efidev;
|
|
924
|
+ INIT_LIST_HEAD ( &snpdev->rx );
|
925
|
925
|
|
926
|
926
|
/* Sanity check */
|
927
|
927
|
if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
|