Gnuk->NeuG
[gnuk/neug.git] / src / main.c
index c02a678..8a7dec1 100644 (file)
@@ -13,7 +13,7 @@
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
- * Gnuk is distributed in the hope that it will be useful, but WITHOUT
+ * NeuG is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
  * License for more details.
@@ -30,6 +30,7 @@
 #include "neug.h"
 #include "usb_lld.h"
 #include "sys.h"
+#include "adc.h"
 
 /*
  * We are trying to avoid dependency to C library. 
@@ -38,6 +39,8 @@
 extern void *memcpy(void *dest, const void *src, size_t n);
 extern void *memset (void *s, int c, size_t n);
 
+static Thread *main_thread = NULL;
+
 \f
 #define ENDP0_RXADDR        (0x40)
 #define ENDP0_TXADDR        (0x80)
@@ -202,6 +205,8 @@ static const struct Descriptor string_descs[] = {
 
 uint32_t bDeviceState = UNCONNECTED; /* USB device status */
 
+#define NEUG_WAIT_FOR_TX_READY 1
+static uint8_t neug_state;
 
 static void
 neug_device_reset (void)
@@ -218,19 +223,26 @@ neug_device_reset (void)
   usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, 64);
 }
 
-#define USB_REGNUAL_MEMINFO    0
-#define USB_REGNUAL_SEND       1
-#define USB_REGNUAL_RESULT     2
-#define USB_REGNUAL_FLASH      3
-#define USB_REGNUAL_PROTECT    4
-#define USB_REGNUAL_FINISH     5
+extern uint8_t _regnual_start, __heap_end__;
 
-static uint8_t mem[256];
-static uint32_t result;
+static const uint8_t *const mem_info[] = { &_regnual_start,  &__heap_end__, };
 
-extern uint8_t __flash_start__,  __flash_end__;
-static const uint8_t *const mem_info[] = { &__flash_start__,  &__flash_end__ };
+/* USB vendor requests to control pipe */
+#define USB_FSIJ_MEMINFO         0
+#define USB_FSIJ_DOWNLOAD        1
+#define USB_FSIJ_EXEC            2
+#define USB_NEUG_GET_ERR_COUNT 254
+#define USB_NEUG_EXIT          255 /* Ask to exit and to receive reGNUal */
 
+enum {
+  FSIJ_DEVICE_RUNNING = 0,
+  FSIJ_DEVICE_EXITED,
+  FSIJ_DEVICE_EXEC_REQUESTED,
+  /**/
+  FSIJ_DEVICE_NEUG_EXIT_REQUESTED = 255
+}; 
+
+static uint8_t fsij_device_state = FSIJ_DEVICE_RUNNING;
 
 static uint32_t rbit (uint32_t v)
 {
@@ -240,26 +252,23 @@ static uint32_t rbit (uint32_t v)
   return r;
 }
 
-static uint32_t fetch (int i)
-{
-  uint32_t v;
-
-  v = *(uint32_t *)(&mem[i*4]);
-  return rbit (v);
-}
-
-static uint32_t calc_crc32 (void)
+/* After calling this function, CRC module remain enabled.  */
+static int download_check_crc32 (const uint32_t *end_p)
 {
-  int i;
+  uint32_t crc32 = *end_p;
+  const uint32_t *p;
 
+  RCC->AHBENR |= RCC_AHBENR_CRCEN;
   CRC->CR = CRC_CR_RESET;
 
-  for (i = 0; i < 256/4; i++)
-    CRC->DR = fetch (i);
+  for (p = (const uint32_t *)&_regnual_start; p < end_p; p++)
+    CRC->DR = rbit (*p);
 
-  return rbit (CRC->DR);
-}
+  if ((rbit (CRC->DR) ^ crc32) == 0xffffffff)
+    return USB_SUCCESS;
 
+  return USB_UNSUPPORT;
+}
 
 static void neug_ctrl_write_finish (uint8_t req, uint8_t req_no,
                                    uint16_t value, uint16_t index,
@@ -267,22 +276,30 @@ static void neug_ctrl_write_finish (uint8_t req, uint8_t req_no,
 {
   uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
 
-  if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT) && USB_SETUP_SET (req))
+  if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)
+      && USB_SETUP_SET (req) && len == 0)
     {
-      if (req_no == USB_REGNUAL_SEND && value == 0)
-       result = calc_crc32 ();
-      else if (req_no == USB_REGNUAL_FLASH && len == 0 && index == 0)
+      if (req_no == USB_FSIJ_EXEC)
        {
-         uint32_t dst_addr = (0x08000000 + value * 0x100);
+         if (fsij_device_state != FSIJ_DEVICE_EXITED)
+           return;
 
-         result = flash_write (dst_addr, mem, 256);
+         (void)value; (void)index;
+         usb_lld_prepare_shutdown (); /* No further USB communication */
+         fsij_device_state = FSIJ_DEVICE_EXEC_REQUESTED;
+       }
+      else if (req_no == USB_NEUG_EXIT)
+       {
+         chSysLockFromIsr ();
+         if (neug_state == NEUG_WAIT_FOR_TX_READY)
+           {
+             main_thread->p_u.rdymsg = RDY_OK;
+             chSchReadyI (main_thread);
+           }
+         else
+           chEvtSignalFlagsI (main_thread, 1);
+         chSysUnlockFromIsr ();
        }
-      else if (req_no == USB_REGNUAL_PROTECT && len == 0
-              && value == 0 && index == 0)
-       result = flash_protect ();
-      else if (req_no == USB_REGNUAL_FINISH && len == 0
-              && value == 0 && index == 0)
-       nvic_system_reset ();
     }
 }
 
@@ -325,13 +342,25 @@ vcom_port_data_setup (uint8_t req, uint8_t req_no, uint16_t value)
        {
          if (value != 0)
            {
-             if ((connected & 1) == 0)
+             if (connected == 0)
                /* It's Open call */
                connected++;
            }
          else
-           /* Close call */
-           connected++;
+           {
+             if (connected)
+               {
+                 /* Close call */
+                 connected = 0;
+                 chSysLockFromIsr ();
+                 if (neug_state == NEUG_WAIT_FOR_TX_READY)
+                   {
+                     main_thread->p_u.rdymsg = RDY_OK;
+                     chSchReadyI (main_thread);
+                   }
+                 chSysUnlockFromIsr ();
+               }
+           }
 
          return USB_SUCCESS;
        }
@@ -350,43 +379,55 @@ neug_setup (uint8_t req, uint8_t req_no,
     {
       if (USB_SETUP_GET (req))
        {
-         if (req_no == USB_REGNUAL_MEMINFO)
+         if (req_no == USB_FSIJ_MEMINFO)
            {
              usb_lld_set_data_to_send (mem_info, sizeof (mem_info));
              return USB_SUCCESS;
            }
-         else if (req_no == USB_REGNUAL_RESULT)
+         else if (req_no == USB_NEUG_GET_ERR_COUNT)
            {
-             usb_lld_set_data_to_send (&result, sizeof (uint32_t));
+             extern uint16_t neug_err_count;
+
+             usb_lld_set_data_to_send (&neug_err_count, sizeof (uint16_t));
              return USB_SUCCESS;
            }
        }
       else /* SETUP_SET */
        {
-         if (req_no == USB_REGNUAL_SEND)
+         uint8_t *addr = (uint8_t *)(0x20000000 + value * 0x100 + index);
+
+         if (req_no == USB_FSIJ_DOWNLOAD)
            {
-             if (value != 0 || index + len > 256)
+             if (fsij_device_state != FSIJ_DEVICE_EXITED)
+               return USB_UNSUPPORT;
+
+             if (addr < &_regnual_start || addr + len > &__heap_end__)
                return USB_UNSUPPORT;
 
              if (index + len < 256)
-               memset (mem + index + len, 0xff, 256 - (index + len));
+               memset (addr + index + len, 0, 256 - (index + len));
 
-             usb_lld_set_data_to_recv (mem + index, len);
+             usb_lld_set_data_to_recv (addr, len);
              return USB_SUCCESS;
            }
-         else if (req_no == USB_REGNUAL_FLASH && len == 0 && index == 0)
+         else if (req_no == USB_FSIJ_EXEC && len == 0)
+           {
+             if (fsij_device_state != FSIJ_DEVICE_EXITED)
+               return USB_UNSUPPORT;
+
+             if (((uint32_t)addr & 0x03))
+               return USB_UNSUPPORT;
+
+             return download_check_crc32 ((uint32_t *)addr);
+           }
+         else if (req_no == USB_NEUG_EXIT && len == 0)
            {
-             uint32_t dst_addr = (0x08000000 + value * 0x100);
+             if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+               return USB_UNSUPPORT;
 
-             if (dst_addr + 256 <= (uint32_t)&__flash_end__)
-               return USB_SUCCESS;
+             fsij_device_state = FSIJ_DEVICE_NEUG_EXIT_REQUESTED;
+             return USB_SUCCESS;
            }
-         else if (req_no == USB_REGNUAL_PROTECT && len == 0
-                  && value == 0 && index == 0)
-           return USB_SUCCESS;
-         else if (req_no == USB_REGNUAL_FINISH && len == 0
-                  && value == 0 && index == 0)
-           return USB_SUCCESS;
        }
     }
   else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT))
@@ -556,8 +597,6 @@ static void fill_serial_no_by_unique_id (void)
     }
 }
 \f
-static Thread *main_thread = NULL;
-
 CH_IRQ_HANDLER (Vector90)
 {
   CH_IRQ_PROLOGUE();
@@ -572,13 +611,13 @@ CH_IRQ_HANDLER (Vector90)
 void
 EP1_IN_Callback (void)
 {
-  if (main_thread != NULL)
+  chSysLockFromIsr ();
+  if (main_thread != NULL && neug_state == NEUG_WAIT_FOR_TX_READY)
     {
-      chSysLockFromIsr ();
       main_thread->p_u.rdymsg = RDY_OK;
       chSchReadyI (main_thread);
-      chSysUnlockFromIsr ();
     }
+  chSysUnlockFromIsr ();
 }
 
 void
@@ -594,8 +633,9 @@ EP3_OUT_Callback (void)
 \f
 static WORKING_AREA(wa_led, 64);
 
-#define LED_ONESHOT_SHORT ((eventmask_t)1)
-#define LED_ONESHOT_LONG  ((eventmask_t)2)
+#define LED_ONESHOT_SHORT      ((eventmask_t)1)
+#define LED_TWOSHOTS           ((eventmask_t)2)
+#define LED_ONESHOT_LONG       ((eventmask_t)4)
 static Thread *led_thread;
 
 /*
@@ -613,9 +653,20 @@ static msg_t led_blinker (void *arg)
       eventmask_t m;
 
       m = chEvtWaitOne (ALL_EVENTS);
+      if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+       break;
+
       set_led (1);
       if (m == LED_ONESHOT_SHORT)
        chThdSleep (MS2ST (100));
+      else if (m == LED_TWOSHOTS)
+       {
+         chThdSleep (MS2ST (50));
+         set_led (0);
+         chThdSleep (MS2ST (50));
+         set_led (1);
+         chThdSleep (MS2ST (50));
+       }
       else
        chThdSleep (MS2ST (250));
       set_led (0);
@@ -624,7 +675,7 @@ static msg_t led_blinker (void *arg)
   return 0;
 }
 \f
-#define RANDOM_BYTES_LENGTH 60
+#define RANDOM_BYTES_LENGTH 32
 static uint32_t random_word[RANDOM_BYTES_LENGTH/sizeof (uint32_t)];
 
 /*
@@ -640,66 +691,144 @@ main (int argc, char **argv)
 
   fill_serial_no_by_unique_id ();
 
-  halInit();
-  chSysInit();
+  halInit ();
+  adc_init ();
+  chSysInit ();
 
   main_thread = chThdSelf ();
 
-  usb_lld_init (config_desc.Descriptor[7]);
-
   chThdCreateStatic (wa_led, sizeof (wa_led), NORMALPRIO, led_blinker, NULL);
 
   neug_init (random_word, RANDOM_BYTES_LENGTH/sizeof (uint32_t));
 
+  usb_lld_init (config_desc.Descriptor[7]);
+
   while (1)
     {
       unsigned int count = 0;
 
-      connected = 0;
+      if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+       break;
+
       while (count < NEUG_PRE_LOOP || bDeviceState != CONFIGURED)
        {
-         (void)neug_get (NEUG_KICK_FILLING);
-         if ((count & 0x000f) == 0)
+         if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+           break;
+
+         neug_wait_full ();
+         neug_flush ();
+
+         if ((count & 0x0007) == 0)
            chEvtSignalFlags (led_thread, LED_ONESHOT_SHORT);
-         chThdSleep (MS2ST (25));
+         chEvtWaitOneTimeout (ALL_EVENTS, MS2ST (25));
          count++;
        }
 
     waiting_connection:
-      while ((connected & 1) == 0)
+      while (connected == 0)
        {
+         if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+           break;
+
          neug_flush ();
-         chEvtSignalFlags (led_thread, LED_ONESHOT_LONG);
-         chThdSleep (MS2ST (2500));
+         chEvtSignalFlags (led_thread, LED_TWOSHOTS);
+         chEvtWaitOneTimeout (ALL_EVENTS, MS2ST (5000));
        }
 
+      if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+       break;
+
       /* The connection opened.  */
       count = 0;
+      /*
+       * No parity is standard.  It means to provide conditioned output.
+       * When parity enabled, it means to provide raw output.
+       */
+      neug_mode_select (line_coding.paritytype); /* 0: None, 1: Odd, 2: Even */
 
       while (1)
        {
-         if ((connected & 1) == 0)
-           goto waiting_connection;
+         if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+           break;
 
          if (bDeviceState != CONFIGURED)
            break;
 
          neug_wait_full ();
 
-         if ((count & 0x00ff) == 0)
+         if ((count & 0x03ff) == 0)
            chEvtSignalFlags (led_thread, LED_ONESHOT_SHORT);
 
          usb_lld_txcpy (random_word, ENDP1, 0, RANDOM_BYTES_LENGTH);
          neug_flush ();
 
          chSysLock ();
-         usb_lld_tx_enable (ENDP1, RANDOM_BYTES_LENGTH);
-         chSchGoSleepS (THD_STATE_SUSPENDED);
+         if (connected == 0)
+           {
+             chSysUnlock();
+             goto waiting_connection;
+           }
+         else
+           {
+             neug_state = NEUG_WAIT_FOR_TX_READY;
+             usb_lld_tx_enable (ENDP1, RANDOM_BYTES_LENGTH);
+             chSchGoSleepS (THD_STATE_SUSPENDED);
+             neug_state = 0;
+           }
          chSysUnlock();
 
          count++;
        }
     }
 
+  chEvtSignalFlags (led_thread, LED_ONESHOT_SHORT);
+  chThdWait (led_thread);
+
+  /*
+   * We come here, because of FSIJ_DEVICE_NEUG_EXIT_REQUESTED.
+   */
+  neug_fini ();
+
+  fsij_device_state = FSIJ_DEVICE_EXITED;
+
+  while (fsij_device_state == FSIJ_DEVICE_EXITED)
+    chThdSleep (MS2ST (500));
+
+  flash_unlock ();             /* Flash unlock should be done here */
+  set_led (1);
+  usb_lld_shutdown ();
+  /* Disable SysTick */
+  SysTick->CTRL = 0;
+  /* Disable all interrupts */
+  port_disable ();
+  /* Set vector */
+  SCB->VTOR = (uint32_t)&_regnual_start;
+#ifdef DFU_SUPPORT
+#define FLASH_SYS_START_ADDR 0x08000000
+#define FLASH_SYS_END_ADDR (0x08000000+0x1000)
+  {
+    extern uint8_t _sys;
+    uint32_t addr;
+    handler *new_vector = (handler *)FLASH_SYS_START_ADDR;
+    void (*func) (void (*)(void)) = (void (*)(void (*)(void)))new_vector[10];
+
+    /* Kill DFU */
+    for (addr = FLASH_SYS_START_ADDR; addr < FLASH_SYS_END_ADDR;
+        addr += FLASH_PAGE_SIZE)
+      flash_erase_page (addr);
+
+    /* copy system service routines */
+    flash_write (FLASH_SYS_START_ADDR, &_sys, 0x1000);
+
+    /* Leave NeuG to exec reGNUal */
+    (*func) (*((void (**)(void))(&_regnual_start+4)));
+    for (;;);
+  }
+#else
+  /* Leave NeuG to exec reGNUal */
+  flash_erase_all_and_exec (*((void (**)(void))(&_regnual_start+4)));
+#endif
+
+  /* Never reached */
   return 0;
 }