fix reset handling
[gnuk/neug.git] / src / main.c
index eab5296..1bba0b5 100644 (file)
@@ -2,7 +2,8 @@
  * main.c - main routine of neug
  *
  * Main routine:
- * Copyright (C) 2011, 2012, 2013 Free Software Initiative of Japan
+ * Copyright (C) 2011, 2012, 2013, 2015
+ *              Free Software Initiative of Japan
  * Author: NIIBE Yutaka <gniibe@fsij.org>
  *
  * This file is a part of NeuG, a True Random Number Generator
@@ -40,9 +41,14 @@ enum {
   FSIJ_DEVICE_EXITED,
   FSIJ_DEVICE_EXEC_REQUESTED,
   /**/
+  FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED = 254,
   FSIJ_DEVICE_NEUG_EXIT_REQUESTED = 255
 }; 
 
+#ifdef FRAUCHEKY_SUPPORT
+static uint8_t running_neug;
+#endif
+
 static chopstx_mutex_t usb_mtx;
 static chopstx_cond_t cnd_usb;
 static uint32_t bDeviceState = UNCONNECTED; /* USB device status */
@@ -82,7 +88,7 @@ static const uint8_t vcom_device_desc[18] = {
 };
 
 /* Configuration Descriptor tree for a CDC.*/
-static const uint8_t vcom_configuration_desc[67] = {
+static const uint8_t vcom_config_desc[67] = {
   9,
   USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
   /* Configuration Descriptor.*/
@@ -174,40 +180,43 @@ static const uint8_t vcom_string0[4] = {
   0x09, 0x04                   /* LangID = 0x0409: US-English */
 };
 
-#define USB_STRINGS_FOR_NEUG 1
 #include "usb-strings.c.inc"
 
-/*
- * Serial Number string.  NOTE: This does not have CONST qualifier.
- */
-static uint8_t vcom_string3[28] = {
-  28,                               /* bLength.                         */
-  USB_STRING_DESCRIPTOR_TYPE,       /* bDescriptorType.                 */
-  '0', 0,  '.', 0,  '1', 0, '1', 0, /* Version number of NeuG.          */
-  '-', 0,
-  0, 0, 0, 0,  /* Filled by Unique device ID.      */
-  0, 0, 0, 0,
-  0, 0, 0, 0,
-  0, 0, 0, 0,
-};
+#ifdef FRAUCHEKY_SUPPORT
+extern int fraucheky_enabled (void);
+extern void fraucheky_main (void);
 
+extern void fraucheky_setup_endpoints_for_interface (int stop);
+extern int fraucheky_setup (uint8_t req, uint8_t req_no,
+                           struct control_info *detail);
+extern int fraucheky_get_descriptor (uint8_t rcp, uint8_t desc_type,
+                                    uint8_t desc_index,
+                                    struct control_info *detail);
+#endif
 
 #define NUM_INTERFACES 2
 
-
 void
 usb_cb_device_reset (void)
 {
-  /* Set DEVICE as not configured */
+  /* Set DEVICE as not configured */
   usb_lld_set_configuration (0);
 
-  /* Current Feature initialization */
-  usb_lld_set_feature (vcom_configuration_desc[7]);
+  /* Current Feature initialization */
+  usb_lld_set_feature (vcom_config_desc[7]);
 
   usb_lld_reset ();
 
-  /* Initialize Endpoint 0 */
+  /* Initialize Endpoint 0 */
   usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, 64);
+
+  /* Notify upper layer.  */
+  chopstx_mutex_lock (&usb_mtx);
+  bDeviceState = ATTACHED;
+  connected = 0;
+  if (!wait_usb_connection)
+    chopstx_cond_signal (&cnd_usb);
+  chopstx_mutex_unlock (&usb_mtx);
 }
 
 extern uint8_t _regnual_start, __heap_end__;
@@ -218,9 +227,31 @@ static const uint8_t *const mem_info[] = { &_regnual_start,  &__heap_end__, };
 #define USB_FSIJ_MEMINFO         0
 #define USB_FSIJ_DOWNLOAD        1
 #define USB_FSIJ_EXEC            2
+#define USB_NEUG_SET_PASSWD    253
 #define USB_NEUG_GET_INFO      254
 #define USB_NEUG_EXIT          255 /* Ask to exit and to receive reGNUal */
 
+uint8_t neug_passwd[33] __attribute__ ((section(".passwd"))) = {
+  0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+static uint8_t usbbuf[64];
+
+#define DEFAULT_PASSWD "12345678"
+#define DEFAULT_PASSWD_LEN 8
+
+static void set_passwd (void)
+{
+  flash_unlock ();
+  if (neug_passwd[0] != 0xff)
+    flash_erase_page ((uint32_t)neug_passwd);
+  if (usbbuf[0] == DEFAULT_PASSWD_LEN
+      && !memcmp (usbbuf + 1, DEFAULT_PASSWD, DEFAULT_PASSWD_LEN))
+    return;
+  flash_write ((uint32_t)neug_passwd, usbbuf, usbbuf[0] + 1);
+}
 
 static uint32_t rbit (uint32_t v)
 {
@@ -248,14 +279,32 @@ static int download_check_crc32 (const uint32_t *end_p)
   return USB_UNSUPPORT;
 }
 
+
+#define NEUG_SPECIAL_BITRATE 110
+
+struct line_coding
+{
+  uint32_t bitrate;
+  uint8_t format;
+  uint8_t paritytype;
+  uint8_t datatype;
+} __attribute__((packed));
+
+static struct line_coding line_coding = {
+  115200, /* baud rate: 115200    */
+  0x00,   /* stop bits: 1         */
+  0x00,   /* parity:    none      */
+  0x08    /* bits:      8         */
+};
+
+#define CDC_CTRL_DTR            0x0001
+
 void
-usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, uint16_t value,
-                         uint16_t index, uint16_t len)
+usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, uint16_t value)
 {
   uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
 
-  if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)
-      && USB_SETUP_SET (req) && len == 0)
+  if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT) && USB_SETUP_SET (req))
     {
       if (req_no == USB_FSIJ_EXEC)
        {
@@ -267,77 +316,71 @@ usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, uint16_t value,
            }
          chopstx_mutex_unlock (&usb_mtx);
        }
+      else if (req_no == USB_NEUG_SET_PASSWD)
+       set_passwd ();
       else if (req_no == USB_NEUG_EXIT)
        {
-         /* Force exit from the main loop.  */
-         if (wait_usb_connection)
-           {
-             wait_usb_connection = 0;
-             chopstx_wakeup_usec_wait (main_thd);
-           }
-         else
+         if ((neug_passwd[0] == 0xff && usbbuf[0] == DEFAULT_PASSWD_LEN
+              && !memcmp (usbbuf + 1, DEFAULT_PASSWD, DEFAULT_PASSWD_LEN))
+             || (neug_passwd[0] == usbbuf[0]
+                 && !memcmp (neug_passwd+1, usbbuf+1, neug_passwd[0])))
            {
              chopstx_mutex_lock (&usb_mtx);
+             fsij_device_state = FSIJ_DEVICE_NEUG_EXIT_REQUESTED;
+             chopstx_wakeup_usec_wait (main_thd);
              chopstx_cond_signal (&cnd_usb);
              chopstx_mutex_unlock (&usb_mtx);
            }
        }
     }
   else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)
-          && index == 0 && USB_SETUP_SET (req)
-          && req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+          && USB_SETUP_SET (req))
     {
-      /* Open/close the connection.  */
-      chopstx_mutex_lock (&usb_mtx);
-      connected = (value != 0)? 1 : 0;
-      if (wait_usb_connection)
-       {                       /* It is waiting a connection.  */
-         if (connected)        /* It's now connected.  */
+      if (req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+       {
+         /* Open/close the connection.  */
+         chopstx_mutex_lock (&usb_mtx);
+         connected = (value & CDC_CTRL_DTR)? 1 : 0;
+         if (wait_usb_connection)
+           {                   /* It is waiting a connection.  */
+             if (connected)    /* It's now connected.  */
+               chopstx_wakeup_usec_wait (main_thd);
+           }
+         else
+           chopstx_cond_signal (&cnd_usb);
+         chopstx_mutex_unlock (&usb_mtx);
+       }
+#ifdef FRAUCHEKY_SUPPORT
+      else if (req_no == USB_CDC_REQ_SET_LINE_CODING)
+       {
+         chopstx_mutex_lock (&usb_mtx);
+         if (line_coding.bitrate == NEUG_SPECIAL_BITRATE)
            {
-             wait_usb_connection = 0;
+             fsij_device_state = FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED;
              chopstx_wakeup_usec_wait (main_thd);
+             chopstx_cond_signal (&cnd_usb);
            }
+         else if (fsij_device_state == FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED)
+           fsij_device_state = FSIJ_DEVICE_RUNNING;
+         chopstx_mutex_unlock (&usb_mtx);
        }
-      else
-       chopstx_cond_signal (&cnd_usb);
-      chopstx_mutex_unlock (&usb_mtx);
+#endif
     }
 }
 
-struct line_coding
-{
-  uint32_t bitrate;
-  uint8_t format;
-  uint8_t paritytype;
-  uint8_t datatype;
-} __attribute__((packed));
-
-static struct line_coding line_coding = {
-  115200, /* baud rate: 115200    */
-  0x00,   /* stop bits: 1         */
-  0x00,   /* parity:    none      */
-  0x08    /* bits:      8         */
-};
-
 
 static int
-vcom_port_data_setup (uint8_t req, uint8_t req_no, uint16_t value,
-                     uint16_t len)
+vcom_port_data_setup (uint8_t req, uint8_t req_no, struct control_info *detail)
 {
-  (void)value;
   if (USB_SETUP_GET (req))
     {
-      if (req_no == USB_CDC_REQ_GET_LINE_CODING
-         && len == sizeof (line_coding))
-       {
-         usb_lld_set_data_to_send (&line_coding, sizeof (line_coding));
-         return USB_SUCCESS;
-       }
+      if (req_no == USB_CDC_REQ_GET_LINE_CODING)
+       return usb_lld_reply_request (&line_coding, sizeof(line_coding), detail);
     }
   else  /* USB_SETUP_SET (req) */
     {
       if (req_no == USB_CDC_REQ_SET_LINE_CODING
-         && len == sizeof (line_coding))
+         && detail->len == sizeof (line_coding))
        {
          usb_lld_set_data_to_recv (&line_coding, sizeof (line_coding));
          return USB_SUCCESS;
@@ -350,8 +393,7 @@ vcom_port_data_setup (uint8_t req, uint8_t req_no, uint16_t value,
 }
 
 int
-usb_cb_setup (uint8_t req, uint8_t req_no,
-              uint16_t value, uint16_t index, uint16_t len)
+usb_cb_setup (uint8_t req, uint8_t req_no, struct control_info *detail)
 {
   uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
 
@@ -361,27 +403,34 @@ usb_cb_setup (uint8_t req, uint8_t req_no,
        {
          if (req_no == USB_FSIJ_MEMINFO)
            {
-             usb_lld_set_data_to_send (mem_info, sizeof (mem_info));
+             chopstx_mutex_lock (&usb_mtx);
+             if (fsij_device_state != FSIJ_DEVICE_EXITED)
+               {
+                 chopstx_mutex_unlock (&usb_mtx);
+                 return USB_UNSUPPORT;
+               }
+             chopstx_mutex_unlock (&usb_mtx);
+             usb_lld_reply_request (mem_info, sizeof (mem_info), detail);
              return USB_SUCCESS;
            }
          else if (req_no == USB_NEUG_GET_INFO)
            {
-             if (index == 0)
-               usb_lld_set_data_to_send (&neug_mode, sizeof (uint8_t));
-             else if (index == 1)
-               usb_lld_set_data_to_send (&neug_err_cnt, sizeof (uint16_t));
-             else if (index == 2)
-               usb_lld_set_data_to_send (&neug_err_cnt_rc, sizeof (uint16_t));
-             else if (index == 3)
-               usb_lld_set_data_to_send (&neug_err_cnt_p64, sizeof (uint16_t));
-             else if (index == 4)
-               usb_lld_set_data_to_send (&neug_err_cnt_p4k, sizeof (uint16_t));
-             else if (index == 5)
-               usb_lld_set_data_to_send (&neug_rc_max, sizeof (uint16_t));
-             else if (index == 6)
-               usb_lld_set_data_to_send (&neug_p64_max, sizeof (uint16_t));
-             else if (index == 7)
-               usb_lld_set_data_to_send (&neug_p4k_max, sizeof (uint16_t));
+             if (detail->index == 0)
+               usb_lld_reply_request (&neug_mode, sizeof (uint8_t), detail);
+             else if (detail->index == 1)
+               usb_lld_reply_request (&neug_err_cnt, sizeof (uint16_t), detail);
+             else if (detail->index == 2)
+               usb_lld_reply_request (&neug_err_cnt_rc, sizeof (uint16_t), detail);
+             else if (detail->index == 3)
+               usb_lld_reply_request (&neug_err_cnt_p64, sizeof (uint16_t), detail);
+             else if (detail->index == 4)
+               usb_lld_reply_request (&neug_err_cnt_p4k, sizeof (uint16_t), detail);
+             else if (detail->index == 5)
+               usb_lld_reply_request (&neug_rc_max, sizeof (uint16_t), detail);
+             else if (detail->index == 6)
+               usb_lld_reply_request (&neug_p64_max, sizeof (uint16_t), detail);
+             else if (detail->index == 7)
+               usb_lld_reply_request (&neug_p4k_max, sizeof (uint16_t), detail);
              else
                return USB_UNSUPPORT;
 
@@ -390,7 +439,7 @@ usb_cb_setup (uint8_t req, uint8_t req_no,
        }
       else /* SETUP_SET */
        {
-         uint8_t *addr = (uint8_t *)(0x20000000 + value * 0x100 + index);
+         uint8_t *addr = (uint8_t *)(0x20000000 + detail->value * 0x100 + detail->index);
 
          if (req_no == USB_FSIJ_DOWNLOAD)
            {
@@ -402,16 +451,16 @@ usb_cb_setup (uint8_t req, uint8_t req_no,
                }
              chopstx_mutex_unlock (&usb_mtx);
 
-             if (addr < &_regnual_start || addr + len > &__heap_end__)
+             if (addr < &_regnual_start || addr + detail->len > &__heap_end__)
                return USB_UNSUPPORT;
 
-             if (index + len < 256)
-               memset (addr + index + len, 0, 256 - (index + len));
+             if (detail->index + detail->len < 256)
+               memset (addr + detail->index + detail->len, 0, 256 - (detail->index + detail->len));
 
-             usb_lld_set_data_to_recv (addr, len);
+             usb_lld_set_data_to_recv (addr, detail->len);
              return USB_SUCCESS;
            }
-         else if (req_no == USB_FSIJ_EXEC && len == 0)
+         else if (req_no == USB_FSIJ_EXEC && detail->len == 0)
            {
              chopstx_mutex_lock (&usb_mtx);
              if (fsij_device_state != FSIJ_DEVICE_EXITED)
@@ -426,7 +475,13 @@ usb_cb_setup (uint8_t req, uint8_t req_no,
 
              return download_check_crc32 ((uint32_t *)addr);
            }
-         else if (req_no == USB_NEUG_EXIT && len == 0)
+         else if (req_no == USB_NEUG_SET_PASSWD && detail->len <= 32)
+           {
+             usbbuf[0] = detail->len;
+             usb_lld_set_data_to_recv (usbbuf + 1, detail->len);
+             return USB_SUCCESS;
+           }
+         else if (req_no == USB_NEUG_EXIT && detail->len <= 32)
            {
              chopstx_mutex_lock (&usb_mtx);
              if (fsij_device_state != FSIJ_DEVICE_RUNNING)
@@ -434,42 +489,48 @@ usb_cb_setup (uint8_t req, uint8_t req_no,
                  chopstx_mutex_unlock (&usb_mtx);
                  return USB_UNSUPPORT;
                }
-
-             fsij_device_state = FSIJ_DEVICE_NEUG_EXIT_REQUESTED;
-             chopstx_wakeup_usec_wait (main_thd);
-             chopstx_cond_signal (&cnd_usb);
              chopstx_mutex_unlock (&usb_mtx);
 
+             usbbuf[0] = detail->len;
+             usb_lld_set_data_to_recv (usbbuf + 1, detail->len);
              return USB_SUCCESS;
            }
        }
     }
-  else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT))
-    if (index == 0)
-      return vcom_port_data_setup (req, req_no, value, len);
+  else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)
+          && detail->index == 0)
+    {
+#ifdef FRAUCHEKY_SUPPORT
+      if (running_neug)
+       return vcom_port_data_setup (req, req_no, detail);
+      else
+       fraucheky_setup (req, req_no, detail);
+#else
+      return vcom_port_data_setup (req, req_no, detail);
+#endif
+    }
 
   return USB_UNSUPPORT;
 }
 
 int
 usb_cb_get_descriptor (uint8_t rcp, uint8_t desc_type, uint8_t desc_index,
-                      uint16_t index);
+                      struct control_info *detail)
 {
-  (void)index;
+#ifdef FRAUCHEKY_SUPPORT
+  if (!running_neug)
+    return fraucheky_get_descriptor (rcp, desc_type, desc_index, detail);
+#endif
+
   if (rcp != DEVICE_RECIPIENT)
     return USB_UNSUPPORT;
 
   if (desc_type == DEVICE_DESCRIPTOR)
-    {
-      usb_lld_set_data_to_send (vcom_device_desc, sizeof (vcom_device_desc));
-      return USB_SUCCESS;
-    }
+    return usb_lld_reply_request (vcom_device_desc, sizeof (vcom_device_desc),
+                                 detail);
   else if (desc_type == CONFIG_DESCRIPTOR)
-    {
-      usb_lld_set_data_to_send (vcom_configuration_desc,
-                               sizeof (vcom_configuration_desc));
-      return USB_SUCCESS;
-    }
+    return usb_lld_reply_request (vcom_config_desc, sizeof (vcom_config_desc),
+                                 detail);
   else if (desc_type == STRING_DESCRIPTOR)
     {
       const uint8_t *str;
@@ -490,8 +551,8 @@ usb_cb_get_descriptor (uint8_t rcp, uint8_t desc_type, uint8_t desc_index,
          size = sizeof (neug_string_product);
          break;
        case 3:
-         str = vcom_string3;
-         size = sizeof (vcom_string3);
+         str = neug_string_serial;
+         size = sizeof (neug_string_serial);
          break;
        case 4:
          str = neug_revision_detail;
@@ -505,12 +566,28 @@ usb_cb_get_descriptor (uint8_t rcp, uint8_t desc_type, uint8_t desc_index,
          str = sys_version;
          size = sizeof (sys_version);
          break;
+       case 7:
+         {
+           int i;
+           str = usbbuf;
+           for (i = 0; i < (int)sizeof (usbbuf)/2 - 2; i++)
+             {
+               if (sys_board_name[i] == 0)
+                 break;
+
+               usbbuf[i*2+2] = sys_board_name[i];
+               usbbuf[i*2+3] = 0;
+             }
+           usbbuf[0] = i*2 + 2;
+           usbbuf[1] = USB_STRING_DESCRIPTOR_TYPE;
+           size = i*2 + 2;
+         }
+         break;
        default:
          return USB_UNSUPPORT;
        }
 
-      usb_lld_set_data_to_send (str, size);
-      return USB_SUCCESS;
+      return usb_lld_reply_request (str, size, detail);
     }
 
   return USB_UNSUPPORT;
@@ -521,10 +598,22 @@ neug_setup_endpoints_for_interface (uint16_t interface, int stop)
 {
   if (interface == 0)
     {
+#ifdef FRAUCHEKY_SUPPORT
+      if (running_neug)
+       {
+         if (!stop)
+           usb_lld_setup_endpoint (ENDP2, EP_INTERRUPT, 0, 0, ENDP2_TXADDR, 0);
+         else
+           usb_lld_stall_tx (ENDP2);
+       }
+      else
+       fraucheky_setup_endpoints_for_interface (stop);
+#else
       if (!stop)
        usb_lld_setup_endpoint (ENDP2, EP_INTERRUPT, 0, 0, ENDP2_TXADDR, 0);
       else
        usb_lld_stall_tx (ENDP2);
+#endif
     }
   else if (interface == 1)
     {
@@ -591,9 +680,11 @@ int usb_cb_handle_event (uint8_t event_type, uint16_t value)
 }
 
 
-int usb_cb_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
+int usb_cb_interface (uint8_t cmd, struct control_info *detail)
 {
-  static uint8_t zero = 0;
+  const uint8_t zero = 0;
+  uint16_t interface = detail->index;
+  uint16_t alt = detail->value;
 
   if (interface >= NUM_INTERFACES)
     return USB_UNSUPPORT;
@@ -610,7 +701,7 @@ int usb_cb_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
        }
 
     case USB_GET_INTERFACE:
-      usb_lld_set_data_to_send (&zero, 1);
+      usb_lld_reply_request (&zero, 1, detail);
       return USB_SUCCESS;
 
     default:
@@ -628,7 +719,7 @@ usb_intr (void *arg)
   chopstx_intr_t interrupt;
 
   (void)arg;
-  usb_lld_init (vcom_configuration_desc[7]);
+  usb_lld_init (vcom_config_desc[7]);
   chopstx_claim_irq (&interrupt, INTR_REQ_USB);
   usb_interrupt_handler ();
 
@@ -643,25 +734,27 @@ usb_intr (void *arg)
   return NULL;
 }
 
-
+#define ID_OFFSET (2+SERIALNO_STR_LEN*2)
 static void fill_serial_no_by_unique_id (void)
 {
   extern const uint8_t * unique_device_id (void);
-  uint8_t *p = &vcom_string3[12];
-  const uint8_t *u = unique_device_id ();
+  uint8_t *p = &neug_string_serial[ID_OFFSET];
+  const uint8_t *u = unique_device_id () + 8;
   int i;
 
   for (i = 0; i < 4; i++)
     {
-      uint8_t b = u[i];
+      uint8_t b = u[3-i];
       uint8_t nibble; 
 
       nibble = (b >> 4);
       nibble += (nibble >= 10 ? ('A' - 10) : '0');
       p[i*4] = nibble;
+      p[i*4+1] = 0;
       nibble = (b & 0x0f);
       nibble += (nibble >= 10 ? ('A' - 10) : '0');
       p[i*4+2] = nibble;
+      p[i*4+3] = 0;
     }
 }
 \f
@@ -755,8 +848,6 @@ led_blinker (void *arg)
       eventmask_t m;
 
       m = event_flag_waitone (&led_event, ALL_EVENTS);
-      if (fsij_device_state != FSIJ_DEVICE_RUNNING) /* No usb_mtx lock.  */
-       break;
 
       set_led (1);
       if (m == LED_ONESHOT_SHORT)
@@ -824,12 +915,29 @@ main (int argc, char **argv)
 
   event_flag_init (&led_event);
   
-  led_thread = chopstx_create (PRIO_LED, __stackaddr_led, __stacksize_led,
-                              led_blinker, NULL);
-
   chopstx_mutex_init (&usb_mtx);
   chopstx_cond_init (&cnd_usb);
 
+#ifdef FRAUCHEKY_SUPPORT
+  if (fraucheky_enabled ())
+    {
+    go_fraucheky:
+      running_neug = 0;
+      usb_thd = chopstx_create (PRIO_USB, __stackaddr_usb, __stacksize_usb,
+                               usb_intr, NULL);
+      set_led (1);
+      fraucheky_main ();
+      chopstx_cancel (usb_thd);
+      chopstx_join (usb_thd, NULL);
+      usb_lld_shutdown ();
+    }
+
+  running_neug = 1;
+#endif
+
+  led_thread = chopstx_create (PRIO_LED, __stackaddr_led, __stacksize_led,
+                              led_blinker, NULL);
+
   usb_thd = chopstx_create (PRIO_USB, __stackaddr_usb, __stacksize_usb,
                            usb_intr, NULL);
 
@@ -940,11 +1048,34 @@ main (int argc, char **argv)
   chopstx_join (led_thread, NULL);
 
   /*
-   * We come here, because of FSIJ_DEVICE_NEUG_EXIT_REQUESTED.
+   * We come here, because of FSIJ_DEVICE_NEUG_EXIT_REQUESTED
+   * or FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED.
    */
   neug_fini ();
 
   chopstx_mutex_lock (&usb_mtx);
+#ifdef FRAUCHEKY_SUPPORT
+  if (line_coding.bitrate == NEUG_SPECIAL_BITRATE)
+    {
+      while (fsij_device_state == FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED)
+       {
+         chopstx_mutex_unlock (&usb_mtx);
+         set_led (1);
+         chopstx_usec_wait (500*1000);
+         set_led (0);
+         chopstx_usec_wait (500*1000);
+         chopstx_mutex_lock (&usb_mtx);
+       }
+
+      usb_lld_prepare_shutdown ();
+      chopstx_mutex_unlock (&usb_mtx);
+      chopstx_cancel (usb_thd);
+      chopstx_join (usb_thd, NULL);
+      usb_lld_shutdown ();
+      goto go_fraucheky;
+    }
+#endif
+
   fsij_device_state = FSIJ_DEVICE_EXITED;
 
   while (fsij_device_state == FSIJ_DEVICE_EXITED)
@@ -969,15 +1100,20 @@ main (int argc, char **argv)
 #ifdef DFU_SUPPORT
 #define FLASH_SYS_START_ADDR 0x08000000
 #define FLASH_SYS_END_ADDR (0x08000000+0x1000)
+#define CHIP_ID_REG ((uint32_t *)0xE0042000)
   {
     extern uint8_t _sys;
     uint32_t addr;
     handler *new_vector = (handler *)FLASH_SYS_START_ADDR;
     void (*func) (void (*)(void)) = (void (*)(void (*)(void)))new_vector[9];
+    uint32_t flash_page_size = 1024; /* 1KiB default */
+
+   if ((*CHIP_ID_ADDR)&0x07 == 0x04) /* High dencity device.  */
+     flash_page_size += 0x0400; /* It's 2KiB. */
 
     /* Kill DFU */
     for (addr = FLASH_SYS_START_ADDR; addr < FLASH_SYS_END_ADDR;
-        addr += FLASH_PAGE_SIZE)
+        addr += flash_page_size)
       flash_erase_page (addr);
 
     /* copy system service routines */