1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: Serial port driver 4 * FILE: drivers/dd/serial/create.c 5 * PURPOSE: Serial IRP_MJ_READ/IRP_MJ_WRITE operations 6 * 7 * PROGRAMMERS: Herv� Poussineau (hpoussin@reactos.org) 8 */ 9 10 #include "serial.h" 11 12 #include <debug.h> 13 14 static IO_WORKITEM_ROUTINE SerialReadWorkItem; 15 16 static PVOID 17 SerialGetUserBuffer(IN PIRP Irp) 18 { 19 ASSERT(Irp); 20 21 if (Irp->MdlAddress) 22 return Irp->MdlAddress; 23 else 24 return Irp->AssociatedIrp.SystemBuffer; 25 } 26 27 static VOID 28 ReadBytes( 29 IN PDEVICE_OBJECT DeviceObject, 30 IN PIRP Irp, 31 PWORKITEM_DATA WorkItemData) 32 { 33 PSERIAL_DEVICE_EXTENSION DeviceExtension; 34 ULONG Length; 35 PUCHAR Buffer; 36 UCHAR ReceivedByte; 37 KTIMER TotalTimeoutTimer; 38 KIRQL Irql; 39 ULONG ObjectCount; 40 PVOID ObjectsArray[2]; 41 ULONG_PTR Information = 0; 42 NTSTATUS Status; 43 44 ASSERT(DeviceObject); 45 ASSERT(WorkItemData); 46 47 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 48 Length = IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length; 49 Buffer = SerialGetUserBuffer(Irp); 50 51 INFO_(SERIAL, "UseIntervalTimeout = %s, IntervalTimeout = %lu\n", 52 WorkItemData->UseIntervalTimeout ? "YES" : "NO", 53 WorkItemData->UseIntervalTimeout ? WorkItemData->IntervalTimeout.QuadPart : 0); 54 INFO_(SERIAL, "UseTotalTimeout = %s\n", 55 WorkItemData->UseTotalTimeout ? "YES" : "NO"); 56 57 ObjectCount = 1; 58 ObjectsArray[0] = &DeviceExtension->InputBufferNotEmpty; 59 if (WorkItemData->UseTotalTimeout) 60 { 61 KeInitializeTimer(&TotalTimeoutTimer); 62 KeSetTimer(&TotalTimeoutTimer, WorkItemData->TotalTimeoutTime, NULL); 63 ObjectsArray[ObjectCount] = &TotalTimeoutTimer; 64 ObjectCount++; 65 } 66 67 /* while buffer is not fully filled */ 68 while (TRUE) 69 { 70 /* read already received bytes from buffer */ 71 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql); 72 while (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer) 73 && Length > 0) 74 { 75 PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte); 76 INFO_(SERIAL, "Reading byte from buffer: 0x%02x\n", ReceivedByte); 77 78 Buffer[Information++] = ReceivedByte; 79 Length--; 80 } 81 KeClearEvent(&DeviceExtension->InputBufferNotEmpty); 82 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql); 83 84 if (Length == 0) 85 { 86 INFO_(SERIAL, "All bytes read\n"); 87 break; 88 } 89 90 if (WorkItemData->DontWait 91 && !(WorkItemData->ReadAtLeastOneByte && Information == 0)) 92 { 93 INFO_(SERIAL, "Buffer empty. Don't wait more bytes\n"); 94 break; 95 } 96 97 Status = KeWaitForMultipleObjects( 98 ObjectCount, 99 ObjectsArray, 100 WaitAny, 101 Executive, 102 KernelMode, 103 FALSE, 104 (WorkItemData->UseIntervalTimeout && Information > 0) ? &WorkItemData->IntervalTimeout : NULL, 105 NULL); 106 107 if (Status == STATUS_TIMEOUT /* interval timeout */ 108 || Status == STATUS_WAIT_1) /* total timeout */ 109 { 110 TRACE_(SERIAL, "Timeout when reading bytes. Status = 0x%08lx\n", Status); 111 break; 112 } 113 } 114 115 /* stop total timeout timer */ 116 if (WorkItemData->UseTotalTimeout) 117 KeCancelTimer(&TotalTimeoutTimer); 118 119 Irp->IoStatus.Information = Information; 120 if (Information == 0) 121 Irp->IoStatus.Status = STATUS_TIMEOUT; 122 else 123 Irp->IoStatus.Status = STATUS_SUCCESS; 124 } 125 126 static VOID NTAPI 127 SerialReadWorkItem( 128 IN PDEVICE_OBJECT DeviceObject, 129 IN PVOID pWorkItemData /* real type PWORKITEM_DATA */) 130 { 131 PWORKITEM_DATA WorkItemData; 132 PIRP Irp; 133 134 TRACE_(SERIAL, "SerialReadWorkItem() called\n"); 135 136 WorkItemData = (PWORKITEM_DATA)pWorkItemData; 137 Irp = WorkItemData->Irp; 138 139 ReadBytes(DeviceObject, Irp, WorkItemData); 140 141 IoCompleteRequest(Irp, IO_NO_INCREMENT); 142 143 IoFreeWorkItem(WorkItemData->IoWorkItem); 144 ExFreePoolWithTag(pWorkItemData, SERIAL_TAG); 145 } 146 147 NTSTATUS NTAPI 148 SerialRead( 149 IN PDEVICE_OBJECT DeviceObject, 150 IN PIRP Irp) 151 { 152 PIO_STACK_LOCATION Stack; 153 PSERIAL_DEVICE_EXTENSION DeviceExtension; 154 ULONG Length; 155 PUCHAR Buffer; 156 PWORKITEM_DATA WorkItemData; 157 PIO_WORKITEM WorkItem; 158 NTSTATUS Status; 159 160 TRACE_(SERIAL, "IRP_MJ_READ\n"); 161 162 Stack = IoGetCurrentIrpStackLocation(Irp); 163 Length = Stack->Parameters.Read.Length; 164 Buffer = SerialGetUserBuffer(Irp); 165 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 166 167 if (Stack->Parameters.Read.ByteOffset.QuadPart != 0 || Buffer == NULL) 168 { 169 Status = STATUS_INVALID_PARAMETER; 170 goto ByeBye; 171 } 172 173 if (Length == 0) 174 { 175 Status = STATUS_SUCCESS; 176 goto ByeBye; 177 } 178 179 /* Allocate memory for parameters */ 180 WorkItemData = ExAllocatePoolWithTag(PagedPool, sizeof(WORKITEM_DATA), SERIAL_TAG); 181 if (!WorkItemData) 182 { 183 Status = STATUS_INSUFFICIENT_RESOURCES; 184 goto ByeBye; 185 } 186 RtlZeroMemory(WorkItemData, sizeof(WORKITEM_DATA)); 187 WorkItemData->Irp = Irp; 188 189 /* Calculate time outs */ 190 if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE && 191 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == INFINITE && 192 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant > 0 && 193 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant < INFINITE) 194 { 195 /* read at least one byte, and at most bytes already received */ 196 WorkItemData->DontWait = TRUE; 197 WorkItemData->ReadAtLeastOneByte = TRUE; 198 } 199 else if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE && 200 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0 && 201 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) 202 { 203 /* read only bytes that are already in buffer */ 204 WorkItemData->DontWait = TRUE; 205 } 206 else 207 { 208 /* use timeouts */ 209 if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout != 0) 210 { 211 WorkItemData->UseIntervalTimeout = TRUE; 212 WorkItemData->IntervalTimeout.QuadPart = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout; 213 } 214 if (DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier != 0 || 215 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant != 0) 216 { 217 ULONG TotalTimeout; 218 LARGE_INTEGER SystemTime; 219 220 WorkItemData->UseTotalTimeout = TRUE; 221 TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant + 222 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier * Length; 223 KeQuerySystemTime(&SystemTime); 224 WorkItemData->TotalTimeoutTime.QuadPart = SystemTime.QuadPart + 225 TotalTimeout * 10000; 226 } 227 } 228 229 /* Pend IRP */ 230 WorkItem = IoAllocateWorkItem(DeviceObject); 231 if (WorkItem) 232 { 233 WorkItemData->IoWorkItem = WorkItem; 234 IoMarkIrpPending(Irp); 235 IoQueueWorkItem(WorkItem, SerialReadWorkItem, DelayedWorkQueue, WorkItemData); 236 return STATUS_PENDING; 237 } 238 239 /* Insufficient resources, we can't pend the Irp */ 240 INFO_(SERIAL, "Insufficient resources\n"); 241 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); 242 if (!NT_SUCCESS(Status)) 243 { 244 ExFreePoolWithTag(WorkItemData, SERIAL_TAG); 245 goto ByeBye; 246 } 247 ReadBytes(DeviceObject, Irp, WorkItemData); 248 Status = Irp->IoStatus.Status; 249 250 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); 251 252 ByeBye: 253 Irp->IoStatus.Status = Status; 254 IoCompleteRequest(Irp, IO_NO_INCREMENT); 255 return Status; 256 } 257 258 NTSTATUS NTAPI 259 SerialWrite( 260 IN PDEVICE_OBJECT DeviceObject, 261 IN PIRP Irp) 262 { 263 PIO_STACK_LOCATION Stack; 264 PSERIAL_DEVICE_EXTENSION DeviceExtension; 265 ULONG Length; 266 ULONG_PTR Information = 0; 267 PUCHAR Buffer; 268 KIRQL Irql; 269 NTSTATUS Status = STATUS_SUCCESS; 270 271 TRACE_(SERIAL, "IRP_MJ_WRITE\n"); 272 273 /* FIXME: pend operation if possible */ 274 /* FIXME: use write timeouts */ 275 276 Stack = IoGetCurrentIrpStackLocation(Irp); 277 Length = Stack->Parameters.Write.Length; 278 Buffer = SerialGetUserBuffer(Irp); 279 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 280 281 if (Stack->Parameters.Write.ByteOffset.QuadPart != 0 || Buffer == NULL) 282 { 283 Status = STATUS_INVALID_PARAMETER; 284 goto ByeBye; 285 } 286 287 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); 288 if (!NT_SUCCESS(Status)) 289 goto ByeBye; 290 291 /* push bytes into output buffer */ 292 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql); 293 while (Information < Length) 294 { 295 Status = PushCircularBufferEntry(&DeviceExtension->OutputBuffer, Buffer[Information]); 296 if (!NT_SUCCESS(Status)) 297 { 298 if (Status == STATUS_BUFFER_TOO_SMALL) 299 { 300 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql); 301 SerialSendByte(NULL, DeviceExtension, NULL, NULL); 302 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql); 303 continue; 304 } 305 else 306 { 307 WARN_(SERIAL, "Buffer overrun on COM%lu\n", DeviceExtension->ComPort); 308 DeviceExtension->SerialPerfStats.BufferOverrunErrorCount++; 309 break; 310 } 311 } 312 Information++; 313 } 314 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql); 315 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); 316 317 /* send bytes */ 318 SerialSendByte(NULL, DeviceExtension, NULL, NULL); 319 320 ByeBye: 321 Irp->IoStatus.Information = Information; 322 Irp->IoStatus.Status = Status; 323 IoCompleteRequest(Irp, IO_NO_INCREMENT); 324 return Status; 325 } 326