Fix changelog email address
[freerdp-ubuntu-pcb-backport.git] / channels / rdpdr / serial / serial_tty.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol client.
3  * Serial Port Device Service Virtual Channel
4  *
5  * Copyright 2011 O.S. Systems Software Ltda.
6  * Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <freerdp/utils/memory.h>
26 #include <freerdp/utils/stream.h>
27 #include <freerdp/utils/unicode.h>
28 #include <freerdp/utils/list.h>
29 #include <freerdp/utils/thread.h>
30 #include <freerdp/utils/svc_plugin.h>
31 #include <freerdp/utils/hexdump.h>
32
33 #include <unistd.h>
34 #include <termios.h>
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <dirent.h>
39 #include <sys/ioctl.h>
40 #include <errno.h>
41
42 #include "rdpdr_constants.h"
43 #include "rdpdr_types.h"
44 #include "serial_tty.h"
45 #include "serial_constants.h"
46
47 #ifdef HAVE_SYS_MODEM_H
48 #include <sys/modem.h>
49 #endif
50 #ifdef HAVE_SYS_FILIO_H
51 #include <sys/filio.h>
52 #endif
53 #ifdef HAVE_SYS_STRTIO_H
54 #include <sys/strtio.h>
55 #endif
56
57 #ifndef CRTSCTS
58 #define CRTSCTS 0
59 #endif
60
61 /* FIONREAD should really do the same thing as TIOCINQ, where it is
62  * not available */
63 #if !defined(TIOCINQ) && defined(FIONREAD)
64 #define TIOCINQ FIONREAD
65 #endif
66 #if !defined(TIOCOUTQ) && defined(FIONWRITE)
67 #define TIOCOUTQ FIONWRITE
68 #endif
69
70
71 static uint32 tty_write_data(SERIAL_TTY* tty, uint8* data, int len);
72 static void tty_set_termios(SERIAL_TTY* tty);
73 static boolean tty_get_termios(SERIAL_TTY* tty);
74 static int tty_get_error_status();
75
76 uint32 serial_tty_control(SERIAL_TTY* tty, uint32 IoControlCode, STREAM* input, STREAM* output, uint32* abort_io)
77 {
78         int purge_mask;
79         uint32 result;
80         uint32 modemstate;
81         uint8 immediate;
82         uint32 ret = STATUS_SUCCESS;
83         uint32 length = 0;
84         uint32 pos;
85
86         DEBUG_SVC("in");
87
88         stream_seek(output, sizeof(uint32));
89
90         switch (IoControlCode)
91         {
92                 case IOCTL_SERIAL_SET_BAUD_RATE:
93                         stream_read_uint32(input, tty->baud_rate);
94                         tty_set_termios(tty);
95                         DEBUG_SVC("SERIAL_SET_BAUD_RATE %d", tty->baud_rate);
96                         break;
97
98                 case IOCTL_SERIAL_GET_BAUD_RATE:
99                         length = 4;
100                         stream_write_uint32(output, tty->baud_rate);
101                         DEBUG_SVC("SERIAL_GET_BAUD_RATE %d", tty->baud_rate);
102                         break;
103
104                 case IOCTL_SERIAL_SET_QUEUE_SIZE:
105                         stream_read_uint32(input, tty->queue_in_size);
106                         stream_read_uint32(input, tty->queue_out_size);
107                         DEBUG_SVC("SERIAL_SET_QUEUE_SIZE in %d out %d", tty->queue_in_size, tty->queue_out_size);
108                         break;
109
110                 case IOCTL_SERIAL_SET_LINE_CONTROL:
111                         stream_read_uint8(input, tty->stop_bits);
112                         stream_read_uint8(input, tty->parity);
113                         stream_read_uint8(input, tty->word_length);
114                         tty_set_termios(tty);
115                         DEBUG_SVC("SERIAL_SET_LINE_CONTROL stop %d parity %d word %d",
116                                 tty->stop_bits, tty->parity, tty->word_length);
117                         break;
118
119                 case IOCTL_SERIAL_GET_LINE_CONTROL:
120                         DEBUG_SVC("SERIAL_GET_LINE_CONTROL");
121                         length = 3;
122                         stream_write_uint8(output, tty->stop_bits);
123                         stream_write_uint8(output, tty->parity);
124                         stream_write_uint8(output, tty->word_length);
125                         break;
126
127                 case IOCTL_SERIAL_IMMEDIATE_CHAR:
128                         DEBUG_SVC("SERIAL_IMMEDIATE_CHAR");
129                         stream_read_uint8(input, immediate);
130                         tty_write_data(tty, &immediate, 1);
131                         break;
132
133                 case IOCTL_SERIAL_CONFIG_SIZE:
134                         DEBUG_SVC("SERIAL_CONFIG_SIZE");
135                         length = 4;
136                         stream_write_uint32(output, 0);
137                         break;
138
139                 case IOCTL_SERIAL_GET_CHARS:
140                         DEBUG_SVC("SERIAL_GET_CHARS");
141                         length = 6;
142                         stream_write(output, tty->chars, 6);
143                         break;
144
145                 case IOCTL_SERIAL_SET_CHARS:
146                         DEBUG_SVC("SERIAL_SET_CHARS");
147                         stream_read(input, tty->chars, 6);
148                         tty_set_termios(tty);
149                         break;
150
151                 case IOCTL_SERIAL_GET_HANDFLOW:
152                         length = 16;
153                         tty_get_termios(tty);
154                         stream_write_uint32(output, tty->control);
155                         stream_write_uint32(output, tty->xonoff);
156                         stream_write_uint32(output, tty->onlimit);
157                         stream_write_uint32(output, tty->offlimit);
158                         DEBUG_SVC("IOCTL_SERIAL_GET_HANDFLOW %X %X %X %X",
159                                 tty->control, tty->xonoff, tty->onlimit, tty->offlimit);
160                         break;
161
162                 case IOCTL_SERIAL_SET_HANDFLOW:
163                         stream_read_uint32(input, tty->control);
164                         stream_read_uint32(input, tty->xonoff);
165                         stream_read_uint32(input, tty->onlimit);
166                         stream_read_uint32(input, tty->offlimit);
167                         DEBUG_SVC("IOCTL_SERIAL_SET_HANDFLOW %X %X %X %X",
168                                 tty->control, tty->xonoff, tty->onlimit, tty->offlimit);
169                         tty_set_termios(tty);
170                         break;
171
172                 case IOCTL_SERIAL_SET_TIMEOUTS:
173                         stream_read_uint32(input, tty->read_interval_timeout);
174                         stream_read_uint32(input, tty->read_total_timeout_multiplier);
175                         stream_read_uint32(input, tty->read_total_timeout_constant);
176                         stream_read_uint32(input, tty->write_total_timeout_multiplier);
177                         stream_read_uint32(input, tty->write_total_timeout_constant);
178
179                         /* http://www.codeproject.com/KB/system/chaiyasit_t.aspx, see 'ReadIntervalTimeout' section
180                                 http://msdn.microsoft.com/en-us/library/ms885171.aspx */
181                         if (tty->read_interval_timeout == SERIAL_TIMEOUT_MAX)
182                         {
183                                 tty->read_interval_timeout = 0;
184                                 tty->read_total_timeout_multiplier = 0;
185                         }
186
187                         DEBUG_SVC("SERIAL_SET_TIMEOUTS read timeout %d %d %d",
188                                 tty->read_interval_timeout,
189                                 tty->read_total_timeout_multiplier,
190                                 tty->read_total_timeout_constant);
191                         break;
192
193                 case IOCTL_SERIAL_GET_TIMEOUTS:
194                         DEBUG_SVC("SERIAL_GET_TIMEOUTS read timeout %d %d %d",
195                                 tty->read_interval_timeout,
196                                 tty->read_total_timeout_multiplier,
197                                 tty->read_total_timeout_constant);
198                         length = 20;
199                         stream_write_uint32(output, tty->read_interval_timeout);
200                         stream_write_uint32(output, tty->read_total_timeout_multiplier);
201                         stream_write_uint32(output, tty->read_total_timeout_constant);
202                         stream_write_uint32(output, tty->write_total_timeout_multiplier);
203                         stream_write_uint32(output, tty->write_total_timeout_constant);
204                         break;
205
206                 case IOCTL_SERIAL_GET_WAIT_MASK:
207                         DEBUG_SVC("SERIAL_GET_WAIT_MASK %X", tty->wait_mask);
208                         length = 4;
209                         stream_write_uint32(output, tty->wait_mask);
210                         break;
211
212                 case IOCTL_SERIAL_SET_WAIT_MASK:
213                         stream_read_uint32(input, tty->wait_mask);
214                         DEBUG_SVC("SERIAL_SET_WAIT_MASK %X", tty->wait_mask);
215                         break;
216
217                 case IOCTL_SERIAL_SET_DTR:
218                         DEBUG_SVC("SERIAL_SET_DTR");
219                         ioctl(tty->fd, TIOCMGET, &result);
220                         result |= TIOCM_DTR;
221                         ioctl(tty->fd, TIOCMSET, &result);
222                         tty->dtr = 1;
223                         break;
224
225                 case IOCTL_SERIAL_CLR_DTR:
226                         DEBUG_SVC("SERIAL_CLR_DTR");
227                         ioctl(tty->fd, TIOCMGET, &result);
228                         result &= ~TIOCM_DTR;
229                         ioctl(tty->fd, TIOCMSET, &result);
230                         tty->dtr = 0;
231                         break;
232
233                 case IOCTL_SERIAL_SET_RTS:
234                         DEBUG_SVC("SERIAL_SET_RTS");
235                         ioctl(tty->fd, TIOCMGET, &result);
236                         result |= TIOCM_RTS;
237                         ioctl(tty->fd, TIOCMSET, &result);
238                         tty->rts = 1;
239                         break;
240
241                 case IOCTL_SERIAL_CLR_RTS:
242                         DEBUG_SVC("SERIAL_CLR_RTS");
243                         ioctl(tty->fd, TIOCMGET, &result);
244                         result &= ~TIOCM_RTS;
245                         ioctl(tty->fd, TIOCMSET, &result);
246                         tty->rts = 0;
247                         break;
248
249                 case IOCTL_SERIAL_GET_MODEMSTATUS:
250                         modemstate = 0;
251 #ifdef TIOCMGET
252                         ioctl(tty->fd, TIOCMGET, &result);
253                         if (result & TIOCM_CTS)
254                                 modemstate |= SERIAL_MS_CTS;
255                         if (result & TIOCM_DSR)
256                                 modemstate |= SERIAL_MS_DSR;
257                         if (result & TIOCM_RNG)
258                                 modemstate |= SERIAL_MS_RNG;
259                         if (result & TIOCM_CAR)
260                                 modemstate |= SERIAL_MS_CAR;
261                         if (result & TIOCM_DTR)
262                                 modemstate |= SERIAL_MS_DTR;
263                         if (result & TIOCM_RTS)
264                                 modemstate |= SERIAL_MS_RTS;
265 #endif
266                         DEBUG_SVC("SERIAL_GET_MODEMSTATUS %X", modemstate);
267                         length = 4;
268                         stream_write_uint32(output, modemstate);
269                         break;
270
271                 case IOCTL_SERIAL_GET_COMMSTATUS:
272                         length = 18;
273                         stream_write_uint32(output, 0); /* Errors */
274                         stream_write_uint32(output, 0); /* Hold reasons */
275
276                         result = 0;
277 #ifdef TIOCINQ
278                         ioctl(tty->fd, TIOCINQ, &result);
279 #endif
280                         stream_write_uint32(output, result); /* Amount in in queue */
281                         if (result)
282                                 DEBUG_SVC("SERIAL_GET_COMMSTATUS in queue %d", result);
283
284                         result = 0;
285 #ifdef TIOCOUTQ
286                         ioctl(tty->fd, TIOCOUTQ, &result);
287 #endif
288                         stream_write_uint32(output, result);    /* Amount in out queue */
289                         DEBUG_SVC("SERIAL_GET_COMMSTATUS out queue %d", result);
290
291                         stream_write_uint8(output, 0); /* EofReceived */
292                         stream_write_uint8(output, 0); /* WaitForImmediate */
293                         break;
294
295                 case IOCTL_SERIAL_PURGE:
296                         stream_read_uint32(input, purge_mask);
297                         DEBUG_SVC("SERIAL_PURGE purge_mask %X", purge_mask);
298
299                 /*      See http://msdn.microsoft.com/en-us/library/ms901431.aspx
300                         PURGE_TXCLEAR   Clears the output buffer, if the driver has one.
301                         PURGE_RXCLEAR   Clears the input buffer, if the driver has one.
302
303                         It clearly states to clear the *driver* buffer, not the port buffer
304                 */
305
306 #ifdef DEBUG_SVC
307                         if (purge_mask & SERIAL_PURGE_TXCLEAR)
308                                 DEBUG_SVC("Ignoring SERIAL_PURGE_TXCLEAR");
309                         if (purge_mask & SERIAL_PURGE_RXCLEAR)
310                                 DEBUG_SVC("Ignoring SERIAL_PURGE_RXCLEAR");
311 #endif
312
313                         if (purge_mask & SERIAL_PURGE_TXABORT)
314                                 *abort_io |= SERIAL_ABORT_IO_WRITE;
315                         if (purge_mask & SERIAL_PURGE_RXABORT)
316                                 *abort_io |= SERIAL_ABORT_IO_READ;
317                         break;
318                 case IOCTL_SERIAL_WAIT_ON_MASK:
319                         DEBUG_SVC("SERIAL_WAIT_ON_MASK %X", tty->wait_mask);
320                         tty->event_pending = 1;
321                         length = 4;
322                         if (serial_tty_get_event(tty, &result))
323                         {
324                                 DEBUG_SVC("WAIT end  event = %X", result);
325                                 stream_write_uint32(output, result);
326                                 break;
327                         }
328                         ret = STATUS_PENDING;
329                         break;
330
331                 case IOCTL_SERIAL_SET_BREAK_ON:
332                         DEBUG_SVC("SERIAL_SET_BREAK_ON");
333                         tcsendbreak(tty->fd, 0);
334                         break;
335
336                 case IOCTL_SERIAL_RESET_DEVICE:
337                         DEBUG_SVC("SERIAL_RESET_DEVICE");
338                         break;
339
340                 case IOCTL_SERIAL_SET_BREAK_OFF:
341                         DEBUG_SVC("SERIAL_SET_BREAK_OFF");
342                         break;
343
344                 case IOCTL_SERIAL_SET_XOFF:
345                         DEBUG_SVC("SERIAL_SET_XOFF");
346                         break;
347
348                 case IOCTL_SERIAL_SET_XON:
349                         DEBUG_SVC("SERIAL_SET_XON");
350                         tcflow(tty->fd, TCION);
351                         break;
352
353                 default:
354                         DEBUG_SVC("NOT FOUND IoControlCode SERIAL IOCTL %d", IoControlCode);
355                         return STATUS_INVALID_PARAMETER;
356         }
357
358         /* Write OutputBufferLength */
359         pos = stream_get_pos(output);
360         stream_set_pos(output, 16);
361         stream_write_uint32(output, length);
362         stream_set_pos(output, pos);
363
364         return ret;
365 }
366
367 boolean serial_tty_read(SERIAL_TTY* tty, uint8* buffer, uint32* Length)
368 {
369         long timeout = 90;
370         struct termios *ptermios;
371         ssize_t r;
372
373         DEBUG_SVC("in");
374         ptermios = tty->ptermios;
375
376         /* Set timeouts kind of like the windows serial timeout parameters. Multiply timeout
377            with requested read size */
378         if (tty->read_total_timeout_multiplier | tty->read_total_timeout_constant)
379         {
380                 timeout =
381                         (tty->read_total_timeout_multiplier * (*Length) +
382                          tty->read_total_timeout_constant + 99) / 100;
383         }
384         else if (tty->read_interval_timeout)
385         {
386                 timeout = (tty->read_interval_timeout * (*Length) + 99) / 100;
387         }
388
389         /* If a timeout is set, do a blocking read, which times out after some time.
390            It will make FreeRDP less responsive, but it will improve serial performance,
391            by not reading one character at a time. */
392         if (timeout == 0)
393         {
394                 ptermios->c_cc[VTIME] = 0;
395                 ptermios->c_cc[VMIN] = 0;
396         }
397         else
398         {
399                 ptermios->c_cc[VTIME] = timeout;
400                 ptermios->c_cc[VMIN] = 1;
401         }
402
403         tcsetattr(tty->fd, TCSANOW, ptermios);
404
405         memset(buffer, 0, *Length);
406         r = read(tty->fd, buffer, *Length);
407         if (r < 0)
408                 return false;
409
410         tty->event_txempty = r;
411         *Length = r;
412
413         return true;
414 }
415
416 boolean serial_tty_write(SERIAL_TTY* tty, uint8* buffer, uint32 Length)
417 {
418         ssize_t r;
419         uint32 event_txempty = Length;
420
421         DEBUG_SVC("in");
422
423         while (Length > 0)
424         {
425                 r = write(tty->fd, buffer, Length);
426                 if (r < 0)
427                         return false;
428
429                 Length -= r;
430                 buffer += r;
431         }
432         tty->event_txempty = event_txempty;
433
434         return true;
435 }
436
437 void serial_tty_free(SERIAL_TTY* tty)
438 {
439         DEBUG_SVC("in");
440
441         if (tty->fd >= 0)
442         {
443                 tcsetattr(tty->fd, TCSANOW, tty->pold_termios);
444                 close(tty->fd);
445         }
446
447         xfree(tty->ptermios);
448         xfree(tty->pold_termios);
449         xfree(tty);
450 }
451
452 SERIAL_TTY* serial_tty_new(const char* path, uint32 id)
453 {
454         SERIAL_TTY* tty;
455
456         tty = xnew(SERIAL_TTY);
457         tty->id = id;
458         tty->fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
459         if (tty->fd < 0)
460         {
461                 perror("open");
462                 DEBUG_WARN("failed to open device %s", path);
463                 return NULL;
464         }
465         else
466                 DEBUG_SVC("tty fd %d successfully opened", tty->fd);
467
468         tty->ptermios = (struct termios*) malloc(sizeof(struct termios));
469         memset(tty->ptermios, 0, sizeof(struct termios));
470         tty->pold_termios = (struct termios*) malloc(sizeof(struct termios));
471         memset(tty->pold_termios, 0, sizeof(struct termios));
472         tcgetattr(tty->fd, tty->pold_termios);
473
474         if (!tty_get_termios(tty))
475         {
476                 DEBUG_WARN("%s access denied", path);
477                 fflush(stdout);
478                 return NULL;
479         }
480
481         tty->ptermios->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
482         tty->ptermios->c_iflag = IGNPAR | ICRNL;
483         tty->ptermios->c_oflag &= ~OPOST;
484         tty->ptermios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
485         tty->ptermios->c_cflag &= ~(CSIZE | PARENB);
486         tty->ptermios->c_cflag |= CLOCAL | CREAD | CS8;
487         tcsetattr(tty->fd, TCSANOW, tty->ptermios);
488
489         tty->event_txempty = 0;
490         tty->event_cts = 0;
491         tty->event_dsr = 0;
492         tty->event_rlsd = 0;
493         tty->event_pending = 0;
494
495         /* all read and writes should be non-blocking */
496         if (fcntl(tty->fd, F_SETFL, O_NONBLOCK) == -1)
497         {
498                 DEBUG_WARN("%s fcntl", path);
499                 perror("fcntl");
500                 return NULL;
501         }
502
503         tty->read_total_timeout_constant = 5;
504
505         return tty;
506 }
507
508 boolean serial_tty_get_event(SERIAL_TTY* tty, uint32* result)
509 {
510         int bytes;
511         boolean ret = false;
512
513         DEBUG_SVC("in");
514
515         *result = 0;
516
517 #ifdef TIOCINQ
518         /* When wait_mask is set to zero we ought to cancel it all
519            For reference: http://msdn.microsoft.com/en-us/library/aa910487.aspx */
520         if (tty->wait_mask == 0)
521         {
522                 tty->event_pending = 0;
523                 return true;
524         }
525
526         ioctl(tty->fd, TIOCINQ, &bytes);
527
528         if (bytes > 0)
529         {
530                 DEBUG_SVC("bytes %d", bytes);
531
532                 if (bytes > tty->event_rlsd)
533                 {
534                         tty->event_rlsd = bytes;
535                         if (tty->wait_mask & SERIAL_EV_RLSD)
536                         {
537                                 DEBUG_SVC("SERIAL_EV_RLSD");
538                                 *result |= SERIAL_EV_RLSD;
539                                 ret = true;
540                         }
541
542                 }
543
544                 if ((bytes > 1) && (tty->wait_mask & SERIAL_EV_RXFLAG))
545                 {
546                         DEBUG_SVC("SERIAL_EV_RXFLAG bytes %d", bytes);
547                         *result |= SERIAL_EV_RXFLAG;
548                         ret = true;
549                 }
550                 if ((tty->wait_mask & SERIAL_EV_RXCHAR))
551                 {
552                         DEBUG_SVC("SERIAL_EV_RXCHAR bytes %d", bytes);
553                         *result |= SERIAL_EV_RXCHAR;
554                         ret = true;
555                 }
556
557         }
558         else
559         {
560                 tty->event_rlsd = 0;
561         }
562 #endif
563
564 #ifdef TIOCOUTQ
565         ioctl(tty->fd, TIOCOUTQ, &bytes);
566         if ((bytes == 0)
567             && (tty->event_txempty > 0) && (tty->wait_mask & SERIAL_EV_TXEMPTY))
568         {
569                 DEBUG_SVC("SERIAL_EV_TXEMPTY");
570                 *result |= SERIAL_EV_TXEMPTY;
571                 ret = true;
572         }
573         tty->event_txempty = bytes;
574 #endif
575
576         ioctl(tty->fd, TIOCMGET, &bytes);
577         if ((bytes & TIOCM_DSR) != tty->event_dsr)
578         {
579                 tty->event_dsr = bytes & TIOCM_DSR;
580                 if (tty->wait_mask & SERIAL_EV_DSR)
581                 {
582                         DEBUG_SVC("SERIAL_EV_DSR %s", (bytes & TIOCM_DSR) ? "ON" : "OFF");
583                         *result |= SERIAL_EV_DSR;
584                         ret = true;
585                 }
586         }
587
588         if ((bytes & TIOCM_CTS) != tty->event_cts)
589         {
590                 tty->event_cts = bytes & TIOCM_CTS;
591                 if (tty->wait_mask & SERIAL_EV_CTS)
592                 {
593                         DEBUG_SVC("SERIAL_EV_CTS %s", (bytes & TIOCM_CTS) ? "ON" : "OFF");
594                         *result |= SERIAL_EV_CTS;
595                         ret = true;
596                 }
597         }
598
599         if (ret)
600                 tty->event_pending = 0;
601
602         return ret;
603 }
604
605 static boolean tty_get_termios(SERIAL_TTY* tty)
606 {
607         speed_t speed;
608         struct termios *ptermios;
609         ptermios = tty->ptermios;
610
611         DEBUG_SVC("tcgetattr? %d", tcgetattr(tty->fd, ptermios) >= 0);
612         if (tcgetattr(tty->fd, ptermios) < 0)
613                 return false;
614
615         speed = cfgetispeed(ptermios);
616         switch (speed)
617         {
618 #ifdef B75
619                 case B75:
620                         tty->baud_rate = 75;
621                         break;
622 #endif
623 #ifdef B110
624                 case B110:
625                         tty->baud_rate = 110;
626                         break;
627 #endif
628 #ifdef B134
629                 case B134:
630                         tty->baud_rate = 134;
631                         break;
632 #endif
633 #ifdef B150
634                 case B150:
635                         tty->baud_rate = 150;
636                         break;
637 #endif
638 #ifdef B300
639                 case B300:
640                         tty->baud_rate = 300;
641                         break;
642 #endif
643 #ifdef B600
644                 case B600:
645                         tty->baud_rate = 600;
646                         break;
647 #endif
648 #ifdef B1200
649                 case B1200:
650                         tty->baud_rate = 1200;
651                         break;
652 #endif
653 #ifdef B1800
654                 case B1800:
655                         tty->baud_rate = 1800;
656                         break;
657 #endif
658 #ifdef B2400
659                 case B2400:
660                         tty->baud_rate = 2400;
661                         break;
662 #endif
663 #ifdef B4800
664                 case B4800:
665                         tty->baud_rate = 4800;
666                         break;
667 #endif
668 #ifdef B9600
669                 case B9600:
670                         tty->baud_rate = 9600;
671                         break;
672 #endif
673 #ifdef B19200
674                 case B19200:
675                         tty->baud_rate = 19200;
676                         break;
677 #endif
678 #ifdef B38400
679                 case B38400:
680                         tty->baud_rate = 38400;
681                         break;
682 #endif
683 #ifdef B57600
684                 case B57600:
685                         tty->baud_rate = 57600;
686                         break;
687 #endif
688 #ifdef B115200
689                 case B115200:
690                         tty->baud_rate = 115200;
691                         break;
692 #endif
693 #ifdef B230400
694                 case B230400:
695                         tty->baud_rate = 230400;
696                         break;
697 #endif
698 #ifdef B460800
699                 case B460800:
700                         tty->baud_rate = 460800;
701                         break;
702 #endif
703                 default:
704                         tty->baud_rate = 9600;
705                         break;
706         }
707
708         speed = cfgetospeed(ptermios);
709         tty->dtr = (speed == B0) ? 0 : 1;
710
711         tty->stop_bits = (ptermios->c_cflag & CSTOPB) ? SERIAL_STOP_BITS_2 : SERIAL_STOP_BITS_1;
712         tty->parity =
713                 (ptermios->c_cflag & PARENB) ? ((ptermios->c_cflag & PARODD) ? SERIAL_ODD_PARITY :
714                                                 SERIAL_EVEN_PARITY) : SERIAL_NO_PARITY;
715         switch (ptermios->c_cflag & CSIZE)
716         {
717                 case CS5:
718                         tty->word_length = 5;
719                         break;
720                 case CS6:
721                         tty->word_length = 6;
722                         break;
723                 case CS7:
724                         tty->word_length = 7;
725                         break;
726                 default:
727                         tty->word_length = 8;
728                         break;
729         }
730
731         if (ptermios->c_cflag & CRTSCTS)
732         {
733                 tty->control = SERIAL_DTR_CONTROL | SERIAL_CTS_HANDSHAKE | SERIAL_ERROR_ABORT;
734         }
735         else
736         {
737                 tty->control = SERIAL_DTR_CONTROL | SERIAL_ERROR_ABORT;
738         }
739
740         tty->xonoff = SERIAL_DSR_SENSITIVITY;
741         if (ptermios->c_iflag & IXON)
742                 tty->xonoff |= SERIAL_XON_HANDSHAKE;
743
744         if (ptermios->c_iflag & IXOFF)
745                 tty->xonoff |= SERIAL_XOFF_HANDSHAKE;
746
747         tty->chars[SERIAL_CHAR_XON] = ptermios->c_cc[VSTART];
748         tty->chars[SERIAL_CHAR_XOFF] = ptermios->c_cc[VSTOP];
749         tty->chars[SERIAL_CHAR_EOF] = ptermios->c_cc[VEOF];
750         tty->chars[SERIAL_CHAR_BREAK] = ptermios->c_cc[VINTR];
751         tty->chars[SERIAL_CHAR_ERROR] = ptermios->c_cc[VKILL];
752
753         return true;
754 }
755
756 static void tty_set_termios(SERIAL_TTY* tty)
757 {
758         speed_t speed;
759         struct termios *ptermios;
760
761         DEBUG_SVC("in");
762         ptermios = tty->ptermios;
763         switch (tty->baud_rate)
764         {
765 #ifdef B75
766                 case 75:
767                         speed = B75;
768                         break;
769 #endif
770 #ifdef B110
771                 case 110:
772                         speed = B110;
773                         break;
774 #endif
775 #ifdef B134
776                 case 134:
777                         speed = B134;
778                         break;
779 #endif
780 #ifdef B150
781                 case 150:
782                         speed = B150;
783                         break;
784 #endif
785 #ifdef B300
786                 case 300:
787                         speed = B300;
788                         break;
789 #endif
790 #ifdef B600
791                 case 600:
792                         speed = B600;
793                         break;
794 #endif
795 #ifdef B1200
796                 case 1200:
797                         speed = B1200;
798                         break;
799 #endif
800 #ifdef B1800
801                 case 1800:
802                         speed = B1800;
803                         break;
804 #endif
805 #ifdef B2400
806                 case 2400:
807                         speed = B2400;
808                         break;
809 #endif
810 #ifdef B4800
811                 case 4800:
812                         speed = B4800;
813                         break;
814 #endif
815 #ifdef B9600
816                 case 9600:
817                         speed = B9600;
818                         break;
819 #endif
820 #ifdef B19200
821                 case 19200:
822                         speed = B19200;
823                         break;
824 #endif
825 #ifdef B38400
826                 case 38400:
827                         speed = B38400;
828                         break;
829 #endif
830 #ifdef B57600
831                 case 57600:
832                         speed = B57600;
833                         break;
834 #endif
835 #ifdef B115200
836                 case 115200:
837                         speed = B115200;
838                         break;
839 #endif
840 #ifdef B230400
841                 case 230400:
842                         speed = B115200;
843                         break;
844 #endif
845 #ifdef B460800
846                 case 460800:
847                         speed = B115200;
848                         break;
849 #endif
850                 default:
851                         speed = B9600;
852                         break;
853         }
854
855 #ifdef CBAUD
856         ptermios->c_cflag &= ~CBAUD;
857         ptermios->c_cflag |= speed;
858 #else
859         /* on systems with separate ispeed and ospeed, we can remember the speed
860            in ispeed while changing DTR with ospeed */
861         cfsetispeed(tty->ptermios, speed);
862         cfsetospeed(tty->ptermios, tty->dtr ? speed : 0);
863 #endif
864
865         ptermios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CSIZE | CRTSCTS);
866         switch (tty->stop_bits)
867         {
868                 case SERIAL_STOP_BITS_2:
869                         ptermios->c_cflag |= CSTOPB;
870                         break;
871                 default:
872                         ptermios->c_cflag &= ~CSTOPB;
873                         break;
874         }
875
876         switch (tty->parity)
877         {
878                 case SERIAL_EVEN_PARITY:
879                         ptermios->c_cflag |= PARENB;
880                         break;
881                 case SERIAL_ODD_PARITY:
882                         ptermios->c_cflag |= PARENB | PARODD;
883                         break;
884                 case SERIAL_NO_PARITY:
885                         ptermios->c_cflag &= ~(PARENB | PARODD);
886                         break;
887         }
888
889         switch (tty->word_length)
890         {
891                 case 5:
892                         ptermios->c_cflag |= CS5;
893                         break;
894                 case 6:
895                         ptermios->c_cflag |= CS6;
896                         break;
897                 case 7:
898                         ptermios->c_cflag |= CS7;
899                         break;
900                 default:
901                         ptermios->c_cflag |= CS8;
902                         break;
903         }
904
905 #if 0
906         if (tty->rts)
907                 ptermios->c_cflag |= CRTSCTS;
908         else
909                 ptermios->c_cflag &= ~CRTSCTS;
910 #endif
911
912         if (tty->control & SERIAL_CTS_HANDSHAKE)
913         {
914                 ptermios->c_cflag |= CRTSCTS;
915         }
916         else
917         {
918                 ptermios->c_cflag &= ~CRTSCTS;
919         }
920
921
922         if (tty->xonoff & SERIAL_XON_HANDSHAKE)
923         {
924                 ptermios->c_iflag |= IXON | IMAXBEL;
925         }
926         if (tty->xonoff & SERIAL_XOFF_HANDSHAKE)
927         {
928                 ptermios->c_iflag |= IXOFF | IMAXBEL;
929         }
930
931         if ((tty->xonoff & (SERIAL_XOFF_HANDSHAKE | SERIAL_XON_HANDSHAKE)) == 0)
932         {
933                 ptermios->c_iflag &= ~IXON;
934                 ptermios->c_iflag &= ~IXOFF;
935         }
936
937         ptermios->c_cc[VSTART] = tty->chars[SERIAL_CHAR_XON];
938         ptermios->c_cc[VSTOP] = tty->chars[SERIAL_CHAR_XOFF];
939         ptermios->c_cc[VEOF] = tty->chars[SERIAL_CHAR_EOF];
940         ptermios->c_cc[VINTR] = tty->chars[SERIAL_CHAR_BREAK];
941         ptermios->c_cc[VKILL] = tty->chars[SERIAL_CHAR_ERROR];
942
943         tcsetattr(tty->fd, TCSANOW, ptermios);
944 }
945
946 static uint32 tty_write_data(SERIAL_TTY* tty, uint8* data, int len)
947 {
948         ssize_t r;
949
950         DEBUG_SVC("in");
951
952         r = write(tty->fd, data, len);
953         if (r < 0)
954                 return tty_get_error_status();
955
956         tty->event_txempty = r;
957
958         return STATUS_SUCCESS;
959 }
960
961 static int tty_get_error_status()
962 {
963         DEBUG_SVC("in errno %d", errno);
964
965         switch (errno)
966         {
967                 case EACCES:
968                 case ENOTDIR:
969                 case ENFILE:
970                         return STATUS_ACCESS_DENIED;
971                 case EISDIR:
972                         return STATUS_FILE_IS_A_DIRECTORY;
973                 case EEXIST:
974                         return STATUS_OBJECT_NAME_COLLISION;
975                 case EBADF:
976                         return STATUS_INVALID_HANDLE;
977                 default:
978                         return STATUS_NO_SUCH_FILE;
979         }
980 }