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.

303 lines
8.3 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. write.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 WriteComPort(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 write endpoint or not.
  25. */
  26. if(!pdoExt->outputEndpointInfo.pipeHandle) {
  27. DBGVERBOSE(("This PORT does not have an OUT endpoint - Write request Rejected."));
  28. return STATUS_NOT_SUPPORTED;
  29. }
  30. currentIrpSp = IoGetCurrentIrpStackLocation(irp);
  31. /*
  32. * Because this pdo's buffering method is METHOD_NEITHER,
  33. * the buffer pointer is irp->UserBuffer, which may be an application address.
  34. * Since we may not be able to send this buffer synchronously on this thread,
  35. * we have to allocate an MDL for it.
  36. */
  37. ASSERT(!irp->MdlAddress);
  38. ASSERT(currentIrpSp->Parameters.Write.Length);
  39. irp->MdlAddress = MmCreateMdl(NULL, irp->UserBuffer, currentIrpSp->Parameters.Write.Length);
  40. if (irp->MdlAddress){
  41. status = STATUS_SUCCESS;
  42. __try {
  43. /*
  44. * We're reading the write data from the buffer, so probe for ReadAccess.
  45. */
  46. MmProbeAndLockPages(irp->MdlAddress, UserMode, IoReadAccess);
  47. }
  48. __except(EXCEPTION_EXECUTE_HANDLER) {
  49. status = GetExceptionCode();
  50. DBGERR(("MmProbeAndLockPages triggered exception status %xh.", status));
  51. }
  52. if (NT_SUCCESS(status)){
  53. status = TryWrite(pdoExt, irp);
  54. }
  55. }
  56. else {
  57. DBGERR(("MmCreateMdl failed"));
  58. status = STATUS_DATA_ERROR;
  59. }
  60. return status;
  61. }
  62. NTSTATUS TryWrite(POSPDOEXT *pdoExt, PIRP irp)
  63. {
  64. NTSTATUS status = STATUS_PENDING;
  65. BOOLEAN isBusy;
  66. KIRQL oldIrql;
  67. BOOLEAN irpWasCancelled = FALSE;
  68. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  69. if (pdoExt->outputEndpointInfo.endpointIsBusy){
  70. /*
  71. * Another thread is writing to this endpoint now.
  72. * Queue the IRP.
  73. */
  74. PDRIVER_CANCEL oldCancelRoutine;
  75. DBGWARN(("WriteComPort: endpoint is busy so queuing irp"));
  76. oldCancelRoutine = IoSetCancelRoutine(irp, WriteCancelRoutine);
  77. ASSERT(!oldCancelRoutine);
  78. if (irp->Cancel){
  79. DBGWARN(("WriteComPort: irp %ph was cancelled.", irp));
  80. irpWasCancelled = TRUE;
  81. oldCancelRoutine = IoSetCancelRoutine(irp, NULL);
  82. if (oldCancelRoutine){
  83. /*
  84. * Cancel routine was not called, so complete the IRP here.
  85. */
  86. ASSERT(oldCancelRoutine == ReadCancelRoutine);
  87. status = STATUS_CANCELLED;
  88. }
  89. else {
  90. /*
  91. * Cancel routine was called and it will complete the IRP
  92. * as soon as we drop the spinlock. So don't touch this IRP.
  93. * Return PENDING so dispatch routine won't complete the IRP.
  94. */
  95. status = STATUS_PENDING;
  96. }
  97. }
  98. else {
  99. InsertTailList(&pdoExt->pendingWriteIrpsList, &irp->Tail.Overlay.ListEntry);
  100. }
  101. isBusy = TRUE;
  102. }
  103. else {
  104. pdoExt->outputEndpointInfo.endpointIsBusy = TRUE;
  105. isBusy = FALSE;
  106. }
  107. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  108. if (!isBusy){
  109. PUCHAR mappedUserBuffer;
  110. ULONG dataLen;
  111. ULONG dataWritten = 0;
  112. PIO_STACK_LOCATION currentIrpSp;
  113. BOOLEAN callWriteWorkItem = FALSE;
  114. currentIrpSp = IoGetCurrentIrpStackLocation(irp);
  115. status = STATUS_SUCCESS; // in case dataLen = 0
  116. /*
  117. * This function is called from the dispatch routine and also
  118. * from a workItem callback. So we may not be on the calling thread.
  119. * Therefore, we cannot use irp->UserBuffer because it may not be
  120. * mapped in this context. Use the MDL we created at call time instead.
  121. */
  122. mappedUserBuffer = PosMmGetSystemAddressForMdlSafe(irp->MdlAddress);
  123. if(mappedUserBuffer) {
  124. dataLen = currentIrpSp->Parameters.Write.Length;
  125. while (dataLen){
  126. ULONG len = MIN(dataLen, pdoExt->outputEndpointInfo.pipeLen);
  127. DBGVERBOSE(("Writing %xh bytes to pipe.", len));
  128. status = WritePipe( pdoExt->parentFdoExt,
  129. pdoExt->outputEndpointInfo.pipeHandle,
  130. mappedUserBuffer,
  131. len);
  132. if (NT_SUCCESS(status)){
  133. dataLen -= len;
  134. dataWritten += len;
  135. mappedUserBuffer += len;
  136. }
  137. else {
  138. DBGERR(("Write failed with status %xh.", status));
  139. break;
  140. }
  141. }
  142. /*
  143. * Free the MDL we created for the UserBuffer
  144. */
  145. ASSERT(irp->MdlAddress);
  146. MmUnlockPages(irp->MdlAddress);
  147. FREEPOOL(irp->MdlAddress);
  148. irp->MdlAddress = NULL;
  149. irp->IoStatus.Information = dataWritten;
  150. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  151. pdoExt->outputEndpointInfo.endpointIsBusy = FALSE;
  152. if (!IsListEmpty(&pdoExt->pendingWriteIrpsList)){
  153. callWriteWorkItem = TRUE;;
  154. }
  155. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  156. /*
  157. * If there are some writes waiting, schedule a workItem to process them.
  158. */
  159. if (callWriteWorkItem){
  160. ExQueueWorkItem(&pdoExt->writeWorkItem, DelayedWorkQueue);
  161. }
  162. }
  163. else {
  164. /*
  165. * Return STATUS_UNSUCCESSFUL and free the MDL we created for the UserBuffer.
  166. */
  167. DBGERR(("PosMmGetSystemAddressForMdlSafe failed"));
  168. irp->IoStatus.Information = 0;
  169. status = STATUS_UNSUCCESSFUL;
  170. ASSERT(irp->MdlAddress);
  171. MmUnlockPages(irp->MdlAddress);
  172. FREEPOOL(irp->MdlAddress);
  173. irp->MdlAddress = NULL;
  174. }
  175. }
  176. return status;
  177. }
  178. VOID WorkItemCallback_Write(PVOID context)
  179. {
  180. POSPDOEXT *pdoExt = (POSPDOEXT *)context;
  181. KIRQL oldIrql;
  182. PIRP irp = NULL;
  183. DBGVERBOSE(("WorkItemCallback_Write: pdoExt=%ph ", pdoExt));
  184. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  185. if (IsListEmpty(&pdoExt->pendingWriteIrpsList)){
  186. DBGERR(("WorkItemCallback_Write: list is empty ?!"));
  187. }
  188. else {
  189. while (!irp && !IsListEmpty(&pdoExt->pendingWriteIrpsList)){
  190. PDRIVER_CANCEL cancelRoutine;
  191. PLIST_ENTRY listEntry = RemoveHeadList(&pdoExt->pendingWriteIrpsList);
  192. ASSERT(listEntry);
  193. irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
  194. cancelRoutine = IoSetCancelRoutine(irp, NULL);
  195. if (cancelRoutine){
  196. ASSERT(cancelRoutine == WriteCancelRoutine);
  197. }
  198. else {
  199. /*
  200. * This IRP was cancelled and the cancel routine was called.
  201. * The cancel routine will complete this IRP as soon as we drop
  202. * the spinlock, so don't touch the IRP.
  203. */
  204. ASSERT(irp->Cancel);
  205. DBGWARN(("WorkItemCallback_Write: irp was cancelled"));
  206. irp = NULL;
  207. }
  208. }
  209. }
  210. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  211. if (irp){
  212. NTSTATUS status = TryWrite(pdoExt, irp);
  213. if (status != STATUS_PENDING){
  214. /*
  215. * Set the SL_PENDING in the IRP
  216. * to indicate that the IRP is completing on a different thread.
  217. */
  218. IoMarkIrpPending(irp);
  219. irp->IoStatus.Status = status;
  220. IoCompleteRequest(irp, IO_NO_INCREMENT);
  221. }
  222. }
  223. }
  224. VOID WriteCancelRoutine(PDEVICE_OBJECT devObj, PIRP irp)
  225. {
  226. DEVEXT *devExt;
  227. POSPDOEXT *pdoExt;
  228. KIRQL oldIrql;
  229. DBGWARN(("WriteCancelRoutine: devObj=%ph, irp=%ph.", devObj, irp));
  230. devExt = devObj->DeviceExtension;
  231. ASSERT(devExt->signature == DEVICE_EXTENSION_SIGNATURE);
  232. ASSERT(devExt->isPdo);
  233. pdoExt = &devExt->pdoExt;
  234. KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
  235. RemoveEntryList(&irp->Tail.Overlay.ListEntry);
  236. KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
  237. IoReleaseCancelSpinLock(irp->CancelIrql);
  238. irp->IoStatus.Status = STATUS_CANCELLED;
  239. IoCompleteRequest(irp, IO_NO_INCREMENT);
  240. }