- Timestamp:
- Jan 9, 2019 9:38:28 AM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/USB/linux/USBProxyDevice-linux.cpp
r76553 r76724 20 20 * Defined Constants And Macros * 21 21 *********************************************************************************************************************************/ 22 /** Define NO_PORT_RESET to skip the slow and broken linux port reset.23 * Resetting will break PalmOne. */24 #define NO_PORT_RESET25 /** Define NO_LOGICAL_RECONNECT to skip the broken logical reconnect handling. */26 #define NO_LOGICAL_RECONNECT27 22 28 23 … … 95 90 #include <iprt/string.h> 96 91 #include <iprt/list.h> 97 #if defined(NO_PORT_RESET) && !defined(NO_LOGICAL_RECONNECT)98 # include <iprt/thread.h>99 #endif100 92 #include <iprt/time.h> 101 93 #include "../USBProxyDevice.h" … … 163 155 * Used to figure out the configuration after a reset. */ 164 156 char *pszPath; 157 /** Mask of claimed interfaces. */ 158 uint32_t fClaimedIfsMask; 165 159 } USBPROXYDEVLNX, *PUSBPROXYDEVLNX; 166 160 … … 169 163 * Internal Functions * 170 164 *********************************************************************************************************************************/ 171 static int usbProxyLinuxDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries);172 165 static void usbProxLinuxUrbUnplugged(PUSBPROXYDEV pProxyDev); 173 static void usbProxyLinuxSetConnected(PUSBPROXYDEV pProyxDev, int iIf, bool fConnect, bool fQuiet); 174 static PUSBPROXYURBLNX usbProxyLinuxUrbAlloc(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pSplitHead); 175 static void usbProxyLinuxUrbFree(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx); 176 static void usbProxyLinuxUrbFreeSplitList(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx); 177 static int usbProxyLinuxFindActiveConfig(PUSBPROXYDEV pProxyDev, const char *pszPath, int *piFirstCfg); 178 166 static DECLCALLBACK(int) usbProxyLinuxClaimInterface(PUSBPROXYDEV pProxyDev, int iIf); 167 static DECLCALLBACK(int) usbProxyLinuxReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf); 179 168 180 169 … … 676 665 pDevLnx->fUsingSysfs = fUsingSysfs; 677 666 pDevLnx->hFile = hFile; 667 pDevLnx->fClaimedIfsMask = 0; 678 668 rc = RTCritSectInit(&pDevLnx->CritSect); 679 669 if (RT_SUCCESS(rc)) … … 775 765 else if (errno != ENODEV) 776 766 LogRel(("USB: Reset failed, errno=%d, pProxyDev=%s.\n", errno, usbProxyGetName(pProxyDev))); 777 else 767 else /* This will happen if device was detached. */ 778 768 Log(("USB: Reset failed, errno=%d (ENODEV), pProxyDev=%s.\n", errno, usbProxyGetName(pProxyDev))); 779 769 } … … 833 823 834 824 835 #if defined(NO_PORT_RESET) && !defined(NO_LOGICAL_RECONNECT)836 /**837 * Look for the logically reconnected device.838 * After 5 seconds we'll give up.839 *840 * @returns VBox status code.841 * @thread Reset thread or EMT.842 */843 static int usb_reset_logical_reconnect(PUSBPROXYDEV pDev)844 {845 FILE * pFile;846 uint64_t u64StartTS = RTTimeMilliTS();847 848 Log2(("usb_reset_logical_reconnect: pDev=%p:{.bBus=%#x, .bDevNum=%#x, .idVendor=%#x, .idProduct=%#x, .bcdDevice=%#x, .u64SerialHash=%#llx .bDevNumParent=%#x .bPort=%#x .bLevel=%#x}\n",849 pDev, pDev->Info.bBus, pDev->Info.bDevNum, pDev->Info.idVendor, pDev->Info.idProduct, pDev->Info.bcdDevice,850 pDev->Info.u64SerialHash, pDev->Info.bDevNumParent, pDev->Info.bPort, pDev->Info.bLevel));851 852 /* First, let hubd get a chance to logically reconnect the device. */853 if (!RTThreadYield())854 RTThreadSleep(1);855 856 /*857 * Search for the new device address.858 */859 pFile = get_devices_file();860 if (!pFile)861 return VERR_FILE_NOT_FOUND;862 863 /*864 * Loop until found or 5seconds have elapsed.865 */866 for (;;) {867 struct pollfd pfd;868 uint8_t tmp;869 int rc;870 char buf[512];871 uint64_t u64Elapsed;872 int got = 0;873 struct usb_dev_entry id = {0};874 875 /*876 * Since this is kernel ABI we don't need to be too fussy about877 * the parsing.878 */879 while (fgets(buf, sizeof(buf), pFile)) {880 char *psz = strchr(buf, '\n');881 if ( psz == NULL ) {882 AssertMsgFailed(("usb_reset_logical_reconnect: Line to long!!\n"));883 break;884 }885 *psz = '\0';886 887 switch ( buf[0] ) {888 case 'T': /* topology */889 /* Check if we've got enough for a device. */890 if (got >= 2) {891 Log2(("usb_reset_logical_reconnect: {.bBus=%#x, .bDevNum=%#x, .idVendor=%#x, .idProduct=%#x, .bcdDevice=%#x, .u64SerialHash=%#llx, .bDevNumParent=%#x, .bPort=%#x, .bLevel=%#x}\n",892 id.bBus, id.bDevNum, id.idVendor, id.idProduct, id.bcdDevice, id.u64SerialHash, id.bDevNumParent, id.bPort, id.bLevel));893 if ( id.bDevNumParent == pDev->Info.bDevNumParent894 && id.idVendor == pDev->Info.idVendor895 && id.idProduct == pDev->Info.idProduct896 && id.bcdDevice == pDev->Info.bcdDevice897 && id.u64SerialHash == pDev->Info.u64SerialHash898 && id.bBus == pDev->Info.bBus899 && id.bPort == pDev->Info.bPort900 && id.bLevel == pDev->Info.bLevel) {901 goto l_found;902 }903 }904 905 /* restart */906 got = 0;907 memset(&id, 0, sizeof(id));908 909 /*T: Bus=04 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0*/910 Log2(("usb_reset_logical_reconnect: %s\n", buf));911 buf[10] = '\0';912 if ( !get_u8(buf + 8, &id.bBus) )913 break;914 buf[49] = '\0';915 psz = buf + 46;916 while ( *psz == ' ' )917 psz++;918 if ( !get_u8(psz, &id.bDevNum) )919 break;920 921 buf[17] = '\0';922 if ( !get_u8(buf + 15, &id.bLevel) )923 break;924 buf[25] = '\0';925 if ( !get_u8(buf + 23, &id.bDevNumParent) )926 break;927 buf[33] = '\0';928 if ( !get_u8(buf + 31, &id.bPort) )929 break;930 got++;931 break;932 933 case 'P': /* product */934 Log2(("usb_reset_logical_reconnect: %s\n", buf));935 buf[15] = '\0';936 if ( !get_x16(buf + 11, &id.idVendor) )937 break;938 buf[27] = '\0';939 if ( !get_x16(buf + 23, &id.idProduct) )940 break;941 buf[34] = '\0';942 if ( buf[32] == ' ' )943 buf[32] = '0';944 id.bcdDevice = 0;945 if ( !get_x8(buf + 32, &tmp) )946 break;947 id.bcdDevice = tmp << 8;948 if ( !get_x8(buf + 35, &tmp) )949 break;950 id.bcdDevice |= tmp;951 got++;952 break;953 954 case 'S': /* String descriptor */955 /* Skip past "S:" and then the whitespace */956 for(psz = buf + 2; *psz != '\0'; psz++)957 if ( !RT_C_IS_SPACE(*psz) )958 break;959 960 /* If it is a serial number string, skip past961 * "SerialNumber="962 */963 if (strncmp(psz, RT_STR_TUPLE("SerialNumber=")))964 break;965 966 Log2(("usb_reset_logical_reconnect: %s\n", buf));967 psz += sizeof("SerialNumber=") - 1;968 969 usb_serial_hash(psz, &id.u64SerialHash);970 break;971 }972 }973 974 /*975 * Check last.976 */977 if ( got >= 2978 && id.bDevNumParent == pDev->Info.bDevNumParent979 && id.idVendor == pDev->Info.idVendor980 && id.idProduct == pDev->Info.idProduct981 && id.bcdDevice == pDev->Info.bcdDevice982 && id.u64SerialHash == pDev->Info.u64SerialHash983 && id.bBus == pDev->Info.bBus984 && id.bPort == pDev->Info.bPort985 && id.bLevel == pDev->Info.bLevel) {986 l_found:987 /* close the existing file descriptor. */988 RTFileClose(pDevLnx->File);989 pDevLnx->File = NIL_RTFILE;990 991 /* open stuff at the new address. */992 pDev->Info = id;993 if (usbProxyLinuxOpen(pDev, &id))994 return VINF_SUCCESS;995 break;996 }997 998 /*999 * Wait for a while and then check the file again.1000 */1001 u64Elapsed = RTTimeMilliTS() - u64StartTS;1002 if (u64Elapsed >= 5000/*ms*/)1003 break; /* done */1004 1005 pfd.fd = fileno(pFile);1006 pfd.events = POLLIN;1007 rc = poll(&pfd, 1, 5000 - u64Elapsed);1008 if (rc < 0) {1009 AssertMsg(errno == EINTR, ("errno=%d\n", errno));1010 RTThreadSleep(32); /* paranoia: don't eat cpu on failure */1011 }1012 1013 rewind(pFile);1014 } /* for loop */1015 1016 return VERR_GENERAL_FAILURE;1017 }1018 #endif /* !NO_PORT_RESET && !NO_LOGICAL_RECONNECT */1019 1020 1021 825 /** @interface_method_impl{USBPROXYBACK,pfnReset} */ 1022 static DECLCALLBACK(int) usbProxyLinuxReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux) 1023 { 1024 #ifdef NO_PORT_RESET 826 static DECLCALLBACK(int) usbProxyLinuxReset(PUSBPROXYDEV pProxyDev, bool fRootHubReset) 827 { 1025 828 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX); 1026 1027 /* 1028 * Specific device resets are NOPs. 1029 * Root hub resets that affects all devices are executed. 1030 * 1031 * The reasoning is that when a root hub reset is done, the guest shouldn't 1032 * will have to re enumerate the devices after doing this kind of reset. 1033 * So, it doesn't really matter if a device is 'logically disconnected'. 1034 */ 1035 if ( !fResetOnLinux 1036 || pProxyDev->fMaskedIfs) 1037 LogFlow(("usbProxyLinuxReset: pProxyDev=%s - NO_PORT_RESET\n", usbProxyGetName(pProxyDev))); 1038 else 1039 { 1040 LogFlow(("usbProxyLinuxReset: pProxyDev=%s - Real Reset!\n", usbProxyGetName(pProxyDev))); 1041 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RESET, NULL, false, 10)) 1042 { 1043 int rc = errno; 1044 Log(("usb-linux: Reset failed, rc=%s errno=%d.\n", 1045 RTErrGetShort(RTErrConvertFromErrno(rc)), rc)); 1046 pProxyDev->iActiveCfg = -1; 1047 return RTErrConvertFromErrno(rc); 1048 } 1049 1050 /* find the active config - damn annoying. */ 1051 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pDevLnx->pszPath, NULL); 1052 LogFlow(("usbProxyLinuxReset: returns successfully iActiveCfg=%d\n", pProxyDev->iActiveCfg)); 1053 } 1054 pProxyDev->cIgnoreSetConfigs = 2; 1055 1056 #else /* !NO_PORT_RESET */ 1057 1058 /* 1059 * This is the alternative, we will always reset when asked to do so. 1060 * 1061 * The problem we're facing here is that on reset failure linux will do 1062 * a 'logical reconnect' on the device. This will invalidate the current 1063 * handle and we'll have to reopen the device. This is problematic to say 1064 * the least, especially since it happens pretty often. 1065 */ 829 RT_NOREF(fRootHubReset); 830 Assert(!pProxyDev->fMaskedIfs); 1066 831 LogFlow(("usbProxyLinuxReset: pProxyDev=%s\n", usbProxyGetName(pProxyDev))); 1067 # ifndef NO_LOGICAL_RECONNECT 1068 ASMAtomicIncU32(&g_cResetActive); 1069 # endif 832 833 uint32_t fActiveIfsMask = pDevLnx->fClaimedIfsMask; 834 unsigned i; 835 836 /* 837 * Before reset, release claimed interfaces. This less than obvious move 838 * prevents Linux from rebinding in-kernel drivers to the device after reset. 839 */ 840 for (i = 0; i < (sizeof(fActiveIfsMask) * 8); ++i) 841 { 842 if (fActiveIfsMask & RT_BIT(i)) 843 { 844 usbProxyLinuxReleaseInterface(pProxyDev, i); 845 } 846 } 1070 847 1071 848 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RESET, NULL, false, 10)) 1072 849 { 1073 850 int rc = errno; 1074 # ifndef NO_LOGICAL_RECONNECT 1075 if (rc == ENODEV) 1076 { 1077 /* 1078 * This usually happens because of a 'logical disconnection'. 1079 * So, we're in for a real treat from our excellent OS now... 1080 */ 1081 rc2 = usb_reset_logical_reconnect(pProxyDev); 1082 if (RT_FAILURE(rc2)) 1083 usbProxLinuxUrbUnplugged(pProxyDev); 1084 if (RT_SUCCESS(rc2)) 1085 { 1086 ASMAtomicDecU32(&g_cResetActive); 1087 LogFlow(("usbProxyLinuxReset: returns success (after recovering disconnected device!)\n")); 1088 return VINF_SUCCESS; 1089 } 1090 } 1091 ASMAtomicDecU32(&g_cResetActive); 1092 # endif /* NO_LOGICAL_RECONNECT */ 1093 1094 Log(("usb-linux: Reset failed, rc=%s errno=%d.\n", 1095 RTErrGetShort(RTErrConvertFromErrno(rc)), rc)); 851 LogRel(("usb-linux: Reset failed, rc=%s errno=%d.\n", 852 RTErrGetShort(RTErrConvertFromErrno(rc)), rc)); 1096 853 pProxyDev->iActiveCfg = -1; 1097 854 return RTErrConvertFromErrno(rc); 1098 855 } 1099 856 1100 # ifndef NO_LOGICAL_RECONNECT 1101 ASMAtomicDecU32(&g_cResetActive); 1102 # endif 857 /* 858 * Now reclaim previously claimed interfaces. If that doesn't work, let's hope 859 * the guest/VUSB can recover from that. Can happen if reset changes configuration. 860 */ 861 for (i = 0; i < (sizeof(fActiveIfsMask) * 8); ++i) 862 { 863 if (fActiveIfsMask & RT_BIT(i)) 864 { 865 usbProxyLinuxClaimInterface(pProxyDev, i); 866 } 867 } 868 869 /* find the active config - damn annoying. */ 870 pProxyDev->iActiveCfg = usbProxyLinuxFindActiveConfig(pProxyDev, pDevLnx->pszPath, NULL); 871 LogFlow(("usbProxyLinuxReset: returns successfully iActiveCfg=%d\n", pProxyDev->iActiveCfg)); 1103 872 1104 873 pProxyDev->cIgnoreSetConfigs = 2; 1105 LogFlow(("usbProxyLinuxReset: returns success\n"));1106 #endif /* !NO_PORT_RESET */1107 874 return VINF_SUCCESS; 1108 875 } … … 1139 906 static DECLCALLBACK(int) usbProxyLinuxClaimInterface(PUSBPROXYDEV pProxyDev, int iIf) 1140 907 { 908 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX); 909 1141 910 LogFlow(("usbProxyLinuxClaimInterface: pProxyDev=%s ifnum=%#x\n", usbProxyGetName(pProxyDev), iIf)); 1142 911 usbProxyLinuxSetConnected(pProxyDev, iIf, false, false); … … 1144 913 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_CLAIMINTERFACE, &iIf, true, UINT32_MAX)) 1145 914 { 1146 Log(("usb-linux: Claim interface. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev))); 915 pDevLnx->fClaimedIfsMask &= ~RT_BIT(iIf); 916 LogRel(("usb-linux: Claim interface. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev))); 1147 917 return RTErrConvertFromErrno(errno); 1148 918 } 919 pDevLnx->fClaimedIfsMask |= RT_BIT(iIf); 1149 920 return VINF_SUCCESS; 1150 921 } … … 1157 928 static DECLCALLBACK(int) usbProxyLinuxReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf) 1158 929 { 930 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX); 931 1159 932 LogFlow(("usbProxyLinuxReleaseInterface: pProxyDev=%s ifnum=%#x\n", usbProxyGetName(pProxyDev), iIf)); 1160 933 1161 934 if (usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_RELEASEINTERFACE, &iIf, true, UINT32_MAX)) 1162 935 { 1163 Log (("usb-linux: Release interface, errno=%d. pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));936 LogRel(("usb-linux: Release interface, errno=%d. pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev))); 1164 937 return RTErrConvertFromErrno(errno); 1165 938 } 939 pDevLnx->fClaimedIfsMask &= ~RT_BIT(iIf); 1166 940 return VINF_SUCCESS; 1167 941 }
Note:
See TracChangeset
for help on using the changeset viewer.