Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1017 lines
27 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. All Rights Reserved
  4. Module Name:
  5. Usbmon.c
  6. Abstract:
  7. USBMON core port monitor routines
  8. Author:
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "ntddpar.h"
  13. #define LPT_NOT_ERROR 0x8
  14. #define LPT_SELECT 0x10
  15. #define LPT_PAPER_EMPTY 0x20
  16. #define LPT_BENIGN_STATUS LPT_NOT_ERROR | LPT_SELECT
  17. #define MAX_TIMEOUT 300000 //5 minutes
  18. #define JOB_ABORTCHECK_TIMEOUT 5000
  19. const TCHAR cszCFGMGR32[]=TEXT("cfgmgr32.dll");
  20. const CHAR cszReenumFunc[]="CM_Reenumerate_DevNode_Ex";
  21. #ifdef UNICODE
  22. const CHAR cszLocalFunc[]="CM_Locate_DevNode_ExW";
  23. #else
  24. const CHAR cszLocalFunc[]="CM_Locate_DevNode_ExA";
  25. #endif
  26. BOOL GetLptStatus(HANDLE hDeviceHandle,BYTE *Return);
  27. DWORD UsbmonDebug;
  28. BOOL
  29. APIENTRY
  30. DllMain(
  31. HANDLE hModule,
  32. DWORD dwReason,
  33. LPVOID lpRes
  34. )
  35. {
  36. if ( dwReason == DLL_PROCESS_ATTACH )
  37. DisableThreadLibraryCalls(hModule);
  38. return TRUE;
  39. }
  40. BOOL
  41. AbortThisJob(PUSBMON_PORT_INFO pPortInfo)
  42. /*++
  43. Tells if the job should be aborted. A job should be aborted if it has
  44. been deleted or it needs to be restarted.
  45. --*/
  46. {
  47. BOOL bRet = FALSE;
  48. DWORD dwNeeded;
  49. LPJOB_INFO_1 pJobInfo = NULL;
  50. dwNeeded = 0;
  51. GetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 1, NULL, 0, &dwNeeded);
  52. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
  53. goto Done;
  54. if ( !(pJobInfo = (LPJOB_INFO_1) AllocSplMem(dwNeeded)) ||
  55. !GetJob(pPortInfo->hPrinter, pPortInfo->dwJobId,
  56. 1, (LPBYTE)pJobInfo, dwNeeded, &dwNeeded)
  57. )
  58. goto Done;
  59. bRet = (pJobInfo->Status & JOB_STATUS_DELETING) ||
  60. (pJobInfo->Status & JOB_STATUS_DELETED) ||
  61. (pJobInfo->Status & JOB_STATUS_RESTART);
  62. Done:
  63. FreeSplMem(pJobInfo);
  64. return bRet;
  65. }
  66. BOOL
  67. WINAPI
  68. USBMON_OpenPort(
  69. LPTSTR pszPortName,
  70. LPHANDLE pHandle
  71. )
  72. {
  73. PUSBMON_PORT_INFO pPortInfo, pPrev;
  74. pPortInfo = FindPort(&gUsbmonInfo, pszPortName, &pPrev);
  75. if ( pPortInfo ) {
  76. *pHandle = (LPHANDLE)pPortInfo;
  77. InitializeCriticalSection(&pPortInfo->CriticalSection);
  78. return TRUE;
  79. } else {
  80. SetLastError(ERROR_PATH_NOT_FOUND);
  81. return FALSE;
  82. }
  83. }
  84. BOOL
  85. WINAPI
  86. USBMON_ClosePort(
  87. HANDLE hPort
  88. )
  89. {
  90. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  91. DeleteCriticalSection(&pPortInfo->CriticalSection);
  92. return TRUE;
  93. }
  94. //
  95. // Dot4Pnp - test whether we need to force a dot4 pnp event if the dot4 stack doesn't exist.
  96. //
  97. BOOL
  98. Dot4Pnp(
  99. PUSBMON_PORT_INFO pPortInfo
  100. )
  101. {
  102. BOOL bRet = FALSE;
  103. HANDLE hToken;
  104. DEVINST hLPTDevInst;
  105. TCHAR szPnpEntry[]=TEXT("Root\\ParallelClass\\0000"); // The pnp node to reenumerate.
  106. TCHAR cszDot4[]=TEXT("DOT4"); // This relates to the array size below.
  107. TCHAR szPort[5]; // 4 chars for "DOT4" and the ending 0.
  108. UINT uOldErrorMode;
  109. HINSTANCE hCfgMgr32 = 0; // Library instance.
  110. // Pointers to pnp functions...
  111. pfCM_Locate_DevNode_Ex pfnLocateDevNode;
  112. pfCM_Reenumerate_DevNode_Ex pfnReenumDevNode;
  113. //
  114. // Make a copy of the first 4 chars of the port name - to compare against Dot4.
  115. // Copy length of 4 chars + 1 for null.
  116. lstrcpyn( szPort, pPortInfo->szPortName, lstrlen(cszDot4)+1 );
  117. szPort[lstrlen(cszDot4)]=0;
  118. if( lstrcmpi( szPort, cszDot4) != 0)
  119. {
  120. //
  121. // If this is not a dot4 port and we failed to open it - fail.
  122. //
  123. goto Done;
  124. }
  125. //
  126. // If it is a dot4 device we need to force a pnp event on the parallel port to get the
  127. // dot4 stack rebuilt.
  128. // If any of these fail, fail the call just as if the port couldn't be opened.
  129. //
  130. // Load the pnp dll.
  131. //
  132. uOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  133. hCfgMgr32 = LoadLibrary( cszCFGMGR32 );
  134. if(!hCfgMgr32)
  135. {
  136. SetErrorMode(uOldErrorMode);
  137. goto Done;
  138. }
  139. SetErrorMode(uOldErrorMode);
  140. //
  141. // Get the Addressed of pnp functions we want to call...
  142. //
  143. pfnLocateDevNode = (pfCM_Locate_DevNode_Ex)GetProcAddress( hCfgMgr32, cszLocalFunc );
  144. pfnReenumDevNode = (pfCM_Reenumerate_DevNode_Ex)GetProcAddress( hCfgMgr32, cszReenumFunc );
  145. if( !pfnLocateDevNode || !pfnReenumDevNode )
  146. goto Done;
  147. //
  148. // We need to revert to system context here as otherwise the pnp call will fail if the user
  149. // is anything other than an admin as this requires admin rights.
  150. // If this fails, the pnp will fail anyway, so we don't need to test the return value.
  151. //
  152. hToken = RevertToPrinterSelf();
  153. //
  154. // Reenumerate from the root of the devnode tree
  155. //
  156. if( ( pfnLocateDevNode( &hLPTDevInst, szPnpEntry, CM_LOCATE_DEVNODE_NORMAL, NULL ) != CR_SUCCESS) ||
  157. ( pfnReenumDevNode( hLPTDevInst, CM_REENUMERATE_NORMAL, NULL ) != CR_SUCCESS) )
  158. {
  159. //
  160. // Revert back to the user's context in case we failed for another reason other than
  161. // ACCESS DENIED (not admin)
  162. //
  163. ImpersonatePrinterClient(hToken);
  164. goto Done;
  165. }
  166. //
  167. // Revert back to the user's context.
  168. //
  169. ImpersonatePrinterClient(hToken);
  170. //
  171. // Try and open the port again.
  172. // If we fail, then the device must not be there any more or still switched off - fail as usual.
  173. //
  174. pPortInfo->hDeviceHandle = CreateFile(pPortInfo->szDevicePath,
  175. GENERIC_WRITE | GENERIC_READ,
  176. FILE_SHARE_READ | FILE_SHARE_WRITE,
  177. NULL,
  178. OPEN_EXISTING,
  179. FILE_FLAG_OVERLAPPED,
  180. NULL);
  181. if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE )
  182. goto Done;
  183. bRet = TRUE;
  184. Done:
  185. if(hCfgMgr32)
  186. FreeLibrary(hCfgMgr32);
  187. return bRet;
  188. }
  189. BOOL
  190. LocalOpenPort(
  191. PUSBMON_PORT_INFO pPortInfo
  192. )
  193. {
  194. BOOL bRet = FALSE;
  195. EnterCriticalSection(&pPortInfo->CriticalSection);
  196. if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE ) {
  197. //
  198. // If we have an invalid handle and refcount is non-zero we want the
  199. // job to fail and restart to accept writes. In other words if the
  200. // handle got closed prematurely, because of failing writes, then we
  201. // need the ref count to go down to 0 before calling CreateFile again
  202. //
  203. if ( pPortInfo->cRef )
  204. goto Done;
  205. pPortInfo->hDeviceHandle = CreateFile(pPortInfo->szDevicePath,
  206. GENERIC_WRITE | GENERIC_READ,
  207. FILE_SHARE_READ | FILE_SHARE_WRITE,
  208. NULL,
  209. OPEN_EXISTING,
  210. FILE_FLAG_OVERLAPPED,
  211. NULL);
  212. //
  213. // If we failed to open the port - test to see if it is a Dot4 port.
  214. //
  215. if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE )
  216. {
  217. //
  218. // ERROR_FILE_NOT_FOUND -> Error code for port not there.
  219. //
  220. if( ERROR_FILE_NOT_FOUND != GetLastError() ||
  221. !Dot4Pnp(pPortInfo) )
  222. goto Done;
  223. }
  224. pPortInfo->Ov.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
  225. if ( pPortInfo->Ov.hEvent == NULL ) {
  226. CloseHandle(pPortInfo->hDeviceHandle);
  227. pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
  228. goto Done;
  229. }
  230. }
  231. ++(pPortInfo->cRef);
  232. bRet = TRUE;
  233. Done:
  234. LeaveCriticalSection(&pPortInfo->CriticalSection);
  235. return bRet;
  236. }
  237. BOOL
  238. LocalClosePort(
  239. PUSBMON_PORT_INFO pPortInfo
  240. )
  241. {
  242. BOOL bRet = TRUE;
  243. BOOL bJobCanceled=FALSE;
  244. EnterCriticalSection(&pPortInfo->CriticalSection);
  245. --(pPortInfo->cRef);
  246. if ( pPortInfo->cRef != 0 )
  247. goto Done;
  248. bRet = CloseHandle(pPortInfo->hDeviceHandle);
  249. CloseHandle(pPortInfo->Ov.hEvent);
  250. pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
  251. Done:
  252. LeaveCriticalSection(&pPortInfo->CriticalSection);
  253. return bRet;
  254. }
  255. VOID
  256. FreeWriteBuffer(
  257. PUSBMON_PORT_INFO pPortInfo
  258. )
  259. {
  260. FreeSplMem(pPortInfo->pWriteBuffer);
  261. pPortInfo->pWriteBuffer=NULL;
  262. pPortInfo->dwBufferSize = pPortInfo->dwDataSize
  263. = pPortInfo->dwDataCompleted
  264. = pPortInfo->dwDataScheduled = 0;
  265. }
  266. BOOL
  267. WINAPI
  268. USBMON_StartDocPort(
  269. HANDLE hPort,
  270. LPTSTR pPrinterName,
  271. DWORD dwJobId,
  272. DWORD dwLevel,
  273. LPBYTE pDocInfo
  274. )
  275. {
  276. BOOL bRet = FALSE;
  277. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  278. SPLASSERT(pPortInfo->pWriteBuffer == NULL &&
  279. pPortInfo->dwBufferSize == 0 &&
  280. pPortInfo->dwDataSize == 0 &&
  281. pPortInfo->dwDataCompleted == 0 &&
  282. pPortInfo->dwDataScheduled == 0);
  283. if ( !OpenPrinter(pPrinterName, &pPortInfo->hPrinter, NULL) )
  284. return FALSE;
  285. pPortInfo->dwJobId = dwJobId;
  286. bRet = LocalOpenPort(pPortInfo);
  287. if ( !bRet ) {
  288. if ( pPortInfo->hPrinter ) {
  289. ClosePrinter(pPortInfo->hPrinter);
  290. pPortInfo->hPrinter = NULL;
  291. }
  292. } else
  293. pPortInfo->dwFlags |= USBMON_STARTDOC;
  294. return bRet;
  295. }
  296. BOOL
  297. NeedToResubmitJob(
  298. DWORD dwLastError
  299. )
  300. {
  301. //
  302. // I used winerror -s ntstatus to map KM error codes to user mode errors
  303. //
  304. // 5 ERROR_ACCESS_DENIED <--> c0000056 STATUS_DELETE_PENDING
  305. // 6 ERROR_INVALID_HANDLE <--> c0000008 STATUS_INVALID_HANDLE
  306. // 23 ERROR_CRC <--> 0xc000003e STATUS_DATA_ERROR
  307. // 23 ERROR_CRC <--> 0xc000003f STATUS_CRC_ERROR
  308. // 23 ERROR_CRC <--> 0xc000009c STATUS_DEVICE_DATA_ERROR
  309. // 55 ERROR_DEV_NOT_EXIST <--> c00000c0 STATUS_DEVICE_DOES_NOT_EXIST
  310. //
  311. return dwLastError == ERROR_ACCESS_DENIED ||
  312. dwLastError == ERROR_INVALID_HANDLE ||
  313. dwLastError == ERROR_CRC ||
  314. dwLastError == ERROR_DEV_NOT_EXIST;
  315. }
  316. VOID
  317. InvalidatePortHandle(
  318. PUSBMON_PORT_INFO pPortInfo
  319. )
  320. {
  321. SPLASSERT(pPortInfo->hDeviceHandle != INVALID_HANDLE_VALUE);
  322. CloseHandle(pPortInfo->hDeviceHandle);
  323. pPortInfo->hDeviceHandle = INVALID_HANDLE_VALUE;
  324. CloseHandle(pPortInfo->Ov.hEvent);
  325. pPortInfo->Ov.hEvent = NULL;
  326. FreeWriteBuffer(pPortInfo);
  327. }
  328. DWORD
  329. ScheduleWrite(
  330. PUSBMON_PORT_INFO pPortInfo
  331. )
  332. /*++
  333. Routine Description:
  334. Arguments:
  335. Return Value:
  336. ERROR_SUCCESS : Write got succesfully scheduled
  337. (may or may not have completed on return)
  338. pPortInfo->dwScheduledData is the amount that got scheduled
  339. Others: Write failed, return code is the Win32 error
  340. --*/
  341. {
  342. DWORD dwLastError = ERROR_SUCCESS, dwDontCare;
  343. //
  344. // When a sheduled write is pending we should not try to send data
  345. // any more
  346. //
  347. SPLASSERT(pPortInfo->dwDataScheduled == 0);
  348. //
  349. // Send all the data that is not confirmed
  350. //
  351. SPLASSERT(pPortInfo->dwDataSize >= pPortInfo->dwDataCompleted);
  352. pPortInfo->dwDataScheduled = pPortInfo->dwDataSize -
  353. pPortInfo->dwDataCompleted;
  354. if ( !WriteFile(pPortInfo->hDeviceHandle,
  355. pPortInfo->pWriteBuffer + pPortInfo->dwDataCompleted,
  356. pPortInfo->dwDataScheduled,
  357. &dwDontCare,
  358. &pPortInfo->Ov) ) {
  359. if ( (dwLastError = GetLastError()) == ERROR_SUCCESS )
  360. dwLastError = STG_E_UNKNOWN;
  361. else if ( dwLastError == ERROR_IO_PENDING )
  362. dwLastError = ERROR_SUCCESS;
  363. }
  364. //
  365. // If scheduling of the write failed then no data is pending
  366. //
  367. if ( dwLastError != ERROR_SUCCESS )
  368. pPortInfo->dwDataScheduled = 0;
  369. return dwLastError;
  370. }
  371. DWORD
  372. ScheduledWriteStatus(
  373. PUSBMON_PORT_INFO pPortInfo,
  374. DWORD dwTimeout
  375. )
  376. /*++
  377. Routine Description:
  378. Arguments:
  379. Return Value:
  380. ERROR_SUCCESS : Write got done succesfully
  381. ERROR_TIMEOUT : Timeout occured
  382. Others : Write completed with a failure
  383. --*/
  384. {
  385. DWORD dwLastError = ERROR_SUCCESS;
  386. DWORD dwWritten = 0;
  387. SPLASSERT(pPortInfo->dwDataScheduled > 0);
  388. if ( WAIT_TIMEOUT == WaitForSingleObject(pPortInfo->Ov.hEvent,
  389. dwTimeout) ) {
  390. dwLastError = ERROR_TIMEOUT;
  391. goto Done;
  392. }
  393. if ( !GetOverlappedResult(pPortInfo->hDeviceHandle,
  394. &pPortInfo->Ov,
  395. &dwWritten,
  396. FALSE) ) {
  397. if ( (dwLastError = GetLastError()) == ERROR_SUCCESS )
  398. dwLastError = STG_E_UNKNOWN;
  399. }
  400. ResetEvent(pPortInfo->Ov.hEvent);
  401. //
  402. // We are here because either a write completed succesfully,
  403. // or failed but the error is not serious enough to resubmit job
  404. //
  405. if ( dwWritten <= pPortInfo->dwDataScheduled )
  406. pPortInfo->dwDataCompleted += dwWritten;
  407. else
  408. SPLASSERT(dwWritten <= pPortInfo->dwDataScheduled);
  409. pPortInfo->dwDataScheduled = 0;
  410. Done:
  411. //
  412. // Either we timed out, or write sheduled completed (success of failure)
  413. //
  414. SPLASSERT(dwLastError == ERROR_TIMEOUT || pPortInfo->dwDataScheduled == 0);
  415. return dwLastError;
  416. }
  417. BOOL
  418. WINAPI
  419. USBMON_EndDocPort(
  420. HANDLE hPort
  421. )
  422. {
  423. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  424. DWORD dwLastError = ERROR_SUCCESS;
  425. //
  426. // Wait for any outstanding write to complete
  427. //
  428. while ( pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
  429. //
  430. // If job needs to be aborted ask KM driver to cancel the I/O
  431. //
  432. if ( AbortThisJob(pPortInfo) ) {
  433. if ( pPortInfo->dwDataScheduled ) {
  434. CancelIo(pPortInfo->hDeviceHandle);
  435. dwLastError = ScheduledWriteStatus(pPortInfo, INFINITE);
  436. }
  437. goto Done;
  438. }
  439. if ( pPortInfo->dwDataScheduled )
  440. dwLastError = ScheduledWriteStatus(pPortInfo,
  441. JOB_ABORTCHECK_TIMEOUT);
  442. else {
  443. //
  444. // If for some reason KM is failing to complete all write do not
  445. // send data in a busy loop. Use 1 sec between Writes
  446. //
  447. if ( dwLastError != ERROR_SUCCESS )
  448. Sleep(1*1000);
  449. dwLastError = ScheduleWrite(pPortInfo);
  450. }
  451. //
  452. // Check if we can use the same handle and continue
  453. //
  454. if ( NeedToResubmitJob(dwLastError) ) {
  455. InvalidatePortHandle(pPortInfo);
  456. SetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 0,
  457. NULL, JOB_CONTROL_RESTART);
  458. goto Done;
  459. }
  460. }
  461. Done:
  462. FreeWriteBuffer(pPortInfo);
  463. pPortInfo->dwFlags &= ~USBMON_STARTDOC;
  464. LocalClosePort(pPortInfo);
  465. SetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 0,
  466. NULL, JOB_CONTROL_SENT_TO_PRINTER);
  467. ClosePrinter(pPortInfo->hPrinter);
  468. pPortInfo->hPrinter = NULL;
  469. return TRUE;
  470. }
  471. BOOL
  472. WINAPI
  473. USBMON_GetPrinterDataFromPort(
  474. HANDLE hPort,
  475. DWORD dwControlID,
  476. LPWSTR pValueName,
  477. LPWSTR lpInBuffer,
  478. DWORD cbInBuffer,
  479. LPWSTR lpOutBuffer,
  480. DWORD cbOutBuffer,
  481. LPDWORD lpcbReturned
  482. )
  483. {
  484. BOOL bRet = FALSE;
  485. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  486. OVERLAPPED Ov;
  487. HANDLE hDeviceHandle;
  488. DWORD dwWaitResult;
  489. *lpcbReturned = 0;
  490. if ( dwControlID == 0 ) {
  491. SetLastError(ERROR_INVALID_PARAMETER);
  492. return FALSE;
  493. }
  494. ZeroMemory(&Ov, sizeof(Ov));
  495. if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
  496. return FALSE;
  497. if ( !LocalOpenPort(pPortInfo) ) {
  498. CloseHandle(Ov.hEvent);
  499. return FALSE;
  500. }
  501. if(dwControlID==IOCTL_PAR_QUERY_DEVICE_ID)
  502. {
  503. hDeviceHandle=CreateFile(pPortInfo->szDevicePath,
  504. GENERIC_WRITE | GENERIC_READ,
  505. FILE_SHARE_READ | FILE_SHARE_WRITE,
  506. NULL,
  507. OPEN_EXISTING,
  508. FILE_FLAG_OVERLAPPED,
  509. NULL);
  510. if(hDeviceHandle==INVALID_HANDLE_VALUE)
  511. goto Done;
  512. if ( !DeviceIoControl(pPortInfo->hDeviceHandle, dwControlID,lpInBuffer, cbInBuffer,lpOutBuffer, cbOutBuffer, lpcbReturned, &Ov)
  513. && GetLastError() != ERROR_IO_PENDING )
  514. {
  515. CloseHandle(hDeviceHandle);
  516. goto Done;
  517. }
  518. if(WaitForSingleObject(Ov.hEvent,PAR_QUERY_TIMEOUT)!=WAIT_OBJECT_0)
  519. CancelIo(hDeviceHandle);
  520. bRet = GetOverlappedResult(pPortInfo->hDeviceHandle, &Ov,lpcbReturned,TRUE);
  521. CloseHandle(hDeviceHandle);
  522. }
  523. else
  524. {
  525. if ( !DeviceIoControl(pPortInfo->hDeviceHandle, dwControlID,
  526. lpInBuffer, cbInBuffer,
  527. lpOutBuffer, cbOutBuffer, lpcbReturned, &Ov) &&
  528. GetLastError() != ERROR_IO_PENDING )
  529. goto Done;
  530. bRet = GetOverlappedResult(pPortInfo->hDeviceHandle, &Ov,
  531. lpcbReturned, TRUE);
  532. }
  533. Done:
  534. CloseHandle(Ov.hEvent);
  535. LocalClosePort(pPortInfo);
  536. return bRet;
  537. }
  538. BOOL
  539. WINAPI
  540. USBMON_ReadPort(
  541. HANDLE hPort,
  542. LPBYTE pBuffer,
  543. DWORD cbBuffer,
  544. LPDWORD pcbRead
  545. )
  546. {
  547. DWORD dwLastError = ERROR_SUCCESS;
  548. DWORD dwTimeout;
  549. HANDLE hReadHandle;
  550. OVERLAPPED Ov;
  551. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  552. //
  553. // Create separate read handle since we have to cancel reads which do
  554. // not complete within the specified timeout without cancelling writes
  555. //
  556. hReadHandle = CreateFile(pPortInfo->szDevicePath,
  557. GENERIC_WRITE | GENERIC_READ,
  558. FILE_SHARE_READ | FILE_SHARE_WRITE,
  559. NULL,
  560. OPEN_EXISTING,
  561. FILE_FLAG_OVERLAPPED,
  562. NULL);
  563. if ( hReadHandle == INVALID_HANDLE_VALUE )
  564. return FALSE;
  565. ZeroMemory(&Ov, sizeof(Ov));
  566. if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
  567. goto Done;
  568. if ( !ReadFile(hReadHandle, pBuffer, cbBuffer, pcbRead, &Ov) &&
  569. (dwLastError = GetLastError()) != ERROR_IO_PENDING )
  570. goto Done;
  571. dwTimeout = pPortInfo->ReadTimeoutConstant +
  572. pPortInfo->ReadTimeoutMultiplier * cbBuffer;
  573. if ( dwTimeout == 0 )
  574. dwTimeout=MAX_TIMEOUT;
  575. if( WaitForSingleObject(Ov.hEvent, dwTimeout) == WAIT_TIMEOUT ) {
  576. CancelIo(hReadHandle);
  577. WaitForSingleObject(Ov.hEvent, INFINITE);
  578. }
  579. if( !GetOverlappedResult(hReadHandle, &Ov, pcbRead, FALSE) ) {
  580. *pcbRead = 0;
  581. dwLastError = GetLastError();
  582. } else
  583. dwLastError = ERROR_SUCCESS;
  584. Done:
  585. if ( Ov.hEvent )
  586. CloseHandle(Ov.hEvent);
  587. CloseHandle(hReadHandle);
  588. if ( dwLastError )
  589. SetLastError(dwLastError);
  590. return dwLastError == ERROR_SUCCESS;
  591. }
  592. DWORD dwGetTimeLeft(DWORD dwStartTime,DWORD dwTimeout)
  593. {
  594. DWORD dwCurrentTime;
  595. DWORD dwTimeLeft;
  596. if(dwTimeout==MAX_TIMEOUT)
  597. return MAX_TIMEOUT;
  598. dwCurrentTime=GetTickCount();
  599. if(dwTimeout<(dwCurrentTime-dwStartTime))
  600. dwTimeLeft=0;
  601. else
  602. dwTimeLeft=dwTimeout-(dwCurrentTime-dwStartTime);
  603. return dwTimeLeft;
  604. }
  605. BOOL
  606. WINAPI
  607. USBMON_WritePort(
  608. HANDLE hPort,
  609. LPBYTE pBuffer,
  610. DWORD cbBuffer,
  611. LPDWORD pcbWritten
  612. )
  613. {
  614. DWORD dwLastError = ERROR_SUCCESS;
  615. DWORD dwBytesLeft, dwBytesSent;
  616. DWORD dwStartTime, dwTimeLeft, dwTimeout;
  617. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  618. BOOL bStartDoc = (pPortInfo->dwFlags & USBMON_STARTDOC) != 0;
  619. BYTE bPrinterStatus;
  620. *pcbWritten = 0;
  621. dwStartTime = GetTickCount();
  622. dwTimeout = pPortInfo->WriteTimeoutConstant + pPortInfo->WriteTimeoutMultiplier * cbBuffer;
  623. if ( dwTimeout == 0 )
  624. dwTimeout = MAX_TIMEOUT;
  625. //
  626. // Usbprint currently can't handle write greater than 4K.
  627. // For Win2K we will make a fix here, later usbprint will be fixed
  628. //
  629. // It is ok to change the size here since spooler will resubmit the rest
  630. // later
  631. //
  632. if ( cbBuffer > 0x1000 &&
  633. !lstrncmpi(pPortInfo->szPortName, TEXT("USB"), lstrlen(TEXT("USB"))) )
  634. cbBuffer = 0x1000;
  635. //
  636. // For writes outside startdoc/enddoc we do not carry them across WritePort
  637. // calls. These are typically from language monitors (i.e. not job data)
  638. //
  639. SPLASSERT(bStartDoc || pPortInfo->pWriteBuffer == NULL);
  640. if ( pPortInfo->hDeviceHandle == INVALID_HANDLE_VALUE ) {
  641. SetJob(pPortInfo->hPrinter, pPortInfo->dwJobId, 0,
  642. NULL, JOB_CONTROL_RESTART);
  643. SetLastError(ERROR_CANCELLED);
  644. return FALSE;
  645. }
  646. if ( !LocalOpenPort(pPortInfo) )
  647. return FALSE;
  648. //
  649. // First complete any data from previous WritePort call
  650. //
  651. while ( pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
  652. if ( pPortInfo->dwDataScheduled ) {
  653. dwTimeLeft = dwGetTimeLeft(dwStartTime, dwTimeout);
  654. dwLastError = ScheduledWriteStatus(pPortInfo, dwTimeLeft);
  655. } else
  656. dwLastError = ScheduleWrite(pPortInfo);
  657. if ( dwLastError != ERROR_SUCCESS )
  658. goto Done;
  659. }
  660. SPLASSERT(pPortInfo->dwDataSize == pPortInfo->dwDataCompleted &&
  661. pPortInfo->dwDataScheduled == 0 &&
  662. dwLastError == ERROR_SUCCESS);
  663. //
  664. // Copy the data to our own buffer
  665. //
  666. if ( pPortInfo->dwBufferSize < cbBuffer ) {
  667. FreeWriteBuffer(pPortInfo);
  668. if ( pPortInfo->pWriteBuffer = AllocSplMem(cbBuffer) )
  669. pPortInfo->dwBufferSize = cbBuffer;
  670. else {
  671. dwLastError = ERROR_OUTOFMEMORY;
  672. goto Done;
  673. }
  674. } else {
  675. pPortInfo->dwDataCompleted = pPortInfo->dwDataScheduled = 0;
  676. }
  677. CopyMemory(pPortInfo->pWriteBuffer, pBuffer, cbBuffer);
  678. pPortInfo->dwDataSize = cbBuffer;
  679. //
  680. // Now do the write for the data for this WritePort call
  681. //
  682. while ( pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
  683. if ( pPortInfo->dwDataScheduled ) {
  684. dwTimeLeft = dwGetTimeLeft(dwStartTime, dwTimeout);
  685. dwLastError = ScheduledWriteStatus(pPortInfo, dwTimeLeft);
  686. } else
  687. dwLastError = ScheduleWrite(pPortInfo);
  688. if ( dwLastError != ERROR_SUCCESS )
  689. break;
  690. }
  691. //
  692. // For writes outside startdoc/enddoc, which are from language monitors,
  693. // do not carry pending writes to next WritePort.
  694. //
  695. if ( !bStartDoc && pPortInfo->dwDataSize > pPortInfo->dwDataCompleted ) {
  696. CancelIo(pPortInfo->hDeviceHandle);
  697. dwLastError = ScheduledWriteStatus(pPortInfo, INFINITE);
  698. *pcbWritten = pPortInfo->dwDataCompleted;
  699. FreeWriteBuffer(pPortInfo);
  700. }
  701. //
  702. // We will tell spooler we wrote all the data if some data got scheduled
  703. // (or scheduled and completed)
  704. //
  705. if ( pPortInfo->dwDataCompleted > 0 || pPortInfo->dwDataScheduled != 0 )
  706. *pcbWritten = cbBuffer;
  707. else
  708. FreeWriteBuffer(pPortInfo);
  709. Done:
  710. if ( NeedToResubmitJob(dwLastError) )
  711. InvalidatePortHandle(pPortInfo);
  712. else if ( dwLastError == ERROR_TIMEOUT ) {
  713. GetLptStatus(pPortInfo->hDeviceHandle, &bPrinterStatus);
  714. if ( bPrinterStatus & LPT_PAPER_EMPTY )
  715. dwLastError=ERROR_OUT_OF_PAPER;
  716. }
  717. LocalClosePort(pPortInfo);
  718. SetLastError(dwLastError);
  719. return dwLastError == ERROR_SUCCESS;
  720. }
  721. BOOL
  722. WINAPI
  723. USBMON_SetPortTimeOuts(
  724. HANDLE hPort,
  725. LPCOMMTIMEOUTS lpCTO,
  726. DWORD reserved
  727. )
  728. {
  729. PUSBMON_PORT_INFO pPortInfo = (PUSBMON_PORT_INFO)hPort;
  730. pPortInfo->ReadTimeoutMultiplier = lpCTO->ReadTotalTimeoutMultiplier;
  731. pPortInfo->ReadTimeoutConstant = lpCTO->ReadTotalTimeoutConstant;
  732. pPortInfo->WriteTimeoutMultiplier = lpCTO->WriteTotalTimeoutMultiplier;
  733. pPortInfo->WriteTimeoutConstant = lpCTO->WriteTotalTimeoutConstant;
  734. return TRUE;
  735. }
  736. BOOL GetLptStatus(HANDLE hDeviceHandle,BYTE *Status)
  737. {
  738. BYTE StatusByte;
  739. OVERLAPPED Ov;
  740. BOOL bResult;
  741. DWORD dwBytesReturned;
  742. DWORD dwLastError;
  743. Ov.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
  744. bResult=DeviceIoControl(hDeviceHandle,IOCTL_USBPRINT_GET_LPT_STATUS,NULL,0,&StatusByte,1,&dwBytesReturned,&Ov);
  745. dwLastError=GetLastError();
  746. if((bResult)||(dwLastError==ERROR_IO_PENDING))
  747. bResult=GetOverlappedResult(hDeviceHandle,&Ov,&dwBytesReturned,TRUE);
  748. if(bResult)
  749. {
  750. *Status=StatusByte;
  751. }
  752. else
  753. {
  754. *Status=LPT_BENIGN_STATUS; //benign printer status... 0 would indicate a particular error status from the printer
  755. }
  756. CloseHandle(Ov.hEvent);
  757. return bResult;
  758. }
  759. MONITOREX MonitorEx = {
  760. sizeof(MONITOR),
  761. {
  762. USBMON_EnumPorts,
  763. USBMON_OpenPort,
  764. NULL, // OpenPortEx not supported
  765. USBMON_StartDocPort,
  766. USBMON_WritePort,
  767. USBMON_ReadPort,
  768. USBMON_EndDocPort,
  769. USBMON_ClosePort,
  770. NULL, // AddPort not supported
  771. NULL, // AddPortEx not supported
  772. NULL, // ConfigurePort not supported
  773. NULL, // DeletePort not supported
  774. USBMON_GetPrinterDataFromPort,
  775. USBMON_SetPortTimeOuts,
  776. NULL, // XcvOpenPort not supported
  777. NULL, // XcvDataPort not supported
  778. NULL // XcvClosePort not supported
  779. }
  780. };
  781. USBMON_MONITOR_INFO gUsbmonInfo;
  782. LPMONITOREX
  783. WINAPI
  784. InitializePrintMonitor(
  785. LPTSTR pszRegistryRoot
  786. )
  787. {
  788. ZeroMemory(&gUsbmonInfo, sizeof(gUsbmonInfo));
  789. InitializeCriticalSection(&gUsbmonInfo.EnumPortsCS);
  790. InitializeCriticalSection(&gUsbmonInfo.BackThreadCS);
  791. return &MonitorEx;
  792. }