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

View File

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