2
* Serial back end (Windows-specific).
11
#define SERIAL_MAX_BACKLOG 4096
13
typedef struct serial_backend_data {
15
struct handle *out, *in;
19
int break_in_progress;
22
static void serial_terminate(Serial serial)
25
handle_free(serial->out);
29
handle_free(serial->in);
32
if (serial->port != INVALID_HANDLE_VALUE) {
33
if (serial->break_in_progress)
34
ClearCommBreak(serial->port);
35
CloseHandle(serial->port);
36
serial->port = INVALID_HANDLE_VALUE;
40
static int serial_gotdata(struct handle *h, void *data, int len)
42
Serial serial = (Serial)handle_get_privdata(h);
44
const char *error_msg;
47
* Currently, len==0 should never happen because we're
48
* ignoring EOFs. However, it seems not totally impossible
49
* that this same back end might be usable to talk to named
50
* pipes or some other non-serial device, in which case EOF
51
* may become meaningful here.
54
error_msg = "End of file reading from serial device";
56
error_msg = "Error reading from serial device";
58
serial_terminate(serial);
60
notify_remote_exit(serial->frontend);
62
logevent(serial->frontend, error_msg);
64
connection_fatal(serial->frontend, "%s", error_msg);
66
return 0; /* placate optimiser */
68
return from_backend(serial->frontend, 0, data, len);
72
static void serial_sentdata(struct handle *h, int new_backlog)
74
Serial serial = (Serial)handle_get_privdata(h);
75
if (new_backlog < 0) {
76
const char *error_msg = "Error writing to serial device";
78
serial_terminate(serial);
80
notify_remote_exit(serial->frontend);
82
logevent(serial->frontend, error_msg);
84
connection_fatal(serial->frontend, "%s", error_msg);
86
serial->bufsize = new_backlog;
90
static const char *serial_configure(Serial serial, HANDLE serport, Config *cfg)
93
COMMTIMEOUTS timeouts;
96
* Set up the serial port parameters. If we can't even
97
* GetCommState, we ignore the problem on the grounds that the
98
* user might have pointed us at some other type of two-way
99
* device instead of a serial port.
101
if (GetCommState(serport, &dcb)) {
109
dcb.fDtrControl = DTR_CONTROL_ENABLE;
110
dcb.fDsrSensitivity = FALSE;
111
dcb.fTXContinueOnXoff = FALSE;
114
dcb.fErrorChar = FALSE;
116
dcb.fRtsControl = RTS_CONTROL_ENABLE;
117
dcb.fAbortOnError = FALSE;
118
dcb.fOutxCtsFlow = FALSE;
119
dcb.fOutxDsrFlow = FALSE;
122
* Configurable parameters.
124
dcb.BaudRate = cfg->serspeed;
125
msg = dupprintf("Configuring baud rate %d", cfg->serspeed);
126
logevent(serial->frontend, msg);
129
dcb.ByteSize = cfg->serdatabits;
130
msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
131
logevent(serial->frontend, msg);
134
switch (cfg->serstopbits) {
135
case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break;
136
case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break;
137
case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break;
138
default: return "Invalid number of stop bits (need 1, 1.5 or 2)";
140
msg = dupprintf("Configuring %s data bits", str);
141
logevent(serial->frontend, msg);
144
switch (cfg->serparity) {
145
case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break;
146
case SER_PAR_ODD: dcb.Parity = ODDPARITY; str = "odd"; break;
147
case SER_PAR_EVEN: dcb.Parity = EVENPARITY; str = "even"; break;
148
case SER_PAR_MARK: dcb.Parity = MARKPARITY; str = "mark"; break;
149
case SER_PAR_SPACE: dcb.Parity = SPACEPARITY; str = "space"; break;
151
msg = dupprintf("Configuring %s parity", str);
152
logevent(serial->frontend, msg);
155
switch (cfg->serflow) {
159
case SER_FLOW_XONXOFF:
160
dcb.fOutX = dcb.fInX = TRUE;
163
case SER_FLOW_RTSCTS:
164
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
165
dcb.fOutxCtsFlow = TRUE;
168
case SER_FLOW_DSRDTR:
169
dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
170
dcb.fOutxDsrFlow = TRUE;
174
msg = dupprintf("Configuring %s flow control", str);
175
logevent(serial->frontend, msg);
178
if (!SetCommState(serport, &dcb))
179
return "Unable to configure serial port";
181
timeouts.ReadIntervalTimeout = 1;
182
timeouts.ReadTotalTimeoutMultiplier = 0;
183
timeouts.ReadTotalTimeoutConstant = 0;
184
timeouts.WriteTotalTimeoutMultiplier = 0;
185
timeouts.WriteTotalTimeoutConstant = 0;
186
if (!SetCommTimeouts(serport, &timeouts))
187
return "Unable to configure serial timeouts";
194
* Called to set up the serial connection.
196
* Returns an error message, or NULL on success.
198
* Also places the canonical host name into `realhost'. It must be
199
* freed by the caller.
201
static const char *serial_init(void *frontend_handle, void **backend_handle,
203
char *host, int port, char **realhost, int nodelay,
210
serial = snew(struct serial_backend_data);
211
serial->port = INVALID_HANDLE_VALUE;
212
serial->out = serial->in = NULL;
214
serial->break_in_progress = FALSE;
215
*backend_handle = serial;
217
serial->frontend = frontend_handle;
220
char *msg = dupprintf("Opening serial device %s", cfg->serline);
221
logevent(serial->frontend, msg);
226
* Munge the string supplied by the user into a Windows filename.
228
* Windows supports opening a few "legacy" devices (including
229
* COM1-9) by specifying their names verbatim as a filename to
230
* open. (Thus, no files can ever have these names. See
231
* <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>
232
* ("Naming a File") for the complete list of reserved names.)
234
* However, this doesn't let you get at devices COM10 and above.
235
* For that, you need to specify a filename like "\\.\COM10".
236
* This is also necessary for special serial and serial-like
237
* devices such as \\.\WCEUSBSH001. It also works for the "legacy"
238
* names, so you can do \\.\COM1 (verified as far back as Win95).
239
* See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>
240
* (CreateFile() docs).
242
* So, we believe that prepending "\\.\" should always be the
243
* Right Thing. However, just in case someone finds something to
244
* talk to that doesn't exist under there, if the serial line
245
* contains a backslash, we use it verbatim. (This also lets
246
* existing configurations using \\.\ continue working.)
250
strchr(cfg->serline, '\\') ? "" : "\\\\.\\",
252
serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
253
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
257
if (serport == INVALID_HANDLE_VALUE)
258
return "Unable to open serial port";
260
err = serial_configure(serial, serport, cfg);
264
serial->port = serport;
265
serial->out = handle_output_new(serport, serial_sentdata, serial,
266
HANDLE_FLAG_OVERLAPPED);
267
serial->in = handle_input_new(serport, serial_gotdata, serial,
268
HANDLE_FLAG_OVERLAPPED |
269
HANDLE_FLAG_IGNOREEOF |
270
HANDLE_FLAG_UNITBUFFER);
272
*realhost = dupstr(cfg->serline);
275
* Specials are always available.
277
update_specials_menu(serial->frontend);
282
static void serial_free(void *handle)
284
Serial serial = (Serial) handle;
286
serial_terminate(serial);
287
expire_timer_context(serial);
291
static void serial_reconfig(void *handle, Config *cfg)
293
Serial serial = (Serial) handle;
296
err = serial_configure(serial, serial->port, cfg);
299
* FIXME: what should we do if err returns something?
304
* Called to send data down the serial connection.
306
static int serial_send(void *handle, char *buf, int len)
308
Serial serial = (Serial) handle;
310
if (serial->out == NULL)
313
serial->bufsize = handle_write(serial->out, buf, len);
314
return serial->bufsize;
318
* Called to query the current sendability status.
320
static int serial_sendbuffer(void *handle)
322
Serial serial = (Serial) handle;
323
return serial->bufsize;
327
* Called to set the size of the window
329
static void serial_size(void *handle, int width, int height)
335
static void serbreak_timer(void *ctx, long now)
337
Serial serial = (Serial)ctx;
339
if (now >= serial->clearbreak_time && serial->port) {
340
ClearCommBreak(serial->port);
341
serial->break_in_progress = FALSE;
342
logevent(serial->frontend, "Finished serial break");
347
* Send serial special codes.
349
static void serial_special(void *handle, Telnet_Special code)
351
Serial serial = (Serial) handle;
353
if (serial->port && code == TS_BRK) {
354
logevent(serial->frontend, "Starting serial break at user request");
355
SetCommBreak(serial->port);
357
* To send a serial break on Windows, we call SetCommBreak
358
* to begin the break, then wait a bit, and then call
359
* ClearCommBreak to finish it. Hence, I must use timing.c
360
* to arrange a callback when it's time to do the latter.
362
* SUS says that a default break length must be between 1/4
363
* and 1/2 second. FreeBSD apparently goes with 2/5 second,
366
serial->clearbreak_time =
367
schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial);
368
serial->break_in_progress = TRUE;
375
* Return a list of the special codes that make sense in this
378
static const struct telnet_special *serial_get_specials(void *handle)
380
static const struct telnet_special specials[] = {
387
static int serial_connected(void *handle)
389
return 1; /* always connected */
392
static int serial_sendok(void *handle)
397
static void serial_unthrottle(void *handle, int backlog)
399
Serial serial = (Serial) handle;
401
handle_unthrottle(serial->in, backlog);
404
static int serial_ldisc(void *handle, int option)
407
* Local editing and local echo are off by default.
412
static void serial_provide_ldisc(void *handle, void *ldisc)
414
/* This is a stub. */
417
static void serial_provide_logctx(void *handle, void *logctx)
419
/* This is a stub. */
422
static int serial_exitcode(void *handle)
424
Serial serial = (Serial) handle;
425
if (serial->port != INVALID_HANDLE_VALUE)
426
return -1; /* still connected */
428
/* Exit codes are a meaningless concept with serial ports */
433
* cfg_info for Serial does nothing at all.
435
static int serial_cfg_info(void *handle)
440
Backend serial_backend = {
453
serial_provide_ldisc,
454
serial_provide_logctx,