Add firmware for the ATUSB IEEE 802.15.4 USB Adapter
[linux-libre-firmware.git] / atusb / 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
27 bool (*mac_irq)(void) = NULL;
28
29
30 static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
31 static uint8_t tx_buf[MAX_PSDU];
32 static uint8_t tx_size = 0;
33 static bool txing = 0;
34 static bool queued_tx_ack = 0;
35 static uint8_t next_seq, this_seq, queued_seq;
36
37
38 /* ----- Receive buffer management ----------------------------------------- */
39
40
41 static uint8_t rx_in = 0, rx_out = 0;
42
43
44 static inline void next_buf(uint8_t *index)
45 {
46         *index = (*index+1) % RX_BUFS;
47 }
48
49
50 /* ----- Interrupt handling ------------------------------------------------ */
51
52
53 static void rx_done(void *user);
54 static void tx_ack_done(void *user);
55
56
57 static 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
74 static void tx_ack_done(void *user)
75 {
76         usb_next();
77 }
78
79 static 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
91 static 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
117 static 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
147 bool 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
162 static 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
228 bool 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
239 void 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 }