Root/atusb/fw/mac.c

Source at commit 0ff0c31c3ff24d40c5ee4d2391cb63f83e2b6e7f created 7 years 3 months ago.
By Werner Almesberger, atusb/Makefile (f, b, upload): convenience targets
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/* ----- Register access --------------------------------------------------- */
51
52
53static uint8_t reg_read(uint8_t reg)
54{
55    uint8_t value;
56
57    spi_begin();
58    spi_send(AT86RF230_REG_READ | reg);
59    value = spi_recv();
60    spi_end();
61
62    return value;
63}
64
65
66static void reg_write(uint8_t reg, uint8_t value)
67{
68    spi_begin();
69    spi_send(AT86RF230_REG_WRITE | reg);
70    spi_send(value);
71    spi_end();
72}
73
74
75/* ----- Interrupt handling ------------------------------------------------ */
76
77
78static void rx_done(void *user);
79static void tx_ack_done(void *user);
80
81
82static void usb_next(void)
83{
84    const uint8_t *buf;
85
86    if (rx_in != rx_out) {
87        buf = rx_buf[rx_out];
88        led(1);
89        usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
90    }
91
92    if (queued_tx_ack) {
93        usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
94        queued_tx_ack = 0;
95    }
96}
97
98
99static void tx_ack_done(void *user)
100{
101    usb_next();
102}
103
104static void change_state(uint8_t new)
105{
106    while ((reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK) ==
107        TRX_STATUS_TRANSITION);
108    reg_write(REG_TRX_STATE, new);
109}
110
111static void rx_done(void *user)
112{
113    led(0);
114    next_buf(&rx_out);
115    usb_next();
116#ifdef AT86RF230
117    /* slap at86rf230 - reduce fragmentation issue */
118    change_state(TRX_STATUS_RX_AACK_ON);
119#endif
120}
121
122
123static void receive_frame(void)
124{
125    uint8_t size;
126    uint8_t *buf;
127
128    spi_begin();
129    spi_io(AT86RF230_BUF_READ);
130
131    size = spi_recv();
132    if (!size || (size & 0x80)) {
133        spi_end();
134        return;
135    }
136
137    buf = rx_buf[rx_in];
138    spi_recv_block(buf+1, size+1);
139    spi_end();
140
141    buf[0] = size;
142    next_buf(&rx_in);
143
144    if (eps[1].state == EP_IDLE)
145        usb_next();
146}
147
148
149static bool handle_irq(void)
150{
151    uint8_t irq;
152
153    irq = reg_read(REG_IRQ_STATUS);
154    if (!(irq & IRQ_TRX_END))
155        return 1;
156
157    if (txing) {
158        if (eps[1].state == EP_IDLE) {
159            usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
160        } else {
161            queued_tx_ack = 1;
162            queued_seq = this_seq;
163        }
164        txing = 0;
165        return 1;
166    }
167
168    /* likely */
169    if (eps[1].state == EP_IDLE || rx_in != rx_out)
170        receive_frame();
171
172    return 1;
173}
174
175
176/* ----- TX/RX ------------------------------------------------------------- */
177
178
179bool mac_rx(int on)
180{
181    if (on) {
182        mac_irq = handle_irq;
183        reg_read(REG_IRQ_STATUS);
184        change_state(TRX_CMD_RX_AACK_ON);
185    } else {
186        mac_irq = NULL;
187        change_state(TRX_CMD_FORCE_TRX_OFF);
188        txing = 0;
189    }
190    return 1;
191}
192
193
194static void do_tx(void *user)
195{
196    uint16_t timeout = 0xffff;
197    uint8_t status;
198    uint8_t i;
199
200    /*
201     * If we time out here, the host driver will time out waiting for the
202     * TRX_END acknowledgement.
203     */
204    do {
205        if (!--timeout)
206            return;
207        status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
208    }
209    while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
210
211#ifdef AT86RF231
212    /*
213     * We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
214     * reception may have begun while we were still working on the previous
215     * one.
216     */
217    reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
218#endif
219#ifdef AT86RF230
220    /*
221     * at86rf230 doesn't support force change, nevetherless this works
222     * somehow
223     */
224    reg_write(REG_TRX_STATE, TRX_CMD_PLL_ON);
225#endif
226
227    handle_irq();
228
229    spi_begin();
230    spi_send(AT86RF230_BUF_WRITE);
231    spi_send(tx_size+2); /* CRC */
232    for (i = 0; i != tx_size; i++)
233        spi_send(tx_buf[i]);
234    spi_end();
235
236    change_state(TRX_STATUS_TX_ARET_ON);
237
238    slp_tr();
239
240    txing = 1;
241    this_seq = next_seq;
242
243    /*
244     * Wait until we reach BUSY_TX_ARET, so that we command the transition to
245     * RX_AACK_ON which will be executed upon TX completion.
246     */
247    change_state(TRX_CMD_PLL_ON);
248    change_state(TRX_CMD_RX_AACK_ON);
249}
250
251
252bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
253{
254    if (len > MAX_PSDU)
255        return 0;
256    tx_size = len;
257    next_seq = seq;
258    usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
259    return 1;
260}
261
262
263void mac_reset(void)
264{
265    mac_irq = NULL;
266    txing = 0;
267    queued_tx_ack = 0;
268    rx_in = rx_out = 0;
269    next_seq = this_seq = queued_seq = 0;
270
271    /* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
272    reg_write(REG_TRX_CTRL_1,
273        TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
274}
275

Archive Download this file



interactive