altos: Mostly working SPI radio link
[fw/altos] / src / drivers / ao_radio_master.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_radio_spi.h>
20 #include <ao_exti.h>
21 #include <ao_radio_cmac.h>
22
23 static __xdata struct ao_radio_spi_reply        ao_radio_spi_reply;
24 static __xdata struct ao_radio_spi_request      ao_radio_spi_request;
25 static volatile __xdata uint8_t                 ao_radio_done = 1;
26 static __xdata uint8_t                          ao_radio_mutex;
27
28 __xdata int8_t                                  ao_radio_cmac_rssi;
29
30 #if 0
31 #define PRINTD(...) do { printf ("\r%s: ", __func__); printf(__VA_ARGS__); flush(); } while(0)
32 #else
33 #define PRINTD(...) 
34 #endif
35
36 static void
37 ao_radio_isr(void)
38 {
39         ao_exti_disable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
40         ao_radio_done = 1;
41         ao_wakeup((void *) &ao_radio_done);
42 }
43
44 static void
45 ao_radio_master_delay(void)
46 {
47 //      uint16_t        i;
48 //      for (i = 0; i < 1000; i++)
49 //              ao_arch_nop();
50         ao_delay(1);
51 }
52
53 static void
54 ao_radio_master_start(void)
55 {
56         ao_radio_master_delay();
57         ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
58                        AO_RADIO_SPI_BUS,
59                        AO_SPI_SPEED_200kHz);
60 }
61
62 static void
63 ao_radio_master_stop(void)
64 {
65         ao_spi_put_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
66                        AO_RADIO_SPI_BUS);
67 //      ao_delay(1);
68 }
69
70 static uint8_t
71 ao_radio_master_send(void)
72 {
73         if (!ao_radio_done)
74                 printf ("radio not done in ao_radio_master_send\n");
75         PRINTD("send %d\n", ao_radio_spi_request.len);
76         ao_radio_done = 0;
77         ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
78         ao_radio_master_start();
79         ao_spi_send(&ao_radio_spi_request,
80                     ao_radio_spi_request.len,
81                     AO_RADIO_SPI_BUS);
82         ao_radio_master_stop();
83         cli();
84         while (!ao_radio_done)
85                 if (ao_sleep((void *) &ao_radio_done)) {
86                         printf ("ao_radio_master awoken\n");
87                         break;
88                 }
89         sei();
90         PRINTD ("sent, radio done %d\n", ao_radio_done);
91         if (!ao_radio_done)
92                 printf ("radio didn't finish after ao_radio_master_send\n");
93         return ao_radio_done;
94 }
95
96 static void
97 ao_radio_get(uint8_t req, uint8_t len)
98 {
99         ao_config_get();
100         ao_mutex_get(&ao_radio_mutex);
101         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN + len;
102         ao_radio_spi_request.request = req;
103         ao_radio_spi_request.setting = ao_config.radio_setting;
104 }
105
106 static void
107 ao_radio_put(void)
108 {
109         ao_mutex_put(&ao_radio_mutex);
110 }
111
112 static void
113 ao_radio_get_data(__xdata void *d, uint8_t size)
114 {
115         uint8_t ret;
116
117         PRINTD ("send fetch req\n");
118         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
119         ao_radio_spi_request.request = AO_RADIO_SPI_RECV_FETCH;
120         ao_radio_spi_request.recv_len = size;
121         ret = ao_radio_master_send();
122         PRINTD ("fetch req sent %d\n", ret);
123
124         PRINTD ("fetch\n");
125         ao_radio_master_start();
126         ao_spi_recv(&ao_radio_spi_reply,
127                     AO_RADIO_SPI_REPLY_HEADER_LEN + size,
128                     AO_RADIO_SPI_BUS);
129         ao_radio_master_stop();
130         ao_xmemcpy(d, ao_radio_spi_reply.payload, size);
131         PRINTD ("fetched %d\n", size);
132 }
133
134 void
135 ao_radio_recv_abort(void)
136 {
137         ao_radio_get(AO_RADIO_SPI_RECV_ABORT, 0);
138         ao_radio_master_send();
139         ao_radio_put();
140 }
141
142 void
143 ao_radio_send(const void *d, uint8_t size)
144 {
145         ao_radio_get(AO_RADIO_SPI_SEND, size);
146         ao_xmemcpy(&ao_radio_spi_request.payload, d, size);
147         ao_radio_master_send();
148         ao_radio_put();
149 }
150
151
152 uint8_t
153 ao_radio_recv(__xdata void *d, uint8_t size)
154 {
155         int8_t  ret;
156         uint8_t recv;
157
158         /* Recv the data
159          */
160         
161         ao_radio_get(AO_RADIO_SPI_RECV, 0);
162         ao_radio_spi_request.recv_len = size;
163         recv = ao_radio_master_send();
164         if (!recv) {
165                 ao_radio_put();
166                 ao_radio_recv_abort();
167                 return 0;
168         }
169         ao_radio_get_data(d, size);
170
171         recv = ao_radio_spi_reply.status;
172
173         ao_radio_put();
174
175         return recv;
176 }
177
178 int8_t
179 ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
180 {
181         if (len > AO_CMAC_MAX_LEN)
182                 return AO_RADIO_CMAC_LEN_ERROR;
183
184         PRINTD ("cmac_send: send %d\n", len);
185         /* Set the key.
186          */
187         PRINTD ("set key\n");
188         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
189         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
190         ao_radio_master_send();
191         ao_radio_put();
192         PRINTD ("key set\n");
193
194         /* Send the data
195          */
196         
197         PRINTD ("sending packet\n");
198         ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len);
199         ao_xmemcpy(&ao_radio_spi_request.payload, packet, len);
200         ao_radio_master_send();
201         ao_radio_put();
202         PRINTD ("packet sent\n");
203         return AO_RADIO_CMAC_OK;
204 }
205
206 int8_t
207 ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
208 {
209         int8_t  ret;
210         int8_t  recv;
211
212         if (len > AO_CMAC_MAX_LEN)
213                 return AO_RADIO_CMAC_LEN_ERROR;
214
215         /* Set the key.
216          */
217         PRINTD ("setting key\n");
218         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
219         ao_radio_spi_request.timeout = timeout;
220         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
221         recv = ao_radio_master_send();
222         ao_radio_put();
223         PRINTD ("key set: %d\n", recv);
224
225         /* Recv the data
226          */
227         PRINTD ("queuing recv\n");
228         ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0);
229         ao_radio_spi_request.recv_len = len;
230         recv = ao_radio_master_send();
231         PRINTD ("recv queued: %d\n", recv);
232         if (!recv) {
233                 ao_radio_put();
234                 ao_radio_recv_abort();
235                 return AO_RADIO_CMAC_TIMEOUT;
236         }
237
238         PRINTD ("fetching data\n");
239         ao_radio_get_data(packet, len);
240         recv = ao_radio_spi_reply.status;
241         ao_radio_cmac_rssi = ao_radio_spi_reply.rssi;
242         ao_radio_put();
243         PRINTD ("data fetched: %d %d\n", recv, ao_radio_cmac_rssi);
244         return recv;
245 }
246
247 static uint8_t  ao_radio_test_on;
248
249 void
250 ao_radio_test(uint8_t on)
251 {
252         if (on) {
253                 if (!ao_radio_test_on) {
254                         ao_radio_get(AO_RADIO_SPI_TEST_ON, 0);
255                         ao_radio_test_on = 1;
256                         ao_radio_master_send();
257                 }
258         } else {
259                 if (ao_radio_test_on) {
260                         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
261                         ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF;
262                         ao_radio_master_send();
263                         ao_radio_test_on = 0;
264                         ao_radio_put();
265                 }
266         }
267 }
268
269 static void
270 ao_radio_test_cmd(void)
271 {
272         uint8_t mode = 2;
273         ao_cmd_white();
274         if (ao_cmd_lex_c != '\n') {
275                 ao_cmd_decimal();
276                 mode = (uint8_t) ao_cmd_lex_u32;
277         }
278         mode++;
279         if ((mode & 2))
280                 ao_radio_test(1);
281         if (mode == 3) {
282                 printf ("Hit a character to stop..."); flush();
283                 getchar();
284                 putchar('\n');
285         }
286         if ((mode & 1))
287                 ao_radio_test(0);
288 }
289
290 __code struct ao_cmds ao_radio_cmds[] = {
291         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
292         { 0,    NULL },
293 };
294
295 void
296 ao_radio_init(void)
297 {
298         ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN));
299
300         ao_enable_port(AO_RADIO_INT_PORT);
301         ao_exti_setup(AO_RADIO_INT_PORT,
302                       AO_RADIO_INT_PIN,
303                       AO_EXTI_MODE_FALLING,
304                       ao_radio_isr);
305         ao_cmd_register(&ao_radio_cmds[0]);
306 }