2008-09-14 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / support / serial.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* serial port functions
4  *
5  * Author: Chris Toshok <toshok@ximian.com>
6  */
7
8 #include <termios.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <string.h>
12 #include <sys/poll.h>
13 #include <sys/ioctl.h>
14 #include <errno.h>
15
16 #include <glib.h>
17
18 /* This is for FIONREAD on solaris */
19 #if defined(sun)
20 #include <sys/filio.h>
21 #endif
22
23 /* sys/time.h (for timeval) is required when using osx 10.3 (but not 10.4) */
24 #ifdef __APPLE__
25 #include <sys/time.h>
26 #endif
27
28 /* This is a copy of System.IO.Ports.Handshake */
29 typedef enum {
30         NoneHandshake = 0,
31         XOnXOff = 1,
32         RequestToSend = 2,
33         RequestToSendXOnXOff = 3
34 } MonoHandshake;
35
36 /* This is a copy of System.IO.Ports.Parity */
37 typedef enum {
38         NoneParity = 0,
39         Odd = 1,
40         Even = 2,
41         Mark = 3,
42         Space = 4
43 } MonoParity;
44
45 /* This is a copy of System.IO.Ports.StopBits */
46 typedef enum {
47         NoneStopBits = 0,
48         One = 1,
49         Two = 2,
50         OnePointFive = 3
51 } MonoStopBits;
52
53 /* This is a copy of System.IO.Ports.SerialSignal */
54 typedef enum {
55         NoneSignal,
56         Cd = 1, /* Carrier detect */
57         Cts = 2, /* Clear to send */
58         Dsr = 4, /* Data set ready */
59         Dtr = 8, /* Data terminal ready */
60         Rts = 16  /* Request to send */
61 } MonoSerialSignal;
62
63 int
64 open_serial (char* devfile)
65 {
66         int fd;
67         fd = open (devfile, O_RDWR | O_NOCTTY | O_NONBLOCK);
68
69         if (fd == -1)
70                 return -1;
71
72         return fd;
73 }
74
75 void
76 close_serial (int unix_fd)
77 {
78         close (unix_fd);
79 }
80
81 guint32
82 read_serial (int fd, guchar *buffer, int offset, int count)
83 {
84         guint32 n;
85  
86         n = read (fd, buffer + offset, count);
87
88         return (guint32) n;
89 }
90
91 int
92 write_serial (int fd, guchar *buffer, int offset, int count, int timeout)
93 {
94         struct pollfd pinfo;
95         guint32 n;
96
97         pinfo.fd = fd;
98         pinfo.events = POLLOUT;
99         pinfo.revents = POLLOUT;
100
101         n = count;
102
103         while (n > 0)
104         {
105                 size_t t;
106                         
107                 if (timeout != 0) {
108                         int c;
109                         
110                         while ((c = poll (&pinfo, 1, timeout)) == -1 && errno == EINTR)
111                                 ;
112                         if (c == -1)
113                                 return -1;
114                 }               
115
116                 do {
117                         t = write (fd, buffer + offset, n);
118                 } while (t == -1 && errno == EINTR);
119
120                 if (t < 0)
121                         return -1;
122                 
123                 offset += t;
124                 n -= t; 
125         }
126  
127         return 0;
128 }
129
130 void
131 discard_buffer (int fd, gboolean input)
132 {
133         tcflush(fd, input ? TCIFLUSH : TCOFLUSH);
134 }
135
136 gint32
137 get_bytes_in_buffer (int fd, gboolean input)
138 {
139         gint32 retval;
140
141         if (ioctl (fd, input ? FIONREAD : TIOCOUTQ, &retval) == -1) {
142                 return -1;
143         }
144
145         return retval;
146 }
147
148 gboolean
149 set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStopBits stopBits, MonoHandshake handshake)
150 {
151         struct termios newtio;
152
153         tcgetattr (fd, &newtio);
154         newtio.c_cflag |=  (CLOCAL | CREAD);
155         newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN );
156         newtio.c_oflag &= ~(OPOST);
157         newtio.c_iflag = IGNBRK;
158
159         /* setup baudrate */
160         switch (baud_rate)
161         {
162         case 230400: 
163             baud_rate = B230400;
164             break;
165         case 115200: 
166             baud_rate = B115200;
167             break;
168         case 57600:
169             baud_rate = B57600;
170             break;
171         case 38400: 
172             baud_rate = B38400;
173             break;
174         case 19200: 
175             baud_rate = B19200;
176             break;
177         case 9600: 
178                 baud_rate = B9600;
179                 break;
180         case 4800: 
181             baud_rate = B4800;
182                 break;
183         case 2400: 
184             baud_rate = B2400;
185                 break;
186         case 1800: 
187             baud_rate = B1800;
188                 break;
189         case 1200: 
190             baud_rate = B1200;
191                 break;
192         case 600: 
193             baud_rate = B600;
194             break;
195         case 300: 
196             baud_rate = B300;
197             break;
198         case 200: 
199             baud_rate = B200;
200             break;
201         case 150: 
202             baud_rate = B150;
203             break;
204         case 134: 
205             baud_rate = B134;
206             break;
207         case 110: 
208             baud_rate = B110;
209             break;
210         case 75: 
211             baud_rate = B75;
212             break;
213         case 50:
214         case 0:
215         default:
216             baud_rate = B9600;
217                 break;
218         }
219
220         /* char lenght */
221         newtio.c_cflag &= ~CSIZE;
222         switch (dataBits)
223         {
224         case 5: 
225             newtio.c_cflag |= CS5;
226             break;
227         case 6: 
228             newtio.c_cflag |= CS6;
229             break;
230         case 7: 
231             newtio.c_cflag |= CS7;
232             break;
233         case 8:
234         default:
235                 newtio.c_cflag |= CS8;
236                 break;
237         }
238
239         /* stopbits */
240         switch (stopBits)
241         {
242         case NoneStopBits:
243                 /* Unhandled */
244                 break;
245         case One: /* One */
246                 /* do nothing, the default is one stop bit */
247             newtio.c_cflag &= ~CSTOPB;
248                 break;
249         case Two: /* Two */
250                 newtio.c_cflag |= CSTOPB;
251                 break;
252         case OnePointFive: /* OnePointFive */
253                 /* XXX unhandled */
254                 break;
255         }
256
257         /* parity */
258         newtio.c_iflag &= ~(INPCK | ISTRIP );
259
260         switch (parity)
261         {
262         case NoneParity: /* None */
263             newtio.c_cflag &= ~(PARENB | PARODD);
264             break;
265             
266         case Odd: /* Odd */
267             newtio.c_cflag |= PARENB | PARODD;
268             break;
269             
270         case Even: /* Even */
271             newtio.c_cflag &= ~(PARODD);
272             newtio.c_cflag |= (PARENB);
273             break;
274             
275         case Mark: /* Mark */
276             /* XXX unhandled */
277             break;
278         case Space: /* Space */
279             /* XXX unhandled */
280             break;
281         }
282
283         newtio.c_iflag &= ~(IXOFF | IXON);
284 #ifdef CRTSCTS
285         newtio.c_cflag &= ~CRTSCTS;
286 #endif /* def CRTSCTS */
287
288         switch (handshake)
289         {
290         case NoneHandshake: /* None */
291                 /* do nothing */
292                 break;
293         case RequestToSend: /* RequestToSend (RTS) */
294 #ifdef CRTSCTS
295                 newtio.c_cflag |= CRTSCTS;
296 #endif /* def CRTSCTS */
297                 break;
298         case RequestToSendXOnXOff: /* RequestToSendXOnXOff (RTS + XON/XOFF) */
299 #ifdef CRTSCTS
300                 newtio.c_cflag |= CRTSCTS;
301 #endif /* def CRTSCTS */
302                 /* fall through */
303         case XOnXOff: /* XOnXOff */
304             newtio.c_iflag |= IXOFF | IXON;
305                 break;
306         }
307         
308         if (cfsetospeed (&newtio, baud_rate) < 0 || cfsetispeed (&newtio, baud_rate) < 0 ||
309             tcsetattr (fd, TCSANOW, &newtio) < 0)
310         {
311                 return FALSE;
312         }
313         else
314         {
315         return TRUE;
316         }
317 }
318
319
320 static gint32
321 get_signal_code (MonoSerialSignal signal)
322 {
323         switch (signal) {
324                 case Cd:
325                         return TIOCM_CAR;
326                 case Cts:
327                         return TIOCM_CTS;
328                 case Dsr:
329                         return TIOCM_DSR;
330                 case Dtr:
331                         return TIOCM_DTR;
332                 case Rts:
333                         return TIOCM_RTS;
334                 default:
335                         return 0;
336         }
337
338         /* Not reached */
339         return 0;
340 }
341
342 static MonoSerialSignal
343 get_mono_signal_codes (int signals)
344 {
345         MonoSerialSignal retval = NoneSignal;
346
347         if ((signals & TIOCM_CAR) != 0)
348                 retval |= Cd;
349         if ((signals & TIOCM_CTS) != 0)
350                 retval |= Cts;
351         if ((signals & TIOCM_DSR) != 0)
352                 retval |= Dsr;
353         if ((signals & TIOCM_DTR) != 0)
354                 retval |= Dtr;
355         if ((signals & TIOCM_RTS) != 0)
356                 retval |= Rts;
357
358         return retval;
359 }
360
361 MonoSerialSignal
362 get_signals (int fd, gint32 *error)
363 {
364         int signals;
365
366         *error = 0;
367         
368         if (ioctl (fd, TIOCMGET, &signals) == -1) {
369                 *error = -1;
370                 return NoneSignal;
371         }
372         
373         return get_mono_signal_codes (signals);
374 }
375
376 gint32
377 set_signal (int fd, MonoSerialSignal signal, gboolean value)
378 {
379         int signals, expected, activated;
380
381         expected = get_signal_code (signal);
382         if (ioctl (fd, TIOCMGET, &signals) == -1)
383                 return -1;
384         
385         activated = (signals & expected) != 0;
386         if (activated == value) /* Already set */
387                 return 1;
388         
389         if (value)
390                 signals |= expected;
391         else
392                 signals &= ~expected;
393         
394         if (ioctl (fd, TIOCMSET, &signals) == -1)
395                 return -1;
396         
397         return 1;
398 }
399
400 void
401 breakprop (int fd)
402 {
403         tcsendbreak (fd, 0);
404 }
405
406 gboolean
407 poll_serial (int fd, gint32 *error, int timeout)
408 {
409         struct pollfd pinfo;
410         
411         *error = 0;
412         
413         pinfo.fd = fd;
414         pinfo.events = POLLIN;
415         pinfo.revents = 0;
416
417         while (poll (&pinfo, 1, timeout) == -1 && errno == EINTR) {
418                 /* EINTR is an OK condition, we should not throw in the upper layer an IOException */
419                 if (errno != EINTR){
420                         *error = -1;
421                         return FALSE;
422                 }
423         }
424
425         return (pinfo.revents & POLLIN) != 0 ? 1 : 0;
426 }
427
428 /*
429  * mono internals should not be used here.
430  * this serial stuff needs to be implemented with icalls.
431  * make this at least compile until the code is moved elsewhere
432  * defined(linux) is wrong, too
433  */
434 void*
435 list_serial_devices (void)
436 {
437         return NULL;
438 }
439
440 #if 0
441 MonoArray *
442 list_serial_devices (void)
443 {
444         MonoArray *array;
445 #if defined(linux)
446         /* Linux serial files are of the form ttyS[0-9]+ */
447         GSList *l, *list = NULL;
448         GDir* dir = g_dir_open ("/dev", 0, NULL);
449         const char *filename;
450         int i = 0;
451
452         while ((filename = g_dir_read_name (dir))) {
453                 if (filename) {
454                         if (!strncmp (filename, "ttyS", 4))
455                                 list = g_slist_append (list, g_strconcat ("/dev/", filename, NULL));
456                 }
457         }
458
459         g_dir_close (dir);
460   
461         array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
462         for (l = list; l; l = l->next) {
463                 mono_array_set (array, gpointer, i++, mono_string_new (mono_domain_get (), (char*)l->data));
464                 g_free (l->data);
465         }
466
467         g_slist_free (list);
468
469 #else
470 #warning "list_serial_devices isn't ported to this OS"
471 #endif
472
473         return array;
474 }
475 #endif
476