cleaning up uart_posix.c
[proxmark3-svn] / uart / uart_posix.c
CommitLineData
fd667521 1/*\r
2 * Generic uart / rs232/ serial port library\r
3 *\r
4 * Copyright (c) 2013, Roel Verdult\r
5 * Copyright (c) 2018 Google\r
6 * All rights reserved.\r
7 *\r
8 * Redistribution and use in source and binary forms, with or without\r
9 * modification, are permitted provided that the following conditions are met:\r
10 * 1. Redistributions of source code must retain the above copyright\r
11 * notice, this list of conditions and the following disclaimer.\r
12 * 2. Redistributions in binary form must reproduce the above copyright\r
13 * notice, this list of conditions and the following disclaimer in the\r
14 * documentation and/or other materials provided with the distribution.\r
15 * 3. Neither the name of the copyright holders nor the\r
16 * names of its contributors may be used to endorse or promote products\r
17 * derived from this software without specific prior written permission.\r
18 *\r
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY\r
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
29 *\r
30 * @file uart_posix.c\r
31 *\r
32 * This version of the library has functionality removed which was not used by\r
33 * proxmark3 project.\r
34 */\r
35\r
36// Test if we are dealing with posix operating systems\r
37#ifndef _WIN32\r
38#define _DEFAULT_SOURCE\r
39\r
40#include "uart.h"\r
41\r
42#include <stdlib.h>\r
43#include <stdio.h>\r
44#include <string.h>\r
45#include <stdbool.h>\r
46#include <termios.h>\r
47#include <sys/ioctl.h>\r
48#include <unistd.h>\r
49#include <fcntl.h>\r
50#include <sys/types.h>\r
51#include <sys/stat.h>\r
52#include <sys/socket.h>\r
53#include <netinet/tcp.h>\r
54#include <netdb.h>\r
55\r
56// Fix missing definition on OS X.\r
57// Taken from https://github.com/unbit/uwsgi/commit/b608eb1772641d525bfde268fe9d6d8d0d5efde7\r
58#ifndef SOL_TCP\r
59#define SOL_TCP IPPROTO_TCP\r
60#endif\r
61\r
62typedef struct termios term_info;\r
63typedef struct {\r
64 int fd; // Serial port file descriptor\r
65 term_info tiOld; // Terminal info before using the port\r
66 term_info tiNew; // Terminal info during the transaction\r
67} serial_port_unix;\r
68\r
69// Set time-out on 30 miliseconds\r
70static struct timeval timeout = {\r
71 .tv_sec = 0, // 0 second\r
72 .tv_usec = 30000 // 30000 micro seconds\r
73};\r
74\r
75\r
76void uart_close(const serial_port sp) {\r
77 serial_port_unix* spu = (serial_port_unix*)sp;\r
78 tcflush(spu->fd,TCIOFLUSH);\r
79 tcsetattr(spu->fd,TCSANOW,&(spu->tiOld));\r
80 struct flock fl;\r
81 fl.l_type = F_UNLCK;\r
82 fl.l_whence = SEEK_SET;\r
83 fl.l_start = 0;\r
84 fl.l_len = 0;\r
85 fl.l_pid = getpid();\r
86 fcntl(spu->fd, F_SETLK, &fl);\r
87 close(spu->fd);\r
88 free(sp);\r
89}\r
90\r
91\r
92serial_port uart_open(const char* pcPortName) {\r
93\r
94 serial_port_unix* sp = malloc(sizeof(serial_port_unix));\r
95 if (sp == 0) return INVALID_SERIAL_PORT;\r
96\r
97 if (memcmp(pcPortName, "tcp:", 4) == 0) {\r
98 struct addrinfo *addr = NULL, *rp;\r
99 char *addrstr = strdup(pcPortName + 4);\r
100 if (addrstr == NULL) {\r
101 printf("Error: strdup\n");\r
102 return INVALID_SERIAL_PORT;\r
103 }\r
104 char *colon = strrchr(addrstr, ':');\r
105 char *portstr;\r
106\r
107 // Set time-out to 300 milliseconds only for TCP port\r
108 timeout.tv_usec = 300000;\r
109\r
110 if (colon) {\r
111 portstr = colon + 1;\r
112 *colon = '\0';\r
113 } else {\r
114 portstr = "7901";\r
115 }\r
116\r
117 struct addrinfo info;\r
118\r
119 memset(&info, 0, sizeof(info));\r
120\r
121 info.ai_socktype = SOCK_STREAM;\r
122\r
123 int s = getaddrinfo(addrstr, portstr, &info, &addr);\r
124 if (s != 0) {\r
125 printf("Error: getaddrinfo: %s\n", gai_strerror(s));\r
126 return INVALID_SERIAL_PORT;\r
127 }\r
128\r
129 int sfd;\r
130 for (rp = addr; rp != NULL; rp = rp->ai_next) {\r
131 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\r
132 if (sfd == -1)\r
133 continue;\r
134 if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)\r
135 break;\r
136 close(sfd);\r
137 }\r
138\r
139 if (rp == NULL) { /* No address succeeded */\r
140 printf("Error: Could not connect\n");\r
141 return INVALID_SERIAL_PORT;\r
142 }\r
143\r
144 freeaddrinfo(addr);\r
145 free(addrstr);\r
146\r
147 sp->fd = sfd;\r
148\r
149 int one = 1;\r
150 setsockopt(sp->fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));\r
151 return sp;\r
152 }\r
153\r
154 sp->fd = open(pcPortName, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);\r
155 if (sp->fd == -1) {\r
156 uart_close(sp);\r
157 return INVALID_SERIAL_PORT;\r
158 }\r
159\r
160 // Finally figured out a way to claim a serial port interface under unix\r
161 // We just try to set a (advisory) lock on the file descriptor\r
162 struct flock fl;\r
163 fl.l_type = F_WRLCK;\r
164 fl.l_whence = SEEK_SET;\r
165 fl.l_start = 0;\r
166 fl.l_len = 0;\r
167 fl.l_pid = getpid();\r
168\r
169 // Does the system allows us to place a lock on this file descriptor\r
170 if (fcntl(sp->fd, F_SETLK, &fl) == -1) {\r
171 // A conflicting lock is held by another process\r
172 free(sp);\r
173 return CLAIMED_SERIAL_PORT;\r
174 }\r
175\r
176 // Try to retrieve the old (current) terminal info struct\r
177 if(tcgetattr(sp->fd,&sp->tiOld) == -1) {\r
178 uart_close(sp);\r
179 return INVALID_SERIAL_PORT;\r
180 }\r
181\r
182 // Duplicate the (old) terminal info struct\r
183 sp->tiNew = sp->tiOld;\r
184\r
185 // Configure the serial port\r
186 sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD;\r
187 sp->tiNew.c_iflag = IGNPAR;\r
188 sp->tiNew.c_oflag = 0;\r
189 sp->tiNew.c_lflag = 0;\r
190\r
191 // Block until n bytes are received\r
192 sp->tiNew.c_cc[VMIN] = 0;\r
193 // Block until a timer expires (n * 100 mSec.)\r
194 sp->tiNew.c_cc[VTIME] = 0;\r
195\r
196 // Try to set the new terminal info struct\r
197 if(tcsetattr(sp->fd,TCSANOW,&sp->tiNew) == -1) {\r
198 uart_close(sp);\r
199 return INVALID_SERIAL_PORT;\r
200 }\r
201\r
202 // Flush all lingering data that may exist\r
203 tcflush(sp->fd, TCIOFLUSH);\r
204\r
205 return sp;\r
206}\r
207\r
208\r
209bool uart_receive(const serial_port sp, uint8_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) {\r
210 int byteCount;\r
211 fd_set rfds;\r
212 struct timeval tv;\r
213\r
214 // Reset the output count\r
215 *pszRxLen = 0;\r
216\r
217 do {\r
218 // Reset file descriptor\r
219 FD_ZERO(&rfds);\r
220 FD_SET(((serial_port_unix*)sp)->fd,&rfds);\r
221 tv = timeout;\r
222 int res = select(((serial_port_unix*)sp)->fd+1, &rfds, NULL, NULL, &tv);\r
223\r
224 // Read error\r
225 if (res < 0) {\r
226 return false;\r
227 }\r
228\r
229 // Read time-out\r
230 if (res == 0) {\r
231 if (*pszRxLen == 0) {\r
232 // Error, we received no data\r
233 return false;\r
234 } else {\r
235 // We received some data, but nothing more is available\r
236 return true;\r
237 }\r
238 }\r
239\r
240 // Retrieve the count of the incoming bytes\r
241 res = ioctl(((serial_port_unix*)sp)->fd, FIONREAD, &byteCount);\r
242 if (res < 0) return false;\r
243\r
244 // Cap the number of bytes, so we don't overrun the buffer\r
245 if (pszMaxRxLen - (*pszRxLen) < byteCount) {\r
246 byteCount = pszMaxRxLen - (*pszRxLen);\r
247 }\r
248\r
249 // There is something available, read the data\r
250 res = read(((serial_port_unix*)sp)->fd, pbtRx+(*pszRxLen), byteCount);\r
251\r
252 // Stop if the OS has some troubles reading the data\r
253 if (res <= 0) return false;\r
254\r
255 *pszRxLen += res;\r
256\r
257 if (*pszRxLen == pszMaxRxLen) {\r
258 // We have all the data we wanted.\r
259 return true;\r
260 }\r
261\r
262 } while (byteCount);\r
263\r
264 return true;\r
265}\r
266\r
267\r
268bool uart_send(const serial_port sp, const uint8_t* pbtTx, const size_t szTxLen) {\r
269 size_t szPos = 0;\r
270 fd_set rfds;\r
271 struct timeval tv;\r
272\r
273 while (szPos < szTxLen) {\r
274 // Reset file descriptor\r
275 FD_ZERO(&rfds);\r
276 FD_SET(((serial_port_unix*)sp)->fd,&rfds);\r
277 tv = timeout;\r
278 int res = select(((serial_port_unix*)sp)->fd+1, NULL, &rfds, NULL, &tv);\r
279\r
280 // Write error\r
281 if (res < 0) {\r
282 return false;\r
283 }\r
284\r
285 // Write time-out\r
286 if (res == 0) {\r
287 return false;\r
288 }\r
289\r
290 // Send away the bytes\r
291 res = write(((serial_port_unix*)sp)->fd, pbtTx+szPos, szTxLen-szPos);\r
292\r
293 // Stop if the OS has some troubles sending the data\r
294 if (res <= 0) return false;\r
295\r
296 szPos += res;\r
297 }\r
298 return true;\r
299}\r
300\r
301\r
302bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) {\r
303 const serial_port_unix* spu = (serial_port_unix*)sp;\r
304 speed_t stPortSpeed;\r
305 switch (uiPortSpeed) {\r
306 case 0: stPortSpeed = B0; break;\r
307 case 50: stPortSpeed = B50; break;\r
308 case 75: stPortSpeed = B75; break;\r
309 case 110: stPortSpeed = B110; break;\r
310 case 134: stPortSpeed = B134; break;\r
311 case 150: stPortSpeed = B150; break;\r
312 case 300: stPortSpeed = B300; break;\r
313 case 600: stPortSpeed = B600; break;\r
314 case 1200: stPortSpeed = B1200; break;\r
315 case 1800: stPortSpeed = B1800; break;\r
316 case 2400: stPortSpeed = B2400; break;\r
317 case 4800: stPortSpeed = B4800; break;\r
318 case 9600: stPortSpeed = B9600; break;\r
319 case 19200: stPortSpeed = B19200; break;\r
320 case 38400: stPortSpeed = B38400; break;\r
321# ifdef B57600\r
322 case 57600: stPortSpeed = B57600; break;\r
323# endif\r
324# ifdef B115200\r
325 case 115200: stPortSpeed = B115200; break;\r
326# endif\r
327# ifdef B230400\r
328 case 230400: stPortSpeed = B230400; break;\r
329# endif\r
330# ifdef B460800\r
331 case 460800: stPortSpeed = B460800; break;\r
332# endif\r
333# ifdef B921600\r
334 case 921600: stPortSpeed = B921600; break;\r
335# endif\r
336 default: return false;\r
337 };\r
338 struct termios ti;\r
339 if (tcgetattr(spu->fd, &ti) == -1) return false;\r
340 // Set port speed (Input and Output)\r
341 cfsetispeed(&ti,stPortSpeed);\r
342 cfsetospeed(&ti,stPortSpeed);\r
343 return (tcsetattr(spu->fd, TCSANOW, &ti) != -1);\r
344}\r
345\r
346uint32_t uart_get_speed(const serial_port sp) {\r
347 struct termios ti;\r
348 uint32_t uiPortSpeed;\r
349 const serial_port_unix* spu = (serial_port_unix*)sp;\r
350 if (tcgetattr(spu->fd, &ti) == -1) return 0;\r
351 // Set port speed (Input)\r
352 speed_t stPortSpeed = cfgetispeed(&ti);\r
353 switch (stPortSpeed) {\r
354 case B0: uiPortSpeed = 0; break;\r
355 case B50: uiPortSpeed = 50; break;\r
356 case B75: uiPortSpeed = 75; break;\r
357 case B110: uiPortSpeed = 110; break;\r
358 case B134: uiPortSpeed = 134; break;\r
359 case B150: uiPortSpeed = 150; break;\r
360 case B300: uiPortSpeed = 300; break;\r
361 case B600: uiPortSpeed = 600; break;\r
362 case B1200: uiPortSpeed = 1200; break;\r
363 case B1800: uiPortSpeed = 1800; break;\r
364 case B2400: uiPortSpeed = 2400; break;\r
365 case B4800: uiPortSpeed = 4800; break;\r
366 case B9600: uiPortSpeed = 9600; break;\r
367 case B19200: uiPortSpeed = 19200; break;\r
368 case B38400: uiPortSpeed = 38400; break;\r
369# ifdef B57600\r
370 case B57600: uiPortSpeed = 57600; break;\r
371# endif\r
372# ifdef B115200\r
373 case B115200: uiPortSpeed = 115200; break;\r
374# endif\r
375# ifdef B230400\r
376 case B230400: uiPortSpeed = 230400; break;\r
377# endif\r
378# ifdef B460800\r
379 case B460800: uiPortSpeed = 460800; break;\r
380# endif\r
381# ifdef B921600\r
382 case B921600: uiPortSpeed = 921600; break;\r
383# endif\r
384 default: return 0;\r
385 };\r
386 return uiPortSpeed;\r
387}\r
388\r
389#endif\r
Impressum, Datenschutz