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...
This commit is contained in:
François Revol 2014-08-28 18:39:21 +02:00
parent c6870d53dc
commit 7e613b4759
3 changed files with 63 additions and 16 deletions

View File

@ -10,6 +10,7 @@
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <dpc.h>
#include <Drivers.h>
#include <image.h>
#include <malloc.h>
@ -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;
}
}

View File

@ -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);

View File

@ -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;