diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/ACM.cpp b/src/add-ons/kernel/drivers/ports/usb_serial/ACM.cpp index e5ee99c5b5..0114e52c20 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/ACM.cpp +++ b/src/add-ons/kernel/drivers/ports/usb_serial/ACM.cpp @@ -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); -} diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/ACM.h b/src/add-ons/kernel/drivers/ports/usb_serial/ACM.h index 025ce5ad88..459b945392 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/ACM.h +++ b/src/add-ons/kernel/drivers/ports/usb_serial/ACM.h @@ -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_ diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp index 98f9caf54d..5c12841c78 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp +++ b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp @@ -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); } } diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.h b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.h index a7b5b72c49..c88e01c6d9 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.h +++ b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.h @@ -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_