Root/tools/atrf-txrx/atrf-txrx.c

Source at commit f9aee543234110dba3d0a2104024a668a1bc32ae created 9 years 8 months ago.
By Werner Almesberger, atrf-txrx: added ability to record received frames in pcap format
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
47static 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
55static 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
63static volatile int run = 1;
64
65
66/*
67 * clkm: 0 disable CLKM
68 * >0 output 2^(clkm-1) MHz signal
69 */
70
71static 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
107static 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
113static 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
154static 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
184static void write_pcap_hdr(FILE *file)
185{
186    struct pcap_file_header hdr = {
187        .magic = 0xa1b2c3d4,
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
203static 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
224static 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
265static 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
281static 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
309static 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
328static 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
378static int test_mode(struct atrf_dsc *dsc, uint8_t cont_tx, const char *cmd)
379{
380    int status = 0;
381
382    switch (atrf_identify(dsc)) {
383    case artf_at86rf230:
384        enter_test_mode_230(dsc, cont_tx);
385        break;
386    case artf_at86rf231:
387        enter_test_mode_231(dsc, cont_tx);
388        break;
389    default:
390        abort();
391    }
392
393    if (cmd)
394        status = system(cmd);
395    else {
396        while (run)
397            sleep(1);
398    }
399
400    if (atrf_identify(dsc) == artf_at86rf231)
401        atrf_reg_write(dsc, REG_PART_NUM, 0);
402    
403    atrf_reset_rf(dsc);
404
405    return status;
406}
407
408
409static void die(int sig)
410{
411    run = 0;
412}
413
414
415static void usage(const char *name)
416{
417    fprintf(stderr,
418"usage: %s [common_options] [message [repetitions]]\n"
419" %s [common_options] -T offset [command]\n\n"
420" text message mode:\n"
421" message message string to send (if absent, receive)\n"
422" repetitions number of times the message is sent (default 1)\n\n"
423" constant wave test mode (transmit only):\n"
424" -T offset test mode. offset is the frequency offset of the constant\n"
425" wave in MHz: -2, -0.5, or +0.5\n"
426" command shell command to run while transmitting (default: wait for\n"
427" SIGINT instead)\n\n"
428" common options: [-c channel|-f freq] [-C mhz] [-o file] [-p power] "
429"[-t trim]\n"
430" -c channel channel number, 11 to 26 (default %d)\n"
431" -C mhz output clock at 1, 2, 4, 8, or 16 MHz (default: off)\n"
432" -f freq frequency in MHz, 2405 to 2480 (default %d)\n"
433" -o file write received data to a file in pcap format\n"
434" -p power transmit power, -17.2 to 3.0 dBm (default %.1f)\n"
435" -t trim trim capacitor, 0 to 15 (default 0)\n"
436        , name, name, DEFAULT_CHANNEL, 2405+5*(DEFAULT_CHANNEL-11),
437        DEFAULT_POWER);
438    exit(1);
439}
440
441
442int main(int argc, char *const *argv)
443{
444    int channel = DEFAULT_CHANNEL;
445    double power = DEFAULT_POWER;
446    int trim = 0, times = 1;
447    uint8_t cont_tx = 0;
448    char *end;
449    int c, freq;
450    unsigned tmp, clkm = 0;
451    int status = 0;
452    const char *pcap_file = NULL;
453    struct atrf_dsc *dsc;
454
455    while ((c = getopt(argc, argv, "c:C:f:o:p:t:T:")) != EOF)
456        switch (c) {
457        case 'c':
458            channel = strtoul(optarg, &end, 0);
459            if (*end)
460                usage(*argv);
461            if (channel < 11 || channel > 26)
462                usage(*argv);
463            break;
464        case 'f':
465            freq = strtoul(optarg, &end, 0);
466            if (*end)
467                usage(*argv);
468            if (freq % 5)
469                usage(*argv);
470            channel = (freq-2405)/5+11;
471            if (channel < 11 || channel > 26)
472                usage(*argv);
473            break;
474        case 'o':
475            pcap_file = optarg;
476            break;
477        case 'p':
478            power = strtod(optarg, &end);
479            if (*end)
480                usage(*argv);
481            break;
482        case 't':
483            trim = strtoul(optarg, &end, 0);
484            if (*end)
485                usage(*argv);
486            if (trim > 15)
487                usage(*argv);
488            break;
489        case 'C':
490            tmp = strtol(optarg, &end, 0);
491            if (*end)
492                usage(*argv);
493            if (!tmp)
494                usage(*argv);
495            for (clkm = 1; !(tmp & 1); tmp >>= 1)
496                clkm++;
497            if (tmp != 1 || clkm > 5)
498                usage(*argv);
499            break;
500        case 'T':
501            if (!strcmp(optarg, "-2"))
502                cont_tx = CONT_TX_M2M;
503            else if (!strcmp(optarg, "-0.5"))
504                cont_tx = CONT_TX_M500K;
505            else if (!strcmp(optarg, "+0.5"))
506                cont_tx = CONT_TX_P500K;
507            else
508                usage(*argv);
509            break;
510        default:
511            usage(*argv);
512        }
513
514    signal(SIGINT, die);
515
516    switch (argc-optind) {
517    case 0:
518        dsc = init_txrx(trim, clkm);
519        set_channel(dsc, channel);
520        if (!cont_tx)
521            receive(dsc, pcap_file);
522        else {
523            set_power(dsc, power, 0);
524            status = test_mode(dsc, cont_tx, NULL);
525        }
526        break;
527    case 2:
528        if (cont_tx)
529            usage(*argv);
530        times = strtoul(argv[optind+1], &end, 0);
531        if (*end)
532            usage(*argv);
533        /* fall through */
534    case 1:
535        dsc = init_txrx(trim, clkm);
536        set_channel(dsc, channel);
537        if (!cont_tx) {
538            set_power(dsc, power, 1);
539            transmit(dsc, argv[optind], times);
540        } else {
541            set_power(dsc, power, 0);
542            status = test_mode(dsc, cont_tx, argv[optind]);
543        }
544        break;
545    default:
546        usage(*argv);
547    }
548
549    atrf_close(dsc);
550
551    if (status) {
552        if (WIFEXITED(status))
553            return WEXITSTATUS(status);
554        if (WIFSIGNALED(status))
555            raise(WTERMSIG(status));
556        fprintf(stderr, "unexpected exit status %d\n", status);
557        abort();
558    }
559    return 0;
560}
561

Archive Download this file



interactive