libnfc  1.4.2
uart_posix.c
Go to the documentation of this file.
1 /*-
2  * Public platform independent Near Field Communication (NFC) library
3  *
4  * Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>
18  *
19  */
20 
26 # include <sys/select.h>
27 # include <sys/param.h>
28 # include <termios.h>
29 typedef struct termios term_info;
30 typedef struct {
31  int fd; // Serial port file descriptor
32  term_info tiOld; // Terminal info before using the port
33  term_info tiNew; // Terminal info during the transaction
34 } serial_port_unix;
35 
36 // timeval struct that define timeout delay for serial port:
37 // first is constant and currently related to PN53x response delay
38 static const unsigned long int uiTimeoutStatic = 15000; // 15 ms to allow device to respond
39 // second is a per-byte timeout (sets when setting baudrate)
40 static unsigned long int uiTimeoutPerByte = 0;
41 
42 // Work-around to claim uart interface using the c_iflag (software input processing) from the termios struct
43 # define CCLAIMED 0x80000000
44 
45 serial_port
46 uart_open (const char *pcPortName)
47 {
48  serial_port_unix *sp = malloc (sizeof (serial_port_unix));
49 
50  if (sp == 0)
51  return INVALID_SERIAL_PORT;
52 
53  sp->fd = open (pcPortName, O_RDWR | O_NOCTTY | O_NONBLOCK);
54  if (sp->fd == -1) {
55  uart_close (sp);
56  return INVALID_SERIAL_PORT;
57  }
58 
59  if (tcgetattr (sp->fd, &sp->tiOld) == -1) {
60  uart_close (sp);
61  return INVALID_SERIAL_PORT;
62  }
63  // Make sure the port is not claimed already
64  if (sp->tiOld.c_iflag & CCLAIMED) {
65  uart_close (sp);
66  return CLAIMED_SERIAL_PORT;
67  }
68  // Copy the old terminal info struct
69  sp->tiNew = sp->tiOld;
70 
71  sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD;
72  sp->tiNew.c_iflag = CCLAIMED | IGNPAR;
73  sp->tiNew.c_oflag = 0;
74  sp->tiNew.c_lflag = 0;
75 
76  sp->tiNew.c_cc[VMIN] = 0; // block until n bytes are received
77  sp->tiNew.c_cc[VTIME] = 0; // block until a timer expires (n * 100 mSec.)
78 
79  if (tcsetattr (sp->fd, TCSANOW, &sp->tiNew) == -1) {
80  uart_close (sp);
81  return INVALID_SERIAL_PORT;
82  }
83 
84  tcflush (sp->fd, TCIFLUSH);
85  return sp;
86 }
87 
99 #define UART_BAUDRATE_T0_BYTE_DURATION(X) ((1000000 * 10)/ X)
100 
101 void
102 uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
103 {
104  // Set per-byte timeout
105  uiTimeoutPerByte = UART_BAUDRATE_T0_BYTE_DURATION(uiPortSpeed);
106  DBG ("Serial port speed requested to be set to %d bauds (%lu µs).", uiPortSpeed, uiTimeoutPerByte);
107  const serial_port_unix *spu = (serial_port_unix *) sp;
108 
109  // Portability note: on some systems, B9600 != 9600 so we have to do
110  // uint32_t <=> speed_t associations by hand.
111  speed_t stPortSpeed = B9600;
112  switch (uiPortSpeed) {
113  case 9600:
114  stPortSpeed = B9600;
115  break;
116  case 19200:
117  stPortSpeed = B19200;
118  break;
119  case 38400:
120  stPortSpeed = B38400;
121  break;
122 # ifdef B57600
123  case 57600:
124  stPortSpeed = B57600;
125  break;
126 # endif
127 # ifdef B115200
128  case 115200:
129  stPortSpeed = B115200;
130  break;
131 # endif
132 # ifdef B230400
133  case 230400:
134  stPortSpeed = B230400;
135  break;
136 # endif
137 # ifdef B460800
138  case 460800:
139  stPortSpeed = B460800;
140  break;
141 # endif
142  default:
143  ERR ("Unable to set serial port speed to %d bauds. Speed value must be one of those defined in termios(3).",
144  uiPortSpeed);
145  return;
146  };
147 
148  // Set port speed (Input and Output)
149  cfsetispeed ((struct termios *) &(spu->tiNew), stPortSpeed);
150  cfsetospeed ((struct termios *) &(spu->tiNew), stPortSpeed);
151  if (tcsetattr (spu->fd, TCSADRAIN, &(spu->tiNew)) == -1) {
152  ERR ("%s", "Unable to apply new speed settings.");
153  }
154 }
155 
156 uint32_t
157 uart_get_speed (serial_port sp)
158 {
159  uint32_t uiPortSpeed = 0;
160  const serial_port_unix *spu = (serial_port_unix *) sp;
161  switch (cfgetispeed (&spu->tiNew)) {
162  case B9600:
163  uiPortSpeed = 9600;
164  break;
165  case B19200:
166  uiPortSpeed = 19200;
167  break;
168  case B38400:
169  uiPortSpeed = 38400;
170  break;
171 # ifdef B57600
172  case B57600:
173  uiPortSpeed = 57600;
174  break;
175 # endif
176 # ifdef B115200
177  case B115200:
178  uiPortSpeed = 115200;
179  break;
180 # endif
181 # ifdef B230400
182  case B230400:
183  uiPortSpeed = 230400;
184  break;
185 # endif
186 # ifdef B460800
187  case B460800:
188  uiPortSpeed = 460800;
189  break;
190 # endif
191  }
192 
193  return uiPortSpeed;
194 }
195 
196 void
197 uart_close (const serial_port sp)
198 {
199  if (((serial_port_unix *) sp)->fd >= 0) {
200  tcsetattr (((serial_port_unix *) sp)->fd, TCSANOW, &((serial_port_unix *) sp)->tiOld);
201  close (((serial_port_unix *) sp)->fd);
202  }
203  free (sp);
204 }
205 
211 int
212 uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
213 {
214  int res;
215  int byteCount;
216  fd_set rfds;
217 
218  int iExpectedByteCount = (int)*pszRx;
219  DBG ("iExpectedByteCount == %d", iExpectedByteCount);
220  struct timeval tvTimeout = {
221  .tv_sec = 0,
222  .tv_usec = uiTimeoutStatic + (uiTimeoutPerByte * iExpectedByteCount),
223  };
224  struct timeval tv = tvTimeout;
225 
226  // Reset the output count
227  *pszRx = 0;
228  do {
229  // Reset file descriptor
230  FD_ZERO (&rfds);
231  FD_SET (((serial_port_unix *) sp)->fd, &rfds);
232  res = select (((serial_port_unix *) sp)->fd + 1, &rfds, NULL, NULL, &tv);
233 
234  // Read error
235  if (res < 0) {
236  DBG ("%s", "RX error.");
237  return DEIO;
238  }
239  // Read time-out
240  if (res == 0) {
241  if (*pszRx == 0) {
242  // Error, we received no data
243  // DBG ("RX time-out (%lu µs), buffer empty.", tvTimeout.tv_usec);
244  return DETIMEOUT;
245  } else {
246  // We received some data, but nothing more is available
247  return 0;
248  }
249  }
250  // Retrieve the count of the incoming bytes
251  res = ioctl (((serial_port_unix *) sp)->fd, FIONREAD, &byteCount);
252  if (res < 0) {
253  return DEIO;
254  }
255  // There is something available, read the data
256  res = read (((serial_port_unix *) sp)->fd, pbtRx + (*pszRx), MIN(byteCount, iExpectedByteCount));
257  iExpectedByteCount -= MIN (byteCount, iExpectedByteCount);
258 
259  // Stop if the OS has some troubles reading the data
260  if (res <= 0) {
261  return DEIO;
262  }
263 
264  *pszRx += res;
265  // Reload timeout with a low value to prevent from waiting too long on slow devices (16x is enought to took at least 1 byte)
266  tv.tv_usec = uiTimeoutPerByte * MIN( iExpectedByteCount, 16 );
267  // DBG("Timeout reloaded at: %d µs", tv.tv_usec);
268  } while (byteCount && (iExpectedByteCount > 0));
269  DBG ("byteCount == %d, iExpectedByteCount == %d", byteCount, iExpectedByteCount);
270  return 0;
271 }
272 
278 int
279 uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
280 {
281  int32_t res;
282  size_t szPos = 0;
283  fd_set rfds;
284  struct timeval tvTimeout = {
285  .tv_sec = 0,
286  .tv_usec = uiTimeoutStatic + (uiTimeoutPerByte * szTx),
287  };
288  struct timeval tv = tvTimeout;
289 
290  while (szPos < szTx) {
291  // Reset file descriptor
292  FD_ZERO (&rfds);
293  FD_SET (((serial_port_unix *) sp)->fd, &rfds);
294  res = select (((serial_port_unix *) sp)->fd + 1, NULL, &rfds, NULL, &tv);
295 
296  // Write error
297  if (res < 0) {
298  DBG ("%s", "TX error.");
299  return DEIO;
300  }
301  // Write time-out
302  if (res == 0) {
303  DBG ("%s", "TX time-out.");
304  return DETIMEOUT;
305  }
306  // Send away the bytes
307  res = write (((serial_port_unix *) sp)->fd, pbtTx + szPos, szTx - szPos);
308 
309  // Stop if the OS has some troubles sending the data
310  if (res <= 0) {
311  return DEIO;
312  }
313 
314  szPos += res;
315 
316  // Reload timeout with a low value to prevent from waiting too long on slow devices (16x is enought to took at least 1 byte)
317  tv.tv_usec = uiTimeoutStatic + uiTimeoutPerByte * MIN( szTx - szPos, 16 );
318  }
319  return 0;
320 }
321