IEEE 802.15.4 subsystem
Sign in or create your account | Project List | Help
IEEE 802.15.4 subsystem Git Source Tree
Root/
Source at commit 172208e4511f1a6ad9b80229871329554fd4995a created 10 years 9 months ago. By Werner Almesberger, tools/atrf-txrx/atrf-txrx.c: use -x also on reception, to output message in hex | |
---|---|
1 | /* |
2 | * atrf-txrx/atrf-txrx.c - ben-wpan AT86RF230 TX/RX |
3 | * |
4 | * Written 2010-2011, 2013 by Werner Almesberger |
5 | * Copyright 2010-2011, 2013 Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #include <stdint.h> |
15 | #include <stdlib.h> |
16 | #include <stdio.h> |
17 | #include <unistd.h> |
18 | #include <string.h> |
19 | #include <math.h> |
20 | #include <signal.h> |
21 | #include <sys/wait.h> |
22 | #include <sys/time.h> |
23 | |
24 | #include "at86rf230.h" |
25 | #include "atrf.h" |
26 | #include "misctxrx.h" |
27 | #include "cwtest.h" |
28 | |
29 | #include "pcap.h" |
30 | |
31 | |
32 | /* |
33 | * According to IEEE 802.15.4-2003 section E.2.6, channel 15 is the only |
34 | * channel that falls into the 802.11 guard bands in North America an Europe. |
35 | */ |
36 | |
37 | #define DEFAULT_CHANNEL 15 /* channel 15, 2425 MHz */ |
38 | |
39 | #define DEFAULT_TRIM 8 /* trim range is 0-15, see also ECN0002 */ |
40 | |
41 | |
42 | /* |
43 | * Transmit power, dBm. IEEE 802.15.4-2003 section E.3.1.3 specifies a transmit |
44 | * power of 0 dBm for IEEE 802.15.4. We assume an antenna gain of 3 dB or |
45 | * better. |
46 | */ |
47 | |
48 | #define DEFAULT_POWER -3.2 /* transmit power, dBm */ |
49 | |
50 | |
51 | struct ping { |
52 | uint32_t seq; /* sequence number from originator, > 0 */ |
53 | uint32_t ack; /* last sequence number received, 0 if none */ |
54 | uint8_t pad[117]; /* pad to 127 bytes */ |
55 | uint16_t crc; |
56 | } __attribute__((__packed__)); |
57 | |
58 | enum rx_res { |
59 | rx_exit, |
60 | rx_good, |
61 | rx_bad, |
62 | rx_timeout, |
63 | }; |
64 | |
65 | enum mode { |
66 | mode_msg, |
67 | mode_hmac, |
68 | mode_per, |
69 | mode_ping, |
70 | mode_rtt, |
71 | mode_rtt_hmac, |
72 | mode_cont_tx, |
73 | }; |
74 | |
75 | |
76 | static volatile int run = 1; |
77 | |
78 | |
79 | /* ----- Helper functions -------------------------------------------------- */ |
80 | |
81 | |
82 | /* |
83 | * mhz: 0 disable CLKM |
84 | * >0 output specified clock |
85 | */ |
86 | |
87 | static struct atrf_dsc *init_txrx(const char *driver, int trim, unsigned mhz) |
88 | { |
89 | struct atrf_dsc *dsc; |
90 | |
91 | dsc = atrf_open(driver); |
92 | if (!dsc) |
93 | exit(1); |
94 | |
95 | atrf_reset_rf(dsc); |
96 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
97 | |
98 | #if 1 // def HAVE_USB /* @@@ yeah, ugly */ |
99 | atrf_reg_write(dsc, REG_XOSC_CTRL, |
100 | (XTAL_MODE_INT << XTAL_MODE_SHIFT) | trim); |
101 | #else |
102 | atrf_reg_write(dsc, REG_XOSC_CTRL, XTAL_MODE_EXT << XTAL_MODE_SHIFT); |
103 | #endif |
104 | |
105 | if (!atrf_set_clkm(dsc, mhz)) |
106 | if (mhz) { |
107 | atrf_close(dsc); |
108 | exit(1); |
109 | } |
110 | |
111 | /* We want to see all interrupts, not only the ones we're expecting. */ |
112 | atrf_reg_write(dsc, REG_IRQ_MASK, 0xff); |
113 | |
114 | flush_interrupts(dsc); |
115 | if (atrf_identify(dsc) == atrf_at86rf231) |
116 | wait_for_interrupt(dsc, IRQ_CCA_ED_DONE, IRQ_CCA_ED_DONE, 1); |
117 | /* according to table 7-1, 37 us max */ |
118 | |
119 | return dsc; |
120 | } |
121 | |
122 | |
123 | static void set_channel(struct atrf_dsc *dsc, int channel) |
124 | { |
125 | atrf_reg_write(dsc, REG_PHY_CC_CCA, (1 << CCA_MODE_SHIFT) | channel); |
126 | } |
127 | |
128 | |
129 | static void set_rate(struct atrf_dsc *dsc, uint8_t rate) |
130 | { |
131 | if (!rate) |
132 | return; |
133 | switch (atrf_identify(dsc)) { |
134 | case atrf_at86rf230: |
135 | fprintf(stderr, "AT86RF230 only supports 250 kbps\n"); |
136 | break; |
137 | case atrf_at86rf231: |
138 | atrf_reg_write(dsc, REG_TRX_CTRL_2, rate); |
139 | break; |
140 | default: |
141 | abort(); |
142 | } |
143 | } |
144 | |
145 | |
146 | /* ----- Message transmit/receive ------------------------------------------ */ |
147 | |
148 | |
149 | static void receive_message(struct atrf_dsc *dsc, int hex) |
150 | { |
151 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
152 | int n, ok, i; |
153 | uint8_t ed, lqi; |
154 | |
155 | fprintf(stderr, "Ready.\n"); |
156 | wait_for_interrupt(dsc, IRQ_TRX_END, |
157 | IRQ_TRX_END | IRQ_RX_START | IRQ_AMI, 0); |
158 | if (!run) |
159 | return; |
160 | |
161 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
162 | if (n < 0) |
163 | exit(1); |
164 | if (n < 3) { |
165 | fprintf(stderr, "%d bytes received\n", n); |
166 | exit(1); |
167 | } |
168 | ed = atrf_reg_read(dsc, REG_PHY_ED_LEVEL); |
169 | ok = !!(atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID); |
170 | lqi = buf[n-1]; |
171 | fprintf(stderr, "%d bytes payload, CRC %s, LQI %u, ED %d dBm\n", |
172 | n-3, ok ? "OK" : "BAD", lqi, -91+ed); |
173 | if (hex) { |
174 | for (i = 0; i != n-3; i++) |
175 | printf("%s%02x", i ? " " : "", buf[i]); |
176 | } else { |
177 | for (i = 0; i != n-3; i++) |
178 | putchar(buf[i] < ' ' || buf[i] > '~' ? '?' : buf[i]); |
179 | } |
180 | putchar('\n'); |
181 | } |
182 | |
183 | |
184 | static void write_pcap_hdr(FILE *file) |
185 | { |
186 | struct pcap_file_header hdr = { |
187 | .magic = PCAP_FILE_MAGIC, |
188 | .version_major = 2, |
189 | .version_minor = 4, |
190 | .thiszone = 0, |
191 | .sigfigs = 0, |
192 | .snaplen = MAX_PSDU, |
193 | .linktype = DLT_IEEE802_15_4 |
194 | }; |
195 | |
196 | if (fwrite(&hdr, sizeof(hdr), 1, file) != 1) { |
197 | perror("fwrite"); |
198 | exit(1); |
199 | } |
200 | } |
201 | |
202 | |
203 | static void write_pcap_rec(FILE *file, const struct timeval *tv, |
204 | const void *buf, int n) |
205 | { |
206 | struct pcap_pkthdr hdr = { |
207 | .ts_sec = tv->tv_sec, |
208 | .ts_usec = tv->tv_usec, |
209 | .caplen = n, |
210 | .len = n |
211 | }; |
212 | |
213 | if (fwrite(&hdr, sizeof(hdr), 1, file) != 1) { |
214 | perror("fwrite"); |
215 | exit(1); |
216 | } |
217 | if (fwrite(buf, n, 1, file) != 1) { |
218 | perror("fwrite"); |
219 | exit(1); |
220 | } |
221 | } |
222 | |
223 | |
224 | static void receive_pcap(struct atrf_dsc *dsc, const char *name) |
225 | { |
226 | FILE *file; |
227 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
228 | struct timeval now; |
229 | int n; |
230 | int count = 0; |
231 | |
232 | file = fopen(name, "w"); |
233 | if (!file) { |
234 | perror(name); |
235 | exit(1); |
236 | } |
237 | write_pcap_hdr(file); |
238 | while (run) { |
239 | wait_for_interrupt(dsc, |
240 | IRQ_TRX_END, IRQ_TRX_END | IRQ_RX_START | IRQ_AMI, 0); |
241 | if (!run) |
242 | break; |
243 | gettimeofday(&now, NULL); |
244 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
245 | if (n < 0) |
246 | exit(1); |
247 | if (n < 2) { |
248 | fprintf(stderr, "%d bytes received\n", n); |
249 | continue; |
250 | } |
251 | write_pcap_rec(file, &now, buf, n-1); |
252 | (void) write(2, ".", 1); |
253 | count++; |
254 | } |
255 | if (fclose(file) == EOF) { |
256 | perror(name); |
257 | exit(1); |
258 | } |
259 | fprintf(stderr, "%sreceived %d message%s\n", count ? "\n" : "", |
260 | count, count == 1 ? "" : "s"); |
261 | } |
262 | |
263 | |
264 | static void receive(struct atrf_dsc *dsc, const char *name, int hex) |
265 | { |
266 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
267 | /* |
268 | * 180 us, according to AVR2001 section 4.2. We time out after |
269 | * nominally 200 us. |
270 | */ |
271 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 1); |
272 | |
273 | if (name) |
274 | receive_pcap(dsc, name); |
275 | else |
276 | receive_message(dsc, hex); |
277 | } |
278 | |
279 | |
280 | static int dehex(uint8_t *buf, const char *s) |
281 | { |
282 | uint8_t *p; |
283 | int nibbles = 0; |
284 | uint8_t v = 0, nibble; |
285 | char cvt[2] = "?"; |
286 | char *end; |
287 | |
288 | for (p = buf; *s; s++) { |
289 | if (strchr(" \t,.:-", *s)) { |
290 | if (nibbles) |
291 | *p++ = v; |
292 | nibbles = 0; |
293 | continue; |
294 | } |
295 | cvt[0] = *s; |
296 | nibble = strtoul(cvt, &end, 16); |
297 | if (*end) { |
298 | fprintf(stderr, "invalid hex character \"%c\"\n", *s); |
299 | exit(1); |
300 | } |
301 | if (nibbles) { |
302 | *p++ = v << 4 | nibble; |
303 | nibbles = 0; |
304 | } else { |
305 | v = nibble; |
306 | nibbles++; |
307 | } |
308 | } |
309 | if (nibbles) |
310 | *p++ = v; |
311 | return p-buf; |
312 | } |
313 | |
314 | |
315 | static void transmit(struct atrf_dsc *dsc, const char *msg, int hex, int times) |
316 | { |
317 | uint8_t buf[MAX_PSDU]; |
318 | int len; |
319 | |
320 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
321 | /* |
322 | * 180 us, according to AVR2001 section 4.3. We time out after |
323 | * nominally 200 us. |
324 | */ |
325 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 1); |
326 | |
327 | /* |
328 | * We need to copy the message to append the CRC placeholders. |
329 | */ |
330 | if (hex) { |
331 | len = dehex(buf, msg); |
332 | } else { |
333 | strcpy((void *) buf, msg); |
334 | len = strlen(msg); |
335 | } |
336 | atrf_buf_write(dsc, buf, len+2); |
337 | |
338 | while (run && times--) { |
339 | /* @@@ should wait for clear channel */ |
340 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
341 | |
342 | /* wait up to 10 ms (nominally) */ |
343 | wait_for_interrupt(dsc, IRQ_TRX_END, |
344 | IRQ_TRX_END | IRQ_PLL_LOCK, 10); |
345 | } |
346 | } |
347 | |
348 | |
349 | static void receive_hmac(struct atrf_dsc *dsc) |
350 | { |
351 | uint8_t buf[MAX_PSDU]; |
352 | uint8_t lqi; |
353 | int n, i; |
354 | |
355 | atrf_rx_mode(dsc, 1); |
356 | n = atrf_rx(dsc, buf, sizeof(buf), 0, &lqi); |
357 | atrf_rx_mode(dsc, 0); |
358 | |
359 | if (n < 2) { |
360 | fprintf(stderr, "%d bytes received\n", n); |
361 | exit(1); |
362 | } |
363 | for (i = 0; i != n-2; i++) |
364 | putchar(buf[i] < ' ' || buf[i] > '~' ? '?' : buf[i]); |
365 | putchar('\n'); |
366 | } |
367 | |
368 | |
369 | static void transmit_hmac(struct atrf_dsc *dsc, const char *msg) |
370 | { |
371 | atrf_rx_mode(dsc, 1); |
372 | atrf_tx(dsc, msg, strlen(msg)); |
373 | atrf_rx_mode(dsc, 0); |
374 | } |
375 | |
376 | |
377 | /* ----- PER test ---------------------------------------------------------- */ |
378 | |
379 | |
380 | static void transmit_pattern(struct atrf_dsc *dsc, double pause_s, int times) |
381 | { |
382 | uint8_t buf[MAX_PSDU]; |
383 | uint8_t n = 0; |
384 | int us = fmod(pause_s, 1)*1000000; |
385 | |
386 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
387 | /* |
388 | * 180 us, according to AVR2001 section 4.3. We time out after |
389 | * nominally 200 us. |
390 | */ |
391 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 1); |
392 | |
393 | while (run) { |
394 | memset(buf, n, sizeof(buf)); |
395 | atrf_buf_write(dsc, buf, sizeof(buf)); |
396 | |
397 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
398 | |
399 | /* wait up to 10 ms (nominally) */ |
400 | wait_for_interrupt(dsc, IRQ_TRX_END, |
401 | IRQ_TRX_END | IRQ_PLL_LOCK, 10); |
402 | |
403 | if (pause_s >= 1) |
404 | sleep(pause_s); |
405 | if (us) |
406 | usleep(us); |
407 | |
408 | if (times && !--times) |
409 | break; |
410 | n++; |
411 | } |
412 | } |
413 | |
414 | |
415 | /* ----- Ping -------------------------------------------------------------- */ |
416 | |
417 | |
418 | static void ping_tx(struct atrf_dsc *dsc, const struct ping *pck) |
419 | { |
420 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
421 | /* |
422 | * 180 us, according to AVR2001 section 4.3. We time out after |
423 | * nominally 200 us. |
424 | */ |
425 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 1); |
426 | |
427 | atrf_buf_write(dsc, pck, sizeof(*pck)); |
428 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
429 | |
430 | /* wait up to 10 ms (nominally) */ |
431 | wait_for_interrupt(dsc, IRQ_TRX_END, |
432 | IRQ_TRX_END | IRQ_PLL_LOCK, 10); |
433 | } |
434 | |
435 | |
436 | static enum rx_res ping_rx(struct atrf_dsc *dsc, struct ping *pck, int wait_ms) |
437 | { |
438 | uint8_t irq; |
439 | int n; |
440 | |
441 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
442 | irq = wait_for_interrupt(dsc, IRQ_TRX_END, |
443 | IRQ_TRX_END | IRQ_RX_START | IRQ_PLL_LOCK, wait_ms); |
444 | if (!run) |
445 | return rx_exit; |
446 | if (!irq) |
447 | return rx_timeout; |
448 | |
449 | n = atrf_buf_read(dsc, pck, sizeof(*pck)); |
450 | if (n < 0) |
451 | exit(1); |
452 | if (n != sizeof(*pck)) { |
453 | fprintf(stderr, "%d bytes received\n", n); |
454 | return rx_bad; |
455 | } |
456 | return atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID ? |
457 | rx_good : rx_bad; |
458 | } |
459 | |
460 | |
461 | static void ping(struct atrf_dsc *dsc, double max_wait_s, int master) |
462 | { |
463 | static int first = 1; |
464 | struct ping tx_pck = { |
465 | .seq = 0, |
466 | .ack = 0, |
467 | }; |
468 | struct ping rx_pck; |
469 | enum rx_res res; |
470 | |
471 | while (run) { |
472 | tx_pck.seq++; |
473 | if (master || !first) { |
474 | ping_tx(dsc, &tx_pck); |
475 | if (!run) |
476 | break; |
477 | } |
478 | first = 0; |
479 | res = ping_rx(dsc, &rx_pck, master ? max_wait_s*1000 : 0); |
480 | switch (res) { |
481 | case rx_good: |
482 | tx_pck.ack = rx_pck.seq; |
483 | if (tx_pck.seq == rx_pck.ack) |
484 | write(2, ".", 1); |
485 | else |
486 | write(2, "*", 1); |
487 | break; |
488 | case rx_bad: |
489 | write(2, "-", 1); |
490 | break; |
491 | case rx_timeout: |
492 | write(2, "+", 1); |
493 | break; |
494 | case rx_exit: |
495 | return; |
496 | default: |
497 | abort(); |
498 | } |
499 | } |
500 | } |
501 | |
502 | |
503 | /* ----- Round-trip time --------------------------------------------------- */ |
504 | |
505 | |
506 | static void rtt_slave(struct atrf_dsc *dsc) |
507 | { |
508 | uint8_t buf[MAX_PSDU]; |
509 | int n; |
510 | |
511 | while (run) { |
512 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
513 | wait_for_interrupt(dsc, IRQ_TRX_END, |
514 | IRQ_TRX_END | IRQ_RX_START | IRQ_PLL_LOCK | IRQ_AMI, 0); |
515 | if (!run) |
516 | break; |
517 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
518 | if (n < 0) |
519 | exit(1); |
520 | if (n < 2) { |
521 | fprintf(stderr, "%d bytes received\n", n); |
522 | continue; |
523 | } |
524 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
525 | atrf_buf_write(dsc, buf, n); |
526 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
527 | wait_for_interrupt(dsc, IRQ_TRX_END, |
528 | IRQ_TRX_END | IRQ_PLL_LOCK, 10); |
529 | } |
530 | } |
531 | |
532 | |
533 | static void rtt_slave_hmac(struct atrf_dsc *dsc) |
534 | { |
535 | uint8_t buf[MAX_PSDU]; |
536 | int n; |
537 | |
538 | atrf_rx_mode(dsc, 1); |
539 | while (run) { |
540 | n = atrf_rx(dsc, buf, sizeof(buf), 0, NULL); |
541 | if (n < 0) |
542 | exit(1); |
543 | atrf_tx(dsc, buf, n-2); |
544 | } |
545 | atrf_rx_mode(dsc, 0); |
546 | } |
547 | |
548 | |
549 | static void rtt_master(struct atrf_dsc *dsc, int packets, int size) |
550 | { |
551 | uint8_t buf[size+2]; /* +CRC */ |
552 | struct timeval t0, t1; |
553 | uint8_t irq; |
554 | int first = 1; |
555 | double min = 0, max = 0, sum = 0, sum2 = 0, d; |
556 | int lost = 0, n; |
557 | int i; |
558 | |
559 | memset(buf, 0, size+2); |
560 | for (i = 0; i != packets; i++) { |
561 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
562 | atrf_buf_write(dsc, buf, size+2); |
563 | gettimeofday(&t0, NULL); |
564 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
565 | |
566 | /* prepare transition to RX_ON while still sending */ |
567 | while ((atrf_reg_read(dsc, REG_TRX_STATUS) & TRX_STATUS_MASK) |
568 | == TRX_STATUS_TRANSITION); |
569 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
570 | |
571 | /* wait for transmission to end */ |
572 | wait_for_interrupt(dsc, IRQ_TRX_END, |
573 | IRQ_TRX_END | IRQ_RX_START | IRQ_PLL_LOCK | IRQ_AMI, 1); |
574 | |
575 | /* wait for reception */ |
576 | irq = wait_for_interrupt(dsc, IRQ_TRX_END, |
577 | IRQ_TRX_END | IRQ_RX_START | IRQ_PLL_LOCK | IRQ_AMI, 1000); |
578 | if (irq) { |
579 | gettimeofday(&t1, NULL); |
580 | d = t1.tv_sec-t0.tv_sec+(t1.tv_usec-t0.tv_usec)*1e-6; |
581 | sum += d; |
582 | sum2 += d*d; |
583 | if (first || d < min) |
584 | min = d; |
585 | if (first || d > max) |
586 | max = d; |
587 | first = 0; |
588 | n = atrf_buf_read(dsc, buf, size); |
589 | if (n != size) |
590 | fprintf(stderr, "%d bytes received\n", n); |
591 | } else { |
592 | lost++; |
593 | } |
594 | } |
595 | n = packets-lost; |
596 | printf("%d sent, %d received, %d lost (%g%%)\n", |
597 | packets, n, lost, lost*100.0/packets); |
598 | if (n) |
599 | printf("rtt min/avg/max = %.3f/%.3f/%.3f ms, mdev = %.3f ms\n", |
600 | min*1000.0, sum*1000.0/n, max*1000.0, |
601 | sqrt((sum2-sum*sum/n)/n)*1000.0); |
602 | } |
603 | |
604 | |
605 | /* ----- Continuous wave test ---------------------------------------------- */ |
606 | |
607 | |
608 | static int test_mode(struct atrf_dsc *dsc, uint8_t cont_tx, const char *cmd) |
609 | { |
610 | int status = 0; |
611 | |
612 | cw_test_begin(dsc, cont_tx); |
613 | |
614 | if (cmd) |
615 | status = system(cmd); |
616 | else { |
617 | while (run) |
618 | sleep(1); |
619 | } |
620 | |
621 | cw_test_end(dsc); |
622 | |
623 | return status; |
624 | } |
625 | |
626 | |
627 | /* ----- Command-line processing ------------------------------------------- */ |
628 | |
629 | |
630 | static void die(int sig) |
631 | { |
632 | run = 0; |
633 | } |
634 | |
635 | |
636 | static void usage(const char *name) |
637 | { |
638 | fprintf(stderr, |
639 | "usage: %s [common_options] [-x] [message [repetitions]]\n" |
640 | " %s [common_options] -H [message]\n" |
641 | " %s [common_options] -E pause_s [repetitions]\n" |
642 | " %s [common_options] -P [max_wait_s]\n" |
643 | " %s [common_options] -R [-H|packets size]\n" |
644 | " %s [common_options] -T offset [command]\n\n" |
645 | " text message mode:\n" |
646 | " -x message consists of hex bytes, optionally separated by\n" |
647 | " ' ', '.', ',', ':', or '-'\n" |
648 | " message message string to send (if absent, receive)\n" |
649 | " repetitions number of times the message is sent (default 1)\n\n" |
650 | " text message mode (hard MAC):\n" |
651 | " -H use hard MAC mode\n" |
652 | " message message string to send (if absent, receive)\n\n" |
653 | " PER test mode (transmit only):\n" |
654 | " -E pause_s seconds to pause between frames (floating-point)\n" |
655 | " repetitions number of messages to send (default: infinite)\n\n" |
656 | " Ping-pong mode:\n" |
657 | " -P exchange packets between two stations\n" |
658 | " max_wait_s generate a new packet if no response is received (master)\n\n" |
659 | " Round-trip time measurement:\n" |
660 | " -R send/receive RTT measurement packets\n" |
661 | " packets number of packets to send (master)\n" |
662 | " size size of packets in bytes\n\n" |
663 | " constant wave test mode (transmit only):\n" |
664 | " -T offset test mode. offset is the frequency offset of the constant\n" |
665 | " wave in MHz: -2, -0.5, or +0.5\n" |
666 | " command shell command to run while transmitting (default: wait for\n" |
667 | " SIGINT instead)\n\n" |
668 | " common options: [-c channel|-f freq] [-C mhz] [-d driver[:arg]] [-o file]\n" |
669 | " [-p power] [-r rate] [-t trim]\n" |
670 | " -c channel channel number, 11 to 26 (default %d)\n" |
671 | " -C mhz output clock at 1, 2, 4, 8, or 16 MHz (default: off)\n" |
672 | " -d driver[:arg]\n" |
673 | " use the specified driver (default: %s)\n" |
674 | " -f freq frequency in MHz, 2405 to 2480 (default %d)\n" |
675 | " -o file write received data to a file in pcap format\n" |
676 | " -p power transmit power, -17.2 to 3.0 dBm (default %.1f)\n" |
677 | " -r rate data rate, 250k, 500k, 1M, or 2M (default: 250k)\n" |
678 | " -t trim trim capacitor, 0 to 15 (default %d)\n" |
679 | , name, name, name, name, name, name, |
680 | DEFAULT_CHANNEL, atrf_default_driver_name(), |
681 | 2405+5*(DEFAULT_CHANNEL-11), DEFAULT_POWER, |
682 | DEFAULT_TRIM); |
683 | exit(1); |
684 | } |
685 | |
686 | |
687 | static void set_mode(enum mode *mode, enum mode new) |
688 | { |
689 | if (*mode == mode_msg) { |
690 | *mode = new; |
691 | return; |
692 | } |
693 | if ((*mode == mode_hmac && new == mode_rtt) || |
694 | (*mode == mode_rtt && new == mode_hmac)) { |
695 | *mode = mode_rtt_hmac; |
696 | return; |
697 | } |
698 | fprintf(stderr, "multiple mode selections\n"); |
699 | exit(1); |
700 | } |
701 | |
702 | |
703 | int main(int argc, char *const *argv) |
704 | { |
705 | enum mode mode = mode_msg; |
706 | const char *driver = NULL; |
707 | int channel = DEFAULT_CHANNEL; |
708 | double power = DEFAULT_POWER; |
709 | uint8_t rate = OQPSK_DATA_RATE_250; |
710 | int trim = DEFAULT_TRIM, times = 1, bytes; |
711 | uint8_t cont_tx = 0; |
712 | double pause_s = 0; |
713 | int hex = 0; |
714 | char *end; |
715 | int c, freq; |
716 | unsigned clkm = 0; |
717 | int status = 0; |
718 | const char *pcap_file = NULL; |
719 | struct atrf_dsc *dsc; |
720 | |
721 | while ((c = getopt(argc, argv, "c:C:d:E:f:Ho:p:Pr:Rt:T:x")) != EOF) |
722 | switch (c) { |
723 | case 'c': |
724 | channel = strtoul(optarg, &end, 0); |
725 | if (*end) |
726 | usage(*argv); |
727 | if (channel < 11 || channel > 26) |
728 | usage(*argv); |
729 | break; |
730 | case 'C': |
731 | clkm = strtol(optarg, &end, 0); |
732 | if (*end) |
733 | usage(*argv); |
734 | if (!clkm) |
735 | usage(*argv); |
736 | break; |
737 | case 'd': |
738 | driver = optarg; |
739 | break; |
740 | case 'E': |
741 | set_mode(&mode, mode_per); |
742 | pause_s = strtof(optarg, &end); |
743 | if (*end) |
744 | usage(*argv); |
745 | break; |
746 | case 'f': |
747 | freq = strtoul(optarg, &end, 0); |
748 | if (*end) |
749 | usage(*argv); |
750 | if (freq % 5) |
751 | usage(*argv); |
752 | channel = (freq-2405)/5+11; |
753 | if (channel < 11 || channel > 26) |
754 | usage(*argv); |
755 | break; |
756 | case 'H': |
757 | set_mode(&mode, mode_hmac); |
758 | break; |
759 | case 'o': |
760 | pcap_file = optarg; |
761 | break; |
762 | case 'p': |
763 | power = strtod(optarg, &end); |
764 | if (*end) |
765 | usage(*argv); |
766 | break; |
767 | case 'P': |
768 | set_mode(&mode, mode_ping); |
769 | break; |
770 | case 'r': |
771 | if (!strcmp(optarg, "250k")) |
772 | rate = OQPSK_DATA_RATE_250; |
773 | else if (!strcmp(optarg, "500k")) |
774 | rate = OQPSK_DATA_RATE_500; |
775 | else if (!strcmp(optarg, "1M")) |
776 | rate = OQPSK_DATA_RATE_1000; |
777 | else if (!strcmp(optarg, "2M")) |
778 | rate = OQPSK_DATA_RATE_2000; |
779 | else |
780 | usage(*argv); |
781 | break; |
782 | case 'R': |
783 | set_mode(&mode, mode_rtt); |
784 | break; |
785 | case 't': |
786 | trim = strtoul(optarg, &end, 0); |
787 | if (*end) |
788 | usage(*argv); |
789 | if (trim > 15) |
790 | usage(*argv); |
791 | break; |
792 | case 'T': |
793 | set_mode(&mode, mode_cont_tx); |
794 | if (!strcmp(optarg, "-2")) |
795 | cont_tx = CONT_TX_M2M; |
796 | else if (!strcmp(optarg, "-0.5")) |
797 | cont_tx = CONT_TX_M500K; |
798 | else if (!strcmp(optarg, "+0.5")) |
799 | cont_tx = CONT_TX_P500K; |
800 | else |
801 | usage(*argv); |
802 | break; |
803 | case 'x': |
804 | hex = 1; |
805 | break; |
806 | default: |
807 | usage(*argv); |
808 | } |
809 | |
810 | signal(SIGINT, die); |
811 | |
812 | switch (argc-optind) { |
813 | case 0: |
814 | dsc = init_txrx(driver, trim, clkm); |
815 | set_channel(dsc, channel); |
816 | set_rate(dsc, rate); |
817 | switch (mode) { |
818 | case mode_msg: |
819 | receive(dsc, pcap_file, hex); |
820 | break; |
821 | case mode_hmac: |
822 | receive_hmac(dsc); |
823 | break; |
824 | case mode_per: |
825 | set_power_dBm(dsc, power, 0); |
826 | transmit_pattern(dsc, pause_s, 0); |
827 | break; |
828 | case mode_ping: |
829 | set_power_dBm(dsc, power, 1); |
830 | ping(dsc, pause_s, 0); |
831 | break; |
832 | case mode_rtt: |
833 | set_power_dBm(dsc, power, 1); |
834 | rtt_slave(dsc); |
835 | break; |
836 | case mode_rtt_hmac: |
837 | set_power_dBm(dsc, power, 1); |
838 | rtt_slave_hmac(dsc); |
839 | break; |
840 | case mode_cont_tx: |
841 | set_power_dBm(dsc, power, 0); |
842 | status = test_mode(dsc, cont_tx, NULL); |
843 | break; |
844 | default: |
845 | abort(); |
846 | } |
847 | break; |
848 | case 2: |
849 | switch (mode) { |
850 | case mode_msg: |
851 | case mode_hmac: |
852 | break; |
853 | case mode_per: |
854 | case mode_ping: |
855 | case mode_rtt_hmac: |
856 | case mode_cont_tx: |
857 | usage(*argv); |
858 | case mode_rtt: |
859 | times = strtoul(argv[optind], &end, 0); |
860 | if (*end) |
861 | usage(*argv); |
862 | bytes = strtoul(argv[optind+1], &end, 0); |
863 | if (*end) |
864 | usage(*argv); |
865 | dsc = init_txrx(driver, trim, clkm); |
866 | set_channel(dsc, channel); |
867 | set_rate(dsc, rate); |
868 | set_power_dBm(dsc, power, 1); |
869 | rtt_master(dsc, times, bytes); |
870 | goto done; |
871 | default: |
872 | abort(); |
873 | } |
874 | times = strtoul(argv[optind+1], &end, 0); |
875 | if (*end) |
876 | usage(*argv); |
877 | /* fall through */ |
878 | case 1: |
879 | dsc = init_txrx(driver, trim, clkm); |
880 | set_channel(dsc, channel); |
881 | set_rate(dsc, rate); |
882 | switch (mode) { |
883 | case mode_msg: |
884 | set_power_dBm(dsc, power, 1); |
885 | transmit(dsc, argv[optind], hex, times); |
886 | break; |
887 | case mode_hmac: |
888 | set_power_dBm(dsc, power, 1); |
889 | transmit_hmac(dsc, argv[optind]); |
890 | break; |
891 | case mode_per: |
892 | times = strtoul(argv[optind], &end, 0); |
893 | if (*end) |
894 | usage(*argv); |
895 | set_power_dBm(dsc, power, 0); |
896 | transmit_pattern(dsc, pause_s, times); |
897 | break; |
898 | case mode_ping: |
899 | pause_s = strtof(argv[optind], &end); |
900 | if (*end) |
901 | usage(*argv); |
902 | set_power_dBm(dsc, power, 1); |
903 | ping(dsc, pause_s, 1); |
904 | break; |
905 | case mode_rtt: |
906 | case mode_rtt_hmac: |
907 | usage(*argv); |
908 | break; |
909 | case mode_cont_tx: |
910 | set_power_dBm(dsc, power, 0); |
911 | status = test_mode(dsc, cont_tx, argv[optind]); |
912 | break; |
913 | default: |
914 | abort(); |
915 | |
916 | } |
917 | break; |
918 | default: |
919 | usage(*argv); |
920 | } |
921 | done: |
922 | |
923 | atrf_close(dsc); |
924 | |
925 | if (status) { |
926 | if (WIFEXITED(status)) |
927 | return WEXITSTATUS(status); |
928 | if (WIFSIGNALED(status)) |
929 | raise(WTERMSIG(status)); |
930 | fprintf(stderr, "unexpected exit status %d\n", status); |
931 | abort(); |
932 | } |
933 | return 0; |
934 | } |
935 |