1
0
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:
Simon Tatham
2006-08-28 10:35:12 +00:00
parent 38f003dbe9
commit 34f747421d
23 changed files with 1056 additions and 43 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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. */

View File

@ -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
View 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
};

View File

@ -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