Root/atusb/fw/mac.c

Source at commit bf067b3e73b0c766449fb36eae51c12523290af0 created 6 years 4 months ago.
By Werner Almesberger, atusb/fw/mac.c (RX_BUFS): increase buffer size from 2 to 3 frames
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
25#define RX_BUFS 3
26
27
28bool (*mac_irq)(void) = NULL;
29
30
31static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
32static uint8_t tx_buf[MAX_PSDU];
33static uint8_t tx_size = 0;
34static bool txing = 0;
35static bool queued_tx_ack = 0;
36static uint8_t next_seq, this_seq, queued_seq;
37
38
39/* ----- Receive buffer management ----------------------------------------- */
40
41
42static uint8_t rx_in = 0, rx_out = 0;
43
44
45static inline void next_buf(uint8_t *index)
46{
47    *index = (*index+1) % RX_BUFS;
48}
49
50
51/* ----- Register access --------------------------------------------------- */
52
53
54static uint8_t reg_read(uint8_t reg)
55{
56    uint8_t value;
57
58    spi_begin();
59    spi_send(AT86RF230_REG_READ | reg);
60    value = spi_recv();
61    spi_end();
62
63    return value;
64}
65
66
67static void reg_write(uint8_t reg, uint8_t value)
68{
69    spi_begin();
70    spi_send(AT86RF230_REG_WRITE | reg);
71    spi_send(value);
72    spi_end();
73}
74
75
76/* ----- Interrupt handling ------------------------------------------------ */
77
78
79static void rx_done(void *user);
80static void tx_ack_done(void *user);
81
82
83static void usb_next(void)
84{
85    const uint8_t *buf;
86
87    if (rx_in != rx_out) {
88        buf = rx_buf[rx_out];
89        led(1);
90        usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
91    }
92
93    if (queued_tx_ack) {
94        usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
95        queued_tx_ack = 0;
96    }
97}
98
99
100static void tx_ack_done(void *user)
101{
102    usb_next();
103}
104
105
106static void rx_done(void *user)
107{
108    led(0);
109    next_buf(&rx_out);
110    usb_next();
111}
112
113
114static void receive_frame(void)
115{
116    uint8_t size;
117    uint8_t *buf;
118
119    spi_begin();
120    if (!(spi_io(AT86RF230_BUF_READ) & RX_CRC_VALID)) {
121        spi_end();
122        return;
123    }
124    size = spi_recv();
125    if (!size || (size & 0x80)) {
126        spi_end();
127        return;
128    }
129
130    buf = rx_buf[rx_in];
131    spi_recv_block(buf+1, size+1);
132    spi_end();
133
134    buf[0] = size;
135    next_buf(&rx_in);
136
137    if (eps[1].state == EP_IDLE)
138        usb_next();
139}
140
141
142static bool handle_irq(void)
143{
144    uint8_t irq;
145
146    irq = reg_read(REG_IRQ_STATUS);
147    if (!(irq & IRQ_TRX_END))
148        return 1;
149
150    if (txing) {
151        if (eps[1].state == EP_IDLE) {
152            usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
153        } else {
154            queued_tx_ack = 1;
155            queued_seq = this_seq;
156        }
157        txing = 0;
158        return 1;
159    }
160
161    /* likely */
162    if (eps[1].state == EP_IDLE || rx_in != rx_out)
163        receive_frame();
164
165    return 1;
166}
167
168
169/* ----- TX/RX ------------------------------------------------------------- */
170
171
172static void change_state(uint8_t new)
173{
174    while ((reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK) ==
175        TRX_STATUS_TRANSITION);
176    reg_write(REG_TRX_STATE, new);
177}
178
179
180bool mac_rx(int on)
181{
182    if (on) {
183        mac_irq = handle_irq;
184        reg_read(REG_IRQ_STATUS);
185        change_state(TRX_CMD_RX_ON);
186    } else {
187        mac_irq = NULL;
188        change_state(TRX_CMD_FORCE_TRX_OFF);
189        txing = 0;
190    }
191    return 1;
192}
193
194
195static void do_tx(void *user)
196{
197    uint16_t timeout = 0xffff;
198    uint8_t status;
199    uint8_t i;
200
201    /*
202     * If we time out here, the host driver will time out waiting for the
203     * TRX_END acknowledgement.
204     */
205    do {
206        if (!--timeout)
207            return;
208        status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
209    }
210    while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
211
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
219    handle_irq();
220
221    spi_begin();
222    spi_send(AT86RF230_BUF_WRITE);
223    spi_send(tx_size+2); /* CRC */
224    for (i = 0; i != tx_size; i++)
225        spi_send(tx_buf[i]);
226    spi_end();
227
228    slp_tr();
229
230    txing = 1;
231    this_seq = next_seq;
232
233    /*
234     * Wait until we reach BUSY_TX, so that we command the transition to
235     * RX_ON which will be executed upon TX completion.
236     */
237    change_state(TRX_CMD_RX_ON);
238}
239
240
241bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
242{
243    if (len > MAX_PSDU)
244        return 0;
245    tx_size = len;
246    next_seq = seq;
247    usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
248    return 1;
249}
250
251
252void mac_reset(void)
253{
254    mac_irq = NULL;
255    txing = 0;
256    queued_tx_ack = 0;
257    rx_in = rx_out = 0;
258    next_seq = this_seq = queued_seq = 0;
259
260    /* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
261    reg_write(REG_TRX_CTRL_1,
262        TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
263}
264

Archive Download this file



interactive