--- sourceforge.net/trunk/rdesktop/serial.c 2003/07/01 09:31:25 432 +++ sourceforge.net/trunk/rdesktop/serial.c 2004/02/07 17:32:21 602 @@ -1,6 +1,7 @@ #include #include #include +#include #include "rdesktop.h" #define FILE_DEVICE_SERIAL_PORT 0x1b @@ -52,150 +53,419 @@ #define ODD_PARITY 1 #define EVEN_PARITY 2 -int serial_fd; -struct termios termios; +extern RDPDR_DEVICE g_rdpdr_device[]; -int dtr; -uint32 baud_rate; -uint32 queue_in_size, queue_out_size; -uint32 wait_mask; -uint8 stop_bits, parity, word_length; +SERIAL_DEVICE * +get_serial_info(HANDLE 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) +BOOL +get_termios(SERIAL_DEVICE * pser_inf, HANDLE 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) { - case B75: baud_rate = 75; break; - case B110: baud_rate = 110; break; - case B134: baud_rate = 134; break; - case B150: baud_rate = 150; break; - case B300: baud_rate = 300; break; - case B600: baud_rate = 600; break; - case B1200: baud_rate = 1200; break; - case B1800: baud_rate = 1800; break; - case B2400: baud_rate = 2400; break; - case B4800: baud_rate = 4800; break; - case B9600: baud_rate = 9600; break; - case B19200: baud_rate = 19200; break; - case B38400: baud_rate = 38400; break; - case B57600: baud_rate = 57600; break; - case B115200: baud_rate = 115200; break; - default: baud_rate = 0; break; - } - - speed = cfgetospeed(&termios); - 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) - { - case CS5: word_length = 5; break; - case CS6: word_length = 6; break; - case CS7: word_length = 7; break; - default: word_length = 8; break; +#ifdef B75 + case B75: + pser_inf->baud_rate = 75; + break; +#endif +#ifdef B110 + case B110: + pser_inf->baud_rate = 110; + break; +#endif +#ifdef B134 + case B134: + pser_inf->baud_rate = 134; + break; +#endif +#ifdef B150 + case B150: + pser_inf->baud_rate = 150; + break; +#endif +#ifdef B300 + case B300: + pser_inf->baud_rate = 300; + break; +#endif +#ifdef B600 + case B600: + pser_inf->baud_rate = 600; + break; +#endif +#ifdef B1200 + case B1200: + pser_inf->baud_rate = 1200; + break; +#endif +#ifdef B1800 + case B1800: + pser_inf->baud_rate = 1800; + break; +#endif +#ifdef B2400 + case B2400: + pser_inf->baud_rate = 2400; + break; +#endif +#ifdef B4800 + case B4800: + pser_inf->baud_rate = 4800; + break; +#endif +#ifdef B9600 + case B9600: + pser_inf->baud_rate = 9600; + break; +#endif +#ifdef B19200 + case B19200: + pser_inf->baud_rate = 19200; + break; +#endif +#ifdef B38400 + case B38400: + pser_inf->baud_rate = 38400; + break; +#endif +#ifdef B57600 + case B57600: + pser_inf->baud_rate = 57600; + break; +#endif +#ifdef B115200 + case B115200: + pser_inf->baud_rate = 115200; + break; +#endif + default: + pser_inf->baud_rate = 0; + break; + } + + speed = cfgetospeed(ptermios); + pser_inf->dtr = (speed == B0) ? 0 : 1; + + 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: + pser_inf->word_length = 5; + break; + case CS6: + pser_inf->word_length = 6; + break; + case CS7: + pser_inf->word_length = 7; + break; + default: + pser_inf->word_length = 8; + break; } return True; } static void -set_termios(void) +set_termios(SERIAL_DEVICE * pser_inf, HANDLE serial_fd) { speed_t speed; - switch (baud_rate) + struct termios *ptermios; + + ptermios = pser_inf->ptermios; + + + switch (pser_inf->baud_rate) { - case 75: speed = B75; break; - case 110: speed = B110; break; - case 134: speed = B134; break; - case 150: speed = B150; break; - case 300: speed = B300; break; - case 600: speed = B600; break; - case 1200: speed = B1200; break; - case 1800: speed = B1800; break; - case 2400: speed = B2400; break; - case 4800: speed = B4800; break; - case 9600: speed = B9600; break; - case 19200: speed = B19200; break; - case 38400: speed = B38400; break; - case 57600: speed = B57600; break; - case 115200: speed = B115200; break; - default: speed = B0; break; +#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 + default: + speed = B0; + break; } /* 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); - termios.c_cflag &= ~(CSTOPB|PARENB|PARODD|CSIZE); - switch (stop_bits) + ptermios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CSIZE); + switch (pser_inf->stop_bits) { case STOP_BITS_2: - termios.c_cflag |= CSTOPB; + 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; } - switch (word_length) + switch (pser_inf->word_length) { - case 5: termios.c_cflag |= CS5; break; - case 6: termios.c_cflag |= CS6; break; - case 7: termios.c_cflag |= CS7; break; - default: termios.c_cflag |= CS8; break; + case 5: + ptermios->c_cflag |= CS5; + break; + case 6: + ptermios->c_cflag |= CS6; + break; + case 7: + ptermios->c_cflag |= CS7; + break; + default: + ptermios->c_cflag |= CS8; + break; } - tcsetattr(serial_fd, TCSANOW, &termios); + tcsetattr(serial_fd, TCSANOW, ptermios); } -static NTSTATUS -serial_create(HANDLE *handle) +/* 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) { - /* XXX do we have to handle concurrent open attempts? */ - serial_fd = open("/dev/ttyS0", O_RDWR); + 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)); + pser_inf->pold_termios = (struct termios *) xmalloc(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; +} + +NTSTATUS +serial_create(uint32 device_id, uint32 access, uint32 share_mode, uint32 disposition, + uint32 flags_and_attributes, char *filename, HANDLE * handle) +{ + HANDLE 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); + if (serial_fd == -1) + { + perror("open"); return STATUS_ACCESS_DENIED; + } - if (!get_termios()) + if (!get_termios(pser_inf, serial_fd)) return STATUS_ACCESS_DENIED; - *handle = 0; + // Store handle for later use + g_rdpdr_device[device_id].handle = serial_fd; + + /* some sane information */ + printf("INFO: SERIAL %s to %s\nINFO: speed %u baud, stop bits %u, parity %u, word length %u bits, dtr %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); + printf("INFO: use stty to change settings\n"); + +/* ptermios->c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD; + ptermios->c_cflag |= CREAD; + ptermios->c_lflag |= ICANON; + ptermios->c_iflag = IGNPAR | ICRNL; + + tcsetattr(serial_fd, TCSANOW, ptermios); +*/ + + *handle = serial_fd; + + /* all read and writes should be non blocking */ + if (fcntl(*handle, F_SETFL, O_NONBLOCK) == -1) + perror("fcntl"); + return STATUS_SUCCESS; } static NTSTATUS serial_close(HANDLE handle) { - close(serial_fd); + g_rdpdr_device[get_device_index(handle)].handle = 0; + close(handle); return STATUS_SUCCESS; } -static NTSTATUS -serial_read(HANDLE handle, uint8 *data, uint32 length, uint32 *result) +NTSTATUS +serial_read(HANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result) { - *result = read(serial_fd, data, length); + long timeout; + SERIAL_DEVICE *pser_inf; + struct termios *ptermios; + + 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); + + + *result = read(handle, data, length); + return STATUS_SUCCESS; } -static NTSTATUS -serial_write(HANDLE handle, uint8 *data, uint32 length, uint32 *result) +NTSTATUS +serial_write(HANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result) { - *result = write(serial_fd, data, length); + *result = write(handle, data, length); return STATUS_SUCCESS; } @@ -204,10 +474,15 @@ { uint32 result; 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; @@ -217,30 +492,30 @@ 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); break; case SERIAL_GET_BAUD_RATE: - out_uint32_le(out, baud_rate); + out_uint32_le(out, 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); 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); break; case SERIAL_GET_LINE_CONTROL: - out_uint8(out, stop_bits); - out_uint8(out, parity); - out_uint8(out, word_length); + 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: in_uint8(in, immediate); - serial_write(handle, &immediate, 1, &result); + serial_write(handle, &immediate, 1, 0, &result); break; case SERIAL_CONFIG_SIZE: out_uint32_le(out, 0); @@ -253,7 +528,7 @@ break; case SERIAL_GET_HANDFLOW: out_uint32_le(out, 0); - out_uint32_le(out, 3); /* Xon/Xoff */ + out_uint32_le(out, 3); /* Xon/Xoff */ out_uint32_le(out, 0); out_uint32_le(out, 0); break; @@ -267,18 +542,18 @@ out_uint8s(out, 20); break; case SERIAL_GET_WAIT_MASK: - out_uint32(out, 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); break; case SERIAL_SET_DTR: - dtr = 1; - set_termios(); + pser_inf->dtr = 1; + set_termios(pser_inf, handle); break; case SERIAL_CLR_DTR: - dtr = 0; - set_termios(); + pser_inf->dtr = 0; + set_termios(pser_inf, handle); break; #if 0 case SERIAL_WAIT_ON_MASK: @@ -288,9 +563,21 @@ tcsendbreak(serial_fd, 0); break; case SERIAL_PURGE: - in_uint32(purge_mask); - /* tcflush */ + + printf("SERIAL_PURGE\n"); + in_uint32(in, purge_mask); + if (purge_mask & 0x04) + flush_mask |= TCOFLUSH; + if (purge_mask & 0x08) + flush_mask |= TCIFLUSH; + if (flush_mask != 0) + tcflush(handle, flush_mask); + if (purge_mask & 0x01) + rdpdr_abort_io(handle, 4, STATUS_CANCELLED); + if (purge_mask & 0x02) + rdpdr_abort_io(handle, 3, STATUS_CANCELLED); break; + case SERIAL_RESET_DEVICE: case SERIAL_SET_BREAK_OFF: case SERIAL_SET_RTS: @@ -309,12 +596,33 @@ return STATUS_SUCCESS; } -DEVICE_FNS serial_fns = +/* Read timeout for a given file descripter (device) when adding fd's to select() */ +BOOL +serial_get_timeout(HANDLE handle, uint32 length, uint32 * timeout, uint32 * itv_timeout) { + int index; + SERIAL_DEVICE *pser_inf; + + index = get_device_index(handle); + + 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, serial_read, serial_write, serial_device_control }; -