pc_serial: WIP: kinda supports writes through tty module

It's still not fully working, and the tty module doesn't help
since it's not callable from an irq handler.
This commit is contained in:
François Revol 2014-08-28 16:53:48 +02:00
parent 4a02fc4f42
commit c6870d53dc
2 changed files with 120 additions and 70 deletions

View File

@ -12,6 +12,8 @@
#include "SerialDevice.h" #include "SerialDevice.h"
#include "UART.h" #include "UART.h"
#include <sys/ioctl.h>
SerialDevice::SerialDevice(const struct serial_support_descriptor *device, SerialDevice::SerialDevice(const struct serial_support_descriptor *device,
uint32 ioBase, uint32 irq, const SerialDevice *master) uint32 ioBase, uint32 irq, const SerialDevice *master)
: /*fSupportDescriptor(device->descriptor), : /*fSupportDescriptor(device->descriptor),
@ -30,9 +32,11 @@ SerialDevice::SerialDevice(const struct serial_support_descriptor *device,
fReadBufferAvail(0), fReadBufferAvail(0),
fReadBufferIn(0), fReadBufferIn(0),
fReadBufferOut(0), fReadBufferOut(0),
fReadBufferSem(-1),
fWriteBufferAvail(0), fWriteBufferAvail(0),
fWriteBufferIn(0), fWriteBufferIn(0),
fWriteBufferOut(0), fWriteBufferOut(0),
fWriteBufferSem(-1),
fDoneRead(-1), fDoneRead(-1),
fDoneWrite(-1), fDoneWrite(-1),
fControlOut(0), fControlOut(0),
@ -44,6 +48,8 @@ SerialDevice::SerialDevice(const struct serial_support_descriptor *device,
fDeviceThread(-1), fDeviceThread(-1),
fStopDeviceThread(false) fStopDeviceThread(false)
{ {
memset(fReadBuffer, 'z', DEF_BUFFER_SIZE);
memset(fWriteBuffer, 'z', DEF_BUFFER_SIZE);
} }
@ -51,18 +57,18 @@ SerialDevice::~SerialDevice()
{ {
Removed(); Removed();
if (fDoneRead >= B_OK) if (fReadBufferSem >= B_OK)
delete_sem(fDoneRead); delete_sem(fReadBufferSem);
if (fDoneWrite >= B_OK) if (fWriteBufferSem >= B_OK)
delete_sem(fDoneWrite); delete_sem(fWriteBufferSem);
} }
status_t status_t
SerialDevice::Init() SerialDevice::Init()
{ {
fDoneRead = create_sem(0, "usb_serial:done_read"); fReadBufferSem = create_sem(0, "pc_serial:done_read");
fDoneWrite = create_sem(0, "usb_serial:done_write"); fWriteBufferSem = create_sem(0, "pc_serial:done_write");
// disable DLAB // disable DLAB
WriteReg8(LCR, 0); WriteReg8(LCR, 0);
@ -204,6 +210,7 @@ SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length)
uint8 msr; uint8 msr;
status_t err; status_t err;
TRACE("%s(,0x%08lx,,%d)\n", __FUNCTION__, op, length);
if (tty != fMasterTTY) if (tty != fMasterTTY)
return false; return false;
@ -320,9 +327,12 @@ SerialDevice::InterruptHandler()
//XXX: what should we do here ? (certainly not use a mutex !) //XXX: what should we do here ? (certainly not use a mutex !)
uint8 iir, lsr, msr; uint8 iir, lsr, msr;
TRACE(("InterruptHandler()\n"));
while (((iir = ReadReg8(IIR)) & IIR_PENDING) == 0) { // 0 means yes while (((iir = ReadReg8(IIR)) & IIR_PENDING) == 0) { // 0 means yes
int fifoavail = 1; int fifoavail = 1;
int avail;
int i;
//DEBUG //DEBUG
// for (int count = 0; ReadReg8(LSR) & LSR_DR; count++) // for (int count = 0; ReadReg8(LSR) & LSR_DR; count++)
@ -337,24 +347,31 @@ SerialDevice::InterruptHandler()
fifoavail = 16; fifoavail = 16;
if (iir & IIR_F64EN) if (iir & IIR_F64EN)
fifoavail = 64; fifoavail = 64;
for (int i = 0; i < fifoavail; i++) { avail = atomic_get(&fWriteBufferAvail);
int chr; for (i = 0; i < fifoavail && i < avail; i++) {
chr = 'H';//XXX: what should we do here ? (certainly not call tty_read() !) uint8 chr = fWriteBuffer[fWriteBufferOut];
if (chr < 0) {
//WriteReg8(THB, (uint8)chr);
break;
}
WriteReg8(THB, (uint8)chr); WriteReg8(THB, (uint8)chr);
fWriteBufferOut++;
fWriteBufferOut %= DEF_BUFFER_SIZE;
} }
atomic_add(&fWriteBufferAvail, -i);
release_sem_etc(fWriteBufferSem, 1,
B_DO_NOT_RESCHEDULE | B_RELEASE_IF_WAITING_ONLY);
break; break;
case IIR_TO: case IIR_TO:
case IIR_TO | IIR_RDA: case IIR_TO | IIR_RDA:
// timeout: FALLTHROUGH // timeout: FALLTHROUGH
case IIR_RDA: case IIR_RDA:
TRACE(("IIR_TO/RDA\n")); TRACE(("IIR_TO/RDA\n"));
// while data is ready... get it // while data is ready... and we have room for it, get it
while (ReadReg8(LSR) & LSR_DR) avail = DEF_BUFFER_SIZE - atomic_get(&fReadBufferAvail);
ReadReg8(RBR);//XXX: what should we do here ? (certainly not call tty_write() !) for (i = 0; i < avail && (ReadReg8(LSR) & LSR_DR); i++) {
fReadBuffer[fReadBufferIn] = ReadReg8(RBR);
fReadBufferIn++;
fReadBufferIn %= DEF_BUFFER_SIZE;
}
atomic_add(&fReadBufferAvail, i);
break; break;
case IIR_RLS: case IIR_RLS:
TRACE(("IIR_RLS\n")); TRACE(("IIR_RLS\n"));
@ -384,8 +401,6 @@ SerialDevice::InterruptHandler()
TRACE(("IRQ:h\n")); TRACE(("IRQ:h\n"));
} }
//XXX: what should we do here ? (certainly not use a mutex !)
TRACE_FUNCRET("< IRQ:%d\n", ret); TRACE_FUNCRET("< IRQ:%d\n", ret);
return ret; return ret;
} }
@ -436,13 +451,17 @@ SerialDevice::Open(uint32 flags)
ResetDevice(); ResetDevice();
//XXX: we shouldn't have to do this!
bool en = true;
Service(fMasterTTY, TTYENABLE, &en, sizeof(en));
if (status < B_OK) { if (status < B_OK) {
TRACE_ALWAYS("open: failed to open tty\n"); TRACE_ALWAYS("open: failed to open tty\n");
return status; return status;
} }
#if 0 #if 0
fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread", fDeviceThread = spawn_kernel_thread(_DeviceThread, "usb_serial device thread",
B_NORMAL_PRIORITY, this); B_NORMAL_PRIORITY, this);
if (fDeviceThread < B_OK) { if (fDeviceThread < B_OK) {
@ -485,64 +504,55 @@ SerialDevice::Read(char *buffer, size_t *numBytes)
status_t status_t
SerialDevice::Write(const char *buffer, size_t *numBytes) SerialDevice::Write(const char *buffer, size_t *numBytes)
{ {
//size_t bytesLeft = *numBytes; TRACE("%s(,&%d)\n", __FUNCTION__, *numBytes);
//*numBytes = 0;
status_t status = EINVAL;
//XXX: WTF tty_write() is not for write() hook ?
//status = gTTYModule->tty_write(fSystemTTYCookie, buffer, numBytes);
#if 0
status_t status = mutex_lock(&fWriteLock);
if (status != B_OK) {
TRACE_ALWAYS("write: failed to get write lock\n");
return status;
}
if (fDeviceRemoved) { if (fDeviceRemoved) {
mutex_unlock(&fWriteLock); *numBytes = 0;
return B_DEV_NOT_READY; return B_DEV_NOT_READY;
} }
size_t bytesLeft = *numBytes;
*numBytes = 0;
while (bytesLeft > 0) { while (bytesLeft > 0) {
size_t length = MIN(bytesLeft, fWriteBufferSize); size_t length = MIN(bytesLeft, 256);
size_t packetLength = length; // TODO: This is an ugly hack; We use a small buffer size so that
OnWrite(buffer, &length, &packetLength); // we don't overrun the tty line buffer and cause it to block. While
// that isn't a problem, we shouldn't just hardcode the value here.
status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer, TRACE("%s: tty_write(,&%d)\n", __FUNCTION__, length);
packetLength, WriteCallbackFunction, this); status_t result = gTTYModule->tty_write(fSystemTTYCookie, buffer,
if (status < B_OK) { &length);
TRACE_ALWAYS("write: queueing failed with status 0x%08x\n", status); if (result != B_OK) {
break; TRACE_ALWAYS("failed to write to tty: %s\n", strerror(result));
} return result;
status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK) {
TRACE_ALWAYS("write: failed to get write done sem 0x%08x\n", status);
break;
}
if (fStatusWrite != B_OK) {
TRACE("write: device status error 0x%08x\n", fStatusWrite);
status = gUSBModule->clear_feature(fWritePipe,
USB_FEATURE_ENDPOINT_HALT);
if (status < B_OK) {
TRACE_ALWAYS("write: failed to clear device halt\n");
status = B_ERROR;
break;
}
continue;
} }
buffer += length; buffer += length;
*numBytes += length; *numBytes += length;
bytesLeft -= length; bytesLeft -= length;
while (true) {
// Write to the device as long as there's anything in the tty buffer
int readable = 0;
gTTYModule->tty_control(fDeviceTTYCookie, FIONREAD, &readable,
sizeof(readable));
TRACE("%s: FIONREAD: %d\n", __FUNCTION__, readable);
if (readable == 0)
break;
result = _WriteToDevice();
if (result != B_OK) {
TRACE_ALWAYS("failed to write to device: %s\n",
strerror(result));
return result;
}
}
} }
mutex_unlock(&fWriteLock); if (*numBytes > 0)
#endif return B_OK;
return status;
return B_ERROR;
} }
@ -595,6 +605,10 @@ SerialDevice::Close()
#endif #endif
} }
//XXX: we shouldn't have to do this!
bool en = false;
Service(fMasterTTY, TTYENABLE, &en, sizeof(en));
gTTYModule->tty_destroy_cookie(fSystemTTYCookie); gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
gTTYModule->tty_destroy_cookie(fDeviceTTYCookie); gTTYModule->tty_destroy_cookie(fDeviceTTYCookie);
@ -633,8 +647,8 @@ SerialDevice::Removed()
gUSBModule->cancel_queued_transfers(fControlPipe); gUSBModule->cancel_queued_transfers(fControlPipe);
#endif #endif
//int32 result = B_OK; int32 result = B_OK;
//wait_for_thread(fDeviceThread, &result); wait_for_thread(fDeviceThread, &result);
fDeviceThread = -1; fDeviceThread = -1;
} }
@ -695,7 +709,7 @@ SerialDevice::OnClose()
int32 int32
SerialDevice::DeviceThread(void *data) SerialDevice::_DeviceThread(void *data)
{ {
#if 0 #if 0
SerialDevice *device = (SerialDevice *)data; SerialDevice *device = (SerialDevice *)data;
@ -753,6 +767,39 @@ SerialDevice::DeviceThread(void *data)
} }
status_t
SerialDevice::_WriteToDevice()
{
char *buffer = &fWriteBuffer[fWriteBufferIn];
size_t bytesLeft = DEF_BUFFER_SIZE - atomic_get(&fWriteBufferAvail);
bytesLeft = MIN(bytesLeft, DEF_BUFFER_SIZE - fWriteBufferIn);
TRACE("%s: in %d left %d\n", __FUNCTION__, fWriteBufferIn, bytesLeft);
status_t status = gTTYModule->tty_read(fDeviceTTYCookie, buffer,
&bytesLeft);
TRACE("%s: tty_read: %d\n", __FUNCTION__, bytesLeft);
if (status != B_OK) {
TRACE_ALWAYS("write to device: failed to read from TTY: %s\n",
strerror(status));
return status;
}
fWriteBufferIn += bytesLeft;
fWriteBufferIn %= DEF_BUFFER_SIZE;
atomic_add(&fWriteBufferAvail, bytesLeft);
// XXX: WTF: this ought to be done by the tty module calling service_func!
// enable irqs
Service(fMasterTTY, TTYOSTART, NULL, 0);
status = acquire_sem_etc(fWriteBufferSem, 1, B_CAN_INTERRUPT, 0);
if (status != B_OK) {
TRACE_ALWAYS("write to device: failed to acquire sem: %s\n",
strerror(status));
return status;
}
return B_OK;
}
void void
SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data, SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength) uint32 actualLength)

View File

@ -79,7 +79,8 @@ virtual void OnClose();
uint32 IRQ() const { return fIRQ; }; uint32 IRQ() const { return fIRQ; };
private: private:
static int32 DeviceThread(void *data); static int32 _DeviceThread(void *data);
status_t _WriteToDevice();
static void ReadCallbackFunction(void *cookie, static void ReadCallbackFunction(void *cookie,
int32 status, void *data, int32 status, void *data,
@ -113,13 +114,15 @@ static void InterruptCallbackFunction(void *cookie,
/* data buffers */ /* data buffers */
char fReadBuffer[DEF_BUFFER_SIZE]; char fReadBuffer[DEF_BUFFER_SIZE];
uint32 fReadBufferAvail; int32 fReadBufferAvail;
uint32 fReadBufferIn; uint32 fReadBufferIn;
uint32 fReadBufferOut; uint32 fReadBufferOut;
sem_id fReadBufferSem;
char fWriteBuffer[DEF_BUFFER_SIZE]; char fWriteBuffer[DEF_BUFFER_SIZE];
uint32 fWriteBufferAvail; int32 fWriteBufferAvail;
uint32 fWriteBufferIn; uint32 fWriteBufferIn;
uint32 fWriteBufferOut; uint32 fWriteBufferOut;
sem_id fWriteBufferSem;
/* variables used in callback functionality */ /* variables used in callback functionality */
size_t fActualLengthRead; size_t fActualLengthRead;