altos/telelco: Crank up SPI speed to cc1111
[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         cli();
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         sei();
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         cli();
103         while (!ao_radio_done)
104                 if (ao_sleep((void *) &ao_radio_done))
105                         break;
106         sei();
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)
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         recv = ao_radio_master_send();
170         if (!recv) {
171                 ao_radio_put();
172                 ao_radio_recv_abort();
173                 return 0;
174         }
175         ao_radio_get_data(d, size);
176         recv = ao_radio_spi_reply.status;
177         ao_radio_put();
178
179         return recv;
180 }
181
182 static void
183 ao_radio_cmac_set_key(void)
184 {
185         if (ao_radio_aes_seq == ao_config_aes_seq)
186                 return;
187         /* Set the key.
188          */
189         PRINTD ("set key\n");
190         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
191         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
192         ao_radio_master_send();
193         ao_radio_put();
194         PRINTD ("key set\n");
195         ao_radio_aes_seq = ao_config_aes_seq;
196 }
197
198 int8_t
199 ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
200 {
201         if (len > AO_CMAC_MAX_LEN)
202                 return AO_RADIO_CMAC_LEN_ERROR;
203
204         ao_radio_cmac_set_key();
205
206         PRINTD ("cmac_send: send %d\n", len);
207
208         /* Send the data
209          */
210         
211         PRINTD ("sending packet\n");
212         ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len);
213         ao_xmemcpy(&ao_radio_spi_request.payload, packet, len);
214         ao_radio_master_send();
215         ao_radio_put();
216         PRINTD ("packet sent\n");
217         return AO_RADIO_CMAC_OK;
218 }
219
220 int8_t
221 ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
222 {
223         int8_t  ret;
224         int8_t  recv;
225
226         if (len > AO_CMAC_MAX_LEN)
227                 return AO_RADIO_CMAC_LEN_ERROR;
228
229         ao_radio_cmac_set_key();
230
231         /* Recv the data
232          */
233         PRINTD ("queuing recv\n");
234         ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0);
235         ao_radio_spi_request.recv_len = len;
236         ao_radio_spi_request.timeout = timeout;
237         recv = ao_radio_master_send();
238         PRINTD ("recv queued: %d\n", recv);
239         if (!recv) {
240                 ao_radio_put();
241                 ao_radio_recv_abort();
242                 return AO_RADIO_CMAC_TIMEOUT;
243         }
244
245         PRINTD ("fetching data\n");
246         ao_radio_get_data(packet, len);
247         recv = ao_radio_spi_reply.status;
248         ao_radio_cmac_rssi = ao_radio_spi_reply.rssi;
249         ao_radio_put();
250         PRINTD ("data fetched: %d %d\n", recv, ao_radio_cmac_rssi);
251         return recv;
252 }
253
254 static uint8_t  ao_radio_test_on;
255
256 void
257 ao_radio_test(uint8_t on)
258 {
259         if (on) {
260                 if (!ao_radio_test_on) {
261                         ao_radio_get(AO_RADIO_SPI_TEST_ON, 0);
262                         ao_radio_test_on = 1;
263                         ao_radio_master_send();
264                 }
265         } else {
266                 if (ao_radio_test_on) {
267                         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
268                         ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF;
269                         ao_radio_master_send();
270                         ao_radio_test_on = 0;
271                         ao_radio_put();
272                 }
273         }
274 }
275
276 static void
277 ao_radio_test_cmd(void)
278 {
279         uint8_t mode = 2;
280         ao_cmd_white();
281         if (ao_cmd_lex_c != '\n') {
282                 ao_cmd_decimal();
283                 mode = (uint8_t) ao_cmd_lex_u32;
284         }
285         mode++;
286         if ((mode & 2))
287                 ao_radio_test(1);
288         if (mode == 3) {
289                 printf ("Hit a character to stop..."); flush();
290                 getchar();
291                 putchar('\n');
292         }
293         if ((mode & 1))
294                 ao_radio_test(0);
295 }
296
297 __code struct ao_cmds ao_radio_cmds[] = {
298         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
299         { 0,    NULL },
300 };
301
302 void
303 ao_radio_init(void)
304 {
305         ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN));
306
307         ao_enable_port(AO_RADIO_INT_PORT);
308         ao_exti_setup(AO_RADIO_INT_PORT,
309                       AO_RADIO_INT_PIN,
310                       AO_EXTI_MODE_RISING|AO_EXTI_MODE_FALLING,
311                       ao_radio_isr);
312         ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
313         ao_cmd_register(&ao_radio_cmds[0]);
314 }