Changeset 60107 in vbox for trunk/src/VBox/Main
- Timestamp:
- Mar 19, 2016 10:22:46 AM (9 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/idl/VirtualBox.xidl
r60089 r60107 571 571 <!-- 572 572 Machine changes: NVMe storage controller. 573 VirtualBox.xml: Add support for additional USB device sources (e.g. USB/IP) 573 574 --> 574 575 </const> -
trunk/src/VBox/Main/include/USBProxyBackend.h
r60089 r60107 26 26 #include <iprt/poll.h> 27 27 #include <iprt/semaphore.h> 28 #include <iprt/cpp/utils.h> 28 29 29 30 #include "VirtualBoxBase.h" … … 52 53 bool isActive(void); 53 54 const com::Utf8Str &i_getId(); 55 const com::Utf8Str &i_getAddress(); 56 virtual const com::Utf8Str &i_getBackend(); 54 57 uint32_t i_getRefCount(); 55 58 … … 117 120 /** Id of the instance. */ 118 121 const com::Utf8Str m_strId; 122 /** Address of the instance. */ 123 const com::Utf8Str m_strAddress; 124 /** Backend identifier as used in the settings. */ 125 const com::Utf8Str m_strBackend; 119 126 /** Reference counter which prevents the backend instance from being removed. */ 120 127 uint32_t m_cRefs; … … 140 147 int init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId, const com::Utf8Str &strAddress); 141 148 void uninit(); 149 150 const com::Utf8Str &i_getBackend(); 142 151 143 152 virtual void *insertFilter(PCUSBFILTER aFilter); -
trunk/src/VBox/Main/include/USBProxyService.h
r60089 r60107 22 22 #include <VBox/usb.h> 23 23 #include <VBox/usbfilter.h> 24 #include <VBox/settings.h> 24 25 25 26 #include "VirtualBoxBase.h" … … 85 86 void i_getUSBFilters(USBDeviceFilterList *pGlobalFilters); 86 87 88 HRESULT i_loadSettings(const settings::USBDeviceSourcesList &llUSBDeviceSources); 89 HRESULT i_saveSettings(settings::USBDeviceSourcesList &llUSBDeviceSources); 90 87 91 protected: 88 92 ComObjPtr<HostUSBDevice> findDeviceById(IN_GUID aId); … … 91 95 92 96 USBProxyBackend *findUsbProxyBackendById(const com::Utf8Str &strId); 97 98 HRESULT createUSBDeviceSource(const com::Utf8Str &aBackend, const com::Utf8Str &aId, 99 const com::Utf8Str &aAddress, const std::vector<com::Utf8Str> &aPropertyNames, 100 const std::vector<com::Utf8Str> &aPropertyValues); 93 101 94 102 private: -
trunk/src/VBox/Main/src-server/HostImpl.cpp
r60089 r60107 1761 1761 } 1762 1762 } 1763 1764 rc = m->pUSBProxyService->i_loadSettings(data.llUSBDeviceSources); 1763 1765 #else 1764 1766 NOREF(data); … … 1791 1793 #endif /* VBOX_WITH_USB */ 1792 1794 1793 return S_OK;1795 return m->pUSBProxyService->i_saveSettings(data.llUSBDeviceSources); 1794 1796 } 1795 1797 -
trunk/src/VBox/Main/src-server/USBProxyBackend.cpp
r60089 r60107 33 33 #include <iprt/mem.h> 34 34 #include <iprt/string.h> 35 #include <iprt/cpp/utils.h>36 35 37 36 … … 69 68 int USBProxyBackend::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId, const com::Utf8Str &strAddress) 70 69 { 71 NOREF(strAddress); 72 73 m_pUsbProxyService = pUsbProxyService; 74 mThread = NIL_RTTHREAD; 75 mTerminate = false; 76 unconst(m_strId) = strId; 77 m_cRefs = 0; 70 m_pUsbProxyService = pUsbProxyService; 71 mThread = NIL_RTTHREAD; 72 mTerminate = false; 73 unconst(m_strId) = strId; 74 m_cRefs = 0; 75 unconst(m_strAddress) = strAddress; 76 77 unconst(m_strBackend) = Utf8Str::Empty; 78 78 79 79 return VINF_SUCCESS; … … 111 111 } 112 112 113 114 /** 115 * Returns the address of the instance. 116 * 117 * @returns ID string for the instance. 118 */ 119 const com::Utf8Str &USBProxyBackend::i_getAddress() 120 { 121 return m_strAddress; 122 } 123 124 125 /** 126 * Returns the backend of the instance. 127 * 128 * @returns ID string for the instance. 129 */ 130 const com::Utf8Str &USBProxyBackend::i_getBackend() 131 { 132 return m_strBackend; 133 } 113 134 114 135 /** -
trunk/src/VBox/Main/src-server/USBProxyService.cpp
r60089 r60107 210 210 const std::vector<com::Utf8Str> &aPropertyNames, const std::vector<com::Utf8Str> &aPropertyValues) 211 211 { 212 HRESULT hrc = S_OK; 213 214 /* Check whether the ID is used first. */ 212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 213 214 HRESULT hrc = createUSBDeviceSource(aBackend, aId, aAddress, aPropertyNames, aPropertyValues); 215 if (SUCCEEDED(hrc)) 216 { 217 alock.release(); 218 AutoWriteLock vboxLock(mHost->i_parent() COMMA_LOCKVAL_SRC_POS); 219 return mHost->i_parent()->i_saveSettings(); 220 } 221 222 return hrc; 223 } 224 225 HRESULT USBProxyService::removeUSBDeviceSource(const com::Utf8Str &aId) 226 { 227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 228 215 229 for (USBProxyBackendList::iterator it = mBackends.begin(); 216 230 it != mBackends.end(); 217 231 ++it) 218 232 { 219 USBProxyBackend *pUsbProxyBackend = *it;220 221 if (aId.equals(pUsbProxyBackend->i_getId()))222 return setError(VBOX_E_OBJECT_IN_USE,223 tr("The USB device source \"%s\" exists already"), aId.c_str());224 }225 226 /* Create appropriate proxy backend. */227 if (aBackend.equalsIgnoreCase("USBIP"))228 {229 ComObjPtr<USBProxyBackendUsbIp> UsbProxyBackend;230 231 UsbProxyBackend.createObject();232 int vrc = UsbProxyBackend->init(this, aId, aAddress);233 if (RT_FAILURE(vrc))234 hrc = setError(E_FAIL,235 tr("Creating the USB device source \"%s\" using backend \"%s\" failed with %Rrc"),236 aId.c_str(), aBackend.c_str(), vrc);237 else238 mBackends.push_back(static_cast<ComObjPtr<USBProxyBackend> >(UsbProxyBackend));239 }240 else241 hrc = setError(VBOX_E_OBJECT_NOT_FOUND,242 tr("The USB backend \"%s\" is not supported"), aBackend.c_str());243 244 return hrc;245 }246 247 HRESULT USBProxyService::removeUSBDeviceSource(const com::Utf8Str &aId)248 {249 for (USBProxyBackendList::iterator it = mBackends.begin();250 it != mBackends.end();251 ++it)252 {253 233 ComObjPtr<USBProxyBackend> UsbProxyBackend = *it; 254 234 … … 257 237 mBackends.erase(it); 258 238 UsbProxyBackend->uninit(); 259 return S_OK; 239 240 alock.release(); 241 AutoWriteLock vboxLock(mHost->i_parent() COMMA_LOCKVAL_SRC_POS); 242 return mHost->i_parent()->i_saveSettings(); 260 243 } 261 244 } … … 712 695 713 696 /** 697 * Loads the given settings and constructs the additional USB device sources. 698 * 699 * @returns COM status code. 700 * @param llUSBDeviceSources The list of additional device sources. 701 */ 702 HRESULT USBProxyService::i_loadSettings(const settings::USBDeviceSourcesList &llUSBDeviceSources) 703 { 704 HRESULT hrc = S_OK; 705 706 for (settings::USBDeviceSourcesList::const_iterator it = llUSBDeviceSources.begin(); 707 it != llUSBDeviceSources.end() && SUCCEEDED(hrc); 708 ++it) 709 { 710 std::vector<com::Utf8Str> vecPropNames, vecPropValues; 711 const settings::USBDeviceSource &src = *it; 712 hrc = createUSBDeviceSource(src.strBackend, src.strName, src.strAddress, vecPropNames, vecPropValues); 713 } 714 715 return hrc; 716 } 717 718 /** 719 * Saves the additional device sources in the given settings. 720 * 721 * @returns COM status code. 722 * @param llUSBDeviceSources The list of additional device sources. 723 */ 724 HRESULT USBProxyService::i_saveSettings(settings::USBDeviceSourcesList &llUSBDeviceSources) 725 { 726 for (USBProxyBackendList::iterator it = mBackends.begin(); 727 it != mBackends.end(); 728 ++it) 729 { 730 USBProxyBackend *pUsbProxyBackend = *it; 731 732 /* Host backends are not saved as they are always created during startup. */ 733 if (!pUsbProxyBackend->i_getBackend().equals("host")) 734 { 735 settings::USBDeviceSource src; 736 737 src.strBackend = pUsbProxyBackend->i_getBackend(); 738 src.strName = pUsbProxyBackend->i_getId(); 739 src.strAddress = pUsbProxyBackend->i_getAddress(); 740 741 llUSBDeviceSources.push_back(src); 742 } 743 } 744 745 return S_OK; 746 } 747 748 /** 714 749 * Searches the list of devices (mDevices) for the given device. 715 750 * … … 732 767 733 768 return Dev; 769 } 770 771 /** 772 * Creates a new USB device source. 773 * 774 * @returns COM status code. 775 * @param aBackend The backend to use. 776 * @param aId The ID of the source. 777 * @param aAddress The backend specific address. 778 * @param aPropertyNames Vector of optional property keys the backend supports. 779 * @param aPropertyValues Vector of optional property values the backend supports. 780 */ 781 HRESULT USBProxyService::createUSBDeviceSource(const com::Utf8Str &aBackend, const com::Utf8Str &aId, 782 const com::Utf8Str &aAddress, const std::vector<com::Utf8Str> &aPropertyNames, 783 const std::vector<com::Utf8Str> &aPropertyValues) 784 { 785 HRESULT hrc = S_OK; 786 787 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 788 789 /* Check whether the ID is used first. */ 790 for (USBProxyBackendList::iterator it = mBackends.begin(); 791 it != mBackends.end(); 792 ++it) 793 { 794 USBProxyBackend *pUsbProxyBackend = *it; 795 796 if (aId.equals(pUsbProxyBackend->i_getId())) 797 return setError(VBOX_E_OBJECT_IN_USE, 798 tr("The USB device source \"%s\" exists already"), aId.c_str()); 799 } 800 801 /* Create appropriate proxy backend. */ 802 if (aBackend.equalsIgnoreCase("USBIP")) 803 { 804 ComObjPtr<USBProxyBackendUsbIp> UsbProxyBackend; 805 806 UsbProxyBackend.createObject(); 807 int vrc = UsbProxyBackend->init(this, aId, aAddress); 808 if (RT_FAILURE(vrc)) 809 hrc = setError(E_FAIL, 810 tr("Creating the USB device source \"%s\" using backend \"%s\" failed with %Rrc"), 811 aId.c_str(), aBackend.c_str(), vrc); 812 else 813 mBackends.push_back(static_cast<ComObjPtr<USBProxyBackend> >(UsbProxyBackend)); 814 } 815 else 816 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, 817 tr("The USB backend \"%s\" is not supported"), aBackend.c_str()); 818 819 return hrc; 734 820 } 735 821 -
trunk/src/VBox/Main/src-server/darwin/USBProxyBackendDarwin.cpp
r60089 r60107 57 57 USBProxyBackend::init(pUsbProxyService, strId, strAddress); 58 58 59 unconst(m_strBackend) = Utf8Str("host"); 60 59 61 /* 60 62 * Initialize the USB library. -
trunk/src/VBox/Main/src-server/freebsd/USBProxyBackendFreeBSD.cpp
r60089 r60107 72 72 * @returns S_OK on success and non-fatal failures, some COM error otherwise. 73 73 */ 74 int USBProxyBackendFreeBSD::init(const com::Utf8Str &strAddress) 75 { 76 NOREF(strAddress); 74 int USBProxyBackendFreeBSD::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId, const com::Utf8Str &strAddresss) 75 { 76 USBProxyBackend::init(pUsbProxyService, strId, strAddress); 77 78 unconst(m_strBackend) = Utf8Str("host"); 77 79 78 80 /* -
trunk/src/VBox/Main/src-server/generic/USBProxyBackendUsbIp.cpp
r60089 r60107 286 286 USBProxyBackend::init(aUsbProxyService, strId, strAddress); 287 287 288 unconst(m_strBackend) = Utf8Str("USBIP"); 289 288 290 m = new Data; 289 291 … … 327 329 int rc2 = RTPollSetDestroy(m->hPollSet); 328 330 AssertRC(rc2); 331 m->hPollSet = NIL_RTPOLLSET; 329 332 } 330 333 } … … 336 339 rc2 = RTPipeClose(m->hWakeupPipeW); 337 340 AssertRC(rc2); 341 m->hWakeupPipeR = m->hWakeupPipeW = NIL_RTPIPE; 338 342 } 339 343 } 340 344 if (RT_FAILURE(rc)) 345 { 341 346 RTSemFastMutexDestroy(m->hMtxDevices); 347 m->hMtxDevices = NIL_RTSEMFASTMUTEX; 348 } 342 349 } 343 350 … … 888 895 /* Make sure the Bus id is 0 terminated. */ 889 896 pDev->szBusId[31] = '\0'; 890 RTStrAPrintf((char **)&pNew->pszAddress, "usbip://%s:%u:%s", m->pszHost, m->uPort, &pDev->szBusId[0]); 891 892 pNew->idVendor = pDev->u16VendorId; 893 pNew->idProduct = pDev->u16ProductId; 894 pNew->bcdDevice = pDev->u16BcdDevice; 895 pNew->bDeviceClass = pDev->bDeviceClass; 896 pNew->bDeviceSubClass = pDev->bDeviceSubClass; 897 pNew->bDeviceProtocol = pDev->bDeviceProtocol; 898 pNew->bNumConfigurations = pDev->bNumConfigurations; 899 pNew->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; 900 pNew->u64SerialHash = 0; 901 pNew->bBus = (uint8_t)pDev->u32BusNum; 902 pNew->bPort = (uint8_t)pDev->u32DevNum; 903 904 switch (pDev->u32Speed) 905 { 906 case USBIP_SPEED_LOW: 907 pNew->enmSpeed = USBDEVICESPEED_LOW; 908 pNew->bcdUSB = 1 << 8; 909 break; 910 case USBIP_SPEED_FULL: 911 pNew->enmSpeed = USBDEVICESPEED_FULL; 912 pNew->bcdUSB = 1 << 8; 913 break; 914 case USBIP_SPEED_HIGH: 915 pNew->enmSpeed = USBDEVICESPEED_HIGH; 916 pNew->bcdUSB = 2 << 8; 917 break; 918 case USBIP_SPEED_WIRELESS: 919 pNew->enmSpeed = USBDEVICESPEED_VARIABLE; 920 pNew->bcdUSB = 1 << 8; 921 break; 922 case USBIP_SPEED_SUPER: 923 pNew->enmSpeed = USBDEVICESPEED_SUPER; 924 pNew->bcdUSB = 3 << 8; 925 break; 926 case USBIP_SPEED_UNKNOWN: 927 default: 928 pNew->bcdUSB = 1 << 8; 929 pNew->enmSpeed = USBDEVICESPEED_UNKNOWN; 930 } 931 932 /* link it */ 933 pNew->pNext = NULL; 934 pNew->pPrev = *m->ppNext; 935 *m->ppNext = pNew; 936 m->ppNext = &pNew->pNext; 937 m->cDevicesCur++; 938 939 return VINF_SUCCESS; 897 int rc = RTStrAPrintf((char **)&pNew->pszAddress, "usbip://%s:%u:%s", m->pszHost, m->uPort, &pDev->szBusId[0]); 898 if (RT_SUCCESS(rc)) 899 { 900 pNew->idVendor = pDev->u16VendorId; 901 pNew->idProduct = pDev->u16ProductId; 902 pNew->bcdDevice = pDev->u16BcdDevice; 903 pNew->bDeviceClass = pDev->bDeviceClass; 904 pNew->bDeviceSubClass = pDev->bDeviceSubClass; 905 pNew->bDeviceProtocol = pDev->bDeviceProtocol; 906 pNew->bNumConfigurations = pDev->bNumConfigurations; 907 pNew->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; 908 pNew->u64SerialHash = 0; 909 pNew->bBus = (uint8_t)pDev->u32BusNum; 910 pNew->bPort = (uint8_t)pDev->u32DevNum; 911 912 switch (pDev->u32Speed) 913 { 914 case USBIP_SPEED_LOW: 915 pNew->enmSpeed = USBDEVICESPEED_LOW; 916 pNew->bcdUSB = 1 << 8; 917 break; 918 case USBIP_SPEED_FULL: 919 pNew->enmSpeed = USBDEVICESPEED_FULL; 920 pNew->bcdUSB = 1 << 8; 921 break; 922 case USBIP_SPEED_HIGH: 923 pNew->enmSpeed = USBDEVICESPEED_HIGH; 924 pNew->bcdUSB = 2 << 8; 925 break; 926 case USBIP_SPEED_WIRELESS: 927 pNew->enmSpeed = USBDEVICESPEED_VARIABLE; 928 pNew->bcdUSB = 1 << 8; 929 break; 930 case USBIP_SPEED_SUPER: 931 pNew->enmSpeed = USBDEVICESPEED_SUPER; 932 pNew->bcdUSB = 3 << 8; 933 break; 934 case USBIP_SPEED_UNKNOWN: 935 default: 936 pNew->bcdUSB = 1 << 8; 937 pNew->enmSpeed = USBDEVICESPEED_UNKNOWN; 938 } 939 940 /* link it */ 941 pNew->pNext = NULL; 942 pNew->pPrev = *m->ppNext; 943 *m->ppNext = pNew; 944 m->ppNext = &pNew->pNext; 945 m->cDevicesCur++; 946 } 947 948 if (RT_FAILURE(rc)) 949 { 950 if (pNew->pszManufacturer) 951 RTStrFree((char *)pNew->pszManufacturer); 952 if (pNew->pszProduct) 953 RTStrFree((char *)pNew->pszProduct); 954 if (pNew->pszBackend) 955 RTStrFree((char *)pNew->pszBackend); 956 if (pNew->pszAddress) 957 RTStrFree((char *)pNew->pszAddress); 958 RTMemFree(pNew); 959 } 960 961 return rc; 940 962 } 941 963 -
trunk/src/VBox/Main/src-server/linux/USBProxyBackendLinux.cpp
r60106 r60107 84 84 USBProxyBackend::init(pUsbProxyService, strId, strAddress); 85 85 86 unconst(m_strBackend) = Utf8Str("host"); 87 86 88 const char *pcszDevicesRoot; 87 89 int rc = USBProxyLinuxChooseMethod(&mUsingUsbfsDevices, &pcszDevicesRoot); … … 118 120 USBProxyBackend::uninit(); 119 121 } 122 120 123 121 124 /** -
trunk/src/VBox/Main/src-server/solaris/USBProxyBackendSolaris.cpp
r60089 r60107 73 73 { 74 74 USBProxyBackend::init(aUsbProxyService, strId, strAddress); 75 76 unconst(m_strBackend) = Utf8Str("host"); 75 77 76 78 /* -
trunk/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp
r60089 r60107 57 57 USBProxyBackend::init(aUsbProxyService, strId, strAddress); 58 58 59 unconst(m_strBackend) = Utf8Str("host"); 60 59 61 /* 60 62 * Create the semaphore (considered fatal). -
trunk/src/VBox/Main/xml/Settings.cpp
r58437 r60107 1506 1506 1507 1507 /** 1508 * Creates \<USBDeviceSource\> nodes under the given parent element according to 1509 * the contents of the given USBDeviceSourcesList. 1510 * 1511 * @param elmParent 1512 * @param ll 1513 */ 1514 void MainConfigFile::buildUSBDeviceSources(xml::ElementNode &elmParent, 1515 const USBDeviceSourcesList &ll) 1516 { 1517 for (USBDeviceSourcesList::const_iterator it = ll.begin(); 1518 it != ll.end(); 1519 ++it) 1520 { 1521 const USBDeviceSource &src = *it; 1522 xml::ElementNode *pelmSource = elmParent.createChild("USBDeviceSource"); 1523 pelmSource->setAttribute("name", src.strName); 1524 pelmSource->setAttribute("backend", src.strBackend); 1525 pelmSource->setAttribute("address", src.strAddress); 1526 1527 /* Write the properties. */ 1528 for (StringsMap::const_iterator itProp = src.properties.begin(); 1529 itProp != src.properties.end(); 1530 ++itProp) 1531 { 1532 xml::ElementNode *pelmProp = pelmSource->createChild("Property"); 1533 pelmProp->setAttribute("name", itProp->first); 1534 pelmProp->setAttribute("value", itProp->second); 1535 } 1536 } 1537 } 1538 1539 /** 1540 * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and 1541 * stores them in the given linklist. This is in ConfigFileBase because it's used 1542 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine 1543 * filters). 1544 * @param elmDeviceFilters 1545 * @param ll 1546 */ 1547 void MainConfigFile::readUSBDeviceSources(const xml::ElementNode &elmDeviceSources, 1548 USBDeviceSourcesList &ll) 1549 { 1550 xml::NodesLoop nl1(elmDeviceSources, "USBDeviceSource"); 1551 const xml::ElementNode *pelmChild; 1552 while ((pelmChild = nl1.forAllNodes())) 1553 { 1554 USBDeviceSource src; 1555 1556 if ( pelmChild->getAttributeValue("name", src.strName) 1557 && pelmChild->getAttributeValue("backend", src.strBackend) 1558 && pelmChild->getAttributeValue("address", src.strAddress)) 1559 { 1560 // handle medium properties 1561 xml::NodesLoop nl2(*pelmChild, "Property"); 1562 const xml::ElementNode *pelmSrcChild; 1563 while ((pelmSrcChild = nl2.forAllNodes())) 1564 { 1565 Utf8Str strPropName, strPropValue; 1566 if ( pelmSrcChild->getAttributeValue("name", strPropName) 1567 && pelmSrcChild->getAttributeValue("value", strPropValue) ) 1568 src.properties[strPropName] = strPropValue; 1569 else 1570 throw ConfigFileError(this, pelmSrcChild, N_("Required USBDeviceSource/Property/@name or @value attribute is missing")); 1571 } 1572 1573 ll.push_back(src); 1574 } 1575 } 1576 } 1577 1578 /** 1508 1579 * Constructor. 1509 1580 * … … 1574 1645 else if (pelmGlobalChild->nameEquals("USBDeviceFilters")) 1575 1646 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters); 1647 else if (pelmGlobalChild->nameEquals("USBDeviceSources")) 1648 readUSBDeviceSources(*pelmGlobalChild, host.llUSBDeviceSources); 1576 1649 } 1577 1650 } // end if (pelmRootChild->nameEquals("Global")) … … 1608 1681 void MainConfigFile::bumpSettingsVersionIfNeeded() 1609 1682 { 1683 if (m->sv < SettingsVersion_v1_16) 1684 { 1685 // VirtualBox 5.1 add support for additional USB device sources. 1686 if (!host.llUSBDeviceSources.empty()) 1687 m->sv = SettingsVersion_v1_16; 1688 } 1689 1610 1690 if (m->sv < SettingsVersion_v1_14) 1611 1691 { … … 1780 1860 host.llUSBDeviceFilters, 1781 1861 true); // fHostMode 1862 1863 if (!host.llUSBDeviceSources.empty()) 1864 buildUSBDeviceSources(*pelmGlobal->createChild("USBDeviceSources"), 1865 host.llUSBDeviceSources); 1782 1866 1783 1867 // now go write the XML
Note:
See TracChangeset
for help on using the changeset viewer.