From 2ff5200f81fb9e80e5eecdb532d87a25523884db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Wed, 26 Mar 2014 15:21:44 -0400 Subject: [PATCH] channels/serial: remove old async irp code, cleanup to make use of newer utils --- channels/serial/client/serial_main.c | 520 ++------------------------- channels/serial/client/serial_tty.c | 56 +-- channels/serial/client/serial_tty.h | 1 + 3 files changed, 75 insertions(+), 502 deletions(-) diff --git a/channels/serial/client/serial_main.c b/channels/serial/client/serial_main.c index 8761a43ea..7730c16ef 100644 --- a/channels/serial/client/serial_main.c +++ b/channels/serial/client/serial_main.c @@ -65,37 +65,24 @@ struct _SERIAL_DEVICE SERIAL_TTY* tty; HANDLE thread; - HANDLE mthread; - HANDLE stopEvent; - HANDLE newEvent; - - wQueue* queue; - LIST* pending_irps; - - fd_set read_fds; - fd_set write_fds; - UINT32 nfds; - struct timeval tv; - UINT32 select_timeout; - UINT32 timeout_id; + wMessageQueue* IrpQueue; }; -static void serial_abort_single_io(SERIAL_DEVICE* serial, UINT32 file_id, UINT32 abort_io, UINT32 io_status); -static void serial_check_for_events(SERIAL_DEVICE* serial); -static void serial_handle_async_irp(SERIAL_DEVICE* serial, IRP* irp); -static BOOL serial_check_fds(SERIAL_DEVICE* serial); -static void* serial_thread_mfunc(void* arg); - static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp) { - char* path = NULL; int status; - SERIAL_TTY* tty; - UINT32 PathLength; UINT32 FileId; + UINT32 PathLength; + SERIAL_TTY* tty; + char* path = NULL; + + Stream_Seek_UINT32(irp->input); /* DesiredAccess (4 bytes) */ + Stream_Seek_UINT64(irp->input); /* AllocationSize (8 bytes) */ + Stream_Seek_UINT32(irp->input); /* FileAttributes (4 bytes) */ + Stream_Seek_UINT32(irp->input); /* SharedAccess (4 bytes) */ + Stream_Seek_UINT32(irp->input); /* CreateDisposition (4 bytes) */ + Stream_Seek_UINT32(irp->input); /* CreateOptions (4 bytes) */ - Stream_Seek(irp->input, 28); /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */ - /* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */ Stream_Read_UINT32(irp->input, PathLength); status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input), @@ -108,7 +95,7 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp) tty = serial_tty_new(serial->path, FileId); - if (tty == NULL) + if (!tty) { irp->IoStatus = STATUS_UNSUCCESSFUL; FileId = 0; @@ -118,18 +105,6 @@ static void serial_process_irp_create(SERIAL_DEVICE* serial, IRP* irp) else { serial->tty = tty; - - serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_NONE, - STATUS_CANCELLED); - serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_READ, - STATUS_CANCELLED); - serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_WRITE, - STATUS_CANCELLED); - - serial->mthread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) serial_thread_mfunc, (void*) serial, - 0, NULL); - DEBUG_SVC("%s(%d) created.", serial->path, FileId); } @@ -147,7 +122,7 @@ static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp) tty = serial->tty; - if (tty == NULL) + if (!tty) { irp->IoStatus = STATUS_UNSUCCESSFUL; DEBUG_WARN("tty not valid."); @@ -156,16 +131,11 @@ static void serial_process_irp_close(SERIAL_DEVICE* serial, IRP* irp) { DEBUG_SVC("%s(%d) closed.", serial->path, tty->id); - TerminateThread(serial->mthread, 0); - WaitForSingleObject(serial->mthread, INFINITE); - CloseHandle(serial->mthread); - serial->mthread = NULL; - serial_tty_free(tty); serial->tty = NULL; } - Stream_Zero(irp->output, 5); /* Padding(5) */ + Stream_Zero(irp->output, 5); /* Padding (5 bytes) */ irp->Complete(irp); } @@ -184,7 +154,7 @@ static void serial_process_irp_read(SERIAL_DEVICE* serial, IRP* irp) tty = serial->tty; - if (tty == NULL) + if (!tty) { irp->IoStatus = STATUS_UNSUCCESSFUL; Length = 0; @@ -237,7 +207,7 @@ static void serial_process_irp_write(SERIAL_DEVICE* serial, IRP* irp) tty = serial->tty; - if (tty == NULL) + if (!tty) { irp->IoStatus = STATUS_UNSUCCESSFUL; Length = 0; @@ -268,9 +238,7 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp) UINT32 IoControlCode; UINT32 InputBufferLength; UINT32 OutputBufferLength; - UINT32 abort_io = SERIAL_ABORT_IO_NONE; - - DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps)); + UINT32 abortIo = SERIAL_ABORT_IO_NONE; Stream_Read_UINT32(irp->input, InputBufferLength); Stream_Read_UINT32(irp->input, OutputBufferLength); @@ -288,18 +256,10 @@ static void serial_process_irp_device_control(SERIAL_DEVICE* serial, IRP* irp) } else { - irp->IoStatus = serial_tty_control(tty, IoControlCode, irp->input, irp->output, &abort_io); + irp->IoStatus = serial_tty_control(tty, IoControlCode, irp->input, irp->output, &abortIo); } - if (abort_io & SERIAL_ABORT_IO_WRITE) - serial_abort_single_io(serial, tty->id, SERIAL_ABORT_IO_WRITE, STATUS_CANCELLED); - if (abort_io & SERIAL_ABORT_IO_READ) - serial_abort_single_io(serial, tty->id, SERIAL_ABORT_IO_READ, STATUS_CANCELLED); - - if (irp->IoStatus == STATUS_PENDING) - list_enqueue(serial->pending_irps, irp); - else - irp->Complete(irp); + irp->Complete(irp); } static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp) @@ -317,13 +277,11 @@ static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp) break; case IRP_MJ_READ: - serial_handle_async_irp(serial, irp); - //serial_process_irp_read(serial, irp); + serial_process_irp_read(serial, irp); break; case IRP_MJ_WRITE: - serial_handle_async_irp(serial, irp); - //serial_process_irp_write(serial, irp); + serial_process_irp_write(serial, irp); break; case IRP_MJ_DEVICE_CONTROL: @@ -336,74 +294,29 @@ static void serial_process_irp(SERIAL_DEVICE* serial, IRP* irp) irp->Complete(irp); break; } - - serial_check_for_events(serial); -} - -/* This thread is used as a workaround for the missing serial event - * support in WaitForMultipleObjects. - * It monitors the terminal for events and posts it in a supported - * form that WaitForMultipleObjects can use it. */ -void* serial_thread_mfunc(void* arg) -{ - SERIAL_DEVICE* serial = (SERIAL_DEVICE*)arg; - - assert(NULL != serial); - while(1) - { - int sl; - fd_set rd; - - if(!serial->tty || serial->tty->fd <= 0) - { - DEBUG_WARN("Monitor thread still running, but no terminal opened!"); - sleep(1); - } - else - { - FD_ZERO(&rd); - FD_SET(serial->tty->fd, &rd); - sl = select(serial->tty->fd + 1, &rd, NULL, NULL, NULL); - if( sl > 0 ) - SetEvent(serial->newEvent); - } - } - - ExitThread(0); - return NULL; } static void* serial_thread_func(void* arg) { IRP* irp; - DWORD status; - SERIAL_DEVICE* serial = (SERIAL_DEVICE*)arg; - HANDLE ev[] = {serial->stopEvent, Queue_Event(serial->queue), serial->newEvent}; + wMessage message; + SERIAL_DEVICE* drive = (SERIAL_DEVICE*) arg; - assert(NULL != serial); while (1) { - status = WaitForMultipleObjects(3, ev, FALSE, INFINITE); - - if (WAIT_OBJECT_0 == status) + if (!MessageQueue_Wait(drive->IrpQueue)) break; - else if (status == WAIT_OBJECT_0 + 1) - { - FD_ZERO(&serial->read_fds); - FD_ZERO(&serial->write_fds); - serial->tv.tv_sec = 0; - serial->tv.tv_usec = 0; - serial->select_timeout = 0; + if (!MessageQueue_Peek(drive->IrpQueue, &message, TRUE)) + break; - if ((irp = (IRP*) Queue_Dequeue(serial->queue))) - serial_process_irp(serial, irp); - } - else if (status == WAIT_OBJECT_0 + 2) - ResetEvent(serial->newEvent); + if (message.id == WMQ_QUIT) + break; - if(serial->tty) - serial_check_fds(serial); + irp = (IRP*) message.wParam; + + if (irp) + serial_process_irp(drive, irp); } ExitThread(0); @@ -413,8 +326,7 @@ static void* serial_thread_func(void* arg) static void serial_irp_request(DEVICE* device, IRP* irp) { SERIAL_DEVICE* serial = (SERIAL_DEVICE*) device; - - Queue_Enqueue(serial->queue, irp); + MessageQueue_Post(serial->IrpQueue, NULL, 0, (void*) irp, NULL); } static void serial_free(DEVICE* device) @@ -423,370 +335,19 @@ static void serial_free(DEVICE* device) DEBUG_SVC("freeing device"); - if (serial->stopEvent) - { - SetEvent(serial->stopEvent); - - if (serial->mthread) - { - TerminateThread(serial->mthread, 0); - WaitForSingleObject(serial->mthread, INFINITE); - CloseHandle(serial->mthread); - serial->mthread = NULL; - } - - if (serial->thread) - { - WaitForSingleObject(serial->thread, INFINITE); - CloseHandle(serial->thread); - serial->thread = NULL; - } - - CloseHandle(serial->stopEvent); - serial->stopEvent = NULL; - } + MessageQueue_PostQuit(serial->IrpQueue, 0); + WaitForSingleObject(serial->thread, INFINITE); + CloseHandle(serial->thread); serial_tty_free(serial->tty); /* Clean up resources */ Stream_Free(serial->device.data, TRUE); - Queue_Free(serial->queue); - list_free(serial->pending_irps); - CloseHandle(serial->newEvent); + MessageQueue_Free(serial->IrpQueue); free(serial); } -static void serial_abort_single_io(SERIAL_DEVICE* serial, UINT32 file_id, UINT32 abort_io, UINT32 io_status) -{ - UINT32 major; - IRP* irp = NULL; - SERIAL_TTY* tty; - - DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps)); - - tty = serial->tty; - - if (!tty) - { - DEBUG_WARN("tty = %p", tty); - return; - } - - switch (abort_io) - { - case SERIAL_ABORT_IO_NONE: - major = 0; - break; - - case SERIAL_ABORT_IO_READ: - major = IRP_MJ_READ; - break; - - case SERIAL_ABORT_IO_WRITE: - major = IRP_MJ_WRITE; - break; - - default: - DEBUG_SVC("unexpected abort_io code %d", abort_io); - return; - } - - irp = (IRP*) list_peek(serial->pending_irps); - - while (irp) - { - if (irp->FileId != file_id || irp->MajorFunction != major) - { - irp = (IRP*) list_next(serial->pending_irps, irp); - continue; - } - - /* Process a SINGLE FileId and MajorFunction */ - list_remove(serial->pending_irps, irp); - irp->IoStatus = io_status; - Stream_Write_UINT32(irp->output, 0); - irp->Complete(irp); - - break; - } - - DEBUG_SVC("[out] pending size %d", list_size(serial->pending_irps)); -} - -static void serial_check_for_events(SERIAL_DEVICE* serial) -{ - IRP* irp = NULL; - IRP* prev; - UINT32 result = 0; - SERIAL_TTY* tty; - - tty = serial->tty; - - if (!tty) - { - DEBUG_WARN("tty = %p", tty); - return; - } - - DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps)); - - irp = (IRP*) list_peek(serial->pending_irps); - - while (irp) - { - prev = NULL; - - if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL) - { - if (serial_tty_get_event(tty, &result)) - { - DEBUG_SVC("got event result %u", result); - - irp->IoStatus = STATUS_SUCCESS; - Stream_Write_UINT32(irp->output, result); - irp->Complete(irp); - - prev = irp; - irp = (IRP*) list_next(serial->pending_irps, irp); - list_remove(serial->pending_irps, prev); - } - } - - if (!prev) - irp = (IRP*) list_next(serial->pending_irps, irp); - } - - DEBUG_SVC("[out] pending size %d", list_size(serial->pending_irps)); -} - -void serial_get_timeouts(SERIAL_DEVICE* serial, IRP* irp, UINT32* timeout, UINT32* interval_timeout) -{ - SERIAL_TTY* tty; - UINT32 Length; - UINT32 pos; - - pos = Stream_GetPosition(irp->input); - Stream_Read_UINT32(irp->input, Length); - Stream_SetPosition(irp->input, pos); - - DEBUG_SVC("length read %u", Length); - - tty = serial->tty; - if(!tty) - { - DEBUG_WARN("tty = %p", tty); - return; - } - - *timeout = (tty->read_total_timeout_multiplier * Length) + tty->read_total_timeout_constant; - *interval_timeout = tty->read_interval_timeout; - - DEBUG_SVC("timeouts %u %u", *timeout, *interval_timeout); -} - -static void serial_handle_async_irp(SERIAL_DEVICE* serial, IRP* irp) -{ - UINT32 timeout = 0; - UINT32 itv_timeout = 0; - SERIAL_TTY* tty; - - tty = serial->tty; - if(!tty) - { - DEBUG_WARN("tty = %p", tty); - return; - } - - switch (irp->MajorFunction) - { - case IRP_MJ_WRITE: - DEBUG_SVC("handling IRP_MJ_WRITE"); - break; - - case IRP_MJ_READ: - DEBUG_SVC("handling IRP_MJ_READ"); - - serial_get_timeouts(serial, irp, &timeout, &itv_timeout); - - /* Check if io request timeout is smaller than current (but not 0). */ - if (timeout && ((serial->select_timeout == 0) || (timeout < serial->select_timeout))) - { - serial->select_timeout = timeout; - serial->tv.tv_sec = serial->select_timeout / 1000; - serial->tv.tv_usec = (serial->select_timeout % 1000) * 1000; - serial->timeout_id = tty->id; - } - if (itv_timeout && ((serial->select_timeout == 0) || (itv_timeout < serial->select_timeout))) - { - serial->select_timeout = itv_timeout; - serial->tv.tv_sec = serial->select_timeout / 1000; - serial->tv.tv_usec = (serial->select_timeout % 1000) * 1000; - serial->timeout_id = tty->id; - } - DEBUG_SVC("select_timeout %u, tv_sec %lu tv_usec %lu, timeout_id %u", - serial->select_timeout, serial->tv.tv_sec, serial->tv.tv_usec, serial->timeout_id); - break; - - default: - DEBUG_SVC("no need to handle %d", irp->MajorFunction); - return; - } - - irp->IoStatus = STATUS_PENDING; - list_enqueue(serial->pending_irps, irp); -} - -static void __serial_check_fds(SERIAL_DEVICE* serial) -{ - IRP* irp; - IRP* prev; - SERIAL_TTY* tty; - UINT32 result = 0; - BOOL irp_completed = FALSE; - - ZeroMemory(&serial->tv, sizeof(struct timeval)); - tty = serial->tty; - if(!tty) - { - DEBUG_WARN("tty = %p", tty); - return; - } - - /* scan every pending */ - irp = list_peek(serial->pending_irps); - - while (irp) - { - DEBUG_SVC("MajorFunction %u", irp->MajorFunction); - - switch (irp->MajorFunction) - { - case IRP_MJ_READ: - if (FD_ISSET(tty->fd, &serial->read_fds)) - { - irp->IoStatus = STATUS_SUCCESS; - serial_process_irp_read(serial, irp); - irp_completed = TRUE; - } - break; - - case IRP_MJ_WRITE: - if (FD_ISSET(tty->fd, &serial->write_fds)) - { - irp->IoStatus = STATUS_SUCCESS; - serial_process_irp_write(serial, irp); - irp_completed = TRUE; - } - break; - - case IRP_MJ_DEVICE_CONTROL: - if (serial_tty_get_event(tty, &result)) - { - DEBUG_SVC("got event result %u", result); - - irp->IoStatus = STATUS_SUCCESS; - Stream_Write_UINT32(irp->output, result); - irp->Complete(irp); - irp_completed = TRUE; - } - break; - - default: - DEBUG_SVC("no request found"); - break; - } - - prev = irp; - irp = (IRP*) list_next(serial->pending_irps, irp); - - if (irp_completed || (prev->IoStatus == STATUS_SUCCESS)) - list_remove(serial->pending_irps, prev); - } -} - -static void serial_set_fds(SERIAL_DEVICE* serial) -{ - IRP* irp; - fd_set* fds; - SERIAL_TTY* tty; - - DEBUG_SVC("[in] pending size %d", list_size(serial->pending_irps)); - - tty = serial->tty; - if(!tty) - { - DEBUG_WARN("tty = %p", tty); - return; - } - irp = (IRP*) list_peek(serial->pending_irps); - - while (irp) - { - fds = NULL; - - switch (irp->MajorFunction) - { - case IRP_MJ_WRITE: - fds = &serial->write_fds; - break; - - case IRP_MJ_READ: - fds = &serial->read_fds; - break; - } - - if (fds && (tty->fd >= 0)) - { - FD_SET(tty->fd, fds); - serial->nfds = MAX(serial->nfds, tty->fd); - } - - irp = (IRP*) list_next(serial->pending_irps, irp); - } -} - -static BOOL serial_check_fds(SERIAL_DEVICE* serial) -{ - if (list_size(serial->pending_irps) == 0) - return 1; - - FD_ZERO(&serial->read_fds); - FD_ZERO(&serial->write_fds); - - serial->tv.tv_sec = 0; - serial->tv.tv_usec = 0; - serial->select_timeout = 0; - - serial_set_fds(serial); - DEBUG_SVC("waiting %lu %lu", serial->tv.tv_sec, serial->tv.tv_usec); - - switch (select(serial->nfds + 1, &serial->read_fds, &serial->write_fds, NULL, &serial->tv)) - { - case -1: - DEBUG_SVC("select has returned -1 with error: %s", strerror(errno)); - return 0; - - case 0: - if (serial->select_timeout) - { - __serial_check_fds(serial); - serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_NONE, STATUS_TIMEOUT); - serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_READ, STATUS_TIMEOUT); - serial_abort_single_io(serial, serial->timeout_id, SERIAL_ABORT_IO_WRITE, STATUS_TIMEOUT); - } - DEBUG_SVC("select has timed out"); - return 0; - - default: - break; - } - - __serial_check_fds(serial); - - return 1; -} - #ifdef STATIC_CHANNELS #define DeviceServiceEntry serial_DeviceServiceEntry #endif @@ -828,17 +389,12 @@ int DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints) Stream_Write_UINT8(serial->device.data, name[i] < 0 ? '_' : name[i]); serial->path = path; - serial->queue = Queue_New(TRUE, -1, -1); - serial->pending_irps = list_new(); - - serial->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - serial->newEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + serial->IrpQueue = MessageQueue_New(NULL); pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*) serial); serial->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) serial_thread_func, (void*) serial, 0, NULL); - serial->mthread = NULL; } return 0; diff --git a/channels/serial/client/serial_tty.c b/channels/serial/client/serial_tty.c index 95bf375fe..9b5a3182e 100644 --- a/channels/serial/client/serial_tty.c +++ b/channels/serial/client/serial_tty.c @@ -373,10 +373,8 @@ BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length) { ssize_t status; long timeout = 90; - struct termios* ptermios; DEBUG_SVC("in"); - ptermios = tty->ptermios; /* Set timeouts kind of like the windows serial timeout parameters. Multiply timeout with requested read size */ @@ -391,21 +389,35 @@ BOOL serial_tty_read(SERIAL_TTY* tty, BYTE* buffer, UINT32* Length) timeout = (tty->read_interval_timeout * (*Length) + 99) / 100; } - /* If a timeout is set, do a blocking read, which times out after some time. - It will make FreeRDP less responsive, but it will improve serial performance, - by not reading one character at a time. */ - if (timeout == 0) - { - ptermios->c_cc[VTIME] = 0; - ptermios->c_cc[VMIN] = 0; - } - else - { - ptermios->c_cc[VTIME] = timeout; - ptermios->c_cc[VMIN] = 1; - } + if (tty->timeout != timeout) + { + struct termios* ptermios; - tcsetattr(tty->fd, TCSANOW, ptermios); + ptermios = (struct termios*) calloc(1, sizeof(struct termios)); + + if (tcgetattr(tty->fd, ptermios) < 0) + return FALSE; + + /** + * If a timeout is set, do a blocking read, which times out after some time. + * It will make FreeRDP less responsive, but it will improve serial performance, + * by not reading one character at a time. + */ + + if (timeout == 0) + { + ptermios->c_cc[VTIME] = 0; + ptermios->c_cc[VMIN] = 0; + } + else + { + ptermios->c_cc[VTIME] = timeout; + ptermios->c_cc[VMIN] = 1; + } + + tcsetattr(tty->fd, TCSANOW, ptermios); + tty->timeout = timeout; + } ZeroMemory(buffer, *Length); @@ -480,8 +492,7 @@ SERIAL_TTY* serial_tty_new(const char* path, UINT32 id) { SERIAL_TTY* tty; - tty = (SERIAL_TTY*) malloc(sizeof(SERIAL_TTY)); - ZeroMemory(tty, sizeof(SERIAL_TTY)); + tty = (SERIAL_TTY*) calloc(1, sizeof(SERIAL_TTY)); tty->id = id; tty->fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -526,11 +537,14 @@ SERIAL_TTY* serial_tty_new(const char* path, UINT32 id) } tty->ptermios->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - tty->ptermios->c_iflag = IGNPAR | ICRNL; tty->ptermios->c_oflag &= ~OPOST; tty->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tty->ptermios->c_cflag &= ~(CSIZE | PARENB); - tty->ptermios->c_cflag |= CLOCAL | CREAD | CS8; + tty->ptermios->c_cflag |= CS8; + + tty->ptermios->c_iflag = IGNPAR; + tty->ptermios->c_cflag |= CLOCAL | CREAD; + tcsetattr(tty->fd, TCSANOW, tty->ptermios); tty->event_txempty = 0; @@ -802,6 +816,8 @@ static BOOL tty_get_termios(SERIAL_TTY* tty) tty->chars[SERIAL_CHAR_BREAK] = ptermios->c_cc[VINTR]; tty->chars[SERIAL_CHAR_ERROR] = ptermios->c_cc[VKILL]; + tty->timeout = ptermios->c_cc[VTIME]; + return TRUE; } diff --git a/channels/serial/client/serial_tty.h b/channels/serial/client/serial_tty.h index f75a3988d..20a7bec54 100644 --- a/channels/serial/client/serial_tty.h +++ b/channels/serial/client/serial_tty.h @@ -65,6 +65,7 @@ struct _SERIAL_TTY int event_dsr; int event_rlsd; int event_pending; + long timeout; }; SERIAL_TTY* serial_tty_new(const char* path, UINT32 id);