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.

2054 lines
54 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. All Rights Reserved
  4. Module Name:
  5. Abstract:
  6. Author:
  7. Revision History:
  8. --*/
  9. #define USECOMM
  10. #include "precomp.h"
  11. #include <winioctl.h>
  12. #include <ntddpar.h>
  13. // ---------------------------------------------------------------------
  14. // PROTO, CONSTANT, and GLOBAL
  15. //
  16. // ---------------------------------------------------------------------
  17. DWORD ProcessPJLString(PINIPORT, CHAR *, DWORD *);
  18. VOID ProcessParserError(DWORD);
  19. VOID InterpreteTokens(PINIPORT, PTOKENPAIR, DWORD);
  20. BOOL IsPJL(PINIPORT);
  21. BOOL WriteCommand(HANDLE, LPSTR);
  22. BOOL ReadCommand(HANDLE);
  23. #define WAIT_FOR_WRITE 100 // 0.1 sec
  24. #define WAIT_FOR_DATA_TIMEOUT 100 // 0.1 sec
  25. #define WAIT_FOR_USTATUS_THREAD_TIMEOUT 500 // 0.5 sec
  26. #define GETDEVICEID IOCTL_PAR_QUERY_DEVICE_ID
  27. #define MAX_DEVID 1024
  28. static TCHAR cszInstalledMemory[] = TEXT("Installed Memory");
  29. static TCHAR cszAvailableMemory[] = TEXT("Available Memory");
  30. BOOL
  31. DllMain(
  32. IN HANDLE hModule,
  33. IN DWORD dwReason,
  34. IN LPVOID lpRes
  35. )
  36. /*++
  37. Routine Description:
  38. Dll entry point
  39. Arguments:
  40. Return Value:
  41. --*/
  42. {
  43. UNREFERENCED_PARAMETER(lpRes);
  44. switch (dwReason) {
  45. case DLL_PROCESS_ATTACH:
  46. InitializeCriticalSection(&pjlMonSection);
  47. DisableThreadLibraryCalls(hModule);
  48. break;
  49. default:
  50. // do nothing
  51. ;
  52. }
  53. return TRUE;
  54. }
  55. VOID
  56. ClearPrinterStatusAndIniJobs(
  57. PINIPORT pIniPort
  58. )
  59. {
  60. PORT_INFO_3 PortInfo3;
  61. if ( pIniPort->PrinterStatus ||
  62. (pIniPort->status & PP_PRINTER_OFFLINE) ) {
  63. pIniPort->PrinterStatus = 0;
  64. pIniPort->status &= ~PP_PRINTER_OFFLINE;
  65. ZeroMemory(&PortInfo3, sizeof(PortInfo3));
  66. SetPort(NULL, pIniPort->pszPortName, 3, (LPBYTE)&PortInfo3);
  67. }
  68. SendJobLastPageEjected(pIniPort, ALL_JOBS, FALSE);
  69. }
  70. VOID
  71. RefreshPrinterInfo(
  72. PINIPORT pIniPort
  73. )
  74. {
  75. //
  76. // Only one thread should write to the printer at a time
  77. //
  78. if ( WAIT_OBJECT_0 != WaitForSingleObject(pIniPort->DoneWriting,
  79. WAIT_FOR_WRITE) ) {
  80. return;
  81. }
  82. //
  83. // If printer is power cycled and it does not talk back (but answers
  84. // PnP id) we got to clear the error on spooler to send jobs
  85. //
  86. ClearPrinterStatusAndIniJobs(pIniPort);
  87. if ( !IsPJL(pIniPort) ) {
  88. pIniPort->status &= ~PP_IS_PJL;
  89. }
  90. SetEvent(pIniPort->DoneWriting);
  91. }
  92. VOID
  93. UstatusThread(
  94. HANDLE hPort
  95. )
  96. /*++
  97. Routine Description:
  98. Unsolicited status information thread. This thread will continue to
  99. read unsolicited until it's asked to terminate, which will happen
  100. under one of these conditions:
  101. 1) Receive EOJ confirmation from the printer.
  102. 2) Timeout waiting for EOJ confirmation.
  103. 3) The port is been closed.
  104. Arguments:
  105. hPort : IniPort structure for the port
  106. Return Value:
  107. --*/
  108. {
  109. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  110. HANDLE hToken;
  111. DBGMSG (DBG_TRACE, ("Enter UstatusThread hPort=%x\n", hPort));
  112. SPLASSERT(pIniPort &&
  113. pIniPort->signature == PJ_SIGNATURE &&
  114. (pIniPort->status & PP_THREAD_RUNNING) == 0);
  115. if ( IsPJL(pIniPort) )
  116. pIniPort->status |= PP_IS_PJL;
  117. SetEvent(pIniPort->DoneWriting);
  118. if ( !(pIniPort->status & PP_IS_PJL) )
  119. goto StopThread;
  120. //
  121. // manual-reset event, initially signal state
  122. //
  123. pIniPort->DoneReading = CreateEvent(NULL, TRUE, TRUE, NULL);
  124. if ( !pIniPort->DoneReading )
  125. goto StopThread;
  126. pIniPort->status |= PP_THREAD_RUNNING;
  127. pIniPort->PrinterStatus = 0;
  128. pIniPort->status &= ~PP_PRINTER_OFFLINE;
  129. pIniPort->dwLastReadTime = 0;
  130. for ( ; ; ) {
  131. //
  132. // check if PP_RUN_THREAD has been cleared to terminate
  133. //
  134. if ( !(pIniPort->status & PP_RUN_THREAD) ) {
  135. if ( pIniPort->status & PP_INSTARTDOC ) {
  136. //
  137. // there's an active job, can't end the thread
  138. //
  139. pIniPort->status |= PP_RUN_THREAD;
  140. } else {
  141. DBGMSG(DBG_INFO,
  142. ("PJLMon Read Thread for Port %ws Closing Down.\n",
  143. pIniPort->pszPortName));
  144. pIniPort->status &= ~PP_THREAD_RUNNING;
  145. ClearPrinterStatusAndIniJobs(pIniPort);
  146. goto StopThread;
  147. }
  148. }
  149. //
  150. // check if the printer is bi-di
  151. //
  152. if (pIniPort->status & PP_IS_PJL) {
  153. (VOID)ReadCommand(hPort);
  154. //
  155. // If we are under error condition or if we have jobs pending
  156. // read status back from printer more frequently
  157. //
  158. if ( pIniPort->pIniJob ||
  159. (pIniPort->status & PP_PRINTER_OFFLINE) ||
  160. (pIniPort->status & PP_WRITE_ERROR) ) {
  161. WaitForSingleObject(pIniPort->WakeUp,
  162. dwReadThreadErrorTimeout);
  163. } else {
  164. WaitForSingleObject(pIniPort->WakeUp,
  165. dwReadThreadIdleTimeoutOther);
  166. }
  167. if ( pIniPort->pIniJob &&
  168. !(pIniPort->status & PP_PRINTER_OFFLINE) &&
  169. !(pIniPort->status & PP_WRITE_ERROR) ) {
  170. //
  171. // Some printers are PJL bi-di, but do not send
  172. // EOJ. We want jobs to disappear from printman
  173. //
  174. SendJobLastPageEjected(pIniPort,
  175. GetTickCount() - dwReadThreadEOJTimeout,
  176. TRUE);
  177. }
  178. //
  179. // If we did not read from printer for more than a minute
  180. // and no more jobs talk to printer again
  181. //
  182. if ( !(pIniPort->status & PP_INSTARTDOC) &&
  183. (GetTickCount() - pIniPort->dwLastReadTime) > 240000
  184. )
  185. RefreshPrinterInfo(pIniPort);
  186. } else {
  187. //
  188. // exit the thread if printer is not PJL bi-di capable
  189. //
  190. Sleep(2000);
  191. pIniPort->status &= ~PP_RUN_THREAD;
  192. #ifdef DEBUG
  193. OutputDebugStringA("Set ~PP_RUN_THREAD because printer is not bi-di\n");
  194. #endif
  195. }
  196. }
  197. StopThread:
  198. pIniPort->status &= ~PP_RUN_THREAD;
  199. pIniPort->status &= ~PP_THREAD_RUNNING;
  200. CloseHandle(pIniPort->DoneReading);
  201. //
  202. // By closing the handle and then setting it to NULL we know the main
  203. // thread will not end up setting a wrong event
  204. //
  205. CloseHandle(pIniPort->WakeUp);
  206. pIniPort->WakeUp = NULL;
  207. DBGMSG (DBG_TRACE, ("Leave UstatusThread\n"));
  208. }
  209. BOOL
  210. CreateUstatusThread(
  211. PINIPORT pIniPort
  212. )
  213. /*++
  214. Routine Description:
  215. Creates the Ustatus thread
  216. Arguments:
  217. pIniPort : IniPort structure for the port
  218. Return Value:
  219. TRUE on succesfully creating the thread, else FALSE
  220. --*/
  221. {
  222. HANDLE ThreadHandle;
  223. DWORD ThreadId;
  224. DBGMSG(DBG_INFO, ("PJLMon Read Thread for Port %ws Starting.\n",
  225. pIniPort->pszPortName));
  226. pIniPort->status |= PP_RUN_THREAD;
  227. WaitForSingleObject(pIniPort->DoneWriting, INFINITE);
  228. pIniPort->WakeUp = CreateEvent(NULL, FALSE, FALSE, NULL);
  229. if ( !pIniPort->WakeUp )
  230. goto Fail;
  231. ThreadHandle = CreateThread(NULL,
  232. 0,
  233. (LPTHREAD_START_ROUTINE)UstatusThread,
  234. pIniPort,
  235. 0, &ThreadId);
  236. if ( ThreadHandle ) {
  237. SetThreadPriority(ThreadHandle, THREAD_PRIORITY_LOWEST);
  238. CloseHandle(ThreadHandle);
  239. return TRUE;
  240. }
  241. Fail:
  242. if ( pIniPort->WakeUp ) {
  243. CloseHandle(pIniPort->WakeUp);
  244. pIniPort->WakeUp = NULL;
  245. }
  246. pIniPort->status &= ~PP_RUN_THREAD;
  247. SetEvent(pIniPort->DoneWriting);
  248. DBGMSG (DBG_TRACE, ("Leave CreateUstatusThread\n"));
  249. return FALSE;
  250. }
  251. BOOL
  252. WINAPI
  253. PJLMonOpenPortEx(
  254. IN HANDLE hMonitor,
  255. IN HANDLE hMonitorPort,
  256. IN LPWSTR pszPortName,
  257. IN LPWSTR pszPrinterName,
  258. IN OUT LPHANDLE pHandle,
  259. IN OUT LPMONITOR2 pMonitor
  260. )
  261. /*++
  262. Routine Description:
  263. Opens the port
  264. Arguments:
  265. pszPortName : Port name
  266. pszPrinterName : Printer name
  267. pHandle : Pointer to the handle to return
  268. pMonitor : Port monitor function table
  269. Return Value:
  270. TRUE on success, FALSE on error
  271. --*/
  272. {
  273. PINIPORT pIniPort;
  274. BOOL bRet = FALSE;
  275. BOOL bInSem = FALSE;
  276. DBGMSG (DBG_TRACE, ("Enter PJLMonOpenPortEx (portname = %s)\n", pszPortName));
  277. //
  278. // Validate port monitor
  279. //
  280. if ( !pMonitor ||
  281. !pMonitor->pfnOpenPort ||
  282. !pMonitor->pfnStartDocPort ||
  283. !pMonitor->pfnWritePort ||
  284. !pMonitor->pfnReadPort ||
  285. !pMonitor->pfnClosePort ) {
  286. DBGMSG(DBG_WARNING,
  287. ("PjlMon: Invalid port monitors passed to OpenPortEx\n"));
  288. SetLastError(ERROR_INVALID_PRINT_MONITOR);
  289. goto Cleanup;
  290. }
  291. EnterSplSem();
  292. bInSem = TRUE;
  293. //
  294. // Is the port open already?
  295. //
  296. if ( pIniPort = FindIniPort(pszPortName) ) {
  297. SetLastError(ERROR_BUSY);
  298. goto Cleanup;
  299. }
  300. pIniPort = CreatePortEntry(pszPortName);
  301. LeaveSplSem();
  302. bInSem = FALSE;
  303. if ( pIniPort &&
  304. (*pMonitor->pfnOpenPort)(hMonitorPort, pszPortName, &pIniPort->hPort) ) {
  305. *pHandle = pIniPort;
  306. CopyMemory((LPBYTE)&pIniPort->fn, (LPBYTE)pMonitor, sizeof(*pMonitor));
  307. //
  308. // Create the ustatus thread always
  309. // If printer is not PJL it will die by itself
  310. // We do not want to write to the printer in this thread to determine
  311. // printer is PJL since that may take several seconds to fail
  312. //
  313. CreateUstatusThread(pIniPort);
  314. bRet = TRUE;
  315. } else {
  316. DBGMSG(DBG_WARNING, ("PjlMon: OpenPort %s : Failed\n", pszPortName));
  317. }
  318. Cleanup:
  319. if ( bInSem ) {
  320. LeaveSplSem();
  321. }
  322. SplOutSem();
  323. DBGMSG (DBG_TRACE, ("Leave PJLMonOpenPortEx bRet=%d\n", bRet));
  324. return bRet;
  325. }
  326. BOOL
  327. WINAPI
  328. PJLMonStartDocPort(
  329. IN HANDLE hPort,
  330. IN LPTSTR pszPrinterName,
  331. IN DWORD dwJobId,
  332. IN DWORD dwLevel,
  333. IN LPBYTE pDocInfo
  334. )
  335. /*++
  336. Routine Description:
  337. Language monitor StartDocPort
  338. Arguments:
  339. hPort : Port handle
  340. pszPrinterName : Printer name
  341. dwJobId : Job identifier
  342. dwLevel : Level of Doc info strucuture
  343. pDocInfo : Pointer to doc info structure
  344. Return Value:
  345. TRUE on success, FALSE on error
  346. --*/
  347. {
  348. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  349. PINIJOB pIniJob = NULL;
  350. DWORD cbJob;
  351. BOOL bRet = FALSE;
  352. DBGMSG (DBG_TRACE, ("Enter PJLMonStartDocPort hPort=0x%x\n", hPort));
  353. //
  354. // Validate parameters
  355. //
  356. if ( !pIniPort ||
  357. pIniPort->signature != PJ_SIGNATURE ||
  358. !pDocInfo ||
  359. !pszPrinterName ||
  360. !*pszPrinterName ) {
  361. SPLASSERT(pIniPort &&
  362. pIniPort->signature == PJ_SIGNATURE &&
  363. pDocInfo);
  364. SetLastError(ERROR_INVALID_PARAMETER);
  365. return FALSE;
  366. }
  367. if ( dwLevel != 1 ) {
  368. SPLASSERT(dwLevel == 1);
  369. SetLastError(ERROR_INVALID_LEVEL);
  370. return FALSE;
  371. }
  372. //
  373. // Serialize access to the port
  374. //
  375. if ( pIniPort->status & PP_INSTARTDOC ) {
  376. SetLastError(ERROR_BUSY);
  377. return FALSE;
  378. }
  379. cbJob = sizeof(*pIniJob) + lstrlen(pszPrinterName) * sizeof(TCHAR)
  380. + sizeof(TCHAR);
  381. pIniJob = (PINIJOB) AllocSplMem(cbJob);
  382. if ( !pIniJob ) {
  383. goto Cleanup;
  384. }
  385. pIniJob->pszPrinterName = wcscpy((LPTSTR)(pIniJob+1), pszPrinterName);
  386. if ( !OpenPrinter(pIniJob->pszPrinterName, &pIniJob->hPrinter, NULL) ) {
  387. DBGMSG(DBG_WARNING,
  388. ("pjlmon: OpenPrinter failed for %s, last error %d\n",
  389. pIniJob->pszPrinterName, GetLastError()));
  390. goto Cleanup;
  391. }
  392. pIniPort->status |= PP_INSTARTDOC;
  393. bRet = (*pIniPort->fn.pfnStartDocPort)(pIniPort->hPort,
  394. pszPrinterName,
  395. dwJobId,
  396. dwLevel,
  397. pDocInfo);
  398. if ( !bRet ) {
  399. pIniPort->status &= ~PP_INSTARTDOC;
  400. goto Cleanup;
  401. }
  402. //
  403. // If Ustatus thread is not running then check if printer understands
  404. // PJL, unless we determined that printer does not understand PJL earlier
  405. //
  406. if ( !(pIniPort->status & PP_RUN_THREAD) &&
  407. !(pIniPort->status & PP_DONT_TRY_PJL) ) {
  408. CreateUstatusThread(pIniPort);
  409. }
  410. //
  411. // set PP_SEND_PJL flag here so the first write of the job
  412. // will try to send PJL command to initiate the job control
  413. //
  414. pIniJob->JobId = dwJobId;
  415. pIniJob->status |= PP_INSTARTDOC;
  416. EnterSplSem();
  417. if ( !pIniPort->pIniJob ) {
  418. pIniPort->pIniJob = pIniJob;
  419. } else {
  420. pIniJob->pNext = pIniPort->pIniJob;
  421. pIniPort->pIniJob = pIniJob;
  422. }
  423. LeaveSplSem();
  424. if ( pIniPort->status & PP_IS_PJL )
  425. pIniJob->status |= PP_SEND_PJL;
  426. WaitForSingleObject(pIniPort->DoneWriting, INFINITE);
  427. Cleanup:
  428. if ( !bRet ) {
  429. if ( pIniJob )
  430. FreeIniJob(pIniJob);
  431. }
  432. DBGMSG (DBG_TRACE, ("Leave PJLMonEndDocPort bRet=%d\n", bRet));
  433. return bRet;
  434. }
  435. BOOL
  436. WINAPI
  437. PJLMonReadPort(
  438. IN HANDLE hPort,
  439. OUT LPBYTE pBuffer,
  440. IN DWORD cbBuf,
  441. OUT LPDWORD pcbRead
  442. )
  443. /*++
  444. Routine Description:
  445. Language monitor ReadPort
  446. Arguments:
  447. hPort : Port handle
  448. pBuffer : Buffer to read data to
  449. cbBuf : Buffer size
  450. pcbRead : Pointer to the variable to return read count
  451. Return Value:
  452. TRUE on success, FALSE on error
  453. --*/
  454. {
  455. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  456. BOOL bRet;
  457. DBGMSG (DBG_TRACE, ("Enter PJLMonReadPort hPort=0x%x\n", hPort));
  458. if ( !pIniPort ||
  459. pIniPort->signature != PJ_SIGNATURE ) {
  460. SPLASSERT(pIniPort && pIniPort->signature == PJ_SIGNATURE);
  461. SetLastError(ERROR_INVALID_PARAMETER);
  462. return FALSE;
  463. }
  464. bRet = (*pIniPort->fn.pfnReadPort)(pIniPort->hPort, pBuffer, cbBuf, pcbRead);
  465. DBGMSG (DBG_TRACE, ("Leave PJLMonReadPort bRet=%d\n", bRet));
  466. return bRet;
  467. }
  468. BOOL
  469. WINAPI
  470. PJLMonWritePort(
  471. IN HANDLE hPort,
  472. IN LPBYTE pBuffer,
  473. IN DWORD cbBuf,
  474. IN LPDWORD pcbWritten
  475. )
  476. /*++
  477. Routine Description:
  478. Language monitor WritePort
  479. Arguments:
  480. hPort : Port handle
  481. pBuffer : Data Buffer
  482. cbBuf : Buffer size
  483. pcbRead : Pointer to the variable to return written count
  484. Return Value:
  485. TRUE on success, FALSE on error
  486. --*/
  487. {
  488. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  489. BOOL bRet;
  490. DBGMSG (DBG_TRACE, ("Enter PJLMonWritePort hPort=0x%x\n", hPort));
  491. if ( !pIniPort ||
  492. pIniPort->signature != PJ_SIGNATURE ) {
  493. SPLASSERT(pIniPort && pIniPort->signature == PJ_SIGNATURE);
  494. SetLastError(ERROR_INVALID_PARAMETER);
  495. return FALSE;
  496. }
  497. //
  498. // check if it's the fist write of the job
  499. //
  500. if ( pIniPort->pIniJob &&
  501. (pIniPort->pIniJob->status & PP_SEND_PJL) ) {
  502. // PP_SEND_PJL is set if it's the first write of the job
  503. char string[256];
  504. if ( !WriteCommand(hPort, "\033%-12345X@PJL \015\012") ) {
  505. return FALSE;
  506. }
  507. //
  508. // clear PP_SEND_PJL here if we have successfully send a PJL command.
  509. //
  510. pIniPort->pIniJob->status &= ~PP_SEND_PJL;
  511. //
  512. // set PP_PJL_SENT meaning that we have successfully sent a
  513. // PJL command to the printer, though it doesn't mean that
  514. // we will get a successfully read. PP_PJL_SENT gets cleared in
  515. // StartDocPort.
  516. //
  517. pIniPort->pIniJob->status |= PP_PJL_SENT;
  518. sprintf(string, "@PJL JOB NAME = \"MSJOB %d\"\015\012",
  519. pIniPort->pIniJob->JobId);
  520. WriteCommand(hPort, string);
  521. WriteCommand(hPort, "@PJL USTATUS JOB = ON \015\012@PJL USTATUS PAGE = OFF \015\012@PJL USTATUS DEVICE = ON \015\012@PJL USTATUS TIMED = 30 \015\012\033%-12345X");
  522. }
  523. //
  524. // writing to port monitor
  525. //
  526. bRet = (*pIniPort->fn.pfnWritePort)(pIniPort->hPort, pBuffer,
  527. cbBuf, pcbWritten);
  528. if ( bRet ) {
  529. pIniPort->status &= ~PP_WRITE_ERROR;
  530. } else {
  531. pIniPort->status |= PP_WRITE_ERROR;
  532. }
  533. if ( (!bRet || pIniPort->PrinterStatus) &&
  534. (pIniPort->status & PP_THREAD_RUNNING) ) {
  535. //
  536. // By waiting for the UStatus thread to finish reading if there
  537. // is an error and printer sends unsolicited status
  538. // and user gets status on queue view before the win32 popup
  539. //
  540. ResetEvent(pIniPort->DoneReading);
  541. SetEvent(pIniPort->WakeUp);
  542. WaitForSingleObject(pIniPort->DoneReading, INFINITE);
  543. }
  544. DBGMSG (DBG_TRACE, ("Leave PJLMonWritePort bRet=%d\n", bRet));
  545. return bRet;
  546. }
  547. BOOL
  548. WINAPI
  549. PJLMonEndDocPort(
  550. HANDLE hPort
  551. )
  552. /*++
  553. Routine Description:
  554. Language monitor EndDocPort
  555. Arguments:
  556. hPort : Port handle
  557. Return Value:
  558. TRUE on success, FALSE on error
  559. --*/
  560. {
  561. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  562. PINIJOB pIniJob;
  563. DBGMSG (DBG_TRACE, ("Enter PJLMonEndDocPort hPort=0x%x\n", hPort));
  564. if ( !pIniPort ||
  565. pIniPort->signature != PJ_SIGNATURE ) {
  566. SPLASSERT(pIniPort && pIniPort->signature == PJ_SIGNATURE);
  567. SetLastError(ERROR_INVALID_PARAMETER);
  568. return FALSE;
  569. }
  570. //
  571. // Find the job (which is the last)
  572. //
  573. pIniJob = pIniPort->pIniJob;
  574. if ( !pIniJob )
  575. DBGMSG(DBG_ERROR, ("No jobs?\n"));
  576. //
  577. // check if we had sent PJL command, i.e. if the printer is bi-di
  578. //
  579. if ( pIniJob && (pIniJob->status & PP_PJL_SENT) ) {
  580. //
  581. // if the printer is bi-di, tell printer to let us know when the job
  582. // is don't in the printer and we'll really EndDoc then. this is so
  583. // that we can continue to monitor the job status until the job is
  584. // really done in case there's an error occurs.
  585. // but some cheap printers like 4L, doesn't handle this EOJ command
  586. // reliably, so we time out if printer doesn't tell us EOJ after a
  587. // while so that we don't end up having the port open forever in this
  588. // case.
  589. //
  590. char string[256];
  591. sprintf(string,
  592. "\033%%-12345X@PJL EOJ NAME = \"MSJOB %d\"\015\012\033%%-12345X",
  593. pIniPort->pIniJob->JobId);
  594. WriteCommand(hPort, string);
  595. pIniJob->TimeoutCount = GetTickCount();
  596. pIniJob->status &= ~PP_INSTARTDOC;
  597. }
  598. (*pIniPort->fn.pfnEndDocPort)(pIniPort->hPort);
  599. if ( pIniJob && !(pIniJob->status & PP_PJL_SENT) ) {
  600. //
  601. // This is not bi-di printer send EOJ so that spooler deletes it
  602. //
  603. SendJobLastPageEjected(pIniPort, pIniJob->JobId, FALSE);
  604. }
  605. pIniPort->status &= ~PP_INSTARTDOC;
  606. // wake up the UStatus read thread if printer is bi-di
  607. if ( pIniPort->status & PP_THREAD_RUNNING )
  608. SetEvent(pIniPort->WakeUp);
  609. SetEvent(pIniPort->DoneWriting);
  610. DBGMSG (DBG_TRACE, ("Leave PJLMonEndDocPort\n"));
  611. return TRUE;
  612. }
  613. BOOL
  614. WINAPI
  615. PJLMonClosePort(
  616. HANDLE hPort
  617. )
  618. /*++
  619. Routine Description:
  620. Language monitor ClosePort
  621. Arguments:
  622. hPort : Port handle
  623. Return Value:
  624. TRUE on success, FALSE on error
  625. --*/
  626. {
  627. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  628. DBGMSG (DBG_TRACE, ("Enter PJLMonClosePort hPort=0x%x\n", hPort));
  629. if ( !pIniPort ||
  630. pIniPort->signature != PJ_SIGNATURE ) {
  631. SPLASSERT(pIniPort && pIniPort->signature == PJ_SIGNATURE);
  632. SetLastError(ERROR_INVALID_PARAMETER);
  633. return FALSE;
  634. }
  635. pIniPort->status &= ~PP_INSTARTDOC;
  636. //
  637. // Kill Ustatus thread if it is running
  638. //
  639. if (pIniPort->status & PP_RUN_THREAD) {
  640. pIniPort->status &= ~PP_RUN_THREAD;
  641. #ifdef DEBUG
  642. OutputDebugStringA("Set ~PP_RUN_THREAD from close port\n");
  643. #endif
  644. SetEvent(pIniPort->WakeUp);
  645. //
  646. // if UStatusThread is still running at this point,
  647. // wait utill it terminates, because we can't DeletePortEntry
  648. // until it terminates.
  649. //
  650. while (pIniPort->WakeUp)
  651. Sleep(WAIT_FOR_USTATUS_THREAD_TIMEOUT);
  652. }
  653. if ( pIniPort->fn.pfnClosePort )
  654. (*pIniPort->fn.pfnClosePort)(pIniPort->hPort);
  655. EnterSplSem();
  656. DeletePortEntry(pIniPort);
  657. LeaveSplSem();
  658. DBGMSG (DBG_TRACE, ("Leave PJLMonClosePort\n"));
  659. return TRUE;
  660. }
  661. BOOL
  662. WriteCommand(
  663. HANDLE hPort,
  664. LPSTR cmd
  665. )
  666. /*++
  667. Routine Description:
  668. Write a command to the port
  669. Arguments:
  670. hPort : Port handle
  671. cmd : Command buffer
  672. Return Value:
  673. TRUE on success, FALSE on error
  674. --*/
  675. {
  676. DWORD cbWrite, cbWritten, dwRet;
  677. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  678. DBGMSG (DBG_TRACE, ("Enter WriteCommand cmd=|%s|\n", cmd));
  679. cbWrite = strlen(cmd);
  680. dwRet = (*pIniPort->fn.pfnWritePort)(pIniPort->hPort, (LPBYTE) cmd, cbWrite, &cbWritten);
  681. if ( dwRet ) {
  682. pIniPort->status &= ~PP_WRITE_ERROR;
  683. } else {
  684. pIniPort->status |= PP_WRITE_ERROR;
  685. DBGMSG(DBG_INFO, ("PJLMON!No data Written\n"));
  686. if ( pIniPort->status & PP_THREAD_RUNNING )
  687. SetEvent(pIniPort->WakeUp);
  688. }
  689. DBGMSG (DBG_TRACE, ("Leave WriteCommand dwRet=%d\n", dwRet));
  690. return dwRet;
  691. }
  692. #define CBSTRING 1024
  693. BOOL
  694. ReadCommand(
  695. HANDLE hPort
  696. )
  697. /*++
  698. Routine Description:
  699. Read a command from the port
  700. Arguments:
  701. hPort : Port handle
  702. Return Value:
  703. TRUE on successfully reading one or more commands, FALSE on error
  704. --*/
  705. {
  706. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  707. DWORD cbRead, cbToRead, cbProcessed, cbPrevious;
  708. char string[CBSTRING];
  709. DWORD status = STATUS_SYNTAX_ERROR; //Value should not matter
  710. BOOL bRet=FALSE;
  711. DBGMSG (DBG_TRACE, ("Enter ReadCommand\n"));
  712. cbPrevious = 0;
  713. ResetEvent(pIniPort->DoneReading);
  714. cbToRead = CBSTRING - 1;
  715. for ( ; ; ) {
  716. if ( !PJLMonReadPort(hPort, (LPBYTE) &string[cbPrevious], cbToRead, &cbRead) )
  717. break;
  718. if ( cbRead ) {
  719. string[cbPrevious + cbRead] = '\0';
  720. DBGMSG(DBG_INFO, ("Read |%s|\n", &string[cbPrevious] ));
  721. status = ProcessPJLString(pIniPort, string, &cbProcessed);
  722. if ( cbProcessed )
  723. bRet = TRUE;
  724. if (status == STATUS_END_OF_STRING ) {
  725. if ( cbProcessed )
  726. strcpy(string, string+cbProcessed);
  727. cbPrevious = cbRead + cbPrevious - cbProcessed;
  728. }
  729. } else {
  730. SPLASSERT(!cbPrevious);
  731. }
  732. if ( status != STATUS_END_OF_STRING && cbRead != cbToRead )
  733. break;
  734. cbToRead = CBSTRING - cbPrevious - 1;
  735. if ( cbToRead == 0 )
  736. DBGMSG(DBG_ERROR,
  737. ("ReadCommand cbToRead is 0 (buffer too small)\n"));
  738. Sleep(WAIT_FOR_DATA_TIMEOUT);
  739. }
  740. SetEvent(pIniPort->DoneReading);
  741. //
  742. // Update the time we last read from printer
  743. //
  744. if ( bRet )
  745. pIniPort->dwLastReadTime = GetTickCount();
  746. DBGMSG (DBG_TRACE, ("Leave ReadCommand bRet=%d\n", bRet));
  747. return bRet;
  748. }
  749. BOOL
  750. WINAPI
  751. PJLMonGetPrinterDataFromPort(
  752. HANDLE hPort,
  753. DWORD ControlID,
  754. LPTSTR pValueName,
  755. LPTSTR lpInBuffer,
  756. DWORD cbInBuffer,
  757. LPTSTR lpOutBuffer,
  758. DWORD cbOutBuffer,
  759. LPDWORD lpcbReturned
  760. )
  761. /*++
  762. Routine Description:
  763. GetPrinter data from port. Supports predefined commands/valuenames.
  764. When we support Value name commands (not supported by DeviceIoControl)
  765. we should check for startdoc -- MuhuntS
  766. This monitor function supports the following two functionalities,
  767. 1. Allow spooler or language monitor to call DeviceIoControl to get
  768. information from the port driver vxd, i.e. ControlID != 0.
  769. And only port monitor support this functionality, language monitor
  770. doesn't, so language monitor just pass this kind of calls down to
  771. port monitor.
  772. 2. Allow app or printer driver query language monitor for some device
  773. information by specifying some key names that both parties understand,
  774. i.e. ControlID == 0 && pValueName != 0. So when printer driver call
  775. DrvGetPrinterData DDI, gdi will call spooler -> language monitor
  776. to get specific device information, for example, UNIDRV does this
  777. to get installed printer memory from PJL printers thru PJLMON.
  778. Only language monitor support this functionality,
  779. port monitor doesn't.
  780. Arguments:
  781. hPort : Port handle
  782. ControId : Control id
  783. pValueName : Value name
  784. lpInBuffer : Input buffer for the command
  785. cbinBuffer : Input buffer size
  786. lpOutBuffer : Output buffer
  787. cbOutBuffer : Output buffer size
  788. lpcbReturned : Set to the amount of data in output buffer on success
  789. Return Value:
  790. TRUE on success, FALSE on error
  791. --*/
  792. {
  793. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  794. BOOL bRet = FALSE, bStopUstatusThread = FALSE;
  795. DBGMSG (DBG_TRACE, ("Enter PJLMonGetPrinterDataFromPort hPort=0x%x\n", hPort));
  796. SPLASSERT(pIniPort && pIniPort->signature == PJ_SIGNATURE);
  797. if ( ControlID ) {
  798. if ( !pIniPort->fn.pfnGetPrinterDataFromPort ) {
  799. SetLastError(ERROR_INVALID_PARAMETER);
  800. return FALSE;
  801. }
  802. return (*pIniPort->fn.pfnGetPrinterDataFromPort)(
  803. pIniPort->hPort,
  804. ControlID,
  805. pValueName,
  806. lpInBuffer,
  807. cbInBuffer,
  808. lpOutBuffer,
  809. cbOutBuffer,
  810. lpcbReturned);
  811. }
  812. //
  813. // Only 2 keys supported
  814. //
  815. if ( lstrcmpi(pValueName, cszInstalledMemory) &&
  816. lstrcmpi(pValueName, cszAvailableMemory) ) {
  817. SetLastError(ERROR_INVALID_PARAMETER);
  818. return FALSE;
  819. }
  820. //
  821. // Wait for crrent job to print since we can't send a PJL command
  822. // in the middle of job
  823. //
  824. WaitForSingleObject(pIniPort->DoneWriting, INFINITE);
  825. // make sure the first write succeeds
  826. // WIN95C BUG 14299, ccteng, 5/18/95
  827. //
  828. // The multi-language printers (4M, 4ML, 4MP, 4V, 4SI), if you print a
  829. // PS print job, the memory resources claimed by the PS processor are not
  830. // release until you enter PCL or reset the printer with "EscE".
  831. //
  832. // So if we had just printed a PS job, the available memory will be
  833. // incorrect if we don't have the "EscE" here.
  834. if ( (pIniPort->status & PP_IS_PJL) &&
  835. WriteCommand(hPort, "\033E\033%-12345X@PJL INFO CONFIG\015\012") ) {
  836. if ( !(pIniPort->status & PP_RUN_THREAD) ) {
  837. bStopUstatusThread = TRUE;
  838. CreateUstatusThread(pIniPort);
  839. }
  840. // PJLMON currently only supports the following pValueName
  841. // 1. installed printer memory
  842. // 2. available printer memory
  843. if ( !lstrcmpi(pValueName, cszInstalledMemory) )
  844. pIniPort->dwInstalledMemory = 0;
  845. else if (!lstrcmpi(pValueName, cszAvailableMemory))
  846. pIniPort->dwAvailableMemory = 0;
  847. ResetEvent(pIniPort->DoneReading);
  848. SetEvent(pIniPort->WakeUp);
  849. WaitForSingleObject(pIniPort->DoneReading, READTHREADTIMEOUT);
  850. WriteCommand(hPort,
  851. "@PJL INFO MEMORY\015\012@PJL INFO STATUS\015\012");
  852. ResetEvent(pIniPort->DoneReading);
  853. SetEvent(pIniPort->WakeUp);
  854. WaitForSingleObject(pIniPort->DoneReading, READTHREADTIMEOUT);
  855. if ( bStopUstatusThread ) {
  856. pIniPort->status &= ~PP_RUN_THREAD;
  857. SetEvent(pIniPort->WakeUp);
  858. }
  859. if ( !lstrcmpi(pValueName, cszInstalledMemory) ) {
  860. *lpcbReturned = sizeof(DWORD);
  861. if ( lpOutBuffer &&
  862. cbOutBuffer >= sizeof(DWORD) &&
  863. pIniPort->dwInstalledMemory ) {
  864. *((LPDWORD)lpOutBuffer) = pIniPort->dwInstalledMemory;
  865. bRet = TRUE;
  866. }
  867. } else if ( !lstrcmpi(pValueName, cszAvailableMemory) ) {
  868. *lpcbReturned = sizeof(DWORD);
  869. if ( lpOutBuffer &&
  870. cbOutBuffer >= sizeof(DWORD) &&
  871. pIniPort->dwAvailableMemory)
  872. {
  873. *((LPDWORD)lpOutBuffer) = pIniPort->dwAvailableMemory;
  874. bRet = TRUE;
  875. }
  876. }
  877. if ( bStopUstatusThread ) {
  878. while (pIniPort->WakeUp)
  879. Sleep(WAIT_FOR_USTATUS_THREAD_TIMEOUT);
  880. }
  881. }
  882. if ( !bRet )
  883. SetLastError(ERROR_INVALID_PARAMETER);
  884. SetEvent(pIniPort->DoneWriting);
  885. DBGMSG (DBG_TRACE, ("Leave PJLMonGetPrinterDataFromPort bRet=0d\n", bRet));
  886. return bRet;
  887. }
  888. #if 0
  889. PBIDI_RESPONSE_CONTAINER
  890. AllocResponse (DWORD dwCount)
  891. {
  892. PBIDI_RESPONSE_CONTAINER pResponse;
  893. pResponse = (PBIDI_RESPONSE_CONTAINER)
  894. LocalAlloc (LPTR, sizeof (PBIDI_RESPONSE_CONTAINER) +
  895. (dwCount - 1) * sizeof (BIDI_RESPONSE_DATA) );
  896. pResponse->Version = 1;
  897. pResponse->Flags = 0;
  898. pResponse->Count = dwCount;
  899. return pResponse;
  900. }
  901. #endif
  902. DWORD
  903. WINAPI
  904. PJLMonBidiSendRecv (
  905. HANDLE hPort,
  906. DWORD dwAccessBit,
  907. LPCWSTR pszAction,
  908. PBIDI_REQUEST_CONTAINER pRequestContainer,
  909. PBIDI_RESPONSE_CONTAINER* ppResponse)
  910. /*++
  911. --*/
  912. {
  913. PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
  914. BOOL bRet = FALSE, bStopUstatusThread = FALSE;
  915. PBIDI_RESPONSE_CONTAINER pResponse = NULL;
  916. #define BIDI_SCHEMA_DUPLEX L"/Printer/Installableoption/Duplexunit"
  917. #define BIDI_SCHEMA_MULTICHANNEL L"/Capability/MultiChannel"
  918. #define BIDI_SCHEMA_VERSION L"/Communication/Version"
  919. #define BIDI_SCHEMA_BIDIPROTOCOL L"/Communication/BidiProtocol"
  920. #define BIDI_SCHEMA_INK_LEVEL L"/Printer/BlackInk1/Level"
  921. #define BIDI_SCHEMA_ALERTS L"/Printer/Alerts"
  922. #define BIDI_PJL L"PJL"
  923. #define BIDI_ALERTNAME L"/Printer/Alerts/1/Name"
  924. #define BIDI_ALERTVALUE L"CoverOpen"
  925. static LPWSTR ppszSchema[] = {
  926. BIDI_SCHEMA_DUPLEX, BIDI_SCHEMA_MULTICHANNEL,
  927. BIDI_SCHEMA_VERSION, BIDI_SCHEMA_BIDIPROTOCOL,
  928. BIDI_SCHEMA_INK_LEVEL, BIDI_SCHEMA_ALERTS
  929. };
  930. static DWORD dwSchemaCount = sizeof (ppszSchema) / sizeof (ppszSchema[0]);
  931. DWORD i, j;
  932. DWORD dwCount;
  933. DWORD dwIndex = 0;
  934. SPLASSERT(pIniPort && pIniPort->signature == PJ_SIGNATURE);
  935. if (!lstrcmpi (pszAction, BIDI_ACTION_ENUM_SCHEMA)) {
  936. // Enum Schema call
  937. dwCount = dwSchemaCount;
  938. pResponse = RouterAllocBidiResponseContainer (dwCount);
  939. pResponse->Version = 1;
  940. pResponse->Count = dwCount;
  941. for (i = 0; i <dwCount; i++ ) {
  942. pResponse->aData[i].dwReqNumber = 0;
  943. pResponse->aData[i].dwResult = S_OK;
  944. pResponse->aData[i].data.dwBidiType = BIDI_TEXT;
  945. pResponse->aData[i].data.u.sData = (LPTSTR)RouterAllocBidiMem (sizeof (TCHAR) * (1 + lstrlen (ppszSchema[i])));
  946. SPLASSERT (pResponse->aData[i].data.u.sData);
  947. lstrcpy (pResponse->aData[i].data.u.sData, ppszSchema[i]);
  948. }
  949. }
  950. else if (!lstrcmpi (pszAction, BIDI_ACTION_GET)) {
  951. dwCount = pRequestContainer->Count;
  952. pResponse = RouterAllocBidiResponseContainer (dwCount);
  953. SPLASSERT (pResponse);
  954. pResponse->Version = 1;
  955. pResponse->Count = dwCount;
  956. for (i = 0; i <dwCount; i++ ) {
  957. DWORD dwSchemaId = 0xffffffff;
  958. for (j = 0; j < dwSchemaCount;j++ ) {
  959. if (!lstrcmpi (ppszSchema[j], pRequestContainer->aData[i].pSchema)) {
  960. dwSchemaId = j;
  961. break;
  962. }
  963. }
  964. switch (dwSchemaId) {
  965. case 0:
  966. // duplex
  967. pResponse->aData[i].dwReqNumber = i;
  968. pResponse->aData[i].dwResult = S_OK;
  969. pResponse->aData[i].data.dwBidiType = BIDI_BOOL;
  970. pResponse->aData[i].data.u.bData = TRUE;
  971. break;
  972. case 1:
  973. // multiple channel
  974. pResponse->aData[i].dwReqNumber = i;
  975. pResponse->aData[i].dwResult = S_OK;
  976. pResponse->aData[i].data.dwBidiType = BIDI_BOOL;
  977. pResponse->aData[i].data.u.bData = FALSE;
  978. break;
  979. case 2:
  980. // Version
  981. pResponse->aData[i].dwReqNumber = i;
  982. pResponse->aData[i].dwResult = S_OK;
  983. pResponse->aData[i].data.dwBidiType = BIDI_INT;
  984. pResponse->aData[i].data.u.iData = 1;
  985. break;
  986. case 3:
  987. // BidiProtocol
  988. pResponse->aData[i].dwReqNumber = i;
  989. pResponse->aData[i].dwResult = S_OK;
  990. pResponse->aData[i].data.dwBidiType = BIDI_ENUM;
  991. pResponse->aData[i].data.u.sData = (LPWSTR) RouterAllocBidiMem (
  992. sizeof (WCHAR) * (lstrlen (BIDI_PJL) + 1));;
  993. lstrcpy (pResponse->aData[i].data.u.sData , BIDI_PJL);
  994. break;
  995. case 4:
  996. // Ink Level
  997. pResponse->aData[i].dwReqNumber = i;
  998. pResponse->aData[i].dwResult = S_OK;
  999. pResponse->aData[i].data.dwBidiType = BIDI_FLOAT;
  1000. pResponse->aData[i].data.u.fData = (FLOAT) 0.69;
  1001. break;
  1002. default:
  1003. pResponse->aData[i].dwReqNumber = i;
  1004. pResponse->aData[i].dwResult = E_FAIL;
  1005. }
  1006. }
  1007. }
  1008. else if (!lstrcmpi (pszAction, BIDI_ACTION_GET_ALL)) {
  1009. dwCount = pRequestContainer->Count;
  1010. pResponse = RouterAllocBidiResponseContainer (256);
  1011. SPLASSERT (pResponse);
  1012. pResponse->Version = 1;
  1013. pResponse->Count = 256;
  1014. for (i = 0; i < dwCount; i++) {
  1015. if (!lstrcmpi (pRequestContainer->aData[i].pSchema, ppszSchema[5])) {
  1016. for (j = 0; j < 3; j++) {
  1017. pResponse->aData[dwIndex].dwReqNumber = i;
  1018. pResponse->aData[dwIndex].pSchema = (LPTSTR) RouterAllocBidiMem (
  1019. sizeof (TCHAR) * (lstrlen (BIDI_ALERTNAME) + 1));
  1020. lstrcpy (pResponse->aData[dwIndex].pSchema, BIDI_ALERTNAME);
  1021. pResponse->aData[dwIndex].dwResult = S_OK;
  1022. pResponse->aData[dwIndex].data.dwBidiType = BIDI_ENUM;
  1023. pResponse->aData[dwIndex].data.u.sData = (LPTSTR) RouterAllocBidiMem (
  1024. sizeof (TCHAR) * (lstrlen (BIDI_ALERTVALUE) + 1));
  1025. lstrcpy (pResponse->aData[dwIndex].data.u.sData, BIDI_ALERTVALUE);
  1026. dwIndex ++;
  1027. }
  1028. }
  1029. else {
  1030. pResponse->aData[dwIndex].dwReqNumber = i;
  1031. pResponse->aData[dwIndex].dwResult = E_FAIL;
  1032. dwIndex++;
  1033. }
  1034. }
  1035. pResponse->Count = dwIndex;
  1036. }
  1037. else {
  1038. pResponse = NULL;
  1039. }
  1040. *ppResponse = pResponse;
  1041. return 0;
  1042. }
  1043. VOID WINAPI
  1044. PJLShutdown (
  1045. HANDLE hMonitor
  1046. )
  1047. {
  1048. }
  1049. MONITOR2 Monitor2 = {
  1050. sizeof(MONITOR2),
  1051. NULL, // EnumPrinters not supported
  1052. NULL, // OpenPort not supported
  1053. PJLMonOpenPortEx,
  1054. PJLMonStartDocPort,
  1055. PJLMonWritePort,
  1056. PJLMonReadPort,
  1057. PJLMonEndDocPort,
  1058. PJLMonClosePort,
  1059. NULL, // AddPort not supported
  1060. NULL, // AddPortEx not supported
  1061. NULL, // ConfigurePort not supported
  1062. NULL, // DeletePort not supported
  1063. PJLMonGetPrinterDataFromPort,
  1064. NULL, // SetPortTimeOuts not supported
  1065. NULL, // XcvOpen
  1066. NULL, // XcvData
  1067. NULL, // XcvClose
  1068. PJLShutdown, // Shutdown
  1069. PJLMonBidiSendRecv
  1070. };
  1071. LPMONITOR2
  1072. WINAPI
  1073. InitializePrintMonitor2(
  1074. IN PMONITORINIT pMonitorInit,
  1075. IN PHANDLE phMonitor
  1076. )
  1077. /*++
  1078. Routine Description:
  1079. Fill the monitor function table. Spooler makes call to this routine
  1080. to get the monitor functions.
  1081. Arguments:
  1082. pszRegistryRoot : Registry root to be used by this dll
  1083. lpMonitor : Pointer to monitor fucntion table to be filled
  1084. Return Value:
  1085. TRUE on successfully initializing the monitor, false on error.
  1086. --*/
  1087. {
  1088. if ( !pMonitorInit || !(pMonitorInit->hckRegistryRoot)) {
  1089. SetLastError(ERROR_INVALID_PARAMETER);
  1090. return NULL;
  1091. }
  1092. if ( UpdateTimeoutsFromRegistry(pMonitorInit->hSpooler,
  1093. pMonitorInit->hckRegistryRoot,
  1094. pMonitorInit->pMonitorReg) != ERROR_SUCCESS ) {
  1095. return NULL;
  1096. }
  1097. *phMonitor = NULL;
  1098. return &Monitor2;
  1099. }
  1100. #define NTOKEN 20
  1101. DWORD
  1102. ProcessPJLString(
  1103. PINIPORT pIniPort,
  1104. LPSTR pInString,
  1105. DWORD *lpcbProcessed
  1106. )
  1107. /*++
  1108. Routine Description:
  1109. Process a PJL string read from the printer
  1110. Arguments:
  1111. pIniPort : Ini port
  1112. pInString : Input string to process
  1113. lpcbProcessed : On return set to the amount of data processed
  1114. Return Value:
  1115. Status value of the processing
  1116. --*/
  1117. {
  1118. TOKENPAIR tokenPairs[NTOKEN];
  1119. DWORD nTokenParsedRet;
  1120. LPSTR lpRet;
  1121. DWORD status = 0;
  1122. lpRet = pInString;
  1123. #ifdef DEBUG
  1124. OutputDebugStringA("String to process: <");
  1125. OutputDebugStringA(pInString);
  1126. OutputDebugStringA(">\n");
  1127. #endif
  1128. for (*lpcbProcessed = 0; *pInString != 0; pInString = lpRet) {
  1129. //
  1130. // hack to determine if printer is bi-di. LJ 4 does not have p1284
  1131. // device ID so we do PCL memory query and see if it returns anything
  1132. //
  1133. if (!(pIniPort->status & PP_IS_PJL) &&
  1134. !mystrncmp(pInString, "PCL\015\012INFO MEMORY", 16) )
  1135. pIniPort->status |= PP_IS_PJL;
  1136. status = GetPJLTokens(pInString, NTOKEN, tokenPairs,
  1137. &nTokenParsedRet, &lpRet);
  1138. if (status == STATUS_REACHED_END_OF_COMMAND_OK) {
  1139. pIniPort->status |= PP_IS_PJL;
  1140. InterpreteTokens(pIniPort, tokenPairs, nTokenParsedRet);
  1141. } else {
  1142. ProcessParserError(status);
  1143. }
  1144. //
  1145. // if a PJL command straddles between buffers
  1146. //
  1147. if (status == STATUS_END_OF_STRING)
  1148. break;
  1149. *lpcbProcessed += (DWORD)(lpRet - pInString);
  1150. }
  1151. return status;
  1152. }
  1153. DWORD
  1154. SeverityFromPjlStatus(
  1155. DWORD dwPjlStatus
  1156. )
  1157. {
  1158. if ( dwPjlStatus >= 10000 && dwPjlStatus < 12000 ) {
  1159. //
  1160. // 10xyz
  1161. // 11xyz : load paper (paper available on another tray)
  1162. //
  1163. return PORT_STATUS_TYPE_WARNING;
  1164. } else if ( dwPjlStatus >= 30000 && dwPjlStatus < 31000 ) {
  1165. //
  1166. // 30xyz : Auto continuable errors
  1167. //
  1168. return PORT_STATUS_TYPE_WARNING;
  1169. } else if ( dwPjlStatus >= 35000 && dwPjlStatus < 36000 ) {
  1170. //
  1171. // 35xyz : Potential operator intervention conditions
  1172. //
  1173. return PORT_STATUS_TYPE_WARNING;
  1174. } else if ( dwPjlStatus > 40000 && dwPjlStatus < 42000 ) {
  1175. //
  1176. // 40xyz : Operator intervention required
  1177. // 41xyz : Load paper errors
  1178. //
  1179. return PORT_STATUS_TYPE_ERROR;
  1180. }
  1181. DBGMSG(DBG_ERROR,
  1182. ("SeverityFromPjlStatus: Unknown status %d\n", dwPjlStatus));
  1183. return PORT_STATUS_TYPE_INFO;
  1184. }
  1185. VOID
  1186. InterpreteTokens(
  1187. PINIPORT pIniPort,
  1188. PTOKENPAIR tokenPairs,
  1189. DWORD nTokenParsed
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. Interpret succesfully read PJL tokens
  1194. Arguments:
  1195. pIniPort : Ini port
  1196. tokenPairs : List of token pairs
  1197. nTokenParsed : Number of token pairs
  1198. Return Value:
  1199. None
  1200. --*/
  1201. {
  1202. DWORD i, OldStatus;
  1203. PJLTOPRINTERSTATUS *pMap;
  1204. PORT_INFO_3 PortInfo3;
  1205. DWORD dwSeverity = 0;
  1206. HANDLE hToken;
  1207. #ifdef DEBUG
  1208. char msg[CBSTRING];
  1209. msg[0] = '\0';
  1210. #endif
  1211. OldStatus = pIniPort->PrinterStatus;
  1212. pIniPort->PrinterStatus = 0;
  1213. for (i = 0; i < nTokenParsed; i++) {
  1214. // DBGMSG(DBG_INFO, ("pjlmon!Token=0x%x, Value=%d\n",
  1215. // tokenPairs[i].token, tokenPairs[i].value));
  1216. switch(tokenPairs[i].token) {
  1217. case TOKEN_INFO_STATUS_CODE:
  1218. case TOKEN_USTATUS_DEVICE_CODE:
  1219. for (pMap = PJLToStatus; pMap->pjl; pMap++) {
  1220. if (pMap->pjl == tokenPairs[i].value) {
  1221. pIniPort->PrinterStatus = pMap->status;
  1222. dwSeverity = SeverityFromPjlStatus(pMap->pjl);
  1223. if ( dwSeverity == PORT_STATUS_TYPE_ERROR )
  1224. pIniPort->status |= PP_PRINTER_OFFLINE;
  1225. else
  1226. pIniPort->status &= ~PP_PRINTER_OFFLINE;
  1227. break;
  1228. }
  1229. }
  1230. if ( pMap->pjl && pMap->pjl == tokenPairs[i].value )
  1231. break;
  1232. //
  1233. // some printers use this to signal online/ready
  1234. //
  1235. if ( tokenPairs[i].value == 10001 ||
  1236. tokenPairs[i].value == 10002 ||
  1237. tokenPairs[i].value == 11002 ) {
  1238. pIniPort->status &= ~PP_PRINTER_OFFLINE;
  1239. pIniPort->PrinterStatus = 0;
  1240. dwSeverity = 0;
  1241. }
  1242. //
  1243. // background or foreground paper out
  1244. //
  1245. if ( tokenPairs[i].value > 11101 && tokenPairs[i].value < 12000 ||
  1246. tokenPairs[i].value > 41101 && tokenPairs[i].value < 42000 ) {
  1247. pIniPort->PrinterStatus = PORT_STATUS_PAPER_OUT;
  1248. if ( tokenPairs[i].value > 4000 ) {
  1249. dwSeverity = PORT_STATUS_TYPE_ERROR;
  1250. pIniPort->status |= PP_PRINTER_OFFLINE;
  1251. } else {
  1252. dwSeverity = PORT_STATUS_TYPE_WARNING;
  1253. }
  1254. } else if (tokenPairs[i].value > 40000) {
  1255. pIniPort->PrinterStatus = PORT_STATUS_USER_INTERVENTION;
  1256. pIniPort->status |= PP_PRINTER_OFFLINE;
  1257. dwSeverity = PORT_STATUS_TYPE_ERROR;
  1258. }
  1259. break;
  1260. case TOKEN_INFO_STATUS_ONLINE:
  1261. case TOKEN_USTATUS_DEVICE_ONLINE:
  1262. // DBGMSG(DBG_INFO, ("PJLMON:ONLINE = %d\n", tokenPairs[i].value));
  1263. if (tokenPairs[i].value) {
  1264. pIniPort->status &= ~PP_PRINTER_OFFLINE;
  1265. dwSeverity = pIniPort->PrinterStatus ? PORT_STATUS_TYPE_WARNING :
  1266. 0;
  1267. } else {
  1268. if ( !pIniPort->PrinterStatus )
  1269. pIniPort->PrinterStatus = PORT_STATUS_OFFLINE;
  1270. pIniPort->status |= PP_PRINTER_OFFLINE;
  1271. dwSeverity = PORT_STATUS_TYPE_ERROR;
  1272. }
  1273. break;
  1274. case TOKEN_USTATUS_JOB_NAME_MSJOB:
  1275. #ifdef DEBUG
  1276. sprintf(msg, "EOJ for %d\n", tokenPairs[i].value);
  1277. OutputDebugStringA(msg);
  1278. #endif
  1279. SendJobLastPageEjected(pIniPort, (DWORD)tokenPairs[i].value, FALSE);
  1280. break;
  1281. case TOKEN_INFO_CONFIG_MEMORY:
  1282. case TOKEN_INFO_CONFIG_MEMORY_SPACE:
  1283. // IMPORTANT NOTE:
  1284. //
  1285. // Use SetPrinterData to cache the information in printer's registry.
  1286. // GDI's DrvGetPrinterData will check the printer's registry first,
  1287. // and if cache data is available, it will use it and not call
  1288. // GetPrinterData (which calls language monitor's
  1289. // GetPrinterDataFromPort).
  1290. #ifdef DEBUG
  1291. sprintf(msg, "PJLMON installed memory %d\n", tokenPairs[i].value);
  1292. OutputDebugStringA(msg);
  1293. #endif
  1294. pIniPort->dwInstalledMemory = (DWORD)tokenPairs[i].value;
  1295. break;
  1296. case TOKEN_INFO_MEMORY_TOTAL:
  1297. // IMPORTANT NOTE:
  1298. //
  1299. // Use SetPrinterData to cache the information in printer's registry.
  1300. // GDI's DrvGetPrinterData will check the printer's registry first,
  1301. // and if cache data is available, it will use it and not call
  1302. // GetPrinterData (which calls language monitor's
  1303. // GetPrinterDataFromPort).
  1304. #ifdef DEBUG
  1305. sprintf(msg, "PJLMON available memory %d\n", tokenPairs[i].value);
  1306. OutputDebugStringA(msg);
  1307. #endif
  1308. pIniPort->dwAvailableMemory = (DWORD)tokenPairs[i].value;
  1309. break;
  1310. default:
  1311. break;
  1312. }
  1313. }
  1314. if ( OldStatus != pIniPort->PrinterStatus ) {
  1315. ZeroMemory(&PortInfo3, sizeof(PortInfo3));
  1316. PortInfo3.dwStatus = pIniPort->PrinterStatus;
  1317. PortInfo3.dwSeverity = dwSeverity;
  1318. if ( !SetPort(NULL,
  1319. pIniPort->pszPortName,
  1320. 3,
  1321. (LPBYTE)&PortInfo3) ) {
  1322. DBGMSG(DBG_WARNING,
  1323. ("pjlmon: SetPort failed %d (LE: %d)\n",
  1324. pIniPort->PrinterStatus, GetLastError()));
  1325. pIniPort->PrinterStatus = OldStatus;
  1326. }
  1327. }
  1328. }
  1329. VOID
  1330. ProcessParserError(
  1331. DWORD status
  1332. )
  1333. /*++
  1334. Routine Description:
  1335. Print error messages on parsing error
  1336. Arguments:
  1337. status : status
  1338. Return Value:
  1339. None
  1340. --*/
  1341. {
  1342. #ifdef DEBUG
  1343. LPSTR pString;
  1344. switch (status)
  1345. {
  1346. case STATUS_REACHED_END_OF_COMMAND_OK:
  1347. pString = "STATUS_REACHED_END_OF_COMMAND_OK\n";
  1348. break;
  1349. case STATUS_CONTINUE:
  1350. pString = "STATUS_CONTINUE\n";
  1351. break;
  1352. case STATUS_REACHED_FF:
  1353. pString = "STATUS_REACHED_FF\n";
  1354. break;
  1355. case STATUS_END_OF_STRING:
  1356. pString = "STATUS_END_OF_STRING\n";
  1357. break;
  1358. case STATUS_SYNTAX_ERROR:
  1359. pString = "STATUS_SYNTAX_ERROR\n";
  1360. break;
  1361. case STATUS_ATPJL_NOT_FOUND:
  1362. pString = "STATUS_ATPJL_NOT_FOUND\n";
  1363. break;
  1364. case STATUS_NOT_ENOUGH_ROOM_FOR_TOKENS:
  1365. pString = "STATUS_NOT_ENOUGH_ROOM_FOR_TOKENS\n";
  1366. break;
  1367. default:
  1368. pString = "INVALID STATUS RETURNED!!!!!!\n";
  1369. break;
  1370. };
  1371. OutputDebugStringA(pString);
  1372. #endif
  1373. }
  1374. #define MODEL "MODEL:"
  1375. #define MDL "MDL:"
  1376. #define COMMAND "COMMAND SET:"
  1377. #define CMD "CMD:"
  1378. #define COLON ':'
  1379. #define SEMICOLON ';'
  1380. LPSTR
  1381. FindP1284Key(
  1382. PINIPORT pIniPort,
  1383. LPSTR lpKey
  1384. )
  1385. /*++
  1386. Routine Description:
  1387. Find the 1284 key identifying the device id
  1388. Arguments:
  1389. status : status
  1390. Return Value:
  1391. Pointer to the command string, NULL if not found.
  1392. --*/
  1393. {
  1394. LPSTR lpValue; // Pointer to the Key's value
  1395. WORD wKeyLength; // Length for the Key (for stringcmps)
  1396. LPSTR bRet = NULL;
  1397. // While there are still keys to look at.
  1398. #ifdef DEBUG
  1399. OutputDebugStringA("PJLMon!DeviceId : <");
  1400. OutputDebugStringA(lpKey);
  1401. OutputDebugStringA(">\n");
  1402. #endif
  1403. while (lpKey && *lpKey) {
  1404. //
  1405. // Is there a terminating COLON character for the current key?
  1406. //
  1407. if (!(lpValue = mystrchr(lpKey, COLON)) ) {
  1408. //
  1409. // N: OOPS, somthing wrong with the Device ID
  1410. //
  1411. return bRet;
  1412. }
  1413. //
  1414. // The actual start of the Key value is one past the COLON
  1415. //
  1416. ++lpValue;
  1417. //
  1418. // Compute the Key length for Comparison, including the COLON
  1419. // which will serve as a terminator
  1420. //
  1421. wKeyLength = (WORD)(lpValue - lpKey);
  1422. //
  1423. // Compare the Key to the Know quantities. To speed up the comparison
  1424. // a Check is made on the first character first, to reduce the number
  1425. // of strings to compare against.
  1426. // If a match is found, the appropriate lpp parameter is set to the
  1427. // key's value, and the terminating SEMICOLON is converted to a NULL
  1428. // In all cases lpKey is advanced to the next key if there is one.
  1429. //
  1430. if ( *lpKey == 'C' ) {
  1431. //
  1432. // Look for COMMAND SET or CMD
  1433. //
  1434. if ( !mystrncmp(lpKey, COMMAND, wKeyLength) ||
  1435. !mystrncmp(lpKey, CMD, wKeyLength) ) {
  1436. bRet = lpValue;
  1437. }
  1438. }
  1439. // Go to the next Key
  1440. if ( lpKey = mystrchr(lpValue, SEMICOLON) ) {
  1441. *lpKey = '\0';
  1442. ++lpKey;
  1443. }
  1444. }
  1445. return bRet;
  1446. }
  1447. BOOL
  1448. IsPJL(
  1449. PINIPORT pIniPort
  1450. )
  1451. /*++
  1452. Routine Description:
  1453. Finds out if the printer is a PJL bi-di printer
  1454. Arguments:
  1455. pIniPort : Points to an INIPORT
  1456. Return Value:
  1457. TRUE if printer is PJL bi-di, else FALSE
  1458. On failure PP_DONT_TRY_PJL is set
  1459. --*/
  1460. {
  1461. char szID[MAX_DEVID];
  1462. DWORD cbRet;
  1463. LPSTR lpCMD;
  1464. HANDLE hPort = (HANDLE)pIniPort;
  1465. BOOL bRet = FALSE;
  1466. //
  1467. // for printers that supports P1284 plug and play like LJ 4L, DJ540.
  1468. // we parse the COMMAND string and see if PJL is supported
  1469. //
  1470. if (pIniPort->fn.pfnGetPrinterDataFromPort) {
  1471. //
  1472. // Only try P1284 if port monitor supports DeviceIOCtl
  1473. //
  1474. memset((LPBYTE)szID, 0, sizeof(szID));
  1475. cbRet = 0;
  1476. if ((*pIniPort->fn.pfnGetPrinterDataFromPort)
  1477. (pIniPort->hPort, GETDEVICEID, NULL, NULL,
  1478. 0, (LPWSTR)szID, sizeof(szID), &cbRet)
  1479. && cbRet) {
  1480. //
  1481. // succeeded the P1284 plug and play protocol
  1482. //
  1483. szID[cbRet] = '\0';
  1484. if ( lpCMD = FindP1284Key(pIniPort, szID) ) {
  1485. // found the COMMAND string
  1486. while (*lpCMD) {
  1487. //
  1488. // look for "PJL"
  1489. //
  1490. if ( lpCMD[0] == 'P' && lpCMD[1] == 'J' && lpCMD[2] == 'L' ){
  1491. pIniPort->status &= ~PP_DONT_TRY_PJL;
  1492. bRet = TRUE;
  1493. goto Cleanup;
  1494. }
  1495. lpCMD++;
  1496. }
  1497. pIniPort->status |= PP_DONT_TRY_PJL;
  1498. goto Cleanup;
  1499. }
  1500. }
  1501. //
  1502. // fall thru to try PJL bi-di if we failed the P1284 communication
  1503. // or P1284 didn't return a COMMAND string
  1504. //
  1505. }
  1506. //
  1507. // for printers that don't support P1284 plug and play, but support PJL
  1508. // language command, like LJ 4 and 4M. we try to write/read PJL
  1509. // command and see if it succeeds.
  1510. // if we can't set the time outs we don't want to try to read, just fail.
  1511. //
  1512. if ( pIniPort->fn.pfnSetPortTimeOuts &&
  1513. !(pIniPort->status & PP_DONT_TRY_PJL)) {
  1514. COMMTIMEOUTS CTO;
  1515. memset((LPSTR)&CTO, 0, sizeof(CTO));
  1516. CTO.ReadTotalTimeoutConstant = 5000;
  1517. CTO.ReadIntervalTimeout = 200;
  1518. if ( !(*pIniPort->fn.pfnSetPortTimeOuts)(pIniPort->hPort, &CTO, 0) ) {
  1519. goto Cleanup;
  1520. }
  1521. // This <ESC>*s1M is a PCL5 command to determine the amount of memory
  1522. // in a PCL5 printer, and if the printer is PCL5 and bi-di capable,
  1523. // it will return "PCL\015\012INFO MEMORY".
  1524. // See PJL Tech Ref Manual page 7-21.
  1525. pIniPort->status &= ~PP_IS_PJL;
  1526. if ( !WriteCommand(hPort, "\033*s1M") )
  1527. goto Cleanup;
  1528. // ReadCommand->ProcessPJLString will set PP_IS_PJL
  1529. // if we read any valid PJL command back from the printer
  1530. if ( !ReadCommand(hPort) ) {
  1531. //
  1532. // We have jumped through the hoop to determin if this printer can
  1533. // understand PJL. It DOES NOT. We are not going to try again.
  1534. // until there is a printer change.
  1535. //
  1536. pIniPort->status |= PP_DONT_TRY_PJL;
  1537. }
  1538. if (pIniPort->status & PP_IS_PJL) {
  1539. bRet = TRUE;
  1540. goto Cleanup;
  1541. }
  1542. }
  1543. Cleanup:
  1544. if ( bRet ) {
  1545. WriteCommand(hPort, "\033%-12345X@PJL \015\012@PJL USTATUS TIMED 30 \015\012\033%-12345X");
  1546. pIniPort->dwLastReadTime = GetTickCount();
  1547. }
  1548. //return bRet;
  1549. return TRUE;
  1550. }