84
84
#include <unistd.h>
85
85
#include <fcntl.h>
86
86
#include <ctype.h>
87
+ #include <semaphore.h>
87
88
#include "usb.h"
88
89
#include "usbdi.h"
89
90
#include "xhcireg.h"
@@ -253,6 +254,13 @@ struct pci_xhci_rtsregs {
253
254
uint32_t event_pcs ; /* producer cycle state flag */
254
255
};
255
256
257
+ /* this is used to describe the VBus Drop state */
258
+ enum pci_xhci_vbdp_state {
259
+ S3_VBDP_NONE = 0 ,
260
+ S3_VBDP_START ,
261
+ S3_VBDP_END
262
+ };
263
+
256
264
struct pci_xhci_excap_ptr {
257
265
uint8_t cap_id ;
258
266
uint8_t cap_ptr ;
@@ -351,6 +359,13 @@ struct pci_xhci_native_port {
351
359
uint8_t state ;
352
360
};
353
361
362
+ /* This is used to describe the VBus Drop state */
363
+ struct pci_xhci_vbdp_dev_state {
364
+ struct usb_devpath path ;
365
+ uint8_t vport ;
366
+ uint8_t state ;
367
+ };
368
+
354
369
struct pci_xhci_vdev {
355
370
struct pci_vdev * dev ;
356
371
pthread_mutex_t mtx ;
@@ -384,6 +399,12 @@ struct pci_xhci_vdev {
384
399
int usb2_port_start ;
385
400
int usb3_port_start ;
386
401
402
+ pthread_t vbdp_thread ;
403
+ sem_t vbdp_sem ;
404
+ bool vbdp_polling ;
405
+ int vbdp_dev_num ;
406
+ struct pci_xhci_vbdp_dev_state vbdp_devs [XHCI_MAX_VIRT_PORTS ];
407
+
387
408
/*
388
409
* native_ports uses for record the command line assigned native root
389
410
* hub ports and its child external hub ports.
@@ -491,7 +512,7 @@ pci_xhci_get_free_vport(struct pci_xhci_vdev *xdev,
491
512
struct usb_native_devinfo * di )
492
513
{
493
514
int ports , porte ;
494
- int i , j ;
515
+ int i , j , k ;
495
516
496
517
assert (xdev );
497
518
assert (di );
@@ -504,10 +525,15 @@ pci_xhci_get_free_vport(struct pci_xhci_vdev *xdev,
504
525
porte = ports + (XHCI_MAX_DEVS / 2 );
505
526
506
527
for (i = ports ; i <= porte ; i ++ ) {
507
- for (j = 0 ; j < XHCI_MAX_VIRT_PORTS ; j ++ )
528
+ for (j = 0 ; j < XHCI_MAX_VIRT_PORTS ; j ++ ) {
508
529
if (xdev -> native_ports [j ].vport == i )
509
530
break ;
510
531
532
+ k = xdev -> vbdp_dev_num ;
533
+ if (k > 0 && xdev -> vbdp_devs [j ].state == S3_VBDP_START
534
+ && xdev -> vbdp_devs [j ].vport == i )
535
+ break ;
536
+ }
511
537
if (j >= XHCI_MAX_VIRT_PORTS )
512
538
return i ;
513
539
}
@@ -673,15 +699,54 @@ pci_xhci_unassign_hub_ports(struct pci_xhci_vdev *xdev,
673
699
return 0 ;
674
700
}
675
701
702
+
703
+ static void *
704
+ xhci_vbdp_thread (void * data )
705
+ {
706
+ int i , j ;
707
+ int speed ;
708
+ struct pci_xhci_vdev * xdev ;
709
+ struct pci_xhci_native_port * p ;
710
+
711
+ xdev = data ;
712
+ assert (xdev );
713
+
714
+ while (xdev -> vbdp_polling ) {
715
+
716
+ sem_wait (& xdev -> vbdp_sem );
717
+ for (i = 0 ; i < XHCI_MAX_VIRT_PORTS ; ++ i )
718
+ if (xdev -> vbdp_devs [i ].state == S3_VBDP_END ) {
719
+ xdev -> vbdp_devs [i ].state = S3_VBDP_NONE ;
720
+ break ;
721
+ }
722
+
723
+ j = pci_xhci_get_native_port_index_by_path (xdev ,
724
+ & xdev -> vbdp_devs [i ].path );
725
+ if (j < 0 )
726
+ continue ;
727
+
728
+ p = & xdev -> native_ports [j ];
729
+ if (p -> state != VPORT_CONNECTED )
730
+ continue ;
731
+
732
+ speed = pci_xhci_convert_speed (p -> info .speed );
733
+ pci_xhci_connect_port (xdev , p -> vport , speed , 1 );
734
+ UPRINTF (LINF , "change portsc for %d-%s\r\n" , p -> info .path .bus ,
735
+ usb_dev_path (& p -> info .path ));
736
+ }
737
+ return NULL ;
738
+ }
739
+
676
740
static int
677
741
pci_xhci_native_usb_dev_conn_cb (void * hci_data , void * dev_data )
678
742
{
679
743
struct pci_xhci_vdev * xdev ;
680
744
struct usb_native_devinfo * di ;
681
- int vport ;
745
+ int vport = -1 ;
682
746
int index ;
683
747
int rc ;
684
- int speed ;
748
+ int i ;
749
+ int s3_conn = 0 ;
685
750
686
751
xdev = hci_data ;
687
752
@@ -715,7 +780,23 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
715
780
UPRINTF (LDBG , "%04x:%04x %d-%s belong to this vm.\r\n" , di -> vid ,
716
781
di -> pid , di -> path .bus , usb_dev_path (& di -> path ));
717
782
718
- vport = pci_xhci_get_free_vport (xdev , di );
783
+ for (i = 0 ; xdev -> vbdp_dev_num && i < XHCI_MAX_VIRT_PORTS ; ++ i ) {
784
+ if (xdev -> vbdp_devs [i ].state != S3_VBDP_START )
785
+ continue ;
786
+
787
+ if (!usb_dev_path_cmp (& di -> path , & xdev -> vbdp_devs [i ].path ))
788
+ continue ;
789
+
790
+ s3_conn = 1 ;
791
+ vport = xdev -> vbdp_devs [i ].vport ;
792
+ UPRINTF (LINF , "Skip and cache connect event for %d-%s\r\n" ,
793
+ di -> path .bus , usb_dev_path (& di -> path ));
794
+ break ;
795
+ }
796
+
797
+ if (vport <= 0 )
798
+ vport = pci_xhci_get_free_vport (xdev , di );
799
+
719
800
if (vport <= 0 ) {
720
801
UPRINTF (LFTL , "no free virtual port for native device %d-%s"
721
802
"\r\n" , di -> path .bus ,
@@ -731,15 +812,11 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
731
812
di -> vid , di -> pid , di -> path .bus ,
732
813
usb_dev_path (& di -> path ), vport );
733
814
734
- /* TODO: should revisit in deeper level */
735
- if (XHCI_PS_PLS_GET (XHCI_PORTREG_PTR (xdev , vport )-> portsc ) ==
736
- UPS_PORT_LS_U3 ) {
737
- speed = pci_xhci_convert_speed (di -> speed );
738
- XHCI_PORTREG_PTR (xdev , vport )-> portsc |= (XHCI_PS_CCS |
739
- XHCI_PS_PP | XHCI_PS_CSC |
740
- XHCI_PS_SPEED_SET (speed ));
815
+ /* we will report connecting event in xhci_vbdp_thread for
816
+ * device that hasn't complete the S3 process
817
+ */
818
+ if (s3_conn )
741
819
return 0 ;
742
- }
743
820
744
821
/* Trigger port change event for the arriving device */
745
822
if (pci_xhci_connect_port (xdev , vport , di -> speed , 1 ))
@@ -761,6 +838,7 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
761
838
int need_intr = 1 ;
762
839
int index ;
763
840
int rc ;
841
+ int i ;
764
842
765
843
assert (hci_data );
766
844
assert (dev_data );
@@ -814,25 +892,24 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
814
892
if (xdev -> slots [slot ] == edev )
815
893
break ;
816
894
817
- assert ( state == VPORT_EMULATED || state == VPORT_CONNECTED );
818
- xdev -> native_ports [ index ].state = VPORT_ASSIGNED ;
819
- xdev -> native_ports [ index ]. vport = 0 ;
895
+ for ( i = 0 ; xdev -> vbdp_dev_num && i < XHCI_MAX_VIRT_PORTS ; ++ i ) {
896
+ if ( xdev -> vbdp_devs [ i ].state != S3_VBDP_START )
897
+ continue ;
820
898
821
- /* TODO: should revisit in deeper level */
822
- if (XHCI_PS_PLS_GET (XHCI_PORTREG_PTR (xdev , vport )-> portsc ) ==
823
- UPS_PORT_LS_U3 ) {
899
+ if (!usb_dev_path_cmp (& xdev -> vbdp_devs [i ].path , & di -> path ))
900
+ continue ;
824
901
825
- XHCI_PORTREG_PTR (xdev , vport )-> portsc &= ~(XHCI_PS_CSC |
826
- XHCI_PS_CCS | XHCI_PS_PED | XHCI_PS_PP );
827
- edev -> dev_slotstate = XHCI_ST_DISABLED ;
828
- xdev -> devices [vport ] = NULL ;
829
- xdev -> slots [slot ] = NULL ;
830
- xdev -> slot_allocated [slot ] = false;
831
- pci_xhci_dev_destroy (edev );
832
- need_intr = 0 ;
902
+ /*
903
+ * we do nothing here for device that is in the middle of
904
+ * S3 resuming process.
905
+ */
833
906
return 0 ;
834
907
}
835
908
909
+ assert (state == VPORT_EMULATED || state == VPORT_CONNECTED );
910
+ xdev -> native_ports [index ].state = VPORT_ASSIGNED ;
911
+ xdev -> native_ports [index ].vport = 0 ;
912
+
836
913
UPRINTF (LDBG , "report virtual port %d status %d\r\n" , vport , state );
837
914
if (pci_xhci_disconnect_port (xdev , vport , need_intr )) {
838
915
UPRINTF (LFTL , "fail to report event\r\n" );
@@ -1107,6 +1184,9 @@ pci_xhci_reset(struct pci_xhci_vdev *xdev)
1107
1184
static uint32_t
1108
1185
pci_xhci_usbcmd_write (struct pci_xhci_vdev * xdev , uint32_t cmd )
1109
1186
{
1187
+ int i , j ;
1188
+ struct pci_xhci_native_port * p ;
1189
+
1110
1190
if (cmd & XHCI_CMD_RS ) {
1111
1191
xdev -> opregs .usbcmd |= XHCI_CMD_RS ;
1112
1192
xdev -> opregs .usbsts &= ~XHCI_STS_HCH ;
@@ -1126,6 +1206,37 @@ pci_xhci_usbcmd_write(struct pci_xhci_vdev *xdev, uint32_t cmd)
1126
1206
cmd &= ~XHCI_CMD_HCRST ;
1127
1207
}
1128
1208
1209
+ if (cmd & XHCI_CMD_CSS ) {
1210
+ /* TODO: should think about what happen if system S3 fail
1211
+ * and under that situation, the vbdp_devs and se_dev_num
1212
+ * should also need to be cleared
1213
+ */
1214
+ xdev -> vbdp_dev_num = 0 ;
1215
+ memset (xdev -> vbdp_devs , 0 , sizeof (xdev -> vbdp_devs ));
1216
+
1217
+ for (i = 0 ; i < XHCI_MAX_VIRT_PORTS ; ++ i ) {
1218
+ p = & xdev -> native_ports [i ];
1219
+ if (xdev -> native_ports [i ].state == VPORT_EMULATED ) {
1220
+ /* save the device state before suspending */
1221
+ j = xdev -> vbdp_dev_num ;
1222
+ xdev -> vbdp_devs [j ].path = p -> info .path ;
1223
+ xdev -> vbdp_devs [j ].vport = p -> vport ;
1224
+ xdev -> vbdp_devs [j ].state = S3_VBDP_START ;
1225
+ xdev -> vbdp_dev_num ++ ;
1226
+
1227
+ /* clear PORTSC register */
1228
+ pci_xhci_init_port (xdev , p -> vport );
1229
+
1230
+ /* clear other information for this device*/
1231
+ p -> vport = 0 ;
1232
+ p -> state = VPORT_ASSIGNED ;
1233
+ UPRINTF (LINF , "s3: save %d-%s state\r\n" ,
1234
+ p -> info .path .bus ,
1235
+ usb_dev_path (& p -> info .path ));
1236
+ }
1237
+ }
1238
+ }
1239
+
1129
1240
cmd &= ~(XHCI_CMD_CSS | XHCI_CMD_CRS );
1130
1241
return cmd ;
1131
1242
}
@@ -1631,9 +1742,10 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
1631
1742
{
1632
1743
struct pci_xhci_dev_emu * dev ;
1633
1744
struct usb_dev * udev ;
1634
- struct usb_native_devinfo * di ;
1745
+ struct usb_native_devinfo * di = NULL ;
1746
+ struct usb_devpath * path ;
1635
1747
uint32_t cmderr ;
1636
- int i , index ;
1748
+ int i , j , index ;
1637
1749
1638
1750
UPRINTF (LDBG , "pci_xhci disable slot %u\r\n" , slot );
1639
1751
@@ -1678,11 +1790,31 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
1678
1790
di = & udev -> info ;
1679
1791
index = pci_xhci_get_native_port_index_by_path (xdev , & di -> path );
1680
1792
if (index < 0 ) {
1793
+ /*
1794
+ * one possible reason for failing to find the device is
1795
+ * it is plugged out during the resuming process. we
1796
+ * should give the xhci_vbdp_thread an opportunity to
1797
+ * try.
1798
+ */
1799
+ sem_post (& xdev -> vbdp_sem );
1681
1800
cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON ;
1682
1801
goto done ;
1683
1802
}
1684
1803
1685
1804
pci_xhci_dev_destroy (dev );
1805
+
1806
+ for (j = 0 ; j < XHCI_MAX_VIRT_PORTS ; ++ j ) {
1807
+ path = & xdev -> vbdp_devs [j ].path ;
1808
+
1809
+ if (!usb_dev_path_cmp (path , & di -> path ))
1810
+ continue ;
1811
+
1812
+ xdev -> vbdp_devs [j ].state = S3_VBDP_END ;
1813
+ xdev -> vbdp_dev_num -- ;
1814
+ sem_post (& xdev -> vbdp_sem );
1815
+ UPRINTF (LINF , "signal device %d-%s to connect\r\n" ,
1816
+ di -> path .bus , usb_dev_path (& di -> path ));
1817
+ }
1686
1818
UPRINTF (LINF , "disable slot %d for native device %d-%s"
1687
1819
"\r\n" , slot , di -> path .bus ,
1688
1820
usb_dev_path (& di -> path ));
@@ -4018,6 +4150,14 @@ pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
4018
4150
4019
4151
pthread_mutex_init (& xdev -> mtx , NULL );
4020
4152
4153
+ /* create vbdp_thread */
4154
+ xdev -> vbdp_polling = true;
4155
+ sem_init (& xdev -> vbdp_sem , 0 , 0 );
4156
+ error = pthread_create (& xdev -> vbdp_thread , NULL , xhci_vbdp_thread ,
4157
+ xdev );
4158
+ if (error )
4159
+ goto done ;
4160
+
4021
4161
xhci_in_use = 1 ;
4022
4162
done :
4023
4163
if (error ) {
@@ -4057,6 +4197,11 @@ pci_xhci_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
4057
4197
4058
4198
usb_dev_sys_deinit ();
4059
4199
4200
+ xdev -> vbdp_polling = false;
4201
+ sem_post (& xdev -> vbdp_sem );
4202
+ pthread_join (xdev -> vbdp_thread , NULL );
4203
+ sem_close (& xdev -> vbdp_sem );
4204
+
4060
4205
pthread_mutex_destroy (& xdev -> mtx );
4061
4206
free (xdev );
4062
4207
xhci_in_use = 0 ;
0 commit comments