2 * Copyright © 2011 Keith Packard <keithp@keithp.com>
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.
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.
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.
20 #define AO_CMAC_KEY_LEN AO_AES_LEN
21 #define AO_CMAC_MAX_LEN (128 - AO_CMAC_KEY_LEN)
23 static __xdata uint8_t ao_radio_cmac_mutex;
24 static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN + AO_CMAC_KEY_LEN + 2 + AO_CMAC_KEY_LEN];
25 static __pdata uint8_t ao_radio_cmac_len;
32 b = ao_cmd_hexchar(getchar());
34 ao_cmd_status = ao_cmd_lex_error;
50 round_len(uint8_t len)
54 /* Make sure we transfer at least one packet, and
55 * then make sure every packet is full. Note that
56 * there is no length encoded, and that the receiver
57 * must deal with any extra bytes in the packet
59 if (len < AO_CMAC_KEY_LEN)
60 len = AO_CMAC_KEY_LEN;
61 rem = len % AO_CMAC_KEY_LEN;
63 len += (AO_CMAC_KEY_LEN - rem);
68 * Sign and deliver the data sitting in the cmac buffer
71 radio_cmac_send(uint8_t len) __reentrant
76 /* Make sure the AES key is loaded */
83 ao_mutex_get(&ao_aes_mutex);
84 ao_aes_set_mode(ao_aes_mode_cbc_mac);
85 ao_aes_set_key(ao_config.aes_key);
87 for (i = 0; i < len; i += AO_CMAC_KEY_LEN) {
88 if (i + AO_CMAC_KEY_LEN < len)
89 ao_aes_run(&cmac_data[i], NULL);
91 ao_aes_run(&cmac_data[i], &cmac_data[len]);
93 ao_mutex_put(&ao_aes_mutex);
95 ao_radio_send(cmac_data, len + AO_CMAC_KEY_LEN);
99 * Receive and validate an incoming packet
103 radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant
107 len = round_len(len);
114 i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2);
118 return AO_RADIO_CMAC_TIMEOUT;
120 if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & PKT_APPEND_STATUS_1_CRC_OK))
121 return AO_RADIO_CMAC_CRC_ERROR;
125 /* Compute the packet signature
127 ao_mutex_get(&ao_aes_mutex);
128 ao_aes_set_mode(ao_aes_mode_cbc_mac);
129 ao_aes_set_key(ao_config.aes_key);
131 for (i = 0; i < len; i += AO_CMAC_KEY_LEN) {
132 if (i + AO_CMAC_KEY_LEN < len)
133 ao_aes_run(&cmac_data[i], NULL);
135 ao_aes_run(&cmac_data[i], &cmac_data[len + AO_CMAC_KEY_LEN + 2]);
137 ao_mutex_put(&ao_aes_mutex);
139 /* Check the packet signature against the signature provided
143 if (memcmp(&cmac_data[len],
144 &cmac_data[len + AO_CMAC_KEY_LEN + 2],
145 AO_CMAC_KEY_LEN) != 0) {
146 return AO_RADIO_CMAC_MAC_ERROR;
149 return AO_RADIO_CMAC_OK;
153 ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
155 if (len > AO_CMAC_MAX_LEN)
156 return AO_RADIO_CMAC_LEN_ERROR;
157 ao_mutex_get(&ao_radio_cmac_mutex);
158 memcpy(cmac_data, packet, len);
159 radio_cmac_send(len);
160 ao_mutex_put(&ao_radio_cmac_mutex);
161 return AO_RADIO_CMAC_OK;
165 ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
168 if (len > AO_CMAC_MAX_LEN)
169 return AO_RADIO_CMAC_LEN_ERROR;
170 ao_mutex_get(&ao_radio_cmac_mutex);
171 i = radio_cmac_recv(len, timeout);
172 if (i == AO_RADIO_CMAC_OK)
173 memcpy(packet, cmac_data, len);
174 ao_mutex_put(&ao_radio_cmac_mutex);
179 radio_cmac_send_cmd(void) __reentrant
185 if (ao_cmd_status != ao_cmd_success)
188 if (len > AO_CMAC_MAX_LEN) {
189 ao_cmd_status = ao_cmd_syntax_error;
193 ao_mutex_get(&ao_radio_cmac_mutex);
195 for (i = 0; i < len; i++) {
196 cmac_data[i] = getbyte();
197 if (ao_cmd_status != ao_cmd_success)
200 radio_cmac_send(len);
201 ao_mutex_put(&ao_radio_cmac_mutex);
205 radio_cmac_recv_cmd(void) __reentrant
211 if (ao_cmd_status != ao_cmd_success)
215 if (ao_cmd_status != ao_cmd_success)
217 timeout = AO_MS_TO_TICKS(ao_cmd_lex_i);
218 ao_mutex_get(&ao_radio_cmac_mutex);
219 i = radio_cmac_recv(len, timeout);
220 if (i == AO_RADIO_CMAC_OK) {
222 for (i = 0; i < len; i++)
223 printf("%02x", cmac_data[i]);
226 printf ("ERROR %d\n", i);
227 ao_mutex_put(&ao_radio_cmac_mutex);
230 static __xdata struct ao_launch_command command;
231 static __xdata struct ao_launch_query query;
235 launch_query(uint16_t serial, uint8_t channel)
238 int8_t r = AO_RADIO_CMAC_OK;
240 for (i = 0; i < 10; i++) {
241 printf ("."); flush();
242 command.tick = ao_time();
243 command.serial = serial;
244 command.cmd = AO_LAUNCH_QUERY;
245 command.channel = channel;
246 ao_radio_cmac_send(&command, sizeof (command));
247 r = ao_radio_cmac_recv(&query, sizeof (query), AO_MS_TO_TICKS(500));
248 if (r == AO_RADIO_CMAC_OK)
251 printf("\n"); flush();
256 launch_report_cmd(void) __reentrant
263 serial = ao_cmd_lex_i;
265 channel = ao_cmd_lex_i;
266 if (ao_cmd_status != ao_cmd_success)
268 r = launch_query(serial, channel);
270 case AO_RADIO_CMAC_OK:
272 switch (query.arm_status) {
273 case ao_igniter_ready:
274 case ao_igniter_active:
276 switch (query.igniter_status) {
278 printf("unknown status\n");
280 case ao_igniter_ready:
281 printf("igniter good\n");
283 case ao_igniter_open:
284 printf("igniter bad\n");
289 printf("Disarmed\n");
292 printf("Invalid channel %d\n", channel);
296 printf("Error %d\n", r);
302 launch_fire_cmd(void) __reentrant
304 static __xdata struct ao_launch_command command;
310 uint16_t tick_offset;
313 serial = ao_cmd_lex_i;
315 channel = ao_cmd_lex_i;
318 if (ao_cmd_status != ao_cmd_success)
320 tick_offset = ao_time();
321 r = launch_query(serial, channel);
322 tick_offset -= query.tick;
324 for (i = 0; i < 4; i++) {
325 printf("arm %d\n", i); flush();
326 command.tick = ao_time() - tick_offset;
327 command.serial = serial;
328 command.cmd = AO_LAUNCH_ARM;
329 command.channel = channel;
330 ao_radio_cmac_send(&command, sizeof (command));
332 secs = secs * 10 - 5;
335 for (i = 0; i < secs; i++) {
336 printf("fire %d\n", i); flush();
337 command.tick = ao_time() - tick_offset;
338 command.serial = serial;
339 command.cmd = AO_LAUNCH_FIRE;
341 ao_radio_cmac_send(&command, sizeof (command));
342 ao_delay(AO_MS_TO_TICKS(100));
346 static __code struct ao_cmds ao_radio_cmac_cmds[] = {
347 { radio_cmac_send_cmd, "s <length>\0Send AES-CMAC packet. Bytes to send follow on next line" },
348 { radio_cmac_recv_cmd, "S <length> <timeout>\0Receive AES-CMAC packet. Timeout in ms" },
349 { launch_report_cmd, "l <serial> <channel>\0Get remote launch status" },
350 { launch_fire_cmd, "f <serial> <channel> <secs>\0Fire remote igniter" },
355 ao_radio_cmac_init(void)
357 ao_cmd_register(&ao_radio_cmac_cmds[0]);