mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-06-30 19:12:48 -05:00
Support for Windows PuTTY connecting straight to a local serial port
in place of making a network connection. This has involved a couple of minor infrastructure changes: - New dlg_label_change() function in the dialog.h interface, which alters the label on a control. Only used, at present, to switch the Host Name and Port boxes into Serial Line and Speed, which means that any platform not implementing serial connections (i.e. currently all but Windows) does not need to actually do anything in this function. Yet. - New small piece of infrastructure: cfg_launchable() determines whether a Config structure describes a session ready to be launched. This was previously determined by seeing if it had a non-empty host name, but it has to check the serial line as well so there's a centralised function for it. I haven't gone through all front ends and arranged for this function to be used everywhere it needs to be; so far I've only checked Windows. - Similarly, cfg_dest() returns the destination of a connection (host name or serial line) in a text format suitable for putting into messages such as `Unable to connect to %s'. [originally from svn r6815]
This commit is contained in:
@ -371,4 +371,9 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Serial back end is available on Windows.
|
||||
*/
|
||||
ser_setup_config_box(b, midsession);
|
||||
}
|
||||
|
@ -1161,10 +1161,11 @@ void progressbar(struct ctlpos *cp, int id)
|
||||
* Return value is a malloc'ed copy of the processed version of the
|
||||
* string.
|
||||
*/
|
||||
static char *shortcut_escape(char *text, char shortcut)
|
||||
static char *shortcut_escape(const char *text, char shortcut)
|
||||
{
|
||||
char *ret;
|
||||
char *p, *q;
|
||||
char const *p;
|
||||
char *q;
|
||||
|
||||
if (!text)
|
||||
return NULL; /* sfree won't choke on this */
|
||||
@ -2236,6 +2237,53 @@ void dlg_text_set(union control *ctrl, void *dlg, char const *text)
|
||||
SetDlgItemText(dp->hwnd, c->base_id, text);
|
||||
}
|
||||
|
||||
void dlg_label_change(union control *ctrl, void *dlg, char const *text)
|
||||
{
|
||||
struct dlgparam *dp = (struct dlgparam *)dlg;
|
||||
struct winctrl *c = dlg_findbyctrl(dp, ctrl);
|
||||
char *escaped = NULL;
|
||||
int id = -1;
|
||||
|
||||
assert(c);
|
||||
switch (c->ctrl->generic.type) {
|
||||
case CTRL_EDITBOX:
|
||||
escaped = shortcut_escape(text, c->ctrl->editbox.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
case CTRL_RADIO:
|
||||
escaped = shortcut_escape(text, c->ctrl->radio.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
case CTRL_CHECKBOX:
|
||||
escaped = shortcut_escape(text, ctrl->checkbox.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
case CTRL_BUTTON:
|
||||
escaped = shortcut_escape(text, ctrl->button.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
case CTRL_LISTBOX:
|
||||
escaped = shortcut_escape(text, ctrl->listbox.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
case CTRL_FILESELECT:
|
||||
escaped = shortcut_escape(text, ctrl->fileselect.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
case CTRL_FONTSELECT:
|
||||
escaped = shortcut_escape(text, ctrl->fontselect.shortcut);
|
||||
id = c->base_id;
|
||||
break;
|
||||
default:
|
||||
assert(!"Can't happen");
|
||||
break;
|
||||
}
|
||||
if (escaped) {
|
||||
SetDlgItemText(dp->hwnd, id, escaped);
|
||||
sfree(escaped);
|
||||
}
|
||||
}
|
||||
|
||||
void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
|
||||
{
|
||||
struct dlgparam *dp = (struct dlgparam *)dlg;
|
||||
|
@ -32,6 +32,8 @@ Filename platform_default_filename(const char *name)
|
||||
|
||||
char *platform_default_s(const char *name)
|
||||
{
|
||||
if (!strcmp(name, "SerialLine"))
|
||||
return dupstr("COM1");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ static void start_backend(void)
|
||||
if (error) {
|
||||
char *str = dupprintf("%s Error", appname);
|
||||
sprintf(msg, "Unable to open connection to\n"
|
||||
"%.800s\n" "%s", cfg.host, error);
|
||||
"%.800s\n" "%s", cfg_dest(&cfg), error);
|
||||
MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
|
||||
sfree(str);
|
||||
exit(0);
|
||||
|
@ -131,6 +131,12 @@
|
||||
#define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2"
|
||||
#define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2"
|
||||
#define WINHELP_CTX_ssh_bugs_rekey2 "ssh.bugs.rekey2"
|
||||
#define WINHELP_CTX_serial_line "serial.line"
|
||||
#define WINHELP_CTX_serial_speed "serial.speed"
|
||||
#define WINHELP_CTX_serial_databits "serial.databits"
|
||||
#define WINHELP_CTX_serial_stopbits "serial.stopbits"
|
||||
#define WINHELP_CTX_serial_parity "serial.parity"
|
||||
#define WINHELP_CTX_serial_flow "serial.flow"
|
||||
|
||||
/* These are used in Windows-specific bits of the frontend.
|
||||
* We (ab)use "help context identifiers" (dwContextId) to identify them. */
|
||||
|
@ -319,7 +319,7 @@ int main(int argc, char **argv)
|
||||
errors = 1;
|
||||
}
|
||||
} else if (*p) {
|
||||
if (!*cfg.host) {
|
||||
if (!cfg_launchable(&cfg)) {
|
||||
char *q = p;
|
||||
/*
|
||||
* If the hostname starts with "telnet:", set the
|
||||
@ -392,7 +392,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
Config cfg2;
|
||||
do_defaults(host, &cfg2);
|
||||
if (loaded_session || cfg2.host[0] == '\0') {
|
||||
if (loaded_session || !cfg_launchable(&cfg2)) {
|
||||
/* No settings for this host; use defaults */
|
||||
/* (or session was already loaded with -load) */
|
||||
strncpy(cfg.host, host, sizeof(cfg.host) - 1);
|
||||
@ -446,7 +446,7 @@ int main(int argc, char **argv)
|
||||
if (errors)
|
||||
return 1;
|
||||
|
||||
if (!*cfg.host) {
|
||||
if (!cfg_launchable(&cfg)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
@ -459,7 +459,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* See if host is of the form user@host */
|
||||
if (cfg.host[0] != '\0') {
|
||||
if (cfg_launchable(&cfg)) {
|
||||
char *atsign = strrchr(cfg.host, '@');
|
||||
/* Make sure we're not overflowing the user field */
|
||||
if (atsign) {
|
||||
|
396
windows/winser.c
Normal file
396
windows/winser.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#define SERIAL_MAX_BACKLOG 4096
|
||||
|
||||
typedef struct serial_backend_data {
|
||||
HANDLE port;
|
||||
struct handle *out, *in;
|
||||
void *frontend;
|
||||
int bufsize;
|
||||
} *Serial;
|
||||
|
||||
static void serial_terminate(Serial serial)
|
||||
{
|
||||
if (serial->out) {
|
||||
handle_free(serial->out);
|
||||
serial->out = NULL;
|
||||
}
|
||||
if (serial->in) {
|
||||
handle_free(serial->in);
|
||||
serial->in = NULL;
|
||||
}
|
||||
if (serial->port) {
|
||||
CloseHandle(serial->port);
|
||||
serial->port = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int serial_gotdata(struct handle *h, void *data, int len)
|
||||
{
|
||||
Serial serial = (Serial)handle_get_privdata(h);
|
||||
if (len <= 0) {
|
||||
const char *error_msg;
|
||||
|
||||
/*
|
||||
* Currently, len==0 should never happen because we're
|
||||
* ignoring EOFs. However, it seems not totally impossible
|
||||
* that this same back end might be usable to talk to named
|
||||
* pipes or some other non-serial device, in which case EOF
|
||||
* may become meaningful here.
|
||||
*/
|
||||
if (len == 0)
|
||||
error_msg = "End of file reading from serial device";
|
||||
else
|
||||
error_msg = "Error reading from serial device";
|
||||
|
||||
serial_terminate(serial);
|
||||
|
||||
notify_remote_exit(serial->frontend);
|
||||
|
||||
logevent(serial->frontend, error_msg);
|
||||
|
||||
connection_fatal(serial->frontend, "%s", error_msg);
|
||||
|
||||
return 0; /* placate optimiser */
|
||||
} else {
|
||||
return from_backend(serial->frontend, 0, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_sentdata(struct handle *h, int new_backlog)
|
||||
{
|
||||
Serial serial = (Serial)handle_get_privdata(h);
|
||||
if (new_backlog < 0) {
|
||||
const char *error_msg = "Error writing to serial device";
|
||||
|
||||
serial_terminate(serial);
|
||||
|
||||
notify_remote_exit(serial->frontend);
|
||||
|
||||
logevent(serial->frontend, error_msg);
|
||||
|
||||
connection_fatal(serial->frontend, "%s", error_msg);
|
||||
} else {
|
||||
serial->bufsize = new_backlog;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
|
||||
{
|
||||
DCB dcb;
|
||||
COMMTIMEOUTS timeouts;
|
||||
|
||||
/*
|
||||
* Set up the serial port parameters. If we can't even
|
||||
* GetCommState, we ignore the problem on the grounds that the
|
||||
* user might have pointed us at some other type of two-way
|
||||
* device instead of a serial port.
|
||||
*/
|
||||
if (GetCommState(serport, &dcb)) {
|
||||
char *msg;
|
||||
const char *str;
|
||||
|
||||
/*
|
||||
* Boilerplate.
|
||||
*/
|
||||
dcb.fBinary = TRUE;
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcb.fDsrSensitivity = FALSE;
|
||||
dcb.fTXContinueOnXoff = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fErrorChar = FALSE;
|
||||
dcb.fNull = FALSE;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
dcb.fAbortOnError = FALSE;
|
||||
dcb.fOutxCtsFlow = FALSE;
|
||||
dcb.fOutxDsrFlow = FALSE;
|
||||
|
||||
/*
|
||||
* Configurable parameters.
|
||||
*/
|
||||
dcb.BaudRate = cfg->serspeed;
|
||||
msg = dupprintf("Configuring baud rate %d", cfg->serspeed);
|
||||
logevent(serial->frontend, msg);
|
||||
sfree(msg);
|
||||
|
||||
dcb.ByteSize = cfg->serdatabits;
|
||||
msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
|
||||
logevent(serial->frontend, msg);
|
||||
sfree(msg);
|
||||
|
||||
switch (cfg->serstopbits) {
|
||||
case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;
|
||||
case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;
|
||||
case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;
|
||||
default: return "Invalid number of stop bits (need 1, 1.5 or 2)";
|
||||
}
|
||||
msg = dupprintf("Configuring %s data bits", str);
|
||||
logevent(serial->frontend, msg);
|
||||
sfree(msg);
|
||||
|
||||
switch (cfg->serparity) {
|
||||
case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;
|
||||
case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;
|
||||
case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;
|
||||
case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;
|
||||
case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;
|
||||
}
|
||||
msg = dupprintf("Configuring %s parity", str);
|
||||
logevent(serial->frontend, msg);
|
||||
sfree(msg);
|
||||
|
||||
switch (cfg->serflow) {
|
||||
case SER_FLOW_NONE:
|
||||
str = "no";
|
||||
break;
|
||||
case SER_FLOW_XONXOFF:
|
||||
dcb.fOutX = dcb.fInX = TRUE;
|
||||
str = "XON/XOFF";
|
||||
break;
|
||||
case SER_FLOW_RTSCTS:
|
||||
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
|
||||
dcb.fOutxCtsFlow = TRUE;
|
||||
str = "RTS/CTS";
|
||||
break;
|
||||
case SER_FLOW_DSRDTR:
|
||||
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
|
||||
dcb.fOutxDsrFlow = TRUE;
|
||||
str = "DSR/DTR";
|
||||
break;
|
||||
}
|
||||
msg = dupprintf("Configuring %s flow control", str);
|
||||
logevent(serial->frontend, msg);
|
||||
sfree(msg);
|
||||
|
||||
if (!SetCommState(serport, &dcb))
|
||||
return "Unable to configure serial port";
|
||||
|
||||
timeouts.ReadIntervalTimeout = 1;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 0;
|
||||
timeouts.ReadTotalTimeoutConstant = 0;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 0;
|
||||
timeouts.WriteTotalTimeoutConstant = 0;
|
||||
if (!SetCommTimeouts(serport, &timeouts))
|
||||
return "Unable to configure serial timeouts";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set up the serial connection.
|
||||
*
|
||||
* Returns an error message, or NULL on success.
|
||||
*
|
||||
* Also places the canonical host name into `realhost'. It must be
|
||||
* freed by the caller.
|
||||
*/
|
||||
static const char *serial_init(void *frontend_handle, void **backend_handle,
|
||||
Config *cfg,
|
||||
char *host, int port, char **realhost, int nodelay,
|
||||
int keepalive)
|
||||
{
|
||||
Serial serial;
|
||||
HANDLE serport;
|
||||
const char *err;
|
||||
|
||||
serial = snew(struct serial_backend_data);
|
||||
serial->port = NULL;
|
||||
serial->out = serial->in = NULL;
|
||||
*backend_handle = serial;
|
||||
|
||||
serial->frontend = frontend_handle;
|
||||
|
||||
{
|
||||
char *msg = dupprintf("Opening serial device %s", host);
|
||||
logevent(serial->frontend, msg);
|
||||
}
|
||||
|
||||
serport = CreateFile(cfg->serline, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (serport == INVALID_HANDLE_VALUE)
|
||||
return "Unable to open serial port";
|
||||
|
||||
err = serial_configure(serial, serport, cfg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
serial->port = serport;
|
||||
serial->out = handle_output_new(serport, serial_sentdata, serial,
|
||||
HANDLE_FLAG_OVERLAPPED);
|
||||
serial->in = handle_input_new(serport, serial_gotdata, serial,
|
||||
HANDLE_FLAG_OVERLAPPED |
|
||||
HANDLE_FLAG_IGNOREEOF);
|
||||
|
||||
*realhost = dupstr(cfg->serline);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void serial_free(void *handle)
|
||||
{
|
||||
Serial serial = (Serial) handle;
|
||||
|
||||
serial_terminate(serial);
|
||||
sfree(serial);
|
||||
}
|
||||
|
||||
static void serial_reconfig(void *handle, Config *cfg)
|
||||
{
|
||||
Serial serial = (Serial) handle;
|
||||
const char *err;
|
||||
|
||||
err = serial_configure(serial, serial->port, cfg);
|
||||
|
||||
/*
|
||||
* FIXME: what should we do if err returns something?
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the serial connection.
|
||||
*/
|
||||
static int serial_send(void *handle, char *buf, int len)
|
||||
{
|
||||
Serial serial = (Serial) handle;
|
||||
|
||||
if (serial->out == NULL)
|
||||
return 0;
|
||||
|
||||
serial->bufsize = handle_write(serial->out, buf, len);
|
||||
return serial->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to query the current sendability status.
|
||||
*/
|
||||
static int serial_sendbuffer(void *handle)
|
||||
{
|
||||
Serial serial = (Serial) handle;
|
||||
return serial->bufsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to set the size of the window
|
||||
*/
|
||||
static void serial_size(void *handle, int width, int height)
|
||||
{
|
||||
/* Do nothing! */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send serial special codes.
|
||||
*/
|
||||
static void serial_special(void *handle, Telnet_Special code)
|
||||
{
|
||||
/*
|
||||
* FIXME: serial break? XON? XOFF?
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a list of the special codes that make sense in this
|
||||
* protocol.
|
||||
*/
|
||||
static const struct telnet_special *serial_get_specials(void *handle)
|
||||
{
|
||||
/*
|
||||
* FIXME: serial break? XON? XOFF?
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int serial_connected(void *handle)
|
||||
{
|
||||
return 1; /* always connected */
|
||||
}
|
||||
|
||||
static int serial_sendok(void *handle)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void serial_unthrottle(void *handle, int backlog)
|
||||
{
|
||||
Serial serial = (Serial) handle;
|
||||
if (serial->in)
|
||||
handle_unthrottle(serial->in, backlog);
|
||||
}
|
||||
|
||||
static int serial_ldisc(void *handle, int option)
|
||||
{
|
||||
/*
|
||||
* Local editing and local echo are off by default.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serial_provide_ldisc(void *handle, void *ldisc)
|
||||
{
|
||||
/* This is a stub. */
|
||||
}
|
||||
|
||||
static void serial_provide_logctx(void *handle, void *logctx)
|
||||
{
|
||||
/* This is a stub. */
|
||||
}
|
||||
|
||||
static int serial_exitcode(void *handle)
|
||||
{
|
||||
Serial serial = (Serial) handle;
|
||||
if (serial->port != NULL)
|
||||
return -1; /* still connected */
|
||||
else
|
||||
/* Exit codes are a meaningless concept with serial ports */
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfg_info for Serial does nothing at all.
|
||||
*/
|
||||
static int serial_cfg_info(void *handle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Backend serial_backend = {
|
||||
serial_init,
|
||||
serial_free,
|
||||
serial_reconfig,
|
||||
serial_send,
|
||||
serial_sendbuffer,
|
||||
serial_size,
|
||||
serial_special,
|
||||
serial_get_specials,
|
||||
serial_connected,
|
||||
serial_exitcode,
|
||||
serial_sendok,
|
||||
serial_ldisc,
|
||||
serial_provide_ldisc,
|
||||
serial_provide_logctx,
|
||||
serial_unthrottle,
|
||||
serial_cfg_info,
|
||||
1
|
||||
};
|
@ -437,4 +437,9 @@ void agent_schedule_callback(void (*callback)(void *, void *, int),
|
||||
void *callback_ctx, void *data, int len);
|
||||
#define FLAG_SYNCAGENT 0x1000
|
||||
|
||||
/*
|
||||
* Exports from winser.c.
|
||||
*/
|
||||
extern Backend serial_backend;
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user