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.

538 lines
14 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. read.c
  5. Abstract: ESC/POS (serial) interface for USB Point-of-Sale devices
  6. Author:
  7. ervinp
  8. Environment:
  9. Kernel mode
  10. Revision History:
  11. --*/
  12. #include <WDM.H>
  13. #include <usbdi.h>
  14. #include <usbdlib.h>
  15. #include <usbioctl.h>
  16. #include "escpos.h"
  17. #include "debug.h"
  18. NTSTATUS ReadComPort(POSPDOEXT *pdoExt, PIRP irp)
  19. {
  20. NTSTATUS status;
  21. PIO_STACK_LOCATION currentIrpSp;
  22. /*
  23. * In order to support ODD ENDPOINTs, we check
  24. * whether this COM port has a read endpoint or not.
  25. */
  26. if(!pdoExt->inputEndpointInfo.pipeHandle) {
  27. DBGVERBOSE(("This PORT does not have an IN endpoint - Read request Rejected."));
  28. return STATUS_NOT_SUPPORTED;
  29. }
  30. DBGVERBOSE(("ReadComPort"));
  31. currentIrpSp = IoGetCurrentIrpStackLocation(irp);
  32. ASSERT(currentIrpSp->Parameters.Read.Length);
  33. ASSERT(!irp->MdlAddress);
  34. /*
  35. * Because this device object uses buffering method METHOD_NEITHER,
  36. * the read buffer is irp->UserBuffer, which is potentially an application
  37. * read buffer. If the read completes on a different thread than this calling
  38. * thread, then the completion routine will not have the read buffer addressed
  39. * correctly.
  40. * Therefore, we have to map the UserBuffer using an MDL.
  41. */
  42. irp->MdlAddress = MmCreateMdl(NULL, irp->UserBuffer, currentIrpSp->Parameters.Read.Length);
  43. if (irp->MdlAddress){
  44. status = STATUS_SUCCESS;
  45. __try {
  46. /*
  47. * We're writing the read data to the buffer, so probe for WriteAccess.
  48. */
  49. MmProbeAndLockPages(irp->MdlAddress, UserMode, IoWriteAccess);
  50. }
  51. __except(EXCEPTION_EXECUTE_HANDLER) {
  52. status = GetExceptionCode();
  53. DBGERR(("MmProbeAndLockPages triggered exception status %xh.", status));
  54. }
  55. if (NT_SUCCESS(status)){
  56. status = EnqueueReadIrp(pdoExt, irp, FALSE, FALSE);
  57. if (status == STATUS_PENDING){
  58. BOOLEAN doReadNow;
  59. KIRQL oldIrql;
  60. /*
  61. * Atomically test-and-set the endpointIsBusy flag.
  62. * If the endpoint was not busy, issue a read after dropping the lock.
  63. */
  64. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  65. if (pdoExt->inputEndpointInfo.endpointIsBusy){
  66. doReadNow = FALSE;
  67. }
  68. else {
  69. pdoExt->inputEndpointInfo.endpointIsBusy = TRUE;
  70. doReadNow = TRUE;
  71. }
  72. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  73. if (doReadNow){
  74. IssueReadForClient(pdoExt);
  75. }
  76. }
  77. }
  78. }
  79. else {
  80. DBGERR(("MmCreateMdl failed"));
  81. status = STATUS_DATA_ERROR;
  82. }
  83. return status;
  84. }
  85. VOID SatisfyPendingReads(POSPDOEXT *pdoExt)
  86. {
  87. LIST_ENTRY irpsToCompleteList, readPacketsToFree;
  88. PLIST_ENTRY listEntry;
  89. PIRP irp;
  90. READPACKET *readPacket;
  91. KIRQL oldIrql;
  92. DBGVERBOSE(("SatisfyPendingReads"));
  93. /*
  94. * Accumulate the complete-ready IRPs on a private queue before completing.
  95. * This is so we don't loop forever if they get re-queued on the same thread.
  96. */
  97. InitializeListHead(&irpsToCompleteList);
  98. InitializeListHead(&readPacketsToFree);
  99. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  100. while (irp = DequeueReadIrp(pdoExt, TRUE)){
  101. PIO_STACK_LOCATION currentIrpSp = IoGetCurrentIrpStackLocation(irp);
  102. BOOLEAN canSatisfyOneIrp;
  103. /*
  104. * Do we have enough bytes to satisfy this IRP ?
  105. */
  106. #if PARTIAL_READ_BUFFERS_OK
  107. canSatisfyOneIrp = (pdoExt->totalQueuedReadDataLength > 0);
  108. #else
  109. canSatisfyOneIrp = (pdoExt->totalQueuedReadDataLength >= currentIrpSp->Parameters.Read.Length);
  110. #endif
  111. if (canSatisfyOneIrp){
  112. ULONG userBufferOffset = 0;
  113. BOOLEAN satisfiedThisIrp = FALSE;
  114. PUCHAR mappedUserBuffer;
  115. ASSERT(irp->MdlAddress);
  116. ASSERT(!IsListEmpty(&pdoExt->completedReadPacketsList));
  117. /*
  118. * We may be completing this IRP on a different thread than the calling thread.
  119. * So we cannot dereference UserBuffer directly.
  120. * Use the MDL we created at call time instead.
  121. */
  122. mappedUserBuffer = PosMmGetSystemAddressForMdlSafe(irp->MdlAddress);
  123. if (mappedUserBuffer){
  124. while (!IsListEmpty(&pdoExt->completedReadPacketsList) &&
  125. (userBufferOffset < currentIrpSp->Parameters.Read.Length)){
  126. ULONG bytesToCopy;
  127. BOOLEAN thisIrpFull;
  128. listEntry = RemoveHeadList(&pdoExt->completedReadPacketsList);
  129. ASSERT(listEntry);
  130. readPacket = CONTAINING_RECORD(listEntry, READPACKET, listEntry);
  131. ASSERT(readPacket->signature == READPACKET_SIG);
  132. bytesToCopy = MIN(currentIrpSp->Parameters.Read.Length-userBufferOffset,
  133. readPacket->length-readPacket->offset);
  134. ASSERT(bytesToCopy <= pdoExt->totalQueuedReadDataLength);
  135. DBGVERBOSE(("SatisfyPendingReads: transferring %xh bytes to read irp", bytesToCopy));
  136. /*
  137. * Since we may be completing this IRP on a different thread than
  138. * the one we got it on, we cannot write into the UserBuffer.
  139. * We have to write into the MDL we allocated when we queued this IRP.
  140. */
  141. RtlCopyMemory(mappedUserBuffer+userBufferOffset,
  142. readPacket->data+readPacket->offset,
  143. bytesToCopy);
  144. userBufferOffset += bytesToCopy;
  145. readPacket->offset += bytesToCopy;
  146. pdoExt->totalQueuedReadDataLength -= bytesToCopy;
  147. ASSERT(userBufferOffset <= currentIrpSp->Parameters.Read.Length);
  148. ASSERT(readPacket->offset <= readPacket->length);
  149. #if PARTIAL_READ_BUFFERS_OK
  150. thisIrpFull = (userBufferOffset > 0);
  151. #else
  152. thisIrpFull = (userBufferOffset >= currentIrpSp->Parameters.Read.Length);
  153. #endif
  154. if (thisIrpFull){
  155. /*
  156. * We've satisfied this IRP.
  157. * Break out of the inner loop so we get a new IRP.
  158. * Put the readPacket back in its queue in case there
  159. * are more bytes left in it.
  160. */
  161. irp->IoStatus.Information = userBufferOffset;
  162. irp->IoStatus.Status = STATUS_SUCCESS;
  163. InsertTailList(&irpsToCompleteList, &irp->Tail.Overlay.ListEntry);
  164. InsertHeadList(&pdoExt->completedReadPacketsList, &readPacket->listEntry);
  165. satisfiedThisIrp = TRUE;
  166. break;
  167. }
  168. else if (readPacket->offset == readPacket->length){
  169. /*
  170. * We've depleted this readPacket buffer.
  171. */
  172. InsertTailList(&readPacketsToFree, &readPacket->listEntry);
  173. ASSERT(!IsListEmpty(&pdoExt->completedReadPacketsList));
  174. }
  175. else {
  176. DBGERR(("SatisfyPendingReads - data error"));
  177. break;
  178. }
  179. }
  180. ASSERT(satisfiedThisIrp);
  181. }
  182. else {
  183. DBGERR(("PosMmGetSystemAddressForMdlSafe failed"));
  184. irp->IoStatus.Information = 0;
  185. irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
  186. InsertTailList(&irpsToCompleteList, &irp->Tail.Overlay.ListEntry);
  187. }
  188. }
  189. else {
  190. /*
  191. * We can't satisfy this IRP, so put it back at the head of the list.
  192. */
  193. NTSTATUS status;
  194. DBGVERBOSE(("SatisfyPendingReads: not enough bytes to satisfy irp (%xh/%xh)", pdoExt->totalQueuedReadDataLength, currentIrpSp->Parameters.Read.Length));
  195. status = EnqueueReadIrp(pdoExt, irp, TRUE, TRUE);
  196. if (status == STATUS_CANCELLED){
  197. /*
  198. * The IRP was cancelled and the cancel routine was not called,
  199. * so complete the IRP here.
  200. */
  201. irp->IoStatus.Information = 0;
  202. irp->IoStatus.Status = status;
  203. InsertTailList(&irpsToCompleteList, &irp->Tail.Overlay.ListEntry);
  204. }
  205. break;
  206. }
  207. }
  208. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  209. while (!IsListEmpty(&irpsToCompleteList)){
  210. listEntry = RemoveHeadList(&irpsToCompleteList);
  211. ASSERT(listEntry);
  212. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  213. DBGVERBOSE(("SatisfyPendingReads: completing irp with %xh bytes.", irp->IoStatus.Information));
  214. /*
  215. * Free the MDL we created for the UserBuffer
  216. */
  217. ASSERT(irp->MdlAddress);
  218. MmUnlockPages(irp->MdlAddress);
  219. FREEPOOL(irp->MdlAddress);
  220. irp->MdlAddress = NULL;
  221. IoCompleteRequest(irp, IO_NO_INCREMENT);
  222. }
  223. while (!IsListEmpty(&readPacketsToFree)){
  224. listEntry = RemoveHeadList(&readPacketsToFree);
  225. ASSERT(listEntry);
  226. readPacket = CONTAINING_RECORD(listEntry, READPACKET, listEntry);
  227. FreeReadPacket(readPacket);
  228. }
  229. }
  230. /*
  231. * IssueReadForClient
  232. *
  233. * Must be called with exclusive access on the input endpoint held.
  234. */
  235. VOID IssueReadForClient(POSPDOEXT *pdoExt)
  236. {
  237. PUCHAR readBuf;
  238. ULONG readLen;
  239. DBGVERBOSE(("IssueReadForClient"));
  240. /*
  241. * We always read the full pipe size.
  242. *
  243. * BUGBUG - pipe info needs to be moved to pdoExt.
  244. */
  245. readLen = pdoExt->inputEndpointInfo.pipeLen;
  246. readBuf = ALLOCPOOL(NonPagedPool, readLen);
  247. if (readBuf){
  248. READPACKET *readPacket;
  249. RtlZeroMemory(readBuf, readLen);
  250. readPacket = AllocReadPacket(readBuf, readLen, pdoExt);
  251. if (readPacket){
  252. ReadPipe(pdoExt->parentFdoExt, pdoExt->inputEndpointInfo.pipeHandle, readPacket, FALSE);
  253. }
  254. else {
  255. FREEPOOL(readBuf);
  256. FlushBuffers(pdoExt);
  257. }
  258. }
  259. else {
  260. ASSERT(readBuf);
  261. FlushBuffers(pdoExt);
  262. }
  263. }
  264. VOID WorkItemCallback_Read(PVOID context)
  265. {
  266. POSPDOEXT *pdoExt = context;
  267. KIRQL oldIrql;
  268. BOOLEAN issueReadNow = FALSE;
  269. DBGVERBOSE(("WorkItemCallback_Read"));
  270. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  271. if (IsListEmpty(&pdoExt->pendingReadIrpsList)){
  272. DBGERR(("WorkItemCallback_Read: list is empty ?!"));
  273. }
  274. else {
  275. if (pdoExt->inputEndpointInfo.endpointIsBusy){
  276. DBGWARN(("WorkItemCallback_Read: endpoint is busy"));
  277. }
  278. else {
  279. pdoExt->inputEndpointInfo.endpointIsBusy = TRUE;
  280. issueReadNow = TRUE;
  281. }
  282. }
  283. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  284. if (issueReadNow){
  285. IssueReadForClient(pdoExt);
  286. }
  287. }
  288. NTSTATUS EnqueueReadIrp(POSPDOEXT *pdoExt, PIRP irp, BOOLEAN enqueueAtFront, BOOLEAN lockHeld)
  289. {
  290. PDRIVER_CANCEL oldCancelRoutine;
  291. NTSTATUS status = STATUS_PENDING;
  292. KIRQL oldIrql;
  293. if (!lockHeld) KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  294. /*
  295. * Enqueue the IRP
  296. */
  297. if (enqueueAtFront){
  298. InsertHeadList(&pdoExt->pendingReadIrpsList, &irp->Tail.Overlay.ListEntry);
  299. }
  300. else {
  301. InsertTailList(&pdoExt->pendingReadIrpsList, &irp->Tail.Overlay.ListEntry);
  302. }
  303. /*
  304. * Apply the IoMarkIrpPending macro to indicate that the
  305. * irp may complete on a different thread.
  306. * The kernel will see this flag set when we complete the IRP and get the IRP
  307. * back on the right thread if there was a synchronous client.
  308. */
  309. IoMarkIrpPending(irp);
  310. /*
  311. * Must set the cancel routine before checking the cancel flag.
  312. */
  313. oldCancelRoutine = IoSetCancelRoutine(irp, ReadCancelRoutine);
  314. ASSERT(!oldCancelRoutine);
  315. if (irp->Cancel){
  316. /*
  317. * This IRP was cancelled.
  318. * We need to coordinate with the cancel routine to complete this irp.
  319. */
  320. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  321. if (oldCancelRoutine){
  322. /*
  323. * Cancel routine was not called, so dequeue the IRP and return
  324. * error so the dispatch routine completes the IRP.
  325. */
  326. ASSERT(oldCancelRoutine == ReadCancelRoutine);
  327. RemoveEntryList(&irp->Tail.Overlay.ListEntry);
  328. status = STATUS_CANCELLED;
  329. }
  330. else {
  331. /*
  332. * Cancel routine was called and it will complete the IRP
  333. * as soon as we drop the spinlock. So don't touch this IRP.
  334. * Return PENDING so dispatch routine won't complete the IRP.
  335. */
  336. }
  337. }
  338. if (!lockHeld) KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  339. return status;
  340. }
  341. PIRP DequeueReadIrp(POSPDOEXT *pdoExt, BOOLEAN lockHeld)
  342. {
  343. PIRP nextIrp = NULL;
  344. KIRQL oldIrql;
  345. if (!lockHeld) KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  346. while (!nextIrp && !IsListEmpty(&pdoExt->pendingReadIrpsList)){
  347. PDRIVER_CANCEL oldCancelRoutine;
  348. PLIST_ENTRY listEntry = RemoveHeadList(&pdoExt->pendingReadIrpsList);
  349. nextIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  350. oldCancelRoutine = IoSetCancelRoutine(nextIrp, NULL);
  351. /*
  352. * IoCancelIrp() could have just been called on this IRP.
  353. * What we're interested in is not whether IoCancelIrp() was called (nextIrp->Cancel flag set),
  354. * but whether IoCancelIrp() called (or is about to call) our cancel routine.
  355. * To check that, check the result of the test-and-set macro IoSetCancelRoutine.
  356. */
  357. if (oldCancelRoutine){
  358. /*
  359. * Cancel routine not called for this IRP. Return this IRP.
  360. */
  361. ASSERT(oldCancelRoutine == ReadCancelRoutine);
  362. }
  363. else {
  364. /*
  365. * This IRP was just cancelled and the cancel routine was (or will be) called.
  366. * The cancel routine will complete this IRP as soon as we drop the spinlock.
  367. * So don't do anything with the IRP.
  368. * Also, the cancel routine will try to dequeue the IRP,
  369. * so make the IRP's listEntry point to itself.
  370. */
  371. ASSERT(nextIrp->Cancel);
  372. InitializeListHead(&nextIrp->Tail.Overlay.ListEntry);
  373. nextIrp = NULL;
  374. }
  375. }
  376. if (!lockHeld) KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  377. return nextIrp;
  378. }
  379. VOID ReadCancelRoutine(PDEVICE_OBJECT devObj, PIRP irp)
  380. {
  381. DEVEXT *devExt;
  382. POSPDOEXT *pdoExt;
  383. KIRQL oldIrql;
  384. DBGWARN(("ReadCancelRoutine: devObj=%ph, irp=%ph.", devObj, irp));
  385. devExt = devObj->DeviceExtension;
  386. ASSERT(devExt->signature == DEVICE_EXTENSION_SIGNATURE);
  387. ASSERT(devExt->isPdo);
  388. pdoExt = &devExt->pdoExt;
  389. IoReleaseCancelSpinLock(irp->CancelIrql);
  390. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  391. RemoveEntryList(&irp->Tail.Overlay.ListEntry);
  392. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  393. irp->IoStatus.Status = STATUS_CANCELLED;
  394. IoCompleteRequest(irp, IO_NO_INCREMENT);
  395. }
  396. READPACKET *AllocReadPacket(PVOID data, ULONG dataLen, PVOID context)
  397. {
  398. READPACKET *readPacket;
  399. readPacket = ALLOCPOOL(NonPagedPool, sizeof(READPACKET));
  400. if (readPacket){
  401. readPacket->signature = READPACKET_SIG;
  402. readPacket->data = data;
  403. readPacket->length = dataLen;
  404. readPacket->offset = 0;
  405. readPacket->context = context;
  406. readPacket->urb = BAD_POINTER;
  407. readPacket->listEntry.Flink = readPacket->listEntry.Blink = BAD_POINTER;
  408. }
  409. else {
  410. ASSERT(readPacket);
  411. }
  412. return readPacket;
  413. }
  414. VOID FreeReadPacket(READPACKET *readPacket)
  415. {
  416. DBGVERBOSE(("Freeing readPacket..."));
  417. ASSERT(readPacket->signature == READPACKET_SIG);
  418. FREEPOOL(readPacket->data);
  419. FREEPOOL(readPacket);
  420. }