]> git.zerfleddert.de Git - proxmark3-svn/blame - client/comms.c
replace msleep() by thread signalling in comms.c
[proxmark3-svn] / client / comms.c
CommitLineData
f5ecd97b 1//-----------------------------------------------------------------------------
2// Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
3// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
4//
5// This code is licensed to you under the terms of the GNU GPL, version 2 or,
6// at your option, any later version. See the LICENSE.txt file for the text of
7// the license.
8//-----------------------------------------------------------------------------
9// Code for communicating with the proxmark3 hardware.
10//-----------------------------------------------------------------------------
11
f5ecd97b 12#include "comms.h"
ad939de5 13
929b61c6 14#include <stdio.h>
ac37ee81 15#include <stddef.h>
16#include <string.h>
ad939de5 17#include <pthread.h>
b8ed9975 18#include <inttypes.h>
3458bb27 19#include <time.h>
b8ed9975 20
f5ecd97b 21#include "uart.h"
22#include "ui.h"
23#include "common.h"
2a537311 24#include "util_darwin.h"
f5ecd97b 25#include "util_posix.h"
26
f5ecd97b 27
28// Serial port that we are communicating with the PM3 on.
ad939de5 29static serial_port sp = NULL;
30static char *serial_port_name = NULL;
f5ecd97b 31
32// If TRUE, then there is no active connection to the PM3, and we will drop commands sent.
61aaee35 33static bool offline;
f5ecd97b 34
ad939de5 35typedef struct {
36 bool run; // If TRUE, continue running the uart_communication thread
ad939de5 37} communication_arg_t;
38
39static communication_arg_t conn;
40static pthread_t USB_communication_thread;
41
f5ecd97b 42// Transmit buffer.
ad939de5 43static UsbCommand txBuffer;
44static bool txBuffer_pending = false;
45static pthread_mutex_t txBufferMutex = PTHREAD_MUTEX_INITIALIZER;
46static pthread_cond_t txBufferSig = PTHREAD_COND_INITIALIZER;
f5ecd97b 47
48// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be
49// processed by a command handler (WaitForResponse{,Timeout})
b8ed9975 50#define CMD_BUFFER_SIZE 50
ad939de5 51static UsbCommand rxBuffer[CMD_BUFFER_SIZE];
f5ecd97b 52
53// Points to the next empty position to write to
54static int cmd_head = 0;
55
56// Points to the position of the last unread command
57static int cmd_tail = 0;
58
ad939de5 59// to lock rxBuffer operations from different threads
60static pthread_mutex_t rxBufferMutex = PTHREAD_MUTEX_INITIALIZER;
3458bb27 61static pthread_cond_t rxBufferSig = PTHREAD_COND_INITIALIZER;
f5ecd97b 62
61aaee35 63// These wrappers are required because it is not possible to access a static
64// global variable outside of the context of a single file.
65
66void SetOffline(bool new_offline) {
67 offline = new_offline;
68}
69
70bool IsOffline() {
71 return offline;
72}
f5ecd97b 73
74void SendCommand(UsbCommand *c) {
61aaee35 75 #ifdef COMMS_DEBUG
d2ca5dbf 76 printf("Sending %04" PRIx64 " cmd\n", c->cmd);
f5ecd97b 77 #endif
78
79 if (offline) {
80 PrintAndLog("Sending bytes to proxmark failed - offline");
81 return;
929b61c6 82 }
ad939de5 83
84 pthread_mutex_lock(&txBufferMutex);
f5ecd97b 85 /**
929b61c6 86 This causes hangups at times, when the pm3 unit is unresponsive or disconnected. The main console thread is alive,
ad939de5 87 but comm thread just spins here. Not good.../holiman
f5ecd97b 88 **/
ad939de5 89 while (txBuffer_pending) {
90 pthread_cond_wait(&txBufferSig, &txBufferMutex); // wait for communication thread to complete sending a previous commmand
91 }
92
93 txBuffer = *c;
94 txBuffer_pending = true;
95 pthread_cond_signal(&txBufferSig); // tell communication thread that a new command can be send
96
97 pthread_mutex_unlock(&txBufferMutex);
818efbeb 98
f5ecd97b 99}
100
101
102/**
103 * @brief This method should be called when sending a new command to the pm3. In case any old
104 * responses from previous commands are stored in the buffer, a call to this method should clear them.
105 * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which
106 * operation. Right now we'll just have to live with this.
107 */
d2ca5dbf 108void clearCommandBuffer() {
f5ecd97b 109 //This is a very simple operation
ad939de5 110 pthread_mutex_lock(&rxBufferMutex);
f5ecd97b 111 cmd_tail = cmd_head;
ad939de5 112 pthread_mutex_unlock(&rxBufferMutex);
f5ecd97b 113}
114
115/**
116 * @brief storeCommand stores a USB command in a circular buffer
117 * @param UC
118 */
d2ca5dbf 119static void storeCommand(UsbCommand *command) {
ad939de5 120 pthread_mutex_lock(&rxBufferMutex);
d2ca5dbf 121 if ((cmd_head + 1) % CMD_BUFFER_SIZE == cmd_tail) {
f5ecd97b 122 // If these two are equal, we're about to overwrite in the
123 // circular buffer.
124 PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!");
125 }
126
127 // Store the command at the 'head' location
ad939de5 128 UsbCommand* destination = &rxBuffer[cmd_head];
f5ecd97b 129 memcpy(destination, command, sizeof(UsbCommand));
130
d2ca5dbf 131 cmd_head = (cmd_head + 1) % CMD_BUFFER_SIZE; //increment head and wrap
3458bb27 132 pthread_cond_signal(&rxBufferSig); // tell main thread that a new command can be retreived
ad939de5 133 pthread_mutex_unlock(&rxBufferMutex);
f5ecd97b 134}
135
136
137/**
138 * @brief getCommand gets a command from an internal circular buffer.
139 * @param response location to write command
3458bb27 140 * @return 1 if response was returned, 0 if nothing has been received in time
f5ecd97b 141 */
3458bb27 142static int getCommand(UsbCommand* response, uint32_t ms_timeout) {
143
144 struct timespec end_time;
145 clock_gettime(CLOCK_REALTIME, &end_time);
146 end_time.tv_sec += ms_timeout / 1000;
147 end_time.tv_nsec += (ms_timeout % 1000) * 1000000;
148 if (end_time.tv_nsec > 1000000000) {
149 end_time.tv_nsec -= 1000000000;
150 end_time.tv_sec += 1;
151 }
ad939de5 152 pthread_mutex_lock(&rxBufferMutex);
3458bb27 153 int res = 0;
154 while (cmd_head == cmd_tail && !res) {
155 res = pthread_cond_timedwait(&rxBufferSig, &rxBufferMutex, &end_time);
156 }
157 if (res) { // timeout
ad939de5 158 pthread_mutex_unlock(&rxBufferMutex);
f5ecd97b 159 return 0;
160 }
161
d2ca5dbf 162 // Pick out the next unread command
ad939de5 163 UsbCommand* last_unread = &rxBuffer[cmd_tail];
f5ecd97b 164 memcpy(response, last_unread, sizeof(UsbCommand));
d2ca5dbf 165 // Increment tail - this is a circular buffer, so modulo buffer size
f5ecd97b 166 cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE;
167
ad939de5 168 pthread_mutex_unlock(&rxBufferMutex);
f5ecd97b 169 return 1;
170}
171
172
babca445 173//----------------------------------------------------------------------------------
174// Entry point into our code: called whenever we received a packet over USB.
175// Handle debug commands directly, store all other commands in circular buffer.
176//----------------------------------------------------------------------------------
d2ca5dbf 177static void UsbCommandReceived(UsbCommand *UC) {
178 switch (UC->cmd) {
f5ecd97b 179 // First check if we are handling a debug message
180 case CMD_DEBUG_PRINT_STRING: {
181 char s[USB_CMD_DATA_SIZE+1];
182 memset(s, 0x00, sizeof(s));
d2ca5dbf 183 size_t len = MIN(UC->arg[0], USB_CMD_DATA_SIZE);
184 memcpy(s, UC->d.asBytes,len);
f5ecd97b 185 PrintAndLog("#db# %s", s);
186 return;
187 } break;
188
189 case CMD_DEBUG_PRINT_INTEGERS: {
190 PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]);
191 return;
192 } break;
193
f5ecd97b 194 default:
929b61c6 195 storeCommand(UC);
f5ecd97b 196 break;
197 }
198
199}
200
201
b8ed9975 202static bool receive_from_serial(serial_port sp, uint8_t *rx_buf, size_t len, size_t *received_len) {
203 size_t bytes_read = 0;
204 *received_len = 0;
3458bb27 205 // we eventually need to call uart_receive several times because it may timeout in the middle of a transfer
b8ed9975 206 while (uart_receive(sp, rx_buf + *received_len, len - *received_len, &bytes_read) && bytes_read && *received_len < len) {
929b61c6 207 #ifdef COMMS_DEBUG
b8ed9975 208 if (bytes_read != len - *received_len) {
d2ca5dbf 209 printf("uart_receive() returned true but not enough bytes could be received. received: %zd, wanted to receive: %zd, already received before: %zd\n",
b8ed9975 210 bytes_read, len - *received_len, *received_len);
211 }
929b61c6 212 #endif
b8ed9975 213 *received_len += bytes_read;
214 bytes_read = 0;
215 }
216 return (*received_len == len);
217}
929b61c6 218
b8ed9975 219
ad939de5 220static void
f5ecd97b 221#ifdef __has_attribute
222#if __has_attribute(force_align_arg_pointer)
929b61c6 223__attribute__((force_align_arg_pointer))
f5ecd97b 224#endif
225#endif
ad939de5 226*uart_communication(void *targ) {
227 communication_arg_t *conn = (communication_arg_t*)targ;
b8ed9975 228 uint8_t rx[sizeof(UsbCommand)];
229 size_t rxlen = 0;
230 uint8_t *prx = rx;
231 UsbCommand *command = (UsbCommand*)rx;
232 UsbResponse *response = (UsbResponse*)rx;
f5ecd97b 233
2a537311
A
234#if defined(__MACH__) && defined(__APPLE__)
235 disableAppNap("Proxmark3 polling UART");
236#endif
237
818efbeb 238 while (conn->run) {
ad939de5 239 bool ACK_received = false;
b8ed9975 240 prx = rx;
241 size_t bytes_to_read = offsetof(UsbResponse, d); // the fixed part of a new style UsbResponse. Otherwise this will be cmd and arg[0] (64 bit each)
242 if (receive_from_serial(sp, prx, bytes_to_read, &rxlen)) {
f5ecd97b 243 prx += rxlen;
b8ed9975 244 if (response->cmd & CMD_VARIABLE_SIZE_FLAG) { // new style response with variable size
d2ca5dbf 245#ifdef COMMS_DEBUG
246 PrintAndLog("received new style response %04" PRIx16 ", datalen = %zd, arg[0] = %08" PRIx32 ", arg[1] = %08" PRIx32 ", arg[2] = %08" PRIx32,
247 response->cmd, response->datalen, response->arg[0], response->arg[1], response->arg[2]);
248#endif
b8ed9975 249 bytes_to_read = response->datalen;
250 if (receive_from_serial(sp, prx, bytes_to_read, &rxlen)) {
251 UsbCommand resp;
929b61c6 252 resp.cmd = response->cmd & ~CMD_VARIABLE_SIZE_FLAG; // remove the flag
b8ed9975 253 resp.arg[0] = response->arg[0];
254 resp.arg[1] = response->arg[1];
255 resp.arg[2] = response->arg[2];
256 memcpy(&resp.d.asBytes, &response->d.asBytes, response->datalen);
257 UsbCommandReceived(&resp);
258 if (resp.cmd == CMD_ACK) {
259 ACK_received = true;
260 }
261 }
262 } else { // old style response uses same data structure as commands. Fixed size.
d2ca5dbf 263#ifdef COMMS_DEBUG
264 PrintAndLog("received old style response %016" PRIx64 ", arg[0] = %016" PRIx64, command->cmd, command->arg[0]);
265#endif
b8ed9975 266 bytes_to_read = sizeof(UsbCommand) - bytes_to_read;
929b61c6 267 if (receive_from_serial(sp, prx, bytes_to_read, &rxlen)) {
b8ed9975 268 UsbCommandReceived(command);
269 if (command->cmd == CMD_ACK) {
270 ACK_received = true;
271 }
272 }
ad939de5 273 }
f5ecd97b 274 }
f5ecd97b 275
929b61c6 276 pthread_mutex_lock(&txBufferMutex);
277 // if we received an ACK the PM has done its job and waits for another command.
278 // We therefore can wait here as well until a new command is to be transmitted.
279 // The advantage is that the next command will be transmitted immediately without the need to wait for a receive timeout
280 if (ACK_received) {
281 while (!txBuffer_pending) {
282 pthread_cond_wait(&txBufferSig, &txBufferMutex);
ad939de5 283 }
284 }
929b61c6 285 if (txBuffer_pending) {
ad939de5 286 if (!uart_send(sp, (uint8_t*) &txBuffer, sizeof(UsbCommand))) {
f5ecd97b 287 PrintAndLog("Sending bytes to proxmark failed");
288 }
ad939de5 289 txBuffer_pending = false;
f5ecd97b 290 }
929b61c6 291 pthread_cond_signal(&txBufferSig); // tell main thread that txBuffer is empty
ad939de5 292 pthread_mutex_unlock(&txBufferMutex);
f5ecd97b 293 }
294
2a537311
A
295#if defined(__MACH__) && defined(__APPLE__)
296 enableAppNap();
297#endif
298
f5ecd97b 299 pthread_exit(NULL);
300 return NULL;
301}
302
303
babca445 304/**
305 * Data transfer from Proxmark to client. This method times out after
306 * ms_timeout milliseconds.
307 * @brief GetFromBigBuf
308 * @param dest Destination address for transfer
309 * @param bytes number of bytes to be transferred
310 * @param start_index offset into Proxmark3 BigBuf[]
311 * @param response struct to copy last command (CMD_ACK) into
312 * @param ms_timeout timeout in milliseconds
313 * @param show_warning display message after 2 seconds
314 * @return true if command was returned, otherwise false
315 */
d2ca5dbf 316bool GetFromBigBuf(uint8_t *dest, int bytes, int start_index, UsbCommand *response, size_t ms_timeout, bool show_warning) {
babca445 317
318 uint64_t start_time = msclock();
3458bb27 319 uint32_t poll_time = 100; // loop every 100ms
320
321 UsbCommand c = {CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K, {start_index, bytes, 0}};
322 SendCommand(&c);
babca445 323
324 UsbCommand resp;
929b61c6 325 if (response == NULL) {
babca445 326 response = &resp;
327 }
328
329 int bytes_completed = 0;
d2ca5dbf 330 while (true) {
3458bb27 331 if (msclock() - start_time > ms_timeout) {
332 break; // timeout
333 }
334 if (msclock() - start_time > 2000 && show_warning) {
335 // 2 seconds elapsed (but this doesn't mean the timeout was exceeded)
336 PrintAndLog("Waiting for a response from the proxmark...");
337 PrintAndLog("You can cancel this operation by pressing the pm3 button");
338 show_warning = false;
339 }
340 if (getCommand(response, poll_time)) {
babca445 341 if (response->cmd == CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {
342 int copy_bytes = MIN(bytes - bytes_completed, response->arg[1]);
343 memcpy(dest + response->arg[0], response->d.asBytes, copy_bytes);
344 bytes_completed += copy_bytes;
345 } else if (response->cmd == CMD_ACK) {
346 return true;
347 }
348 }
babca445 349 }
350
351 return false;
352}
353
929b61c6 354
d2ca5dbf 355bool GetFromFpgaRAM(uint8_t *dest, int bytes) {
fc52fbd4 356
357 uint64_t start_time = msclock();
3458bb27 358 uint32_t poll_time = 100; // loop every 100ms
359
360 UsbCommand c = {CMD_HF_PLOT, {0, 0, 0}};
361 SendCommand(&c);
fc52fbd4 362
363 UsbCommand response;
929b61c6 364
fc52fbd4 365 int bytes_completed = 0;
366 bool show_warning = true;
d2ca5dbf 367 while (true) {
3458bb27 368 if (msclock() - start_time > 2000 && show_warning) {
369 PrintAndLog("Waiting for a response from the proxmark...");
370 PrintAndLog("You can cancel this operation by pressing the pm3 button");
371 show_warning = false;
372 }
373 if (getCommand(&response, poll_time)) {
fc52fbd4 374 if (response.cmd == CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K) {
375 int copy_bytes = MIN(bytes - bytes_completed, response.arg[1]);
376 memcpy(dest + response.arg[0], response.d.asBytes, copy_bytes);
377 bytes_completed += copy_bytes;
378 } else if (response.cmd == CMD_ACK) {
379 return true;
380 }
381 }
fc52fbd4 382 }
383
384 return false;
385}
386
387
929b61c6 388bool OpenProxmark(void *port, bool wait_for_port, int timeout) {
ad939de5 389 char *portname = (char *)port;
390 if (!wait_for_port) {
391 sp = uart_open(portname);
392 } else {
393 printf("Waiting for Proxmark to appear on %s ", portname);
394 fflush(stdout);
395 int openCount = 0;
396 do {
397 sp = uart_open(portname);
398 msleep(1000);
399 printf(".");
400 fflush(stdout);
d2ca5dbf 401 } while (++openCount < timeout && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT));
ad939de5 402 printf("\n");
403 }
404
405 // check result of uart opening
406 if (sp == INVALID_SERIAL_PORT) {
407 printf("ERROR: invalid serial port\n");
408 sp = NULL;
409 serial_port_name = NULL;
410 return false;
411 } else if (sp == CLAIMED_SERIAL_PORT) {
412 printf("ERROR: serial port is claimed by another process\n");
413 sp = NULL;
414 serial_port_name = NULL;
415 return false;
416 } else {
417 // start the USB communication thread
418 serial_port_name = portname;
419 conn.run = true;
ad939de5 420 pthread_create(&USB_communication_thread, NULL, &uart_communication, &conn);
421 return true;
422 }
423}
424
425
426void CloseProxmark(void) {
427 conn.run = false;
7b2cd970
MF
428
429#ifdef __BIONIC__
430 // In Android O and later, if an invalid pthread_t is passed to pthread_join, it calls fatal().
431 // https://github.com/aosp-mirror/platform_bionic/blob/ed16b344e75f422fb36fbfd91fb30de339475880/libc/bionic/pthread_internal.cpp#L116-L128
432 //
433 // In Bionic libc, pthread_t is an integer.
434
435 if (USB_communication_thread != 0) {
436 pthread_join(USB_communication_thread, NULL);
437 }
438#else
439 // pthread_t is a struct on other libc, treat as an opaque memory reference
ad939de5 440 pthread_join(USB_communication_thread, NULL);
7b2cd970 441#endif
2bb7f7e3
MF
442
443 if (sp) {
444 uart_close(sp);
445 }
446
2bb7f7e3
MF
447 // Clean up our state
448 sp = NULL;
449 serial_port_name = NULL;
eed83b91 450#ifdef __BIONIC__
7b2cd970 451 memset(&USB_communication_thread, 0, sizeof(pthread_t));
eed83b91 452#endif
ad939de5 453}
454
babca445 455
f5ecd97b 456/**
457 * Waits for a certain response type. This method waits for a maximum of
458 * ms_timeout milliseconds for a specified response command.
459 *@brief WaitForResponseTimeout
61aaee35 460 * @param cmd command to wait for, or CMD_UNKNOWN to take any command.
f5ecd97b 461 * @param response struct to copy received command into.
462 * @param ms_timeout
babca445 463 * @param show_warning display message after 2 seconds
f5ecd97b 464 * @return true if command was returned, otherwise false
465 */
466bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) {
467
468 UsbCommand resp;
469
61aaee35 470 #ifdef COMMS_DEBUG
471 printf("Waiting for %04x cmd\n", cmd);
472 #endif
473
3458bb27 474 uint64_t start_time = msclock();
475 uint64_t end_time = start_time + ms_timeout;
476
f5ecd97b 477 if (response == NULL) {
478 response = &resp;
479 }
480
f5ecd97b 481 // Wait until the command is received
482 while (true) {
3458bb27 483 int32_t remaining_time = end_time - msclock();
484 if (remaining_time <= 0) {
485 break; // timeout
f5ecd97b 486 }
f5ecd97b 487 if (msclock() - start_time > 2000 && show_warning) {
61aaee35 488 // 2 seconds elapsed (but this doesn't mean the timeout was exceeded)
f5ecd97b 489 PrintAndLog("Waiting for a response from the proxmark...");
490 PrintAndLog("You can cancel this operation by pressing the pm3 button");
491 show_warning = false;
492 }
3458bb27 493 if (getCommand(response, remaining_time)) {
494 if (cmd == CMD_UNKNOWN || response->cmd == cmd) {
495 return true;
496 }
497 }
f5ecd97b 498 }
499 return false;
500}
501
502
503bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) {
504 return WaitForResponseTimeoutW(cmd, response, ms_timeout, true);
505}
506
507bool WaitForResponse(uint32_t cmd, UsbCommand* response) {
508 return WaitForResponseTimeoutW(cmd, response, -1, true);
509}
510
Impressum, Datenschutz