1 /* sqUnixSerial.c -- Unix serial support
2 *
3 * Last edited: 2011-03-14 14:01:56 by piumarta on emilia.ipe.media.kyoto-u.ac.jp
4 */
5
6 #include "sq.h"
7 #include "SerialPlugin.h"
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <termios.h>
17 #include <errno.h>
18
19 /*** Module variables ***/
20
21 #define PORT_NAME_SIZE 64
22
23 static const char serialPortBaseName[] = "/dev/tty";
24 static const char serialPortBaseNameDefault[] = "/dev/ttyU0";
25
26 /* stopBits 0=1.5, 1=1, 2=2 */
27 /* I don't know how to get 1.5 stop bits. Oh well. So you get 2 instead */
28 #define MAX_STOP_BITS 2
29 /* c_cflag definitions */
30 static const unsigned int stopBitsDecode[MAX_STOP_BITS + 1] = { CSTOPB, 0, CSTOPB };
31
32 /* dataBits number of bits per character (5..8) */
33 /* note that since CS5 is 0, you will get 5 data bits if you ask for 0..4
34 * as well */
35 #define MAX_DATA_BITS 8
36 /* c_cflag definitions */
37 static const unsigned int dataBitsDecode[MAX_DATA_BITS+1] = {
38 0, 0, 0, 0, 0, CS5, CS6, CS7, CS8
39 };
40
41 /* parityType 0=no, 1=odd, 2=even */
42 #define MAX_PARITY_TYPE 2
43 /* c_cflag definitions */
44 static const unsigned int parityTypeDecode[MAX_PARITY_TYPE+1] = {
45 0, /* none */
46 PARENB | PARODD, /* odd */
47 PARENB /* even */
48 };
49
50 /* inFlowCtrl 0=none, 1=XOn/XOff, 2=hardware handshaking
51 * outFlowCtrl 0=none, 1=XOn/XOff, 2=hardware handshaking */
52 #define MAX_FLOW_CTRL 2
53
54 typedef struct _serial_port_type
55 {
56 char spName[PORT_NAME_SIZE];
57 int spDescriptor;
58 struct termios spTermios;
59 } serial_port_type;
60
61 /* must be <= 10, because of 1-digit filename generation */
62 #define MAX_SERIAL_PORTS 32
63 static int sp_count= 0;
64 static serial_port_type previousSerialFiles[MAX_SERIAL_PORTS]; /* index=squeak port# */
65
66 /* dataRate rate in bps */
67 typedef struct drDecode { int dataRate; speed_t code; } drDecode;
68 static drDecode dataRateDecode[] = {
69 { 0, B0 }, /* hang up */
70 { 50, B50 },
71 { 75, B75 },
72 { 110, B110 },
73 { 134, B134 },
74 { 150, B150 },
75 { 200, B200 },
76 { 300, B300 },
77 { 600, B600 },
78 { 1200, B1200 },
79 { 1800, B1800 },
80 { 2400, B2400 },
81 { 4800, B4800 },
82 { 9600, B9600 },
83 { 19200, B19200 },
84 { 38400, B38400 },
85 #if defined(B57600) /* missing on SunOS 4 */
86 { 57600, B57600 },
87 { 115200, B115200 },
88 #endif
89 #if defined(B230400) /* missing on Digital Unix (ex DEC OSF/1) */
90 { 230400, B230400 },
91 #endif
92 #if defined(B460800) /* missing on FreeBSD */
93 { 460800, B460800 },
94 #endif
95 #if defined(B500000) /* missing on GNU/Linux prior to 2.2 */
96 { 500000, B500000 },
97 { 921600, B921600 },
98 { 1000000, B1000000 },
99 { 1500000, B1500000 },
100 { 2000000, B2000000 },
101 #endif
102 #if defined(B2500000) /* missing on GNU/Linux Sparc64 */
103 { 2500000, B2500000 },
104 { 3000000, B3000000 },
105 { 3500000, B3500000 },
106 { 4000000, B4000000 },
107 #endif
108 { -1, B0 } /* end marker */
109 };
110
111 /* This is the default setting for a termios structure on open */
112 static struct termios defaultTermios;
113
114 /*** Private Functions ***/
115
116 /* return the speed_t corresponding to the given data rate in bps,
117 * or B0 if not found */
serialDecodeSpeed(int speed)118 static speed_t serialDecodeSpeed(int speed)
119 {
120 drDecode *p;
121 for (p= dataRateDecode; p->dataRate >= 0; p++)
122 {
123 if (p->dataRate == speed)
124 return p->code;
125 }
126 return B0;
127 }
128
129 /* Compare entries in previousSerialFiles. */
130
serial_port_cmp(const serial_port_type * sp1,const serial_port_type * sp2)131 int serial_port_cmp(const serial_port_type *sp1, const serial_port_type *sp2)
132 {
133 return strcmp(sp1->spName, sp2->spName);
134 }
135
136 /* Return a previously-opened serial port by name, or NULL if the serial port was not found. */
137
find_stored_serialport(const char * serialPortName)138 serial_port_type *find_stored_serialport (const char *serialPortName)
139 {
140 serial_port_type target, *result;
141 strcpy((&target)->spName, serialPortName);
142 result= bsearch(&target, &previousSerialFiles, sp_count, sizeof (serial_port_type), (int(*)(const void *, const void *))serial_port_cmp);
143 return result;
144 }
145
146 /* generate a serial port filename (with last digit set to port number).
147 * If the port number is greater than 9, the portnumber is defaulted to 0. */
148
make_portname_from_portnum(char * serialPortName,const int portNum)149 void make_portname_from_portnum(char *serialPortName, const int portNum)
150 {
151 strcpy(serialPortName, serialPortBaseNameDefault);
152 if (portNum <= 9) serialPortName[strlen(serialPortName) - 1]= '0' + portNum;
153 }
154
155 /*** Public Functions ***/
156
157 /* return value ignored */
serialPortClose(int portNum)158 int serialPortClose(int portNum)
159 {
160 char serialPortName[PORT_NAME_SIZE];
161
162 if (portNum < 0 || portNum >= MAX_SERIAL_PORTS)
163 {
164 success(false);
165 return 0;
166 }
167
168 make_portname_from_portnum(serialPortName, portNum);
169
170 return serialPortCloseByName(serialPortName);
171 }
172
serialPortCloseByName(const char * portName)173 int serialPortCloseByName(const char *portName)
174 {
175 serial_port_type * sp= find_stored_serialport(portName);
176
177 /* Squeak wants to close inexistant or already-closed ports... */
178 if (sp == NULL || sp->spDescriptor < 0)
179 {
180 success(true);
181 return 0;
182 }
183
184 if (tcsetattr(sp->spDescriptor, TCSAFLUSH, &sp->spTermios))
185 {
186 fprintf(stderr, "Error while unsetting the com port parameter (errno:%d)\n", errno);
187 success(false);
188 return -1;
189 }
190
191 if (close(sp->spDescriptor))
192 {
193 fprintf(stderr, "Error while closing the com port (errno:%d)\n", errno);
194 success(false);
195 return -1;
196 }
197
198 /* Invalidate descriptor but leave name entry. If file will be reopened
199 * the same entry will be used. */
200 sp->spDescriptor= -1;
201
202 success(true);
203 return 0;
204 }
205
206 /* Open the given serial port using the given port number.
207 * "/dev/ttySxx" port name are assumed. */
serialPortOpen(int portNum,int dataRate,int stopBitsType,int parityType,int dataBits,int inFlowCtrl,int outFlowCtrl,int xOnChar,int xOffChar)208 int serialPortOpen(int portNum, int dataRate, int stopBitsType, int parityType, int dataBits,
209 int inFlowCtrl, int outFlowCtrl, int xOnChar, int xOffChar)
210 {
211 char serialPortName[PORT_NAME_SIZE];
212 make_portname_from_portnum(serialPortName, portNum);
213
214 return serialPortOpenByName(serialPortName, dataRate, stopBitsType, parityType, dataBits,
215 inFlowCtrl, outFlowCtrl, xOnChar, xOffChar);
216 }
217
218 /* If anything goes wrong during opening make sure the file descriptor
219 * is closed again, if it was opened already. */
portOpenFailed(serial_port_type * sp)220 static int portOpenFailed(serial_port_type *sp)
221 {
222 if (sp && 0 <= sp->spDescriptor)
223 {
224 if (close(sp->spDescriptor))
225 {
226 fprintf(stderr, "Error while closing the com port (errno:%d)\n", errno);
227 }
228 sp->spDescriptor= -1;
229 }
230
231 success(false);
232 return -1;
233 }
234
235 /* Open the given serial port using the given node as serial port. The
236 * data rate can be any number that is in the table above; the driver
237 * is not as flexible about the speed as the Mac driver, apparently.
238 * If the port is already open, it does nothing. */
239
serialPortOpenByName(char * portName,int dataRate,int stopBitsType,int parityType,int dataBits,int inFlowCtrl,int outFlowCtrl,int xOnChar,int xOffChar)240 int serialPortOpenByName(char *portName, int dataRate, int stopBitsType, int parityType, int dataBits,
241 int inFlowCtrl, int outFlowCtrl, int xOnChar, int xOffChar)
242 {
243 int newPort= false;
244 serial_port_type *sp= find_stored_serialport(portName);
245 if (!sp)
246 {
247 if (sp_count >= MAX_SERIAL_PORTS)
248 {
249 fprintf( stderr, "Error: maximum serial ports (%d) used.", MAX_SERIAL_PORTS);
250 success( false);
251 return -1;
252 }
253 sp= &previousSerialFiles[sp_count];
254 /* save the serial port name */
255 strcpy(sp->spName, portName);
256 newPort= true;
257 }
258 else if (sp->spDescriptor >= 0)
259 {
260 return 0;
261 }
262
263 {
264 speed_t speed= serialDecodeSpeed(dataRate);
265 struct termios flags;
266
267 /* validate arguments */
268 if (speed == B0
269 || stopBitsType < 0 || stopBitsType > MAX_STOP_BITS
270 || parityType < 0 || parityType > MAX_PARITY_TYPE
271 || dataBits < 0 || dataBits > MAX_DATA_BITS
272 || inFlowCtrl < 0 || inFlowCtrl > MAX_FLOW_CTRL
273 || outFlowCtrl < 0 || outFlowCtrl > MAX_FLOW_CTRL
274 || (( inFlowCtrl == 1 || outFlowCtrl == 1 )
275 && ( xOnChar < 0 || xOnChar > 255
276 || xOffChar < 0 || xOffChar > 255 )))
277 {
278 fprintf(stderr, "Incorrect serial port parameters.\n");
279 return portOpenFailed(sp);
280 }
281
282 /* open the device and save the file descriptor */
283 if ((sp->spDescriptor= open(portName, O_RDWR|O_NONBLOCK|O_NOCTTY)) < 0)
284 {
285 fprintf(stderr, "Error while opening the serial port (%s).\n", portName);
286 return portOpenFailed(sp);
287 }
288
289 /* save the old state */
290 if (tcgetattr(sp->spDescriptor, &sp->spTermios))
291 {
292 fprintf(stderr, "Error while saving old state.\n");
293 return portOpenFailed(sp);
294 }
295
296 /* set up the new modes */
297 flags= defaultTermios;
298
299 /* input & output data rate */
300 cfsetispeed(&flags, speed);
301 cfsetospeed(&flags, speed);
302
303 /* stop bits */
304 flags.c_cflag &= ~CSTOPB;
305 flags.c_cflag |= stopBitsDecode[ stopBitsType ];
306
307 /* parity */
308 flags.c_cflag &= ~(PARENB|PARODD);
309 flags.c_cflag |= parityTypeDecode[ parityType ];
310
311 /* data bits */
312 flags.c_cflag &= ~CSIZE;
313 flags.c_cflag |= dataBitsDecode[ dataBits ];
314
315 /* flow control characters */
316 if (inFlowCtrl == 1 || outFlowCtrl == 1)
317 {
318 flags.c_cc[VSTART] = xOnChar;
319 flags.c_cc[VSTOP] = xOffChar;
320 }
321
322 flags.c_iflag &= ~(IXON|IXOFF|IXANY);
323
324 if (inFlowCtrl == 1) flags.c_iflag |= IXOFF;
325 if (outFlowCtrl == 1) flags.c_iflag |= IXON;
326
327 # if defined(CRTSCTS)
328 flags.c_cflag &= ~CRTSCTS;
329 if (inFlowCtrl == 2 || outFlowCtrl == 2) flags.c_cflag |= CRTSCTS;
330 # else /* not defined in IRIX!? */
331 if (inFlowCtrl == 2 || outFlowCtrl == 2)
332 {
333 fprintf(stderr, "CRTSCTS not supported.\n");
334 return portOpenFailed(sp);
335 }
336 # endif
337
338 if (tcsetattr(sp->spDescriptor, TCSANOW, &flags)) /* set it NOW */
339 {
340 fprintf(stderr, "Error while setting terminal attributes.\n");
341 return portOpenFailed(sp);
342 }
343
344 if (newPort)
345 {
346 ++sp_count;
347 }
348
349 /* sorts the table of serial port, to ensure a reliable later retrieval. */
350 qsort(previousSerialFiles, sp_count, sizeof (serial_port_type), (int(*)(const void *, const void *))serial_port_cmp);
351 }
352
353 success(true);
354 return 0;
355 }
356
357 /* Read up to count bytes from the given serial port into the given
358 byte array. Read only up to the number of bytes in the port's
359 input buffer; if fewer bytes than count have been received, do not
360 wait for additional data to arrive. Return zero if no data is
361 available, else number of bytes read */
362
serialPortReadInto(int portNum,int count,void * startPtr)363 int serialPortReadInto(int portNum, int count, void *startPtr)
364 {
365 char serialPortName[PORT_NAME_SIZE];
366
367 if ((portNum < 0) || (portNum >= MAX_SERIAL_PORTS))
368 {
369 success(false);
370 return 0;
371 }
372
373 make_portname_from_portnum(serialPortName, portNum);
374
375 return serialPortReadIntoByName(serialPortName, count, startPtr);
376 }
377
serialPortReadIntoByName(const char * portName,int count,void * startPtr)378 int serialPortReadIntoByName(const char *portName, int count, void *startPtr)
379 {
380 serial_port_type *sp= find_stored_serialport(portName);
381 ssize_t bytesRead;
382
383 /* If the serialport doesn't exist or if it is already closed. */
384 if ((sp == NULL) || (sp->spDescriptor < 0))
385 {
386 fprintf(stderr, "Error while reading: serial port is not open.\n");
387 success(false);
388 return 0;
389 }
390
391 bytesRead= read(sp->spDescriptor, startPtr, (size_t)count);
392
393 if ((ssize_t)-1 == bytesRead)
394 {
395 if (EAGAIN == errno)
396 bytesRead= 0;
397 else
398 {
399 success(false);
400 return 0;
401 }
402 }
403
404 success(true);
405 return (int)bytesRead;
406 }
407
408 /* Write count bytes from the given byte array to the given serial
409 port's output buffer. Return the number of bytes written. This
410 implementation is synchronous: it doesn't return until the data has
411 been sent. However, other implementations may return before
412 transmission is complete. */
413
serialPortWriteFrom(int portNum,int count,void * startPtr)414 int serialPortWriteFrom(int portNum, int count, void *startPtr)
415 {
416 char serialPortName[PORT_NAME_SIZE];
417
418 if ((portNum < 0) || (portNum >= MAX_SERIAL_PORTS))
419 {
420 success(false);
421 return 0;
422 }
423
424 make_portname_from_portnum(serialPortName, portNum);
425
426 return serialPortWriteFromByName(serialPortName, count, startPtr);
427 }
428
serialPortWriteFromByName(const char * portName,int count,void * startPtr)429 int serialPortWriteFromByName(const char *portName, int count, void *startPtr)
430 {
431 serial_port_type *sp= find_stored_serialport(portName);
432 int bytesWritten;
433
434 /* If the serialport doesn't exist or if it is already closed. */
435 if ((sp == NULL) || (sp->spDescriptor < 0))
436 {
437 fprintf(stderr, "Error while writing: serial port is not open.\n");
438 success(false);
439 return 0;
440 }
441
442 bytesWritten= write(sp->spDescriptor, startPtr, (size_t)count);
443
444 if ((ssize_t)-1 == bytesWritten)
445 {
446 success(false);
447 return 0;
448 }
449
450 success(true);
451 return bytesWritten;
452 }
453
454 /* return true on success */
455
serialPortInit(void)456 int serialPortInit(void)
457 {
458 int i;
459
460 /* initialize the file descriptors to invalid */
461 for (i= 0; i < MAX_SERIAL_PORTS; i++)
462 {
463 previousSerialFiles[i].spDescriptor= -1;
464 }
465
466 /* initialize our default termios structure (already 0'd) */
467 defaultTermios.c_iflag= IGNBRK | IGNPAR; /* ignore break, parity/framing errs */
468 /* tcflag_t c_oflag output modes */
469 /* defaultTermios.c_oflag= 0; */
470 /* tcflag_t c_cflag control modes */
471 defaultTermios.c_cflag= CREAD;
472 /* tcflag_t c_lflag local modes */
473 /* defaultTermios.c_lflag= 0; */
474 /* cc_t c_cc[NCCS] control chars */
475 defaultTermios.c_cc[VTIME]= 0;
476 defaultTermios.c_cc[VMIN ]= 0;
477
478 success(true);
479 return 1;
480 }
481
482 /* return true on success */
483
serialPortShutdown(void)484 int serialPortShutdown(void)
485 {
486 success(true);
487 return 1;
488 }
489