X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/dcf640d6d86e98b149c6bb1256c6c2afc5385405..a4451dd11df30dc2d0d262dd6c36642aa2a02b91:/windows/winser.c diff --git a/windows/winser.c b/windows/winser.c index fac04b1d..ab88406d 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -2,17 +2,6 @@ * Serial back end (Windows-specific). */ -/* - * TODO: - * - * - sending breaks? - * + looks as if you do this by calling SetCommBreak(handle), - * then waiting a bit, then doing ClearCommBreak(handle). A - * small job for timing.c, methinks. - * - * - why are we dropping data when talking to judicator? - */ - #include #include #include @@ -26,6 +15,8 @@ typedef struct serial_backend_data { struct handle *out, *in; void *frontend; int bufsize; + long clearbreak_time; + int break_in_progress; } *Serial; static void serial_terminate(Serial serial) @@ -38,9 +29,11 @@ static void serial_terminate(Serial serial) handle_free(serial->in); serial->in = NULL; } - if (serial->port) { + if (serial->port != INVALID_HANDLE_VALUE) { + if (serial->break_in_progress) + ClearCommBreak(serial->port); CloseHandle(serial->port); - serial->port = NULL; + serial->port = INVALID_HANDLE_VALUE; } } @@ -215,20 +208,52 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, const char *err; serial = snew(struct serial_backend_data); - serial->port = NULL; + serial->port = INVALID_HANDLE_VALUE; serial->out = serial->in = NULL; serial->bufsize = 0; + serial->break_in_progress = FALSE; *backend_handle = serial; serial->frontend = frontend_handle; { - char *msg = dupprintf("Opening serial device %s", host); + char *msg = dupprintf("Opening serial device %s", cfg->serline); logevent(serial->frontend, msg); } - serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + { + /* + * Munge the string supplied by the user into a Windows filename. + * + * Windows supports opening a few "legacy" devices (including + * COM1-9) by specifying their names verbatim as a filename to + * open. (Thus, no files can ever have these names. See + * + * ("Naming a File") for the complete list of reserved names.) + * + * However, this doesn't let you get at devices COM10 and above. + * For that, you need to specify a filename like "\\.\COM10". + * This is also necessary for special serial and serial-like + * devices such as \\.\WCEUSBSH001. It also works for the "legacy" + * names, so you can do \\.\COM1 (verified as far back as Win95). + * See + * (CreateFile() docs). + * + * So, we believe that prepending "\\.\" should always be the + * Right Thing. However, just in case someone finds something to + * talk to that doesn't exist under there, if the serial line + * contains a backslash, we use it verbatim. (This also lets + * existing configurations using \\.\ continue working.) + */ + char *serfilename = + dupprintf("%s%s", + strchr(cfg->serline, '\\') ? "" : "\\\\.\\", + cfg->serline); + serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + sfree(serfilename); + } + if (serport == INVALID_HANDLE_VALUE) return "Unable to open serial port"; @@ -241,10 +266,16 @@ static const char *serial_init(void *frontend_handle, void **backend_handle, HANDLE_FLAG_OVERLAPPED); serial->in = handle_input_new(serport, serial_gotdata, serial, HANDLE_FLAG_OVERLAPPED | - HANDLE_FLAG_IGNOREEOF); + HANDLE_FLAG_IGNOREEOF | + HANDLE_FLAG_UNITBUFFER); *realhost = dupstr(cfg->serline); + /* + * Specials are always available. + */ + update_specials_menu(serial->frontend); + return NULL; } @@ -253,6 +284,7 @@ static void serial_free(void *handle) Serial serial = (Serial) handle; serial_terminate(serial); + expire_timer_context(serial); sfree(serial); } @@ -300,14 +332,42 @@ static void serial_size(void *handle, int width, int height) return; } +static void serbreak_timer(void *ctx, long now) +{ + Serial serial = (Serial)ctx; + + if (now >= serial->clearbreak_time && serial->port) { + ClearCommBreak(serial->port); + serial->break_in_progress = FALSE; + logevent(serial->frontend, "Finished serial break"); + } +} + /* * Send serial special codes. */ static void serial_special(void *handle, Telnet_Special code) { - /* - * FIXME: serial break? XON? XOFF? - */ + Serial serial = (Serial) handle; + + if (serial->port && code == TS_BRK) { + logevent(serial->frontend, "Starting serial break at user request"); + SetCommBreak(serial->port); + /* + * To send a serial break on Windows, we call SetCommBreak + * to begin the break, then wait a bit, and then call + * ClearCommBreak to finish it. Hence, I must use timing.c + * to arrange a callback when it's time to do the latter. + * + * SUS says that a default break length must be between 1/4 + * and 1/2 second. FreeBSD apparently goes with 2/5 second, + * and so will I. + */ + serial->clearbreak_time = + schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial); + serial->break_in_progress = TRUE; + } + return; } @@ -317,10 +377,11 @@ static void serial_special(void *handle, Telnet_Special code) */ static const struct telnet_special *serial_get_specials(void *handle) { - /* - * FIXME: serial break? XON? XOFF? - */ - return NULL; + static const struct telnet_special specials[] = { + {"Break", TS_BRK}, + {NULL, TS_EXITMENU} + }; + return specials; } static int serial_connected(void *handle) @@ -361,7 +422,7 @@ static void serial_provide_logctx(void *handle, void *logctx) static int serial_exitcode(void *handle) { Serial serial = (Serial) handle; - if (serial->port != NULL) + if (serial->port != INVALID_HANDLE_VALUE) return -1; /* still connected */ else /* Exit codes are a meaningless concept with serial ports */ @@ -393,5 +454,7 @@ Backend serial_backend = { serial_provide_logctx, serial_unthrottle, serial_cfg_info, - 1 + "serial", + PROT_SERIAL, + 0 };