Merge branch 'master' of git://git.gag.com/fw/altos
[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
75 static void
76 ao_pad(void)
77 {
78         static __xdata struct ao_pad_command    command;
79         static __xdata struct ao_pad_query      query;
80         int16_t time_difference;
81         uint8_t c;
82
83         ao_led_off(AO_LED_RED);
84         ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
85         ao_pad_box = ao_74hc497_read();
86         for (;;) {
87                 flush();
88                 if (ao_radio_cmac_recv(&command, sizeof (command), 0) != AO_RADIO_CMAC_OK)
89                         continue;
90                 
91                 PRINTD ("tick %d serial %d cmd %d channel %d\n",
92                         command.tick, command.serial, command.cmd, command.channel);
93
94                 switch (command.cmd) {
95                 case AO_LAUNCH_ARM:
96                         if (command.box != ao_pad_box) {
97                                 PRINTD ("box number mismatch\n");
98                                 break;
99                         }
100
101                         if (command.channels & ~(AO_PAD_ALL_PINS))
102                                 break;
103
104                         time_difference = command.tick - ao_time();
105                         PRINTD ("arm tick %d local tick %d\n", command.tick, ao_time());
106                         if (time_difference < 0)
107                                 time_difference = -time_difference;
108                         if (time_difference > 10) {
109                                 PRINTD ("time difference too large %d\n", time_difference);
110                                 break;
111                         }
112                         PRINTD ("armed\n");
113                         ao_pad_armed = command.channels;
114                         ao_pad_arm_time = ao_time();
115
116                         /* fall through ... */
117
118                 case AO_LAUNCH_QUERY:
119                         if (command.box != ao_pad_box) {
120                                 PRINTD ("box number mismatch\n");
121                                 break;
122                         }
123
124                         query.tick = ao_time();
125                         query.box = ao_pad_box;
126                         query.channels = AO_PAD_ALL_PINS;
127                         query.armed = ao_pad_armed;
128                         query.arm_status = ao_pad_arm_status();
129                         for (c = 0; c < AO_PAD_NUM; c++)
130                                 query.igniter_status[c] = ao_pad_igniter_status(c);
131                         PRINTD ("query tick %d serial %d channel %d valid %d arm %d igniter %d\n",
132                                 query.tick, query.serial, query.channel, query.valid, query.arm_status,
133                                 query.igniter_status);
134                         ao_radio_cmac_send(&query, sizeof (query));
135                         break;
136                 case AO_LAUNCH_FIRE:
137                         if (!ao_pad_armed) {
138                                 PRINTD ("not armed\n");
139                                 break;
140                         }
141                         if ((uint16_t) (ao_time() - ao_pad_arm_time) > AO_SEC_TO_TICKS(20)) {
142                                 PRINTD ("late pad arm_time %d time %d\n",
143                                         ao_pad_arm_time, ao_time());
144                                 break;
145                         }
146                         time_difference = command.tick - ao_time();
147                         if (time_difference < 0)
148                                 time_difference = -time_difference;
149                         if (time_difference > 10) {
150                                 PRINTD ("time different too large %d\n", time_difference);
151                                 break;
152                         }
153                         PRINTD ("ignite\n");
154                         ao_pad_ignite = ao_pad_armed;
155                         ao_wakeup(&ao_pad_ignite);
156                         break;
157                 }
158         }
159 }
160
161 void
162 ao_pad_test(void)
163 {
164 #if 0
165         switch (ao_igniter_status(ao_igniter_drogue)) {
166         case ao_igniter_ready:
167         case ao_igniter_active:
168                 printf ("Armed: ");
169                 switch (ao_igniter_status(ao_igniter_main)) {
170                 default:
171                         printf("unknown status\n");
172                         break;
173                 case ao_igniter_ready:
174                         printf("igniter good\n");
175                         break;
176                 case ao_igniter_open:
177                         printf("igniter bad\n");
178                         break;
179                 }
180                 break;
181         default:
182                 printf("Disarmed\n");
183         }
184 #endif
185 }
186
187 void
188 ao_pad_manual(void)
189 {
190         ao_cmd_white();
191         if (!ao_match_word("DoIt"))
192                 return;
193         ao_cmd_white();
194         ao_pad_ignite = 1;
195         ao_wakeup(&ao_pad_ignite);
196 }
197
198 static __xdata struct ao_task ao_pad_task;
199 static __xdata struct ao_task ao_pad_ignite_task;
200 static __xdata struct ao_task ao_pad_status_task;
201
202 __code struct ao_cmds ao_pad_cmds[] = {
203         { ao_pad_test,  "t\0Test pad continuity" },
204         { ao_pad_manual,        "i <key>\0Fire igniter. <key> is doit with D&I" },
205         { 0, NULL }
206 };
207
208 void
209 ao_pad_init(void)
210 {
211 #if AO_PAD_NUM > 0
212         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_0, AO_PAD_0, 0);
213 #endif
214 #if AO_PAD_NUM > 1
215         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_1, AO_PAD_1, 0);
216 #endif
217 #if AO_PAD_NUM > 2
218         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_2, AO_PAD_2, 0);
219 #endif
220 #if AO_PAD_NUM > 3
221         ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_3, AO_PAD_3, 0);
222 #endif
223         ao_cmd_register(&ao_pad_cmds[0]);
224         ao_add_task(&ao_pad_task, ao_pad, "pad listener");
225         ao_add_task(&ao_pad_ignite_task, ao_pad_run, "pad igniter");
226         ao_add_task(&ao_pad_status_task, ao_pad_status, "pad status");
227 }