USB comms: part 3 towards @micolous PR#463
[proxmark3-svn] / client / comms.c
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.
24 static serial_port sp;
25
26 // If TRUE, then there is no active connection to the PM3, and we will drop commands sent.
27 static bool offline;
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.
34 static UsbCommand txcmd;
35 volatile 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})
39 static UsbCommand cmdBuffer[CMD_BUFFER_SIZE];
40
41 // Points to the next empty position to write to
42 static int cmd_head = 0;
43
44 // Points to the position of the last unread command
45 static int cmd_tail = 0;
46
47 // to lock cmdBuffer operations from different threads
48 static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER;
49
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
53 void SetOffline(bool new_offline) {
54 offline = new_offline;
55 }
56
57 bool IsOffline() {
58 return offline;
59 }
60
61 bool 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
89 void CloseProxmark(void) {
90 uart_close(sp);
91 }
92
93 void SendCommand(UsbCommand *c) {
94 #ifdef COMMS_DEBUG
95 printf("Sending %04x cmd\n", c->cmd);
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);
108
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 */
120 void 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 */
132 void 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 */
156 int 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
176 static void UsbCommandReceived(UsbCommand *UC)
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: {
195 // FIXME: This does unsanitised copies into memory when we don't know
196 // the size of the buffer.
197 memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]);
198 return;
199 } break;
200
201 default:
202 storeCommand(UC);
203 break;
204 }
205
206 }
207
208
209 void
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) {
216 receiver_arg *conn = (receiver_arg*)targ;
217 size_t rxlen;
218 uint8_t rx[sizeof(UsbCommand)];
219 uint8_t *prx = rx;
220
221 while (conn->run) {
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
249 * @param cmd command to wait for, or CMD_UNKNOWN to take any command.
250 * @param response struct to copy received command into.
251 * @param ms_timeout
252 * @param show_warning
253 * @return true if command was returned, otherwise false
254 */
255 bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) {
256
257 UsbCommand resp;
258
259 #ifdef COMMS_DEBUG
260 printf("Waiting for %04x cmd\n", cmd);
261 #endif
262
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)) {
272 if (cmd == CMD_UNKNOWN || response->cmd == cmd) {
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) {
282 // 2 seconds elapsed (but this doesn't mean the timeout was exceeded)
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
292 bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) {
293 return WaitForResponseTimeoutW(cmd, response, ms_timeout, true);
294 }
295
296 bool WaitForResponse(uint32_t cmd, UsbCommand* response) {
297 return WaitForResponseTimeoutW(cmd, response, -1, true);
298 }
299
Impressum, Datenschutz