--- sourceforge.net/trunk/rdesktop/serial.c 2003/07/09 09:18:20 435 +++ sourceforge.net/trunk/rdesktop/serial.c 2005/02/14 00:34:23 812 @@ -1,8 +1,27 @@ #include #include #include +#include +#include + +#ifdef HAVE_SYS_MODEM_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#ifdef HAVE_SYS_STRTIO_H +#include +#endif + #include "rdesktop.h" +#ifdef WITH_DEBUG_SERIAL +#define DEBUG_SERIAL(args) printf args; +#else +#define DEBUG_SERIAL(args) +#endif + #define FILE_DEVICE_SERIAL_PORT 0x1b #define SERIAL_SET_BAUD_RATE 1 @@ -52,337 +71,853 @@ #define ODD_PARITY 1 #define EVEN_PARITY 2 -int serial_fd; -struct termios termios; +#define SERIAL_PURGE_TXABORT 0x00000001 +#define SERIAL_PURGE_RXABORT 0x00000002 +#define SERIAL_PURGE_TXCLEAR 0x00000004 +#define SERIAL_PURGE_RXCLEAR 0x00000008 + +/* SERIAL_WAIT_ON_MASK */ +#define SERIAL_EV_RXCHAR 0x0001 // Any Character received +#define SERIAL_EV_RXFLAG 0x0002 // Received certain character +#define SERIAL_EV_TXEMPTY 0x0004 // Transmitt Queue Empty +#define SERIAL_EV_CTS 0x0008 // CTS changed state +#define SERIAL_EV_DSR 0x0010 // DSR changed state +#define SERIAL_EV_RLSD 0x0020 // RLSD changed state +#define SERIAL_EV_BREAK 0x0040 // BREAK received +#define SERIAL_EV_ERR 0x0080 // Line status error occurred +#define SERIAL_EV_RING 0x0100 // Ring signal detected +#define SERIAL_EV_PERR 0x0200 // Printer error occured +#define SERIAL_EV_RX80FULL 0x0400 // Receive buffer is 80 percent full +#define SERIAL_EV_EVENT1 0x0800 // Provider specific event 1 +#define SERIAL_EV_EVENT2 0x1000 // Provider specific event 2 + +/* Modem Status */ +#define SERIAL_MS_DTR 0x01 +#define SERIAL_MS_RTS 0x02 +#define SERIAL_MS_CTS 0x10 +#define SERIAL_MS_DSR 0x20 +#define SERIAL_MS_RNG 0x40 +#define SERIAL_MS_CAR 0x80 + +/* Handflow */ +#define SERIAL_DTR_CONTROL 0x01 +#define SERIAL_CTS_HANDSHAKE 0x08 +#define SERIAL_ERROR_ABORT 0x80000000 + +#define SERIAL_XON_HANDSHAKE 0x01 +#define SERIAL_XOFF_HANDSHAKE 0x02 +#define SERIAL_DSR_SENSITIVITY 0x40 + +#define SERIAL_CHAR_EOF 0 +#define SERIAL_CHAR_ERROR 1 +#define SERIAL_CHAR_BREAK 2 +#define SERIAL_CHAR_EVENT 3 +#define SERIAL_CHAR_XON 4 +#define SERIAL_CHAR_XOFF 5 -int dtr; -uint32 baud_rate; -uint32 queue_in_size, queue_out_size; -uint32 wait_mask; -uint8 stop_bits, parity, word_length; +#ifndef CRTSCTS +#define CRTSCTS 0 +#endif + +/* FIONREAD should really do the same thing as TIOCINQ, where it is + * not available */ +#if !defined(TIOCINQ) && defined(FIONREAD) +#define TIOCINQ FIONREAD +#endif +#if !defined(TIOCOUTQ) && defined(FIONWRITE) +#define TIOCOUTQ FIONWRITE +#endif + +extern RDPDR_DEVICE g_rdpdr_device[]; + +static SERIAL_DEVICE * +get_serial_info(NTHANDLE handle) +{ + int index; + + for (index = 0; index < RDPDR_MAX_DEVICES; index++) + { + if (handle == g_rdpdr_device[index].handle) + return (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data; + } + return NULL; +} static BOOL -get_termios(void) +get_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd) { speed_t speed; + struct termios *ptermios; + + ptermios = pser_inf->ptermios; - if (tcgetattr(serial_fd, &termios) == -1) + if (tcgetattr(serial_fd, ptermios) == -1) return False; - speed = cfgetispeed(&termios); + speed = cfgetispeed(ptermios); switch (speed) { +#ifdef B75 case B75: - baud_rate = 75; + pser_inf->baud_rate = 75; break; +#endif +#ifdef B110 case B110: - baud_rate = 110; + pser_inf->baud_rate = 110; break; +#endif +#ifdef B134 case B134: - baud_rate = 134; + pser_inf->baud_rate = 134; break; +#endif +#ifdef B150 case B150: - baud_rate = 150; + pser_inf->baud_rate = 150; break; +#endif +#ifdef B300 case B300: - baud_rate = 300; + pser_inf->baud_rate = 300; break; +#endif +#ifdef B600 case B600: - baud_rate = 600; + pser_inf->baud_rate = 600; break; +#endif +#ifdef B1200 case B1200: - baud_rate = 1200; + pser_inf->baud_rate = 1200; break; +#endif +#ifdef B1800 case B1800: - baud_rate = 1800; + pser_inf->baud_rate = 1800; break; +#endif +#ifdef B2400 case B2400: - baud_rate = 2400; + pser_inf->baud_rate = 2400; break; +#endif +#ifdef B4800 case B4800: - baud_rate = 4800; + pser_inf->baud_rate = 4800; break; +#endif +#ifdef B9600 case B9600: - baud_rate = 9600; + pser_inf->baud_rate = 9600; break; +#endif +#ifdef B19200 case B19200: - baud_rate = 19200; + pser_inf->baud_rate = 19200; break; +#endif +#ifdef B38400 case B38400: - baud_rate = 38400; + pser_inf->baud_rate = 38400; break; +#endif +#ifdef B57600 case B57600: - baud_rate = 57600; + pser_inf->baud_rate = 57600; break; +#endif +#ifdef B115200 case B115200: - baud_rate = 115200; + pser_inf->baud_rate = 115200; + break; +#endif +#ifdef B230400 + case B230400: + pser_inf->baud_rate = 230400; break; +#endif +#ifdef B460800 + case B460800: + pser_inf->baud_rate = 460800; + break; +#endif default: - baud_rate = 0; + pser_inf->baud_rate = 9600; break; } - speed = cfgetospeed(&termios); - dtr = (speed == B0) ? 0 : 1; + speed = cfgetospeed(ptermios); + pser_inf->dtr = (speed == B0) ? 0 : 1; - stop_bits = (termios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BITS_1; - parity = (termios. - c_cflag & PARENB) ? ((termios. - c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY) : NO_PARITY; - switch (termios.c_cflag & CSIZE) + pser_inf->stop_bits = (ptermios->c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BITS_1; + pser_inf->parity = + (ptermios-> + c_cflag & PARENB) ? ((ptermios-> + c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY) : NO_PARITY; + switch (ptermios->c_cflag & CSIZE) { case CS5: - word_length = 5; + pser_inf->word_length = 5; break; case CS6: - word_length = 6; + pser_inf->word_length = 6; break; case CS7: - word_length = 7; + pser_inf->word_length = 7; break; default: - word_length = 8; + pser_inf->word_length = 8; break; } + if (ptermios->c_cflag & CRTSCTS) + { + pser_inf->control = SERIAL_DTR_CONTROL | SERIAL_CTS_HANDSHAKE | SERIAL_ERROR_ABORT; + } + else + { + pser_inf->control = SERIAL_DTR_CONTROL | SERIAL_ERROR_ABORT; + } + + pser_inf->xonoff = SERIAL_DSR_SENSITIVITY; + if (ptermios->c_iflag & IXON) + pser_inf->xonoff |= SERIAL_XON_HANDSHAKE; + + if (ptermios->c_iflag & IXOFF) + pser_inf->xonoff |= SERIAL_XOFF_HANDSHAKE; + + pser_inf->chars[SERIAL_CHAR_XON] = ptermios->c_cc[VSTART]; + pser_inf->chars[SERIAL_CHAR_XOFF] = ptermios->c_cc[VSTOP]; + pser_inf->chars[SERIAL_CHAR_EOF] = ptermios->c_cc[VEOF]; + pser_inf->chars[SERIAL_CHAR_BREAK] = ptermios->c_cc[VINTR]; + pser_inf->chars[SERIAL_CHAR_ERROR] = ptermios->c_cc[VKILL]; + return True; } static void -set_termios(void) +set_termios(SERIAL_DEVICE * pser_inf, NTHANDLE serial_fd) { speed_t speed; - switch (baud_rate) + struct termios *ptermios; + + ptermios = pser_inf->ptermios; + + + switch (pser_inf->baud_rate) { +#ifdef B75 case 75: speed = B75; break; +#endif +#ifdef B110 case 110: speed = B110; break; +#endif +#ifdef B134 case 134: speed = B134; break; +#endif +#ifdef B150 case 150: speed = B150; break; +#endif +#ifdef B300 case 300: speed = B300; break; +#endif +#ifdef B600 case 600: speed = B600; break; +#endif +#ifdef B1200 case 1200: speed = B1200; break; +#endif +#ifdef B1800 case 1800: speed = B1800; break; +#endif +#ifdef B2400 case 2400: speed = B2400; break; +#endif +#ifdef B4800 case 4800: speed = B4800; break; +#endif +#ifdef B9600 case 9600: speed = B9600; break; +#endif +#ifdef B19200 case 19200: speed = B19200; break; +#endif +#ifdef B38400 case 38400: speed = B38400; break; +#endif +#ifdef B57600 case 57600: speed = B57600; break; +#endif +#ifdef B115200 case 115200: speed = B115200; break; +#endif +#ifdef B230400 + case 230400: + speed = B115200; + break; +#endif +#ifdef B460800 + case 460800: + speed = B115200; + break; +#endif default: - speed = B0; + speed = B9600; break; } +#ifdef CBAUD + ptermios->c_cflag &= ~CBAUD; + ptermios->c_cflag |= speed; +#else /* on systems with separate ispeed and ospeed, we can remember the speed in ispeed while changing DTR with ospeed */ - cfsetispeed(&termios, speed); - cfsetospeed(&termios, dtr ? speed : 0); + cfsetispeed(pser_inf->ptermios, speed); + cfsetospeed(pser_inf->ptermios, pser_inf->dtr ? speed : 0); +#endif - termios.c_cflag &= ~(CSTOPB | PARENB | PARODD | CSIZE); - switch (stop_bits) + ptermios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CSIZE | CRTSCTS); + switch (pser_inf->stop_bits) { case STOP_BITS_2: - termios.c_cflag |= CSTOPB; + ptermios->c_cflag |= CSTOPB; + break; + default: + ptermios->c_cflag &= ~CSTOPB; break; } - switch (parity) + + switch (pser_inf->parity) { case EVEN_PARITY: - termios.c_cflag |= PARENB; + ptermios->c_cflag |= PARENB; break; case ODD_PARITY: - termios.c_cflag |= PARENB | PARODD; + ptermios->c_cflag |= PARENB | PARODD; + break; + case NO_PARITY: + ptermios->c_cflag &= ~(PARENB | PARODD); break; } - switch (word_length) + + switch (pser_inf->word_length) { case 5: - termios.c_cflag |= CS5; + ptermios->c_cflag |= CS5; break; case 6: - termios.c_cflag |= CS6; + ptermios->c_cflag |= CS6; break; case 7: - termios.c_cflag |= CS7; + ptermios->c_cflag |= CS7; break; default: - termios.c_cflag |= CS8; + ptermios->c_cflag |= CS8; break; } - tcsetattr(serial_fd, TCSANOW, &termios); +#if 0 + if (pser_inf->rts) + ptermios->c_cflag |= CRTSCTS; + else + ptermios->c_cflag &= ~CRTSCTS; +#endif + + if (pser_inf->control & SERIAL_CTS_HANDSHAKE) + { + ptermios->c_cflag |= CRTSCTS; + } + else + { + ptermios->c_cflag &= ~CRTSCTS; + } + + + if (pser_inf->xonoff & SERIAL_XON_HANDSHAKE) + { + ptermios->c_iflag |= IXON | IMAXBEL; + } + if (pser_inf->xonoff & SERIAL_XOFF_HANDSHAKE) + { + ptermios->c_iflag |= IXOFF | IMAXBEL; + } + + if ((pser_inf->xonoff & (SERIAL_XOFF_HANDSHAKE | SERIAL_XON_HANDSHAKE)) == 0) + { + ptermios->c_iflag &= ~IXON; + ptermios->c_iflag &= ~IXOFF; + } + + ptermios->c_cc[VSTART] = pser_inf->chars[SERIAL_CHAR_XON]; + ptermios->c_cc[VSTOP] = pser_inf->chars[SERIAL_CHAR_XOFF]; + ptermios->c_cc[VEOF] = pser_inf->chars[SERIAL_CHAR_EOF]; + ptermios->c_cc[VINTR] = pser_inf->chars[SERIAL_CHAR_BREAK]; + ptermios->c_cc[VKILL] = pser_inf->chars[SERIAL_CHAR_ERROR]; + + tcsetattr(serial_fd, TCSANOW, ptermios); +} + +/* Enumeration of devices from rdesktop.c */ +/* returns numer of units found and initialized. */ +/* optarg looks like ':com1=/dev/ttyS0' */ +/* when it arrives to this function. */ +/* :com1=/dev/ttyS0,com2=/dev/ttyS1 */ +int +serial_enum_devices(uint32 * id, char *optarg) +{ + SERIAL_DEVICE *pser_inf; + + char *pos = optarg; + char *pos2; + int count = 0; + + // skip the first colon + optarg++; + while ((pos = next_arg(optarg, ',')) && *id < RDPDR_MAX_DEVICES) + { + // Init data structures for device + pser_inf = (SERIAL_DEVICE *) xmalloc(sizeof(SERIAL_DEVICE)); + pser_inf->ptermios = (struct termios *) xmalloc(sizeof(struct termios)); + memset(pser_inf->ptermios, 0, sizeof(struct termios)); + pser_inf->pold_termios = (struct termios *) xmalloc(sizeof(struct termios)); + memset(pser_inf->pold_termios, 0, sizeof(struct termios)); + + pos2 = next_arg(optarg, '='); + strcpy(g_rdpdr_device[*id].name, optarg); + + toupper_str(g_rdpdr_device[*id].name); + + g_rdpdr_device[*id].local_path = xmalloc(strlen(pos2) + 1); + strcpy(g_rdpdr_device[*id].local_path, pos2); + printf("SERIAL %s to %s\n", g_rdpdr_device[*id].name, + g_rdpdr_device[*id].local_path); + // set device type + g_rdpdr_device[*id].device_type = DEVICE_TYPE_SERIAL; + g_rdpdr_device[*id].pdevice_data = (void *) pser_inf; + count++; + (*id)++; + + optarg = pos; + } + return count; } static NTSTATUS -serial_create(HANDLE * handle) +serial_create(uint32 device_id, uint32 access, uint32 share_mode, uint32 disposition, + uint32 flags_and_attributes, char *filename, NTHANDLE * handle) { - /* XXX do we have to handle concurrent open attempts? */ - serial_fd = open("/dev/ttyS0", O_RDWR); + NTHANDLE serial_fd; + SERIAL_DEVICE *pser_inf; + struct termios *ptermios; + + pser_inf = (SERIAL_DEVICE *) g_rdpdr_device[device_id].pdevice_data; + ptermios = pser_inf->ptermios; + serial_fd = open(g_rdpdr_device[device_id].local_path, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (serial_fd == -1) + { + perror("open"); return STATUS_ACCESS_DENIED; + } - if (!get_termios()) + if (!get_termios(pser_inf, serial_fd)) + { + printf("INFO: SERIAL %s access denied\n", g_rdpdr_device[device_id].name); + fflush(stdout); return STATUS_ACCESS_DENIED; + } + + // Store handle for later use + g_rdpdr_device[device_id].handle = serial_fd; + + /* some sane information */ + DEBUG_SERIAL(("INFO: SERIAL %s to %s\nINFO: speed %u baud, stop bits %u, parity %u, word length %u bits, dtr %u, rts %u\n", g_rdpdr_device[device_id].name, g_rdpdr_device[device_id].local_path, pser_inf->baud_rate, pser_inf->stop_bits, pser_inf->parity, pser_inf->word_length, pser_inf->dtr, pser_inf->rts)); + + pser_inf->ptermios->c_iflag &= + ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + pser_inf->ptermios->c_oflag &= ~OPOST; + pser_inf->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + pser_inf->ptermios->c_cflag &= ~(CSIZE | PARENB); + pser_inf->ptermios->c_cflag |= CS8; + + tcsetattr(serial_fd, TCSANOW, pser_inf->ptermios); + + pser_inf->event_txempty = 0; + pser_inf->event_cts = 0; + pser_inf->event_dsr = 0; + pser_inf->event_rlsd = 0; + pser_inf->event_pending = 0; + + *handle = serial_fd; + + /* all read and writes should be non blocking */ + if (fcntl(*handle, F_SETFL, O_NONBLOCK) == -1) + perror("fcntl"); + + pser_inf->read_total_timeout_constant = 5; - *handle = 0; return STATUS_SUCCESS; } static NTSTATUS -serial_close(HANDLE handle) +serial_close(NTHANDLE handle) { - close(serial_fd); + int i = get_device_index(handle); + if (i >= 0) + g_rdpdr_device[i].handle = 0; + + rdpdr_abort_io(handle, 0, STATUS_TIMEOUT); + close(handle); return STATUS_SUCCESS; } static NTSTATUS -serial_read(HANDLE handle, uint8 * data, uint32 length, uint32 * result) +serial_read(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result) { - *result = read(serial_fd, data, length); + long timeout; + SERIAL_DEVICE *pser_inf; + struct termios *ptermios; +#ifdef WITH_DEBUG_SERIAL + int bytes_inqueue; +#endif + + + timeout = 90; + pser_inf = get_serial_info(handle); + ptermios = pser_inf->ptermios; + + // Set timeouts kind of like the windows serial timeout parameters. Multiply timeout + // with requested read size + if (pser_inf->read_total_timeout_multiplier | pser_inf->read_total_timeout_constant) + { + timeout = + (pser_inf->read_total_timeout_multiplier * length + + pser_inf->read_total_timeout_constant + 99) / 100; + } + else if (pser_inf->read_interval_timeout) + { + timeout = (pser_inf->read_interval_timeout * length + 99) / 100; + } + + // If a timeout is set, do a blocking read, which times out after some time. + // It will make rdesktop 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(handle, TCSANOW, ptermios); + +#if defined(WITH_DEBUG_SERIAL) && defined(TIOCINQ) + ioctl(handle, TIOCINQ, &bytes_inqueue); + DEBUG_SERIAL(("serial_read inqueue: %d expected %d\n", bytes_inqueue, length)); +#endif + + *result = read(handle, data, length); + +#ifdef WITH_DEBUG_SERIAL + DEBUG_SERIAL(("serial_read Bytes %d\n", *result)); + if (*result > 0) + hexdump(data, *result); +#endif + return STATUS_SUCCESS; } static NTSTATUS -serial_write(HANDLE handle, uint8 * data, uint32 length, uint32 * result) +serial_write(NTHANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result) { - *result = write(serial_fd, data, length); + SERIAL_DEVICE *pser_inf; + + pser_inf = get_serial_info(handle); + + *result = write(handle, data, length); + + if (*result > 0) + pser_inf->event_txempty = *result; + + DEBUG_SERIAL(("serial_write length %d, offset %d result %d\n", length, offset, *result)); + return STATUS_SUCCESS; } static NTSTATUS -serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out) +serial_device_control(NTHANDLE handle, uint32 request, STREAM in, STREAM out) { - uint32 result; + int flush_mask, purge_mask; + uint32 result, modemstate; uint8 immediate; + SERIAL_DEVICE *pser_inf; + struct termios *ptermios; if ((request >> 16) != FILE_DEVICE_SERIAL_PORT) return STATUS_INVALID_PARAMETER; + pser_inf = get_serial_info(handle); + ptermios = pser_inf->ptermios; + /* extract operation */ request >>= 2; request &= 0xfff; - printf("SERIAL IOCTL %d\n", request); - switch (request) { case SERIAL_SET_BAUD_RATE: - in_uint32_le(in, baud_rate); - set_termios(); + in_uint32_le(in, pser_inf->baud_rate); + set_termios(pser_inf, handle); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_BAUD_RATE %d\n", + pser_inf->baud_rate)); break; case SERIAL_GET_BAUD_RATE: - out_uint32_le(out, baud_rate); + out_uint32_le(out, pser_inf->baud_rate); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_BAUD_RATE %d\n", + pser_inf->baud_rate)); break; case SERIAL_SET_QUEUE_SIZE: - in_uint32_le(in, queue_in_size); - in_uint32_le(in, queue_out_size); + in_uint32_le(in, pser_inf->queue_in_size); + in_uint32_le(in, pser_inf->queue_out_size); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_QUEUE_SIZE in %d out %d\n", + pser_inf->queue_in_size, pser_inf->queue_out_size)); break; case SERIAL_SET_LINE_CONTROL: - in_uint8(in, stop_bits); - in_uint8(in, parity); - in_uint8(in, word_length); - set_termios(); + in_uint8(in, pser_inf->stop_bits); + in_uint8(in, pser_inf->parity); + in_uint8(in, pser_inf->word_length); + set_termios(pser_inf, handle); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_LINE_CONTROL stop %d parity %d word %d\n", pser_inf->stop_bits, pser_inf->parity, pser_inf->word_length)); break; case SERIAL_GET_LINE_CONTROL: - out_uint8(out, stop_bits); - out_uint8(out, parity); - out_uint8(out, word_length); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_LINE_CONTROL\n")); + out_uint8(out, pser_inf->stop_bits); + out_uint8(out, pser_inf->parity); + out_uint8(out, pser_inf->word_length); break; case SERIAL_IMMEDIATE_CHAR: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_IMMEDIATE_CHAR\n")); in_uint8(in, immediate); - serial_write(handle, &immediate, 1, &result); + serial_write(handle, &immediate, 1, 0, &result); break; case SERIAL_CONFIG_SIZE: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_CONFIG_SIZE\n")); out_uint32_le(out, 0); break; case SERIAL_GET_CHARS: - out_uint8s(out, 6); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_CHARS\n")); + out_uint8a(out, pser_inf->chars, 6); break; case SERIAL_SET_CHARS: - in_uint8s(in, 6); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_CHARS\n")); + in_uint8a(in, pser_inf->chars, 6); +#ifdef WITH_DEBUG_SERIAL + hexdump(pser_inf->chars, 6); +#endif + set_termios(pser_inf, handle); break; case SERIAL_GET_HANDFLOW: - out_uint32_le(out, 0); - out_uint32_le(out, 3); /* Xon/Xoff */ - out_uint32_le(out, 0); - out_uint32_le(out, 0); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_HANDFLOW\n")); + get_termios(pser_inf, handle); + out_uint32_le(out, pser_inf->control); + out_uint32_le(out, pser_inf->xonoff); /* Xon/Xoff */ + out_uint32_le(out, pser_inf->onlimit); + out_uint32_le(out, pser_inf->offlimit); break; case SERIAL_SET_HANDFLOW: - in_uint8s(in, 16); + in_uint32_le(in, pser_inf->control); + in_uint32_le(in, pser_inf->xonoff); + in_uint32_le(in, pser_inf->onlimit); + in_uint32_le(in, pser_inf->offlimit); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_HANDFLOW %x %x %x %x\n", + pser_inf->control, pser_inf->xonoff, pser_inf->onlimit, + pser_inf->onlimit)); + set_termios(pser_inf, handle); break; case SERIAL_SET_TIMEOUTS: - in_uint8s(in, 20); + in_uint32(in, pser_inf->read_interval_timeout); + in_uint32(in, pser_inf->read_total_timeout_multiplier); + in_uint32(in, pser_inf->read_total_timeout_constant); + in_uint32(in, pser_inf->write_total_timeout_multiplier); + in_uint32(in, pser_inf->write_total_timeout_constant); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_TIMEOUTS read timeout %d %d %d\n", + pser_inf->read_interval_timeout, + pser_inf->read_total_timeout_multiplier, + pser_inf->read_total_timeout_constant)); break; case SERIAL_GET_TIMEOUTS: - out_uint8s(out, 20); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_TIMEOUTS read timeout %d %d %d\n", + pser_inf->read_interval_timeout, + pser_inf->read_total_timeout_multiplier, + pser_inf->read_total_timeout_constant)); + + out_uint32(out, pser_inf->read_interval_timeout); + out_uint32(out, pser_inf->read_total_timeout_multiplier); + out_uint32(out, pser_inf->read_total_timeout_constant); + out_uint32(out, pser_inf->write_total_timeout_multiplier); + out_uint32(out, pser_inf->write_total_timeout_constant); break; case SERIAL_GET_WAIT_MASK: - out_uint32(out, wait_mask); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_WAIT_MASK %X\n", + pser_inf->wait_mask); + out_uint32(out, pser_inf->wait_mask)); break; case SERIAL_SET_WAIT_MASK: - in_uint32(in, wait_mask); + in_uint32(in, pser_inf->wait_mask); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_WAIT_MASK %X\n", + pser_inf->wait_mask)); break; case SERIAL_SET_DTR: - dtr = 1; - set_termios(); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_DTR\n")); + ioctl(handle, TIOCMGET, &result); + result |= TIOCM_DTR; + ioctl(handle, TIOCMSET, &result); + pser_inf->dtr = 1; break; case SERIAL_CLR_DTR: - dtr = 0; - set_termios(); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_CLR_DTR\n")); + ioctl(handle, TIOCMGET, &result); + result &= ~TIOCM_DTR; + ioctl(handle, TIOCMSET, &result); + pser_inf->dtr = 0; break; -#if 0 - case SERIAL_WAIT_ON_MASK: - /* XXX implement me */ + case SERIAL_SET_RTS: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_RTS\n")); + ioctl(handle, TIOCMGET, &result); + result |= TIOCM_RTS; + ioctl(handle, TIOCMSET, &result); + pser_inf->rts = 1; break; - case SERIAL_SET_BREAK_ON: - tcsendbreak(serial_fd, 0); + case SERIAL_CLR_RTS: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_CLR_RTS\n")); + ioctl(handle, TIOCMGET, &result); + result &= ~TIOCM_RTS; + ioctl(handle, TIOCMSET, &result); + pser_inf->rts = 0; + break; + case SERIAL_GET_MODEMSTATUS: + modemstate = 0; +#ifdef TIOCMGET + ioctl(handle, TIOCMGET, &result); + if (result & TIOCM_CTS) + modemstate |= SERIAL_MS_CTS; + if (result & TIOCM_DSR) + modemstate |= SERIAL_MS_DSR; + if (result & TIOCM_RNG) + modemstate |= SERIAL_MS_RNG; + if (result & TIOCM_CAR) + modemstate |= SERIAL_MS_CAR; + if (result & TIOCM_DTR) + modemstate |= SERIAL_MS_DTR; + if (result & TIOCM_RTS) + modemstate |= SERIAL_MS_RTS; +#endif + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_MODEMSTATUS %X\n", modemstate)); + out_uint32_le(out, modemstate); + break; + case SERIAL_GET_COMMSTATUS: + out_uint32_le(out, 0); /* Errors */ + out_uint32_le(out, 0); /* Hold reasons */ + + result = 0; +#ifdef TIOCINQ + ioctl(handle, TIOCINQ, &result); +#endif + out_uint32_le(out, result); /* Amount in in queue */ + if (result) + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_COMMSTATUS in queue %d\n", + result)); + + result = 0; +#ifdef TIOCOUTQ + ioctl(handle, TIOCOUTQ, &result); +#endif + out_uint32_le(out, result); /* Amount in out queue */ + if (result) + DEBUG_SERIAL(("serial_ioctl -> SERIAL_GET_COMMSTATUS out queue %d\n", result)); + + out_uint8(out, 0); /* EofReceived */ + out_uint8(out, 0); /* WaitForImmediate */ break; case SERIAL_PURGE: - in_uint32(purge_mask); - /* tcflush */ + in_uint32(in, purge_mask); + DEBUG_SERIAL(("serial_ioctl -> SERIAL_PURGE purge_mask %X\n", purge_mask)); + flush_mask = 0; + if (purge_mask & SERIAL_PURGE_TXCLEAR) + flush_mask |= TCOFLUSH; + if (purge_mask & SERIAL_PURGE_RXCLEAR) + flush_mask |= TCIFLUSH; + if (flush_mask != 0) + tcflush(handle, flush_mask); + if (purge_mask & SERIAL_PURGE_TXABORT) + rdpdr_abort_io(handle, 4, STATUS_CANCELLED); + if (purge_mask & SERIAL_PURGE_RXABORT) + rdpdr_abort_io(handle, 3, STATUS_CANCELLED); + break; + case SERIAL_WAIT_ON_MASK: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_WAIT_ON_MASK %X\n", + pser_inf->wait_mask)); + pser_inf->event_pending = 1; + if (serial_get_event(handle, &result)) + { + DEBUG_SERIAL(("WAIT end event = %x\n", result)); + out_uint32_le(out, result); + break; + } + return STATUS_PENDING; + break; + case SERIAL_SET_BREAK_ON: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_BREAK_ON\n")); + tcsendbreak(handle, 0); break; case SERIAL_RESET_DEVICE: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_RESET_DEVICE\n")); + break; case SERIAL_SET_BREAK_OFF: - case SERIAL_SET_RTS: - case SERIAL_CLR_RTS: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_BREAK_OFF\n")); + break; case SERIAL_SET_XOFF: + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_XOFF\n")); + break; case SERIAL_SET_XON: - /* ignore */ + DEBUG_SERIAL(("serial_ioctl -> SERIAL_SET_XON\n")); + tcflow(handle, TCION); break; -#endif - default: unimpl("SERIAL IOCTL %d\n", request); return STATUS_INVALID_PARAMETER; @@ -391,6 +926,128 @@ return STATUS_SUCCESS; } +BOOL +serial_get_event(NTHANDLE handle, uint32 * result) +{ + int index; + SERIAL_DEVICE *pser_inf; + int bytes; + BOOL ret = False; + + *result = 0; + index = get_device_index(handle); + if (index < 0) + return False; + +#ifdef TIOCINQ + pser_inf = (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data; + + ioctl(handle, TIOCINQ, &bytes); + + if (bytes > 0) + { + DEBUG_SERIAL(("serial_get_event Bytes %d\n", bytes)); + if (bytes > pser_inf->event_rlsd) + { + pser_inf->event_rlsd = bytes; + if (pser_inf->wait_mask & SERIAL_EV_RLSD) + { + DEBUG_SERIAL(("Event -> SERIAL_EV_RLSD \n")); + *result |= SERIAL_EV_RLSD; + ret = True; + } + + } + + if ((bytes > 1) && (pser_inf->wait_mask & SERIAL_EV_RXFLAG)) + { + DEBUG_SERIAL(("Event -> SERIAL_EV_RXFLAG Bytes %d\n", bytes)); + *result |= SERIAL_EV_RXFLAG; + ret = True; + } + if ((pser_inf->wait_mask & SERIAL_EV_RXCHAR)) + { + DEBUG_SERIAL(("Event -> SERIAL_EV_RXCHAR Bytes %d\n", bytes)); + *result |= SERIAL_EV_RXCHAR; + ret = True; + } + + } + else + { + pser_inf->event_rlsd = 0; + } +#endif + +#ifdef TIOCOUTQ + ioctl(handle, TIOCOUTQ, &bytes); + if ((bytes == 0) + && (pser_inf->event_txempty > 0) && (pser_inf->wait_mask & SERIAL_EV_TXEMPTY)) + { + + DEBUG_SERIAL(("Event -> SERIAL_EV_TXEMPTY\n")); + *result |= SERIAL_EV_TXEMPTY; + ret = True; + } + pser_inf->event_txempty = bytes; +#endif + + ioctl(handle, TIOCMGET, &bytes); + if ((bytes & TIOCM_DSR) != pser_inf->event_dsr) + { + pser_inf->event_dsr = bytes & TIOCM_DSR; + if (pser_inf->wait_mask & SERIAL_EV_DSR) + { + DEBUG_SERIAL(("event -> SERIAL_EV_DSR %s\n", + (bytes & TIOCM_DSR) ? "ON" : "OFF")); + *result |= SERIAL_EV_DSR; + ret = True; + } + } + + if ((bytes & TIOCM_CTS) != pser_inf->event_cts) + { + pser_inf->event_cts = bytes & TIOCM_CTS; + if (pser_inf->wait_mask & SERIAL_EV_CTS) + { + DEBUG_SERIAL((" EVENT-> SERIAL_EV_CTS %s\n", + (bytes & TIOCM_CTS) ? "ON" : "OFF")); + *result |= SERIAL_EV_CTS; + ret = True; + } + } + + if (ret) + pser_inf->event_pending = 0; + + return ret; +} + +/* Read timeout for a given file descripter (device) when adding fd's to select() */ +BOOL +serial_get_timeout(NTHANDLE handle, uint32 length, uint32 * timeout, uint32 * itv_timeout) +{ + int index; + SERIAL_DEVICE *pser_inf; + + index = get_device_index(handle); + if (index < 0) + return True; + + if (g_rdpdr_device[index].device_type != DEVICE_TYPE_SERIAL) + { + return False; + } + + pser_inf = (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data; + + *timeout = + pser_inf->read_total_timeout_multiplier * length + + pser_inf->read_total_timeout_constant; + *itv_timeout = pser_inf->read_interval_timeout; + return True; +} + DEVICE_FNS serial_fns = { serial_create, serial_close,