* 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);
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 SetControlLineState(uint16 state);
virtual void OnWrite(const char *buffer, size_t *numBytes,
size_t *packetBytes);
};
#endif //_USB_ACM_H_
@@ -29,6 +29,8 @@ SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
fBufferArea(-1),
fReadBuffer(NULL),
fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fOutputBuffer(NULL),
fOutputBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fWriteBuffer(NULL),
fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fInterruptBuffer(NULL),
@@ -39,12 +41,12 @@ SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
fInputStopped(false),
fMasterTTY(NULL),
fSlaveTTY(NULL),
fTTYCookie(NULL),
fDeviceThread(-1),
fStopDeviceThread(false)
fSystemTTYCookie(NULL),
fDeviceTTYCookie(NULL),
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)
delete_area(fBufferArea);
mutex_destroy(&fReadLock);
mutex_destroy(&fWriteLock);
}
@@ -70,16 +69,15 @@ SerialDevice::Init()
{
fDoneRead = create_sem(0, "usb_serial:done_read");
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;
fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer,
B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA);
fWriteBuffer = fReadBuffer + fReadBufferSize;
fOutputBuffer = fReadBuffer + fReadBufferSize;
fWriteBuffer = fOutputBuffer + fOutputBufferSize;
fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
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
SerialDevice::SetModes(struct termios *tios)
{
uint16 newControl = fControlOut;
TRACE_FUNCRES(trace_termios, tios);
static uint32 baudRates[] = {
0x00000000, //B0
0x00000032, //B50
0x0000004B, //B75
0x0000006E, //B110
0x00000086, //B134
0x00000096, //B150
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;
int32 speed = baud_index_to_speed(tios->c_cflag & CBAUD);
if (speed < 0)
speed = baud_index_to_speed(tios->c_ispeed);
if (speed < 0)
speed = baud_index_to_speed(tios->c_ospeed);
if (speed < 0)
speed = 19200;
usb_cdc_line_coding lineCoding;
lineCoding.speed = baudRates[baudIndex];
lineCoding.speed = speed;
lineCoding.stopbits = (tios->c_cflag & CSTOPB)
? 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;
TRACE("TTYENABLE: %sable\n", enable ? "en" : "dis");
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWDCD, enable);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWCTS, enable);
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD, enable);
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS, enable);
fControlOut = enable ? USB_CDC_CONTROL_SIGNAL_STATE_DTR
| 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:
fInputStopped = *(bool *)buffer;
TRACE("TTYISTOP: %sstopped\n", fInputStopped ? "" : "not ");
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWCTS,
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
!fInputStopped);
return true;
case TTYGETSIGNALS:
TRACE("TTYGETSIGNALS\n");
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWDCD,
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDCD,
(fControlOut & (USB_CDC_CONTROL_SIGNAL_STATE_DTR
| USB_CDC_CONTROL_SIGNAL_STATE_RTS)) != 0);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWCTS,
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWCTS,
!fInputStopped);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWDSR, false);
gTTYModule->tty_hardware_signal(fTTYCookie, TTYHWRI, false);
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWDSR, false);
gTTYModule->tty_hardware_signal(fSystemTTYCookie, TTYHWRI, false);
return true;
case TTYSETMODES:
@@ -263,16 +267,27 @@ SerialDevice::Open(uint32 flags)
return B_NO_MEMORY;
}
fSlaveTTY = gTTYModule->tty_create(NULL, false);
fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false);
if (fSlaveTTY == NULL) {
TRACE_ALWAYS("open: failed to init slave tty\n");
gTTYModule->tty_destroy(fMasterTTY);
return B_NO_MEMORY;
}
fTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, flags);
if (fTTYCookie == NULL) {
TRACE_ALWAYS("open: failed to init tty cookie\n");
fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY,
flags);
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(fSlaveTTY);
return B_NO_MEMORY;
@@ -280,22 +295,32 @@ SerialDevice::Open(uint32 flags)
ResetDevice();
fDeviceThread = spawn_kernel_thread(DeviceThread,
"usb_serial device thread", B_NORMAL_PRIORITY, this);
fStopThreads = false;
if (fDeviceThread < B_OK) {
TRACE_ALWAYS("open: failed to spawn kernel thread\n");
return fDeviceThread;
fInputThread = spawn_kernel_thread(_InputThread,
"usb_serial input thread", B_NORMAL_PRIORITY, this);
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
| USB_CDC_CONTROL_SIGNAL_STATE_RTS;
SetControlLineState(fControlOut);
status_t status = gUSBModule->queue_interrupt(fControlPipe,
fInterruptBuffer, fInterruptBufferSize, InterruptCallbackFunction,
fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction,
this);
if (status < B_OK)
TRACE_ALWAYS("failed to queue initial interrupt\n");
@@ -313,75 +338,19 @@ SerialDevice::Read(char *buffer, size_t *numBytes)
return B_DEV_NOT_READY;
}
status_t status = mutex_lock(&fReadLock);
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;
return gTTYModule->tty_read(fSystemTTYCookie, buffer, numBytes);
}
status_t
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) {
mutex_unlock(&fWriteLock);
*numBytes = 0;
return B_DEV_NOT_READY;
}
while (bytesLeft > 0) {
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;
return gTTYModule->tty_write(fSystemTTYCookie, buffer, numBytes);
}
@@ -391,7 +360,9 @@ SerialDevice::Control(uint32 op, void *arg, size_t length)
if (fDeviceRemoved)
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)
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)
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();
// TODO: wait for the output buffer to be flushed?
fStopThreads = true;
fInputStopped = false;
if (!fDeviceRemoved) {
gUSBModule->cancel_queued_transfers(fReadPipe);
gUSBModule->cancel_queued_transfers(fWritePipe);
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;
return B_OK;
@@ -436,8 +423,6 @@ SerialDevice::Close()
status_t
SerialDevice::Free()
{
gTTYModule->tty_destroy(fMasterTTY);
gTTYModule->tty_destroy(fSlaveTTY);
return B_OK;
}
@@ -452,18 +437,18 @@ SerialDevice::Removed()
fDeviceRemoved = true;
// we need to ensure that we do not use the device anymore
fStopDeviceThread = true;
fStopThreads = true;
fInputStopped = false;
gUSBModule->cancel_queued_transfers(fReadPipe);
gUSBModule->cancel_queued_transfers(fWritePipe);
gUSBModule->cancel_queued_transfers(fControlPipe);
int32 result = B_OK;
wait_for_thread(fDeviceThread, &result);
fDeviceThread = -1;
wait_for_thread(fInputThread, &result);
fInputThread = -1;
mutex_lock(&fWriteLock);
mutex_unlock(&fWriteLock);
wait_for_thread(fOutputThread, &result);
fOutputThread = -1;
}
@@ -509,7 +494,7 @@ SerialDevice::OnRead(char **buffer, size_t *numBytes)
void
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
SerialDevice::DeviceThread(void *data)
SerialDevice::_InputThread(void *data)
{
SerialDevice *device = (SerialDevice *)data;
while (!device->fStopDeviceThread) {
while (!device->fStopThreads) {
status_t status = gUSBModule->queue_bulk(device->fReadPipe,
device->fReadBuffer, device->fReadBufferSize,
device->ReadCallbackFunction, data);
device->_ReadCallbackFunction, data);
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);
break;
return status;
}
status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
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);
break;
return status;
}
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);
if (gUSBModule->clear_feature(device->fReadPipe,
USB_FEATURE_ENDPOINT_HALT) != B_OK) {
TRACE_ALWAYS("device thread: failed to clear halt feature\n");
break;
TRACE_ALWAYS("input thread: failed to clear halt feature\n");
return B_ERROR;
}
continue;
}
char *buffer = device->fReadBuffer;
@@ -561,7 +548,71 @@ SerialDevice::DeviceThread(void *data)
while (device->fInputStopped)
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;
@@ -569,7 +620,7 @@ SerialDevice::DeviceThread(void *data)
void
SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
SerialDevice::_ReadCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength)
{
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
SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
SerialDevice::_WriteCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength)
{
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
SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
SerialDevice::_InterruptCallbackFunction(void *cookie, int32 status,
void *data, uint32 actualLength)
{
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) {
status = gUSBModule->queue_interrupt(device->fControlPipe,
device->fInterruptBuffer, device->fInterruptBufferSize,
device->InterruptCallbackFunction, device);
device->_InterruptCallbackFunction, device);
}
}
@@ -76,16 +76,18 @@ protected:
void SetReadBufferSize(size_t size) { fReadBufferSize = size; };
void SetWriteBufferSize(size_t size) { fWriteBufferSize = 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,
uint32 actualLength);
static void WriteCallbackFunction(void *cookie,
static void _WriteCallbackFunction(void *cookie,
int32 status, void *data,
uint32 actualLength);
static void InterruptCallbackFunction(void *cookie,
static void _InterruptCallbackFunction(void *cookie,
int32 status, void *data,
uint32 actualLength);
@@ -108,6 +110,8 @@ static void InterruptCallbackFunction(void *cookie,
area_id fBufferArea;
char * fReadBuffer;
size_t fReadBufferSize;
char * fOutputBuffer;
size_t fOutputBufferSize;
char * fWriteBuffer;
size_t fWriteBufferSize;
char * fInterruptBuffer;
@@ -129,15 +133,13 @@ static void InterruptCallbackFunction(void *cookie,
bool fInputStopped;
struct tty * fMasterTTY;
struct tty * fSlaveTTY;
struct tty_cookie * fTTYCookie;
struct tty_cookie * fSystemTTYCookie;
struct tty_cookie * fDeviceTTYCookie;
/* device thread management */
thread_id fDeviceThread;
bool fStopDeviceThread;
/* device locks to ensure no concurent reads/writes */
mutex fReadLock;
mutex fWriteLock;
/* input/output thread management */
thread_id fInputThread;
thread_id fOutputThread;
bool fStopThreads;
};
#endif // _USB_DEVICE_H_