altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / kernel / ao_pyro.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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #ifndef AO_FLIGHT_TEST
20 #include <ao.h>
21 #include <ao_sample.h>
22 #include <ao_flight.h>
23 #endif
24 #include <ao_pyro.h>
25
26 #if IS_COMPANION
27 #include <ao_companion.h>
28 #define ao_accel ao_companion_command.accel
29 #define ao_speed ao_companion_command.speed
30 #define ao_height ao_companion_command.height
31 #define ao_flight_state ao_companion_command.flight_state
32 #define ao_motor_number ao_companion_command.motor_number
33 #endif
34
35 #define ao_lowbit(x)    ((x) & (-x))
36
37 #ifndef AO_FLIGHT_TEST
38 enum ao_igniter_status
39 ao_pyro_status(uint8_t p)
40 {
41         struct ao_data packet;
42         int16_t value;
43
44         ao_arch_critical(
45                 ao_data_get(&packet);
46                 );
47
48         value = AO_SENSE_PYRO(&packet, p);
49         return ao_igniter_check(value, AO_SENSE_PBATT(&packet));
50 }
51
52 void
53 ao_pyro_print_status(void)
54 {
55         uint8_t p;
56
57         for(p = 0; p < AO_PYRO_NUM; p++) {
58                 enum ao_igniter_status status = ao_pyro_status(p);
59                 printf("Igniter: %d Status: %s\n",
60                        p, ao_igniter_status_names[status]);
61         }
62 }
63 #endif
64
65 uint16_t        ao_pyro_fired;
66 uint16_t        ao_pyro_inhibited;
67
68 #ifndef PYRO_DBG
69 #define PYRO_DBG        0
70 #endif
71
72 #if PYRO_DBG
73 int pyro_dbg;
74 #define DBG(...)        do { if (pyro_dbg) { printf("\t%d: ", (int) (pyro - ao_config.pyro)); printf(__VA_ARGS__); } } while (0)
75 #else
76 #define DBG(...)
77 #endif
78
79 static angle_t
80 ao_sample_max_orient(void)
81 {
82         uint8_t i;
83         angle_t max = ao_sample_orients[0];
84
85         for (i = 1; i < AO_NUM_ORIENT; i++) {
86                 angle_t a = ao_sample_orients[i];
87                 if (a > max)
88                         max = a;
89         }
90         return max;
91 }
92 /*
93  * Given a pyro structure, figure out
94  * if the current flight state satisfies all
95  * of the requirements
96  */
97 static uint8_t
98 ao_pyro_ready(const struct ao_pyro *pyro)
99 {
100         enum ao_pyro_flag flag, flags;
101 #if HAS_GYRO
102         angle_t max_orient;
103 #endif
104
105         flags = pyro->flags;
106         while (flags != ao_pyro_none) {
107                 flag = ao_lowbit(flags);
108                 flags &= ~flag;
109                 switch (flag) {
110
111                 case ao_pyro_accel_less:
112                         if (ao_accel <= pyro->accel_less)
113                                 continue;
114                         DBG("accel %d > %d\n", ao_accel, pyro->accel_less);
115                         break;
116                 case ao_pyro_accel_greater:
117                         if (ao_accel >= pyro->accel_greater)
118                                 continue;
119                         DBG("accel %d < %d\n", ao_accel, pyro->accel_greater);
120                         break;
121                 case ao_pyro_speed_less:
122                         if (ao_speed <= pyro->speed_less)
123                                 continue;
124                         DBG("speed %d > %d\n", ao_speed, pyro->speed_less);
125                         break;
126                 case ao_pyro_speed_greater:
127                         if (ao_speed >= pyro->speed_greater)
128                                 continue;
129                         DBG("speed %d < %d\n", ao_speed, pyro->speed_greater);
130                         break;
131                 case ao_pyro_height_less:
132                         if (ao_height <= pyro->height_less)
133                                 continue;
134                         DBG("height %d > %d\n", ao_height, pyro->height_less);
135                         break;
136                 case ao_pyro_height_greater:
137                         if (ao_height >= pyro->height_greater)
138                                 continue;
139                         DBG("height %d < %d\n", ao_height, pyro->height_greater);
140                         break;
141
142 #if HAS_GYRO
143                 case ao_pyro_orient_less:
144                         max_orient = ao_sample_max_orient();
145                         if (max_orient <= pyro->orient_less)
146                                 continue;
147                         DBG("orient %d > %d\n", max_orient, pyro->orient_less);
148                         break;
149                 case ao_pyro_orient_greater:
150                         max_orient = ao_sample_max_orient();
151                         if (max_orient >= pyro->orient_greater)
152                                 continue;
153                         DBG("orient %d < %d\n", max_orient, pyro->orient_greater);
154                         break;
155 #endif
156
157                 case ao_pyro_time_less:
158                         if ((AO_TICK_SIGNED) (ao_time() - ao_launch_tick) <= pyro->time_less)
159                                 continue;
160                         DBG("time %d > %d\n", (AO_TICK_SIGNED)(ao_time() - ao_launch_tick), pyro->time_less);
161                         break;
162                 case ao_pyro_time_greater:
163                         if ((AO_TICK_SIGNED) (ao_time() - ao_launch_tick) >= pyro->time_greater)
164                                 continue;
165                         DBG("time %d < %d\n", (AO_TICK_SIGNED)(ao_time() - ao_launch_tick), pyro->time_greater);
166                         break;
167
168                 case ao_pyro_ascending:
169                         if (ao_speed > 0)
170                                 continue;
171                         DBG("not ascending speed %d\n", ao_speed);
172                         break;
173                 case ao_pyro_descending:
174                         if (ao_speed < 0)
175                                 continue;
176                         DBG("not descending speed %d\n", ao_speed);
177                         break;
178
179                 case ao_pyro_after_motor:
180                         if (ao_motor_number >= pyro->motor)
181                                 continue;
182                         DBG("motor %d != %d\n", ao_motor_number, pyro->motor);
183                         break;
184
185                 case ao_pyro_delay:
186                         /* handled separately */
187                         continue;
188
189                 case ao_pyro_state_less:
190                         if (ao_flight_state < pyro->state_less)
191                                 continue;
192                         DBG("state %d >= %d\n", ao_flight_state, pyro->state_less);
193                         break;
194                 case ao_pyro_state_greater_or_equal:
195                         if (ao_flight_state >= pyro->state_greater_or_equal)
196                                 continue;
197                         DBG("state %d < %d\n", ao_flight_state, pyro->state_greater_or_equal);
198                         break;
199
200                 default:
201                         continue;
202                 }
203                 return false;
204         }
205         return true;
206 }
207
208 #ifndef AO_FLIGHT_TEST
209 static void
210 ao_pyro_pin_set(uint8_t p, uint8_t v)
211 {
212         switch (p) {
213 #if AO_PYRO_NUM > 0
214         case 0: ao_gpio_set(AO_PYRO_PORT_0, AO_PYRO_PIN_0, v); break;
215 #endif
216 #if AO_PYRO_NUM > 1
217         case 1: ao_gpio_set(AO_PYRO_PORT_1, AO_PYRO_PIN_1, v); break;
218 #endif
219 #if AO_PYRO_NUM > 2
220         case 2: ao_gpio_set(AO_PYRO_PORT_2, AO_PYRO_PIN_2, v); break;
221 #endif
222 #if AO_PYRO_NUM > 3
223         case 3: ao_gpio_set(AO_PYRO_PORT_3, AO_PYRO_PIN_3, v); break;
224 #endif
225 #if AO_PYRO_NUM > 4
226         case 4: ao_gpio_set(AO_PYRO_PORT_4, AO_PYRO_PIN_4, v); break;
227 #endif
228 #if AO_PYRO_NUM > 5
229         case 5: ao_gpio_set(AO_PYRO_PORT_5, AO_PYRO_PIN_5, v); break;
230 #endif
231 #if AO_PYRO_NUM > 6
232         case 6: ao_gpio_set(AO_PYRO_PORT_6, AO_PYRO_PIN_6, v); break;
233 #endif
234 #if AO_PYRO_NUM > 7
235         case 7: ao_gpio_set(AO_PYRO_PORT_7, AO_PYRO_PIN_7, v); break;
236 #endif
237         default: break;
238         }
239 }
240 #endif
241
242 uint8_t ao_pyro_wakeup;
243
244 static void
245 ao_pyro_pins_fire(uint16_t fire)
246 {
247         uint8_t p;
248
249         for (p = 0; p < AO_PYRO_NUM; p++) {
250                 if (fire & (1 << p))
251                         ao_pyro_pin_set(p, 1);
252         }
253         ao_delay(ao_config.pyro_time);
254         for (p = 0; p < AO_PYRO_NUM; p++) {
255                 if (fire & (1 << p))
256                         ao_pyro_pin_set(p, 0);
257         }
258         ao_delay(AO_MS_TO_TICKS(50));
259 }
260
261 static AO_TICK_TYPE pyro_delay_done[AO_PYRO_NUM];
262
263 static uint8_t
264 ao_pyro_check(void)
265 {
266         const struct ao_pyro    *pyro;
267         uint8_t         p, any_waiting;
268         uint16_t        fire = 0;
269
270         any_waiting = 0;
271         for (p = 0; p < AO_PYRO_NUM; p++) {
272                 pyro = &ao_config.pyro[p];
273
274                 /* Ignore igniters which have already fired or inhibited
275                  */
276                 if ((ao_pyro_fired|ao_pyro_inhibited) & (1 << p))
277                         continue;
278
279                 /* Ignore disabled igniters
280                  */
281                 if (!pyro->flags)
282                         continue;
283
284                 any_waiting = 1;
285                 /* Check pyro state to see if it should fire
286                  */
287                 if (!pyro_delay_done[p]) {
288                         if (!ao_pyro_ready(pyro))
289                                 continue;
290
291                         /* If there's a delay set, then remember when
292                          * it expires
293                          */
294                         if (pyro->flags & ao_pyro_delay) {
295                                 AO_TICK_TYPE delay_done;
296                                 AO_TICK_SIGNED delay = pyro->delay;
297                                 if (delay < 0)
298                                         delay = 0;
299                                 delay_done = ao_time() + (AO_TICK_TYPE) delay;
300
301                                 /*
302                                  * delay_done is the time at which the
303                                  * delay ends, but it is also used as
304                                  * an indication that a delay is
305                                  * active -- non-zero values indicate
306                                  * an active delay. This code ensures
307                                  * the value is non-zero, which might
308                                  * introduce an additional tick
309                                  * of delay.
310                                  */
311                                 if (delay_done == 0)
312                                         delay_done = 1;
313                                 pyro_delay_done[p] = delay_done;
314                         }
315                 }
316
317                 /* Check to see if we're just waiting for
318                  * the delay to expire
319                  */
320                 if (pyro_delay_done[p] != 0) {
321
322                         /* Check to make sure the required conditions
323                          * remain valid. If not, inhibit the channel
324                          * by setting the inhibited bit
325                          */
326                         if (!ao_pyro_ready(pyro)) {
327                                 ao_pyro_inhibited |= (uint16_t) (1 << p);
328                                 continue;
329                         }
330
331                         if ((AO_TICK_SIGNED) (ao_time() - pyro_delay_done[p]) < 0)
332                                 continue;
333                 }
334
335                 fire |= (uint16_t) (1U << p);
336         }
337
338         if (fire) {
339                 ao_pyro_fired |= fire;
340                 ao_pyro_pins_fire(fire);
341         }
342
343         return any_waiting;
344 }
345
346 #define NO_VALUE        0xff
347
348 #define AO_PYRO_NAME_LEN        4
349
350 #if !DISABLE_HELP
351 #define ENABLE_HELP 1
352 #endif
353
354 #if ENABLE_HELP
355 #define HELP(s) (s)
356 #else
357 #define HELP(s)
358 #endif
359
360 const struct {
361         char                    name[AO_PYRO_NAME_LEN];
362         enum ao_pyro_flag       flag;
363         uint8_t                 offset;
364 #if ENABLE_HELP
365         char                    *help;
366 #endif
367 } ao_pyro_values[] = {
368         { "a<", ao_pyro_accel_less,     offsetof(struct ao_pyro, accel_less), HELP("accel less (m/ss * 16)") },
369         { "a>", ao_pyro_accel_greater,  offsetof(struct ao_pyro, accel_greater), HELP("accel greater (m/ss * 16)") },
370
371         { "s<", ao_pyro_speed_less,     offsetof(struct ao_pyro, speed_less), HELP("speed less (m/s * 16)") },
372         { "s>", ao_pyro_speed_greater,  offsetof(struct ao_pyro, speed_greater), HELP("speed greater (m/s * 16)") },
373
374         { "h<", ao_pyro_height_less,    offsetof(struct ao_pyro, height_less), HELP("height less (m)") },
375         { "h>", ao_pyro_height_greater, offsetof(struct ao_pyro, height_greater), HELP("height greater (m)") },
376
377 #if HAS_GYRO
378         { "o<", ao_pyro_orient_less,    offsetof(struct ao_pyro, orient_less), HELP("orient less (deg)") },
379         { "o>", ao_pyro_orient_greater, offsetof(struct ao_pyro, orient_greater), HELP("orient greater (deg)")  },
380 #endif
381
382         { "t<", ao_pyro_time_less,      offsetof(struct ao_pyro, time_less), HELP("time less (s * 100)") },
383         { "t>", ao_pyro_time_greater,   offsetof(struct ao_pyro, time_greater), HELP("time greater (s * 100)")  },
384
385         { "f<", ao_pyro_state_less,     offsetof(struct ao_pyro, state_less), HELP("state less") },
386         { "f>=",ao_pyro_state_greater_or_equal, offsetof(struct ao_pyro, state_greater_or_equal), HELP("state greater or equal")  },
387
388         { "A", ao_pyro_ascending,       NO_VALUE, HELP("ascending") },
389         { "D", ao_pyro_descending,      NO_VALUE, HELP("descending") },
390
391         { "m", ao_pyro_after_motor,     offsetof(struct ao_pyro, motor), HELP("after motor") },
392
393         { "d", ao_pyro_delay,           offsetof(struct ao_pyro, delay), HELP("delay before firing (s * 100)") },
394         { "", ao_pyro_none,             NO_VALUE, HELP(NULL) },
395 };
396
397 #define NUM_PYRO_VALUES (sizeof ao_pyro_values / sizeof ao_pyro_values[0])
398
399 #ifndef AO_FLIGHT_TEST
400 static void
401 ao_pyro(void)
402 {
403         uint8_t         any_waiting;
404
405         ao_config_get();
406         while (ao_flight_state < ao_flight_boost)
407                 ao_sleep(&ao_flight_state);
408
409         for (;;) {
410                 ao_sleep_for(&ao_pyro_wakeup, AO_MS_TO_TICKS(100));
411                 if (ao_flight_state >= ao_flight_landed)
412                         break;
413                 any_waiting = ao_pyro_check();
414                 if (!any_waiting)
415                         break;
416         }
417         ao_exit();
418 }
419
420 struct ao_task ao_pyro_task;
421
422
423 static void
424 ao_pyro_print_name(uint8_t v)
425 {
426         const char *s = ao_pyro_values[v].name;
427         printf ("%s%s", s, "   " + strlen(s));
428 }
429
430 #if ENABLE_HELP
431 static void
432 ao_pyro_help(void)
433 {
434         uint8_t v;
435         for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
436                 ao_pyro_print_name(v);
437                 if (ao_pyro_values[v].offset != NO_VALUE)
438                         printf ("<n> ");
439                 else
440                         printf ("    ");
441                 printf ("%s\n", ao_pyro_values[v].help);
442         }
443 }
444 #endif
445
446 static int32_t
447 ao_pyro_get(void *base, uint8_t offset, uint8_t size)
448 {
449         int32_t value;
450         switch (size) {
451         case 8:
452                 value = *((uint8_t *) ((char *) base + offset));
453                 break;
454         case 16:
455         default:
456                 value = *((int16_t *) (void *) ((char *) base + offset));
457                 break;
458         case 32:
459                 value = *((int32_t *) (void *) ((char *) base + offset));
460                 break;
461         }
462         return value;
463 }
464
465 static bool
466 ao_pyro_put(void *base, uint8_t offset, uint8_t size, int32_t value)
467 {
468         switch (size) {
469         case 8:
470                 if (value < 0)
471                         return false;
472                 *((uint8_t *) ((char *) base + offset)) = (uint8_t) value;
473                 break;
474         case 16:
475         default:
476                 *((int16_t *) (void *) ((char *) base + offset)) = (int16_t) value;
477                 break;
478         case 32:
479                 *((int32_t *) (void *) ((char *) base + offset)) = value;
480                 break;
481         }
482         return true;
483 }
484
485 static uint8_t
486 ao_pyro_size(enum ao_pyro_flag flag)
487 {
488         if (flag & AO_PYRO_8_BIT_VALUE)
489                 return 8;
490         if (flag & AO_PYRO_32_BIT_VALUE)
491                 return 32;
492         return 16;
493 }
494
495 void
496 ao_pyro_show(void)
497 {
498         uint8_t         p;
499         uint8_t         v;
500         struct ao_pyro  *pyro;
501
502         printf ("Pyro-count: %d\n", AO_PYRO_NUM);
503         for (p = 0; p < AO_PYRO_NUM; p++) {
504                 printf ("Pyro %2d: ", p);
505                 pyro = &ao_config.pyro[p];
506                 if (!pyro->flags) {
507                         printf ("<disabled>\n");
508                         continue;
509                 }
510                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
511                         if (!(pyro->flags & ao_pyro_values[v].flag))
512                                 continue;
513                         ao_pyro_print_name(v);
514                         if (ao_pyro_values[v].offset != NO_VALUE) {
515                                 printf ("%6ld ",
516                                         (long) ao_pyro_get(pyro,
517                                                            ao_pyro_values[v].offset,
518                                                            ao_pyro_size(ao_pyro_values[v].flag)));
519                         } else {
520                                 printf ("       ");
521                         }
522                 }
523                 printf ("\n");
524         }
525 }
526
527 void
528 ao_pyro_set(void)
529 {
530         uint32_t        p;
531         struct ao_pyro pyro_tmp;
532         char    name[AO_PYRO_NAME_LEN];
533         uint8_t c;
534         uint8_t v;
535
536         ao_cmd_white();
537
538 #if ENABLE_HELP
539         switch (ao_cmd_lex_c) {
540         case '?':
541                 ao_pyro_help();
542                 return;
543         }
544 #endif
545
546         p = ao_cmd_decimal();
547         if (ao_cmd_status != ao_cmd_success)
548                 return;
549         if (AO_PYRO_NUM <= p) {
550                 printf ("invalid pyro channel %lu\n", p);
551                 return;
552         }
553         memset(&pyro_tmp, '\0', sizeof (pyro_tmp));
554         for (;;) {
555                 ao_cmd_white();
556                 if (ao_cmd_lex_c == '\n')
557                         break;
558
559                 for (c = 0; c < AO_PYRO_NAME_LEN - 1; c++) {
560                         if (ao_cmd_is_white() || ao_cmd_lex_c == '\n')
561                                 break;
562                         name[c] = ao_cmd_lex_c;
563                         ao_cmd_lex();
564                 }
565                 name[c] = '\0';
566                 for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
567                         if (!strcmp (ao_pyro_values[v].name, name))
568                                 break;
569                 }
570                 if (ao_pyro_values[v].flag == ao_pyro_none) {
571                         printf ("invalid pyro field %s\n", name);
572                         ao_cmd_status = ao_cmd_syntax_error;
573                         return;
574                 }
575                 pyro_tmp.flags |= ao_pyro_values[v].flag;
576                 if (ao_pyro_values[v].offset != NO_VALUE) {
577                         int32_t r = 1;
578                         ao_cmd_white();
579                         if (ao_cmd_lex_c == '-') {
580                                 r = -1;
581                                 ao_cmd_lex();
582                         }
583                         r *= (int32_t) ao_cmd_decimal();
584                         if (ao_cmd_status != ao_cmd_success)
585                                 return;
586                         if (!ao_pyro_put(&pyro_tmp, ao_pyro_values[v].offset,
587                                          ao_pyro_size(ao_pyro_values[v].flag), r))
588                         {
589                                 ao_cmd_status = ao_cmd_syntax_error;
590                                 return;
591                         }
592                 }
593         }
594         _ao_config_edit_start();
595         ao_config.pyro[p] = pyro_tmp;
596         _ao_config_edit_finish();
597 }
598
599 void
600 ao_pyro_manual(uint8_t p)
601 {
602         printf ("ao_pyro_manual %d\n", p);
603         if (p >= AO_PYRO_NUM) {
604                 ao_cmd_status = ao_cmd_syntax_error;
605                 return;
606         }
607         ao_pyro_pins_fire(1 << p);
608 }
609
610 struct ao_pyro_old_values {
611         enum ao_pyro_flag       flag;
612         uint8_t                 offset;
613         uint8_t                 size;
614 };
615
616 static const struct ao_pyro_old_values ao_pyro_1_24_values[] = {
617         { .flag = ao_pyro_accel_less, .offset = offsetof(struct ao_pyro_1_24, accel_less), 16 },
618         { .flag = ao_pyro_accel_greater, .offset = offsetof(struct ao_pyro_1_24, accel_greater), 16 },
619         { .flag = ao_pyro_speed_less, .offset = offsetof(struct ao_pyro_1_24, speed_less), 16 },
620         { .flag = ao_pyro_speed_greater, .offset = offsetof(struct ao_pyro_1_24, speed_greater), 16 },
621         { .flag = ao_pyro_height_less, .offset = offsetof(struct ao_pyro_1_24, height_less), 16 },
622         { .flag = ao_pyro_height_greater, .offset = offsetof(struct ao_pyro_1_24, height_greater), 16 },
623         { .flag = ao_pyro_orient_less, .offset = offsetof(struct ao_pyro_1_24, orient_less), 16 },
624         { .flag = ao_pyro_orient_greater, .offset = offsetof(struct ao_pyro_1_24, orient_greater), 16 },
625         { .flag = ao_pyro_time_less, .offset = offsetof(struct ao_pyro_1_24, time_less), 16 },
626         { .flag = ao_pyro_time_greater, .offset = offsetof(struct ao_pyro_1_24, time_greater), 16 },
627         { .flag = ao_pyro_delay, .offset = offsetof(struct ao_pyro_1_24, delay), 16 },
628         { .flag = ao_pyro_state_less, .offset = offsetof(struct ao_pyro_1_24, state_less), 8 },
629         { .flag = ao_pyro_state_greater_or_equal, .offset = offsetof(struct ao_pyro_1_24, state_greater_or_equal), 8 },
630         { .flag = ao_pyro_after_motor, .offset = offsetof(struct ao_pyro_1_24, motor), 16 },
631 };
632
633 #define NUM_PYRO_1_24_VALUES (sizeof ao_pyro_1_24_values / sizeof ao_pyro_1_24_values[0])
634
635 static int32_t
636 ao_pyro_get_1_24(void *base, enum ao_pyro_flag flag)
637 {
638         unsigned v;
639
640         for (v = 0; v < NUM_PYRO_1_24_VALUES; v++) {
641                 if (ao_pyro_1_24_values[v].flag == flag)
642                         return ao_pyro_get(base, ao_pyro_1_24_values[v].offset, ao_pyro_1_24_values[v].size);
643         }
644         return 0;
645 }
646
647 void
648 ao_pyro_update_version(void)
649 {
650         if (ao_config.minor <= 24)
651         {
652
653                 /* First, move all of the config bits that follow the pyro data */
654
655                 struct ao_config_1_24 *ao_config_1_24 = (void *) &ao_config;
656                 struct ao_pyro          *pyro_1_25 = &ao_config.pyro[0];
657                 struct ao_pyro_1_24     *pyro_1_24 = &(ao_config_1_24->pyro)[0];
658
659
660                 char    *pyro_base_1_25 = (void *) pyro_1_25;
661                 char    *pyro_base_1_24 = (void *) pyro_1_24;
662                 char    *after_pyro_1_25 = pyro_base_1_25 + AO_PYRO_NUM * sizeof (struct ao_pyro);
663                 char    *after_pyro_1_24 = pyro_base_1_24 + AO_PYRO_NUM * sizeof (struct ao_pyro_1_24);
664
665                 char    *config_end_1_25 = (void *) (&ao_config + 1);
666                 size_t  to_move = (size_t) (config_end_1_25 - after_pyro_1_25);
667
668                 memmove(after_pyro_1_25, after_pyro_1_24, to_move);
669
670                 /* Now, adjust all of the pyro entries */
671
672                 int p = AO_PYRO_NUM;
673
674                 /* New struct is larger than the old, so start at the
675                  * last one and work towards the first
676                  */
677                 while (p-- > 0) {
678                         unsigned v;
679                         int32_t value;
680                         struct ao_pyro  tmp;
681
682                         memset(&tmp, '\0', sizeof(tmp));
683                         tmp.flags = pyro_1_24[p].flags;
684
685                         for (v = 0; v < NUM_PYRO_VALUES; v++)
686                         {
687                                 if (ao_pyro_values[v].offset != NO_VALUE) {
688                                         value = ao_pyro_get_1_24(&pyro_1_24[p], ao_pyro_values[v].flag);
689                                         ao_pyro_put(&tmp, ao_pyro_values[v].offset,
690                                                     ao_pyro_size(ao_pyro_values[v].flag), value);
691                                 }
692                         }
693                         memcpy(&pyro_1_25[p], &tmp, sizeof(tmp));
694                 }
695         }
696 }
697
698 void
699 ao_pyro_init(void)
700 {
701 #if AO_PYRO_NUM > 0
702         ao_enable_output(AO_PYRO_PORT_0, AO_PYRO_PIN_0, 0);
703 #endif
704 #if AO_PYRO_NUM > 1
705         ao_enable_output(AO_PYRO_PORT_1, AO_PYRO_PIN_1, 0);
706 #endif
707 #if AO_PYRO_NUM > 2
708         ao_enable_output(AO_PYRO_PORT_2, AO_PYRO_PIN_2, 0);
709 #endif
710 #if AO_PYRO_NUM > 3
711         ao_enable_output(AO_PYRO_PORT_3, AO_PYRO_PIN_3, 0);
712 #endif
713 #if AO_PYRO_NUM > 4
714         ao_enable_output(AO_PYRO_PORT_4, AO_PYRO_PIN_4, 0);
715 #endif
716 #if AO_PYRO_NUM > 5
717         ao_enable_output(AO_PYRO_PORT_5, AO_PYRO_PIN_5, 0);
718 #endif
719 #if AO_PYRO_NUM > 6
720         ao_enable_output(AO_PYRO_PORT_6, AO_PYRO_PIN_6, 0);
721 #endif
722 #if AO_PYRO_NUM > 7
723         ao_enable_output(AO_PYRO_PORT_7, AO_PYRO_PIN_7, 0);
724 #endif
725         ao_add_task(&ao_pyro_task, ao_pyro, "pyro");
726 }
727 #endif