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