2006-03-24 Chris Toshok <toshok@ximian.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
15 #include <glib.h>
16
17 /* This is a copy of System.IO.Ports.Handshake */
18 typedef enum {
19         NoneHandshake = 0,
20         XOnXOff = 1,
21         RequestToSend = 2,
22         RequestToSendXOnXOff = 3
23 } MonoHandshake;
24
25 /* This is a copy of System.IO.Ports.Parity */
26 typedef enum {
27         NoneParity = 0,
28         Odd = 1,
29         Even = 2,
30         Mark = 3,
31         Space = 4
32 } MonoParity;
33
34 /* This is a copy of System.IO.Ports.StopBits */
35 typedef enum {
36         NoneStopBits = 0,
37         One = 1,
38         Two = 2,
39         OnePointFive = 3
40 } MonoStopBits;
41
42 /* This is a copy of System.IO.Ports.SerialSignal */
43 typedef enum {
44         Cd = 0, /* Carrier detect */
45         Cts = 1, /* Clear to send */
46         Dsr = 2, /* Data set ready */
47         Dtr = 3, /* Data terminal ready */
48         Rts = 4  /* Request to send */
49 } MonoSerialSignal;
50
51 int
52 open_serial (char* devfile)
53 {
54         int fd;
55         struct termios newtio;
56
57         fd = open (devfile, O_RDWR);
58
59         if (fd == -1)
60                 return -1;
61
62         newtio.c_cflag = CLOCAL | CREAD;
63         newtio.c_iflag = 0;
64         newtio.c_oflag = 0;
65         newtio.c_lflag = 0;
66
67         tcflush(fd, TCIOFLUSH);
68         tcsetattr(fd,TCSANOW,&newtio);
69
70         fcntl (fd, F_SETFL, O_NONBLOCK);
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, int timeout)
83 {
84         guint32 n;
85         struct pollfd ufd;
86
87         ufd.fd = fd;
88         ufd.events = POLLHUP | POLLIN | POLLERR;
89
90         poll (&ufd, 1, timeout);
91
92         if ((ufd.revents & POLLIN) != POLLIN) {
93                 return -1;
94         }
95  
96         n = read (fd, buffer + offset, count);
97
98         return (guint32) n;
99 }
100
101 void
102 write_serial (int fd, guchar *buffer, int offset, int count, int timeout)
103 {
104         guint32 n;
105
106         struct pollfd ufd;
107
108         ufd.fd = fd;
109         ufd.events = POLLHUP | POLLOUT | POLLERR;
110
111         poll (&ufd, 1, timeout);
112
113         if ((ufd.revents & POLLOUT) != POLLOUT) {
114                 return;
115         }
116  
117         n = write (fd, buffer + offset, count);
118 }
119
120 void
121 discard_buffer (int fd, gboolean input)
122 {
123         tcflush(fd, input ? TCIFLUSH : TCOFLUSH);
124 }
125
126 gboolean
127 set_attributes (int fd, int baud_rate, MonoParity parity, int dataBits, MonoStopBits stopBits, MonoHandshake handshake)
128 {
129         struct termios newtio;
130
131         tcgetattr (fd, &newtio);
132
133         switch (baud_rate) {
134         case 230400: baud_rate = B230400; break;
135         case 115200: baud_rate = B115200; break;
136         case 57600: baud_rate = B57600; break;
137         case 38400: baud_rate = B38400; break;
138         case 19200: baud_rate = B19200; break;
139         case 9600: baud_rate = B9600; break;
140         case 4800: baud_rate = B4800; break;
141         case 2400: baud_rate = B2400; break;
142         case 1800: baud_rate = B1800; break;
143         case 1200: baud_rate = B1200; break;
144         case 600: baud_rate = B600; break;
145         case 300: baud_rate = B300; break;
146         case 200: baud_rate = B200; break;
147         case 150: baud_rate = B150; break;
148         case 134: baud_rate = B134; break;
149         case 110: baud_rate = B110; break;
150         case 75: baud_rate = B75; break;
151         case 50:
152         case 0:
153         default:
154                 baud_rate = B9600;
155                 break;
156         }
157
158         switch (parity) {
159         case NoneParity: /* None */
160                 newtio.c_iflag |= IGNPAR;
161                 newtio.c_cflag &= ~(PARENB | PARODD);
162                 break;
163         case Odd: /* Odd */
164                 newtio.c_iflag &= ~IGNPAR;
165                 newtio.c_cflag |= PARENB | PARODD;
166                 break;
167         case Even: /* Even */
168                 newtio.c_iflag &= ~(IGNPAR | PARODD);
169                 newtio.c_cflag |= PARENB;
170                 break;
171         case Mark: /* Mark */
172                 /* XXX unhandled */
173                 break;
174         case Space: /* Space */
175                 /* XXX unhandled */
176                 break;
177         }
178
179         newtio.c_cflag &= ~CSIZE;
180         switch (dataBits) {
181         case 5: newtio.c_cflag |= CS5; break;
182         case 6: newtio.c_cflag |= CS6; break;
183         case 7: newtio.c_cflag |= CS7; break;
184         case 8:
185         default:
186                 newtio.c_cflag |= CS8;
187                 break;
188         }
189
190         newtio.c_cflag &= ~CSTOPB;
191         switch (stopBits) {
192         case NoneStopBits:
193                 /* Unhandled */
194                 break;
195         case One: /* One */
196                 /* do nothing, the default is one stop bit */
197                 break;
198         case Two: /* Two */
199                 newtio.c_cflag |= CSTOPB;
200                 break;
201         case OnePointFive: /* OnePointFive */
202                 /* XXX unhandled */
203                 break;
204         }
205
206         newtio.c_iflag &= ~IXOFF;
207         newtio.c_oflag &= ~IXON;
208 #ifdef CRTSCTS
209         newtio.c_cflag &= ~CRTSCTS;
210 #endif /* def CRTSCTS */
211         switch (handshake) {
212         case NoneHandshake: /* None */
213                 /* do nothing */
214                 break;
215         case RequestToSend: /* RequestToSend (RTS) */
216 #ifdef CRTSCTS
217                 newtio.c_cflag |= CRTSCTS;
218 #endif /* def CRTSCTS */
219                 break;
220         case RequestToSendXOnXOff: /* RequestToSendXOnXOff (RTS + XON/XOFF) */
221 #ifdef CRTSCTS
222                 newtio.c_cflag |= CRTSCTS;
223 #endif /* def CRTSCTS */
224                 /* fall through */
225         case XOnXOff: /* XOnXOff */
226                 newtio.c_iflag |= IXOFF;
227                 //              newtio.c_oflag |= IXON;
228                 break;
229         }
230         
231         if (cfsetospeed (&newtio, baud_rate) < 0 || cfsetispeed (&newtio, baud_rate) < 0 ||
232                         tcsetattr (fd, TCSADRAIN, &newtio) < 0)
233                 return FALSE;
234
235         return TRUE;
236 }
237
238 static gint32
239 get_signal_code (MonoSerialSignal signal)
240 {
241         switch (signal) {
242                 case Cd:
243                         return TIOCM_CAR;
244                 case Cts:
245                         return TIOCM_CTS;
246                 case Dsr:
247                         return TIOCM_DSR;
248                 case Dtr:
249                         return TIOCM_DTR;
250                 case Rts:
251                         return TIOCM_RTS;
252         }
253
254         /* Not reached */
255         return 0;
256 }
257
258 gint32
259 get_signal (int fd, MonoSerialSignal signal)
260 {
261         int signals, expected;
262
263         expected = get_signal_code (signal);
264         if (ioctl (fd, TIOCMGET, &signals) == -1)
265                 return -1;
266         
267         return (expected & signals) != 0;
268 }
269
270 gint32
271 set_signal (int fd, MonoSerialSignal signal, gboolean value)
272 {
273         int signals, expected, activated;
274
275         expected = get_signal_code (signal);
276         if (ioctl (fd, TIOCMGET, &signals) == -1)
277                 return -1;
278         
279         activated = (signals & expected) != 0;
280         if (activated == value) /* Already set */
281                 return 1;
282         
283         if (value)
284                 signals |= expected;
285         else
286                 signals &= ~expected;
287         
288         if (ioctl (fd, TIOCMSET, &signals) == -1)
289                 return -1;
290         
291         return 1;
292 }
293
294 /*
295  * mono internals should not be used here.
296  * this serial stuff needs to be implemented with icalls.
297  * make this at least compile until the code is moved elsewhere
298  * defined(linux) is wrong, too
299  */
300 void*
301 list_serial_devices (void)
302 {
303         return NULL;
304 }
305
306 #if 0
307 MonoArray *
308 list_serial_devices (void)
309 {
310         MonoArray *array;
311 #if defined(linux)
312         /* Linux serial files are of the form ttyS[0-9]+ */
313         GSList *l, *list = NULL;
314         GDir* dir = g_dir_open ("/dev", 0, NULL);
315         const char *filename;
316         int i = 0;
317
318         while ((filename = g_dir_read_name (dir))) {
319                 if (filename) {
320                         if (!strncmp (filename, "ttyS", 4))
321                                 list = g_slist_append (list, g_strconcat ("/dev/", filename, NULL));
322                 }
323         }
324
325         g_dir_close (dir);
326   
327         array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
328         for (l = list; l; l = l->next) {
329                 mono_array_set (array, gpointer, i++, mono_string_new (mono_domain_get (), (char*)l->data));
330                 g_free (l->data);
331         }
332
333         g_slist_free (list);
334
335 #else
336 #warning "list_serial_devices isn't ported to this OS"
337 #endif
338
339         return array;
340 }
341 #endif
342