Correct timeout issues when reading
Pend read IRPs
Lots of small issues
Modified: trunk/reactos/drivers/dd/serial/devctrl.c
Modified: trunk/reactos/drivers/dd/serial/legacy.c
Modified: trunk/reactos/drivers/dd/serial/misc.c
Modified: trunk/reactos/drivers/dd/serial/pnp.c
Modified: trunk/reactos/drivers/dd/serial/rw.c
Modified: trunk/reactos/drivers/dd/serial/serial.c
Modified: trunk/reactos/drivers/dd/serial/serial.h

Modified: trunk/reactos/drivers/dd/serial/devctrl.c
--- trunk/reactos/drivers/dd/serial/devctrl.c	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/devctrl.c	2005-04-02 14:16:47 UTC (rev 14440)
@@ -7,7 +7,6 @@
  * 
  * PROGRAMMERS:     HervÚ Poussineau (poussine@freesurf.fr)
  */
-/* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
 
 #define NDEBUG
 #include "serial.h"
@@ -219,22 +218,30 @@
 	pCommProp->ServiceMask = SERIAL_SP_SERIALCOMM;
 	pCommProp->MaxTxQueue = pCommProp->CurrentTxQueue = DeviceExtension->OutputBuffer.Length - 1;
 	pCommProp->MaxRxQueue = pCommProp->CurrentRxQueue = DeviceExtension->InputBuffer.Length - 1;
-	pCommProp->MaxBaud = SERIAL_BAUD_115200;
 	pCommProp->ProvSubType = 1; // PST_RS232;
-	/* FIXME: ProvCapabilities may be related to Uart type */
 	pCommProp->ProvCapabilities = SERIAL_PCF_DTRDSR | SERIAL_PCF_INTTIMEOUTS | SERIAL_PCF_PARITY_CHECK
 		| SERIAL_PCF_RTSCTS | SERIAL_PCF_SETXCHAR | SERIAL_PCF_SPECIALCHARS | SERIAL_PCF_TOTALTIMEOUTS
 		| SERIAL_PCF_XONXOFF;
-	/* FIXME: SettableParams may be related to Uart type */
 	pCommProp->SettableParams = SERIAL_SP_BAUD | SERIAL_SP_DATABITS | SERIAL_SP_HANDSHAKING
 		| SERIAL_SP_PARITY | SERIAL_SP_PARITY_CHECK | SERIAL_SP_STOPBITS;
-	/* FIXME: SettableBaud may be related to Uart type */
+	
+	/* SettableBaud is related to Uart type */
 	pCommProp->SettableBaud = SERIAL_BAUD_075 | SERIAL_BAUD_110 | SERIAL_BAUD_134_5
 		| SERIAL_BAUD_150 | SERIAL_BAUD_300 | SERIAL_BAUD_600 | SERIAL_BAUD_1200
 		| SERIAL_BAUD_1800 | SERIAL_BAUD_2400 | SERIAL_BAUD_4800 | SERIAL_BAUD_7200
-		| SERIAL_BAUD_9600 | SERIAL_BAUD_14400 | SERIAL_BAUD_19200 | SERIAL_BAUD_38400
-		| SERIAL_BAUD_56K | SERIAL_BAUD_57600 | SERIAL_BAUD_115200 | SERIAL_BAUD_128K
-		| SERIAL_BAUD_USER;
+		| SERIAL_BAUD_9600 | SERIAL_BAUD_USER;
+	pCommProp->MaxBaud = SERIAL_BAUD_9600;
+	if (DeviceExtension->UartType >= Uart16450)
+	{
+		pCommProp->SettableBaud |= SERIAL_BAUD_14400 | SERIAL_BAUD_19200 | SERIAL_BAUD_38400;
+		pCommProp->MaxBaud = SERIAL_BAUD_38400;
+	}
+	if (DeviceExtension->UartType >= Uart16550)
+	{
+		pCommProp->SettableBaud |= SERIAL_BAUD_56K | SERIAL_BAUD_57600 | SERIAL_BAUD_115200 | SERIAL_BAUD_128K;
+		pCommProp->MaxBaud = SERIAL_BAUD_115200;
+	}
+	
 	pCommProp->SettableData = SERIAL_DATABITS_5 | SERIAL_DATABITS_6 | SERIAL_DATABITS_7 | SERIAL_DATABITS_8;
 	pCommProp->SettableStopParity = SERIAL_STOPBITS_10 | SERIAL_STOPBITS_15 | SERIAL_STOPBITS_20
 		| SERIAL_PARITY_NONE | SERIAL_PARITY_ODD | SERIAL_PARITY_EVEN | SERIAL_PARITY_MARK | SERIAL_PARITY_SPACE;
@@ -264,8 +271,8 @@
 		- DeviceExtension->OutputBuffer.ReadPosition) % DeviceExtension->OutputBuffer.Length;
 	KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
 	
-	pSerialStatus->EofReceived = FALSE; /* FIXME */
-	pSerialStatus->WaitForImmediate = FALSE; /* FIXME */
+	pSerialStatus->EofReceived = FALSE; /* always FALSE */
+	pSerialStatus->WaitForImmediate = FALSE; /* always FALSE */
 	
 	return STATUS_SUCCESS;
 }
@@ -286,8 +293,6 @@
 	
 	DPRINT("Serial: IRP_MJ_DEVICE_CONTROL dispatch\n");
 	
-	/* FIXME: pend operation if possible */
-	
 	Stack = IoGetCurrentIrpStackLocation(Irp);
 	LengthIn = Stack->Parameters.DeviceIoControl.InputBufferLength;
 	LengthOut = Stack->Parameters.DeviceIoControl.OutputBufferLength;
@@ -315,9 +320,13 @@
 			DPRINT("Serial: IOCTL_SERIAL_CLR_DTR\n");
 			/* FIXME: If the handshake flow control of the device is configured to 
 			 * automatically use DTR, return STATUS_INVALID_PARAMETER */
-			DeviceExtension->MCR &= ~SR_MCR_DTR;
-			WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
-			Status = STATUS_SUCCESS;
+			Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+			if (NT_SUCCESS(Status))
+			{
+				DeviceExtension->MCR &= ~SR_MCR_DTR;
+				WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+				IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+			}
 			break;
 		}
 		case IOCTL_SERIAL_CLR_RTS:
@@ -325,9 +334,13 @@
 			DPRINT("Serial: IOCTL_SERIAL_CLR_RTS\n");
 			/* FIXME: If the handshake flow control of the device is configured to 
 			 * automatically use RTS, return STATUS_INVALID_PARAMETER */
-			DeviceExtension->MCR &= ~SR_MCR_RTS;
-			WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
-			Status = STATUS_SUCCESS;
+			Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+			if (NT_SUCCESS(Status))
+			{
+				DeviceExtension->MCR &= ~SR_MCR_RTS;
+				WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+				IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+			}
 			break;
 		}
 		case IOCTL_SERIAL_CONFIG_SIZE:
@@ -546,14 +559,20 @@
 			DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0;
 			DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0;
 			/* Clear receive/transmit buffers */
-			if (DeviceExtension->UartType >= Uart16550)
+			if (DeviceExtension->UartType >= Uart16550A)
 			{
-				WRITE_PORT_UCHAR(SER_FCR(ComPortBase),
-					SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
+				/* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
+				Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				if (NT_SUCCESS(Status))
+				{
+					WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
+					IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				}
 			}
+			else
+				Status = STATUS_SUCCESS;
 			KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql2);
 			KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql1);
-			Status = STATUS_SUCCESS;
 			break;
 		}
 		case IOCTL_SERIAL_RESET_DEVICE:
@@ -604,10 +623,16 @@
 			DPRINT("Serial: IOCTL_SERIAL_SET_DTR\n");
 			if (!(DeviceExtension->MCR & SR_MCR_DTR))
 			{
-				DeviceExtension->MCR |= SR_MCR_DTR;
-				WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+				Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				if (NT_SUCCESS(Status))
+				{
+					DeviceExtension->MCR |= SR_MCR_DTR;
+					WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+					IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				}
 			}
-			Status = STATUS_SUCCESS;
+			else
+				Status = STATUS_SUCCESS;
 			break;
 		}
 		case IOCTL_SERIAL_SET_FIFO_CONTROL:
@@ -617,8 +642,12 @@
 				Status = STATUS_INVALID_PARAMETER;
 			else
 			{
-				WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff));
-				Status = STATUS_SUCCESS;
+				Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				if (NT_SUCCESS(Status))
+				{
+					WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff));
+					IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				}
 			}
 			break;
 		}
@@ -648,10 +677,14 @@
 				Status = STATUS_INVALID_PARAMETER;
 			else
 			{
-				pMCR = (PULONG)BufferIn;
-				DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff);
-				WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
-				Status = STATUS_SUCCESS;
+				Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				if (NT_SUCCESS(Status))
+				{
+					pMCR = (PULONG)BufferIn;
+					DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff);
+					WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+					IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				}
 			}
 			break;
 		}
@@ -688,10 +721,16 @@
 			DPRINT("Serial: IOCTL_SERIAL_SET_RTS\n");
 			if (!(DeviceExtension->MCR & SR_MCR_RTS))
 			{
-				DeviceExtension->MCR |= SR_MCR_RTS;
-				WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+				Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				if (NT_SUCCESS(Status))
+				{
+					DeviceExtension->MCR |= SR_MCR_RTS;
+					WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
+					IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+				}
 			}
-			Status = STATUS_SUCCESS;
+			else
+				Status = STATUS_SUCCESS;
 			break;
 		}
 		case IOCTL_SERIAL_SET_TIMEOUTS:

Modified: trunk/reactos/drivers/dd/serial/legacy.c
--- trunk/reactos/drivers/dd/serial/legacy.c	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/legacy.c	2005-04-02 14:16:47 UTC (rev 14440)
@@ -96,8 +96,8 @@
 	if (!ResourceList)
 		return STATUS_INSUFFICIENT_RESOURCES;
 	ResourceList->Count = 1;
-	ResourceList->List[0].InterfaceType = Isa;
-	ResourceList->List[0].BusNumber = -1; /* FIXME */
+	ResourceList->List[0].InterfaceType = InterfaceTypeUndefined;
+	ResourceList->List[0].BusNumber = -1; /* unknown */
 	ResourceList->List[0].PartialResourceList.Version = 1;
 	ResourceList->List[0].PartialResourceList.Revision = 1;
 	ResourceList->List[0].PartialResourceList.Count = 2;
@@ -137,7 +137,7 @@
 	{
 		Status = IoReportDetectedDevice(
 			DriverObject,
-			ResourceList->List[0].InterfaceType, ResourceList->List[0].BusNumber, -1/*FIXME*/,
+			ResourceList->List[0].InterfaceType, ResourceList->List[0].BusNumber, -1 /* unknown */,
 			ResourceList, NULL,
 			TRUE,
 			&Pdo);

Modified: trunk/reactos/drivers/dd/serial/misc.c
--- trunk/reactos/drivers/dd/serial/misc.c	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/misc.c	2005-04-02 14:16:47 UTC (rev 14440)
@@ -105,9 +105,6 @@
 	DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension;
 	ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
 	
-	DPRINT1("Serial: sending bytes (if any) on COM%lu\n",
-		DeviceExtension->ComPort);
-	
 	KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
 	while (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer)
 		&& READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_TBE)

Modified: trunk/reactos/drivers/dd/serial/pnp.c
--- trunk/reactos/drivers/dd/serial/pnp.c	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/pnp.c	2005-04-02 14:16:47 UTC (rev 14440)
@@ -80,7 +80,7 @@
 	KeInitializeSpinLock(&DeviceExtension->OutputBufferLock);
 	KeInitializeDpc(&DeviceExtension->ReceivedByteDpc, SerialReceiveByte, DeviceExtension);
 	KeInitializeDpc(&DeviceExtension->SendByteDpc, SerialSendByte, DeviceExtension);
-	//Fdo->Flags |= DO_POWER_PAGEABLE (or DO_POWER_INRUSH?)
+	Fdo->Flags |= DO_POWER_PAGABLE;
 	Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
 	if (!NT_SUCCESS(Status))
 	{
@@ -126,8 +126,8 @@
 	 * not called with a NULL Pdo. Block this call (blocks
 	 * unfortunately all the other PnP serial ports devices).
 	 */
-	//return SerialAddDeviceInternal(DriverObject, Pdo, UartUnknown, NULL);
-	return STATUS_UNSUCCESSFUL;
+	return SerialAddDeviceInternal(DriverObject, Pdo, UartUnknown, NULL);
+	//return STATUS_UNSUCCESSFUL;
 }
 
 NTSTATUS STDCALL
@@ -230,8 +230,9 @@
 	}
 	
 	/* Clear receive/transmit buffers */
-	if (DeviceExtension->UartType >= Uart16550)
+	if (DeviceExtension->UartType >= Uart16550A)
 	{
+		/* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
 		WRITE_PORT_UCHAR(SER_FCR(ComPortBase),
 			SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
 	}
@@ -307,6 +308,69 @@
 		{
 			DPRINT("Serial: IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
 			
+			/* FIXME: first HACK: PnP manager can send multiple
+			 * IRP_MN_START_DEVICE for one device
+			 */
+			if (((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->PnpState != dsStopped)
+			{
+				DPRINT1("Serial: device already started. Ignoring this irp!\n");
+				Status = STATUS_SUCCESS;
+				break;
+			}
+			/* FIXME: AllocatedResources MUST never be NULL ;
+			 * that's the second HACK because resource arbitration
+			 * doesn't exist in ReactOS yet...
+			 */
+			if (Stack->Parameters.StartDevice.AllocatedResources == NULL)
+			{
+				ULONG ResourceListSize;
+				PCM_RESOURCE_LIST ResourceList;
+				PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
+				KIRQL Dirql;
+				ULONG ComPortBase;
+				ULONG Irq;
+				
+				DPRINT1("Serial: no allocated resources for this device! Creating fake list\n");
+				/* These values are resources of the ONLY serial
+				 * port that will be managed by this driver
+				 * (default is COM2) */
+				ComPortBase = 0x2f8;
+				Irq = 3;
+				
+				/* Create resource list */
+				ResourceListSize = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
+				ResourceList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(PagedPool, ResourceListSize, SERIAL_TAG);
+				if (!ResourceList)
+					return STATUS_INSUFFICIENT_RESOURCES;
+				ResourceList->Count = 1;
+				ResourceList->List[0].InterfaceType = Isa;
+				ResourceList->List[0].BusNumber = -1; /* FIXME */
+				ResourceList->List[0].PartialResourceList.Version = 1;
+				ResourceList->List[0].PartialResourceList.Revision = 1;
+				ResourceList->List[0].PartialResourceList.Count = 2;
+				ResourceDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0];
+				ResourceDescriptor->Type = CmResourceTypePort;
+				ResourceDescriptor->ShareDisposition = CmResourceShareDriverExclusive;
+				ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
+				ResourceDescriptor->u.Port.Start.u.HighPart = 0;
+				ResourceDescriptor->u.Port.Start.u.LowPart = ComPortBase;
+				ResourceDescriptor->u.Port.Length = 8;
+				
+				ResourceDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[1];
+				ResourceDescriptor->Type = CmResourceTypeInterrupt;
+				ResourceDescriptor->ShareDisposition = CmResourceShareShared;
+				ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
+				ResourceDescriptor->u.Interrupt.Vector = HalGetInterruptVector(
+					Internal, 0, 0, Irq,
+					&Dirql,
+					&ResourceDescriptor->u.Interrupt.Affinity);
+				ResourceDescriptor->u.Interrupt.Level = (ULONG)Dirql;
+				
+				Stack->Parameters.StartDevice.AllocatedResources =
+					Stack->Parameters.StartDevice.AllocatedResourcesTranslated =
+					ResourceList;
+			}
+			
 			/* Call lower driver */
 			Status = ForwardIrpAndWait(DeviceObject, Irp);
 			if (NT_SUCCESS(Status))

Modified: trunk/reactos/drivers/dd/serial/rw.c
--- trunk/reactos/drivers/dd/serial/rw.c	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/rw.c	2005-04-02 14:16:47 UTC (rev 14440)
@@ -15,11 +15,124 @@
 static PVOID
 SerialGetUserBuffer(IN PIRP Irp)
 {
-   ASSERT(Irp);
-   
-   return Irp->AssociatedIrp.SystemBuffer;
+	ASSERT(Irp);
+	
+	return Irp->AssociatedIrp.SystemBuffer;
 }
 
+static VOID
+ReadBytes(
+	IN PDEVICE_OBJECT DeviceObject,
+	IN PIRP Irp,
+	PWORKITEM_DATA WorkItemData)
+{
+	PSERIAL_DEVICE_EXTENSION DeviceExtension;
+	PUCHAR ComPortBase;
+	ULONG Length;
+	PUCHAR Buffer;
+	ULONG Information = 0;
+	LARGE_INTEGER SystemTime, ByteTimeoutTime;
+	UCHAR ReceivedByte;
+	BOOLEAN IsByteReceived;
+	//KIRQL Irql;
+	
+	DPRINT("Serial: ReadBytes() called\n");
+	
+	DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+	ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
+	Length = IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length;
+	Buffer = SerialGetUserBuffer(Irp);
+	
+	/* FIXME: remove disabling interrupts */
+	WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER & ~1);
+	while (Length > 0)
+	{
+		/* Calculate dead line to receive the next byte */
+		KeQuerySystemTime(&SystemTime);
+		ByteTimeoutTime.QuadPart = SystemTime.QuadPart +
+			WorkItemData->IntervalTimeout * 10000;
+		
+		IsByteReceived = FALSE;
+		while (TRUE)
+		{
+#if 1
+			if ((READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR) != 0)
+			{
+				ReceivedByte = READ_PORT_UCHAR(ComPortBase);
+				DPRINT("Serial: received byte 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
+				IsByteReceived = TRUE;
+				break;
+			}
+			else if (WorkItemData->DontWait &&
+			         !(WorkItemData->ReadAtLeastOneByte && Information == 0))
+			{
+				DPRINT("Serial: read buffer empty\n");
+				break;
+			}
+#else
+			KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
+			if (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer))
+			{
+				CHECKPOINT1;
+				PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
+				KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
+				DPRINT("Serial: reading byte from buffer 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
+				IsByteReceived = TRUE;
+				break;
+			}
+			else if (WorkItemData->DontWait &&
+			         !(WorkItemData->ReadAtLeastOneByte && Information == 0))
+			{
+				DPRINT("Serial: read buffer empty\n");
+				break;
+			}
+			KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
+#endif
+			if (IsByteReceived) break;
+			KeQuerySystemTime(&SystemTime);
+			if (WorkItemData->UseIntervalTimeout && Information > 0)
+			{
+				if (SystemTime.QuadPart >= ByteTimeoutTime.QuadPart)
+					break;
+			}
+			if (WorkItemData->UseTotalTimeout)
+			{
+				if (SystemTime.QuadPart >= WorkItemData->TotalTimeoutTime.QuadPart)
+					break;
+			}
+		}
+		if (!IsByteReceived) break;
+		Buffer[Information++] = ReceivedByte;
+		Length--;
+	}
+	/* FIXME: remove enabling interrupts */
+	WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
+	
+	Irp->IoStatus.Information = Information;
+	if (Information == 0)
+		Irp->IoStatus.Status = STATUS_TIMEOUT;
+	else
+		Irp->IoStatus.Status = STATUS_SUCCESS;
+}
+
+static VOID STDCALL
+SerialReadWorkItem(
+	IN PDEVICE_OBJECT DeviceObject,
+	IN PVOID pWorkItemData /* real type PWORKITEM_DATA */)
+{
+	PWORKITEM_DATA WorkItemData;
+	PIRP Irp;
+	
+	DPRINT("Serial: SerialReadWorkItem() called\n");
+	
+	WorkItemData = (PWORKITEM_DATA)pWorkItemData;
+	Irp = WorkItemData->Irp;
+	
+	ReadBytes(DeviceObject, Irp, WorkItemData);
+	ExFreePoolWithTag(pWorkItemData, SERIAL_TAG);
+	IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
 NTSTATUS STDCALL
 SerialRead(
 	IN PDEVICE_OBJECT DeviceObject,
@@ -28,12 +141,10 @@
 	PIO_STACK_LOCATION Stack;
 	PSERIAL_DEVICE_EXTENSION DeviceExtension;
 	ULONG Length;
-	ULONG Information = 0;
 	PUCHAR Buffer;
-	PUCHAR ComPortBase;
-	UCHAR ReceivedByte;
-	KIRQL Irql;
-	NTSTATUS Status = STATUS_SUCCESS;
+	PWORKITEM_DATA WorkItemData;
+	PIO_WORKITEM WorkItem;
+	NTSTATUS Status;
 	
 	DPRINT("Serial: IRP_MJ_READ\n");
 	
@@ -43,7 +154,6 @@
 	Length = Stack->Parameters.Read.Length;
 	Buffer = SerialGetUserBuffer(Irp);
 	DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
-	ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
 	
 	if (Stack->Parameters.Read.ByteOffset.QuadPart != 0 || Buffer == NULL)
 	{
@@ -57,136 +167,79 @@
 		goto ByeBye;
 	}
 	
-	Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
-	if (!NT_SUCCESS(Status))
+	/* Allocate memory for parameters */
+	WorkItemData = ExAllocatePoolWithTag(PagedPool, sizeof(WORKITEM_DATA), SERIAL_TAG);
+	if (!WorkItemData)
+	{
+		Status = STATUS_INSUFFICIENT_RESOURCES;
 		goto ByeBye;
+	}
+	RtlZeroMemory(WorkItemData, sizeof(WORKITEM_DATA));
+	WorkItemData->Irp = Irp;
 	
-	KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
-	while (Length-- > 0)
+	/* Calculate time outs */
+	if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
+		 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == INFINITE &&
+		 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant > 0 &&
+		 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant < INFINITE)
 	{
-		Status = PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
-		if (!NT_SUCCESS(Status))
-			break;
-		DPRINT("Serial: read from buffer 0x%x (%c)\n", ReceivedByte, ReceivedByte);
-		Buffer[Information++] = ReceivedByte;
+		/* read at least one byte, and at most bytes already received */
+		WorkItemData->DontWait = TRUE;
+		WorkItemData->ReadAtLeastOneByte = TRUE;
 	}
-	KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
-	if (Length > 0 &&
-		!(DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
-		  DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0 &&
-		  DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0))
+	else if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
+	         DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0 &&
+	         DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0)
 	{
-		ULONG IntervalTimeout = 0;
-		ULONG TotalTimeout = 0;
-		BOOLEAN UseIntervalTimeout = FALSE;
-		BOOLEAN UseTotalTimeout = FALSE;
-		ULONG ThisByteTimeout;
-		BOOLEAN IsByteReceived;
-		ULONG i;
-		/* Extract timeouts informations */
-		if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
-			DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == INFINITE &&
-			DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant > 0 &&
-			DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant < INFINITE)
-		{
-			if (Information > 0)
+		/* read only bytes that are already in buffer */
+		WorkItemData->DontWait = TRUE;
+	}
+	else
+	{
+		/* use timeouts */
+		if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout != 0)
 			{
-				/* don't read mode bytes */
-				Length = 0;
-			}
-			else
-			{
-				/* read only one byte */
-				UseTotalTimeout = TRUE;
-				TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant;
-				Length = 1;
-			}
+				WorkItemData->UseIntervalTimeout = TRUE;
+				WorkItemData->IntervalTimeout = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout;
 		}
-		else
+		if (DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier != 0 ||
+			 DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant != 0)
 		{
-			if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout != 0)
-			{
-				UseIntervalTimeout = TRUE;
-				IntervalTimeout = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout;
-			}
-			if (DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant != 0 ||
-				DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier != 0)
-			{
-				UseTotalTimeout = TRUE;
-				TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant +
-					DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier * Length;
-			}
+			ULONG TotalTimeout;
+			LARGE_INTEGER SystemTime;
+			
+			WorkItemData->UseTotalTimeout = TRUE;
+			TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant +
+				DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier * Length;
+			KeQuerySystemTime(&SystemTime);
+			WorkItemData->TotalTimeoutTime.QuadPart = SystemTime.QuadPart +
+				TotalTimeout * 10000;
 		}
-		DPRINT("Serial: UseIntervalTimeout = %ws, IntervalTimeout = %lu\n",
-			UseIntervalTimeout ? L"YES" : L"NO",
-			UseIntervalTimeout ? IntervalTimeout : 0);
-		DPRINT("Serial: UseTotalTimeout = %ws, TotalTimeout = %lu\n",
-			UseTotalTimeout ? L"YES" : L"NO",
-			UseTotalTimeout ? TotalTimeout : 0);
-		
-		/* FIXME: it should be better to use input buffer instead of
-		 * disabling interrupts, and try to directly read for port! */
-		
-		/* FIXME: NtQueryPerformanceCounter gives a more accurate
-		 * timer, but it is not available on all computers. First try
-		 * NtQueryPerformanceCounter, and current method if it is not
-		 * implemented. */
-		
-		/* FIXME: remove disabling interrupts */
-		WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER & ~1);
-		while (Length > 0)
-		{
-			ThisByteTimeout = IntervalTimeout;
-			IsByteReceived = FALSE;
-			while (TRUE)
-			{
-				for (i = 0; i < 1000; i++)
-				{
-#if 1
-					if ((READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR) != 0)
-					{
-						ReceivedByte = READ_PORT_UCHAR(ComPortBase);
-						DPRINT("Serial: received byte 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
-						IsByteReceived = TRUE;
-						break;
-					}
-#else
-					KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
-					if (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer))
-					{
-						PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
-						KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
-						DPRINT("Serial: reading byte from buffer 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
-						IsByteReceived = TRUE;
-						break;
-					}
-					KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
-#endif
-					KeStallExecutionProcessor(1);
-				}
-				if (IsByteReceived) break;
-				if (UseIntervalTimeout)
-				{
-					if (ThisByteTimeout == 0) break; else ThisByteTimeout--;
-				}
-				if (UseTotalTimeout)
-				{
-					if (TotalTimeout == 0) break; else TotalTimeout--;
-				}
-			}
-			if (!IsByteReceived) break;
-			Buffer[Information++] = ReceivedByte;
-			Length--;
-		}
-		/* FIXME: remove enabling interrupts */
-		WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
 	}
-	Status = STATUS_SUCCESS;
 	
+	/* Pend IRP */
+	WorkItem = IoAllocateWorkItem(DeviceObject);
+	if (WorkItem)
+	{
+		IoQueueWorkItem(WorkItem, SerialReadWorkItem, DelayedWorkQueue, WorkItemData);
+		IoMarkIrpPending(Irp);
+		return STATUS_PENDING;
+	}
+	
+	/* insufficient resources, we can't pend the Irp */
+	CHECKPOINT;
+	Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
+	if (!NT_SUCCESS(Status))
+	{
+		ExFreePoolWithTag(WorkItemData, SERIAL_TAG);
+		goto ByeBye;
+	}
+	ReadBytes(DeviceObject, Irp, WorkItemData);
+	Status = Irp->IoStatus.Status;
+	
 	IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
 
 ByeBye:
-	Irp->IoStatus.Information = Information;
 	Irp->IoStatus.Status = Status;
 	IoCompleteRequest(Irp, IO_NO_INCREMENT);
 	return Status;

Modified: trunk/reactos/drivers/dd/serial/serial.c
--- trunk/reactos/drivers/dd/serial/serial.c	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/serial.c	2005-04-02 14:16:47 UTC (rev 14440)
@@ -7,7 +7,6 @@
  * 
  * PROGRAMMERS:     HervÚ Poussineau (poussine@freesurf.fr)
  */
-/* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
 
 //#define NDEBUG
 #include "serial.h"

Modified: trunk/reactos/drivers/dd/serial/serial.h
--- trunk/reactos/drivers/dd/serial/serial.h	2005-04-02 11:40:58 UTC (rev 14439)
+++ trunk/reactos/drivers/dd/serial/serial.h	2005-04-02 14:16:47 UTC (rev 14440)
@@ -20,7 +20,7 @@
   #define STDCALL
   
   #define DPRINT1 DbgPrint("(%s:%d) ", __FILE__, __LINE__), DbgPrint
-  #define CHECKPOINT1 DbgPrint("(%s:%d)\n")
+  #define CHECKPOINT1 DbgPrint("(%s:%d)\n", __FILE__, __LINE__)
   
   #define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
   
@@ -102,6 +102,18 @@
 	UCHAR MSR; /* Base+6, Modem Status Register */
 } SERIAL_DEVICE_EXTENSION, *PSERIAL_DEVICE_EXTENSION;
 
+typedef struct _WORKITEM_DATA
+{
+	PIRP Irp;
+	
+	BOOLEAN UseIntervalTimeout;
+	BOOLEAN UseTotalTimeout;
+	ULONG IntervalTimeout;
+	LARGE_INTEGER TotalTimeoutTime;
+	BOOLEAN DontWait;
+	BOOLEAN ReadAtLeastOneByte;
+} WORKITEM_DATA, *PWORKITEM_DATA;
+
 #define SERIAL_TAG TAG('S', 'e', 'r', 'l')
 
 #define INFINITE ((ULONG)-1)