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