initial commit for source
[gnuk/neug.git] / src / main.c
1 /*
2  * main.c - main routine of neug
3  *
4  * Copyright (C) 2011 Free Software Initiative of Japan
5  * Author: NIIBE Yutaka <gniibe@fsij.org>
6  *
7  * This file is a part of NeuG, a Random Number Generator
8  * implementation.
9  *
10  * NeuG is free software: you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Gnuk is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
18  * License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "config.h"
26
27 #include "ch.h"
28 #include "hal.h"
29 #include "usb_cdc.h"
30
31 #include "hw_config.h"
32
33 /*
34  * USB Driver structure.
35  */
36 static SerialUSBDriver SDU1;
37
38 /*
39  * USB Device Descriptor.
40  */
41 static const uint8_t vcom_device_descriptor_data[18] = {
42   USB_DESC_DEVICE       (0x0200,        /* bcdUSB (2.0).                    */
43                          0x02,          /* bDeviceClass (CDC).              */
44                          0x00,          /* bDeviceSubClass.                 */
45                          0x00,          /* bDeviceProtocol.                 */
46                          0x40,          /* bMaxPacketSize.                  */
47                          0x234b,        /* idVendor (FSIJ).                   */
48                          0x0001,        /* idProduct.                       */
49                          0x0100,        /* bcdDevice.                       */
50                          1,             /* iManufacturer.                   */
51                          2,             /* iProduct.                        */
52                          3,             /* iSerialNumber.                   */
53                          1)             /* bNumConfigurations.              */
54 };
55
56 /*
57  * Device Descriptor wrapper.
58  */
59 static const USBDescriptor vcom_device_descriptor = {
60   sizeof vcom_device_descriptor_data,
61   vcom_device_descriptor_data
62 };
63
64 /* Configuration Descriptor tree for a CDC.*/
65 static const uint8_t vcom_configuration_descriptor_data[67] = {
66   /* Configuration Descriptor.*/
67   USB_DESC_CONFIGURATION(67,            /* wTotalLength.                    */
68                          0x02,          /* bNumInterfaces.                  */
69                          0x01,          /* bConfigurationValue.             */
70                          0,             /* iConfiguration.                  */
71                          0x80,          /* bmAttributes (bus powered).      */
72                          50),           /* bMaxPower (100mA).               */
73   /* Interface Descriptor.*/
74   USB_DESC_INTERFACE    (0x00,          /* bInterfaceNumber.                */
75                          0x00,          /* bAlternateSetting.               */
76                          0x01,          /* bNumEndpoints.                   */
77                          0x02,          /* bInterfaceClass (Communications
78                                            Interface Class, CDC section
79                                            4.2).                            */
80                          0x02,          /* bInterfaceSubClass (Abstract
81                                          Control Model, CDC section 4.3).   */
82                          0x01,          /* bInterfaceProtocol (AT commands,
83                                            CDC section 4.4).                */
84                          0),            /* iInterface.                      */
85   /* Header Functional Descriptor (CDC section 5.2.3).*/
86   USB_DESC_BYTE         (5),            /* bLength.                         */
87   USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */
88   USB_DESC_BYTE         (0x00),         /* bDescriptorSubtype (Header
89                                            Functional Descriptor.           */
90   USB_DESC_BCD          (0x0110),       /* bcdCDC.                          */
91   /* Call Management Functional Descriptor. */
92   USB_DESC_BYTE         (5),            /* bFunctionLength.                 */
93   USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */
94   USB_DESC_BYTE         (0x01),         /* bDescriptorSubtype (Call Management
95                                            Functional Descriptor).          */
96   USB_DESC_BYTE         (0x00),         /* bmCapabilities (D0+D1).          */
97   USB_DESC_BYTE         (0x01),         /* bDataInterface.                  */
98   /* ACM Functional Descriptor.*/
99   USB_DESC_BYTE         (4),            /* bFunctionLength.                 */
100   USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */
101   USB_DESC_BYTE         (0x02),         /* bDescriptorSubtype (Abstract
102                                            Control Management Descriptor).  */
103   USB_DESC_BYTE         (0x02),         /* bmCapabilities.                  */
104   /* Union Functional Descriptor.*/
105   USB_DESC_BYTE         (5),            /* bFunctionLength.                 */
106   USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */
107   USB_DESC_BYTE         (0x06),         /* bDescriptorSubtype (Union
108                                            Functional Descriptor).          */
109   USB_DESC_BYTE         (0x00),         /* bMasterInterface (Communication
110                                            Class Interface).                */
111   USB_DESC_BYTE         (0x01),         /* bSlaveInterface0 (Data Class
112                                            Interface).                      */
113   /* Endpoint 2 Descriptor.*/
114   USB_DESC_ENDPOINT     (INTERRUPT_REQUEST_EP|0x80, /* bEndpointAddress.    */
115                          0x03,          /* bmAttributes (Interrupt).        */
116                          0x0008,        /* wMaxPacketSize.                  */
117                          0xFF),         /* bInterval.                       */
118   /* Interface Descriptor.*/
119   USB_DESC_INTERFACE    (0x01,          /* bInterfaceNumber.                */
120                          0x00,          /* bAlternateSetting.               */
121                          0x02,          /* bNumEndpoints.                   */
122                          0x0A,          /* bInterfaceClass (Data Class
123                                            Interface, CDC section 4.5).     */
124                          0x00,          /* bInterfaceSubClass (CDC section
125                                            4.6).                            */
126                          0x00,          /* bInterfaceProtocol (CDC section
127                                            4.7).                            */
128                          0x00),         /* iInterface.                      */
129   /* Endpoint 3 Descriptor.*/
130   USB_DESC_ENDPOINT     (DATA_AVAILABLE_EP,         /* bEndpointAddress.    */
131                          0x02,          /* bmAttributes (Bulk).             */
132                          0x0040,        /* wMaxPacketSize.                  */
133                          0x00),         /* bInterval.                       */
134   /* Endpoint 1 Descriptor.*/
135   USB_DESC_ENDPOINT     (DATA_REQUEST_EP|0x80,      /* bEndpointAddress.    */
136                          0x02,          /* bmAttributes (Bulk).             */
137                          0x0040,        /* wMaxPacketSize.                  */
138                          0x00)          /* bInterval.                       */
139 };
140
141
142 /*
143  * Configuration Descriptor wrapper.
144  */
145 static const USBDescriptor vcom_configuration_descriptor = {
146   sizeof vcom_configuration_descriptor_data,
147   vcom_configuration_descriptor_data
148 };
149
150 /*
151  * U.S. English language identifier.
152  */
153 static const uint8_t vcom_string0[] = {
154   USB_DESC_BYTE(4),                     /* bLength.                         */
155   USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */
156   USB_DESC_WORD(0x0409)                 /* wLANGID (U.S. English).          */
157 };
158
159 /*
160  * Vendor string.
161  */
162 static const uint8_t vcom_string1[] = {
163   USB_DESC_BYTE(68),                    /* bLength.                         */
164   USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */
165   /* Manufacturer: "Free Software Initiative of Japan" */
166   'F', 0, 'r', 0, 'e', 0, 'e', 0, ' ', 0, 'S', 0, 'o', 0, 'f', 0,
167   't', 0, 'w', 0, 'a', 0, 'r', 0, 'e', 0, ' ', 0, 'I', 0, 'n', 0,
168   'i', 0, 't', 0, 'i', 0, 'a', 0, 't', 0, 'i', 0, 'v', 0, 'e', 0,
169   ' ', 0, 'o', 0, 'f', 0, ' ', 0, 'J', 0, 'a', 0, 'p', 0, 'a', 0,
170   'n', 0
171 };
172
173 /*
174  * Device Description string.
175  */
176 static const uint8_t vcom_string2[] = {
177   USB_DESC_BYTE(18),                    /* bLength.                         */
178   USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */
179   /* Product name: "NeuG RNG" */
180   'N', 0, 'e', 0, 'u', 0, 'G', 0, ' ', 0, 'R', 0, 'N', 0, 'G', 0,
181 };
182
183 /*
184  * Serial Number string.
185  */
186 static const uint8_t vcom_string3[] = {
187   USB_DESC_BYTE(8),                     /* bLength.                         */
188   USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */
189   '0' , 0,  '.' , 0,  '1' , 0
190 };
191
192 /*
193  * Strings wrappers array.
194  */
195 static const USBDescriptor vcom_strings[] = {
196   {sizeof vcom_string0, vcom_string0},
197   {sizeof vcom_string1, vcom_string1},
198   {sizeof vcom_string2, vcom_string2},
199   {sizeof vcom_string3, vcom_string3}
200 };
201
202 /*
203  * Handles the GET_DESCRIPTOR callback. All required descriptors must be
204  * handled here.
205  */
206 static const USBDescriptor *get_descriptor(USBDriver *usbp,
207                                            uint8_t dtype,
208                                            uint8_t dindex,
209                                            uint16_t lang) {
210
211   (void)usbp;
212   (void)lang;
213   switch (dtype) {
214   case USB_DESCRIPTOR_DEVICE:
215     return &vcom_device_descriptor;
216   case USB_DESCRIPTOR_CONFIGURATION:
217     return &vcom_configuration_descriptor;
218   case USB_DESCRIPTOR_STRING:
219     if (dindex < 4)
220       return &vcom_strings[dindex];
221   }
222   return NULL;
223 }
224
225 /**
226  * @brief   EP1 initialization structure (IN only).
227  */
228 static const USBEndpointConfig ep1config = {
229   USB_EP_MODE_TYPE_BULK | USB_EP_MODE_PACKET,
230   NULL,
231   sduDataTransmitted,
232   NULL,
233   0x0040,
234   0x0000,
235   NULL,
236   NULL
237 };
238
239 /**
240  * @brief   EP2 initialization structure (IN only).
241  */
242 static const USBEndpointConfig ep2config = {
243   USB_EP_MODE_TYPE_INTR | USB_EP_MODE_PACKET,
244   NULL,
245   sduInterruptTransmitted,
246   NULL,
247   0x0010,
248   0x0000,
249   NULL,
250   NULL
251 };
252
253 /**
254  * @brief   EP3 initialization structure (OUT only).
255  */
256 static const USBEndpointConfig ep3config = {
257   USB_EP_MODE_TYPE_BULK | USB_EP_MODE_PACKET,
258   NULL,
259   NULL,
260   sduDataReceived,
261   0x0000,
262   0x0040,
263   NULL,
264   NULL
265 };
266
267 /*
268  * Handles the USB driver global events.
269  */
270 static void usb_event(USBDriver *usbp, usbevent_t event) {
271
272   switch (event) {
273   case USB_EVENT_RESET:
274     return;
275   case USB_EVENT_ADDRESS:
276     return;
277   case USB_EVENT_CONFIGURED:
278     /* Enables the endpoints specified into the configuration.
279        Note, this callback is invoked from an ISR so I-Class functions
280        must be used.*/
281     chSysLockFromIsr();
282     usbInitEndpointI(usbp, DATA_REQUEST_EP, &ep1config);
283     usbInitEndpointI(usbp, INTERRUPT_REQUEST_EP, &ep2config);
284     usbInitEndpointI(usbp, DATA_AVAILABLE_EP, &ep3config);
285     chSysUnlockFromIsr();
286     return;
287   case USB_EVENT_SUSPEND:
288     return;
289   case USB_EVENT_WAKEUP:
290     return;
291   case USB_EVENT_STALLED:
292     return;
293   }
294   return;
295 }
296
297
298 /*
299  * Serial over USB driver configuration.
300  */
301 static const SerialUSBConfig serusbcfg = {
302   &USBD1,
303   {
304     usb_event,
305     get_descriptor,
306     sduRequestsHook,
307     NULL
308   }
309 };
310 \f
311 struct stdout {
312   Mutex m;
313   CondVar start_cnd;
314   CondVar finish_cnd;
315   const char *str;
316   int size;
317 };
318
319 static struct stdout stdout;
320
321 static void
322 stdout_init (void)
323 {
324   chMtxInit (&stdout.m);
325   chCondInit (&stdout.start_cnd);
326   chCondInit (&stdout.finish_cnd);
327   stdout.size = 0;
328   stdout.str = NULL;
329 }
330
331 void
332 write (const char *s, int size)
333 {
334   if (size == 0)
335     return;
336
337   chMtxLock (&stdout.m);
338   while (stdout.str)
339     chCondWait (&stdout.finish_cnd);
340   stdout.str = s;
341   stdout.size = size;
342   chCondSignal (&stdout.start_cnd);
343   chCondWait (&stdout.finish_cnd);
344   chMtxUnlock ();
345 }
346
347 Thread *stdout_thread;
348 static uint32_t count_in;
349 static uint8_t buffer_in[VIRTUAL_COM_PORT_DATA_SIZE];
350
351 static WORKING_AREA(waSTDOUTthread, 128);
352
353 static msg_t
354 STDOUTthread (void *arg)
355 {
356   (void)arg;
357   stdout_thread = chThdSelf ();
358
359  again:
360
361   while (1)
362     {
363       if (bDeviceState == CONFIGURED)
364         break;
365
366       chThdSleepMilliseconds (100);
367     }
368
369   while (1)
370     {
371       const char *p;
372       int len;
373
374       if (bDeviceState != CONFIGURED)
375         break;
376
377       chMtxLock (&stdout.m);
378       if (stdout.str == NULL)
379         chCondWait (&stdout.start_cnd);
380
381       p = stdout.str;
382       len = stdout.size;
383       while (len > 0)
384         {
385           int i;
386
387           if (len < VIRTUAL_COM_PORT_DATA_SIZE)
388             {
389               for (i = 0; i < len; i++)
390                 buffer_in[i] = p[i];
391               count_in = len;
392               len = 0;
393             }
394           else
395             {
396               for (i = 0; i < VIRTUAL_COM_PORT_DATA_SIZE; i++)
397                 buffer_in[i] = p[i];
398               len -= VIRTUAL_COM_PORT_DATA_SIZE;
399               count_in = VIRTUAL_COM_PORT_DATA_SIZE;
400               p += count_in;
401             }
402
403           chEvtClear (EV_TX_READY);
404
405           USB_SIL_Write (EP3_IN, buffer_in, count_in);
406           SetEPTxValid (ENDP3);
407
408           chEvtWaitOne (EV_TX_READY);
409         }
410
411       stdout.str = NULL;
412       stdout.size = 0;
413       chCondBroadcast (&stdout.finish_cnd);
414       chMtxUnlock ();
415     }
416
417   goto again;
418   return 0;
419 }
420
421
422 static WORKING_AREA(waUSBthread, 128);
423 extern msg_t USBthread (void *arg);
424
425 /*
426  * main thread does 1-bit LED display output
427  */
428 #define LED_TIMEOUT_INTERVAL    MS2ST(100)
429 #define LED_TIMEOUT_ZERO        MS2ST(50)
430 #define LED_TIMEOUT_ONE         MS2ST(200)
431 #define LED_TIMEOUT_STOP        MS2ST(500)
432
433
434 #define ID_OFFSET 12
435 static void
436 device_initialize_once (void)
437 {
438   const uint8_t *p = &gnukStringSerial[ID_OFFSET];
439
440   if (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff && p[3] == 0xff)
441     {
442       /*
443        * This is the first time invocation.
444        * Setup serial number by unique device ID.
445        */
446       const uint8_t *u = unique_device_id ();
447       int i;
448
449       for (i = 0; i < 4; i++)
450         {
451           uint8_t b = u[i];
452           uint8_t nibble; 
453
454           nibble = (b >> 4);
455           nibble += (nibble >= 10 ? ('A' - 10) : '0');
456           flash_put_data_internal (&p[i*4], nibble);
457           nibble = (b & 0x0f);
458           nibble += (nibble >= 10 ? ('A' - 10) : '0');
459           flash_put_data_internal (&p[i*4+2], nibble);
460         }
461     }
462 }
463
464
465 Thread *main_thread;
466 \f
467 /* Total number of channels to be sampled by a single ADC operation.*/
468 #define ADC_GRP1_NUM_CHANNELS   2
469  
470 /* Depth of the conversion buffer, channels are sampled one time each.*/
471 #define ADC_GRP1_BUF_DEPTH      4
472  
473 /*
474  * ADC samples buffer.
475  */
476 static adcsample_t samp[ADC_GRP1_NUM_CHANNELS * ADC_GRP1_BUF_DEPTH];
477  
478 #define ADC_SAMPLE_1P5          0   /**< @brief 1.5 cycles sampling time.   */
479 #define ADC_SAMPLE_13P5         2   /**< @brief 13.5 cycles sampling time.  */
480 #define ADC_SAMPLE_239P5        7   /**< @brief 239.5 cycles sampling time. */
481
482 static void adccb (adcsample_t *buffer, size_t n);
483
484 /*
485  * ADC conversion group.
486  * Mode:        Linear buffer, 4 samples of 2 channels, SW triggered.
487  * Channels:    Vref   (239.5 cycles sample time)
488  *              Sensor (239.5 cycles sample time)
489  */
490 static const ADCConversionGroup adcgrpcfg = {
491   FALSE,
492   ADC_GRP1_NUM_CHANNELS,
493   0,
494   ADC_CR2_EXTSEL_SWSTART | ADC_CR2_TSVREFE | ADC_CR2_CONT,
495   ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_1P5) | ADC_SMPR1_SMP_VREF(ADC_SAMPLE_1P5),
496   0,
497   ADC_SQR1_NUM_CH(ADC_GRP1_NUM_CHANNELS),
498   0,
499   ADC_SQR3_SQ2_N(ADC_CHANNEL_SENSOR) | ADC_SQR3_SQ1_N(ADC_CHANNEL_VREFINT)
500 };
501
502 /*
503  * ADC end conversion callback.
504  */
505 static void adccb (adcsample_t *buffer, size_t n)
506 {
507   ADCDriver *adcp = &ADCD1;
508
509   (void) buffer; (void) n;
510
511   chSysLockFromIsr();
512   if (adcp->ad_state == ADC_COMPLETE)
513     chEvtSignalI (main_thread, (eventmask_t)1);
514   chSysUnlockFromIsr();
515 }
516 \f
517 static volatile uint8_t fatal_code;
518
519 #define TMT_MAT1 0x8f7011ee
520 #define TMT_MAT2 0xfc78ff1f
521 #define TMT_TMAT 0x3793fdff
522
523 static uint32_t tmt[4] = { 0x56781234, TMT_MAT1, TMT_MAT2, TMT_TMAT };
524
525 #define TMT_CALC_LASTWORD(y,v) (y^v)
526
527 static void tmt_one_step (uint32_t v)
528 {
529   uint32_t x, y;
530
531   y = tmt[3];
532   x = (tmt[0] & 0x7fffffff) ^ tmt[1] ^ tmt[2];
533   x ^= (x << 1);
534   y ^= (y >> 1) ^ x;
535   tmt[0] = tmt[1];
536   tmt[1] = tmt[2];
537   tmt[2] = x ^ (y << 10);
538   tmt[3] = TMT_CALC_LASTWORD(y, v);
539   if ((y & 1))
540     {
541       tmt[1] ^= TMT_MAT1;
542       tmt[2] ^= TMT_MAT2;
543     }
544 }
545
546 static uint32_t tmt_value (void)
547 {
548   uint32_t t0, t1;
549
550   t0 = tmt[3];
551   t1 = tmt[0] + (tmt[2] >> 8);
552   t0 ^= t1;
553   if ((t1 & 1))
554     t0 ^= TMT_TMAT;
555   return t0;
556 }
557
558 /* 8 parallel CRC-16 shift registers */
559 static uint8_t epool[16];       /* Big-endian */
560 static uint8_t ep_count;
561
562 static void ep_add (uint8_t bits)
563 {
564   uint8_t v = epool[ep_count];
565
566   /* CRC-16-CCITT's Polynomial is: x^16 + x^12 + x^5 + 1 */
567   epool[ep_count] ^= bits;
568   epool[(ep_count - 5)& 0x0f] ^= v;
569   epool[(ep_count - 12)& 0x0f] ^= v;
570
571   ep_count = (ep_count + 1) & 0x0f;
572 }
573
574 static uint32_t ep_value (void)
575 {
576   unsigned int v = (epool[ep_count] << 24)
577                     | (epool[(ep_count + 1) & 0x0f] << 16)
578                     | (epool[(ep_count + 2) & 0x0f] << 8)
579                     | epool[(ep_count + 3) & 0x0f];
580   return v;
581 }
582
583
584
585 /*
586  * Entry point.
587  *
588  * NOTE: the main function is already a thread in the system on entry.
589  *       See the hwinit1_common function.
590  */
591 int
592 main (int argc, char **argv)
593 {
594   int count = 0;
595
596   (void)argc;
597   (void)argv;
598
599   halInit();
600   chSysInit();
601
602
603   /*
604    * Activates the USB driver and then the USB bus pull-up on D+.
605    */
606   sduObjectInit(&SDU1);
607   sduStart(&SDU1, &serusbcfg);
608   USB_Cable_Config (ENABLE);
609
610   main_thread = chThdSelf ();
611
612   device_initialize_once ();
613
614   adcStart (&ADCD1, NULL);
615   adcStartConversion (&ADCD1, &adcgrpcfg, samp, ADC_GRP1_BUF_DEPTH, adccb);
616
617   stdout_init ();
618
619   /*
620    * Creates 'stdout' thread.
621    */
622   chThdCreateStatic (waSTDOUTthread, sizeof(waSTDOUTthread),
623                      NORMALPRIO, STDOUTthread, NULL);
624
625   chThdCreateStatic (waUSBthread, sizeof(waUSBthread),
626                      NORMALPRIO, USBthread, NULL);
627
628   while (1)
629     {
630       eventmask_t m;
631
632       count++;
633
634       if (bDeviceState != CONFIGURED)
635         continue;
636
637       m = chEvtWaitOne (ALL_EVENTS);
638
639       if (m == (eventmask_t)1)
640         {
641           static uint8_t round;
642           uint8_t b;
643
644           if ((round & 1))
645             b = (((samp[0] & 0x01) << 0) | ((samp[1] & 0x01) << 1)
646                  | ((samp[2] & 0x01) << 2) | ((samp[3] & 0x01) << 3)
647                  | ((samp[4] & 0x01) << 4) | ((samp[5] & 0x01) << 5)
648                  | ((samp[6] & 0x01) << 6) | ((samp[7] & 0x01) << 7));
649           else
650             b = (((samp[0] & 0x01) << 1) | ((samp[1] & 0x01) << 0)
651                  | ((samp[2] & 0x01) << 3) | ((samp[3] & 0x01) << 2)
652                  | ((samp[4] & 0x01) << 5) | ((samp[5] & 0x01) << 4)
653                  | ((samp[6] & 0x01) << 7) | ((samp[7] & 0x01) << 6));
654
655           adcStartConversion (&ADCD1, &adcgrpcfg, samp,
656                               ADC_GRP1_BUF_DEPTH, adccb);
657
658           ep_add (b);
659           if (++round >= 7)
660             {
661               static uint8_t r;
662               char s[32];
663               uint32_t x = ep_value ();
664
665               tmt_one_step (((SysTick->VAL) & 6) == 0);
666               x ^= tmt_value ();
667
668               memcpy (s + (r&7)*4, (const char *)&x, 4);
669               r = (r + 1) & 7;
670               if (r == 0)
671                 {
672                   static uint8_t led;
673
674                   led++;
675                   set_led ((led & 0x80) == 0);
676                   _write (s, 32);
677                 }
678
679               round = 0;
680             }
681         }
682     }
683
684   return 0;
685 }
686
687 void
688 fatal (uint8_t code)
689 {
690   fatal_code = code;
691   for (;;);
692 }