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
, Conf
*conf
)
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
= conf_get_int(conf
, CONF_serspeed
);
125 msg
= dupprintf("Configuring baud rate %d", dcb
.BaudRate
);
126 logevent(serial
->frontend
, msg
);
129 dcb
.ByteSize
= conf_get_int(conf
, CONF_serdatabits
);
130 msg
= dupprintf("Configuring %d data bits", dcb
.ByteSize
);
131 logevent(serial
->frontend
, msg
);
134 switch (conf_get_int(conf
, CONF_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 (conf_get_int(conf
, CONF_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 (conf_get_int(conf
, CONF_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
,
202 Conf
*conf
, char *host
, int port
,
203 char **realhost
, int nodelay
, int keepalive
)
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
;
219 serline
= conf_get_str(conf
, CONF_serline
);
221 char *msg
= dupprintf("Opening serial device %s", serline
);
222 logevent(serial
->frontend
, msg
);
227 * Munge the string supplied by the user into a Windows filename.
229 * Windows supports opening a few "legacy" devices (including
230 * COM1-9) by specifying their names verbatim as a filename to
231 * open. (Thus, no files can ever have these names. See
232 * <http://msdn2.microsoft.com/en-us/library/aa365247.aspx>
233 * ("Naming a File") for the complete list of reserved names.)
235 * However, this doesn't let you get at devices COM10 and above.
236 * For that, you need to specify a filename like "\\.\COM10".
237 * This is also necessary for special serial and serial-like
238 * devices such as \\.\WCEUSBSH001. It also works for the "legacy"
239 * names, so you can do \\.\COM1 (verified as far back as Win95).
240 * See <http://msdn2.microsoft.com/en-us/library/aa363858.aspx>
241 * (CreateFile() docs).
243 * So, we believe that prepending "\\.\" should always be the
244 * Right Thing. However, just in case someone finds something to
245 * talk to that doesn't exist under there, if the serial line
246 * contains a backslash, we use it verbatim. (This also lets
247 * existing configurations using \\.\ continue working.)
250 dupprintf("%s%s", strchr(serline
, '\\') ?
"" : "\\\\.\\", serline
);
251 serport
= CreateFile(serfilename
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
,
252 OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
, NULL
);
256 if (serport
== INVALID_HANDLE_VALUE
)
257 return "Unable to open serial port";
259 err
= serial_configure(serial
, serport
, conf
);
263 serial
->port
= serport
;
264 serial
->out
= handle_output_new(serport
, serial_sentdata
, serial
,
265 HANDLE_FLAG_OVERLAPPED
);
266 serial
->in
= handle_input_new(serport
, serial_gotdata
, serial
,
267 HANDLE_FLAG_OVERLAPPED
|
268 HANDLE_FLAG_IGNOREEOF
|
269 HANDLE_FLAG_UNITBUFFER
);
271 *realhost
= dupstr(serline
);
274 * Specials are always available.
276 update_specials_menu(serial
->frontend
);
281 static void serial_free(void *handle
)
283 Serial serial
= (Serial
) handle
;
285 serial_terminate(serial
);
286 expire_timer_context(serial
);
290 static void serial_reconfig(void *handle
, Conf
*conf
)
292 Serial serial
= (Serial
) handle
;
295 err
= serial_configure(serial
, serial
->port
, conf
);
298 * FIXME: what should we do if err returns something?
303 * Called to send data down the serial connection.
305 static int serial_send(void *handle
, char *buf
, int len
)
307 Serial serial
= (Serial
) handle
;
309 if (serial
->out
== NULL
)
312 serial
->bufsize
= handle_write(serial
->out
, buf
, len
);
313 return serial
->bufsize
;
317 * Called to query the current sendability status.
319 static int serial_sendbuffer(void *handle
)
321 Serial serial
= (Serial
) handle
;
322 return serial
->bufsize
;
326 * Called to set the size of the window
328 static void serial_size(void *handle
, int width
, int height
)
334 static void serbreak_timer(void *ctx
, unsigned long now
)
336 Serial serial
= (Serial
)ctx
;
338 if (now
== serial
->clearbreak_time
&& serial
->port
) {
339 ClearCommBreak(serial
->port
);
340 serial
->break_in_progress
= FALSE
;
341 logevent(serial
->frontend
, "Finished serial break");
346 * Send serial special codes.
348 static void serial_special(void *handle
, Telnet_Special code
)
350 Serial serial
= (Serial
) handle
;
352 if (serial
->port
&& code
== TS_BRK
) {
353 logevent(serial
->frontend
, "Starting serial break at user request");
354 SetCommBreak(serial
->port
);
356 * To send a serial break on Windows, we call SetCommBreak
357 * to begin the break, then wait a bit, and then call
358 * ClearCommBreak to finish it. Hence, I must use timing.c
359 * to arrange a callback when it's time to do the latter.
361 * SUS says that a default break length must be between 1/4
362 * and 1/2 second. FreeBSD apparently goes with 2/5 second,
365 serial
->clearbreak_time
=
366 schedule_timer(TICKSPERSEC
* 2 / 5, serbreak_timer
, serial
);
367 serial
->break_in_progress
= TRUE
;
374 * Return a list of the special codes that make sense in this
377 static const struct telnet_special
*serial_get_specials(void *handle
)
379 static const struct telnet_special specials
[] = {
386 static int serial_connected(void *handle
)
388 return 1; /* always connected */
391 static int serial_sendok(void *handle
)
396 static void serial_unthrottle(void *handle
, int backlog
)
398 Serial serial
= (Serial
) handle
;
400 handle_unthrottle(serial
->in
, backlog
);
403 static int serial_ldisc(void *handle
, int option
)
406 * Local editing and local echo are off by default.
411 static void serial_provide_ldisc(void *handle
, void *ldisc
)
413 /* This is a stub. */
416 static void serial_provide_logctx(void *handle
, void *logctx
)
418 /* This is a stub. */
421 static int serial_exitcode(void *handle
)
423 Serial serial
= (Serial
) handle
;
424 if (serial
->port
!= INVALID_HANDLE_VALUE
)
425 return -1; /* still connected */
427 /* Exit codes are a meaningless concept with serial ports */
432 * cfg_info for Serial does nothing at all.
434 static int serial_cfg_info(void *handle
)
439 Backend serial_backend
= {
452 serial_provide_ldisc
,
453 serial_provide_logctx
,