Source code of Windows XP (NT5)
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.

588 lines
14 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. vdmprint.c
  5. Abstract:
  6. This module contains the support for printing ports which could be
  7. handled in kernel without going to ntvdm.exe
  8. Author:
  9. Sudeep Bharati (sudeepb) 16-Jan-1993
  10. Revision History:
  11. William Hsieh (williamh) 31-May-1996
  12. rewrote for Dongle support
  13. --*/
  14. #include "vdmp.h"
  15. #include <ntddvdm.h>
  16. NTSTATUS
  17. VdmpFlushPrinterWriteData (
  18. IN USHORT Adapter
  19. );
  20. #define DATA_PORT_OFFSET 0
  21. #define STATUS_PORT_OFFSET 1
  22. #define CONTROL_PORT_OFFSET 2
  23. #define LPT1_PORT_STATUS 0x3bd
  24. #define LPT2_PORT_STATUS 0x379
  25. #define LPT3_PORT_STATUS 0x279
  26. #define LPT_MASK 0xff0
  27. #define IRQ 0x10
  28. #define NOTBUSY 0x80
  29. #define HOST_LPT_BUSY (1 << 0)
  30. #define STATUS_REG_MASK 0x07
  31. #ifdef ALLOC_PRAGMA
  32. #pragma alloc_text(PAGE, VdmPrinterStatus)
  33. #pragma alloc_text(PAGE, VdmPrinterWriteData)
  34. #pragma alloc_text(PAGE, VdmpFlushPrinterWriteData)
  35. #pragma alloc_text(PAGE, VdmpPrinterInitialize)
  36. #pragma alloc_text(PAGE, VdmpPrinterDirectIoOpen)
  37. #pragma alloc_text(PAGE, VdmpPrinterDirectIoClose)
  38. #endif
  39. BOOLEAN
  40. VdmPrinterStatus (
  41. IN ULONG iPort,
  42. IN ULONG cbInstructionSize,
  43. IN PKTRAP_FRAME TrapFrame
  44. )
  45. /*++
  46. Routine Description:
  47. This routine handles the read operation on the printer status port
  48. Arguments:
  49. iPort - port on which the io was trapped
  50. cbInstructionSize - Instruction size to update TsEip
  51. TrapFrame - Trap Frame
  52. Return Value:
  53. True if successful, False otherwise
  54. --*/
  55. {
  56. UCHAR PrtMode;
  57. HANDLE PrintHandle;
  58. volatile PUCHAR HostStatus;
  59. volatile PUCHAR AdapterStatus;
  60. volatile PUCHAR AdapterControl;
  61. USHORT adapter;
  62. KIRQL OldIrql;
  63. PVDM_TIB VdmTib;
  64. NTSTATUS Status;
  65. PULONG printer_status;
  66. LOGICAL IssueIoControl;
  67. PVDM_PRINTER_INFO PrtInfo;
  68. PIO_STATUS_BLOCK IoStatusBlock;
  69. PVDM_PROCESS_OBJECTS VdmObjects;
  70. PAGED_CODE();
  71. Status = VdmpGetVdmTib(&VdmTib);
  72. if (!NT_SUCCESS(Status)) {
  73. return FALSE;
  74. }
  75. PrtInfo = &VdmTib->PrinterInfo;
  76. IoStatusBlock = (PIO_STATUS_BLOCK) &VdmTib->TempArea1;
  77. printer_status = &VdmTib->PrinterInfo.prt_Scratch;
  78. IssueIoControl = FALSE;
  79. try {
  80. //
  81. // First figure out which PRT we are dealing with. The
  82. // port addresses in the PrinterInfo are base address of each
  83. // PRT sorted in the adapter order.
  84. //
  85. *FIXED_NTVDMSTATE_LINEAR_PC_AT |= VDM_IDLEACTIVITY;
  86. if ((USHORT)iPort == PrtInfo->prt_PortAddr[0] + STATUS_PORT_OFFSET) {
  87. adapter = 0;
  88. }
  89. else if ((USHORT)iPort == PrtInfo->prt_PortAddr[1] + STATUS_PORT_OFFSET) {
  90. adapter = 1;
  91. }
  92. else if ((USHORT)iPort == PrtInfo->prt_PortAddr[2] + STATUS_PORT_OFFSET) {
  93. adapter = 2;
  94. }
  95. else {
  96. // something must be wrong in our code, better check it out
  97. ASSERT (FALSE);
  98. return FALSE;
  99. }
  100. PrtMode = PrtInfo->prt_Mode[adapter];
  101. VdmObjects = (PVDM_PROCESS_OBJECTS) (PsGetCurrentProcess()->VdmObjects);
  102. AdapterStatus = VdmObjects->PrinterStatus + adapter;
  103. if (PRT_MODE_SIMULATE_STATUS_PORT == PrtMode) {
  104. //
  105. // We are simulating a printer status read.
  106. // Get the current status from softpc.
  107. //
  108. HostStatus = VdmObjects->PrinterHostState + adapter;
  109. if (!(*AdapterStatus & NOTBUSY) && !(*HostStatus & HOST_LPT_BUSY)) {
  110. AdapterControl = VdmObjects->PrinterControl + adapter;
  111. if (*AdapterControl & IRQ) {
  112. return FALSE;
  113. }
  114. *AdapterStatus = (*AdapterStatus | NOTBUSY);
  115. }
  116. *printer_status = (ULONG)(*AdapterStatus | STATUS_REG_MASK);
  117. TrapFrame->Eax &= 0xffffff00;
  118. TrapFrame->Eax |= (UCHAR)*printer_status;
  119. TrapFrame->Eip += cbInstructionSize;
  120. }
  121. else if (PRT_MODE_DIRECT_IO == PrtMode) {
  122. //
  123. // We have to read the I/O directly (of course, through file system
  124. // which in turn goes to the driver).
  125. // Before performing the read, flush out all pending output data
  126. // in our buffer. This is done because the status we are about
  127. // to read may depend on the pending output data.
  128. //
  129. if (PrtInfo->prt_BytesInBuffer[adapter]) {
  130. Status = VdmpFlushPrinterWriteData (adapter);
  131. #ifdef DBG
  132. if (!NT_SUCCESS(Status)) {
  133. DbgPrint("VdmPrintStatus: failed to flush buffered data, status = %ls\n", Status);
  134. }
  135. #endif
  136. }
  137. //
  138. // Capture this argument first as this reference may cause an
  139. // exception.
  140. //
  141. PrintHandle = PrtInfo->prt_Handle[adapter];
  142. //
  143. // Lower irql to PASSIVE before doing any I/O.
  144. //
  145. OldIrql = KeGetCurrentIrql ();
  146. KeLowerIrql (PASSIVE_LEVEL);
  147. IssueIoControl = TRUE;
  148. }
  149. else {
  150. //
  151. // We don't simulate it here.
  152. //
  153. return FALSE;
  154. }
  155. } except(EXCEPTION_EXECUTE_HANDLER) {
  156. Status = GetExceptionCode();
  157. }
  158. if (IssueIoControl == TRUE) {
  159. Status = NtDeviceIoControlFile(PrintHandle,
  160. NULL, // notification event
  161. NULL, // APC routine
  162. NULL, // Apc Context
  163. IoStatusBlock,
  164. IOCTL_VDM_PAR_READ_STATUS_PORT,
  165. NULL,
  166. 0,
  167. printer_status,
  168. sizeof(ULONG));
  169. try {
  170. if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatusBlock->Status)) {
  171. //
  172. // fake a status to make it looks like the port is not connected
  173. // to a printer.
  174. //
  175. *printer_status = 0x7F;
  176. #ifdef DBG
  177. DbgPrint("VdmPrinterStatus: failed to get status from printer, status = %lx\n", Status);
  178. #endif
  179. //
  180. // Always tell the caller that we have simulated the operation.
  181. //
  182. Status = STATUS_SUCCESS;
  183. }
  184. TrapFrame->Eax &= 0xffffff00;
  185. TrapFrame->Eax |= (UCHAR)*printer_status;
  186. TrapFrame->Eip += cbInstructionSize;
  187. }
  188. except (EXCEPTION_EXECUTE_HANDLER) {
  189. Status = GetExceptionCode();
  190. }
  191. //
  192. // Regardless of any exceptions that may have occurred, we must
  193. // restore our caller's IRQL since we lowered it.
  194. //
  195. KeRaiseIrql (OldIrql, &OldIrql);
  196. }
  197. if (!NT_SUCCESS(Status)) {
  198. return FALSE;
  199. }
  200. return TRUE;
  201. }
  202. BOOLEAN
  203. VdmPrinterWriteData (
  204. IN ULONG iPort,
  205. IN ULONG cbInstructionSize,
  206. IN PKTRAP_FRAME TrapFrame
  207. )
  208. {
  209. PVDM_PRINTER_INFO PrtInfo;
  210. USHORT adapter;
  211. PVDM_TIB VdmTib;
  212. NTSTATUS Status;
  213. PAGED_CODE();
  214. Status = VdmpGetVdmTib(&VdmTib);
  215. if (!NT_SUCCESS(Status)) {
  216. return FALSE;
  217. }
  218. PrtInfo = &VdmTib->PrinterInfo;
  219. try {
  220. //
  221. // First figure out which PRT we are dealing with. The
  222. // port addresses in the PrinterInfo are base address of each
  223. // PRT sorted in the adapter order.
  224. //
  225. *FIXED_NTVDMSTATE_LINEAR_PC_AT |= VDM_IDLEACTIVITY;
  226. if ((USHORT)iPort == PrtInfo->prt_PortAddr[0] + DATA_PORT_OFFSET) {
  227. adapter = 0;
  228. }
  229. else if ((USHORT)iPort == PrtInfo->prt_PortAddr[1] + DATA_PORT_OFFSET) {
  230. adapter = 1;
  231. }
  232. else if ((USHORT)iPort == PrtInfo->prt_PortAddr[2] + DATA_PORT_OFFSET) {
  233. adapter = 2;
  234. }
  235. else {
  236. // something must be wrong in our code, better check it out
  237. ASSERT(FALSE);
  238. return FALSE;
  239. }
  240. if (PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[adapter]) {
  241. PrtInfo->prt_Buffer[adapter][PrtInfo->prt_BytesInBuffer[adapter]] = (UCHAR)TrapFrame->Eax;
  242. //
  243. // buffer full, then flush it out
  244. //
  245. if (++PrtInfo->prt_BytesInBuffer[adapter] >= PRT_DATA_BUFFER_SIZE) {
  246. VdmpFlushPrinterWriteData(adapter);
  247. }
  248. TrapFrame->Eip += cbInstructionSize;
  249. }
  250. else {
  251. Status = STATUS_ILLEGAL_INSTRUCTION;
  252. }
  253. } except(EXCEPTION_EXECUTE_HANDLER) {
  254. Status = GetExceptionCode();
  255. }
  256. if (!NT_SUCCESS(Status)) {
  257. return FALSE;
  258. }
  259. return TRUE;
  260. }
  261. NTSTATUS
  262. VdmpFlushPrinterWriteData (
  263. IN USHORT adapter
  264. )
  265. {
  266. KIRQL OldIrql;
  267. PVDM_TIB VdmTib;
  268. HANDLE PrintHandle;
  269. NTSTATUS Status;
  270. PVDM_PRINTER_INFO PrtInfo;
  271. PIO_STATUS_BLOCK IoStatusBlock;
  272. PVOID InputBuffer;
  273. ULONG InputBufferLength;
  274. PAGED_CODE();
  275. Status = VdmpGetVdmTib (&VdmTib);
  276. if (!NT_SUCCESS(Status)) {
  277. return FALSE;
  278. }
  279. PrtInfo = &VdmTib->PrinterInfo;
  280. IoStatusBlock = (PIO_STATUS_BLOCK)&VdmTib->TempArea1;
  281. try {
  282. if (PrtInfo->prt_Handle[adapter] &&
  283. PrtInfo->prt_BytesInBuffer[adapter] &&
  284. PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[adapter]) {
  285. PrintHandle = PrtInfo->prt_Handle[adapter];
  286. InputBuffer = &PrtInfo->prt_Buffer[adapter][0];
  287. InputBufferLength = PrtInfo->prt_BytesInBuffer[adapter];
  288. }
  289. else {
  290. Status = STATUS_INVALID_PARAMETER;
  291. }
  292. } except (EXCEPTION_EXECUTE_HANDLER) {
  293. Status = GetExceptionCode();
  294. }
  295. if (NT_SUCCESS(Status)) {
  296. OldIrql = KeGetCurrentIrql();
  297. KeLowerIrql(PASSIVE_LEVEL);
  298. Status = NtDeviceIoControlFile(PrintHandle,
  299. NULL, // notification event
  300. NULL, // APC routine
  301. NULL, // APC context
  302. IoStatusBlock,
  303. IOCTL_VDM_PAR_WRITE_DATA_PORT,
  304. InputBuffer,
  305. InputBufferLength,
  306. NULL,
  307. 0);
  308. try {
  309. PrtInfo->prt_BytesInBuffer[adapter] = 0;
  310. if (!NT_SUCCESS(Status)) {
  311. #ifdef DBG
  312. DbgPrint("IOCTL_VDM_PAR_WRITE_DATA_PORT failed %lx %x\n",
  313. Status, IoStatusBlock->Status);
  314. #endif
  315. Status = IoStatusBlock->Status;
  316. }
  317. } except (EXCEPTION_EXECUTE_HANDLER) {
  318. Status = GetExceptionCode();
  319. }
  320. KeRaiseIrql (OldIrql, &OldIrql);
  321. }
  322. return Status;
  323. }
  324. NTSTATUS
  325. VdmpPrinterInitialize (
  326. IN PVOID ServiceData
  327. )
  328. /*++
  329. Routine Description:
  330. This routine probes and caches the data associated with kernel
  331. mode printer emulation.
  332. Arguments:
  333. ServiceData - Not used.
  334. Return Value:
  335. --*/
  336. {
  337. PUCHAR State, PrtStatus, Control, HostState;
  338. PVDM_TIB VdmTib;
  339. PVDM_PROCESS_OBJECTS VdmObjects;
  340. NTSTATUS Status;
  341. UNREFERENCED_PARAMETER (ServiceData);
  342. //
  343. // Note: We only support two printers in the kernel.
  344. //
  345. Status = VdmpGetVdmTib(&VdmTib);
  346. if (!NT_SUCCESS(Status)) {
  347. return FALSE;
  348. }
  349. try {
  350. State = VdmTib->PrinterInfo.prt_State;
  351. PrtStatus = VdmTib->PrinterInfo.prt_Status;
  352. Control = VdmTib->PrinterInfo.prt_Control;
  353. HostState = VdmTib->PrinterInfo.prt_HostState;
  354. //
  355. // Probe the locations for two printers
  356. //
  357. ProbeForWrite(
  358. State,
  359. 2 * sizeof(UCHAR),
  360. sizeof(UCHAR)
  361. );
  362. ProbeForWrite(
  363. PrtStatus,
  364. 2 * sizeof(UCHAR),
  365. sizeof(UCHAR)
  366. );
  367. ProbeForWrite(
  368. Control,
  369. 2 * sizeof(UCHAR),
  370. sizeof(UCHAR)
  371. );
  372. ProbeForWrite(
  373. HostState,
  374. 2 * sizeof(UCHAR),
  375. sizeof(UCHAR)
  376. );
  377. } except (EXCEPTION_EXECUTE_HANDLER) {
  378. Status = GetExceptionCode();
  379. }
  380. if (NT_SUCCESS(Status)) {
  381. VdmObjects = PsGetCurrentProcess()->VdmObjects;
  382. VdmObjects->PrinterState = State;
  383. VdmObjects->PrinterStatus = PrtStatus;
  384. VdmObjects->PrinterControl = Control;
  385. VdmObjects->PrinterHostState = HostState;
  386. }
  387. return Status;
  388. }
  389. NTSTATUS
  390. VdmpPrinterDirectIoOpen (
  391. IN PVOID ServiceData
  392. )
  393. {
  394. PAGED_CODE();
  395. UNREFERENCED_PARAMETER (ServiceData);
  396. return STATUS_SUCCESS;
  397. }
  398. NTSTATUS
  399. VdmpPrinterDirectIoClose (
  400. IN PVOID ServiceData
  401. )
  402. {
  403. LOGICAL FlushData;
  404. NTSTATUS Status;
  405. PVDM_PRINTER_INFO PrtInfo;
  406. USHORT Adapter;
  407. PVDM_TIB VdmTib;
  408. PAGED_CODE();
  409. if (NULL == ServiceData) {
  410. return STATUS_ACCESS_VIOLATION;
  411. }
  412. //
  413. // First we fetch vdm tib and do some damage control in case
  414. // this is bad user-mode memory
  415. // PrtInfo points to a stricture
  416. try {
  417. VdmTib = NtCurrentTeb()->Vdm;
  418. if (VdmTib == NULL) {
  419. return STATUS_ACCESS_VIOLATION;
  420. }
  421. ProbeForWrite(VdmTib, sizeof(VDM_TIB), sizeof(UCHAR));
  422. //
  423. // Now verify that servicedata ptr is valid.
  424. //
  425. ProbeForRead(ServiceData, sizeof(USHORT), sizeof(UCHAR));
  426. Adapter = *(PUSHORT)ServiceData;
  427. } except (ExSystemExceptionFilter()) {
  428. return GetExceptionCode();
  429. }
  430. Status = STATUS_SUCCESS;
  431. PrtInfo = &VdmTib->PrinterInfo;
  432. FlushData = FALSE;
  433. try {
  434. if (Adapter < VDM_NUMBER_OF_LPT) {
  435. if (PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[Adapter] &&
  436. PrtInfo->prt_BytesInBuffer[Adapter]) {
  437. FlushData = TRUE;
  438. }
  439. }
  440. else {
  441. Status = STATUS_INVALID_PARAMETER;
  442. }
  443. } except (EXCEPTION_EXECUTE_HANDLER) {
  444. Status = GetExceptionCode();
  445. }
  446. if (FlushData == TRUE) {
  447. Status = VdmpFlushPrinterWriteData (Adapter);
  448. }
  449. return Status;
  450. }