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 16b095ca08e11a0e6f430f5d22a481ea5e8df10f created 12 years 11 months ago. By Werner Almesberger, atrf-txrx.c (receive_message, receive_pcap): also ignore IRQ_AMI | |
---|---|
1 | /* |
2 | * atrf-txrx/atrf-txrx.c - ben-wpan AT86RF230 TX/RX |
3 | * |
4 | * Written 2010-2011 by Werner Almesberger |
5 | * Copyright 2010-2011 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 | |
66 | static volatile int run = 1; |
67 | |
68 | |
69 | /* |
70 | * clkm: 0 disable CLKM |
71 | * >0 output 2^(clkm-1) MHz signal |
72 | */ |
73 | |
74 | static struct atrf_dsc *init_txrx(const char *driver, int trim, unsigned mhz) |
75 | { |
76 | struct atrf_dsc *dsc; |
77 | |
78 | dsc = atrf_open(driver); |
79 | if (!dsc) |
80 | exit(1); |
81 | |
82 | atrf_reset_rf(dsc); |
83 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
84 | |
85 | #if 1 // def HAVE_USB /* @@@ yeah, ugly */ |
86 | atrf_reg_write(dsc, REG_XOSC_CTRL, |
87 | (XTAL_MODE_INT << XTAL_MODE_SHIFT) | trim); |
88 | #else |
89 | atrf_reg_write(dsc, REG_XOSC_CTRL, XTAL_MODE_EXT << XTAL_MODE_SHIFT); |
90 | #endif |
91 | |
92 | if (!atrf_set_clkm(dsc, mhz)) |
93 | if (mhz) { |
94 | atrf_close(dsc); |
95 | exit(1); |
96 | } |
97 | |
98 | /* We want to see all interrupts, not only the ones we're expecting. */ |
99 | atrf_reg_write(dsc, REG_IRQ_MASK, 0xff); |
100 | |
101 | (void) atrf_reg_read(dsc, REG_IRQ_STATUS); |
102 | if (atrf_identify(dsc) == artf_at86rf231) |
103 | wait_for_interrupt(dsc, IRQ_CCA_ED_DONE, IRQ_CCA_ED_DONE, |
104 | 10, 50); /* according to table 7-1, 37 us max */ |
105 | |
106 | return dsc; |
107 | } |
108 | |
109 | |
110 | static void set_channel(struct atrf_dsc *dsc, int channel) |
111 | { |
112 | atrf_reg_write(dsc, REG_PHY_CC_CCA, (1 << CCA_MODE_SHIFT) | channel); |
113 | } |
114 | |
115 | |
116 | static void set_rate(struct atrf_dsc *dsc, uint8_t rate) |
117 | { |
118 | if (!rate) |
119 | return; |
120 | switch (atrf_identify(dsc)) { |
121 | case artf_at86rf230: |
122 | fprintf(stderr, "AT86RF230 only supports 250 kbps\n"); |
123 | break; |
124 | case artf_at86rf231: |
125 | atrf_reg_write(dsc, REG_TRX_CTRL_2, rate); |
126 | break; |
127 | default: |
128 | abort(); |
129 | } |
130 | } |
131 | |
132 | |
133 | static void receive_message(struct atrf_dsc *dsc) |
134 | { |
135 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
136 | int n, ok, i; |
137 | uint8_t ed, lqi; |
138 | |
139 | fprintf(stderr, "Ready.\n"); |
140 | wait_for_interrupt(dsc, IRQ_TRX_END, |
141 | IRQ_TRX_END | IRQ_RX_START | IRQ_AMI, 10, 0); |
142 | if (!run) |
143 | return; |
144 | |
145 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
146 | if (n < 0) |
147 | exit(1); |
148 | if (n < 3) { |
149 | fprintf(stderr, "%d bytes received\n", n); |
150 | exit(1); |
151 | } |
152 | ed = atrf_reg_read(dsc, REG_PHY_ED_LEVEL); |
153 | ok = !!(atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID); |
154 | lqi = buf[n-1]; |
155 | fprintf(stderr, "%d bytes payload, CRC %s, LQI %u, ED %d dBm\n", |
156 | n-3, ok ? "OK" : "BAD", lqi, -91+ed); |
157 | for (i = 0; i != n-3; i++) |
158 | putchar(buf[i] < ' ' || buf[i] > '~' ? '?' : buf[i]); |
159 | putchar('\n'); |
160 | } |
161 | |
162 | |
163 | static void write_pcap_hdr(FILE *file) |
164 | { |
165 | struct pcap_file_header hdr = { |
166 | .magic = PCAP_FILE_MAGIC, |
167 | .version_major = 2, |
168 | .version_minor = 4, |
169 | .thiszone = 0, |
170 | .sigfigs = 0, |
171 | .snaplen = MAX_PSDU, |
172 | .linktype = DLT_IEEE802_15_4 |
173 | }; |
174 | |
175 | if (fwrite(&hdr, sizeof(hdr), 1, file) != 1) { |
176 | perror("fwrite"); |
177 | exit(1); |
178 | } |
179 | } |
180 | |
181 | |
182 | static void write_pcap_rec(FILE *file, const struct timeval *tv, |
183 | const void *buf, int n) |
184 | { |
185 | struct pcap_pkthdr hdr = { |
186 | .ts_sec = tv->tv_sec, |
187 | .ts_usec = tv->tv_usec, |
188 | .caplen = n, |
189 | .len = n |
190 | }; |
191 | |
192 | if (fwrite(&hdr, sizeof(hdr), 1, file) != 1) { |
193 | perror("fwrite"); |
194 | exit(1); |
195 | } |
196 | if (fwrite(buf, n, 1, file) != 1) { |
197 | perror("fwrite"); |
198 | exit(1); |
199 | } |
200 | } |
201 | |
202 | |
203 | static void receive_pcap(struct atrf_dsc *dsc, const char *name) |
204 | { |
205 | FILE *file; |
206 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
207 | struct timeval now; |
208 | int n; |
209 | int count = 0; |
210 | |
211 | file = fopen(name, "w"); |
212 | if (!file) { |
213 | perror(name); |
214 | exit(1); |
215 | } |
216 | write_pcap_hdr(file); |
217 | while (run) { |
218 | wait_for_interrupt(dsc, |
219 | IRQ_TRX_END, IRQ_TRX_END | IRQ_RX_START | IRQ_AMI, |
220 | 10, 0); |
221 | if (!run) |
222 | break; |
223 | gettimeofday(&now, NULL); |
224 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
225 | if (n < 0) |
226 | exit(1); |
227 | if (n < 2) { |
228 | fprintf(stderr, "%d bytes received\n", n); |
229 | continue; |
230 | } |
231 | write_pcap_rec(file, &now, buf, n-1); |
232 | (void) write(2, ".", 1); |
233 | count++; |
234 | } |
235 | if (fclose(file) == EOF) { |
236 | perror(name); |
237 | exit(1); |
238 | } |
239 | fprintf(stderr, "%sreceived %d message%s\n", count ? "\n" : "", |
240 | count, count == 1 ? "" : "s"); |
241 | } |
242 | |
243 | |
244 | static void receive(struct atrf_dsc *dsc, const char *name) |
245 | { |
246 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
247 | /* |
248 | * 180 us, according to AVR2001 section 4.2. We time out after |
249 | * nominally 200 us. |
250 | */ |
251 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
252 | |
253 | if (name) |
254 | receive_pcap(dsc, name); |
255 | else |
256 | receive_message(dsc); |
257 | } |
258 | |
259 | |
260 | static void transmit(struct atrf_dsc *dsc, const char *msg, int times) |
261 | { |
262 | uint8_t buf[MAX_PSDU]; |
263 | |
264 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
265 | /* |
266 | * 180 us, according to AVR2001 section 4.3. We time out after |
267 | * nominally 200 us. |
268 | */ |
269 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
270 | |
271 | /* |
272 | * We need to copy the message to append the CRC placeholders. |
273 | */ |
274 | strcpy((void *) buf, msg); |
275 | atrf_buf_write(dsc, buf, strlen(msg)+2); |
276 | |
277 | while (run && times--) { |
278 | /* @@@ should wait for clear channel */ |
279 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
280 | |
281 | /* wait up to 10 ms (nominally) */ |
282 | wait_for_interrupt(dsc, IRQ_TRX_END, |
283 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
284 | } |
285 | } |
286 | |
287 | |
288 | static void transmit_pattern(struct atrf_dsc *dsc, double pause_s, int times) |
289 | { |
290 | uint8_t buf[MAX_PSDU]; |
291 | uint8_t n = 0; |
292 | int us = fmod(pause_s, 1)*1000000; |
293 | |
294 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
295 | /* |
296 | * 180 us, according to AVR2001 section 4.3. We time out after |
297 | * nominally 200 us. |
298 | */ |
299 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
300 | |
301 | while (run) { |
302 | memset(buf, n, sizeof(buf)); |
303 | atrf_buf_write(dsc, buf, sizeof(buf)); |
304 | |
305 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
306 | |
307 | /* wait up to 10 ms (nominally) */ |
308 | wait_for_interrupt(dsc, IRQ_TRX_END, |
309 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
310 | |
311 | if (pause_s >= 1) |
312 | sleep(pause_s); |
313 | if (us) |
314 | usleep(us); |
315 | |
316 | if (times && !--times) |
317 | break; |
318 | n++; |
319 | } |
320 | } |
321 | |
322 | |
323 | static void ping_tx(struct atrf_dsc *dsc, const struct ping *pck) |
324 | { |
325 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
326 | /* |
327 | * 180 us, according to AVR2001 section 4.3. We time out after |
328 | * nominally 200 us. |
329 | */ |
330 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
331 | |
332 | atrf_buf_write(dsc, pck, sizeof(*pck)); |
333 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
334 | |
335 | /* wait up to 10 ms (nominally) */ |
336 | wait_for_interrupt(dsc, IRQ_TRX_END, |
337 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
338 | } |
339 | |
340 | |
341 | static enum rx_res ping_rx(struct atrf_dsc *dsc, struct ping *pck, int wait_ds) |
342 | { |
343 | uint8_t irq; |
344 | int n; |
345 | |
346 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
347 | irq = wait_for_interrupt(dsc, IRQ_TRX_END, |
348 | IRQ_TRX_END | IRQ_RX_START | IRQ_PLL_LOCK, |
349 | 100000, wait_ds); |
350 | if (!run) |
351 | return rx_exit; |
352 | if (!irq) |
353 | return rx_timeout; |
354 | |
355 | n = atrf_buf_read(dsc, pck, sizeof(*pck)); |
356 | if (n < 0) |
357 | exit(1); |
358 | if (n != sizeof(*pck)) { |
359 | fprintf(stderr, "%d bytes received\n", n); |
360 | return rx_bad; |
361 | } |
362 | return atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID ? |
363 | rx_good : rx_bad; |
364 | } |
365 | |
366 | |
367 | static void ping(struct atrf_dsc *dsc, double max_wait_s, int master) |
368 | { |
369 | static int first = 1; |
370 | struct ping tx_pck = { |
371 | .seq = 0, |
372 | .ack = 0, |
373 | }; |
374 | struct ping rx_pck; |
375 | enum rx_res res; |
376 | |
377 | while (run) { |
378 | tx_pck.seq++; |
379 | if (master || !first) { |
380 | ping_tx(dsc, &tx_pck); |
381 | if (!run) |
382 | break; |
383 | } |
384 | first = 0; |
385 | res = ping_rx(dsc, &rx_pck, master ? max_wait_s*10 : 0); |
386 | switch (res) { |
387 | case rx_good: |
388 | tx_pck.ack = rx_pck.seq; |
389 | if (tx_pck.seq == rx_pck.ack) |
390 | write(2, ".", 1); |
391 | else |
392 | write(2, "*", 1); |
393 | break; |
394 | case rx_bad: |
395 | write(2, "-", 1); |
396 | break; |
397 | case rx_timeout: |
398 | write(2, "+", 1); |
399 | break; |
400 | case rx_exit: |
401 | return; |
402 | default: |
403 | abort(); |
404 | } |
405 | } |
406 | } |
407 | |
408 | |
409 | static int test_mode(struct atrf_dsc *dsc, uint8_t cont_tx, const char *cmd) |
410 | { |
411 | int status = 0; |
412 | |
413 | cw_test_begin(dsc, cont_tx); |
414 | |
415 | if (cmd) |
416 | status = system(cmd); |
417 | else { |
418 | while (run) |
419 | sleep(1); |
420 | } |
421 | |
422 | cw_test_end(dsc); |
423 | |
424 | return status; |
425 | } |
426 | |
427 | |
428 | static void die(int sig) |
429 | { |
430 | run = 0; |
431 | } |
432 | |
433 | |
434 | static void usage(const char *name) |
435 | { |
436 | fprintf(stderr, |
437 | "usage: %s [common_options] [message [repetitions]]\n" |
438 | " %s [common_options] -E pause_s [repetitions]\n" |
439 | " %s [common_options] -P [max_wait_s]\n" |
440 | " %s [common_options] -T offset [command]\n\n" |
441 | " text message mode:\n" |
442 | " message message string to send (if absent, receive)\n" |
443 | " repetitions number of times the message is sent (default 1)\n\n" |
444 | " PER test mode (transmit only):\n" |
445 | " -E pause_s seconds to pause between frames (floating-point)\n" |
446 | " repetitions number of messages to send (default: infinite)\n\n" |
447 | " Ping-pong mode:\n" |
448 | " -P exchange packets between two stations\n" |
449 | " max_wait_s generate a new packet if no response is received (master)\n\n" |
450 | " constant wave test mode (transmit only):\n" |
451 | " -T offset test mode. offset is the frequency offset of the constant\n" |
452 | " wave in MHz: -2, -0.5, or +0.5\n" |
453 | " command shell command to run while transmitting (default: wait for\n" |
454 | " SIGINT instead)\n\n" |
455 | " common options: [-c channel|-f freq] [-C mhz] [-d driver[:arg]] [-o file]\n" |
456 | " [-p power] [-r rate] [-t trim]\n" |
457 | " -c channel channel number, 11 to 26 (default %d)\n" |
458 | " -C mhz output clock at 1, 2, 4, 8, or 16 MHz (default: off)\n" |
459 | " -d driver[:arg]\n" |
460 | " use the specified driver (default: %s)\n" |
461 | " -f freq frequency in MHz, 2405 to 2480 (default %d)\n" |
462 | " -o file write received data to a file in pcap format\n" |
463 | " -p power transmit power, -17.2 to 3.0 dBm (default %.1f)\n" |
464 | " -r rate data rate, 250k, 500k, 1M, or 2M (default: 250k)\n" |
465 | " -t trim trim capacitor, 0 to 15 (default %d)\n" |
466 | , name, name, name, name, |
467 | DEFAULT_CHANNEL, atrf_default_driver_name(), |
468 | 2405+5*(DEFAULT_CHANNEL-11), DEFAULT_POWER, |
469 | DEFAULT_TRIM); |
470 | exit(1); |
471 | } |
472 | |
473 | |
474 | int main(int argc, char *const *argv) |
475 | { |
476 | enum { |
477 | mode_msg, |
478 | mode_per, |
479 | mode_ping, |
480 | mode_cont_tx, |
481 | } mode = mode_msg; |
482 | const char *driver = NULL; |
483 | int channel = DEFAULT_CHANNEL; |
484 | double power = DEFAULT_POWER; |
485 | uint8_t rate = OQPSK_DATA_RATE_250; |
486 | int trim = DEFAULT_TRIM, times = 1; |
487 | uint8_t cont_tx = 0; |
488 | double pause_s = 0; |
489 | char *end; |
490 | int c, freq; |
491 | unsigned clkm = 0; |
492 | int status = 0; |
493 | const char *pcap_file = NULL; |
494 | struct atrf_dsc *dsc; |
495 | |
496 | while ((c = getopt(argc, argv, "c:C:d:f:o:p:r:E:Pt:T:")) != EOF) |
497 | switch (c) { |
498 | case 'c': |
499 | channel = strtoul(optarg, &end, 0); |
500 | if (*end) |
501 | usage(*argv); |
502 | if (channel < 11 || channel > 26) |
503 | usage(*argv); |
504 | break; |
505 | case 'd': |
506 | driver = optarg; |
507 | break; |
508 | case 'f': |
509 | freq = strtoul(optarg, &end, 0); |
510 | if (*end) |
511 | usage(*argv); |
512 | if (freq % 5) |
513 | usage(*argv); |
514 | channel = (freq-2405)/5+11; |
515 | if (channel < 11 || channel > 26) |
516 | usage(*argv); |
517 | break; |
518 | case 'o': |
519 | pcap_file = optarg; |
520 | break; |
521 | case 'p': |
522 | power = strtod(optarg, &end); |
523 | if (*end) |
524 | usage(*argv); |
525 | break; |
526 | case 'r': |
527 | if (!strcmp(optarg, "250k")) |
528 | rate = OQPSK_DATA_RATE_250; |
529 | else if (!strcmp(optarg, "500k")) |
530 | rate = OQPSK_DATA_RATE_500; |
531 | else if (!strcmp(optarg, "1M")) |
532 | rate = OQPSK_DATA_RATE_1000; |
533 | else if (!strcmp(optarg, "2M")) |
534 | rate = OQPSK_DATA_RATE_2000; |
535 | else |
536 | usage(*argv); |
537 | break; |
538 | case 't': |
539 | trim = strtoul(optarg, &end, 0); |
540 | if (*end) |
541 | usage(*argv); |
542 | if (trim > 15) |
543 | usage(*argv); |
544 | break; |
545 | case 'C': |
546 | clkm = strtol(optarg, &end, 0); |
547 | if (*end) |
548 | usage(*argv); |
549 | if (!clkm) |
550 | usage(*argv); |
551 | break; |
552 | case 'E': |
553 | mode = mode_per; |
554 | pause_s = strtof(optarg, &end); |
555 | if (*end) |
556 | usage(*argv); |
557 | break; |
558 | case 'P': |
559 | mode = mode_ping; |
560 | break; |
561 | case 'T': |
562 | mode = mode_cont_tx; |
563 | if (!strcmp(optarg, "-2")) |
564 | cont_tx = CONT_TX_M2M; |
565 | else if (!strcmp(optarg, "-0.5")) |
566 | cont_tx = CONT_TX_M500K; |
567 | else if (!strcmp(optarg, "+0.5")) |
568 | cont_tx = CONT_TX_P500K; |
569 | else |
570 | usage(*argv); |
571 | break; |
572 | default: |
573 | usage(*argv); |
574 | } |
575 | |
576 | signal(SIGINT, die); |
577 | |
578 | switch (argc-optind) { |
579 | case 0: |
580 | dsc = init_txrx(driver, trim, clkm); |
581 | set_channel(dsc, channel); |
582 | set_rate(dsc, rate); |
583 | switch (mode) { |
584 | case mode_msg: |
585 | receive(dsc, pcap_file); |
586 | break; |
587 | case mode_per: |
588 | set_power_dBm(dsc, power, 0); |
589 | transmit_pattern(dsc, pause_s, 0); |
590 | break; |
591 | case mode_ping: |
592 | set_power_dBm(dsc, power, 1); |
593 | ping(dsc, pause_s, 0); |
594 | break; |
595 | case mode_cont_tx: |
596 | set_power_dBm(dsc, power, 0); |
597 | status = test_mode(dsc, cont_tx, NULL); |
598 | break; |
599 | default: |
600 | abort(); |
601 | } |
602 | break; |
603 | case 2: |
604 | switch (mode) { |
605 | case mode_msg: |
606 | break; |
607 | case mode_per: |
608 | case mode_ping: |
609 | /* fall through */ |
610 | case mode_cont_tx: |
611 | usage(*argv); |
612 | default: |
613 | abort(); |
614 | } |
615 | times = strtoul(argv[optind+1], &end, 0); |
616 | if (*end) |
617 | usage(*argv); |
618 | /* fall through */ |
619 | case 1: |
620 | dsc = init_txrx(driver, trim, clkm); |
621 | set_channel(dsc, channel); |
622 | set_rate(dsc, rate); |
623 | switch (mode) { |
624 | case mode_msg: |
625 | set_power_dBm(dsc, power, 1); |
626 | transmit(dsc, argv[optind], times); |
627 | break; |
628 | case mode_per: |
629 | times = strtoul(argv[optind], &end, 0); |
630 | if (*end) |
631 | usage(*argv); |
632 | set_power_dBm(dsc, power, 0); |
633 | transmit_pattern(dsc, pause_s, times); |
634 | break; |
635 | case mode_ping: |
636 | pause_s = strtof(argv[optind], &end); |
637 | if (*end) |
638 | usage(*argv); |
639 | set_power_dBm(dsc, power, 1); |
640 | ping(dsc, pause_s, 1); |
641 | break; |
642 | case mode_cont_tx: |
643 | set_power_dBm(dsc, power, 0); |
644 | status = test_mode(dsc, cont_tx, argv[optind]); |
645 | break; |
646 | default: |
647 | abort(); |
648 | |
649 | } |
650 | break; |
651 | default: |
652 | usage(*argv); |
653 | } |
654 | |
655 | atrf_close(dsc); |
656 | |
657 | if (status) { |
658 | if (WIFEXITED(status)) |
659 | return WEXITSTATUS(status); |
660 | if (WIFSIGNALED(status)) |
661 | raise(WTERMSIG(status)); |
662 | fprintf(stderr, "unexpected exit status %d\n", status); |
663 | abort(); |
664 | } |
665 | return 0; |
666 | } |
667 |