Root/atusb/fw/mac.c

1/*
2 * fw/mac.c - HardMAC functions
3 *
4 * Written 2011, 2013 by Werner Almesberger
5 * Copyright 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#include <stddef.h>
14#include <stdbool.h>
15#include <stdint.h>
16
17#include "usb.h"
18
19#include "at86rf230.h"
20#include "spi.h"
21#include "board.h"
22#include "mac.h"
23
24#define RX_BUFS 3
25
26
27bool (*mac_irq)(void) = NULL;
28
29
30static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
31static uint8_t tx_buf[MAX_PSDU];
32static uint8_t tx_size = 0;
33static bool txing = 0;
34static bool queued_tx_ack = 0;
35static uint8_t next_seq, this_seq, queued_seq;
36
37
38/* ----- Receive buffer management ----------------------------------------- */
39
40
41static uint8_t rx_in = 0, rx_out = 0;
42
43
44static inline void next_buf(uint8_t *index)
45{
46    *index = (*index+1) % RX_BUFS;
47}
48
49
50/* ----- Interrupt handling ------------------------------------------------ */
51
52
53static void rx_done(void *user);
54static void tx_ack_done(void *user);
55
56
57static void usb_next(void)
58{
59    const uint8_t *buf;
60
61    if (rx_in != rx_out) {
62        buf = rx_buf[rx_out];
63        led(1);
64        usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
65    }
66
67    if (queued_tx_ack) {
68        usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
69        queued_tx_ack = 0;
70    }
71}
72
73
74static void tx_ack_done(void *user)
75{
76    usb_next();
77}
78
79static void rx_done(void *user)
80{
81    led(0);
82    next_buf(&rx_out);
83    usb_next();
84#ifdef AT86RF230
85    /* slap at86rf230 - reduce fragmentation issue */
86    change_state(TRX_STATUS_RX_AACK_ON);
87#endif
88}
89
90
91static void receive_frame(void)
92{
93    uint8_t size;
94    uint8_t *buf;
95
96    spi_begin();
97    spi_io(AT86RF230_BUF_READ);
98
99    size = spi_recv();
100    if (!size || (size & 0x80)) {
101        spi_end();
102        return;
103    }
104
105    buf = rx_buf[rx_in];
106    spi_recv_block(buf+1, size+1);
107    spi_end();
108
109    buf[0] = size;
110    next_buf(&rx_in);
111
112    if (eps[1].state == EP_IDLE)
113        usb_next();
114}
115
116
117static bool handle_irq(void)
118{
119    uint8_t irq;
120
121    irq = reg_read(REG_IRQ_STATUS);
122    if (!(irq & IRQ_TRX_END))
123        return 1;
124
125    if (txing) {
126        if (eps[1].state == EP_IDLE) {
127            usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
128        } else {
129            queued_tx_ack = 1;
130            queued_seq = this_seq;
131        }
132        txing = 0;
133        return 1;
134    }
135
136    /* likely */
137    if (eps[1].state == EP_IDLE || rx_in != rx_out)
138        receive_frame();
139
140    return 1;
141}
142
143
144/* ----- TX/RX ------------------------------------------------------------- */
145
146
147bool mac_rx(int on)
148{
149    if (on) {
150        mac_irq = handle_irq;
151        reg_read(REG_IRQ_STATUS);
152        change_state(TRX_CMD_RX_AACK_ON);
153    } else {
154        mac_irq = NULL;
155        change_state(TRX_CMD_FORCE_TRX_OFF);
156        txing = 0;
157    }
158    return 1;
159}
160
161
162static void do_tx(void *user)
163{
164    uint16_t timeout = 0xffff;
165    uint8_t status;
166    uint8_t i;
167
168    /*
169     * If we time out here, the host driver will time out waiting for the
170     * TRX_END acknowledgement.
171     */
172    do {
173        if (!--timeout)
174            return;
175        status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
176    }
177    while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
178
179#ifdef AT86RF231
180    /*
181     * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
182     * reception may have begun while we were still working on the previous
183     * one.
184     */
185    reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
186#endif
187#ifdef AT86RF230
188    /*
189     * at86rf230 doesn't support force change, nevetherless this works
190     * somehow
191     */
192    reg_write(REG_TRX_STATE, TRX_CMD_PLL_ON);
193#endif
194#ifdef AT86RF212
195    /*
196    * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
197    * reception may have begun while we were still working on the previous
198    * one.
199    */
200    reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
201#endif
202
203    handle_irq();
204
205    spi_begin();
206    spi_send(AT86RF230_BUF_WRITE);
207    spi_send(tx_size+2); /* CRC */
208    for (i = 0; i != tx_size; i++)
209        spi_send(tx_buf[i]);
210    spi_end();
211
212    change_state(TRX_STATUS_TX_ARET_ON);
213
214    slp_tr();
215
216    txing = 1;
217    this_seq = next_seq;
218
219    /*
220     * Wait until we reach BUSY_TX_ARET, so that we command the transition to
221     * RX_AACK_ON which will be executed upon TX completion.
222     */
223    change_state(TRX_CMD_PLL_ON);
224    change_state(TRX_CMD_RX_AACK_ON);
225}
226
227
228bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
229{
230    if (len > MAX_PSDU)
231        return 0;
232    tx_size = len;
233    next_seq = seq;
234    usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
235    return 1;
236}
237
238
239void mac_reset(void)
240{
241    mac_irq = NULL;
242    txing = 0;
243    queued_tx_ack = 0;
244    rx_in = rx_out = 0;
245    next_seq = this_seq = queued_seq = 0;
246
247    /* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
248    reg_write(REG_TRX_CTRL_1,
249        TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
250}
251

Archive Download this file



interactive