|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|