Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
[fw/altos] / src / drivers / ao_lco_cmd.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_lco_cmd.h>
20 #include <ao_radio_cmac.h>
21
22 static __xdata struct ao_launch_command command;
23 static __xdata struct ao_launch_query   query;
24 static __pdata uint16_t launch_serial;
25 static __pdata uint8_t  launch_channel;
26 static __pdata uint16_t tick_offset;
27
28 static void
29 launch_args(void) __reentrant
30 {
31         ao_cmd_decimal();
32         launch_serial = ao_cmd_lex_i;
33         ao_cmd_decimal();
34         launch_channel = ao_cmd_lex_i;
35 }
36
37 static int8_t
38 launch_query(void)
39 {
40         uint8_t i;
41         int8_t  r = AO_RADIO_CMAC_OK;
42
43         tick_offset = ao_time();
44         for (i = 0; i < 10; i++) {
45                 printf ("."); flush();
46                 command.tick = ao_time();
47                 command.serial = launch_serial;
48                 command.cmd = AO_LAUNCH_QUERY;
49                 command.channel = launch_channel;
50                 ao_radio_cmac_send(&command, sizeof (command));
51                 r = ao_radio_cmac_recv(&query, sizeof (query), AO_MS_TO_TICKS(500));
52                 if (r == AO_RADIO_CMAC_OK)
53                         break;
54         }
55         tick_offset -= query.tick;
56         printf("\n"); flush();
57         return r;
58 }
59
60 static void
61 launch_report_cmd(void) __reentrant
62 {
63         int8_t          r;
64
65         launch_args();
66         if (ao_cmd_status != ao_cmd_success)
67                 return;
68         r = launch_query();
69         switch (r) {
70         case AO_RADIO_CMAC_OK:
71                 if (query.valid) {
72                         switch (query.arm_status) {
73                         case ao_igniter_ready:
74                         case ao_igniter_active:
75                                 printf ("Armed: ");
76                                 break;
77                         default:
78                                 printf("Disarmed: ");
79                         }
80                         switch (query.igniter_status) {
81                         default:
82                                 printf("unknown\n");
83                                 break;
84                         case ao_igniter_ready:
85                                 printf("igniter good\n");
86                                 break;
87                         case ao_igniter_open:
88                                 printf("igniter bad\n");
89                                 break;
90                         }
91                 } else {
92                         printf("Invalid channel %d\n", launch_channel);
93                 }
94                 printf("Rssi: %d\n", ao_radio_cmac_rssi);
95                 break;
96         default:
97                 printf("Error %d\n", r);
98                 break;
99         }
100 }
101
102 static void
103 launch_arm(void) __reentrant
104 {
105         command.tick = ao_time() - tick_offset;
106         command.serial = launch_serial;
107         command.cmd = AO_LAUNCH_ARM;
108         command.channel = launch_channel;
109         ao_radio_cmac_send(&command, sizeof (command));
110 }
111
112 static void
113 launch_ignite(void) __reentrant
114 {
115         command.tick = ao_time() - tick_offset;
116         command.serial = launch_serial;
117         command.cmd = AO_LAUNCH_FIRE;
118         command.channel = 0;
119         ao_radio_cmac_send(&command, sizeof (command));
120 }
121
122 static void
123 launch_fire_cmd(void) __reentrant
124 {
125         static __xdata struct ao_launch_command command;
126         uint8_t         secs;
127         uint8_t         i;
128         int8_t          r;
129
130         launch_args();
131         ao_cmd_decimal();
132         secs = ao_cmd_lex_i;
133         if (ao_cmd_status != ao_cmd_success)
134                 return;
135         r = launch_query();
136         if (r != AO_RADIO_CMAC_OK) {
137                 printf("query failed %d\n", r);
138                 return;
139         }
140
141         for (i = 0; i < 4; i++) {
142                 printf("arm %d\n", i); flush();
143                 launch_arm();
144         }
145
146         secs = secs * 10 - 5;
147         if (secs > 100)
148                 secs = 100;
149         for (i = 0; i < secs; i++) {
150                 printf("fire %d\n", i); flush();
151                 launch_ignite();
152                 ao_delay(AO_MS_TO_TICKS(100));
153         }
154 }
155
156 static void
157 launch_arm_cmd(void) __reentrant
158 {
159         uint8_t i;
160         int8_t  r;
161         launch_args();
162         r = launch_query();
163         if (r != AO_RADIO_CMAC_OK) {
164                 printf("query failed %d\n", r);
165                 return;
166         }
167         for (i = 0; i < 4; i++)
168                 launch_arm();
169 }
170
171 static void
172 launch_ignite_cmd(void) __reentrant
173 {
174         uint8_t i;
175         launch_args();
176         for (i = 0; i < 4; i++)
177                 launch_ignite();
178 }
179
180 static uint8_t
181 getnibble(void)
182 {
183         int8_t  b;
184
185         b = ao_cmd_hexchar(getchar());
186         if (b < 0) {
187                 ao_cmd_status = ao_cmd_lex_error;
188                 return 0;
189         }
190         return (uint8_t) b;
191 }
192
193 static uint8_t
194 getbyte(void)
195 {
196         uint8_t b;
197         b = getnibble() << 4;
198         b |= getnibble();
199         return b;
200 }
201         
202 static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN];
203
204 static void
205 radio_cmac_send_cmd(void) __reentrant
206 {
207         uint8_t i;
208         uint8_t len;
209
210         ao_cmd_decimal();
211         if (ao_cmd_status != ao_cmd_success)
212                 return;
213         len = ao_cmd_lex_i;
214         if (len > AO_CMAC_MAX_LEN) {
215                 ao_cmd_status = ao_cmd_syntax_error;
216                 return;
217         }
218         flush();
219         len = ao_cmd_lex_i;
220         for (i = 0; i < len; i++) {
221                 cmac_data[i] = getbyte();
222                 if (ao_cmd_status != ao_cmd_success)
223                         return;
224         }
225         ao_radio_cmac_send(cmac_data, len);
226 }
227
228 static void
229 radio_cmac_recv_cmd(void) __reentrant
230 {
231         uint8_t         len, i;
232         uint16_t        timeout;
233
234         ao_cmd_decimal();
235         if (ao_cmd_status != ao_cmd_success)
236                 return;
237         len = ao_cmd_lex_i;
238         ao_cmd_decimal();
239         if (ao_cmd_status != ao_cmd_success)
240                 return;
241         timeout = AO_MS_TO_TICKS(ao_cmd_lex_i);
242         i = ao_radio_cmac_recv(cmac_data, len, timeout);
243         if (i == AO_RADIO_CMAC_OK) {
244                 printf ("PACKET ");
245                 for (i = 0; i < len; i++)
246                         printf("%02x", cmac_data[i]);
247                 printf (" %d\n", ao_radio_cmac_rssi);
248         } else
249                 printf ("ERROR %d %d\n", i, ao_radio_cmac_rssi);
250 }
251
252 static __code struct ao_cmds ao_lco_cmds[] = {
253         { radio_cmac_send_cmd,  "s <length>\0Send AES-CMAC packet. Bytes to send follow on next line" },
254         { radio_cmac_recv_cmd,  "S <length> <timeout>\0Receive AES-CMAC packet. Timeout in ms" },
255         { launch_report_cmd,    "l <serial> <channel>\0Get remote launch status" },
256         { launch_fire_cmd,      "f <serial> <channel> <secs>\0Fire remote igniter" },
257         { launch_arm_cmd,       "a <serial> <channel>\0Arm remote igniter" },
258         { launch_ignite_cmd,    "i <serial> <channel>\0Pulse remote igniter" },
259         { 0, NULL },
260 };
261
262 void
263 ao_lco_cmd_init(void)
264 {
265         ao_cmd_register(&ao_lco_cmds[0]);
266 }