altos: Add SPI linked radio API
[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 __xdata uint8_t                          ao_radio_done;
26 static __xdata uint8_t                          ao_radio_mutex;
27
28 __xdata int8_t                                  ao_radio_cmac_rssi;
29
30 static void
31 ao_radio_isr(void)
32 {
33         ao_exti_disable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
34         ao_radio_done = 1;
35         ao_wakeup(&ao_radio_done);
36 }
37
38 static void
39 ao_radio_master_start(void)
40 {
41         ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
42                        AO_RADIO_SPI_BUS,
43                        AO_SPI_SPEED_1MHz);
44 }
45
46 static void
47 ao_radio_master_stop(void)
48 {
49         ao_spi_put_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
50                        AO_RADIO_SPI_BUS);
51 }
52
53
54 static uint8_t
55 ao_radio_master_send(void)
56 {
57         ao_radio_done = 0;
58         ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
59         ao_radio_master_start();
60         ao_spi_send(&ao_radio_spi_request,
61                     ao_radio_spi_request.len,
62                     AO_RADIO_SPI_BUS);
63         ao_radio_master_stop();
64         cli();
65         while (!ao_radio_done)
66                 if (ao_sleep(&ao_radio_done))
67                         break;
68         sei();
69         return ao_radio_done;
70 }
71
72 static void
73 ao_radio_master_recv(uint16_t len)
74 {
75         ao_radio_master_start();
76         ao_spi_recv(&ao_radio_spi_reply,
77                     len,
78                     AO_RADIO_SPI_BUS);
79         ao_radio_master_stop();
80 }
81
82 static void
83 ao_radio_get(uint8_t req, uint8_t len)
84 {
85         ao_config_get();
86         ao_mutex_get(&ao_radio_mutex);
87         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN + len;
88         ao_radio_spi_request.request = req;
89         ao_radio_spi_request.setting = ao_config.radio_setting;
90 }
91
92 static void
93 ao_radio_put(void)
94 {
95         ao_mutex_put(&ao_radio_mutex);
96 }
97
98 static void
99 ao_radio_get_data(__xdata void *d, uint8_t size)
100 {
101         ao_radio_master_start();
102         ao_spi_recv(&ao_radio_spi_reply,
103                     AO_RADIO_SPI_REPLY_HEADER_LEN + size,
104                     AO_RADIO_SPI_BUS);
105         ao_radio_master_stop();
106         ao_xmemcpy(d, ao_radio_spi_reply.payload, size);
107 }
108
109 void
110 ao_radio_recv_abort(void)
111 {
112         ao_radio_get(AO_RADIO_SPI_RECV_ABORT, 0);
113         ao_radio_master_send();
114         ao_radio_put();
115 }
116
117 void
118 ao_radio_send(const void *d, uint8_t size)
119 {
120         ao_radio_get(AO_RADIO_SPI_SEND, size);
121         ao_xmemcpy(&ao_radio_spi_request.payload, d, size);
122         ao_radio_master_send();
123         ao_radio_put();
124 }
125
126
127 uint8_t
128 ao_radio_recv(__xdata void *d, uint8_t size)
129 {
130         int8_t  ret;
131         uint8_t recv;
132
133         /* Recv the data
134          */
135         
136         ao_radio_get(AO_RADIO_SPI_RECV, 0);
137         ao_radio_spi_request.recv_len = size;
138         recv = ao_radio_master_send();
139         if (!recv) {
140                 ao_radio_put();
141                 ao_radio_recv_abort();
142                 return 0;
143         }
144         ao_radio_get_data(d, size);
145         recv = ao_radio_spi_reply.status;
146         ao_radio_put();
147         return recv;
148 }
149
150 int8_t
151 ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
152 {
153         if (len > AO_CMAC_MAX_LEN)
154                 return AO_RADIO_CMAC_LEN_ERROR;
155
156         /* Set the key.
157          */
158         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
159         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
160         ao_radio_master_send();
161         ao_radio_put();
162
163         /* Send the data
164          */
165         
166         ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len);
167         ao_xmemcpy(&ao_radio_spi_request.payload, packet, len);
168         ao_radio_master_send();
169         ao_radio_put();
170         return AO_RADIO_CMAC_OK;
171 }
172
173 int8_t
174 ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
175 {
176         int8_t  ret;
177         uint8_t recv;
178
179         if (len > AO_CMAC_MAX_LEN)
180                 return AO_RADIO_CMAC_LEN_ERROR;
181
182         /* Set the key.
183          */
184         ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
185         ao_radio_spi_request.timeout = timeout;
186         ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
187         ao_radio_master_send();
188         ao_radio_put();
189
190         /* Recv the data
191          */
192         
193         ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0);
194         ao_radio_spi_request.recv_len = len;
195         recv = ao_radio_master_send();
196         if (!recv) {
197                 ao_radio_put();
198                 ao_radio_recv_abort();
199                 return AO_RADIO_CMAC_TIMEOUT;
200         }
201         ao_radio_get_data(packet, len);
202         recv = ao_radio_spi_reply.status;
203         ao_radio_put();
204         return recv;
205 }
206
207 static uint8_t  ao_radio_test_on;
208
209 void
210 ao_radio_test(uint8_t on)
211 {
212         if (on) {
213                 if (!ao_radio_test_on) {
214                         ao_radio_get(AO_RADIO_SPI_TEST_ON, 0);
215                         ao_radio_test_on = 1;
216                         ao_radio_master_send();
217                 }
218         } else {
219                 if (ao_radio_test_on) {
220                         ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
221                         ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF;
222                         ao_radio_master_send();
223                         ao_radio_test_on = 0;
224                         ao_radio_put();
225                 }
226         }
227 }
228
229 static void
230 ao_radio_test_cmd(void)
231 {
232         uint8_t mode = 2;
233         ao_cmd_white();
234         if (ao_cmd_lex_c != '\n') {
235                 ao_cmd_decimal();
236                 mode = (uint8_t) ao_cmd_lex_u32;
237         }
238         mode++;
239         if ((mode & 2))
240                 ao_radio_test(1);
241         if (mode == 3) {
242                 printf ("Hit a character to stop..."); flush();
243                 getchar();
244                 putchar('\n');
245         }
246         if ((mode & 1))
247                 ao_radio_test(0);
248 }
249
250 __code struct ao_cmds ao_radio_cmds[] = {
251         { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
252         { 0,    NULL },
253 };
254
255 void
256 ao_radio_init(void)
257 {
258         ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN));
259
260         ao_enable_port(AO_RADIO_INT_PORT);
261         ao_exti_setup(AO_RADIO_INT_PORT,
262                       AO_RADIO_INT_PIN,
263                       AO_EXTI_MODE_FALLING,
264                       ao_radio_isr);
265         ao_cmd_register(&ao_radio_cmds[0]);
266 }