Browse Source

[usb] Reset endpoints without waiting for a new transfer to be enqueued

The current endpoint reset logic defers the reset until the caller
attempts to enqueue a new transfer to that endpoint.  This is
insufficient when dealing with endpoints behind a transaction
translator, since the transaction translator is a resource shared
between multiple endpoints.

We cannot reset the endpoint as part of the completion handling, since
that would introduce recursive calls to usb_poll().  Instead, we
add the endpoint to a list of halted endpoints, and perform the reset
on the next call to usb_step().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
de6f4e3ede
2 changed files with 70 additions and 31 deletions
  1. 64
    28
      src/drivers/bus/usb.c
  2. 6
    3
      src/include/ipxe/usb.h

+ 64
- 28
src/drivers/bus/usb.c View File

@@ -296,9 +296,7 @@ int usb_endpoint_open ( struct usb_endpoint *ep ) {
296 296
 		goto err_already;
297 297
 	}
298 298
 	usb->ep[idx] = ep;
299
-
300
-	/* Clear any stale error status */
301
-	ep->rc = 0;
299
+	INIT_LIST_HEAD ( &ep->halted );
302 300
 
303 301
 	/* Open endpoint */
304 302
 	if ( ( rc = ep->host->open ( ep ) ) != 0 ) {
@@ -342,6 +340,7 @@ void usb_endpoint_close ( struct usb_endpoint *ep ) {
342 340
 
343 341
 	/* Remove from endpoint list */
344 342
 	usb->ep[idx] = NULL;
343
+	list_del ( &ep->halted );
345 344
 
346 345
 	/* Discard any recycled buffers, if applicable */
347 346
 	if ( ep->max )
@@ -359,6 +358,9 @@ static int usb_endpoint_reset ( struct usb_endpoint *ep ) {
359 358
 	unsigned int type;
360 359
 	int rc;
361 360
 
361
+	/* Sanity check */
362
+	assert ( ! list_empty ( &ep->halted ) );
363
+
362 364
 	/* Reset endpoint */
363 365
 	if ( ( rc = ep->host->reset ( ep ) ) != 0 ) {
364 366
 		DBGC ( usb, "USB %s %s could not reset: %s\n",
@@ -379,8 +381,9 @@ static int usb_endpoint_reset ( struct usb_endpoint *ep ) {
379 381
 		return rc;
380 382
 	}
381 383
 
382
-	/* Clear recorded error */
383
-	ep->rc = 0;
384
+	/* Remove from list of halted endpoints */
385
+	list_del ( &ep->halted );
386
+	INIT_LIST_HEAD ( &ep->halted );
384 387
 
385 388
 	DBGC ( usb, "USB %s %s reset\n",
386 389
 	       usb->name, usb_endpoint_name ( ep->address ) );
@@ -434,7 +437,8 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request,
434 437
 		return -ENODEV;
435 438
 
436 439
 	/* Reset endpoint if required */
437
-	if ( ( ep->rc != 0 ) && ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) )
440
+	if ( ( ! list_empty ( &ep->halted ) ) &&
441
+	     ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) )
438 442
 		return rc;
439 443
 
440 444
 	/* Zero input data buffer (if applicable) */
@@ -480,7 +484,8 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
480 484
 		return -ENODEV;
481 485
 
482 486
 	/* Reset endpoint if required */
483
-	if ( ( ep->rc != 0 ) && ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) )
487
+	if ( ( ! list_empty ( &ep->halted ) ) &&
488
+	     ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) )
484 489
 		return rc;
485 490
 
486 491
 	/* Enqueue stream transfer */
@@ -507,17 +512,19 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
507 512
 void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
508 513
 			int rc ) {
509 514
 	struct usb_device *usb = ep->usb;
515
+	struct usb_bus *bus = usb->port->hub->bus;
510 516
 
511 517
 	/* Decrement fill level */
512 518
 	assert ( ep->fill > 0 );
513 519
 	ep->fill--;
514 520
 
515
-	/* Record error (if any) */
516
-	ep->rc = rc;
521
+	/* Schedule reset, if applicable */
517 522
 	if ( ( rc != 0 ) && ep->open ) {
518 523
 		DBGC ( usb, "USB %s %s completion failed: %s\n",
519 524
 		       usb->name, usb_endpoint_name ( ep->address ),
520 525
 		       strerror ( rc ) );
526
+		list_del ( &ep->halted );
527
+		list_add_tail ( &ep->halted, &bus->halted );
521 528
 	}
522 529
 
523 530
 	/* Report completion */
@@ -642,6 +649,12 @@ void usb_flush ( struct usb_endpoint *ep ) {
642 649
  ******************************************************************************
643 650
  */
644 651
 
652
+/** USB control transfer pseudo-header */
653
+struct usb_control_pseudo_header {
654
+	/** Completion status */
655
+	int rc;
656
+};
657
+
645 658
 /**
646 659
  * Complete USB control transfer
647 660
  *
@@ -652,13 +665,14 @@ void usb_flush ( struct usb_endpoint *ep ) {
652 665
 static void usb_control_complete ( struct usb_endpoint *ep,
653 666
 				   struct io_buffer *iobuf, int rc ) {
654 667
 	struct usb_device *usb = ep->usb;
668
+	struct usb_control_pseudo_header *pshdr;
655 669
 
656
-	/* Check for failures */
670
+	/* Record completion status in buffer */
671
+	pshdr = iob_push ( iobuf, sizeof ( *pshdr ) );
672
+	pshdr->rc = rc;
657 673
 	if ( rc != 0 ) {
658 674
 		DBGC ( usb, "USB %s control transaction failed: %s\n",
659 675
 		       usb->name, strerror ( rc ) );
660
-		free_iob ( iobuf );
661
-		return;
662 676
 	}
663 677
 
664 678
 	/* Add to list of completed I/O buffers */
@@ -686,17 +700,19 @@ int usb_control ( struct usb_device *usb, unsigned int request,
686 700
 		  size_t len ) {
687 701
 	struct usb_bus *bus = usb->port->hub->bus;
688 702
 	struct usb_endpoint *ep = &usb->control;
703
+	struct usb_control_pseudo_header *pshdr;
689 704
 	struct io_buffer *iobuf;
690 705
 	struct io_buffer *cmplt;
691 706
 	unsigned int i;
692 707
 	int rc;
693 708
 
694 709
 	/* Allocate I/O buffer */
695
-	iobuf = alloc_iob ( len );
710
+	iobuf = alloc_iob ( sizeof ( *pshdr ) + len );
696 711
 	if ( ! iobuf ) {
697 712
 		rc = -ENOMEM;
698 713
 		goto err_alloc;
699 714
 	}
715
+	iob_reserve ( iobuf, sizeof ( *pshdr ) );
700 716
 	iob_put ( iobuf, len );
701 717
 	if ( request & USB_DIR_IN ) {
702 718
 		memset ( data, 0, len );
@@ -722,16 +738,27 @@ int usb_control ( struct usb_device *usb, unsigned int request,
722 738
 			/* Remove from completion list */
723 739
 			list_del ( &cmplt->list );
724 740
 
741
+			/* Extract and strip completion status */
742
+			pshdr = cmplt->data;
743
+			iob_pull ( cmplt, sizeof ( *pshdr ) );
744
+			rc = pshdr->rc;
745
+
725 746
 			/* Discard stale completions */
726 747
 			if ( cmplt != iobuf ) {
727
-				DBGC ( usb, "USB %s stale control "
728
-				       "completion:\n", usb->name );
748
+				DBGC ( usb, "USB %s stale control completion: "
749
+				       "%s\n", usb->name, strerror ( rc ) );
729 750
 				DBGC_HDA ( usb, 0, cmplt->data,
730 751
 					   iob_len ( cmplt ) );
731 752
 				free_iob ( cmplt );
732 753
 				continue;
733 754
 			}
734 755
 
756
+			/* Fail immediately if completion was in error */
757
+			if ( rc != 0 ) {
758
+				free_iob ( cmplt );
759
+				return rc;
760
+			}
761
+
735 762
 			/* Copy completion to data buffer, if applicable */
736 763
 			assert ( iob_len ( cmplt ) <= len );
737 764
 			if ( request & USB_DIR_IN )
@@ -740,10 +767,6 @@ int usb_control ( struct usb_device *usb, unsigned int request,
740 767
 			return 0;
741 768
 		}
742 769
 
743
-		/* Fail immediately if endpoint is in an error state */
744
-		if ( ep->rc )
745
-			return ep->rc;
746
-
747 770
 		/* Delay */
748 771
 		mdelay ( 1 );
749 772
 	}
@@ -1549,8 +1572,8 @@ void usb_port_changed ( struct usb_port *port ) {
1549 1572
 	struct usb_bus *bus = hub->bus;
1550 1573
 
1551 1574
 	/* Record hub port status change */
1552
-	list_del ( &port->list );
1553
-	list_add_tail ( &port->list, &bus->changed );
1575
+	list_del ( &port->changed );
1576
+	list_add_tail ( &port->changed, &bus->changed );
1554 1577
 }
1555 1578
 
1556 1579
 /**
@@ -1559,23 +1582,35 @@ void usb_port_changed ( struct usb_port *port ) {
1559 1582
  * @v bus		USB bus
1560 1583
  */
1561 1584
 static void usb_step ( struct usb_bus *bus ) {
1585
+	struct usb_endpoint *ep;
1562 1586
 	struct usb_port *port;
1563 1587
 
1564 1588
 	/* Poll bus */
1565 1589
 	usb_poll ( bus );
1566 1590
 
1591
+	/* Attempt to reset first halted endpoint in list, if any.  We
1592
+	 * do not attempt to process the complete list, since this
1593
+	 * would require extra code to allow for the facts that the
1594
+	 * halted endpoint list may change as we do so, and that
1595
+	 * resetting an endpoint may fail.
1596
+	 */
1597
+	if ( ( ep = list_first_entry ( &bus->halted, struct usb_endpoint,
1598
+				       halted ) ) != NULL )
1599
+		usb_endpoint_reset ( ep );
1600
+
1567 1601
 	/* Handle any changed ports, allowing for the fact that the
1568 1602
 	 * port list may change as we perform hotplug actions.
1569 1603
 	 */
1570 1604
 	while ( ! list_empty ( &bus->changed ) ) {
1571 1605
 
1572 1606
 		/* Get first changed port */
1573
-		port = list_first_entry ( &bus->changed, struct usb_port, list);
1607
+		port = list_first_entry ( &bus->changed, struct usb_port,
1608
+					  changed );
1574 1609
 		assert ( port != NULL );
1575 1610
 
1576 1611
 		/* Remove from list of changed ports */
1577
-		list_del ( &port->list );
1578
-		INIT_LIST_HEAD ( &port->list );
1612
+		list_del ( &port->changed );
1613
+		INIT_LIST_HEAD ( &port->changed );
1579 1614
 
1580 1615
 		/* Perform appropriate hotplug action */
1581 1616
 		usb_hotplug ( port );
@@ -1628,7 +1663,7 @@ struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, struct usb_device *usb,
1628 1663
 		port->address = i;
1629 1664
 		if ( usb )
1630 1665
 			port->protocol = usb->port->protocol;
1631
-		INIT_LIST_HEAD ( &port->list );
1666
+		INIT_LIST_HEAD ( &port->changed );
1632 1667
 	}
1633 1668
 
1634 1669
 	return hub;
@@ -1702,8 +1737,8 @@ void unregister_usb_hub ( struct usb_hub *hub ) {
1702 1737
 	/* Cancel any pending port status changes */
1703 1738
 	for ( i = 1 ; i <= hub->ports ; i++ ) {
1704 1739
 		port = usb_port ( hub, i );
1705
-		list_del ( &port->list );
1706
-		INIT_LIST_HEAD ( &port->list );
1740
+		list_del ( &port->changed );
1741
+		INIT_LIST_HEAD ( &port->changed );
1707 1742
 	}
1708 1743
 
1709 1744
 	/* Remove from hub list */
@@ -1724,7 +1759,7 @@ void free_usb_hub ( struct usb_hub *hub ) {
1724 1759
 		port = usb_port ( hub, i );
1725 1760
 		assert ( ! port->attached );
1726 1761
 		assert ( port->usb == NULL );
1727
-		assert ( list_empty ( &port->list ) );
1762
+		assert ( list_empty ( &port->changed ) );
1728 1763
 	}
1729 1764
 
1730 1765
 	/* Free hub */
@@ -1762,6 +1797,7 @@ struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports,
1762 1797
 	INIT_LIST_HEAD ( &bus->devices );
1763 1798
 	INIT_LIST_HEAD ( &bus->hubs );
1764 1799
 	INIT_LIST_HEAD ( &bus->changed );
1800
+	INIT_LIST_HEAD ( &bus->halted );
1765 1801
 	process_init_stopped ( &bus->process, &usb_process_desc, NULL );
1766 1802
 	bus->host = &bus->op->bus;
1767 1803
 

+ 6
- 3
src/include/ipxe/usb.h View File

@@ -380,11 +380,12 @@ struct usb_endpoint {
380 380
 
381 381
 	/** Endpoint is open */
382 382
 	int open;
383
-	/** Current failure state (if any) */
384
-	int rc;
385 383
 	/** Buffer fill level */
386 384
 	unsigned int fill;
387 385
 
386
+	/** List of halted endpoints */
387
+	struct list_head halted;
388
+
388 389
 	/** Host controller operations */
389 390
 	struct usb_endpoint_host_operations *host;
390 391
 	/** Host controller private data */
@@ -754,7 +755,7 @@ struct usb_port {
754 755
 	 */
755 756
 	struct usb_device *usb;
756 757
 	/** List of changed ports */
757
-	struct list_head list;
758
+	struct list_head changed;
758 759
 };
759 760
 
760 761
 /** A USB hub */
@@ -888,6 +889,8 @@ struct usb_bus {
888 889
 	struct list_head hubs;
889 890
 	/** List of changed ports */
890 891
 	struct list_head changed;
892
+	/** List of halted endpoints */
893
+	struct list_head halted;
891 894
 	/** Process */
892 895
 	struct process process;
893 896
 

Loading…
Cancel
Save