FreeBASIC  0.91.0
io_serial.c
Go to the documentation of this file.
1 /* serial port access for Linux */
2 
3 #include "../fb.h"
4 #include <sys/ioctl.h>
5 #include <sys/select.h>
6 #include <signal.h>
7 #include <fcntl.h>
8 
9 /* Uncomment HAS_LOCKDEV to active lock file funcionality, not forget
10  * compile whith -llockdev
11  */
12 /* #define HAS_LOCKDEV 1 */
13 #ifdef HAS_LOCKDEV
14 #include <lockdev.h>
15 #endif
16 
17 #define BUFFERSIZE BUFSIZ*16
18 #define ENDSPD 111111
19 #define BADSPEED 999999
20 #define SERIAL_TIMEOUT 3 /* seconds for write on open*/
21 #define SREAD_TIMEOUT 70 /* if not receive any character in less 50 millisecs finish read process */
22 
23 
24 typedef struct _LINUX_SERIAL_INFO {
25  int sfd;
26  struct termios oldtty, newtty;
27 #ifdef HAS_LOCKDEV
28  pid_t pplckid;
29 #endif
30  int iPort;
33 
34 static void alrm()
35 {
36  /* signal callback, do nothing */
37 }
38 
39 static speed_t get_speed( int speed )
40 {
41  static unsigned int sp[][2] =
42  {
43  {0, B0},
44  {50, B50},
45  {150, B150},
46  {300, B300},
47  {600, B600},
48  {1200, B1200},
49  {1800, B1800},
50  {2400, B2400},
51  {4800, B4800},
52  {9600, B9600},
53  {19200, B19200},
54  {38400, B38400},
55 #ifdef B57600
56  {57600, B57600 },
57 #endif
58 #ifdef B115200
59  {115200, B115200 },
60 #endif
61 #ifdef B230400
62  {230400, B230400 },
63 #endif
64 #ifdef B460800
65  {460800, B460800 },
66 #endif
67 #ifdef B500000
68  {500000, B500000 },
69 #endif
70 #ifdef B576000
71  {576000, B576000 },
72 #endif
73 #ifdef B921600
74  {921600, B921600 },
75 #endif
76 #ifdef B1000000
77  {1000000, B1000000 },
78 #endif
79 #ifdef B1152000
80  {1152000, B1152000 },
81 #endif
82 
83  {ENDSPD, 0},
84  {0, 0}
85  };
86 
87  int n;
88  speed_t Rspeed;
89 
90  for (n = 0; sp[n][0] != speed; n++)
91  {
92  if (sp[n][0] == ENDSPD) /*invalid speed */
93  return (BADSPEED);
94  }
95  Rspeed = sp[n][1];
96 
97  return(Rspeed);
98 }
99 
100 int fb_SerialOpen
101  (
102  FB_FILE *handle,
103  int iPort,
104  FB_SERIAL_OPTIONS *options,
105  const char *pszDevice,
106  void **ppvHandle
107  )
108 {
109  int res = FB_RTERROR_OK;
110  int DesiredAccess = O_RDWR|O_NOCTTY|O_NONBLOCK;
111  int SerialFD = (-1);
112  char DeviceName[512];
113  struct termios oldserp, nwserp;
114  speed_t TermSpeed;
115 #ifdef HAS_LOCKDEV
116  pid_t plckid;
117 #endif
118 
119  /* The IRQ stuff is not supported on Linux ... */
120  if( options->IRQNumber != 0 )
121  {
123  }
124 
125  res = fb_ErrorSetNum( FB_RTERROR_OK );
126 
127  switch( handle->access )
128  {
129  case FB_FILE_ACCESS_READ:
130  DesiredAccess |= O_RDONLY;
131  break;
133  DesiredAccess |= O_WRONLY;
134  break;
136  /* fall through */
137  case FB_FILE_ACCESS_ANY:
138  DesiredAccess |= O_RDWR;
139  break;
140  }
141 
142  DeviceName[0] = '\0';
143 
144  if( iPort == 0 )
145  {
146  if( strcasecmp(pszDevice, "COM") == 0 )
147  {
148  strcpy( DeviceName, "/dev/modem" );
149  }
150  else
151  {
152  strcpy( DeviceName, pszDevice );
153  }
154  }
155  else
156  {
157  sprintf(DeviceName, "/dev/ttyS%d", (iPort-1));
158  }
159 
160  /* Setting speed baud line */
161  TermSpeed = get_speed(options->uiSpeed);
162  if( TermSpeed == BADSPEED )
163  {
165  }
166 
167 #ifdef HAS_LOCKDEV
168  if( dev_testlock(DeviceName) )
169  {
171  }
172 
173  plckid = dev_lock(DeviceName);
174  if( plckid < 0 )
175  {
177  }
178 #endif
179 
180  alarm(SERIAL_TIMEOUT);
181  SerialFD = open( DeviceName, DesiredAccess );
182  alarm(0);
183  if( SerialFD < 0)
184  {
185 #ifdef HAS_LOCKDEV
186  dev_unlock(DeviceName, plckid);
187 #endif
189  }
190 
191  /* !!!FIXME!!! Lock file handle (handle->lock) pending, you can use fcnctl or flock functions */
192 
193  /* Make the file descriptor asynchronous */
194  /* fcntl(SerialFD, F_SETFL, FASYNC); */
195 
196  /* Save old status of serial port discipline */
197  if( tcgetattr ( SerialFD, &oldserp ) )
198  {
200  }
201 
202  /* Discard data write/read in serial port not transmitted */
203  if( tcflush( SerialFD, TCIOFLUSH) )
204  {
206  }
207 
208  /* Inittialize new struct termios with old values */
209  if( tcgetattr ( SerialFD, &nwserp ) )
210  {
212  }
213 
214  /* Set timeouts
215  * Timeout not are defined in UNIX termio/s
216  * set CTS > 0 enable CTSRTS flow control,
217  * other are ignored are setting for default in open function
218  * !!!FIXME!!! ???
219  */
220 
221  /* setup generic serial port configuration */
222  if( res == FB_RTERROR_OK )
223  {
224  /* Initialize */
225  nwserp.c_cflag |= CREAD; /* Enable receiver */
226  nwserp.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable Software Flow Control */
227  nwserp.c_cflag |= CREAD; /* Enable receiver */
228 
229  if( options->AddLF )
230  {
231  /*With AddFl Set, Process Canonical output/input */
232  nwserp.c_lflag |= (ICANON|OPOST|ONLCR); /* Postprocess output and map newline at nl/cr */
233  }
234  else
235  {
236  /* Set raw tty settings */
237  cfmakeraw(&nwserp);
238  nwserp.c_cc[VMIN] = 1 ; /* Wait min for 1 char */
239  nwserp.c_cc[VTIME] = 0 ; /* Not use timeout */
240  }
241 
242  if( options->KeepDTREnabled )
243  nwserp.c_cflag &= ~(HUPCL); /* Not Hangup (set DTR) on last close */
244  else
245  nwserp.c_cflag |= (HUPCL); /* Hangup (drop DTR) on last close */
246 
247  /* CD (Carrier Detect) and DS (Data Set Ready) are modem signal
248  * in UNIXes the flag CLOCAL attend the modem signals. Quickly, if your conection is
249  * a modem telephony device active CD[0-n] and DS[0-n]
250  * else for local conections set CD0 or DS0
251  */
252  /* DS and CD are ignored */
253  if( options->DurationDSR || options->DurationCD )
254  {
255  nwserp.c_cflag &= ~(CLOCAL);
256  }
257  else
258  {
259  nwserp.c_cflag |= CLOCAL; /* Ignore modem control Lines */
260  }
261 
262  /* Termios not manage timeout for CTS, but understand RTSCTS flow control
263  * if DurationCTS is greater zero CTSRTS flow will be activate
264  */
265  if( options->DurationCTS != 0 && !options->SuppressRTS )
266  nwserp.c_cflag |= CRTSCTS;
267  else
268  nwserp.c_cflag &= ~CRTSCTS;
269 
270  /* Setting speed baud and other serial parameters */
271  nwserp.c_cflag |= TermSpeed ;
272  /* Set size word 5,6,7,8 sonly support */
273  nwserp.c_cflag &= ~(CSIZE);
274 
275  switch ( options->uiDataBits)
276  {
277  case 5:
278  nwserp.c_cflag |= CS5 ;
279  break;
280  case 6:
281  nwserp.c_cflag |= CS6 ;
282  break;
283  case 7:
284  nwserp.c_cflag |= CS7 ;
285  break;
286  case 8:
287  /* fall through */
288  default:
289  nwserp.c_cflag |= CS8 ;
290  }
291 
292  /* Setting parity, 1.5 StopBit not supported */
293  switch ( options->Parity )
294  {
296  nwserp.c_cflag &= ~(PARENB);
297  break;
298 
299  /* 7bits and Space parity is the same (7S1) that (8N1) 8 bits without parity */
301  nwserp.c_cflag &= ~(PARENB);
302  nwserp.c_cflag |= CS8;
303  break;
304 
305  /* !!!FIXME!!! I'm not sure for mark parity, set the input line. Fix me! for output */
306  case FB_SERIAL_PARITY_MARK:
307  nwserp.c_iflag |= (PARMRK);
308  /* fall through */
309 
311  nwserp.c_iflag |= (INPCK | ISTRIP);
312  nwserp.c_cflag |= PARENB;
313  break;
314 
316  nwserp.c_iflag |= (INPCK | ISTRIP);
317  nwserp.c_cflag |= (PARENB|PARODD);
318  break;
319  }
320 
321  /* Ignore all parity errors, can be dangerous */
322  if ( options->IgnoreAllErrors ) {
323  nwserp.c_iflag |= (IGNPAR);
324  } else {
325  nwserp.c_iflag &= ~(IGNPAR);
326  }
327 
328  switch ( options->StopBits )
329  {
331  nwserp.c_cflag &= ~(CSTOPB);
332  break;
333 
334  /* 1.5 Stop not support 2 Stop bits assumed */
336  /* fall through */
337 
339  nwserp.c_cflag |= CSTOPB;
340  break;
341  }
342 
343  /* If not RTS hardware flow, sotfware IXANY softflow assumed */
344  if( options->SuppressRTS )
345  {
346  nwserp.c_iflag &= ~(IXON | IXOFF | IXANY);
347  nwserp.c_iflag |= (IXON | IXANY);
348  }
349 
350  if( res == FB_RTERROR_OK )
351  {
352  /* Active set serial parameters */
353  if( tcsetattr( SerialFD, TCSAFLUSH, &nwserp ) )
355  }
356  }
357 
358  /* error? */
359  if( res != FB_RTERROR_OK )
360  {
361 #ifdef HAS_LOCKDEV
362  dev_unlock(DeviceName, plckid);
363 #endif
364  tcsetattr( SerialFD, TCSAFLUSH, &oldserp); /* Restore old parameter of serial line */
365  close(SerialFD);
366  }
367  else
368  {
369  LINUX_SERIAL_INFO *pInfo = (LINUX_SERIAL_INFO *) calloc( 1, sizeof(LINUX_SERIAL_INFO) );
370  DBG_ASSERT( ppvHandle!=NULL );
371  *ppvHandle = pInfo;
372  pInfo->sfd = SerialFD;
373  pInfo->oldtty = oldserp;
374  pInfo->newtty = nwserp;
375 #ifdef HAS_LOCKDEV
376  pInfo->pplckid = plckid;
377 #endif
378  pInfo->iPort = iPort;
379  pInfo->pOptions = options;
380  }
381 
382  return res;
383 }
384 
385 int fb_SerialGetRemaining( FB_FILE *handle, void *pvHandle, fb_off_t *pLength )
386 {
387  int rBytes;
388  int SerialFD;
389  LINUX_SERIAL_INFO *pInfo = (LINUX_SERIAL_INFO *) pvHandle;
390 
391  SerialFD = pInfo->sfd;
392  if( ioctl(SerialFD, FIONREAD, &rBytes) )
394 
395  if( pLength )
396  *pLength = rBytes;
397 
398  return fb_ErrorSetNum( FB_RTERROR_OK );
399 }
400 
401 int fb_SerialWrite
402  (
403  FB_FILE *handle,
404  void *pvHandle,
405  const void *data,
406  size_t length
407  )
408 {
409  ssize_t rlng=0;
410  LINUX_SERIAL_INFO *pInfo = (LINUX_SERIAL_INFO *) pvHandle;
411  int SerialFD = pInfo->sfd;
412 
413  (void) signal(SIGALRM, alrm);
414  alarm( SERIAL_TIMEOUT );
415  rlng=write(SerialFD, data, length);
416  alarm(0);
417 
418  if( rlng <= 0 )
420 
421  if( length != rlng )
423 
424  return fb_ErrorSetNum( FB_RTERROR_OK );
425 }
426 
427 int fb_SerialRead( FB_FILE *handle, void *pvHandle, void *data, size_t *pLength )
428 {
429  LINUX_SERIAL_INFO *pInfo = (LINUX_SERIAL_INFO *) pvHandle;
430  int SerialFD;
431  ssize_t count = 0;
432  fd_set rfds;
433  struct timeval tmout;
434 
435  SerialFD = pInfo->sfd;
436 
437  FD_ZERO( &rfds );
438  FD_SET( SerialFD, &rfds );
439 
440  tmout.tv_sec = 0;
441  tmout.tv_usec = (SREAD_TIMEOUT*1000L); /* convert to microsecs */
442 
443  select( SerialFD+1, &rfds, NULL, NULL, &tmout );
444  if ( FD_ISSET(SerialFD, &rfds) )
445  {
446  if ( (count = read(SerialFD, data, *pLength)) < 0 )
447  {
449  }
450  }
451 
452  *pLength = count;
453 
454  return fb_ErrorSetNum( FB_RTERROR_OK );
455 }
456 
457 int fb_SerialClose( FB_FILE *handle, void *pvHandle )
458 {
459  int SerialFD;
460  struct termios oserp;
461 #ifdef HAS_LOCKDEV
462  pid_t plckid;
463 #endif
464  LINUX_SERIAL_INFO *pInfo = (LINUX_SERIAL_INFO *) pvHandle;
465 
466  SerialFD = pInfo->sfd;
467  oserp = pInfo->oldtty;
468 #ifdef HAS_LOCKDEV
469  plckid = pInfo->pplckid;
470 #endif
471 #ifdef HAS_LOCKDEV
472  dev_unlock(DeviceName, plckid);
473 #endif
474 
475  /* Restore old parameter of serial line */
476  tcsetattr( SerialFD, TCSAFLUSH, &oserp);
477 
478  close(SerialFD);
479  free(pInfo);
480 
481  return fb_ErrorSetNum( FB_RTERROR_OK );
482 }