diff --git a/reactos/drivers/dd/serial/misc.c b/reactos/drivers/dd/serial/misc.c index 654db30bfed..e521dee1c2a 100644 --- a/reactos/drivers/dd/serial/misc.c +++ b/reactos/drivers/dd/serial/misc.c @@ -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: diff --git a/reactos/drivers/dd/serial/pnp.c b/reactos/drivers/dd/serial/pnp.c index f1f2f39db59..08b0e879044 100644 --- a/reactos/drivers/dd/serial/pnp.c +++ b/reactos/drivers/dd/serial/pnp.c @@ -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); diff --git a/reactos/drivers/dd/serial/rw.c b/reactos/drivers/dd/serial/rw.c index adb156a6669..2ec9ddc5023 100644 --- a/reactos/drivers/dd/serial/rw.c +++ b/reactos/drivers/dd/serial/rw.c @@ -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) diff --git a/reactos/drivers/dd/serial/serial.h b/reactos/drivers/dd/serial/serial.h index 4ea209aa938..b248160ffcc 100644 --- a/reactos/drivers/dd/serial/serial.h +++ b/reactos/drivers/dd/serial/serial.h @@ -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 */