Browse Source

[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 years ago
parent
commit
f6604627ff
5 changed files with 78 additions and 51 deletions
  1. 12
    10
      src/drivers/bus/usb.c
  2. 8
    1
      src/drivers/usb/ehci.c
  3. 3
    0
      src/drivers/usb/usbhub.c
  4. 49
    40
      src/drivers/usb/xhci.c
  5. 6
    0
      src/include/ipxe/usb.h

+ 12
- 10
src/drivers/bus/usb.c View File

1589
 		return rc;
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
 		usb_detached ( port );
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 View File

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

+ 3
- 0
src/drivers/usb/usbhub.c View File

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

+ 49
- 40
src/drivers/usb/xhci.c View File

1532
  * Handle transfer event
1532
  * Handle transfer event
1533
  *
1533
  *
1534
  * @v xhci		xHCI device
1534
  * @v xhci		xHCI device
1535
- * @v transfer		Transfer event TRB
1535
+ * @v trb		Transfer event TRB
1536
  */
1536
  */
1537
 static void xhci_transfer ( struct xhci_device *xhci,
1537
 static void xhci_transfer ( struct xhci_device *xhci,
1538
-			    struct xhci_trb_transfer *transfer ) {
1538
+			    struct xhci_trb_transfer *trb ) {
1539
 	struct xhci_slot *slot;
1539
 	struct xhci_slot *slot;
1540
 	struct xhci_endpoint *endpoint;
1540
 	struct xhci_endpoint *endpoint;
1541
 	struct io_buffer *iobuf;
1541
 	struct io_buffer *iobuf;
1545
 	profile_start ( &xhci_transfer_profiler );
1545
 	profile_start ( &xhci_transfer_profiler );
1546
 
1546
 
1547
 	/* Identify slot */
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
 		DBGC ( xhci, "XHCI %p transfer event invalid slot %d:\n",
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
 		return;
1553
 		return;
1554
 	}
1554
 	}
1555
 
1555
 
1556
 	/* Identify endpoint */
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
 		DBGC ( xhci, "XHCI %p slot %d transfer event invalid epid "
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
 		return;
1562
 		return;
1563
 	}
1563
 	}
1564
 
1564
 
1567
 	assert ( iobuf != NULL );
1567
 	assert ( iobuf != NULL );
1568
 
1568
 
1569
 	/* Check for errors */
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
 		/* Construct error */
1573
 		/* Construct error */
1574
-		rc = -ECODE ( transfer->code );
1574
+		rc = -ECODE ( trb->code );
1575
 		DBGC ( xhci, "XHCI %p slot %d ctx %d failed (code %d): %s\n",
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
 		       strerror ( rc ) );
1577
 		       strerror ( rc ) );
1578
-		DBGC_HDA ( xhci, 0, transfer, sizeof ( *transfer ) );
1578
+		DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) );
1579
 
1579
 
1580
 		/* Sanity check */
1580
 		/* Sanity check */
1581
 		assert ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK )
1581
 		assert ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK )
1587
 	}
1587
 	}
1588
 
1588
 
1589
 	/* Record actual transfer size */
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
 	/* Sanity check (for successful completions only) */
1592
 	/* Sanity check (for successful completions only) */
1593
 	assert ( xhci_ring_consumed ( &endpoint->ring ) ==
1593
 	assert ( xhci_ring_consumed ( &endpoint->ring ) ==
1594
-		 le64_to_cpu ( transfer->transfer ) );
1594
+		 le64_to_cpu ( trb->transfer ) );
1595
 
1595
 
1596
 	/* Report completion to USB core */
1596
 	/* Report completion to USB core */
1597
 	usb_complete ( endpoint->ep, iobuf );
1597
 	usb_complete ( endpoint->ep, iobuf );
1602
  * Handle command completion event
1602
  * Handle command completion event
1603
  *
1603
  *
1604
  * @v xhci		xHCI device
1604
  * @v xhci		xHCI device
1605
- * @v complete		Command completion event
1605
+ * @v trb		Command completion event
1606
  */
1606
  */
1607
 static void xhci_complete ( struct xhci_device *xhci,
1607
 static void xhci_complete ( struct xhci_device *xhci,
1608
-			    struct xhci_trb_complete *complete ) {
1608
+			    struct xhci_trb_complete *trb ) {
1609
 	int rc;
1609
 	int rc;
1610
 
1610
 
1611
 	/* Ignore "command ring stopped" notifications */
1611
 	/* Ignore "command ring stopped" notifications */
1612
-	if ( complete->code == XHCI_CMPLT_CMD_STOPPED ) {
1612
+	if ( trb->code == XHCI_CMPLT_CMD_STOPPED ) {
1613
 		DBGC2 ( xhci, "XHCI %p command ring stopped\n", xhci );
1613
 		DBGC2 ( xhci, "XHCI %p command ring stopped\n", xhci );
1614
 		return;
1614
 		return;
1615
 	}
1615
 	}
1616
 
1616
 
1617
 	/* Ignore unexpected completions */
1617
 	/* Ignore unexpected completions */
1618
 	if ( ! xhci->pending ) {
1618
 	if ( ! xhci->pending ) {
1619
-		rc = -ECODE ( complete->code );
1619
+		rc = -ECODE ( trb->code );
1620
 		DBGC ( xhci, "XHCI %p unexpected completion (code %d): %s\n",
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
 		return;
1623
 		return;
1624
 	}
1624
 	}
1625
 
1625
 
1628
 
1628
 
1629
 	/* Sanity check */
1629
 	/* Sanity check */
1630
 	assert ( xhci_ring_consumed ( &xhci->command ) ==
1630
 	assert ( xhci_ring_consumed ( &xhci->command ) ==
1631
-		 le64_to_cpu ( complete->command ) );
1631
+		 le64_to_cpu ( trb->command ) );
1632
 
1632
 
1633
 	/* Record completion */
1633
 	/* Record completion */
1634
-	memcpy ( xhci->pending, complete, sizeof ( *xhci->pending ) );
1634
+	memcpy ( xhci->pending, trb, sizeof ( *xhci->pending ) );
1635
 	xhci->pending = NULL;
1635
 	xhci->pending = NULL;
1636
 }
1636
 }
1637
 
1637
 
1639
  * Handle port status event
1639
  * Handle port status event
1640
  *
1640
  *
1641
  * @v xhci		xHCI device
1641
  * @v xhci		xHCI device
1642
- * @v port		Port status event
1642
+ * @v trb		Port status event
1643
  */
1643
  */
1644
 static void xhci_port_status ( struct xhci_device *xhci,
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
 	uint32_t portsc;
1647
 	uint32_t portsc;
1647
 
1648
 
1648
 	/* Sanity check */
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
 	portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE );
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
 	/* Report port status change */
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
  * Handle host controller event
1663
  * Handle host controller event
1662
  *
1664
  *
1663
  * @v xhci		xHCI device
1665
  * @v xhci		xHCI device
1664
- * @v host		Host controller event
1666
+ * @v trb		Host controller event
1665
  */
1667
  */
1666
 static void xhci_host_controller ( struct xhci_device *xhci,
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
 	int rc;
1670
 	int rc;
1669
 
1671
 
1670
 	/* Construct error */
1672
 	/* Construct error */
1671
-	rc = -ECODE ( host->code );
1673
+	rc = -ECODE ( trb->code );
1672
 	DBGC ( xhci, "XHCI %p host controller event (code %d): %s\n",
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
 	unsigned int psiv;
3016
 	unsigned int psiv;
3015
 	int ccs;
3017
 	int ccs;
3016
 	int ped;
3018
 	int ped;
3019
+	int csc;
3017
 	int speed;
3020
 	int speed;
3018
 	int rc;
3021
 	int rc;
3019
 
3022
 
3021
 	portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) );
3024
 	portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) );
3022
 	DBGC2 ( xhci, "XHCI %p port %d status is %08x\n",
3025
 	DBGC2 ( xhci, "XHCI %p port %d status is %08x\n",
3023
 		xhci, port->address, portsc );
3026
 		xhci, port->address, portsc );
3024
-
3025
-	/* Check whether or not port is connected */
3026
 	ccs = ( portsc & XHCI_PORTSC_CCS );
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
 	if ( ! ccs ) {
3038
 	if ( ! ccs ) {
3028
 		port->speed = USB_SPEED_NONE;
3039
 		port->speed = USB_SPEED_NONE;
3029
 		return 0;
3040
 		return 0;
3032
 	/* For USB2 ports, the PSIV field is not valid until the port
3043
 	/* For USB2 ports, the PSIV field is not valid until the port
3033
 	 * completes reset and becomes enabled.
3044
 	 * completes reset and becomes enabled.
3034
 	 */
3045
 	 */
3035
-	ped = ( portsc & XHCI_PORTSC_PED );
3036
 	if ( ( port->protocol < USB_PROTO_3_0 ) && ! ped ) {
3046
 	if ( ( port->protocol < USB_PROTO_3_0 ) && ! ped ) {
3037
 		port->speed = USB_SPEED_FULL;
3047
 		port->speed = USB_SPEED_FULL;
3038
 		return 0;
3048
 		return 0;
3039
 	}
3049
 	}
3040
 
3050
 
3041
 	/* Get port speed and map to generic USB speed */
3051
 	/* Get port speed and map to generic USB speed */
3042
-	psiv = XHCI_PORTSC_PSIV ( portsc );
3043
 	speed = xhci_port_speed ( xhci, port->address, psiv );
3052
 	speed = xhci_port_speed ( xhci, port->address, psiv );
3044
 	if ( speed < 0 ) {
3053
 	if ( speed < 0 ) {
3045
 		rc = speed;
3054
 		rc = speed;

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

747
 	unsigned int protocol;
747
 	unsigned int protocol;
748
 	/** Port speed */
748
 	/** Port speed */
749
 	unsigned int speed;
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
 	/** Port has an attached device */
756
 	/** Port has an attached device */
751
 	int attached;
757
 	int attached;
752
 	/** Currently attached device (if in use)
758
 	/** Currently attached device (if in use)

Loading…
Cancel
Save