|  | @@ -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 ) ) {
 |