2004-12-21 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
14 #include <mono/metadata/object.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/exception.h>
17
18 int
19 open_serial (char* devfile)
20 {
21         int fd;
22         struct termios newtio;
23
24         fd = open (devfile, O_RDWR);
25
26         if (fd == -1)
27                 return -1;
28
29         newtio.c_cflag = CLOCAL | CREAD;
30         newtio.c_iflag = 0;
31         newtio.c_oflag = 0;
32         newtio.c_lflag = 0;
33
34         tcflush(fd, TCIOFLUSH);
35         tcsetattr(fd,TCSANOW,&newtio);
36
37         fcntl (fd, F_SETFL, O_NONBLOCK);
38
39         return fd;
40 }
41
42 void
43 close_serial (int unix_fd)
44 {
45         close (unix_fd);
46 }
47
48 guint32
49 read_serial (int fd, guchar *buffer, int offset, int count, int timeout)
50 {
51         guint32 n;
52         struct pollfd ufd;
53
54         ufd.fd = fd;
55         ufd.events = POLLHUP | POLLIN | POLLERR;
56
57         poll (&ufd, 1, timeout);
58
59         if ((ufd.revents & POLLIN) != POLLIN) {
60                 mono_raise_exception (mono_get_exception_io ("ReadTimeout exceeded"));
61                 return -1;
62         }
63  
64         n = read (fd, buffer + offset, count);
65
66         return (guint32) n;
67 }
68
69 void
70 write_serial (int fd, guchar *buffer, int offset, int count, int timeout)
71 {
72         guint32 n;
73
74         struct pollfd ufd;
75
76         ufd.fd = fd;
77         ufd.events = POLLHUP | POLLOUT | POLLERR;
78
79         poll (&ufd, 1, timeout);
80
81         if ((ufd.revents & POLLOUT) != POLLOUT) {
82                 mono_raise_exception (mono_get_exception_io ("WriteTimeout exceeded"));
83                 return;
84         }
85  
86         n = write (fd, buffer + offset, count);
87 }
88
89 void
90 discard_buffer (int fd, gboolean input)
91 {
92         tcflush(fd, input ? TCIFLUSH : TCOFLUSH);
93 }
94
95 gboolean
96 set_attributes (int fd, int baud_rate, int parity, int dataBits, int stopBits, int handshake)
97 {
98         struct termios newtio;
99
100         tcgetattr (fd, &newtio);
101
102         switch (baud_rate) {
103         case 230400: baud_rate = B230400; break;
104         case 115200: baud_rate = B115200; break;
105         case 57600: baud_rate = B57600; break;
106         case 38400: baud_rate = B38400; break;
107         case 19200: baud_rate = B19200; break;
108         case 9600: baud_rate = B9600; break;
109         case 4800: baud_rate = B4800; break;
110         case 2400: baud_rate = B2400; break;
111         case 1800: baud_rate = B1800; break;
112         case 1200: baud_rate = B1200; break;
113         case 600: baud_rate = B600; break;
114         case 300: baud_rate = B300; break;
115         case 200: baud_rate = B200; break;
116         case 150: baud_rate = B150; break;
117         case 134: baud_rate = B134; break;
118         case 110: baud_rate = B110; break;
119         case 75: baud_rate = B75; break;
120         case 50:
121         case 0:
122         default:
123                 baud_rate = B9600;
124                 break;
125         }
126
127         switch (parity) {
128         case 0: /* Even */
129                 newtio.c_iflag &= ~IGNPAR;
130                 newtio.c_cflag |= PARENB;
131                 break;
132         case 1: /* Mark */
133                 /* XXX unhandled */
134                 break;
135         case 2: /* None */
136                 newtio.c_iflag |= IGNPAR;
137                 newtio.c_cflag &= ~(PARENB | PARODD);
138                 break;
139         case 3: /* Odd */
140                 newtio.c_iflag &= ~IGNPAR;
141                 newtio.c_cflag |= PARENB | PARODD;
142                 break;
143         case 4: /* Space */
144                 /* XXX unhandled */
145                 break;
146         }
147
148         newtio.c_cflag &= ~CSIZE;
149         switch (dataBits) {
150         case 5: newtio.c_cflag |= CS5; break;
151         case 6: newtio.c_cflag |= CS6; break;
152         case 7: newtio.c_cflag |= CS7; break;
153         case 8:
154         default:
155                 newtio.c_cflag |= CS8;
156                 break;
157         }
158
159         newtio.c_cflag &= ~CSTOPB;
160         switch (stopBits) {
161         case 0: /* One */
162                 /* do nothing, the default is one stop bit */
163                 break;
164         case 1: /* OnePointFive */
165                 /* XXX unhandled */
166                 break;
167         case 2: /* Two */
168                 newtio.c_cflag |= CSTOPB;
169                 break;
170         }
171
172         newtio.c_iflag &= ~IXOFF;
173         newtio.c_oflag &= ~IXON;
174         newtio.c_cflag &= ~CRTSCTS;
175         switch (handshake) {
176         case 0: /* None */
177                 /* do nothing */
178                 break;
179         case 1: /* RequestToSend (RTS) */
180                 newtio.c_cflag |= CRTSCTS;
181                 break;
182         case 2: /* RequestToSendXOnXOff (RTS + XON/XOFF) */
183                 newtio.c_cflag |= CRTSCTS;
184                 /* fall through */
185         case 3: /* XOnXOff */
186                 newtio.c_iflag |= IXOFF;
187                 //              newtio.c_oflag |= IXON;
188                 break;
189         }
190         
191         cfsetospeed (&newtio, baud_rate);
192         cfsetispeed (&newtio, baud_rate);
193
194         tcsetattr(fd,TCSADRAIN,&newtio);
195
196         return TRUE;
197 }
198
199 MonoArray *
200 list_serial_devices (void)
201 {
202         MonoArray *array;
203 #if defined(linux)
204         /* Linux serial files are of the form ttyS[0-9]+ */
205         GSList *l, *list = NULL;
206         GDir* dir = g_dir_open ("/dev", 0, NULL);
207         const char *filename;
208         int i = 0;
209
210         while ((filename = g_dir_read_name (dir))) {
211                 if (filename) {
212                         if (!strncmp (filename, "ttyS", 4))
213                                 list = g_slist_append (list, g_strconcat ("/dev/", filename, NULL));
214                 }
215         }
216
217         g_dir_close (dir);
218   
219         array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
220         for (l = list; l; l = l->next) {
221                 mono_array_set (array, gpointer, i++, mono_string_new (mono_domain_get (), (char*)l->data));
222                 g_free (l->data);
223         }
224
225         g_slist_free (list);
226
227 #else
228 #warning "list_serial_devices isn't ported to this OS"
229 #endif
230
231         return array;
232 }