Merge pull request #600 from marshmellow42/master
[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
12#include <pthread.h>
13
14#include "comms.h"
15#include "uart.h"
16#include "ui.h"
17#include "common.h"
18#include "data.h"
19#include "util_posix.h"
20
21// Declare globals.
22
23// Serial port that we are communicating with the PM3 on.
818efbeb 24static serial_port sp;
f5ecd97b 25
26// If TRUE, then there is no active connection to the PM3, and we will drop commands sent.
61aaee35 27static bool offline;
f5ecd97b 28
29// Transmit buffer.
30// TODO: Use locks and execute this on the main thread, rather than the receiver
31// thread. Running on the main thread means we need to be careful in the
32// flasher, as it means SendCommand is no longer async, and can't be used as a
33// buffer for a pending command when the connection is re-established.
34static UsbCommand txcmd;
35volatile static bool txcmd_pending = false;
36
37// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be
38// processed by a command handler (WaitForResponse{,Timeout})
39static UsbCommand cmdBuffer[CMD_BUFFER_SIZE];
40
41// Points to the next empty position to write to
42static int cmd_head = 0;
43
44// Points to the position of the last unread command
45static int cmd_tail = 0;
46
47// to lock cmdBuffer operations from different threads
48static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER;
49
61aaee35 50// These wrappers are required because it is not possible to access a static
51// global variable outside of the context of a single file.
52
53void SetOffline(bool new_offline) {
54 offline = new_offline;
55}
56
57bool IsOffline() {
58 return offline;
59}
f5ecd97b 60
818efbeb 61bool OpenProxmark(char *portname, bool waitCOMPort, int timeout) {
62 if (!waitCOMPort) {
63 sp = uart_open(portname);
64 } else {
65 printf("Waiting for Proxmark to appear on %s ", portname);
66 fflush(stdout);
67 int openCount = 0;
68 do {
69 sp = uart_open(portname);
70 msleep(1000);
71 printf(".");
72 fflush(stdout);
73 } while(++openCount < timeout && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT));
74 printf("\n");
75 }
76
77 // check result of uart opening
78 if (sp == INVALID_SERIAL_PORT) {
79 printf("ERROR: invalid serial port\n");
80 return false;
81 } else if (sp == CLAIMED_SERIAL_PORT) {
82 printf("ERROR: serial port is claimed by another process\n");
83 return false;
84 } else {
85 return true;
86 }
87}
88
89void CloseProxmark(void) {
90 uart_close(sp);
91}
92
f5ecd97b 93void SendCommand(UsbCommand *c) {
61aaee35 94 #ifdef COMMS_DEBUG
95 printf("Sending %04x cmd\n", c->cmd);
f5ecd97b 96 #endif
97
98 if (offline) {
99 PrintAndLog("Sending bytes to proxmark failed - offline");
100 return;
101 }
102 /**
103 The while-loop below causes hangups at times, when the pm3 unit is unresponsive
104 or disconnected. The main console thread is alive, but comm thread just spins here.
105 Not good.../holiman
106 **/
107 while(txcmd_pending);
818efbeb 108
f5ecd97b 109 txcmd = *c;
110 txcmd_pending = true;
111}
112
113
114/**
115 * @brief This method should be called when sending a new command to the pm3. In case any old
116 * responses from previous commands are stored in the buffer, a call to this method should clear them.
117 * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which
118 * operation. Right now we'll just have to live with this.
119 */
120void clearCommandBuffer()
121{
122 //This is a very simple operation
123 pthread_mutex_lock(&cmdBufferMutex);
124 cmd_tail = cmd_head;
125 pthread_mutex_unlock(&cmdBufferMutex);
126}
127
128/**
129 * @brief storeCommand stores a USB command in a circular buffer
130 * @param UC
131 */
132void storeCommand(UsbCommand *command)
133{
134 pthread_mutex_lock(&cmdBufferMutex);
135 if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail)
136 {
137 // If these two are equal, we're about to overwrite in the
138 // circular buffer.
139 PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!");
140 }
141
142 // Store the command at the 'head' location
143 UsbCommand* destination = &cmdBuffer[cmd_head];
144 memcpy(destination, command, sizeof(UsbCommand));
145
146 cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap
147 pthread_mutex_unlock(&cmdBufferMutex);
148}
149
150
151/**
152 * @brief getCommand gets a command from an internal circular buffer.
153 * @param response location to write command
154 * @return 1 if response was returned, 0 if nothing has been received
155 */
156int getCommand(UsbCommand* response)
157{
158 pthread_mutex_lock(&cmdBufferMutex);
159 //If head == tail, there's nothing to read, or if we just got initialized
160 if (cmd_head == cmd_tail){
161 pthread_mutex_unlock(&cmdBufferMutex);
162 return 0;
163 }
164
165 //Pick out the next unread command
166 UsbCommand* last_unread = &cmdBuffer[cmd_tail];
167 memcpy(response, last_unread, sizeof(UsbCommand));
168 //Increment tail - this is a circular buffer, so modulo buffer size
169 cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE;
170
171 pthread_mutex_unlock(&cmdBufferMutex);
172 return 1;
173}
174
175
818efbeb 176static void UsbCommandReceived(UsbCommand *UC)
f5ecd97b 177{
178 switch(UC->cmd) {
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));
183 size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE);
184 memcpy(s,UC->d.asBytes,len);
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
194 case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: {
61aaee35 195 // FIXME: This does unsanitised copies into memory when we don't know
196 // the size of the buffer.
f5ecd97b 197 memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]);
198 return;
199 } break;
200
201 default:
818efbeb 202 storeCommand(UC);
f5ecd97b 203 break;
204 }
205
206}
207
208
209void
210#ifdef __has_attribute
211#if __has_attribute(force_align_arg_pointer)
212__attribute__((force_align_arg_pointer))
213#endif
214#endif
215*uart_receiver(void *targ) {
818efbeb 216 receiver_arg *conn = (receiver_arg*)targ;
f5ecd97b 217 size_t rxlen;
218 uint8_t rx[sizeof(UsbCommand)];
219 uint8_t *prx = rx;
220
818efbeb 221 while (conn->run) {
f5ecd97b 222 rxlen = 0;
223 if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) {
224 prx += rxlen;
225 if (prx-rx < sizeof(UsbCommand)) {
226 continue;
227 }
228 UsbCommandReceived((UsbCommand*)rx);
229 }
230 prx = rx;
231
232 if(txcmd_pending) {
233 if (!uart_send(sp, (uint8_t*) &txcmd, sizeof(UsbCommand))) {
234 PrintAndLog("Sending bytes to proxmark failed");
235 }
236 txcmd_pending = false;
237 }
238 }
239
240 pthread_exit(NULL);
241 return NULL;
242}
243
244
245/**
246 * Waits for a certain response type. This method waits for a maximum of
247 * ms_timeout milliseconds for a specified response command.
248 *@brief WaitForResponseTimeout
61aaee35 249 * @param cmd command to wait for, or CMD_UNKNOWN to take any command.
f5ecd97b 250 * @param response struct to copy received command into.
251 * @param ms_timeout
61aaee35 252 * @param show_warning
f5ecd97b 253 * @return true if command was returned, otherwise false
254 */
255bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) {
256
257 UsbCommand resp;
258
61aaee35 259 #ifdef COMMS_DEBUG
260 printf("Waiting for %04x cmd\n", cmd);
261 #endif
262
f5ecd97b 263 if (response == NULL) {
264 response = &resp;
265 }
266
267 uint64_t start_time = msclock();
268
269 // Wait until the command is received
270 while (true) {
271 while(getCommand(response)) {
61aaee35 272 if (cmd == CMD_UNKNOWN || response->cmd == cmd) {
f5ecd97b 273 return true;
274 }
275 }
276
277 if (msclock() - start_time > ms_timeout) {
278 break;
279 }
280
281 if (msclock() - start_time > 2000 && show_warning) {
61aaee35 282 // 2 seconds elapsed (but this doesn't mean the timeout was exceeded)
f5ecd97b 283 PrintAndLog("Waiting for a response from the proxmark...");
284 PrintAndLog("You can cancel this operation by pressing the pm3 button");
285 show_warning = false;
286 }
287 }
288 return false;
289}
290
291
292bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) {
293 return WaitForResponseTimeoutW(cmd, response, ms_timeout, true);
294}
295
296bool WaitForResponse(uint32_t cmd, UsbCommand* response) {
297 return WaitForResponseTimeoutW(cmd, response, -1, true);
298}
299
Impressum, Datenschutz