* Finish the port to the new tty module. Back when I left off last time it was

only halfway finished. The way it is supposed to work is that there are two
  tty cookies, representing the system and the device side. Reads and writes
  coming from the system and going to the device are using the system cookie
  while reads and writes coming from the device use the device cookie.
* Move writing to the device into an output thread, similar to how reading from
  the device works. This isn't necessarily a good idea and might be moved back
  into the write hook again to ensure that writes can be made blocking. Right
  now if you just write and then close, the writes will most likely be canceled
  before every going out to the device.
* Removed the read and write lock mutex. They aren't necessary as the tty layer
  will serialize the reads and writes anyway.
* Made simply copying the data to the write buffer the default implementation of
  the OnWrite callback and removed the OnWrite in ACMDevice. The ProlificDevice
  didn't provide an OnWrite hook before, so it would've never written anything.
* Break out the baud index to speed mapping into an inline function. Since the
  defines might change in termios.h just adding an array is a bit fragile (it
  already missed one entry and would've been broken for certain speeds).

This should make usb_serial usable for some (most?) cases. I've tested this with
an Arduino board that uses an FTDI interface. As mentioned above, since writes
currently just write into the tty layer and don't wait for the data to be
flushed, and since close doesn't wait for the output buffers to be drained
either, a simple write and close (as in "echo a > /dev/ports/usb0") will in most
cases cancel the output before it is written to the device. I'm looking into
a few ways to fix that next.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42089 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2011-06-10 17:35:46 +00:00
parent 5cc760197a
commit 36986acbbd
4 changed files with 215 additions and 172 deletions
@@ -134,10 +134,3 @@ ACMDevice::SetControlLineState(uint16 state)
TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status); TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status);
return status; return status;
} }
void
ACMDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
{
memcpy(WriteBuffer(), buffer, *numBytes);
}
@@ -19,9 +19,6 @@ virtual status_t AddDevice(const usb_configuration_info *config);
virtual status_t SetLineCoding(usb_cdc_line_coding *coding); virtual status_t SetLineCoding(usb_cdc_line_coding *coding);
virtual status_t SetControlLineState(uint16 state); virtual status_t SetControlLineState(uint16 state);
virtual void OnWrite(const char *buffer, size_t *numBytes,
size_t *packetBytes);
}; };
#endif //_USB_ACM_H_ #endif //_USB_ACM_H_
@@ -29,6 +29,8 @@ SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
fBufferArea(-1), fBufferArea(-1),
fReadBuffer(NULL), fReadBuffer(NULL),
fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)), fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fOutputBuffer(NULL),
fOutputBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fWriteBuffer(NULL), fWriteBuffer(NULL),
fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)), fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fInterruptBuffer(NULL), fInterruptBuffer(NULL),
@@ -39,12 +41,12 @@ SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
fInputStopped(false), fInputStopped(false),
fMasterTTY(NULL), fMasterTTY(NULL),
fSlaveTTY(NULL), fSlaveTTY(NULL),
fTTYCookie(NULL), fSystemTTYCookie(NULL),
fDeviceThread(-1), fDeviceTTYCookie(NULL),
fStopDeviceThread(false) fInputThread(-1),
fOutputThread(-1),
fStopThreads(false)
{ {
mutex_init(&fReadLock, "usb_serial read lock");
mutex_init(&fWriteLock, "usb_serial write lock");
} }
@@ -59,9 +61,6 @@ SerialDevice::~SerialDevice()
if (fBufferArea >= B_OK) if (fBufferArea >= B_OK)
delete_area(fBufferArea); delete_area(fBufferArea);
mutex_destroy(&fReadLock);
mutex_destroy(&fWriteLock);
} }
@@ -70,16 +69,15 @@ SerialDevice::Init()
{ {
fDoneRead = create_sem(0, "usb_serial:done_read"); fDoneRead = create_sem(0, "usb_serial:done_read");
fDoneWrite = create_sem(0, "usb_serial:done_write"); fDoneWrite = create_sem(0, "usb_serial:done_write");
mutex_init(&fReadLock, "usb_serial:read_lock");
mutex_init(&fWriteLock, "usb_serial:write_lock");
size_t totalBuffers = fReadBufferSize + fWriteBufferSize size_t totalBuffers = fReadBufferSize + fOutputBufferSize + fWriteBufferSize
+ fInterruptBufferSize; + fInterruptBufferSize;
fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer, fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer,
B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS, B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA); B_READ_AREA | B_WRITE_AREA);
fWriteBuffer = fReadBuffer + fReadBufferSize; fOutputBuffer = fReadBuffer + fReadBufferSize;
fWriteBuffer = fOutputBuffer + fOutputBufferSize;
fInterruptBuffer = fWriteBuffer + fWriteBufferSize; fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
return B_OK; return B_OK;
} }
@@ -106,47 +104,53 @@ SerialDevice::SetWritePipe(usb_pipe handle)
} }
inline int32
baud_index_to_speed(int index)
{
switch (index) {
case B0: return 0;
case B50: return 50;
case B75: return 75;
case B110: return 110;
case B134: return 134;
case B150: return 150;
case B200: return 200;
case B300: return 300;
case B600: return 600;
case B1200: return 1200;
case B1800: return 1800;
case B2400: return 2400;
case B4800: return 4800;
case B9600: return 9600;
case B19200: return 19200;
case B31250: return 31250;
case B38400: return 38400;
case B57600: return 57600;
case B115200: return 115200;
case B230400: return 230400;
}
TRACE_ALWAYS("invalid baud index %d\n", index);
return -1;
}
void void
SerialDevice::SetModes(struct termios *tios) SerialDevice::SetModes(struct termios *tios)
{ {
uint16 newControl = fControlOut; uint16 newControl = fControlOut;
TRACE_FUNCRES(trace_termios, tios); TRACE_FUNCRES(trace_termios, tios);
static uint32 baudRates[] = { int32 speed = baud_index_to_speed(tios->c_cflag & CBAUD);
0x00000000, //B0 if (speed < 0)
0x00000032, //B50 speed = baud_index_to_speed(tios->c_ispeed);
0x0000004B, //B75 if (speed < 0)
0x0000006E, //B110 speed = baud_index_to_speed(tios->c_ospeed);
0x00000086, //B134 if (speed < 0)
0x00000096, //B150 speed = 19200;
0x000000C8, //B200
0x0000012C, //B300
0x00000258, //B600
0x000004B0, //B1200
0x00000708, //B1800
0x00000960, //B2400
0x000012C0, //B4800
0x00002580, //B9600
0x00004B00, //B19200
0x00009600, //B38400
0x0000E100, //B57600
0x0001C200, //B115200
0x00038400, //B230400
0x00070800, //460800
0x000E1000, //921600
};
uint32 baudCount = sizeof(baudRates) / sizeof(baudRates[0]);
uint32 baudIndex = tios->c_cflag & CBAUD;
if (baudIndex == 0)
baudIndex = tios->c_ispeed;
if (baudIndex == 0)
baudIndex = tios->c_ospeed;
if (baudIndex > baudCount)
baudIndex = baudCount - 1;
usb_cdc_line_coding lineCoding; usb_cdc_line_coding lineCoding;
lineCoding.speed = baudRates[baudIndex]; lineCoding.speed = speed;
lineCoding.stopbits = (tios->c_cflag & CSTOPB) lineCoding.stopbits = (tios->c_cflag & CSTOPB)
? USB_CDC_LINE_CODING_2_STOPBITS : USB_CDC_LINE_CODING_1_STOPBIT; ? USB_CDC_LINE_CODING_2_STOPBITS : USB_CDC_LINE_CODING_1_STOPBIT;
@@ -190,8 +194,8 @@ SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length)
bool enable = *(bool *)buffer; bool enable = *(bool *)buffer;
TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis"); TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis");
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWDCD, enable); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, enable);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWCTS, enable); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, enable);
fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR
| USB_CDC_CONTROL_SIGNAL_STATE_RTS : 0; | USB_CDC_CONTROL_SIGNAL_STATE_RTS : 0;
@@ -202,19 +206,19 @@ SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length)
case TTYISTOP: case TTYISTOP:
fInputStopped = *(bool *)buffer; fInputStopped = *(bool *)buffer;
TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not "); TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not ");
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWCTS, gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
!fInputStopped); !fInputStopped);
return true; return true;
case TTYGETSIGNALS: case TTYGETSIGNALS:
TRACE("TTYGETSIGNALS\n"); TRACE("TTYGETSIGNALS\n");
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWDCD, gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD,
(fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR (fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR
| USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0); | USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWCTS, gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
!fInputStopped); !fInputStopped);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWDSR, false); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDSR, false);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWRI, false); gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWRI, false);
return true; return true;
case TTYSETMODES: case TTYSETMODES:
@@ -263,16 +267,27 @@ SerialDevice::Open(uint32 flags)
return B_NO_MEMORY; return B_NO_MEMORY;
} }
fSlaveTTY = gTTYModule->tty_create(NULL, false); fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false);
if (fSlaveTTY == NULL) { if (fSlaveTTY == NULL) {
TRACE_ALWAYS("open: failed to init slave tty\n"); TRACE_ALWAYS("open: failed to init slave tty\n");
gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fMasterTTY);
return B_NO_MEMORY; return B_NO_MEMORY;
} }
fTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, flags); fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY,
if (fTTYCookie == NULL) { flags);
TRACE_ALWAYS("open: failed to init tty cookie\n"); if (fSystemTTYCookie == NULL) {
TRACE_ALWAYS("open: failed to init system tty cookie\n");
gTTYModule->tty_destroy(fMasterTTY);
gTTYModule->tty_destroy(fSlaveTTY);
return B_NO_MEMORY;
}
fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY,
flags);
if (fDeviceTTYCookie == NULL) {
TRACE_ALWAYS("open: failed to init device tty cookie\n");
gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fMasterTTY);
gTTYModule->tty_destroy(fSlaveTTY); gTTYModule->tty_destroy(fSlaveTTY);
return B_NO_MEMORY; return B_NO_MEMORY;
@@ -280,22 +295,32 @@ SerialDevice::Open(uint32 flags)
ResetDevice(); ResetDevice();
fDeviceThread = spawn_kernel_thread(DeviceThread, fStopThreads = false;
"usb_serial device thread", B_NORMAL_PRIORITY, this);
if (fDeviceThread < B_OK) { fInputThread = spawn_kernel_thread(_InputThread,
TRACE_ALWAYS("open: failed to spawn kernel thread\n"); "usb_serial input thread", B_NORMAL_PRIORITY, this);
return fDeviceThread; if (fInputThread < 0) {
TRACE_ALWAYS("open: failed to spawn input thread\n");
return fInputThread;
} }
resume_thread(fDeviceThread); resume_thread(fInputThread);
fOutputThread = spawn_kernel_thread(_OutputThread,
"usb_serial output thread", B_NORMAL_PRIORITY, this);
if (fOutputThread < 0) {
TRACE_ALWAYS("open: failed to spawn output thread\n");
return fOutputThread;
}
resume_thread(fOutputThread);
fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR
| USB_CDC_CONTROL_SIGNAL_STATE_RTS; | USB_CDC_CONTROL_SIGNAL_STATE_RTS;
SetControlLineState(fControlOut); SetControlLineState(fControlOut);
status_t status = gUSBModule->queue_interrupt(fControlPipe, status_t status = gUSBModule->queue_interrupt(fControlPipe,
fInterruptBuffer, fInterruptBufferSize, InterruptCallbackFunction, fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction,
this); this);
if (status < B_OK) if (status < B_OK)
TRACE_ALWAYS("failed to queue initial interrupt\n"); TRACE_ALWAYS("failed to queue initial interrupt\n");
@@ -313,75 +338,19 @@ SerialDevice::Read(char *buffer, size_t *numBytes)
return B_DEV_NOT_READY; return B_DEV_NOT_READY;
} }
status_t status = mutex_lock(&fReadLock); return gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes);
if (status != B_OK) {
TRACE_ALWAYS("read: failed to get read lock\n");
*numBytes = 0;
return status;
}
status = gTTYModule->tty_read(fTTYCookie, buffer, numBytes);
mutex_unlock(&fReadLock);
return status;
} }
status_t status_t
SerialDevice::Write(const char *buffer, size_t *numBytes) SerialDevice::Write(const char *buffer, size_t *numBytes)
{ {
size_t bytesLeft = *numBytes;
*numBytes = 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;
} }
while (bytesLeft > 0) { return gTTYModule->tty_write(fSystemTTYCookie, buffer, numBytes);
size_t length = MIN(bytesLeft, fWriteBufferSize);
size_t packetLength = length;
OnWrite(buffer, &length, &packetLength);
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;
}
buffer += length;
*numBytes += length;
bytesLeft -= length;
}
mutex_unlock(&fWriteLock);
return status;
} }
@@ -391,7 +360,9 @@ SerialDevice::Control(uint32 op, void *arg, size_t length)
if (fDeviceRemoved) if (fDeviceRemoved)
return B_DEV_NOT_READY; return B_DEV_NOT_READY;
return gTTYModule->tty_control(fTTYCookie, op, arg, length); gTTYModule->tty_control(fSystemTTYCookie, op, arg, length);
gTTYModule->tty_control(fDeviceTTYCookie, op, arg, length);
return B_OK;
} }
@@ -401,7 +372,7 @@ SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
if (fDeviceRemoved) if (fDeviceRemoved)
return B_DEV_NOT_READY; return B_DEV_NOT_READY;
return gTTYModule->tty_select(fTTYCookie, event, ref, sync); return gTTYModule->tty_select(fSystemTTYCookie, event, ref, sync);
} }
@@ -411,7 +382,7 @@ SerialDevice::DeSelect(uint8 event, selectsync *sync)
if (fDeviceRemoved) if (fDeviceRemoved)
return B_DEV_NOT_READY; return B_DEV_NOT_READY;
return gTTYModule->tty_deselect(fTTYCookie, event, sync); return gTTYModule->tty_deselect(fSystemTTYCookie, event, sync);
} }
@@ -420,13 +391,29 @@ SerialDevice::Close()
{ {
OnClose(); OnClose();
// TODO: wait for the output buffer to be flushed?
fStopThreads = true;
fInputStopped = false;
if (!fDeviceRemoved) { if (!fDeviceRemoved) {
gUSBModule->cancel_queued_transfers(fReadPipe); gUSBModule->cancel_queued_transfers(fReadPipe);
gUSBModule->cancel_queued_transfers(fWritePipe); gUSBModule->cancel_queued_transfers(fWritePipe);
gUSBModule->cancel_queued_transfers(fControlPipe); gUSBModule->cancel_queued_transfers(fControlPipe);
} }
gTTYModule->tty_destroy_cookie(fTTYCookie); int32 result = B_OK;
wait_for_thread(fInputThread, &result);
fInputThread = -1;
wait_for_thread(fOutputThread, &result);
fOutputThread = -1;
gTTYModule->tty_destroy_cookie(fSystemTTYCookie);
gTTYModule->tty_destroy_cookie(fDeviceTTYCookie);
gTTYModule->tty_destroy(fMasterTTY);
gTTYModule->tty_destroy(fSlaveTTY);
fDeviceOpen = false; fDeviceOpen = false;
return B_OK; return B_OK;
@@ -436,8 +423,6 @@ SerialDevice::Close()
status_t status_t
SerialDevice::Free() SerialDevice::Free()
{ {
gTTYModule->tty_destroy(fMasterTTY);
gTTYModule->tty_destroy(fSlaveTTY);
return B_OK; return B_OK;
} }
@@ -452,18 +437,18 @@ SerialDevice::Removed()
fDeviceRemoved = true; fDeviceRemoved = true;
// we need to ensure that we do not use the device anymore // we need to ensure that we do not use the device anymore
fStopDeviceThread = true; fStopThreads = true;
fInputStopped = false; fInputStopped = false;
gUSBModule->cancel_queued_transfers(fReadPipe); gUSBModule->cancel_queued_transfers(fReadPipe);
gUSBModule->cancel_queued_transfers(fWritePipe); gUSBModule->cancel_queued_transfers(fWritePipe);
gUSBModule->cancel_queued_transfers(fControlPipe); gUSBModule->cancel_queued_transfers(fControlPipe);
int32 result = B_OK; int32 result = B_OK;
wait_for_thread(fDeviceThread, &result); wait_for_thread(fInputThread, &result);
fDeviceThread = -1; fInputThread = -1;
mutex_lock(&fWriteLock); wait_for_thread(fOutputThread, &result);
mutex_unlock(&fWriteLock); fOutputThread = -1;
} }
@@ -509,7 +494,7 @@ SerialDevice::OnRead(char **buffer, size_t *numBytes)
void void
SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes) SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
{ {
// default implementation - does nothing memcpy(fWriteBuffer, buffer, *numBytes);
} }
@@ -521,35 +506,37 @@ SerialDevice::OnClose()
int32 int32
SerialDevice::DeviceThread(void *data) SerialDevice::_InputThread(void *data)
{ {
SerialDevice *device = (SerialDevice *)data; SerialDevice *device = (SerialDevice *)data;
while (!device->fStopDeviceThread) { while (!device->fStopThreads) {
status_t status = gUSBModule->queue_bulk(device->fReadPipe, status_t status = gUSBModule->queue_bulk(device->fReadPipe,
device->fReadBuffer, device->fReadBufferSize, device->fReadBuffer, device->fReadBufferSize,
device->ReadCallbackFunction, data); device->_ReadCallbackFunction, data);
if (status < B_OK) { if (status < B_OK) {
TRACE_ALWAYS("device thread: queueing failed with error: 0x%08x\n", TRACE_ALWAYS("input thread: queueing failed with error: 0x%08x\n",
status); status);
break; return status;
} }
status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0); status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK) { if (status < B_OK) {
TRACE_ALWAYS("device thread: failed to get read done sem 0x%08x\n", TRACE_ALWAYS("input thread: failed to get read done sem 0x%08x\n",
status); status);
break; return status;
} }
if (device->fStatusRead != B_OK) { if (device->fStatusRead != B_OK) {
TRACE("device thread: device status error 0x%08x\n", TRACE("input thread: device status error 0x%08x\n",
device->fStatusRead); device->fStatusRead);
if (gUSBModule->clear_feature(device->fReadPipe, if (gUSBModule->clear_feature(device->fReadPipe,
USB_FEATURE_ENDPOINT_HALT) != B_OK) { USB_FEATURE_ENDPOINT_HALT) != B_OK) {
TRACE_ALWAYS("device thread: failed to clear halt feature\n"); TRACE_ALWAYS("input thread: failed to clear halt feature\n");
break; return B_ERROR;
} }
continue;
} }
char *buffer = device->fReadBuffer; char *buffer = device->fReadBuffer;
@@ -561,7 +548,71 @@ SerialDevice::DeviceThread(void *data)
while (device->fInputStopped) while (device->fInputStopped)
snooze(100); snooze(100);
gTTYModule->tty_write(device->fTTYCookie, buffer, &readLength); status = gTTYModule->tty_write(device->fDeviceTTYCookie, buffer,
&readLength);
if (status != B_OK) {
TRACE_ALWAYS("input thread: failed to write into TTY\n");
return status;
}
}
return B_OK;
}
int32
SerialDevice::_OutputThread(void *data)
{
SerialDevice *device = (SerialDevice *)data;
while (!device->fStopThreads) {
char *buffer = device->fOutputBuffer;
size_t bytesLeft = device->fOutputBufferSize;
status_t status = gTTYModule->tty_read(device->fDeviceTTYCookie, buffer,
&bytesLeft);
if (status != B_OK) {
TRACE_ALWAYS("output thread: failed to read from TTY\n");
return status;
}
while (bytesLeft > 0) {
size_t length = MIN(bytesLeft, device->fWriteBufferSize);
size_t packetLength = length;
device->OnWrite(buffer, &length, &packetLength);
status = gUSBModule->queue_bulk(device->fWritePipe,
device->fWriteBuffer, packetLength, _WriteCallbackFunction,
device);
if (status < B_OK) {
TRACE_ALWAYS("output thread: queueing failed with status "
"0x%08x\n", status);
return status;
}
status = acquire_sem_etc(device->fDoneWrite, 1, B_CAN_INTERRUPT, 0);
if (status != B_OK) {
TRACE_ALWAYS("output thread: failed to get write done sem "
"0x%08x\n", status);
return status;
}
if (device->fStatusWrite != B_OK) {
TRACE("output thread: device status error 0x%08x\n",
device->fStatusWrite);
status = gUSBModule->clear_feature(device->fWritePipe,
USB_FEATURE_ENDPOINT_HALT);
if (status < B_OK) {
TRACE_ALWAYS("output thread: failed to clear device "
"halt\n");
return B_ERROR;
}
continue;
}
buffer += length;
bytesLeft -= length;
}
} }
return B_OK; return B_OK;
@@ -569,7 +620,7 @@ SerialDevice::DeviceThread(void *data)
void void
SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data, SerialDevice::_ReadCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength) uint32 actualLength)
{ {
TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x " TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x "
@@ -583,7 +634,7 @@ SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
void void
SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data, SerialDevice::_WriteCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength) uint32 actualLength)
{ {
TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x " TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x "
@@ -597,7 +648,7 @@ SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
void void
SerialDevice::InterruptCallbackFunction(void *cookie, int32 status, SerialDevice::_InterruptCallbackFunction(void *cookie, int32 status,
void *data, uint32 actualLength) void *data, uint32 actualLength)
{ {
TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: " TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: "
@@ -612,7 +663,7 @@ SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
if (status == B_OK && !device->fDeviceRemoved) { if (status == B_OK && !device->fDeviceRemoved) {
status = gUSBModule->queue_interrupt(device->fControlPipe, status = gUSBModule->queue_interrupt(device->fControlPipe,
device->fInterruptBuffer, device->fInterruptBufferSize, device->fInterruptBuffer, device->fInterruptBufferSize,
device->InterruptCallbackFunction, device); device->_InterruptCallbackFunction, device);
} }
} }
@@ -76,16 +76,18 @@ protected:
void SetReadBufferSize(size_t size) { fReadBufferSize = size; }; void SetReadBufferSize(size_t size) { fReadBufferSize = size; };
void SetWriteBufferSize(size_t size) { fWriteBufferSize = size; }; void SetWriteBufferSize(size_t size) { fWriteBufferSize = size; };
void SetInterruptBufferSize(size_t size) { fInterruptBufferSize = size; }; void SetInterruptBufferSize(size_t size) { fInterruptBufferSize = size; };
private:
static int32 DeviceThread(void *data);
static void ReadCallbackFunction(void *cookie, private:
static int32 _InputThread(void *data);
static int32 _OutputThread(void *data);
static void _ReadCallbackFunction(void *cookie,
int32 status, void *data, int32 status, void *data,
uint32 actualLength); uint32 actualLength);
static void WriteCallbackFunction(void *cookie, static void _WriteCallbackFunction(void *cookie,
int32 status, void *data, int32 status, void *data,
uint32 actualLength); uint32 actualLength);
static void InterruptCallbackFunction(void *cookie, static void _InterruptCallbackFunction(void *cookie,
int32 status, void *data, int32 status, void *data,
uint32 actualLength); uint32 actualLength);
@@ -108,6 +110,8 @@ static void InterruptCallbackFunction(void *cookie,
area_id fBufferArea; area_id fBufferArea;
char * fReadBuffer; char * fReadBuffer;
size_t fReadBufferSize; size_t fReadBufferSize;
char * fOutputBuffer;
size_t fOutputBufferSize;
char * fWriteBuffer; char * fWriteBuffer;
size_t fWriteBufferSize; size_t fWriteBufferSize;
char * fInterruptBuffer; char * fInterruptBuffer;
@@ -129,15 +133,13 @@ static void InterruptCallbackFunction(void *cookie,
bool fInputStopped; bool fInputStopped;
struct tty * fMasterTTY; struct tty * fMasterTTY;
struct tty * fSlaveTTY; struct tty * fSlaveTTY;
struct tty_cookie * fTTYCookie; struct tty_cookie * fSystemTTYCookie;
struct tty_cookie * fDeviceTTYCookie;
/* device thread management */ /* input/output thread management */
thread_id fDeviceThread; thread_id fInputThread;
bool fStopDeviceThread; thread_id fOutputThread;
bool fStopThreads;
/* device locks to ensure no concurent reads/writes */
mutex fReadLock;
mutex fWriteLock;
}; };
#endif // _USB_DEVICE_H_ #endif // _USB_DEVICE_H_