Replace polling mode in IRP_MJ_READ by an interrupt mode.

It is a lot less CPU consuming...

svn path=/trunk/; revision=14485
This commit is contained in:
Hervé Poussineau 2005-04-03 21:36:15 +00:00
parent ad17913046
commit 6514e33a95
4 changed files with 149 additions and 124 deletions

View file

@ -64,29 +64,37 @@ VOID STDCALL
SerialReceiveByte(
IN PKDPC Dpc,
IN PVOID pDeviceExtension, // real type PSERIAL_DEVICE_EXTENSION
IN PVOID pByte, // real type UCHAR
IN PVOID Unused)
IN PVOID Unused1,
IN PVOID Unused2)
{
PSERIAL_DEVICE_EXTENSION DeviceExtension;
PUCHAR ComPortBase;
UCHAR Byte;
KIRQL Irql;
UCHAR IER;
NTSTATUS Status;
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension;
Byte = (UCHAR)(ULONG_PTR)pByte;
DPRINT1("Serial: received byte on COM%lu: 0x%02x (%c)\n",
DeviceExtension->ComPort, Byte, Byte);
ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
Status = PushCircularBufferEntry(&DeviceExtension->InputBuffer, Byte);
if (!NT_SUCCESS(Status))
while (READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR)
{
/* FIXME: count buffer overflow */
return;
Byte = READ_PORT_UCHAR(SER_RBR(ComPortBase));
DPRINT("Serial: Byte received on COM%lu: 0x%02x\n",
DeviceExtension->ComPort, Byte);
Status = PushCircularBufferEntry(&DeviceExtension->InputBuffer, Byte);
if (NT_SUCCESS(Status))
DeviceExtension->SerialPerfStats.ReceivedCount++;
else
DeviceExtension->SerialPerfStats.BufferOverrunErrorCount++;
}
DPRINT1("Serial: push to buffer done\n");
KeSetEvent(&DeviceExtension->InputBufferNotEmpty, 0, FALSE);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
InterlockedIncrement(&DeviceExtension->SerialPerfStats.ReceivedCount);
/* allow new interrupts */
IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_DATA_RECEIVED);
}
VOID STDCALL
@ -100,6 +108,7 @@ SerialSendByte(
PUCHAR ComPortBase;
UCHAR Byte;
KIRQL Irql;
UCHAR IER;
NTSTATUS Status;
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension;
@ -113,9 +122,15 @@ SerialSendByte(
if (!NT_SUCCESS(Status))
break;
WRITE_PORT_UCHAR(SER_THR(ComPortBase), Byte);
DPRINT("Serial: Byte sent to COM%lu: 0x%02x\n",
DeviceExtension->ComPort, Byte);
DeviceExtension->SerialPerfStats.TransmittedCount++;
}
}
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
/* allow new interrupts */
IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_THR_EMPTY);
}
BOOLEAN STDCALL
@ -125,7 +140,6 @@ SerialInterruptService(
{
PDEVICE_OBJECT DeviceObject;
PSERIAL_DEVICE_EXTENSION DeviceExtension;
UCHAR Byte;
PUCHAR ComPortBase;
UCHAR Iir;
@ -139,32 +153,32 @@ SerialInterruptService(
Iir &= SR_IIR_ID_MASK;
if ((Iir & SR_IIR_SELF) != 0) { return FALSE; }
/* FIXME: sometimes, update DeviceExtension->IER */
/* FIXME: sometimes, update DeviceExtension->MCR */
switch (Iir)
{
case SR_IIR_MSR_CHANGE:
{
UCHAR IER;
DPRINT1("Serial: SR_IIR_MSR_CHANGE\n");
DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
/* FIXME: what to do? */
IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_MSR_CHANGE);
return TRUE;
}
case SR_IIR_THR_EMPTY:
{
DPRINT("Serial: SR_IIR_THR_EMPTY\n");
return KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL);
KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL);
return TRUE;
}
case SR_IIR_DATA_RECEIVED:
{
DPRINT1("Serial: SR_IIR_DATA_RECEIVED\n");
while (READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR)
{
Byte = READ_PORT_UCHAR(SER_RBR(ComPortBase));
DPRINT1("Serial: Byte received: 0x%02x (%c)\n", Byte, Byte);
if (!KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, (PVOID)(ULONG_PTR)Byte, NULL))
break;
}
DPRINT("Serial: SR_IIR_DATA_RECEIVED\n");
KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, NULL, NULL);
return TRUE;
}
case SR_IIR_ERROR:

View file

@ -78,6 +78,7 @@ SerialAddDeviceInternal(
IoInitializeRemoveLock(&DeviceExtension->RemoveLock, SERIAL_TAG, 0, 0);
KeInitializeSpinLock(&DeviceExtension->InputBufferLock);
KeInitializeSpinLock(&DeviceExtension->OutputBufferLock);
KeInitializeEvent(&DeviceExtension->InputBufferNotEmpty, NotificationEvent, FALSE);
KeInitializeDpc(&DeviceExtension->ReceivedByteDpc, SerialReceiveByte, DeviceExtension);
KeInitializeDpc(&DeviceExtension->SendByteDpc, SerialSendByte, DeviceExtension);
Fdo->Flags |= DO_POWER_PAGABLE;
@ -144,6 +145,7 @@ SerialPnpStartDevice(
UNICODE_STRING ComPort;
ULONG Vector = 0;
ULONG i, j;
UCHAR IER;
KIRQL Dirql;
KAFFINITY Affinity = 0;
KINTERRUPT_MODE InterruptMode = Latched;
@ -195,8 +197,6 @@ SerialPnpStartDevice(
DeviceExtension->BaseAddress, Dirql);
if (!DeviceExtension->BaseAddress)
return STATUS_INSUFFICIENT_RESOURCES;
/* FIXME: we should be able to continue and use polling method
* for read/write if we don't have an interrupt */
if (!Dirql)
return STATUS_INSUFFICIENT_RESOURCES;
ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
@ -205,7 +205,6 @@ SerialPnpStartDevice(
DeviceExtension->UartType = SerialDetectUartType(ComPortBase);
/* Get current settings */
DeviceExtension->IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
DeviceExtension->MCR = READ_PORT_UCHAR(SER_MCR(ComPortBase));
DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
DeviceExtension->WaitMask = 0;
@ -279,11 +278,13 @@ SerialPnpStartDevice(
DeviceExtension->PnpState = dsStarted;
DeviceExtension->IER |= 0x1f; /* Activate interrupt mode */
DeviceExtension->IER &= ~1; /* FIXME: Disable receive byte interrupt */
WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
/* Activate interrupt modes */
IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
IER |= SR_IER_DATA_RECEIVED | SR_IER_THR_EMPTY | SR_IER_LSR_CHANGE | SR_IER_MSR_CHANGE;
WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER);
DeviceExtension->MCR |= 0x03; /* Activate DTR, RTS */
/* Activate DTR, RTS */
DeviceExtension->MCR |= SR_MCR_DTR | SR_MCR_RTS;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
return STATUS_SUCCESS;
@ -331,11 +332,19 @@ SerialPnp(
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;
switch (((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->SerialPortNumber)
{
case 0:
ComPortBase = 0x3f8;
Irq = 4;
break;
case 1:
ComPortBase = 0x2f8;
Irq = 3;
break;
default:
ComPortBase = Irq = 0;
}
/* Create resource list */
ResourceListSize = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

View file

@ -7,7 +7,6 @@
*
* PROGRAMMERS: Hervé Poussineau (poussine@freesurf.fr)
*/
/* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
#define NDEBUG
#include "serial.h"
@ -30,83 +29,80 @@ ReadBytes(
PUCHAR ComPortBase;
ULONG Length;
PUCHAR Buffer;
ULONG Information = 0;
LARGE_INTEGER SystemTime, ByteTimeoutTime;
UCHAR ReceivedByte;
BOOLEAN IsByteReceived;
//KIRQL Irql;
DPRINT("Serial: ReadBytes() called\n");
KTIMER TotalTimeoutTimer;
KIRQL Irql;
ULONG ObjectCount;
PVOID ObjectsArray[2];
ULONG Information = 0;
NTSTATUS Status;
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);
DPRINT("Serial: UseIntervalTimeout = %s, IntervalTimeout = %lu\n",
WorkItemData->UseIntervalTimeout ? "YES" : "NO",
WorkItemData->UseIntervalTimeout ? WorkItemData->IntervalTimeout.QuadPart : 0);
DPRINT("Serial: UseTotalTimeout = %s\n",
WorkItemData->UseTotalTimeout ? "YES" : "NO");
ObjectCount = 1;
ObjectsArray[0] = &DeviceExtension->InputBufferNotEmpty;
if (WorkItemData->UseTotalTimeout)
{
KeInitializeTimer(&TotalTimeoutTimer);
KeSetTimer(&TotalTimeoutTimer, WorkItemData->TotalTimeoutTime, NULL);
ObjectsArray[ObjectCount] = &TotalTimeoutTimer;
ObjectCount++;
}
/* while buffer is not fully filled */
while (Length > 0)
{
/* Calculate dead line to receive the next byte */
KeQuerySystemTime(&SystemTime);
ByteTimeoutTime.QuadPart = SystemTime.QuadPart +
WorkItemData->IntervalTimeout * 10000;
IsByteReceived = FALSE;
while (TRUE)
/* read already received bytes from buffer */
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
while (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer)
&& Length > 0)
{
#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;
}
PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
DPRINT("Serial: reading byte from buffer: 0x%02x\n", ReceivedByte);
Buffer[Information++] = ReceivedByte;
Length--;
}
KeClearEvent(&DeviceExtension->InputBufferNotEmpty);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
if (WorkItemData->DontWait
&& !(WorkItemData->ReadAtLeastOneByte && Information == 0))
{
DPRINT("Serial: buffer empty. Don't wait more bytes\n");
break;
}
Status = KeWaitForMultipleObjects(
ObjectCount,
ObjectsArray,
WaitAny,
Executive,
KernelMode,
FALSE,
(WorkItemData->UseIntervalTimeout && Information > 0) ? &WorkItemData->IntervalTimeout : NULL,
NULL);
if (Status == STATUS_TIMEOUT /* interval timeout */
|| Status == STATUS_WAIT_1) /* total timeout */
{
DPRINT("Serial: timeout when reading bytes. Status = 0x%08lx\n", Status);
break;
}
if (!IsByteReceived) break;
Buffer[Information++] = ReceivedByte;
Length--;
}
/* FIXME: remove enabling interrupts */
WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
/* stop total timeout timer */
if (WorkItemData->UseTotalTimeout)
KeCancelTimer(&TotalTimeoutTimer);
Irp->IoStatus.Information = Information;
if (Information == 0)
@ -148,8 +144,6 @@ SerialRead(
DPRINT("Serial: IRP_MJ_READ\n");
/* FIXME: pend operation if possible */
Stack = IoGetCurrentIrpStackLocation(Irp);
Length = Stack->Parameters.Read.Length;
Buffer = SerialGetUserBuffer(Irp);
@ -200,7 +194,7 @@ SerialRead(
if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout != 0)
{
WorkItemData->UseIntervalTimeout = TRUE;
WorkItemData->IntervalTimeout = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout;
WorkItemData->IntervalTimeout.QuadPart = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout;
}
if (DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier != 0 ||
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant != 0)

View file

@ -54,10 +54,12 @@ typedef enum
typedef enum
{
UartUnknown,
Uart8250,
Uart16450,
Uart16550,
Uart16550A
Uart8250, /* initial version */
Uart16450, /* + 38.4 Kbps */
Uart16550, /* + 115 Kbps */
Uart16550A,/* + FIFO 16 bytes */
Uart16650, /* + FIFO 32 bytes, 230 Kbps, power management, auto-flow */
Uart16750 /* + FIFO 64 bytes, 460 Kbps */
} UART_TYPE;
typedef struct _CIRCULAR_BUFFER
@ -90,14 +92,14 @@ typedef struct _SERIAL_DEVICE_EXTENSION
SERIALPERF_STATS SerialPerfStats;
SERIAL_TIMEOUTS SerialTimeOuts;
BOOLEAN IsOpened;
BOOLEAN IsOpened;
KEVENT InputBufferNotEmpty;
CIRCULAR_BUFFER InputBuffer;
KSPIN_LOCK InputBufferLock;
CIRCULAR_BUFFER OutputBuffer;
KSPIN_LOCK OutputBufferLock;
/* Current values */
UCHAR IER; /* Base+1, Interrupt Enable Register */
UCHAR MCR; /* Base+4, Modem Control Register */
UCHAR MSR; /* Base+6, Modem Status Register */
} SERIAL_DEVICE_EXTENSION, *PSERIAL_DEVICE_EXTENSION;
@ -108,7 +110,7 @@ typedef struct _WORKITEM_DATA
BOOLEAN UseIntervalTimeout;
BOOLEAN UseTotalTimeout;
ULONG IntervalTimeout;
LARGE_INTEGER IntervalTimeout;
LARGE_INTEGER TotalTimeoutTime;
BOOLEAN DontWait;
BOOLEAN ReadAtLeastOneByte;
@ -120,22 +122,28 @@ typedef struct _WORKITEM_DATA
/* Baud master clock */
#define BAUD_CLOCK 1843200
#define CLOCKS_PER_BIT 16
#define CLOCKS_PER_BIT 16
/* UART registers and bits */
#define SER_RBR(x) ((x)+0)
#define SER_THR(x) ((x)+0)
#define SER_DLL(x) ((x)+0)
#define SER_IER(x) ((x)+1)
#define SER_DLM(x) ((x)+1)
#define SER_IIR(x) ((x)+2)
#define SER_RBR(x) ((x)+0) /* Receive Register */
#define SER_THR(x) ((x)+0) /* Transmit Register */
#define SER_DLL(x) ((x)+0) /* Baud Rate Divisor LSB */
#define SER_IER(x) ((x)+1) /* Interrupt Enable Register */
#define SR_IER_DATA_RECEIVED 0x01
#define SR_IER_THR_EMPTY 0x02
#define SR_IER_LSR_CHANGE 0x04
#define SR_IER_MSR_CHANGE 0x08
#define SR_IER_SLEEP_MODE 0x10 /* Uart >= 16750 */
#define SR_IER_LOW_POWER 0x20 /* Uart >= 16750 */
#define SER_DLM(x) ((x)+1) /* Baud Rate Divisor MSB */
#define SER_IIR(x) ((x)+2) /* Interrupt Identification Register */
#define SR_IIR_SELF 0x00
#define SR_IIR_ID_MASK 0x07
#define SR_IIR_MSR_CHANGE SR_IIR_SELF
#define SR_IIR_THR_EMPTY (SR_IIR_SELF | 2)
#define SR_IIR_DATA_RECEIVED (SR_IIR_SELF | 4)
#define SR_IIR_ERROR (SR_IIR_SELF | 6)
#define SER_FCR(x) ((x)+2)
#define SER_FCR(x) ((x)+2) /* FIFO Control Register (Uart >= 16550A) */
#define SR_FCR_ENABLE_FIFO 0x01
#define SR_FCR_CLEAR_RCVR (0x02 | SR_FCR_ENABLE_FIFO)
#define SR_FCR_CLEAR_XMIT (0x04 | SR_FCR_ENABLE_FIFO)
@ -143,7 +151,7 @@ typedef struct _WORKITEM_DATA
#define SR_FCR_4_BYTES (0x40 | SR_FCR_ENABLE_FIFO)
#define SR_FCR_8_BYTES (0x80 | SR_FCR_ENABLE_FIFO)
#define SR_FCR_14_BYTES (0xC0 | SR_FCR_ENABLE_FIFO)
#define SER_LCR(x) ((x)+3)
#define SER_LCR(x) ((x)+3) /* Line Control Register */
#define SR_LCR_CS5 0x00
#define SR_LCR_CS6 0x01
#define SR_LCR_CS7 0x02
@ -157,16 +165,16 @@ typedef struct _WORKITEM_DATA
#define SR_LCR_PSP 0x38
#define SR_LCR_BRK 0x40
#define SR_LCR_DLAB 0x80
#define SER_MCR(x) ((x)+4)
#define SER_MCR(x) ((x)+4) /* Modem Control Register */
#define SR_MCR_DTR 0x01
#define SR_MCR_RTS 0x02
#define SER_LSR(x) ((x)+5)
#define SER_LSR(x) ((x)+5) /* Line Status Register */
#define SR_LSR_DR 0x01
#define SR_LSR_TBE 0x20
#define SER_MSR(x) ((x)+6)
#define SER_MSR(x) ((x)+6) /* Modem Status Register */
#define SR_MSR_CTS 0x10
#define SR_MSR_DSR 0x20
#define SER_SCR(x) ((x)+7)
#define SER_SCR(x) ((x)+7) /* Scratch Pad Register */
/************************************ circularbuffer.c */