chiark / gitweb /
hostside: hidrawconv-joytechneos: fixes and improvements to pseudo-descriptors
[trains.git] / hostside / startup.c
1 /*
2  * realtime
3  * startup state machine
4  */
5
6 #include "realtime.h"
7
8 const char *const stastatelist[]= DEFINE_STASTATE_DATA;
9 StartupState sta_state;
10
11 static TimeoutEvent sta_toev= { .pclass="startup", .pinst="sta" };
12
13 static PicInsn piob;
14 static void sta_goto(StartupState new_state);
15
16 /*---------- ping ----------*/
17
18 static int pong_seq;
19 static TimeoutEvent ping_toev= { .pinst="ping" };
20
21 static void timedout_ping(TimeoutEvent *toev) {
22   assert(sta_state >= Sta_Ping);
23   sta_goto(Sta_Off);
24 }
25
26 static void timefor_ping(TimeoutEvent *toev) {
27   enco_pic_ping(&piob, pong_seq ^ PING_PONG_PATTERN);
28   serial_transmit(&piob);
29   ping_toev.callback= timedout_ping;
30   ping_toev.pclass= "startup";
31   toev_start(&ping_toev);
32 }
33
34 static void initial_ping(void) {
35   struct timeval now;
36   
37   mgettimeofday(&now);
38   pong_seq= (now.tv_sec & 0x1fU) << 5; /* bottom 5bi of secs: period 32s */
39   pong_seq |= (now.tv_usec >> 15);      /* top 5bi of 20bi us: res.~2^15us */
40   ping_toev.duration=  300;
41   timefor_ping(0);
42 }
43
44 /*---------- watchdog ----------*/
45
46 static TimeoutEvent watchdog_toev= {
47   .pclass=toev_fast_pclass,
48   .pinst="watchdog",
49   .duration= UNMARGIN_WATCHDOG - MARGIN_WATCHDOG,
50 };
51 static PicInsn watchdog_piob;
52
53 static void watchdog_transmit(TimeoutEvent *toev) {
54   check_rusage_check(0);
55   toev_start(&watchdog_toev);
56   serial_transmit(&watchdog_piob);
57 }
58
59 static void watchdog_start(void) {
60   check_rusage_baseline();
61   watchdog_toev.callback= watchdog_transmit;
62   enco_pic_watchdog(&watchdog_piob, UNMARGIN_WATCHDOG_16);
63   watchdog_transmit(0);
64 }
65
66 static void watchdog_stop(void) {
67   if (!watchdog_toev.running) return;
68   toev_stop(&watchdog_toev);
69   enco_pic_watchdog(&watchdog_piob, 0);
70   serial_transmit(&watchdog_piob);
71   check_rusage_check(1);
72 }
73
74 /*---------- main startup algorithm ----------*/
75
76 static void timedout_onward(TimeoutEvent *toev) {
77   assert(sta_state != Sta_Run);
78   if (sta_state == Sta_Settling) {
79     enco_pic_off(&piob);
80     serial_transmit(&piob);
81   }
82   sta_goto(sta_state == Sta_Flush ? Sta_Ping : sta_state + 1);
83 }
84
85 static void sta_startup_manual(void) {
86   waggle_startup_manual();
87   retransmit_start();
88   ouprintf("stastate %s\n", stastatelist[sta_state]);
89 }
90
91 void sta_startup(void) {
92   if (sta_state == Sta_Manual) sta_startup_manual();
93   else sta_goto(Sta_Flush);
94 }
95
96 void sta_finalising_done(void) { sta_goto(Sta_Run); }
97
98 static void sta_goto(StartupState new_state) {
99   toev_stop(&sta_toev);
100   sta_toev.callback= timedout_onward;
101   sta_toev.duration= -1;
102
103   if (new_state < Sta_Ping) {
104     toev_stop(&ping_toev);
105   } else if (new_state == Sta_Ping) {
106     initial_ping();
107   } else {
108     assert(sta_state >= Sta_Ping);
109   }
110
111   switch (new_state) {
112   case Sta_Flush:      sta_toev.duration=   300;   break;
113   case Sta_Off:                                    break;
114   case Sta_Manual:                                 abort();
115   case Sta_Ping:                                   break;
116   case Sta_Settling:   sta_toev.duration=   750;   break;
117   case Sta_Resolving:  sta_toev.duration=   500;   break;
118   case Sta_Finalising:                             break;
119   case Sta_Run:                                    break;
120   case Sta_Crashed: abort();
121   }
122
123   if (new_state < Sta_Run)
124     choreographers_all_abandon();
125   if (new_state < Sta_Finalising) {
126     safety_abandon_run();
127     motions_all_abandon();
128   }
129
130   piob.l= 0;
131   switch (new_state) {
132   case Sta_Flush:                                                  break;
133   case Sta_Off:   if (sta_state > Sta_Ping) enco_pic_off(&piob);   break;
134   case Sta_Manual:                                                 abort();
135   case Sta_Ping:                                                   break;
136   case Sta_Settling:   waggle_settle();     enco_pic_off(&piob);   break;
137   case Sta_Resolving:
138     resolve_begin();
139     points_turning_on();
140     adjuncts_start_xmit();
141     enco_pic_on(&piob);
142     break;
143   case Sta_Finalising:
144     if (resolve_complete() <0)
145       /* in this case, we get stuck - user has to power cycle the layout */
146       return;
147     /* resolve_motioncheck will move us to Run eventually, we hope */
148     break;
149   case Sta_Run:
150     if (!simulate)
151       persist_install();
152     retransmit_start();
153     break;
154   case Sta_Crashed: abort();
155   }
156   if (piob.l) serial_transmit(&piob);
157
158   if (new_state >= Sta_Run && !disable_watchdog) watchdog_start();
159   else watchdog_stop();
160      
161   toev_start(&sta_toev);
162   sta_state= new_state;
163
164   /* notify various people: */
165   ouprintf("stastate %s\n", stastatelist[sta_state]);
166   /* ... add others here. */
167   if (sta_state == Sta_Finalising) resolve_motioncheck();
168 }   
169
170 void serial_moredata(PicInsn *buf) {
171   const PicInsnInfo *pii;
172   int obj, v, suppress;
173   Byte *ep;
174
175   /* Called when more data is received from PICs.
176    * On entry, buf->l is amount of data available.
177    * Does one of the following:
178    *  - determines that there is no complete message; sets buf->l = 0
179    *  - finds and handles one PicInsn message, sets buf->l = message length
180    *  - handles some series of bytes structured some other way,
181    *       sets buf->l to the numer of bytes handled.
182    */
183   assert(buf->l > 0);
184   
185   if (sta_state == Sta_Flush) {
186     ouhex("picioh in junk", buf->d, buf->l);
187     toev_start(&sta_toev);
188     return; /* junk absolutely everything */
189   }
190   if (PICMSG_AAARGH_P(buf->d[0])) {
191     ouhex("picioh in aaargh", buf->d, buf->l);
192     die("PIC sent us AAARGH!");
193   }
194   if (PICMSG_HELLO_P(buf->d[0])) {
195     ouhex("picioh in hello", buf->d, 1);
196     if (sta_state != Sta_Manual)
197       sta_goto(Sta_Flush);
198     buf->l= 1;
199     return;
200   }
201   if (sta_state == Sta_Off) {
202     ouhex("picioh in off", buf->d, 1);
203     buf->l= 1;
204     return;
205   }
206
207   assert(sta_state >= Sta_Manual);
208   /* So, we are expecting properly formed messages. */
209   
210   for (ep= buf->d; ep < buf->d + buf->l; ep++)
211     if (!(*ep & 0x80u))
212       goto found_end;
213
214   if (buf->l == sizeof(buf->d)) {
215     ouhex("picioh in toolong", buf->d, buf->l);
216     die("PIC sent packet too long");
217   }
218   buf->l= 0; /* message not yet finished, so consume nothing */
219   return;
220
221  found_end:
222   /* Aha! */
223   buf->l= ep - buf->d + 1;
224   picinsn_decode(buf, pic_reply_infos, &pii, &obj, &v);
225   suppress= pii && pii->noiselevel > picio_send_noise;
226
227   if (!suppress && picio_send_noise >= 2)
228     ouhex_nosim("picioh in msg", buf->d, buf->l);
229
230   if (simlog_full || sta_state < Sta_Settling ||
231       !((pii->opcode==PICMSG_NMRADONE && obj==1) ||
232         (pii->opcode==PICMSG_PONG && obj==pong_seq)))
233     simlog_serial(buf->d, buf->l);
234   
235   if (!pii) { ouprintf("picio in unknown\n"); return; }
236   if (!suppress)
237     oupicio("in",pii,obj,v,ouprintf_only);
238   pii->input_fn(pii,buf,obj);
239 }
240
241 void on_pic_nul(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
242   /* layout turned off, probably */
243   if (sta_state == Sta_Manual) return;
244   enco_pic_off(&piob);
245   serial_transmit(&piob);
246   sta_goto(Sta_Flush);
247   return;
248 }
249
250 void on_pic_pong(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
251   if (sta_state == Sta_Manual)
252     return;
253
254   if (objnum != pong_seq) {
255     if (objnum == (pong_seq^PING_PONG_PATTERN))
256       die("PIC connection is looped back (ping %#05x bounced)", objnum);
257     die("PIC sent wrong ping response (%#05x, wanted %#05x)", objnum, pong_seq);
258   }
259
260   ping_toev.duration= 1000;
261   ping_toev.callback= timefor_ping;
262   ping_toev.pclass= toev_fast_pclass;
263   toev_start(&ping_toev);
264
265   if (sta_state == Sta_Ping)
266     sta_goto(Sta_Settling);
267 }
268
269 void on_pic_fault(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
270   if (sta_state <= Sta_Ping) return;
271   die("FAULT");
272 }
273
274 void on_pic_wtimeout(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
275   if (sta_state <= Sta_Settling) return;
276   check_rusage_check(1);
277   die("microcontrollers' watchdog timer triggered\n");
278 }
279
280 void on_pic_hello(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
281   { abort(); }
282 void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
283   { abort(); }
284 void on_pic_spurious(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
285   ouprintf("warning spurious %d : spurious short circuit (fault)"
286            " detection interrupts\n", objnum);
287 }
288 void on_pic_pointed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { }
289 void on_pic_retriable(const PicInsnInfo *pii, const PicInsn *pi, int objnum){}
290
291 static int coalescing_detects;
292
293 static void detect_report_now(Segment *seg) {
294   ouprintf_only("detect %s %d\n", seg->i->pname, seg->detect_actual);
295   seg->detect_reported= seg->detect_actual;
296 }
297
298 static SegmentNum on_pic_detect_prep(int detyn, int objnum) {
299   SegmentNum segn;
300
301   if (objnum >= info_segmentmaplen ||
302       (segn= info_segmentmap[objnum]) < 0)
303     die("PIC sent detect%d @#%#x not in map",detyn,objnum);
304
305   Segment *seg= &segments[segn];
306   seg->detect_actual= detyn;
307
308   if (!(picio_send_noise <= 1 &&
309         seg->owner &&
310         seg->det_ignore)) {
311     if (cmdi.out.total >= 1024) coalescing_detects= 1;
312
313     if (coalescing_detects &&
314         seg->det_ignore &&
315         seg->detect_reported) {
316       assert(seg->detect_flaps < INT_MAX);
317       seg->detect_flaps++;
318     } else {
319       detect_report_now(seg);
320     }
321   }
322
323   return segn;
324 }
325
326 void cmdi_output_bufferempty(OutBufferChain *obc) {
327   SEG_IV;
328   
329   if (!coalescing_detects) return;
330   coalescing_detects= 0;
331
332   FOR_SEG {
333     if (seg->detect_flaps) {
334       ouprintf_only("detect-flaps %s %d\n", segi->pname, seg->detect_flaps);
335       seg->detect_flaps= 0;
336     }
337     if (seg->detect_actual != seg->detect_reported) {
338       detect_report_now(seg);
339     }
340   }
341 }
342
343 void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
344   SegmentNum segn;
345   Segment *seg;
346   const char *pname;
347   
348   segn= on_pic_detect_prep(1,objnum);
349   seg= &segments[segn];
350   pname= info_segments[segn].pname;
351   
352   switch (sta_state) {
353   case Sta_Flush:
354   case Sta_Off:
355   case Sta_Manual:
356   case Sta_Ping:
357   case Sta_Settling:
358     break;
359   case Sta_Resolving:
360     seg->res_detect= 1;
361     break;
362   case Sta_Finalising:
363     if (!seg->res_detect) {
364       ouprintf("resolution new-detection-in-finalising @%s\n", pname);
365       sta_goto(Sta_Settling);
366     }
367     break;
368   case Sta_Run:
369     safety_notify_detection(seg);
370     break;
371   case Sta_Crashed: abort();
372   }
373 }
374
375 void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
376   on_pic_detect_prep(0,objnum);
377 }
378
379 void choreographers_all_abandon(void) { }