OSDN Git Service

Add VC++ Project files for PuTTY DLL without exported functions.
[ffftp/ffftp.git] / putty / UNIX / UXSER.C
1 /*\r
2  * Serial back end (Unix-specific).\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <assert.h>\r
8 #include <limits.h>\r
9 \r
10 #include <errno.h>\r
11 #include <unistd.h>\r
12 #include <fcntl.h>\r
13 #include <termios.h>\r
14 \r
15 #include "putty.h"\r
16 #include "tree234.h"\r
17 \r
18 #define SERIAL_MAX_BACKLOG 4096\r
19 \r
20 typedef struct serial_backend_data {\r
21     void *frontend;\r
22     int fd;\r
23     int finished;\r
24     int inbufsize;\r
25     bufchain output_data;\r
26 } *Serial;\r
27 \r
28 /*\r
29  * We store our serial backends in a tree sorted by fd, so that\r
30  * when we get an uxsel notification we know which backend instance\r
31  * is the owner of the serial port that caused it.\r
32  */\r
33 static int serial_compare_by_fd(void *av, void *bv)\r
34 {\r
35     Serial a = (Serial)av;\r
36     Serial b = (Serial)bv;\r
37 \r
38     if (a->fd < b->fd)\r
39         return -1;\r
40     else if (a->fd > b->fd)\r
41         return +1;\r
42     return 0;\r
43 }\r
44 \r
45 static int serial_find_by_fd(void *av, void *bv)\r
46 {\r
47     int a = *(int *)av;\r
48     Serial b = (Serial)bv;\r
49 \r
50     if (a < b->fd)\r
51         return -1;\r
52     else if (a > b->fd)\r
53         return +1;\r
54     return 0;\r
55 }\r
56 \r
57 static tree234 *serial_by_fd = NULL;\r
58 \r
59 static int serial_select_result(int fd, int event);\r
60 static void serial_uxsel_setup(Serial serial);\r
61 static void serial_try_write(Serial serial);\r
62 \r
63 static const char *serial_configure(Serial serial, Config *cfg)\r
64 {\r
65     struct termios options;\r
66     int bflag, bval;\r
67     const char *str;\r
68     char *msg;\r
69 \r
70     if (serial->fd < 0)\r
71         return "Unable to reconfigure already-closed serial connection";\r
72 \r
73     tcgetattr(serial->fd, &options);\r
74 \r
75     /*\r
76      * Find the appropriate baud rate flag.\r
77      */\r
78 #define SETBAUD(x) (bflag = B ## x, bval = x)\r
79 #define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)\r
80     SETBAUD(50);\r
81 #ifdef B75\r
82     CHECKBAUD(75);\r
83 #endif\r
84 #ifdef B110\r
85     CHECKBAUD(110);\r
86 #endif\r
87 #ifdef B134\r
88     CHECKBAUD(134);\r
89 #endif\r
90 #ifdef B150\r
91     CHECKBAUD(150);\r
92 #endif\r
93 #ifdef B200\r
94     CHECKBAUD(200);\r
95 #endif\r
96 #ifdef B300\r
97     CHECKBAUD(300);\r
98 #endif\r
99 #ifdef B600\r
100     CHECKBAUD(600);\r
101 #endif\r
102 #ifdef B1200\r
103     CHECKBAUD(1200);\r
104 #endif\r
105 #ifdef B1800\r
106     CHECKBAUD(1800);\r
107 #endif\r
108 #ifdef B2400\r
109     CHECKBAUD(2400);\r
110 #endif\r
111 #ifdef B4800\r
112     CHECKBAUD(4800);\r
113 #endif\r
114 #ifdef B9600\r
115     CHECKBAUD(9600);\r
116 #endif\r
117 #ifdef B19200\r
118     CHECKBAUD(19200);\r
119 #endif\r
120 #ifdef B38400\r
121     CHECKBAUD(38400);\r
122 #endif\r
123 #ifdef B57600\r
124     CHECKBAUD(57600);\r
125 #endif\r
126 #ifdef B76800\r
127     CHECKBAUD(76800);\r
128 #endif\r
129 #ifdef B115200\r
130     CHECKBAUD(115200);\r
131 #endif\r
132 #ifdef B153600\r
133     CHECKBAUD(153600);\r
134 #endif\r
135 #ifdef B230400\r
136     CHECKBAUD(230400);\r
137 #endif\r
138 #ifdef B307200\r
139     CHECKBAUD(307200);\r
140 #endif\r
141 #ifdef B460800\r
142     CHECKBAUD(460800);\r
143 #endif\r
144 #ifdef B500000\r
145     CHECKBAUD(500000);\r
146 #endif\r
147 #ifdef B576000\r
148     CHECKBAUD(576000);\r
149 #endif\r
150 #ifdef B921600\r
151     CHECKBAUD(921600);\r
152 #endif\r
153 #ifdef B1000000\r
154     CHECKBAUD(1000000);\r
155 #endif\r
156 #ifdef B1152000\r
157     CHECKBAUD(1152000);\r
158 #endif\r
159 #ifdef B1500000\r
160     CHECKBAUD(1500000);\r
161 #endif\r
162 #ifdef B2000000\r
163     CHECKBAUD(2000000);\r
164 #endif\r
165 #ifdef B2500000\r
166     CHECKBAUD(2500000);\r
167 #endif\r
168 #ifdef B3000000\r
169     CHECKBAUD(3000000);\r
170 #endif\r
171 #ifdef B3500000\r
172     CHECKBAUD(3500000);\r
173 #endif\r
174 #ifdef B4000000\r
175     CHECKBAUD(4000000);\r
176 #endif\r
177 #undef CHECKBAUD\r
178 #undef SETBAUD\r
179     cfsetispeed(&options, bflag);\r
180     cfsetospeed(&options, bflag);\r
181     msg = dupprintf("Configuring baud rate %d", bval);\r
182     logevent(serial->frontend, msg);\r
183     sfree(msg);\r
184 \r
185     options.c_cflag &= ~CSIZE;\r
186     switch (cfg->serdatabits) {\r
187       case 5: options.c_cflag |= CS5; break;\r
188       case 6: options.c_cflag |= CS6; break;\r
189       case 7: options.c_cflag |= CS7; break;\r
190       case 8: options.c_cflag |= CS8; break;\r
191       default: return "Invalid number of data bits (need 5, 6, 7 or 8)";\r
192     }\r
193     msg = dupprintf("Configuring %d data bits", cfg->serdatabits);\r
194     logevent(serial->frontend, msg);\r
195     sfree(msg);\r
196 \r
197     if (cfg->serstopbits >= 4) {\r
198         options.c_cflag |= CSTOPB;\r
199     } else {\r
200         options.c_cflag &= ~CSTOPB;\r
201     }\r
202     msg = dupprintf("Configuring %d stop bits",\r
203                     (options.c_cflag & CSTOPB ? 2 : 1));\r
204     logevent(serial->frontend, msg);\r
205     sfree(msg);\r
206 \r
207     options.c_iflag &= ~(IXON|IXOFF);\r
208 #ifdef CRTSCTS\r
209     options.c_cflag &= ~CRTSCTS;\r
210 #endif\r
211 #ifdef CNEW_RTSCTS\r
212     options.c_cflag &= ~CNEW_RTSCTS;\r
213 #endif\r
214     if (cfg->serflow == SER_FLOW_XONXOFF) {\r
215         options.c_iflag |= IXON | IXOFF;\r
216         str = "XON/XOFF";\r
217     } else if (cfg->serflow == SER_FLOW_RTSCTS) {\r
218 #ifdef CRTSCTS\r
219         options.c_cflag |= CRTSCTS;\r
220 #endif\r
221 #ifdef CNEW_RTSCTS\r
222         options.c_cflag |= CNEW_RTSCTS;\r
223 #endif\r
224         str = "RTS/CTS";\r
225     } else\r
226         str = "no";\r
227     msg = dupprintf("Configuring %s flow control", str);\r
228     logevent(serial->frontend, msg);\r
229     sfree(msg);\r
230 \r
231     /* Parity */\r
232     if (cfg->serparity == SER_PAR_ODD) {\r
233         options.c_cflag |= PARENB;\r
234         options.c_cflag |= PARODD;\r
235         str = "odd";\r
236     } else if (cfg->serparity == SER_PAR_EVEN) {\r
237         options.c_cflag |= PARENB;\r
238         options.c_cflag &= ~PARODD;\r
239         str = "even";\r
240     } else {\r
241         options.c_cflag &= ~PARENB;\r
242         str = "no";\r
243     }\r
244     msg = dupprintf("Configuring %s parity", str);\r
245     logevent(serial->frontend, msg);\r
246     sfree(msg);\r
247 \r
248     options.c_cflag |= CLOCAL | CREAD;\r
249     options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);\r
250     options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL\r
251 #ifdef IUCLC\r
252                          | IUCLC\r
253 #endif\r
254                          );\r
255     options.c_oflag &= ~(OPOST\r
256 #ifdef ONLCR\r
257                          | ONLCR\r
258 #endif\r
259 #ifdef OCRNL\r
260                          | OCRNL\r
261 #endif\r
262 #ifdef ONOCR\r
263                          | ONOCR\r
264 #endif\r
265 #ifdef ONLRET\r
266                          | ONLRET\r
267 #endif\r
268                          );\r
269     options.c_cc[VMIN] = 1;\r
270     options.c_cc[VTIME] = 0;\r
271 \r
272     if (tcsetattr(serial->fd, TCSANOW, &options) < 0)\r
273         return "Unable to configure serial port";\r
274 \r
275     return NULL;\r
276 }\r
277 \r
278 /*\r
279  * Called to set up the serial connection.\r
280  * \r
281  * Returns an error message, or NULL on success.\r
282  *\r
283  * Also places the canonical host name into `realhost'. It must be\r
284  * freed by the caller.\r
285  */\r
286 static const char *serial_init(void *frontend_handle, void **backend_handle,\r
287                                Config *cfg,\r
288                                char *host, int port, char **realhost, int nodelay,\r
289                                int keepalive)\r
290 {\r
291     Serial serial;\r
292     const char *err;\r
293 \r
294     serial = snew(struct serial_backend_data);\r
295     *backend_handle = serial;\r
296 \r
297     serial->frontend = frontend_handle;\r
298     serial->finished = FALSE;\r
299     serial->inbufsize = 0;\r
300     bufchain_init(&serial->output_data);\r
301 \r
302     {\r
303         char *msg = dupprintf("Opening serial device %s", cfg->serline);\r
304         logevent(serial->frontend, msg);\r
305     }\r
306 \r
307     serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);\r
308     if (serial->fd < 0)\r
309         return "Unable to open serial port";\r
310 \r
311     cloexec(serial->fd);\r
312 \r
313     err = serial_configure(serial, cfg);\r
314     if (err)\r
315         return err;\r
316 \r
317     *realhost = dupstr(cfg->serline);\r
318 \r
319     if (!serial_by_fd)\r
320         serial_by_fd = newtree234(serial_compare_by_fd);\r
321     add234(serial_by_fd, serial);\r
322 \r
323     serial_uxsel_setup(serial);\r
324 \r
325     /*\r
326      * Specials are always available.\r
327      */\r
328     update_specials_menu(serial->frontend);\r
329 \r
330     return NULL;\r
331 }\r
332 \r
333 static void serial_close(Serial serial)\r
334 {\r
335     if (serial->fd >= 0) {\r
336         close(serial->fd);\r
337         serial->fd = -1;\r
338     }\r
339 }\r
340 \r
341 static void serial_free(void *handle)\r
342 {\r
343     Serial serial = (Serial) handle;\r
344 \r
345     serial_close(serial);\r
346 \r
347     bufchain_clear(&serial->output_data);\r
348 \r
349     sfree(serial);\r
350 }\r
351 \r
352 static void serial_reconfig(void *handle, Config *cfg)\r
353 {\r
354     Serial serial = (Serial) handle;\r
355 \r
356     /*\r
357      * FIXME: what should we do if this returns an error?\r
358      */\r
359     serial_configure(serial, cfg);\r
360 }\r
361 \r
362 static int serial_select_result(int fd, int event)\r
363 {\r
364     Serial serial;\r
365     char buf[4096];\r
366     int ret;\r
367     int finished = FALSE;\r
368 \r
369     serial = find234(serial_by_fd, &fd, serial_find_by_fd);\r
370 \r
371     if (!serial)\r
372         return 1;                      /* spurious event; keep going */\r
373 \r
374     if (event == 1) {\r
375         ret = read(serial->fd, buf, sizeof(buf));\r
376 \r
377         if (ret == 0) {\r
378             /*\r
379              * Shouldn't happen on a real serial port, but I'm open\r
380              * to the idea that there might be two-way devices we\r
381              * can treat _like_ serial ports which can return EOF.\r
382              */\r
383             finished = TRUE;\r
384         } else if (ret < 0) {\r
385 #ifdef EAGAIN\r
386             if (errno == EAGAIN)\r
387                 return 1;              /* spurious */\r
388 #endif\r
389 #ifdef EWOULDBLOCK\r
390             if (errno == EWOULDBLOCK)\r
391                 return 1;              /* spurious */\r
392 #endif\r
393             perror("read serial port");\r
394             exit(1);\r
395         } else if (ret > 0) {\r
396             serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);\r
397             serial_uxsel_setup(serial); /* might acquire backlog and freeze */\r
398         }\r
399     } else if (event == 2) {\r
400         /*\r
401          * Attempt to send data down the pty.\r
402          */\r
403         serial_try_write(serial);\r
404     }\r
405 \r
406     if (finished) {\r
407         serial_close(serial);\r
408 \r
409         serial->finished = TRUE;\r
410 \r
411         notify_remote_exit(serial->frontend);\r
412     }\r
413 \r
414     return !finished;\r
415 }\r
416 \r
417 static void serial_uxsel_setup(Serial serial)\r
418 {\r
419     int rwx = 0;\r
420 \r
421     if (serial->inbufsize <= SERIAL_MAX_BACKLOG)\r
422         rwx |= 1;\r
423     if (bufchain_size(&serial->output_data))\r
424         rwx |= 2;                      /* might also want to write to it */\r
425     uxsel_set(serial->fd, rwx, serial_select_result);\r
426 }\r
427 \r
428 static void serial_try_write(Serial serial)\r
429 {\r
430     void *data;\r
431     int len, ret;\r
432 \r
433     assert(serial->fd >= 0);\r
434 \r
435     while (bufchain_size(&serial->output_data) > 0) {\r
436         bufchain_prefix(&serial->output_data, &data, &len);\r
437         ret = write(serial->fd, data, len);\r
438 \r
439         if (ret < 0 && (errno == EWOULDBLOCK)) {\r
440             /*\r
441              * We've sent all we can for the moment.\r
442              */\r
443             break;\r
444         }\r
445         if (ret < 0) {\r
446             perror("write serial port");\r
447             exit(1);\r
448         }\r
449         bufchain_consume(&serial->output_data, ret);\r
450     }\r
451 \r
452     serial_uxsel_setup(serial);\r
453 }\r
454 \r
455 /*\r
456  * Called to send data down the serial connection.\r
457  */\r
458 static int serial_send(void *handle, char *buf, int len)\r
459 {\r
460     Serial serial = (Serial) handle;\r
461 \r
462     if (serial->fd < 0)\r
463         return 0;\r
464 \r
465     bufchain_add(&serial->output_data, buf, len);\r
466     serial_try_write(serial);\r
467 \r
468     return bufchain_size(&serial->output_data);\r
469 }\r
470 \r
471 /*\r
472  * Called to query the current sendability status.\r
473  */\r
474 static int serial_sendbuffer(void *handle)\r
475 {\r
476     Serial serial = (Serial) handle;\r
477     return bufchain_size(&serial->output_data);\r
478 }\r
479 \r
480 /*\r
481  * Called to set the size of the window\r
482  */\r
483 static void serial_size(void *handle, int width, int height)\r
484 {\r
485     /* Do nothing! */\r
486     return;\r
487 }\r
488 \r
489 /*\r
490  * Send serial special codes.\r
491  */\r
492 static void serial_special(void *handle, Telnet_Special code)\r
493 {\r
494     Serial serial = (Serial) handle;\r
495 \r
496     if (serial->fd >= 0 && code == TS_BRK) {\r
497         tcsendbreak(serial->fd, 0);\r
498         logevent(serial->frontend, "Sending serial break at user request");\r
499     }\r
500 \r
501     return;\r
502 }\r
503 \r
504 /*\r
505  * Return a list of the special codes that make sense in this\r
506  * protocol.\r
507  */\r
508 static const struct telnet_special *serial_get_specials(void *handle)\r
509 {\r
510     static const struct telnet_special specials[] = {\r
511         {"Break", TS_BRK},\r
512         {NULL, TS_EXITMENU}\r
513     };\r
514     return specials;\r
515 }\r
516 \r
517 static int serial_connected(void *handle)\r
518 {\r
519     return 1;                          /* always connected */\r
520 }\r
521 \r
522 static int serial_sendok(void *handle)\r
523 {\r
524     return 1;\r
525 }\r
526 \r
527 static void serial_unthrottle(void *handle, int backlog)\r
528 {\r
529     Serial serial = (Serial) handle;\r
530     serial->inbufsize = backlog;\r
531     serial_uxsel_setup(serial);\r
532 }\r
533 \r
534 static int serial_ldisc(void *handle, int option)\r
535 {\r
536     /*\r
537      * Local editing and local echo are off by default.\r
538      */\r
539     return 0;\r
540 }\r
541 \r
542 static void serial_provide_ldisc(void *handle, void *ldisc)\r
543 {\r
544     /* This is a stub. */\r
545 }\r
546 \r
547 static void serial_provide_logctx(void *handle, void *logctx)\r
548 {\r
549     /* This is a stub. */\r
550 }\r
551 \r
552 static int serial_exitcode(void *handle)\r
553 {\r
554     Serial serial = (Serial) handle;\r
555     if (serial->fd >= 0)\r
556         return -1;                     /* still connected */\r
557     else\r
558         /* Exit codes are a meaningless concept with serial ports */\r
559         return INT_MAX;\r
560 }\r
561 \r
562 /*\r
563  * cfg_info for Serial does nothing at all.\r
564  */\r
565 static int serial_cfg_info(void *handle)\r
566 {\r
567     return 0;\r
568 }\r
569 \r
570 Backend serial_backend = {\r
571     serial_init,\r
572     serial_free,\r
573     serial_reconfig,\r
574     serial_send,\r
575     serial_sendbuffer,\r
576     serial_size,\r
577     serial_special,\r
578     serial_get_specials,\r
579     serial_connected,\r
580     serial_exitcode,\r
581     serial_sendok,\r
582     serial_ldisc,\r
583     serial_provide_ldisc,\r
584     serial_provide_logctx,\r
585     serial_unthrottle,\r
586     serial_cfg_info,\r
587     "serial",\r
588     PROT_SERIAL,\r
589     0\r
590 };\r