Przeglądaj źródła

[usb] Detect missed disconnections

The USB core will currently fail to detect disconnections if a new
device has attached by the time the port is examined in
usb_hotplug().

Fix by recording the fact that a disconnection has taken place
whenever the "connection status changed" (CSC) bit is observed to be
set.  (Whether the change represents a disconnection or a
reconnection, it indicates that the port has experienced some time of
being disconnected.)

Note that the time at which a disconnection can be detected varies by
hub type.  In particular: root hubs can observe the CSC bit when
polling, and so will record the disconnection before calling
usb_port_changed(), but USB hubs read the port status (and hence the
CSC bit) only during the call to hub_speed(), long after the call to
usb_port_changed().

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 lat temu
rodzic
commit
f6604627ff

+ 12
- 10
src/drivers/bus/usb.c Wyświetl plik

@@ -1589,18 +1589,20 @@ static int usb_hotplug ( struct usb_port *port ) {
1589 1589
 		return rc;
1590 1590
 	}
1591 1591
 
1592
-	/* Handle attached/detached device as applicable */
1593
-	if ( port->speed && ! port->attached ) {
1594
-		/* Newly attached device */
1595
-		return usb_attached ( port );
1596
-	} else if ( port->attached && ! port->speed ) {
1597
-		/* Newly detached device */
1592
+	/* Detach device, if applicable */
1593
+	if ( port->attached && ( port->disconnected || ! port->speed ) )
1598 1594
 		usb_detached ( port );
1599
-		return 0;
1600
-	} else {
1601
-		/* Ignore */
1602
-		return 0;
1595
+
1596
+	/* Attach device, if applicable */
1597
+	if ( port->speed && ! port->attached ) {
1598
+		if ( ( rc = usb_attached ( port ) ) != 0 )
1599
+			return rc;
1603 1600
 	}
1601
+
1602
+	/* Clear any recorded disconnections */
1603
+	port->disconnected = 0;
1604
+
1605
+	return 0;
1604 1606
 }
1605 1607
 
1606 1608
 /******************************************************************************

+ 8
- 1
src/drivers/usb/ehci.c Wyświetl plik

@@ -1498,6 +1498,7 @@ static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
1498 1498
 	unsigned int speed;
1499 1499
 	unsigned int line;
1500 1500
 	int ccs;
1501
+	int csc;
1501 1502
 	int ped;
1502 1503
 
1503 1504
 	/* Read port status */
@@ -1505,9 +1506,14 @@ static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
1505 1506
 	DBGC2 ( ehci, "EHCI %p port %d status is %08x\n",
1506 1507
 		ehci, port->address, portsc );
1507 1508
 	ccs = ( portsc & EHCI_PORTSC_CCS );
1509
+	csc = ( portsc & EHCI_PORTSC_CSC );
1508 1510
 	ped = ( portsc & EHCI_PORTSC_PED );
1509 1511
 	line = EHCI_PORTSC_LINE_STATUS ( portsc );
1510 1512
 
1513
+	/* Record disconnections and clear changes */
1514
+	port->disconnected |= csc;
1515
+	writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
1516
+
1511 1517
 	/* Determine port speed */
1512 1518
 	if ( ! ccs ) {
1513 1519
 		/* Port not connected */
@@ -1564,7 +1570,8 @@ static void ehci_root_poll ( struct usb_hub *hub, struct usb_port *port ) {
1564 1570
 	if ( ! change )
1565 1571
 		return;
1566 1572
 
1567
-	/* Acknowledge changes */
1573
+	/* Record disconnections and clear changes */
1574
+	port->disconnected |= ( portsc & EHCI_PORTSC_CSC );
1568 1575
 	writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
1569 1576
 
1570 1577
 	/* Report port status change */

+ 3
- 0
src/drivers/usb/usbhub.c Wyświetl plik

@@ -331,6 +331,9 @@ static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
331 331
 		port->speed = USB_SPEED_NONE;
332 332
 	}
333 333
 
334
+	/* Record disconnections */
335
+	port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) );
336
+
334 337
 	/* Clear port status change bits */
335 338
 	if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0)
336 339
 		return rc;

+ 49
- 40
src/drivers/usb/xhci.c Wyświetl plik

@@ -1532,10 +1532,10 @@ static void xhci_event_free ( struct xhci_device *xhci ) {
1532 1532
  * Handle transfer event
1533 1533
  *
1534 1534
  * @v xhci		xHCI device
1535
- * @v transfer		Transfer event TRB
1535
+ * @v trb		Transfer event TRB
1536 1536
  */
1537 1537
 static void xhci_transfer ( struct xhci_device *xhci,
1538
-			    struct xhci_trb_transfer *transfer ) {
1538
+			    struct xhci_trb_transfer *trb ) {
1539 1539
 	struct xhci_slot *slot;
1540 1540
 	struct xhci_endpoint *endpoint;
1541 1541
 	struct io_buffer *iobuf;
@@ -1545,20 +1545,20 @@ static void xhci_transfer ( struct xhci_device *xhci,
1545 1545
 	profile_start ( &xhci_transfer_profiler );
1546 1546
 
1547 1547
 	/* Identify slot */
1548
-	if ( ( transfer->slot > xhci->slots ) ||
1549
-	     ( ( slot = xhci->slot[transfer->slot] ) == NULL ) ) {
1548
+	if ( ( trb->slot > xhci->slots ) ||
1549
+	     ( ( slot = xhci->slot[trb->slot] ) == NULL ) ) {
1550 1550
 		DBGC ( xhci, "XHCI %p transfer event invalid slot %d:\n",
1551
-		       xhci, transfer->slot );
1552
-		DBGC_HDA ( xhci, 0, transfer, sizeof ( *transfer ) );
1551
+		       xhci, trb->slot );
1552
+		DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) );
1553 1553
 		return;
1554 1554
 	}
1555 1555
 
1556 1556
 	/* Identify endpoint */
1557
-	if ( ( transfer->endpoint > XHCI_CTX_END ) ||
1558
-	     ( ( endpoint = slot->endpoint[transfer->endpoint] ) == NULL ) ) {
1557
+	if ( ( trb->endpoint > XHCI_CTX_END ) ||
1558
+	     ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) {
1559 1559
 		DBGC ( xhci, "XHCI %p slot %d transfer event invalid epid "
1560
-		       "%d:\n", xhci, slot->id, transfer->endpoint );
1561
-		DBGC_HDA ( xhci, 0, transfer, sizeof ( *transfer ) );
1560
+		       "%d:\n", xhci, slot->id, trb->endpoint );
1561
+		DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) );
1562 1562
 		return;
1563 1563
 	}
1564 1564
 
@@ -1567,15 +1567,15 @@ static void xhci_transfer ( struct xhci_device *xhci,
1567 1567
 	assert ( iobuf != NULL );
1568 1568
 
1569 1569
 	/* Check for errors */
1570
-	if ( ! ( ( transfer->code == XHCI_CMPLT_SUCCESS ) ||
1571
-		 ( transfer->code == XHCI_CMPLT_SHORT ) ) ) {
1570
+	if ( ! ( ( trb->code == XHCI_CMPLT_SUCCESS ) ||
1571
+		 ( trb->code == XHCI_CMPLT_SHORT ) ) ) {
1572 1572
 
1573 1573
 		/* Construct error */
1574
-		rc = -ECODE ( transfer->code );
1574
+		rc = -ECODE ( trb->code );
1575 1575
 		DBGC ( xhci, "XHCI %p slot %d ctx %d failed (code %d): %s\n",
1576
-		       xhci, slot->id, endpoint->ctx, transfer->code,
1576
+		       xhci, slot->id, endpoint->ctx, trb->code,
1577 1577
 		       strerror ( rc ) );
1578
-		DBGC_HDA ( xhci, 0, transfer, sizeof ( *transfer ) );
1578
+		DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) );
1579 1579
 
1580 1580
 		/* Sanity check */
1581 1581
 		assert ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK )
@@ -1587,11 +1587,11 @@ static void xhci_transfer ( struct xhci_device *xhci,
1587 1587
 	}
1588 1588
 
1589 1589
 	/* Record actual transfer size */
1590
-	iob_unput ( iobuf, le16_to_cpu ( transfer->residual ) );
1590
+	iob_unput ( iobuf, le16_to_cpu ( trb->residual ) );
1591 1591
 
1592 1592
 	/* Sanity check (for successful completions only) */
1593 1593
 	assert ( xhci_ring_consumed ( &endpoint->ring ) ==
1594
-		 le64_to_cpu ( transfer->transfer ) );
1594
+		 le64_to_cpu ( trb->transfer ) );
1595 1595
 
1596 1596
 	/* Report completion to USB core */
1597 1597
 	usb_complete ( endpoint->ep, iobuf );
@@ -1602,24 +1602,24 @@ static void xhci_transfer ( struct xhci_device *xhci,
1602 1602
  * Handle command completion event
1603 1603
  *
1604 1604
  * @v xhci		xHCI device
1605
- * @v complete		Command completion event
1605
+ * @v trb		Command completion event
1606 1606
  */
1607 1607
 static void xhci_complete ( struct xhci_device *xhci,
1608
-			    struct xhci_trb_complete *complete ) {
1608
+			    struct xhci_trb_complete *trb ) {
1609 1609
 	int rc;
1610 1610
 
1611 1611
 	/* Ignore "command ring stopped" notifications */
1612
-	if ( complete->code == XHCI_CMPLT_CMD_STOPPED ) {
1612
+	if ( trb->code == XHCI_CMPLT_CMD_STOPPED ) {
1613 1613
 		DBGC2 ( xhci, "XHCI %p command ring stopped\n", xhci );
1614 1614
 		return;
1615 1615
 	}
1616 1616
 
1617 1617
 	/* Ignore unexpected completions */
1618 1618
 	if ( ! xhci->pending ) {
1619
-		rc = -ECODE ( complete->code );
1619
+		rc = -ECODE ( trb->code );
1620 1620
 		DBGC ( xhci, "XHCI %p unexpected completion (code %d): %s\n",
1621
-		       xhci, complete->code, strerror ( rc ) );
1622
-		DBGC_HDA ( xhci, 0, complete, sizeof ( *complete ) );
1621
+		       xhci, trb->code, strerror ( rc ) );
1622
+		DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) );
1623 1623
 		return;
1624 1624
 	}
1625 1625
 
@@ -1628,10 +1628,10 @@ static void xhci_complete ( struct xhci_device *xhci,
1628 1628
 
1629 1629
 	/* Sanity check */
1630 1630
 	assert ( xhci_ring_consumed ( &xhci->command ) ==
1631
-		 le64_to_cpu ( complete->command ) );
1631
+		 le64_to_cpu ( trb->command ) );
1632 1632
 
1633 1633
 	/* Record completion */
1634
-	memcpy ( xhci->pending, complete, sizeof ( *xhci->pending ) );
1634
+	memcpy ( xhci->pending, trb, sizeof ( *xhci->pending ) );
1635 1635
 	xhci->pending = NULL;
1636 1636
 }
1637 1637
 
@@ -1639,38 +1639,40 @@ static void xhci_complete ( struct xhci_device *xhci,
1639 1639
  * Handle port status event
1640 1640
  *
1641 1641
  * @v xhci		xHCI device
1642
- * @v port		Port status event
1642
+ * @v trb		Port status event
1643 1643
  */
1644 1644
 static void xhci_port_status ( struct xhci_device *xhci,
1645
-			       struct xhci_trb_port_status *port ) {
1645
+			       struct xhci_trb_port_status *trb ) {
1646
+	struct usb_port *port = usb_port ( xhci->bus->hub, trb->port );
1646 1647
 	uint32_t portsc;
1647 1648
 
1648 1649
 	/* Sanity check */
1649
-	assert ( ( port->port > 0 ) && ( port->port <= xhci->ports ) );
1650
+	assert ( ( trb->port > 0 ) && ( trb->port <= xhci->ports ) );
1650 1651
 
1651
-	/* Clear port status change bits */
1652
-	portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->port ) );
1652
+	/* Record disconnections and clear changes */
1653
+	portsc = readl ( xhci->op + XHCI_OP_PORTSC ( trb->port ) );
1654
+	port->disconnected |= ( portsc & XHCI_PORTSC_CSC );
1653 1655
 	portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE );
1654
-	writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->port ) );
1656
+	writel ( portsc, xhci->op + XHCI_OP_PORTSC ( trb->port ) );
1655 1657
 
1656 1658
 	/* Report port status change */
1657
-	usb_port_changed ( usb_port ( xhci->bus->hub, port->port ) );
1659
+	usb_port_changed ( port );
1658 1660
 }
1659 1661
 
1660 1662
 /**
1661 1663
  * Handle host controller event
1662 1664
  *
1663 1665
  * @v xhci		xHCI device
1664
- * @v host		Host controller event
1666
+ * @v trb		Host controller event
1665 1667
  */
1666 1668
 static void xhci_host_controller ( struct xhci_device *xhci,
1667
-				   struct xhci_trb_host_controller *host ) {
1669
+				   struct xhci_trb_host_controller *trb ) {
1668 1670
 	int rc;
1669 1671
 
1670 1672
 	/* Construct error */
1671
-	rc = -ECODE ( host->code );
1673
+	rc = -ECODE ( trb->code );
1672 1674
 	DBGC ( xhci, "XHCI %p host controller event (code %d): %s\n",
1673
-	       xhci, host->code, strerror ( rc ) );
1675
+	       xhci, trb->code, strerror ( rc ) );
1674 1676
 }
1675 1677
 
1676 1678
 /**
@@ -3014,6 +3016,7 @@ static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
3014 3016
 	unsigned int psiv;
3015 3017
 	int ccs;
3016 3018
 	int ped;
3019
+	int csc;
3017 3020
 	int speed;
3018 3021
 	int rc;
3019 3022
 
@@ -3021,9 +3024,17 @@ static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
3021 3024
 	portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) );
3022 3025
 	DBGC2 ( xhci, "XHCI %p port %d status is %08x\n",
3023 3026
 		xhci, port->address, portsc );
3024
-
3025
-	/* Check whether or not port is connected */
3026 3027
 	ccs = ( portsc & XHCI_PORTSC_CCS );
3028
+	ped = ( portsc & XHCI_PORTSC_PED );
3029
+	csc = ( portsc & XHCI_PORTSC_CSC );
3030
+	psiv = XHCI_PORTSC_PSIV ( portsc );
3031
+
3032
+	/* Record disconnections and clear changes */
3033
+	port->disconnected |= csc;
3034
+	portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE );
3035
+	writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) );
3036
+
3037
+	/* Port speed is not valid unless port is connected */
3027 3038
 	if ( ! ccs ) {
3028 3039
 		port->speed = USB_SPEED_NONE;
3029 3040
 		return 0;
@@ -3032,14 +3043,12 @@ static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
3032 3043
 	/* For USB2 ports, the PSIV field is not valid until the port
3033 3044
 	 * completes reset and becomes enabled.
3034 3045
 	 */
3035
-	ped = ( portsc & XHCI_PORTSC_PED );
3036 3046
 	if ( ( port->protocol < USB_PROTO_3_0 ) && ! ped ) {
3037 3047
 		port->speed = USB_SPEED_FULL;
3038 3048
 		return 0;
3039 3049
 	}
3040 3050
 
3041 3051
 	/* Get port speed and map to generic USB speed */
3042
-	psiv = XHCI_PORTSC_PSIV ( portsc );
3043 3052
 	speed = xhci_port_speed ( xhci, port->address, psiv );
3044 3053
 	if ( speed < 0 ) {
3045 3054
 		rc = speed;

+ 6
- 0
src/include/ipxe/usb.h Wyświetl plik

@@ -747,6 +747,12 @@ struct usb_port {
747 747
 	unsigned int protocol;
748 748
 	/** Port speed */
749 749
 	unsigned int speed;
750
+	/** Port disconnection has been detected
751
+	 *
752
+	 * This should be set whenever the underlying hardware reports
753
+	 * a connection status change.
754
+	 */
755
+	int disconnected;
750 756
 	/** Port has an attached device */
751 757
 	int attached;
752 758
 	/** Currently attached device (if in use)

Ładowanie…
Anuluj
Zapisz