altos: Disable telefire 'pad' listener when testing radio
[fw/altos] / src / drivers / ao_pad.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_pad.h>
20 #include <ao_74hc497.h>
21
22 __xdata uint8_t ao_pad_ignite;
23
24 #define ao_pad_igniter_status(c)        AO_PAD_IGNITER_STATUS_UNKNOWN
25 #define ao_pad_arm_status()             AO_PAD_ARM_STATUS_UNKNOWN
26
27 #if 0
28 #define PRINTD(...) printf(__VA_ARGS__)
29 #else
30 #define PRINTD(...) 
31 #endif
32
33 static void
34 ao_pad_run(void)
35 {
36         for (;;) {
37                 while (!ao_pad_ignite)
38                         ao_sleep(&ao_pad_ignite);
39                 /*
40                  * Actually set the pad bits
41                  */
42                 AO_PAD_PORT = (AO_PAD_PORT & (~AO_PAD_ALL_PINS)) | ao_pad_ignite;
43                 while (ao_pad_ignite) {
44                         ao_pad_ignite = 0;
45                         ao_delay(AO_PAD_FIRE_TIME);
46                 }
47                 AO_PAD_PORT &= ~(AO_PAD_ALL_PINS);
48         }
49 }
50
51 static void
52 ao_pad_status(void)
53 {
54         for (;;) {
55                 ao_delay(AO_SEC_TO_TICKS(1));
56 #if 0
57                 if (ao_igniter_status(ao_igniter_drogue) == ao_igniter_ready) {
58                         if (ao_igniter_status(ao_igniter_main) == ao_igniter_ready) {
59                                 for (i = 0; i < 5; i++) {
60                                         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(50));
61                                         ao_delay(AO_MS_TO_TICKS(100));
62                                 }
63                         } else {
64                                 ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
65                         }
66                 }
67 #endif
68         }
69 }
70
71 static __pdata uint8_t  ao_pad_armed;
72 static __pdata uint16_t ao_pad_arm_time;
73 static __pdata uint8_t  ao_pad_box;
74 static __xdata uint8_t  ao_pad_disabled;
75
76 void
77 ao_pad_disable(void)
78 {
79         if (!ao_pad_disabled) {
80                 ao_pad_disabled = 1;
81                 ao_radio_recv_abort();
82         }
83 }
84
85 void
86 ao_pad_enable(void)
87 {
88         ao_pad_disabled = 0;
89         ao_wakeup (&ao_pad_disabled);
90 }
91
92 static void
93 ao_pad(void)
94 {
95         static __xdata struct ao_pad_command    command;
96         static __xdata struct ao_pad_query      query;
97         int16_t time_difference;
98         uint8_t c;
99
100         ao_led_off(AO_LED_RED);
101         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
102         ao_pad_box = ao_74hc497_read();
103         for (;;) {
104                 flush();
105                 while (ao_pad_disabled)
106                         ao_sleep(&ao_pad_disabled);
107                 if (ao_radio_cmac_recv(&command, sizeof (command), 0) != AO_RADIO_CMAC_OK)
108                         continue;
109                 
110                 PRINTD ("tick %d serial %d cmd %d channel %d\n",
111                         command.tick, command.serial, command.cmd, command.channel);
112
113                 switch (command.cmd) {
114                 case AO_LAUNCH_ARM:
115                         if (command.box != ao_pad_box) {
116                                 PRINTD ("box number mismatch\n");
117                                 break;
118                         }
119
120                         if (command.channels & ~(AO_PAD_ALL_PINS))
121                                 break;
122
123                         time_difference = command.tick - ao_time();
124                         PRINTD ("arm tick %d local tick %d\n", command.tick, ao_time());
125                         if (time_difference < 0)
126                                 time_difference = -time_difference;
127                         if (time_difference > 10) {
128                                 PRINTD ("time difference too large %d\n", time_difference);
129                                 break;
130                         }
131                         PRINTD ("armed\n");
132                         ao_pad_armed = command.channels;
133                         ao_pad_arm_time = ao_time();
134
135                         /* fall through ... */
136
137                 case AO_LAUNCH_QUERY:
138                         if (command.box != ao_pad_box) {
139                                 PRINTD ("box number mismatch\n");
140                                 break;
141                         }
142
143                         query.tick = ao_time();
144                         query.box = ao_pad_box;
145                         query.channels = AO_PAD_ALL_PINS;
146                         query.armed = ao_pad_armed;
147                         query.arm_status = ao_pad_arm_status();
148                         for (c = 0; c < AO_PAD_NUM; c++)
149                                 query.igniter_status[c] = ao_pad_igniter_status(c);
150                         PRINTD ("query tick %d serial %d channel %d valid %d arm %d igniter %d\n",
151                                 query.tick, query.serial, query.channel, query.valid, query.arm_status,
152                                 query.igniter_status);
153                         ao_radio_cmac_send(&query, sizeof (query));
154                         break;
155                 case AO_LAUNCH_FIRE:
156                         if (!ao_pad_armed) {
157                                 PRINTD ("not armed\n");
158                                 break;
159                         }
160                         if ((uint16_t) (ao_time() - ao_pad_arm_time) > AO_SEC_TO_TICKS(20)) {
161                                 PRINTD ("late pad arm_time %d time %d\n",
162                                         ao_pad_arm_time, ao_time());
163                                 break;
164                         }
165                         time_difference = command.tick - ao_time();
166                         if (time_difference < 0)
167                                 time_difference = -time_difference;
168                         if (time_difference > 10) {
169                                 PRINTD ("time different too large %d\n", time_difference);
170                                 break;
171                         }
172                         PRINTD ("ignite\n");
173                         ao_pad_ignite = ao_pad_armed;
174                         ao_wakeup(&ao_pad_ignite);
175                         break;
176                 }
177         }
178 }
179
180 void
181 ao_pad_test(void)
182 {
183 #if 0
184         switch (ao_igniter_status(ao_igniter_drogue)) {
185         case ao_igniter_ready:
186         case ao_igniter_active:
187                 printf ("Armed: ");
188                 switch (ao_igniter_status(ao_igniter_main)) {
189                 default:
190                         printf("unknown status\n");
191                         break;
192                 case ao_igniter_ready:
193                         printf("igniter good\n");
194                         break;
195                 case ao_igniter_open:
196                         printf("igniter bad\n");
197                         break;
198                 }
199                 break;
200         default:
201                 printf("Disarmed\n");
202         }
203 #endif
204 }
205
206 void
207 ao_pad_manual(void)
208 {
209         ao_cmd_white();
210         if (!ao_match_word("DoIt"))
211                 return;
212         ao_cmd_white();
213         ao_pad_ignite = 1;
214         ao_wakeup(&ao_pad_ignite);
215 }
216
217 static __xdata struct ao_task ao_pad_task;
218 static __xdata struct ao_task ao_pad_ignite_task;
219 static __xdata struct ao_task ao_pad_status_task;
220
221 __code struct ao_cmds ao_pad_cmds[] = {
222         { ao_pad_test,  "t\0Test pad continuity" },
223         { ao_pad_manual,        "i <key>\0Fire igniter. <key> is doit with D&I" },
224         { 0, NULL }
225 };
226
227 void
228 ao_pad_init(void)
229 {
230 #if AO_PAD_NUM > 0
231         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_0, AO_PAD_0, 0);
232 #endif
233 #if AO_PAD_NUM > 1
234         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_1, AO_PAD_1, 0);
235 #endif
236 #if AO_PAD_NUM > 2
237         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_2, AO_PAD_2, 0);
238 #endif
239 #if AO_PAD_NUM > 3
240         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_3, AO_PAD_3, 0);
241 #endif
242         ao_cmd_register(&ao_pad_cmds[0]);
243         ao_add_task(&ao_pad_task, ao_pad, "pad listener");
244         ao_add_task(&ao_pad_ignite_task, ao_pad_run, "pad igniter");
245         ao_add_task(&ao_pad_status_task, ao_pad_status, "pad status");
246 }