5bcc76c4 |
1 | /* |
2 | * Proxmark3 generic uart / rs232/ serial port library |
3 | * |
4 | * Copyright (c) 2012, Roel Verdult |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. Neither the name of the copyright holders nor the |
15 | * names of its contributors may be used to endorse or promote products |
16 | * derived from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY |
19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
21 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | * |
29 | * @file uart.c |
30 | * @brief |
31 | * |
32 | * Partly based on uart-code written by Teunis van Beelen, available: |
33 | * http://www.teuniz.net/RS-232/index.html |
34 | * |
35 | */ |
36 | |
37 | #include "uart.h" |
38 | #include "messages.h" |
39 | |
40 | // Test if we are dealing with unix operating systems |
41 | #ifndef _WIN32 |
42 | |
43 | #include <termios.h> |
44 | typedef struct termios term_info; |
45 | typedef struct { |
46 | int fd; // Serial port file descriptor |
47 | term_info tiOld; // Terminal info before using the port |
48 | term_info tiNew; // Terminal info during the transaction |
49 | } serial_port_unix; |
50 | |
51 | // Set time-out on 30 miliseconds |
52 | const struct timeval timeout = { |
53 | .tv_sec = 0, // 0 second |
54 | .tv_usec = 30000 // 30000 micro seconds |
55 | }; |
56 | |
57 | // Work-around to claim uart interface using the c_iflag (software input processing) from the termios struct |
58 | #define CCLAIMED 0x80000000 |
59 | |
60 | serial_port uart_open(const char* pcPortName) |
61 | { |
62 | serial_port_unix* sp = malloc(sizeof(serial_port_unix)); |
63 | |
64 | if (sp == 0) return INVALID_SERIAL_PORT; |
65 | |
66 | sp->fd = open(pcPortName, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); |
67 | if(sp->fd == -1) |
68 | { |
69 | uart_close(sp); |
70 | return INVALID_SERIAL_PORT; |
71 | } |
72 | |
73 | if(tcgetattr(sp->fd,&sp->tiOld) == -1) |
74 | { |
75 | uart_close(sp); |
76 | return INVALID_SERIAL_PORT; |
77 | } |
78 | |
79 | // Make sure the port is not claimed already |
80 | if (sp->tiOld.c_iflag & CCLAIMED) |
81 | { |
82 | uart_close(sp); |
83 | return CLAIMED_SERIAL_PORT; |
84 | } |
85 | |
86 | // Copy the old terminal info struct |
87 | sp->tiNew = sp->tiOld; |
88 | |
89 | sp->tiNew.c_cflag = CS8 | CLOCAL | CREAD; |
90 | sp->tiNew.c_iflag = CCLAIMED | IGNPAR; |
91 | sp->tiNew.c_oflag = 0; |
92 | sp->tiNew.c_lflag = 0; |
93 | |
94 | sp->tiNew.c_cc[VMIN] = 0; // block until n bytes are received |
95 | sp->tiNew.c_cc[VTIME] = 0; // block until a timer expires (n * 100 mSec.) |
96 | |
97 | if(tcsetattr(sp->fd,TCSANOW,&sp->tiNew) == -1) |
98 | { |
99 | uart_close(sp); |
100 | return INVALID_SERIAL_PORT; |
101 | } |
102 | |
103 | tcflush(sp->fd, TCIFLUSH); |
104 | return sp; |
105 | } |
106 | |
107 | void uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) |
108 | { |
109 | DBG("Serial port speed requested to be set to %d bauds.", uiPortSpeed); |
110 | // Set port speed (Input and Output) |
111 | speed_t stPortSpeed = B9600; |
112 | switch(uiPortSpeed) { |
113 | case 9600: stPortSpeed = B9600; |
114 | break; |
115 | case 19200: stPortSpeed = B19200; |
116 | break; |
117 | case 38400: stPortSpeed = B38400; |
118 | break; |
119 | case 57600: stPortSpeed = B57600; |
120 | break; |
121 | case 115200: stPortSpeed = B115200; |
122 | break; |
123 | case 230400: stPortSpeed = B230400; |
124 | break; |
125 | #ifdef B460800 |
126 | case 460800: stPortSpeed = B460800; |
127 | break; |
128 | #endif |
129 | default: |
130 | #ifdef B460800 |
131 | ERR("Unable to set serial port speed to %d bauds. Speed value must be one of these constants: 9600 (default), 19200, 38400, 57600, 115200, 230400 or 460800.", uiPortSpeed); |
132 | #else |
133 | ERR("Unable to set serial port speed to %d bauds. Speed value must be one of these constants: 9600 (default), 19200, 38400, 57600, 115200 or 230400.", uiPortSpeed); |
134 | #endif |
135 | }; |
136 | const serial_port_unix* spu = (serial_port_unix*)sp; |
137 | cfsetispeed((struct termios*)&spu->tiNew, stPortSpeed); |
138 | cfsetospeed((struct termios*)&spu->tiNew, stPortSpeed); |
139 | if( tcsetattr(spu->fd, TCSADRAIN, &spu->tiNew) == -1) |
140 | { |
141 | ERR("Unable to apply new speed settings."); |
142 | } |
143 | } |
144 | |
145 | uint32_t uart_get_speed(const serial_port sp) |
146 | { |
147 | uint32_t uiPortSpeed = 0; |
148 | const serial_port_unix* spu = (serial_port_unix*)sp; |
149 | switch (cfgetispeed(&spu->tiNew)) |
150 | { |
151 | case B9600: uiPortSpeed = 9600; |
152 | break; |
153 | case B19200: uiPortSpeed = 19200; |
154 | break; |
155 | case B38400: uiPortSpeed = 38400; |
156 | break; |
157 | case B57600: uiPortSpeed = 57600; |
158 | break; |
159 | case B115200: uiPortSpeed = 115200; |
160 | break; |
161 | case B230400: uiPortSpeed = 230400; |
162 | break; |
163 | #ifdef B460800 |
164 | case B460800: uiPortSpeed = 460800; |
165 | break; |
166 | #endif |
167 | } |
168 | |
169 | return uiPortSpeed; |
170 | } |
171 | |
172 | void uart_close(const serial_port sp) |
173 | { |
174 | tcsetattr(((serial_port_unix*)sp)->fd,TCSANOW,&((serial_port_unix*)sp)->tiOld); |
175 | close(((serial_port_unix*)sp)->fd); |
176 | free(sp); |
177 | } |
178 | |
179 | bool uart_cts(const serial_port sp) |
180 | { |
181 | char status; |
182 | if (ioctl(((serial_port_unix*)sp)->fd,TIOCMGET,&status) < 0) return false; |
183 | return (status & TIOCM_CTS); |
184 | } |
185 | |
186 | bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t* pszRxLen) |
187 | { |
188 | int res; |
189 | int byteCount; |
190 | fd_set rfds; |
191 | struct timeval tv; |
192 | |
193 | // Reset the output count |
194 | *pszRxLen = 0; |
195 | |
196 | do { |
197 | // Reset file descriptor |
198 | FD_ZERO(&rfds); |
199 | FD_SET(((serial_port_unix*)sp)->fd,&rfds); |
200 | tv = timeout; |
201 | res = select(((serial_port_unix*)sp)->fd+1, &rfds, NULL, NULL, &tv); |
202 | |
203 | // Read error |
204 | if (res < 0) { |
205 | DBG("RX error."); |
206 | return false; |
207 | } |
208 | |
209 | // Read time-out |
210 | if (res == 0) { |
211 | if (*pszRxLen == 0) { |
212 | // Error, we received no data |
213 | DBG("RX time-out, buffer empty."); |
214 | return false; |
215 | } else { |
216 | // We received some data, but nothing more is available |
217 | return true; |
218 | } |
219 | } |
220 | |
221 | // Retrieve the count of the incoming bytes |
222 | res = ioctl(((serial_port_unix*)sp)->fd, FIONREAD, &byteCount); |
223 | if (res < 0) return false; |
224 | |
225 | // There is something available, read the data |
226 | res = read(((serial_port_unix*)sp)->fd,pbtRx+(*pszRxLen),byteCount); |
227 | |
228 | // Stop if the OS has some troubles reading the data |
229 | if (res <= 0) return false; |
230 | |
231 | *pszRxLen += res; |
232 | |
233 | } while (byteCount); |
234 | |
235 | return true; |
236 | } |
237 | |
238 | bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) |
239 | { |
240 | int32_t res; |
241 | size_t szPos = 0; |
242 | fd_set rfds; |
243 | struct timeval tv; |
244 | |
245 | while (szPos < szTxLen) |
246 | { |
247 | // Reset file descriptor |
248 | FD_ZERO(&rfds); |
249 | FD_SET(((serial_port_unix*)sp)->fd,&rfds); |
250 | tv = timeout; |
251 | res = select(((serial_port_unix*)sp)->fd+1, NULL, &rfds, NULL, &tv); |
252 | |
253 | // Write error |
254 | if (res < 0) { |
255 | DBG("TX error."); |
256 | return false; |
257 | } |
258 | |
259 | // Write time-out |
260 | if (res == 0) { |
261 | DBG("TX time-out."); |
262 | return false; |
263 | } |
264 | |
265 | // Send away the bytes |
266 | res = write(((serial_port_unix*)sp)->fd,pbtTx+szPos,szTxLen-szPos); |
267 | |
268 | // Stop if the OS has some troubles sending the data |
269 | if (res <= 0) return false; |
270 | |
271 | szPos += res; |
272 | } |
273 | return true; |
274 | } |
275 | |
276 | #else |
277 | // The windows serial port implementation |
278 | |
279 | typedef struct { |
280 | HANDLE hPort; // Serial port handle |
281 | DCB dcb; // Device control settings |
282 | COMMTIMEOUTS ct; // Serial port time-out configuration |
283 | } serial_port_windows; |
284 | |
a0bbdb76 |
285 | void upcase(char *p) { |
286 | while(*p != '\0') { |
287 | if(*p >= 97 && *p <= 122) { |
288 | *p -= 32; |
289 | } |
290 | ++p; |
291 | } |
292 | } |
293 | |
5bcc76c4 |
294 | serial_port uart_open(const char* pcPortName) |
295 | { |
296 | char acPortName[255]; |
297 | serial_port_windows* sp = malloc(sizeof(serial_port_windows)); |
298 | |
299 | // Copy the input "com?" to "\\.\COM?" format |
300 | sprintf(acPortName,"\\\\.\\%s",pcPortName); |
a0bbdb76 |
301 | upcase(acPortName); |
5bcc76c4 |
302 | |
303 | // Try to open the serial port |
304 | sp->hPort = CreateFileA(acPortName,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); |
305 | if (sp->hPort == INVALID_HANDLE_VALUE) |
306 | { |
307 | uart_close(sp); |
308 | return INVALID_SERIAL_PORT; |
309 | } |
310 | |
311 | // Prepare the device control |
312 | memset(&sp->dcb, 0, sizeof(DCB)); |
313 | sp->dcb.DCBlength = sizeof(DCB); |
314 | if(!BuildCommDCBA("baud=9600 data=8 parity=N stop=1",&sp->dcb)) |
315 | { |
316 | uart_close(sp); |
317 | return INVALID_SERIAL_PORT; |
318 | } |
319 | |
320 | // Update the active serial port |
321 | if(!SetCommState(sp->hPort,&sp->dcb)) |
322 | { |
323 | uart_close(sp); |
324 | return INVALID_SERIAL_PORT; |
325 | } |
326 | |
327 | sp->ct.ReadIntervalTimeout = 0; |
328 | sp->ct.ReadTotalTimeoutMultiplier = 0; |
329 | sp->ct.ReadTotalTimeoutConstant = 30; |
330 | sp->ct.WriteTotalTimeoutMultiplier = 0; |
331 | sp->ct.WriteTotalTimeoutConstant = 30; |
332 | |
333 | if(!SetCommTimeouts(sp->hPort,&sp->ct)) |
334 | { |
335 | uart_close(sp); |
336 | return INVALID_SERIAL_PORT; |
337 | } |
338 | |
339 | PurgeComm(sp->hPort, PURGE_RXABORT | PURGE_RXCLEAR); |
340 | |
341 | return sp; |
342 | } |
343 | |
344 | void uart_close(const serial_port sp) |
345 | { |
346 | CloseHandle(((serial_port_windows*)sp)->hPort); |
347 | free(sp); |
348 | } |
349 | |
350 | void uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) |
351 | { |
352 | serial_port_windows* spw; |
353 | |
354 | DBG("Serial port speed requested to be set to %d bauds.", uiPortSpeed); |
355 | // Set port speed (Input and Output) |
356 | switch(uiPortSpeed) { |
357 | case 9600: |
358 | case 19200: |
359 | case 38400: |
360 | case 57600: |
361 | case 115200: |
362 | case 230400: |
363 | case 460800: |
364 | break; |
365 | default: |
366 | ERR("Unable to set serial port speed to %d bauds. Speed value must be one of these constants: 9600 (default), 19200, 38400, 57600, 115200, 230400 or 460800.", uiPortSpeed); |
367 | }; |
368 | |
369 | spw = (serial_port_windows*)sp; |
370 | spw->dcb.BaudRate = uiPortSpeed; |
371 | if (!SetCommState(spw->hPort, &spw->dcb)) |
372 | { |
373 | ERR("Unable to apply new speed settings."); |
374 | } |
375 | } |
376 | |
377 | uint32_t uart_get_speed(const serial_port sp) |
378 | { |
379 | const serial_port_windows* spw = (serial_port_windows*)sp; |
380 | if (!GetCommState(spw->hPort, (serial_port)&spw->dcb)) |
381 | return spw->dcb.BaudRate; |
382 | |
383 | return 0; |
384 | } |
385 | |
386 | bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t* pszRxLen) |
387 | { |
388 | ReadFile(((serial_port_windows*)sp)->hPort,pbtRx,*pszRxLen,(LPDWORD)pszRxLen,NULL); |
389 | return (*pszRxLen != 0); |
390 | } |
391 | |
392 | bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) |
393 | { |
394 | DWORD dwTxLen = 0; |
395 | return WriteFile(((serial_port_windows*)sp)->hPort,pbtTx,szTxLen,&dwTxLen,NULL); |
396 | return (dwTxLen != 0); |
397 | } |
398 | |
399 | #endif |