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.

554 lines
13 KiB

  1. /***************************************************************************
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. WRITE.C
  5. Abstract:
  6. Routines that perform write functionality
  7. Environment:
  8. kernel mode only
  9. Notes:
  10. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  11. KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  12. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  13. PURPOSE.
  14. Copyright (c) 1998 Microsoft Corporation. All Rights Reserved.
  15. Revision History:
  16. 9/25/98 : created
  17. Authors:
  18. Louis J. Giliberto, Jr.
  19. ****************************************************************************/
  20. #include <wdm.h>
  21. #include <ntddser.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <usb.h>
  25. #include <usbdrivr.h>
  26. #include <usbdlib.h>
  27. #include <usbcomm.h>
  28. #ifdef WMI_SUPPORT
  29. #include <wmilib.h>
  30. #include <wmidata.h>
  31. #include <wmistr.h>
  32. #endif
  33. #include "usbser.h"
  34. #include "utils.h"
  35. #include "debugwdm.h"
  36. #ifdef ALLOC_PRAGMA
  37. #pragma alloc_text(PAGEUSBS, UsbSer_Write)
  38. #pragma alloc_text(PAGEUSBS, UsbSerGiveWriteToUsb)
  39. #endif // ALLOC_PRAGMA
  40. NTSTATUS
  41. UsbSerFlush(IN PDEVICE_OBJECT PDevObj, PIRP PIrp)
  42. {
  43. NTSTATUS status = STATUS_SUCCESS;
  44. PDEVICE_EXTENSION pDevExt;
  45. ULONG pendingIrps;
  46. UsbSerSerialDump(USBSERTRACEWR, (">UsbSerFlush(%08X)\n", PIrp));
  47. pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
  48. //
  49. // All we will do is wait until the write pipe has nothing pending.
  50. // We do this by checking the outstanding count, and if it hits 1 or 0,
  51. // then the completion routine will set an event we are waiting for.
  52. //
  53. InterlockedIncrement(&pDevExt->PendingDataOutCount);
  54. pendingIrps = InterlockedDecrement(&pDevExt->PendingDataOutCount);
  55. if ((pendingIrps) && (pendingIrps != 1)) {
  56. //
  57. // Wait for flush
  58. //
  59. KeWaitForSingleObject(&pDevExt->PendingFlushEvent, Executive,
  60. KernelMode, FALSE, NULL);
  61. } else {
  62. if (pendingIrps == 0) {
  63. //
  64. // We need to wake others since our decrement caused the event
  65. //
  66. KeSetEvent(&pDevExt->PendingDataOutEvent, IO_NO_INCREMENT, FALSE);
  67. }
  68. }
  69. PIrp->IoStatus.Status = STATUS_SUCCESS;
  70. IoCompleteRequest(PIrp, IO_NO_INCREMENT);
  71. UsbSerSerialDump(USBSERTRACEWR, ("<UsbSerFlush %08X \n", STATUS_SUCCESS));
  72. return STATUS_SUCCESS;
  73. }
  74. NTSTATUS
  75. UsbSer_Write(IN PDEVICE_OBJECT PDevObj, PIRP PIrp)
  76. /*++
  77. Routine Description:
  78. Process the IRPs sent to this device for writing.
  79. Arguments:
  80. PDevObj - Pointer to the device object for the device written to
  81. PIrp - Pointer to the write IRP.
  82. Return Value:
  83. NTSTATUS
  84. --*/
  85. {
  86. KIRQL oldIrql;
  87. LARGE_INTEGER totalTime;
  88. SERIAL_TIMEOUTS timeouts;
  89. NTSTATUS status;
  90. PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
  91. PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
  92. USBSER_ALWAYS_LOCKED_CODE();
  93. UsbSerSerialDump(USBSERTRACEWR, (">UsbSer_Write(%08X)\n", PIrp));
  94. PIrp->IoStatus.Information = 0L;
  95. totalTime.QuadPart = (LONGLONG)0;
  96. //
  97. // Quick check for a zero length write. If it is zero length
  98. // then we are already done!
  99. //
  100. if (pIrpSp->Parameters.Write.Length == 0) {
  101. status = PIrp->IoStatus.Status = STATUS_SUCCESS;
  102. IoCompleteRequest(PIrp, IO_NO_INCREMENT);
  103. goto UsbSer_WriteExit;
  104. }
  105. //
  106. // Make sure the device is accepting request and then...
  107. // Calculate the timeout value needed for the
  108. // request. Note that the values stored in the
  109. // timeout record are in milliseconds. Note that
  110. // if the timeout values are zero then we won't start
  111. // the timer.
  112. //
  113. ACQUIRE_SPINLOCK(pDevExt, &pDevExt->ControlLock, &oldIrql);
  114. if (pDevExt->CurrentDevicePowerState != PowerDeviceD0) {
  115. RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldIrql);
  116. status = PIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
  117. IoCompleteRequest(PIrp, IO_NO_INCREMENT);
  118. goto UsbSer_WriteExit;
  119. }
  120. timeouts = pDevExt->Timeouts;
  121. RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldIrql);
  122. if (timeouts.WriteTotalTimeoutConstant
  123. || timeouts.WriteTotalTimeoutMultiplier) {
  124. //
  125. // We have some timer values to calculate.
  126. //
  127. totalTime.QuadPart
  128. = ((LONGLONG)((UInt32x32To64((pIrpSp->MajorFunction == IRP_MJ_WRITE)
  129. ? (pIrpSp->Parameters.Write.Length)
  130. : 1,
  131. timeouts.WriteTotalTimeoutMultiplier)
  132. + timeouts.WriteTotalTimeoutConstant))) * -10000;
  133. }
  134. //
  135. // The Irp may be going to the write routine shortly. Now
  136. // is a good time to init its ref counts.
  137. //
  138. USBSER_INIT_REFERENCE(PIrp);
  139. //
  140. // We need to see if this Irp should be cancelled.
  141. //
  142. ACQUIRE_CANCEL_SPINLOCK(pDevExt, &oldIrql);
  143. if (PIrp->Cancel) {
  144. RELEASE_CANCEL_SPINLOCK(pDevExt, oldIrql);
  145. status = PIrp->IoStatus.Status = STATUS_CANCELLED;
  146. } else {
  147. // IoMarkIrpPending(PIrp);
  148. // status = STATUS_PENDING;
  149. //
  150. // We give the IRP to the USB subsystem -- he will need
  151. // to know how to cancel it himself
  152. //
  153. IoSetCancelRoutine(PIrp, NULL);
  154. RELEASE_CANCEL_SPINLOCK(pDevExt, oldIrql);
  155. status = UsbSerGiveWriteToUsb(pDevExt, PIrp, totalTime);
  156. }
  157. UsbSer_WriteExit:;
  158. UsbSerSerialDump(USBSERTRACEWR, ("<UsbSer_Write %08X\n", status));
  159. return status;
  160. }
  161. NTSTATUS
  162. UsbSerWriteComplete(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp,
  163. IN PUSBSER_WRITE_PACKET PPacket)
  164. /*++
  165. Routine Description:
  166. This routine is the completion routine for all write requests.
  167. When a write completes, we go through here in order to free up
  168. the URB.
  169. Arguments:
  170. PDevObj - Pointer to device object
  171. PIrp - Irp we are completing
  172. PUrb - Urb which will be freed
  173. Return Value:
  174. NTSTATUS -- as stored in the Irp.
  175. --*/
  176. {
  177. NTSTATUS status;
  178. PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
  179. KIRQL cancelIrql;
  180. PURB pUrb = &PPacket->Urb;
  181. PDEVICE_EXTENSION pDevExt = PPacket->DeviceExtension;
  182. ULONG curCount;
  183. UsbSerSerialDump(USBSERTRACEWR, (">UsbSerWriteComplete(%08X)\n", PIrp));
  184. status = PIrp->IoStatus.Status;
  185. if (status == STATUS_SUCCESS) {
  186. // see if we are reusing an IOCTL IRP
  187. if(pIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
  188. {
  189. PIrp->IoStatus.Information = 0L;
  190. }
  191. else
  192. {
  193. PIrp->IoStatus.Information
  194. = pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength;
  195. pIrpStack->Parameters.Write.Length = (ULONG)PIrp->IoStatus.Information;
  196. }
  197. } else if (status == STATUS_CANCELLED) {
  198. //
  199. // If it comes back as cancelled, it may really have timed out. We
  200. // can tell by looking at the packet attached to it.
  201. //
  202. if (PPacket->Status) {
  203. status = PIrp->IoStatus.Status = PPacket->Status;
  204. UsbSerSerialDump(USBSERTRACEWR, ("Modified Write Status %08X\n",
  205. PIrp->IoStatus.Status));
  206. }
  207. }
  208. //
  209. // Cancel the write timer
  210. //
  211. if (PPacket->WriteTimeout.QuadPart != 0) {
  212. KeCancelTimer(&PPacket->WriteTimer);
  213. }
  214. DEBUG_MEMFREE(PPacket);
  215. //
  216. // Reset the pend if necessary
  217. //
  218. if (PIrp->PendingReturned) {
  219. IoMarkIrpPending(PIrp);
  220. }
  221. //
  222. // See if we should mark the transmit as empty
  223. //
  224. if (InterlockedDecrement(&pDevExt->PendingWriteCount) == 0) {
  225. UsbSerProcessEmptyTransmit(pDevExt);
  226. }
  227. //
  228. // Notify everyone if this is the last IRP
  229. //
  230. curCount = InterlockedDecrement(&pDevExt->PendingDataOutCount);
  231. if ((curCount == 0) || (curCount == 1)) {
  232. UsbSerSerialDump(USBSERTRACEWR, ("DataOut Pipe is flushed\n"));
  233. KeSetEvent(&pDevExt->PendingFlushEvent, IO_NO_INCREMENT, FALSE);
  234. if (curCount == 0) {
  235. UsbSerSerialDump(USBSERTRACEWR, ("DataOut Pipe is empty\n"));
  236. KeSetEvent(&pDevExt->PendingDataOutEvent, IO_NO_INCREMENT, FALSE);
  237. }
  238. }
  239. //
  240. // Finish off this IRP
  241. //
  242. ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql);
  243. UsbSerTryToCompleteCurrent(pDevExt, cancelIrql, status,
  244. &PIrp, NULL, NULL,
  245. &pDevExt->WriteRequestTotalTimer, NULL,
  246. NULL, USBSER_REF_RXBUFFER, FALSE);
  247. UsbSerSerialDump(USBSERTRACEWR, ("<UsbSerWriteComplete %08X\n", status));
  248. return status;
  249. }
  250. NTSTATUS
  251. UsbSerGiveWriteToUsb(IN PDEVICE_EXTENSION PDevExt, IN PIRP PIrp,
  252. IN LARGE_INTEGER TotalTime)
  253. /*++
  254. Routine Description:
  255. This function passes a write IRP down to USB to perform the write
  256. to the device.
  257. Arguments:
  258. PDevExt - Pointer to device extension
  259. PIrp - Write IRP
  260. TotalTime - Timeout value for total timer
  261. Return Value:
  262. NTSTATUS
  263. --*/
  264. {
  265. NTSTATUS status;
  266. PURB pTxUrb;
  267. PIO_STACK_LOCATION pIrpSp;
  268. KIRQL cancelIrql;
  269. PUSBSER_WRITE_PACKET pWrPacket;
  270. USBSER_ALWAYS_LOCKED_CODE();
  271. UsbSerSerialDump(USBSERTRACEWR, (">UsbSerGiveWriteToUsb(%08X)\n",
  272. PIrp));
  273. USBSER_SET_REFERENCE(PIrp, USBSER_REF_RXBUFFER);
  274. pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
  275. //
  276. // Allocate memory for URB / Write packet
  277. //
  278. pWrPacket = DEBUG_MEMALLOC(NonPagedPool, sizeof(USBSER_WRITE_PACKET));
  279. if (pWrPacket == NULL) {
  280. status = PIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
  281. ACQUIRE_CANCEL_SPINLOCK(PDevExt, &cancelIrql);
  282. UsbSerTryToCompleteCurrent(PDevExt, cancelIrql, status, &PIrp,
  283. NULL,
  284. &PDevExt->WriteRequestTotalTimer, NULL, NULL,
  285. NULL, USBSER_REF_RXBUFFER, TRUE);
  286. return status;
  287. }
  288. RtlZeroMemory(pWrPacket, sizeof(USBSER_WRITE_PACKET));
  289. pTxUrb = &pWrPacket->Urb;
  290. pWrPacket->DeviceExtension = PDevExt;
  291. pWrPacket->Irp = PIrp;
  292. pWrPacket->WriteTimeout = TotalTime;
  293. if (TotalTime.QuadPart != 0) {
  294. KeInitializeTimer(&pWrPacket->WriteTimer);
  295. KeInitializeDpc(&pWrPacket->TimerDPC, UsbSerWriteTimeout, pWrPacket);
  296. KeSetTimer(&pWrPacket->WriteTimer, TotalTime, &pWrPacket->TimerDPC);
  297. }
  298. //
  299. // Build USB write request
  300. //
  301. BuildReadRequest(pTxUrb, PIrp->AssociatedIrp.SystemBuffer,
  302. pIrpSp->Parameters.Write.Length, PDevExt->DataOutPipe,
  303. FALSE);
  304. #if DBG
  305. if (UsbSerSerialDebugLevel & USBSERDUMPWR) {
  306. ULONG i;
  307. DbgPrint("WR: ");
  308. for (i = 0; i < pIrpSp->Parameters.Write.Length; i++) {
  309. DbgPrint("%02x ", *(((PUCHAR)PIrp->AssociatedIrp.SystemBuffer) + i) & 0xFF);
  310. }
  311. DbgPrint("\n\n");
  312. }
  313. #endif
  314. //
  315. // Set Irp up for a submit Urb IOCTL
  316. //
  317. IoCopyCurrentIrpStackLocationToNext(PIrp);
  318. pIrpSp = IoGetNextIrpStackLocation(PIrp);
  319. pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  320. pIrpSp->Parameters.Others.Argument1 = pTxUrb;
  321. pIrpSp->Parameters.DeviceIoControl.IoControlCode
  322. = IOCTL_INTERNAL_USB_SUBMIT_URB;
  323. IoSetCompletionRoutine(PIrp, UsbSerWriteComplete, pWrPacket, TRUE, TRUE,
  324. TRUE);
  325. //
  326. // Increment the pending write count
  327. //
  328. InterlockedIncrement(&PDevExt->PendingWriteCount);
  329. InterlockedIncrement(&PDevExt->PendingDataOutCount);
  330. //
  331. // Send IRP down
  332. //
  333. status = IoCallDriver(PDevExt->StackDeviceObject, PIrp);
  334. #if 0
  335. // this is done in the completion routine, so we don't need to do it here
  336. if (!NT_SUCCESS(status)) {
  337. ULONG outCount;
  338. if (InterlockedDecrement(&PDevExt->PendingWriteCount) == 0) {
  339. UsbSerProcessEmptyTransmit(PDevExt);
  340. }
  341. outCount = InterlockedDecrement(&PDevExt->PendingDataOutCount);
  342. if ((outCount == 0) || (outCount == 1)) {
  343. KeSetEvent(&PDevExt->PendingFlushEvent, IO_NO_INCREMENT, FALSE);
  344. if (outCount == 0) {
  345. KeSetEvent(&PDevExt->PendingDataOutEvent, IO_NO_INCREMENT, FALSE);
  346. }
  347. }
  348. }
  349. #endif
  350. UsbSerSerialDump(USBSERTRACEWR, ("<UsbSerGiveWriteToUsb %08X\n", status));
  351. return status;
  352. }
  353. VOID
  354. UsbSerWriteTimeout(IN PKDPC PDpc, IN PVOID DeferredContext,
  355. IN PVOID SystemContext1, IN PVOID SystemContext2)
  356. /*++
  357. Routine Description:
  358. This function is called when the write timeout timer expires.
  359. Arguments:
  360. PDpc - Unused
  361. DeferredContext - Actually the write packet
  362. SystemContext1 - Unused
  363. SystemContext2 - Unused
  364. Return Value:
  365. VOID
  366. --*/
  367. {
  368. PUSBSER_WRITE_PACKET pPacket = (PUSBSER_WRITE_PACKET)DeferredContext;
  369. UNREFERENCED_PARAMETER(PDpc);
  370. UNREFERENCED_PARAMETER(SystemContext1);
  371. UNREFERENCED_PARAMETER(SystemContext2);
  372. UsbSerSerialDump(USBSERTRACETM, (">UsbSerWriteTimeout\n"));
  373. if (IoCancelIrp(pPacket->Irp)) {
  374. pPacket->Status = STATUS_TIMEOUT;
  375. }
  376. UsbSerSerialDump(USBSERTRACETM, ("<UsbSerWriteTimeout\n"));
  377. }
  378.