Changeset 52254 in vbox for trunk/src/VBox/Devices/USB/linux
- Timestamp:
- Aug 2, 2014 3:34:53 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/USB/linux/USBProxyDevice-linux.cpp
r50234 r52254 76 76 # define RTCRITSECT void * 77 77 static inline int rtcsNoop() { return VINF_SUCCESS; } 78 static inline bool rtcsTrue() { return true; } 78 79 # define RTCritSectInit(a) rtcsNoop() 79 80 # define RTCritSectDelete(a) rtcsNoop() 80 81 # define RTCritSectEnter(a) rtcsNoop() 81 82 # define RTCritSectLeave(a) rtcsNoop() 83 # define RTCritSectIsOwner(a) rtcsTrue() 82 84 #endif 83 85 #include <VBox/err.h> … … 91 93 #include <iprt/stream.h> 92 94 #include <iprt/string.h> 95 #include <iprt/list.h> 93 96 #if defined(NO_PORT_RESET) && !defined(NO_LOGICAL_RECONNECT) 94 97 # include <iprt/thread.h> … … 107 110 { 108 111 /** The kernel URB data */ 109 struct usbdevfs_urb KUrb;112 struct usbdevfs_urb KUrb; 110 113 /** Space filler for the isochronous packets. */ 111 114 struct usbdevfs_iso_packet_desc aIsocPktsDonUseTheseUseTheOnesInKUrb[8]; 112 /** The millisecond timestamp when this URB was submitted. */ 113 uint64_t u64SubmitTS; 114 /** Pointer to the next linux URB. */ 115 struct USBPROXYURBLNX *pNext; 116 /** Pointer to the previous linux URB. */ 117 struct USBPROXYURBLNX *pPrev; 115 /** Node to link the URB in of the existing lists. */ 116 RTLISTNODE NodeList; 118 117 /** If we've split the VUSBURB up into multiple linux URBs, this is points to the head. */ 119 struct USBPROXYURBLNX *pSplitHead;118 struct USBPROXYURBLNX *pSplitHead; 120 119 /** The next linux URB if split up. */ 121 struct USBPROXYURBLNX *pSplitNext; 122 /** Whether it has timed out and should be shot down on the next failing reap call. */ 123 bool fTimedOut; 124 /** Indicates that this URB has been canceled by timeout and should return an CRC error. */ 125 bool fCanceledByTimedOut; 120 struct USBPROXYURBLNX *pSplitNext; 126 121 /** Don't report these back. */ 127 bool fCanceledBySubmit;122 bool fCanceledBySubmit; 128 123 /** This split element is reaped. */ 129 bool fSplitElementReaped;124 bool fSplitElementReaped; 130 125 /** Size to transfer in remaining fragments of a split URB */ 131 uint32_t cbSplitRemaining;126 uint32_t cbSplitRemaining; 132 127 } USBPROXYURBLNX, *PUSBPROXYURBLNX; 133 128 … … 139 134 /** The open file. */ 140 135 RTFILE hFile; 141 /** Critical section protecting the twolists. */136 /** Critical section protecting the lists. */ 142 137 RTCRITSECT CritSect; 143 /** The list of free linux URBs . Singly linked. */144 PUSBPROXYURBLNX pFreeHead;145 /** The list of active linux URBs. Doubly linked.138 /** The list of free linux URBs (USBPROXYURBLNX). */ 139 RTLISTANCHOR ListFree; 140 /** The list of active linux URBs. 146 141 * We must maintain this so we can properly reap URBs of a detached device. 147 * Only the split head will appear in this list. */148 PUSBPROXYURBLNX pInFlightHead;142 * Only the split head will appear in this list. (USBPROXYURBLNX) */ 143 RTLISTANCHOR ListInFlight; 149 144 /** The list of landed linux URBs. Doubly linked. 150 * Only the split head will appear in this list. */ 151 PUSBPROXYURBLNX pTaxingHead; 152 /** The tail of the landed linux URBs. */ 153 PUSBPROXYURBLNX pTaxingTail; 145 * Only the split head will appear in this list. (USBPROXYURBLNX) */ 146 RTLISTANCHOR ListTaxing; 154 147 /** Are we using sysfs to find the active configuration? */ 155 148 bool fUsingSysfs; … … 233 226 pProxyDev->fDetached = true; 234 227 235 PUSBPROXYURBLNX pUrbTaxing = NULL; 236 PUSBPROXYURBLNX pUrbLnx = pDevLnx->pInFlightHead; 237 pDevLnx->pInFlightHead = NULL; 238 while (pUrbLnx) 239 { 240 PUSBPROXYURBLNX pCur = pUrbLnx; 241 pUrbLnx = pUrbLnx->pNext; 242 243 ioctl(RTFileToNative(pDevLnx->hFile), USBDEVFS_DISCARDURB, &pCur->KUrb); /* not sure if this is required.. */ 244 if (!pCur->KUrb.status) 245 pCur->KUrb.status = -ENODEV; 228 PUSBPROXYURBLNX pUrbLnx; 229 PUSBPROXYURBLNX pUrbLnxNext; 230 231 RTListForEachSafe(&pDevLnx->ListInFlight, pUrbLnx, pUrbLnxNext, USBPROXYURBLNX, NodeList) 232 { 233 RTListNodeRemove(&pUrbLnx->NodeList); 234 235 ioctl(RTFileToNative(pDevLnx->hFile), USBDEVFS_DISCARDURB, &pUrbLnx->KUrb); /* not sure if this is required.. */ 236 if (!pUrbLnx->KUrb.status) 237 pUrbLnx->KUrb.status = -ENODEV; 246 238 247 239 /* insert into the taxing list. */ 248 pCur->pPrev = NULL; 249 if ( !pCur->pSplitHead 250 || pCur == pCur->pSplitHead) 251 { 252 pCur->pNext = pUrbTaxing; 253 if (pUrbTaxing) 254 pUrbTaxing->pPrev = pCur; 255 pUrbTaxing = pCur; 256 } 257 else 258 pCur->pNext = NULL; 259 } 260 261 /* Append the URBs we shot down to the taxing queue. */ 262 if (pUrbTaxing) 263 { 264 pUrbTaxing->pPrev = pDevLnx->pTaxingTail; 265 if (pUrbTaxing->pPrev) 266 pUrbTaxing->pPrev->pNext = pUrbTaxing; 267 else 268 pDevLnx->pTaxingTail = pDevLnx->pTaxingHead = pUrbTaxing; 240 if ( !pUrbLnx->pSplitHead 241 || pUrbLnx == pUrbLnx->pSplitHead) 242 RTListAppend(&pDevLnx->ListTaxing, &pUrbLnx->NodeList); 269 243 } 270 244 … … 299 273 300 274 /** 275 * Links the given URB into the in flight list. 276 * 277 * @returns nothing. 278 * @param pDevLnx The proxy device instance - Linux specific data. 279 * @param pUrbLnx The URB to link into the in flight list. 280 */ 281 static void usbProxyLinuxUrbLinkInFlight(PUSBPROXYDEVLNX pDevLnx, PUSBPROXYURBLNX pUrbLnx) 282 { 283 Assert(RTCritSectIsOwner(&pDevLnx->CritSect)); 284 Assert(!pUrbLnx->pSplitHead); 285 RTListAppend(&pDevLnx->ListInFlight, &pUrbLnx->NodeList); 286 } 287 288 /** 289 * Unlinks the given URB from the in flight list. 290 * @returns nothing. 291 * @param pDevLnx The proxy device instance - Linux specific data. 292 * @param pUrbLnx The URB to link into the in flight list. 293 */ 294 static void usbProxyLinuxUrbUnlinkInFlight(PUSBPROXYDEVLNX pDevLnx, PUSBPROXYURBLNX pUrbLnx) 295 { 296 RTCritSectEnter(&pDevLnx->CritSect); 297 298 /* 299 * Remove from the active list. 300 */ 301 Assert(!pUrbLnx->pSplitHead || pUrbLnx->pSplitHead == pUrbLnx); 302 303 RTListNodeRemove(&pUrbLnx->NodeList); 304 pUrbLnx->pSplitHead = pUrbLnx->pSplitNext = NULL; 305 306 RTCritSectLeave(&pDevLnx->CritSect); 307 } 308 309 /** 301 310 * Allocates a linux URB request structure. 302 311 * @returns Pointer to an active URB request. … … 315 324 * Try remove a linux URB from the free list, if none there allocate a new one. 316 325 */ 317 pUrbLnx = pDevLnx->pFreeHead;326 pUrbLnx = RTListGetFirst(&pDevLnx->ListFree, USBPROXYURBLNX, NodeList); 318 327 if (pUrbLnx) 319 pDevLnx->pFreeHead = pUrbLnx->pNext; 328 { 329 RTListNodeRemove(&pUrbLnx->NodeList); 330 RTCritSectLeave(&pDevLnx->CritSect); 331 } 320 332 else 321 333 { … … 324 336 if (!pUrbLnx) 325 337 return NULL; 326 RTCritSectEnter(&pDevLnx->CritSect);327 } 338 } 339 328 340 pUrbLnx->pSplitHead = pSplitHead; 329 341 pUrbLnx->pSplitNext = NULL; 330 pUrbLnx->fTimedOut = false;331 pUrbLnx->fCanceledByTimedOut = false;332 342 pUrbLnx->fCanceledBySubmit = false; 333 343 pUrbLnx->fSplitElementReaped = false; 334 335 /*336 * Link it into the active list337 */338 if (!pSplitHead)339 {340 pUrbLnx->pPrev = NULL;341 pUrbLnx->pNext = pDevLnx->pInFlightHead;342 if (pUrbLnx->pNext)343 pUrbLnx->pNext->pPrev = pUrbLnx;344 pDevLnx->pInFlightHead = pUrbLnx;345 }346 else347 pUrbLnx->pPrev = pUrbLnx->pNext = (PUSBPROXYURBLNX)0xdead;348 349 RTCritSectLeave(&pDevLnx->CritSect);350 344 return pUrbLnx; 351 345 } … … 365 359 366 360 /* 367 * Remove from the active list.368 */369 if ( !pUrbLnx->pSplitHead370 || pUrbLnx->pSplitHead == pUrbLnx)371 {372 if (pUrbLnx->pNext)373 pUrbLnx->pNext->pPrev = pUrbLnx->pPrev;374 if (pUrbLnx->pPrev)375 pUrbLnx->pPrev->pNext = pUrbLnx->pNext;376 else377 pDevLnx->pInFlightHead = pUrbLnx->pNext;378 }379 pUrbLnx->pSplitHead = pUrbLnx->pSplitNext = NULL;380 381 /*382 361 * Link it into the free list. 383 362 */ 384 pUrbLnx->pPrev = NULL; 385 pUrbLnx->pNext = pDevLnx->pFreeHead; 386 pDevLnx->pFreeHead = pUrbLnx; 363 RTListAppend(&pDevLnx->ListFree, &pUrbLnx->NodeList); 387 364 388 365 RTCritSectLeave(&pDevLnx->CritSect); … … 409 386 pUrbLnx = pUrbLnx->pSplitNext; 410 387 Assert(pFree->pSplitHead); 388 pFree->pSplitHead = pFree->pSplitNext = NULL; 411 389 usbProxyLinuxUrbFree(pProxyDev, pFree); 412 390 } … … 665 643 */ 666 644 PUSBPROXYDEVLNX pDevLnx = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX); 645 646 RTListInit(&pDevLnx->ListFree); 647 RTListInit(&pDevLnx->ListInFlight); 648 RTListInit(&pDevLnx->ListTaxing); 667 649 pDevLnx->pszPath = RTStrDupN(pszPath, cchPath); 668 650 if (pDevLnx->pszPath) … … 782 764 783 765 PUSBPROXYURBLNX pUrbLnx; 784 while ((pUrbLnx = pDevLnx->pInFlightHead) != NULL) 785 { 786 pDevLnx->pInFlightHead = pUrbLnx->pNext; 766 PUSBPROXYURBLNX pUrbLnxNext; 767 RTListForEachSafe(&pDevLnx->ListInFlight, pUrbLnx, pUrbLnxNext, USBPROXYURBLNX, NodeList) 768 { 769 RTListNodeRemove(&pUrbLnx->NodeList); 770 787 771 if ( usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pUrbLnx->KUrb, false, UINT32_MAX) 788 772 && errno != ENODEV 789 773 && errno != ENOENT) 790 774 AssertMsgFailed(("errno=%d\n", errno)); 775 791 776 if (pUrbLnx->pSplitHead) 792 777 { … … 809 794 } 810 795 811 while ((pUrbLnx = pDevLnx->pFreeHead) != NULL)812 { 813 pDevLnx->pFreeHead = pUrbLnx->pNext;796 RTListForEachSafe(&pDevLnx->ListFree, pUrbLnx, pUrbLnxNext, USBPROXYURBLNX, NodeList) 797 { 798 RTListNodeRemove(&pUrbLnx->NodeList); 814 799 RTMemFree(pUrbLnx); 815 800 } … … 1299 1284 pUrb, errno, pCur->KUrb.type, pCur->KUrb.endpoint, pCur->KUrb.buffer_length, cTries)); 1300 1285 if (errno != EBUSY && ++cTries < 3) /* this doesn't work for the floppy :/ */ 1301 {1302 pCur->u64SubmitTS = RTTimeMilliTS();1303 1286 continue; 1304 } 1287 1305 1288 return RTErrConvertFromErrno(errno); 1306 1289 } … … 1330 1313 return NULL; 1331 1314 } 1332 Assert(pHead->pNext != pNew); Assert(pHead->pPrev != pNew); Assert(pNew->pNext == pNew->pPrev);1333 1315 Assert(pNew->pSplitHead == pHead); 1334 1316 Assert(pNew->pSplitNext == NULL); … … 1433 1415 if (RT_FAILURE(rc)) 1434 1416 break; 1417 usbProxyLinuxUrbLinkInFlight(USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX), pCur); 1435 1418 } 1436 1419 } … … 1439 1422 { 1440 1423 pUrb->Dev.pvPrivate = pUrbLnx; 1424 usbProxyLinuxUrbLinkInFlight(USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVLNX), pUrbLnx); 1441 1425 LogFlow(("usbProxyLinuxUrbQueueSplit: ok\n")); 1442 1426 return VINF_SUCCESS; … … 1513 1497 1514 1498 /* 1499 * We have to serialize access by using the critial section here because this 1500 * thread might be suspended after submitting the URB but before linking it into 1501 * the in flight list. This would get us in trouble when reaping the URB on another 1502 * thread while it isn't in the in flight list. 1503 * 1504 * Linking the URB into the list before submitting it like it was done in the past is not 1505 * possible either because submitting the URB might fail here because the device gets 1506 * detached. The reaper thread gets this event too and might race this thread before we 1507 * can unlink the URB from the active list and the common code might end up freeing 1508 * the common URB structure twice. 1509 */ 1510 RTCritSectEnter(&pDevLnx->CritSect); 1511 /* 1515 1512 * Submit it. 1516 1513 */ … … 1525 1522 if (pUrb->enmType == VUSBXFERTYPE_MSG) 1526 1523 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData); 1524 1525 RTCritSectLeave(&pDevLnx->CritSect); 1527 1526 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx); 1528 1529 1527 usbProxLinuxUrbUnplugged(pProxyDev); 1530 1528 return RTErrConvertFromErrno(errno); … … 1541 1539 if ( errno == EINVAL 1542 1540 && pUrb->cbData >= 8*_1K) 1541 { 1542 RTCritSectLeave(&pDevLnx->CritSect); 1543 1543 return usbProxyLinuxUrbQueueSplit(pProxyDev, pUrbLnx, pUrb); 1544 } 1544 1545 1545 1546 Log(("usb-linux: Queue URB %p -> %d!!! type=%d ep=%#x buffer_length=%#x cTries=%d\n", … … 1548 1549 continue; 1549 1550 1551 RTCritSectLeave(&pDevLnx->CritSect); 1550 1552 rc = RTErrConvertFromErrno(errno); 1551 1552 l_err:1553 1553 if (pUrb->enmType == VUSBXFERTYPE_MSG) 1554 1554 usbProxyLinuxUrbSwapSetup((PVUSBSETUP)pUrb->abData); … … 1556 1556 return rc; 1557 1557 } 1558 pUrbLnx->u64SubmitTS = RTTimeMilliTS(); 1558 1559 usbProxyLinuxUrbLinkInFlight(pDevLnx, pUrbLnx); 1560 RTCritSectLeave(&pDevLnx->CritSect); 1559 1561 1560 1562 LogFlow(("usbProxyLinuxUrbQueue: ok\n")); 1561 1563 pUrb->Dev.pvPrivate = pUrbLnx; 1562 1564 return rc; 1563 }1564 1565 1566 /**1567 * Check if any or the in-flight URBs are taking too long and should be cancelled.1568 *1569 * Cancelling is done in three turns, first a URB is marked for timeout if it's1570 * exceeding a certain time limit. Then the next time it's encountered it is actually1571 * cancelled. The idea now is that it's supposed to be reaped and returned in the next1572 * round of calls.1573 *1574 * @param pProxyDev The proxy device.1575 * @param pDevLnx The linux backend data.1576 *1577 * @todo Make the HCI do proper timeout handling! Current timeout is 3 min and 20 seconds1578 * as not to break bloomberg which queues IN packages with 3 min timeouts.1579 */1580 static void vusbProxyLinuxUrbDoTimeouts(PUSBPROXYDEV pProxyDev, PUSBPROXYDEVLNX pDevLnx)1581 {1582 RTCritSectEnter(&pDevLnx->CritSect);1583 uint64_t u64MilliTS = RTTimeMilliTS();1584 PUSBPROXYURBLNX pCur;1585 for (pCur = pDevLnx->pInFlightHead;1586 pCur;1587 pCur = pCur->pNext)1588 {1589 if (pCur->fTimedOut)1590 {1591 if (pCur->pSplitHead)1592 {1593 /* split */1594 Assert(pCur == pCur->pSplitHead);1595 unsigned cFailures = 0;1596 PUSBPROXYURBLNX pCur2;1597 for (pCur2 = pCur; pCur2; pCur2 = pCur2->pSplitNext)1598 {1599 if (pCur2->fSplitElementReaped)1600 continue;1601 1602 if ( !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur2->KUrb, true, UINT32_MAX)1603 || errno == ENOENT)1604 pCur2->fCanceledByTimedOut = true;1605 else if (errno != ENODEV)1606 Log(("vusbProxyLinuxUrbDoTimeouts: pUrb=%p failed errno=%d (!!split!!)\n", pCur2->KUrb.usercontext, errno));1607 else1608 goto l_leave; /* ENODEV means break and everything cancelled elsewhere. */1609 }1610 LogRel(("USB: Cancelled URB (%p) after %llums!! (cFailures=%d)\n",1611 pCur->KUrb.usercontext, (long long unsigned) u64MilliTS - pCur->u64SubmitTS, cFailures));1612 }1613 else1614 {1615 /* unsplit */1616 if ( !usbProxyLinuxDoIoCtl(pProxyDev, USBDEVFS_DISCARDURB, &pCur->KUrb, true, UINT32_MAX)1617 || errno == -ENOENT)1618 {1619 pCur->fCanceledByTimedOut = true;1620 LogRel(("USB: Cancelled URB (%p) after %llums!!\n", pCur->KUrb.usercontext, (long long unsigned) u64MilliTS - pCur->u64SubmitTS));1621 }1622 else if (errno != ENODEV)1623 LogFlow(("vusbProxyLinuxUrbDoTimeouts: pUrb=%p failed errno=%d\n", pCur->KUrb.usercontext, errno));1624 else1625 goto l_leave; /* ENODEV means break and everything cancelled elsewhere. */1626 }1627 }1628 #if 01629 /* Disabled for the time being as some USB devices have URBs pending for an unknown amount of time.1630 * One example is the OmniKey CardMan 3821. */1631 else if (u64MilliTS - pCur->u64SubmitTS >= 200*1000 /* 200 sec (180 sec has been observed with XP) */)1632 pCur->fTimedOut = true;1633 #endif1634 }1635 1636 l_leave:1637 RTCritSectLeave(&pDevLnx->CritSect);1638 1565 } 1639 1566 … … 1695 1622 static VUSBSTATUS vusbProxyLinuxUrbGetStatus(PUSBPROXYURBLNX pUrbLnx) 1696 1623 { 1697 if ( pUrbLnx->fCanceledByTimedOut1698 && pUrbLnx->KUrb.status == 0)1699 return VUSBSTATUS_CRC;1700 1624 return vusbProxyLinuxStatusToVUsbStatus(pUrbLnx->KUrb.status); 1701 1625 } … … 1718 1642 * Any URBs pending delivery? 1719 1643 */ 1720 if ( pDevLnx->pTaxingHead)1644 if (!RTListIsEmpty(&pDevLnx->ListTaxing)) 1721 1645 { 1722 1646 RTCritSectEnter(&pDevLnx->CritSect); 1723 pUrbLnx = pDevLnx->pTaxingHead;1647 pUrbLnx = RTListGetFirst(&pDevLnx->ListTaxing, USBPROXYURBLNX, NodeList); 1724 1648 if (pUrbLnx) 1725 1649 { 1726 1650 /* unlink from the pending delivery list */ 1727 if (pUrbLnx->pNext) 1728 { 1729 pUrbLnx->pNext->pPrev = NULL; 1730 pDevLnx->pTaxingHead = pUrbLnx->pNext; 1731 } 1732 else 1733 pDevLnx->pTaxingHead = pDevLnx->pTaxingTail = NULL; 1651 RTListNodeRemove(&pDevLnx->ListTaxing); 1734 1652 1735 1653 /* temporarily into the active list, so free works right. */ 1736 pUrbLnx->pPrev = NULL; 1737 pUrbLnx->pNext = pDevLnx->pInFlightHead; 1738 if (pUrbLnx->pNext) 1739 pUrbLnx->pNext->pPrev = pUrbLnx; 1740 pDevLnx->pInFlightHead = pUrbLnx; 1654 RTListAppend(&pDevLnx->ListInFlight, &pUrbLnx->NodeList); 1741 1655 } 1742 1656 RTCritSectLeave(&pDevLnx->CritSect); … … 1744 1658 if (!pUrbLnx) 1745 1659 { 1746 /*1747 * Don't block if nothing is in the air.1748 */1749 if (!pDevLnx->pInFlightHead)1750 {1751 int cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? -1 : cMillies;1752 1753 LogFlow(("Nothing in flight, going to sleep\n"));1754 1755 struct pollfd pfd;1756 1757 pfd.fd = RTPipeToNative(pDevLnx->hPipeWakeupR);1758 pfd.events = POLLIN | POLLHUP;1759 pfd.revents = 0;1760 1761 int rc = poll(&pfd, 1, cMilliesWait);1762 Log(("usbProxyLinuxUrbReap: poll rc = %d\n", rc));1763 if (rc >= 1)1764 {1765 /* Drain pipe. */1766 uint8_t bRead;1767 size_t cbIgnored = 0;1768 RTPipeRead(pDevLnx->hPipeWakeupR, &bRead, 1, &cbIgnored);1769 }1770 return NULL;1771 }1772 1773 1660 /* 1774 1661 * Block for requested period. … … 1807 1694 break; 1808 1695 } 1809 if (rc >= 0 /*|| errno == ETIMEOUT*/) 1810 { 1811 vusbProxyLinuxUrbDoTimeouts(pProxyDev, pDevLnx); 1696 if (rc >= 0) 1812 1697 return NULL; 1813 } 1698 1814 1699 if (errno != EAGAIN) 1815 1700 { … … 1832 1717 if (errno == ENODEV) 1833 1718 usbProxLinuxUrbUnplugged(pProxyDev); 1834 else if (errno == EAGAIN)1835 vusbProxyLinuxUrbDoTimeouts(pProxyDev, pDevLnx);1836 1719 else 1837 1720 Log(("usb-linux: Reap URB. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev))); … … 1922 1805 } 1923 1806 } 1807 usbProxyLinuxUrbUnlinkInFlight(pDevLnx, pUrbLnx); 1924 1808 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx); 1925 1809 } … … 1935 1819 else 1936 1820 { 1821 usbProxyLinuxUrbUnlinkInFlight(pDevLnx, pUrbLnx); 1937 1822 usbProxyLinuxUrbFree(pProxyDev, pUrbLnx); 1938 1823 pUrb = NULL;
Note:
See TracChangeset
for help on using the changeset viewer.