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