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 ea81e70cf9509feb5df19ab2ff5cc7e2ff8efb35 created 13 years 2 months ago. By Werner Almesberger, atrf-txrx: define the pcap file magic number (0xa1b2c3d4) in pcap.h | |
---|---|
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 | |
28 | #include "pcap.h" |
29 | |
30 | |
31 | /* |
32 | * According to IEEE 802.15.4-2003 section E.2.6, channel 15 is the only |
33 | * channel that falls into the 802.11 guard bands in North America an Europe. |
34 | */ |
35 | |
36 | #define DEFAULT_CHANNEL 15 /* channel 15, 2425 MHz */ |
37 | |
38 | /* |
39 | * Transmit power, dBm. IEEE 802.15.4-2003 section E.3.1.3 specifies a transmit |
40 | * power of 0 dBm for IEEE 802.15.4. We assume an antenna gain of 3 dB or |
41 | * better. |
42 | */ |
43 | |
44 | #define DEFAULT_POWER -3.2 /* transmit power, dBm */ |
45 | |
46 | |
47 | static double tx_pwr_230[] = { |
48 | 3.0, 2.6, 2.1, 1.6, |
49 | 1.1, 0.5, -0.2, -1.2, |
50 | -2.2, -3.2, -4.2, -5.2, |
51 | -7.2, -9.2, -12.2, -17.2 |
52 | }; |
53 | |
54 | |
55 | static double tx_pwr_231[] = { |
56 | 3.0, 2.8, 2.3, 1.8, |
57 | 1.3, 0.7, 0.0, -1, |
58 | -2, -3, -4, -5, |
59 | -7, -9, -12, -17 |
60 | }; |
61 | |
62 | |
63 | static volatile int run = 1; |
64 | |
65 | |
66 | /* |
67 | * clkm: 0 disable CLKM |
68 | * >0 output 2^(clkm-1) MHz signal |
69 | */ |
70 | |
71 | static struct atrf_dsc *init_txrx(int trim, unsigned clkm) |
72 | { |
73 | struct atrf_dsc *dsc; |
74 | |
75 | dsc = atrf_open(); |
76 | if (!dsc) |
77 | exit(1); |
78 | |
79 | atrf_reset_rf(dsc); |
80 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TRX_OFF); |
81 | |
82 | #if 1 // def HAVE_USB /* @@@ yeah, ugly */ |
83 | atrf_reg_write(dsc, REG_XOSC_CTRL, |
84 | (XTAL_MODE_INT << XTAL_MODE_SHIFT) | trim); |
85 | #else |
86 | atrf_reg_write(dsc, REG_XOSC_CTRL, XTAL_MODE_EXT << XTAL_MODE_SHIFT); |
87 | #endif |
88 | |
89 | if (!clkm) |
90 | atrf_reg_write(dsc, REG_TRX_CTRL_0, 0); /* disable CLKM */ |
91 | else |
92 | atrf_reg_write(dsc, REG_TRX_CTRL_0, |
93 | (PAD_IO_8mA << PAD_IO_CLKM_SHIFT) | clkm); |
94 | |
95 | /* We want to see all interrupts, not only the ones we're expecting. */ |
96 | atrf_reg_write(dsc, REG_IRQ_MASK, 0xff); |
97 | |
98 | (void) atrf_reg_read(dsc, REG_IRQ_STATUS); |
99 | if (atrf_identify(dsc) == artf_at86rf231) |
100 | wait_for_interrupt(dsc, IRQ_CCA_ED_DONE, IRQ_CCA_ED_DONE, |
101 | 10, 50); /* according to table 7-1, 37 us max */ |
102 | |
103 | return dsc; |
104 | } |
105 | |
106 | |
107 | static void set_channel(struct atrf_dsc *dsc, int channel) |
108 | { |
109 | atrf_reg_write(dsc, REG_PHY_CC_CCA, (1 << CCA_MODE_SHIFT) | channel); |
110 | } |
111 | |
112 | |
113 | static void set_power(struct atrf_dsc *dsc, double power, int crc) |
114 | { |
115 | const double *tx_pwr; |
116 | int n; |
117 | uint8_t tmp; |
118 | |
119 | switch (atrf_identify(dsc)) { |
120 | case artf_at86rf230: |
121 | tx_pwr = tx_pwr_230; |
122 | break; |
123 | case artf_at86rf231: |
124 | tx_pwr = tx_pwr_231; |
125 | break; |
126 | default: |
127 | abort(); |
128 | } |
129 | |
130 | for (n = 0; n != sizeof(tx_pwr_230)/sizeof(*tx_pwr_230)-1; n++) |
131 | if (tx_pwr[n] <= power) |
132 | break; |
133 | if (fabs(tx_pwr[n]-power) > 0.01) |
134 | fprintf(stderr, "TX power %.1f dBm\n", tx_pwr[n]); |
135 | |
136 | switch (atrf_identify(dsc)) { |
137 | case artf_at86rf230: |
138 | atrf_reg_write(dsc, REG_PHY_TX_PWR, |
139 | (crc ? TX_AUTO_CRC_ON_230 : 0) | n); |
140 | break; |
141 | case artf_at86rf231: |
142 | tmp = atrf_reg_read(dsc, REG_PHY_TX_PWR); |
143 | tmp = (tmp & ~TX_PWR_MASK) | n; |
144 | atrf_reg_write(dsc, REG_PHY_TX_PWR, tmp); |
145 | atrf_reg_write(dsc, REG_TRX_CTRL_1, |
146 | crc ? TX_AUTO_CRC_ON : 0); |
147 | break; |
148 | default: |
149 | abort(); |
150 | } |
151 | } |
152 | |
153 | |
154 | static void receive_message(struct atrf_dsc *dsc) |
155 | { |
156 | uint8_t buf[MAX_PSDU+1]; /* PSDU+LQI */ |
157 | int n, ok, i; |
158 | uint8_t ed, lqi; |
159 | |
160 | fprintf(stderr, "Ready.\n"); |
161 | wait_for_interrupt(dsc, IRQ_TRX_END, IRQ_TRX_END | IRQ_RX_START, |
162 | 10, 0); |
163 | if (!run) |
164 | return; |
165 | |
166 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
167 | if (n < 0) |
168 | exit(1); |
169 | if (n < 3) { |
170 | fprintf(stderr, "%d bytes received\n", n); |
171 | exit(1); |
172 | } |
173 | ed = atrf_reg_read(dsc, REG_PHY_ED_LEVEL); |
174 | ok = !!(atrf_reg_read(dsc, REG_PHY_RSSI) & RX_CRC_VALID); |
175 | lqi = buf[n-1]; |
176 | fprintf(stderr, "%d bytes payload, CRC %s, LQI %u, ED %d dBm\n", |
177 | n-3, ok ? "OK" : "BAD", lqi, -91+ed); |
178 | for (i = 0; i != n-3; i++) |
179 | putchar(buf[i] < ' ' || buf[i] > '~' ? '?' : buf[i]); |
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, |
241 | 10, 0); |
242 | if (!run) |
243 | break; |
244 | gettimeofday(&now, NULL); |
245 | n = atrf_buf_read(dsc, buf, sizeof(buf)); |
246 | if (n < 0) |
247 | exit(1); |
248 | if (n < 2) { |
249 | fprintf(stderr, "%d bytes received\n", n); |
250 | continue; |
251 | } |
252 | write_pcap_rec(file, &now, buf, n-1); |
253 | (void) write(2, ".", 1); |
254 | count++; |
255 | } |
256 | if (fclose(file) == EOF) { |
257 | perror(name); |
258 | exit(1); |
259 | } |
260 | fprintf(stderr, "%sreceived %d message%s\n", count ? "\n" : "", |
261 | count, count == 1 ? "" : "s"); |
262 | } |
263 | |
264 | |
265 | static void receive(struct atrf_dsc *dsc, const char *name) |
266 | { |
267 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_RX_ON); |
268 | /* |
269 | * 180 us, according to AVR2001 section 4.2. We time out after |
270 | * nominally 200 us. |
271 | */ |
272 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
273 | |
274 | if (name) |
275 | receive_pcap(dsc, name); |
276 | else |
277 | receive_message(dsc); |
278 | } |
279 | |
280 | |
281 | static void transmit(struct atrf_dsc *dsc, const char *msg, int times) |
282 | { |
283 | uint8_t buf[MAX_PSDU]; |
284 | |
285 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
286 | /* |
287 | * 180 us, according to AVR2001 section 4.3. We time out after |
288 | * nominally 200 us. |
289 | */ |
290 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
291 | |
292 | /* |
293 | * We need to copy the message to append the CRC placeholders. |
294 | */ |
295 | strcpy((void *) buf, msg); |
296 | atrf_buf_write(dsc, buf, strlen(msg)+2); |
297 | |
298 | while (run && times--) { |
299 | /* @@@ should wait for clear channel */ |
300 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
301 | |
302 | /* wait up to 10 ms (nominally) */ |
303 | wait_for_interrupt(dsc, IRQ_TRX_END, |
304 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
305 | } |
306 | } |
307 | |
308 | |
309 | static void enter_test_mode_230(struct atrf_dsc *dsc, uint8_t cont_tx) |
310 | { |
311 | atrf_buf_write(dsc, "", 1); |
312 | atrf_reg_write(dsc, REG_CONT_TX_0, CONT_TX_MAGIC); |
313 | atrf_reg_write(dsc, REG_CONT_TX_1, cont_tx); |
314 | |
315 | if (!atrf_test_mode(dsc)) { |
316 | atrf_reset_rf(dsc); |
317 | fprintf(stderr, "device does not support test mode\n"); |
318 | exit(1); |
319 | } |
320 | |
321 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
322 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
323 | |
324 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
325 | } |
326 | |
327 | |
328 | static void enter_test_mode_231(struct atrf_dsc *dsc, uint8_t cont_tx) |
329 | { |
330 | uint8_t buf[127]; |
331 | uint8_t status; |
332 | |
333 | switch (cont_tx) { |
334 | case CONT_TX_M2M: |
335 | fprintf(stderr, |
336 | "-2 MHz mode is not supported by the AT86RF231\n"); |
337 | atrf_close(dsc); |
338 | exit(1); |
339 | case CONT_TX_M500K: |
340 | memset(buf, 0, sizeof(buf)); |
341 | break; |
342 | case CONT_TX_P500K: |
343 | memset(buf, 0xff, sizeof(buf)); |
344 | break; |
345 | default: |
346 | abort(); |
347 | } |
348 | |
349 | atrf_reg_write(dsc, REG_IRQ_MASK, IRQ_PLL_LOCK); /* 2 */ |
350 | atrf_reg_write(dsc, REG_TRX_CTRL_1, 0); /* 3 */ |
351 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_FORCE_TRX_OFF); /* 4 */ |
352 | /* deleted step 5 - we don't need to enable CLKM */ |
353 | |
354 | status = atrf_reg_read(dsc, REG_TRX_STATUS) & TRX_STATUS_MASK; /* 8 */ |
355 | if (status != TRX_STATUS_TRX_OFF) { |
356 | fprintf(stderr, "expected status 0x%02x, got 0x%02x\n", |
357 | TRX_STATUS_TRX_OFF, status); |
358 | exit(1); |
359 | } |
360 | |
361 | atrf_reg_write(dsc, REG_CONT_TX_0, CONT_TX_MAGIC); /* 9 */ |
362 | atrf_reg_write(dsc, REG_TRX_CTRL_2, OQPSK_DATA_RATE_2000); /*10 */ |
363 | atrf_reg_write(dsc, REG_RX_CTRL, 0xa7); /*11 */ |
364 | |
365 | atrf_buf_write(dsc, buf, sizeof(buf)); /*12 */ |
366 | |
367 | atrf_reg_write(dsc, REG_PART_NUM, 0x54); /*13 */ |
368 | atrf_reg_write(dsc, REG_PART_NUM, 0x46); /*14 */ |
369 | |
370 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); /*15 */ |
371 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 0); /*16 */ |
372 | |
373 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); /*17 */ |
374 | } |
375 | |
376 | |
377 | static void transmit_pattern(struct atrf_dsc *dsc, double pause_s, int times) |
378 | { |
379 | uint8_t buf[MAX_PSDU]; |
380 | uint8_t n = 0; |
381 | int us = fmod(pause_s, 1)*1000000; |
382 | |
383 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_PLL_ON); |
384 | /* |
385 | * 180 us, according to AVR2001 section 4.3. We time out after |
386 | * nominally 200 us. |
387 | */ |
388 | wait_for_interrupt(dsc, IRQ_PLL_LOCK, IRQ_PLL_LOCK, 10, 20); |
389 | |
390 | while (run) { |
391 | memset(buf, n, sizeof(buf)); |
392 | atrf_buf_write(dsc, buf, sizeof(buf)); |
393 | |
394 | atrf_reg_write(dsc, REG_TRX_STATE, TRX_CMD_TX_START); |
395 | |
396 | /* wait up to 10 ms (nominally) */ |
397 | wait_for_interrupt(dsc, IRQ_TRX_END, |
398 | IRQ_TRX_END | IRQ_PLL_LOCK, 10, 1000); |
399 | |
400 | if (pause_s >= 1) |
401 | sleep(pause_s); |
402 | if (us) |
403 | usleep(us); |
404 | |
405 | if (times && !--times) |
406 | break; |
407 | n++; |
408 | } |
409 | } |
410 | |
411 | |
412 | static int test_mode(struct atrf_dsc *dsc, uint8_t cont_tx, const char *cmd) |
413 | { |
414 | int status = 0; |
415 | |
416 | switch (atrf_identify(dsc)) { |
417 | case artf_at86rf230: |
418 | enter_test_mode_230(dsc, cont_tx); |
419 | break; |
420 | case artf_at86rf231: |
421 | enter_test_mode_231(dsc, cont_tx); |
422 | break; |
423 | default: |
424 | abort(); |
425 | } |
426 | |
427 | if (cmd) |
428 | status = system(cmd); |
429 | else { |
430 | while (run) |
431 | sleep(1); |
432 | } |
433 | |
434 | if (atrf_identify(dsc) == artf_at86rf231) |
435 | atrf_reg_write(dsc, REG_PART_NUM, 0); |
436 | |
437 | atrf_reset_rf(dsc); |
438 | |
439 | return status; |
440 | } |
441 | |
442 | |
443 | static void die(int sig) |
444 | { |
445 | run = 0; |
446 | } |
447 | |
448 | |
449 | static void usage(const char *name) |
450 | { |
451 | fprintf(stderr, |
452 | "usage: %s [common_options] [message [repetitions]]\n" |
453 | " %s [common_options] -B pause_s [repetitions]\n" |
454 | " %s [common_options] -T offset [command]\n\n" |
455 | " text message mode:\n" |
456 | " message message string to send (if absent, receive)\n" |
457 | " repetitions number of times the message is sent (default 1)\n\n" |
458 | " BER test mode (transmit only):\n" |
459 | " -B pause_s seconds to pause between frames (floating-point)\n" |
460 | " repetitions number of messages to send (default: infinite)\n\n" |
461 | " constant wave test mode (transmit only):\n" |
462 | " -T offset test mode. offset is the frequency offset of the constant\n" |
463 | " wave in MHz: -2, -0.5, or +0.5\n" |
464 | " command shell command to run while transmitting (default: wait for\n" |
465 | " SIGINT instead)\n\n" |
466 | " common options: [-c channel|-f freq] [-C mhz] [-o file] [-p power] " |
467 | "[-t trim]\n" |
468 | " -c channel channel number, 11 to 26 (default %d)\n" |
469 | " -C mhz output clock at 1, 2, 4, 8, or 16 MHz (default: off)\n" |
470 | " -f freq frequency in MHz, 2405 to 2480 (default %d)\n" |
471 | " -o file write received data to a file in pcap format\n" |
472 | " -p power transmit power, -17.2 to 3.0 dBm (default %.1f)\n" |
473 | " -t trim trim capacitor, 0 to 15 (default 0)\n" |
474 | , name, name, name, DEFAULT_CHANNEL, 2405+5*(DEFAULT_CHANNEL-11), |
475 | DEFAULT_POWER); |
476 | exit(1); |
477 | } |
478 | |
479 | |
480 | int main(int argc, char *const *argv) |
481 | { |
482 | enum { |
483 | mode_msg, |
484 | mode_ber, |
485 | mode_cont_tx, |
486 | } mode = mode_msg; |
487 | int channel = DEFAULT_CHANNEL; |
488 | double power = DEFAULT_POWER; |
489 | int trim = 0, times = 1; |
490 | uint8_t cont_tx = 0; |
491 | double pause_s = 0; |
492 | char *end; |
493 | int c, freq; |
494 | unsigned tmp, clkm = 0; |
495 | int status = 0; |
496 | const char *pcap_file = NULL; |
497 | struct atrf_dsc *dsc; |
498 | |
499 | while ((c = getopt(argc, argv, "B:c:C:f:o:p:t:T:")) != EOF) |
500 | switch (c) { |
501 | case 'c': |
502 | channel = strtoul(optarg, &end, 0); |
503 | if (*end) |
504 | usage(*argv); |
505 | if (channel < 11 || channel > 26) |
506 | usage(*argv); |
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 't': |
527 | trim = strtoul(optarg, &end, 0); |
528 | if (*end) |
529 | usage(*argv); |
530 | if (trim > 15) |
531 | usage(*argv); |
532 | break; |
533 | case 'B': |
534 | mode = mode_ber; |
535 | pause_s = strtof(optarg, &end); |
536 | if (*end) |
537 | usage(*argv); |
538 | break; |
539 | case 'C': |
540 | tmp = strtol(optarg, &end, 0); |
541 | if (*end) |
542 | usage(*argv); |
543 | if (!tmp) |
544 | usage(*argv); |
545 | for (clkm = 1; !(tmp & 1); tmp >>= 1) |
546 | clkm++; |
547 | if (tmp != 1 || clkm > 5) |
548 | usage(*argv); |
549 | break; |
550 | case 'T': |
551 | mode = mode_cont_tx; |
552 | if (!strcmp(optarg, "-2")) |
553 | cont_tx = CONT_TX_M2M; |
554 | else if (!strcmp(optarg, "-0.5")) |
555 | cont_tx = CONT_TX_M500K; |
556 | else if (!strcmp(optarg, "+0.5")) |
557 | cont_tx = CONT_TX_P500K; |
558 | else |
559 | usage(*argv); |
560 | break; |
561 | default: |
562 | usage(*argv); |
563 | } |
564 | |
565 | signal(SIGINT, die); |
566 | |
567 | switch (argc-optind) { |
568 | case 0: |
569 | dsc = init_txrx(trim, clkm); |
570 | set_channel(dsc, channel); |
571 | switch (mode) { |
572 | case mode_msg: |
573 | receive(dsc, pcap_file); |
574 | break; |
575 | case mode_ber: |
576 | set_power(dsc, power, 0); |
577 | transmit_pattern(dsc, pause_s, 0); |
578 | break; |
579 | case mode_cont_tx: |
580 | set_power(dsc, power, 0); |
581 | status = test_mode(dsc, cont_tx, NULL); |
582 | break; |
583 | default: |
584 | abort(); |
585 | } |
586 | break; |
587 | case 2: |
588 | switch (mode) { |
589 | case mode_msg: |
590 | break; |
591 | case mode_ber: |
592 | /* fall through */ |
593 | case mode_cont_tx: |
594 | usage(*argv); |
595 | default: |
596 | abort(); |
597 | } |
598 | times = strtoul(argv[optind+1], &end, 0); |
599 | if (*end) |
600 | usage(*argv); |
601 | /* fall through */ |
602 | case 1: |
603 | dsc = init_txrx(trim, clkm); |
604 | set_channel(dsc, channel); |
605 | switch (mode) { |
606 | case mode_msg: |
607 | set_power(dsc, power, 1); |
608 | transmit(dsc, argv[optind], times); |
609 | break; |
610 | case mode_ber: |
611 | times = strtoul(argv[optind+1], &end, 0); |
612 | if (*end) |
613 | usage(*argv); |
614 | set_power(dsc, power, 0); |
615 | transmit_pattern(dsc, pause_s, times); |
616 | break; |
617 | case mode_cont_tx: |
618 | set_power(dsc, power, 0); |
619 | status = test_mode(dsc, cont_tx, argv[optind]); |
620 | break; |
621 | default: |
622 | abort(); |
623 | |
624 | } |
625 | break; |
626 | default: |
627 | usage(*argv); |
628 | } |
629 | |
630 | atrf_close(dsc); |
631 | |
632 | if (status) { |
633 | if (WIFEXITED(status)) |
634 | return WEXITSTATUS(status); |
635 | if (WIFSIGNALED(status)) |
636 | raise(WTERMSIG(status)); |
637 | fprintf(stderr, "unexpected exit status %d\n", status); |
638 | abort(); |
639 | } |
640 | return 0; |
641 | } |
642 |