From 7e613b4759303d69a721d012dac36a87d75001a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Thu, 28 Aug 2014 18:39:21 +0200 Subject: [PATCH] pc_serial: convert irq handler to use DPC Let's hope we won't loose data because it. We have to cache the value of IIR read from IsInterruptPending(), because some conditions are acknowledged only by reading it... --- .../kernel/drivers/ports/pc_serial/Driver.cpp | 54 +++++++++++++------ .../drivers/ports/pc_serial/SerialDevice.cpp | 21 +++++++- .../drivers/ports/pc_serial/SerialDevice.h | 4 ++ 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp b/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp index e0a56a7ee6..feae10c824 100644 --- a/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp +++ b/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp @@ -10,6 +10,7 @@ * Distributed under the terms of the MIT License. */ #include +#include #include #include #include @@ -27,6 +28,8 @@ config_manager_for_driver_module_info *gConfigManagerModule = NULL; isa_module_info *gISAModule = NULL; pci_module_info *gPCIModule = NULL; tty_module_info *gTTYModule = NULL; +dpc_module_info *gDPCModule = NULL; +void* gDPCHandle = NULL; sem_id gDriverLock = -1; bool gHandleISA = false; @@ -653,6 +656,10 @@ init_driver() TRACE_FUNCALLS("> init_driver()\n"); + status = get_module(B_DPC_MODULE_NAME, (module_info **)&gDPCModule); + if (status < B_OK) + goto err_dpc; + status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule); if (status < B_OK) goto err_tty; @@ -670,6 +677,11 @@ init_driver() if (status < B_OK) goto err_cm; + status = gDPCModule->new_dpc_queue(&gDPCHandle, "pc_serial irq", + B_REAL_TIME_PRIORITY); + if (status != B_OK) + goto err_dpcq; + for (int32 i = 0; i < DEVICES_COUNT; i++) gSerialDevices[i] = NULL; @@ -697,6 +709,9 @@ init_driver() //err_none: delete_sem(gDriverLock); err_sem: + gDPCModule->delete_dpc_queue(gDPCHandle); + gDPCHandle = NULL; +err_dpcq: put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME); err_cm: put_module(B_ISA_MODULE_NAME); @@ -705,6 +720,8 @@ err_isa: err_pci: put_module(B_TTY_MODULE_NAME); err_tty: + put_module(B_DPC_MODULE_NAME); +err_dpc: TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status)); return status; } @@ -735,10 +752,13 @@ uninit_driver() free(gDeviceNames[i]); delete_sem(gDriverLock); + gDPCModule->delete_dpc_queue(gDPCHandle); + gDPCHandle = NULL; put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME); put_module(B_ISA_MODULE_NAME); put_module(B_PCI_MODULE_NAME); put_module(B_TTY_MODULE_NAME); + put_module(B_DPC_MODULE_NAME); TRACE_FUNCRET("< uninit_driver() returns\n"); } @@ -764,28 +784,32 @@ pc_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length) } +static void +pc_serial_dpc(void *arg) +{ + SerialDevice *master = (SerialDevice *)arg; + TRACE_FUNCALLS("> pc_serial_dpc(%p)\n", arg); + master->InterruptHandler(); +} + + int32 pc_serial_interrupt(void *arg) { - int32 ret; - SerialDevice *master = (SerialDevice *)arg; + SerialDevice *device = (SerialDevice *)arg; TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg); - if (!master) + if (!device) return B_UNHANDLED_INTERRUPT; - ret = master->InterruptHandler(); - return ret; - - - for (int32 i = 0; i < DEVICES_COUNT; i++) { - if (gSerialDevices[i] && gSerialDevices[i]->Master() == master) { - ret = gSerialDevices[i]->InterruptHandler(); - // XXX: handle more than 1 ? - if (ret != B_UNHANDLED_INTERRUPT) { - TRACE_FUNCRET("< pc_serial_interrupt() returns: true\n"); - return ret; - } + if (device->IsInterruptPending()) { + status_t err; + err = gDPCModule->queue_dpc(gDPCHandle, pc_serial_dpc, device); + if (err != B_OK) + dprintf(DRIVER_NAME ": error queing irq: %s\n", strerror(err)); + else { + TRACE_FUNCRET("< pc_serial_interrupt() returns: handled\n"); + return B_HANDLED_INTERRUPT; } } diff --git a/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp b/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp index c16123ca0c..af4fe7eb3e 100644 --- a/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp +++ b/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp @@ -29,6 +29,7 @@ SerialDevice::SerialDevice(const struct serial_support_descriptor *device, fIOBase(ioBase), fIRQ(irq), fMaster(master), + fCachedIIR(0x1), fReadBufferAvail(0), fReadBufferIn(0), fReadBufferOut(0), @@ -320,6 +321,19 @@ SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length) } +bool +SerialDevice::IsInterruptPending() +{ + TRACE(("IsInterruptPending()\n")); + + // because reading the IIR acknowledges some IRQ conditions, + // the next time we'll read we'll miss the IRQ condition + // so we just cache the value for the real handler + fCachedIIR = ReadReg8(IIR); + return ((fCachedIIR & IIR_PENDING) == 0); // 0 means yes +} + + int32 SerialDevice::InterruptHandler() { @@ -329,7 +343,9 @@ SerialDevice::InterruptHandler() uint8 iir, lsr, msr; TRACE(("InterruptHandler()\n")); - while (((iir = ReadReg8(IIR)) & IIR_PENDING) == 0) { // 0 means yes + // start with the first (cached) irq condition + iir = fCachedIIR; + while ((iir & IIR_PENDING) == 0) { // 0 means yes int fifoavail = 1; int avail; int i; @@ -399,6 +415,9 @@ SerialDevice::InterruptHandler() } ret = B_HANDLED_INTERRUPT; TRACE(("IRQ:h\n")); + + // check the next IRQ condition + iir = ReadReg8(IIR); } TRACE_FUNCRET("< IRQ:%d\n", ret); diff --git a/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.h b/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.h index a01c0c0f4a..52d92ccdbb 100644 --- a/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.h +++ b/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.h @@ -46,6 +46,7 @@ static SerialDevice * MakeDevice(struct serial_config_descriptor bool Service(struct tty *tty, uint32 op, void *buffer, size_t length); + bool IsInterruptPending(); int32 InterruptHandler(); status_t Open(uint32 flags); @@ -112,6 +113,9 @@ static void InterruptCallbackFunction(void *cookie, /* line coding */ //usb_serial_line_coding fLineCoding; + /* deferred interrupt */ + uint8 fCachedIIR; // cached IRQ condition + /* data buffers */ char fReadBuffer[DEF_BUFFER_SIZE]; int32 fReadBufferAvail;