altos/cc1200: Adjust bit-sync configuration
[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_wait_mode;
26 static volatile __xdata uint8_t                 ao_radio_done = 0;
27 static volatile __xdata uint8_t                 ao_radio_ready = 1;
28 static __xdata uint8_t                          ao_radio_mutex;
29 static __xdata uint8_t                          ao_radio_aes_seq;
30
31 __xdata int8_t                                  ao_radio_cmac_rssi;
32
33 #if 0
34 #define PRINTD(...) do { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
35 #else
36 #define PRINTD(...) 
37 #endif
38
39 static void
40 ao_radio_isr(void)
41 {
42         if (ao_gpio_get(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN, AO_RADIO_INT)) {
43                 ao_radio_ready = 1;
44                 ao_wakeup((void *) &ao_radio_ready);
45         } else {
46                 ao_radio_done = 1;
47                 ao_wakeup((void *) &ao_radio_done);
48         }
49 }
50
51 static void
52 ao_radio_master_start(void)
53 {
54         ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
55                        AO_RADIO_SPI_BUS,
56                        AO_SPI_SPEED_2MHz);
57 }
58
59 static void
60 ao_radio_master_stop(void)
61 {
62         ao_spi_put_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
63                        AO_RADIO_SPI_BUS);
64 }
65
66 static uint8_t
67 ao_radio_master_send(void)
68 {
69         uint8_t ret;
70
71         PRINTD("send %d\n", ao_radio_spi_request.len);
72         ao_radio_done = 0;
73
74         /* Wait for radio chip to be ready for a command
75          */
76
77         PRINTD("Waiting radio ready\n");
78         ao_arch_block_interrupts();
79         ao_radio_ready = ao_gpio_get(AO_RADIO_INT_PORT,
80                                      AO_RADIO_INT_PIN, AO_RADIO_INT);
81         ret = 0;
82         while (!ao_radio_ready) {
83                 ret = ao_sleep((void *) &ao_radio_ready);
84                 if (ret)
85                         break;
86         }
87         ao_arch_release_interrupts();
88         if (ret)
89                 return 0;
90
91         PRINTD("radio_ready %d radio_done %d\n", ao_radio_ready, ao_radio_done);
92
93         /* Send the command
94          */
95         ao_radio_wait_mode = 0;
96         ao_radio_master_start();
97         ao_spi_send(&ao_radio_spi_request,
98                     ao_radio_spi_request.len,
99                     AO_RADIO_SPI_BUS);
100         ao_radio_master_stop();
101         PRINTD("waiting for send done %d\n", ao_radio_done);
102         ao_arch_block_interrupts();
103         while (!ao_radio_done)
104                 if (ao_sleep((void *) &ao_radio_done))
105                         break;
106         ao_arch_release_interrupts();
107         PRINTD ("sent, radio done %d isr_0 %d isr_1 %d\n", ao_radio_done, isr_0_count, isr_1_count);
108         return ao_radio_done;
109 }
110
111 static void
112 ao_radio_get(uint8_t req, uint8_t len)
113 {
114         ao_config_get();
115         ao_mutex_get(&ao_radio_mutex);
116         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN + len;
117         ao_radio_spi_request.request = req;
118         ao_radio_spi_request.setting = ao_config.radio_setting;
119 }
120
121 static void
122 ao_radio_put(void)
123 {
124         ao_mutex_put(&ao_radio_mutex);
125 }
126
127 static void
128 ao_radio_get_data(__xdata void *d, uint8_t size)
129 {
130         PRINTD ("fetch\n");
131         ao_radio_master_start();
132         ao_spi_recv(&ao_radio_spi_reply,
133                     AO_RADIO_SPI_REPLY_HEADER_LEN + size,
134                     AO_RADIO_SPI_BUS);
135         ao_radio_master_stop();
136         ao_xmemcpy(d, ao_radio_spi_reply.payload, size);
137         PRINTD ("fetched %d\n", size);
138 }
139
140 void
141 ao_radio_recv_abort(void)
142 {
143         ao_radio_get(AO_RADIO_SPI_RECV_ABORT, 0);
144         ao_radio_master_send();
145         ao_radio_put();
146 }
147
148 void
149 ao_radio_send(const void *d, uint8_t size)
150 {
151         ao_radio_get(AO_RADIO_SPI_SEND, size);
152         ao_xmemcpy(&ao_radio_spi_request.payload, d, size);
153         ao_radio_master_send();
154         ao_radio_put();
155 }
156
157
158 uint8_t
159 ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout)
160 {
161         int8_t  ret;
162         uint8_t recv;
163
164         /* Recv the data
165          */
166         
167         ao_radio_get(AO_RADIO_SPI_RECV, 0);
168         ao_radio_spi_request.recv_len = size;
169         ao_radio_spi_request.timeout = timeout;
170         recv = ao_radio_master_send();
171         if (!recv) {
172                 ao_radio_put();
173                 ao_radio_recv_abort();
174                 return 0;
175         }
176         ao_radio_get_data(d, size);
177         recv = ao_radio_spi_reply.status;
178         ao_radio_put();
179
180         return recv;
181 }
182
183 static void
184 ao_radio_cmac_set_key(void)
185 {
186         if (ao_radio_aes_seq == ao_config_aes_seq)
187                 return;
188         /* Set the key.
189          */
190         PRINTD ("set key\n");
191         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
192         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
193         ao_radio_master_send();
194         ao_radio_put();
195         PRINTD ("key set\n");
196         ao_radio_aes_seq = ao_config_aes_seq;
197 }
198
199 int8_t
200 ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
201 {
202         if (len > AO_CMAC_MAX_LEN)
203                 return AO_RADIO_CMAC_LEN_ERROR;
204
205         ao_radio_cmac_set_key();
206
207         PRINTD ("cmac_send: send %d\n", len);
208
209         /* Send the data
210          */
211         
212         PRINTD ("sending packet\n");
213         ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len);
214         ao_xmemcpy(&ao_radio_spi_request.payload, packet, len);
215         ao_radio_master_send();
216         ao_radio_put();
217         PRINTD ("packet sent\n");
218         return AO_RADIO_CMAC_OK;
219 }
220
221 int8_t
222 ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
223 {
224         int8_t  ret;
225         int8_t  recv;
226
227         if (len > AO_CMAC_MAX_LEN)
228                 return AO_RADIO_CMAC_LEN_ERROR;
229
230         ao_radio_cmac_set_key();
231
232         /* Recv the data
233          */
234         PRINTD ("queuing recv\n");
235         ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0);
236         ao_radio_spi_request.recv_len = len;
237         ao_radio_spi_request.timeout = timeout;
238         recv = ao_radio_master_send();
239         PRINTD ("recv queued: %d\n", recv);
240         if (!recv) {
241                 ao_radio_put();
242                 ao_radio_recv_abort();
243                 return AO_RADIO_CMAC_TIMEOUT;
244         }
245
246         PRINTD ("fetching data\n");
247         ao_radio_get_data(packet, len);
248         recv = ao_radio_spi_reply.status;
249         ao_radio_cmac_rssi = ao_radio_spi_reply.rssi;
250         ao_radio_put();
251         PRINTD ("data fetched: %d %d\n", recv, ao_radio_cmac_rssi);
252         return recv;
253 }
254
255 static uint8_t  ao_radio_test_on;
256
257 void
258 ao_radio_test(uint8_t on)
259 {
260         if (on) {
261                 if (!ao_radio_test_on) {
262                         ao_radio_get(AO_RADIO_SPI_TEST_ON, 0);
263                         ao_radio_test_on = 1;
264                         ao_radio_master_send();
265                 }
266         } else {
267                 if (ao_radio_test_on) {
268                         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
269                         ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF;
270                         ao_radio_master_send();
271                         ao_radio_test_on = 0;
272                         ao_radio_put();
273                 }
274         }
275 }
276
277 static void
278 ao_radio_test_cmd(void)
279 {
280         uint8_t mode = 2;
281         ao_cmd_white();
282         if (ao_cmd_lex_c != '\n') {
283                 ao_cmd_decimal();
284                 mode = (uint8_t) ao_cmd_lex_u32;
285         }
286         mode++;
287         if ((mode & 2))
288                 ao_radio_test(1);
289         if (mode == 3) {
290                 printf ("Hit a character to stop..."); flush();
291                 getchar();
292                 putchar('\n');
293         }
294         if ((mode & 1))
295                 ao_radio_test(0);
296 }
297
298 __code struct ao_cmds ao_radio_cmds[] = {
299         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
300         { 0,    NULL },
301 };
302
303 void
304 ao_radio_init(void)
305 {
306         ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN));
307
308         ao_enable_port(AO_RADIO_INT_PORT);
309         ao_exti_setup(AO_RADIO_INT_PORT,
310                       AO_RADIO_INT_PIN,
311                       AO_EXTI_MODE_RISING|AO_EXTI_MODE_FALLING,
312                       ao_radio_isr);
313         ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
314         ao_cmd_register(&ao_radio_cmds[0]);
315 }