+#define CDC_CTRL_DTR 0x0001
+
+static void
+usb_ctrl_write_finish (struct usb_dev *dev)
+{
+ struct device_req *arg = &dev->dev_req;
+ uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT);
+
+ if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)
+ && USB_SETUP_SET (arg->type))
+ {
+ if (arg->request == USB_FSIJ_EXEC)
+ {
+ chopstx_mutex_lock (&usb_mtx);
+ if (fsij_device_state == FSIJ_DEVICE_EXITED)
+ {
+ usb_lld_prepare_shutdown (); /* No further USB communication */
+ fsij_device_state = FSIJ_DEVICE_EXEC_REQUESTED;
+ }
+ chopstx_mutex_unlock (&usb_mtx);
+ }
+ else if (arg->request == USB_NEUG_SET_PASSWD)
+ set_passwd ();
+ else if (arg->request == USB_NEUG_EXIT)
+ {
+ 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_cond_signal (&usb_cnd);
+ chopstx_mutex_unlock (&usb_mtx);
+ }
+ }
+ }
+ else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0
+ && USB_SETUP_SET (arg->type))
+ {
+ if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+ {
+ /* Open/close the connection. */
+ chopstx_mutex_lock (&usb_mtx);
+ connected = (arg->value & CDC_CTRL_DTR)? 1 : 0;
+ chopstx_cond_signal (&usb_cnd);
+ chopstx_mutex_unlock (&usb_mtx);
+ }
+#ifdef FRAUCHEKY_SUPPORT
+ else if (running_neug && arg->request == USB_CDC_REQ_SET_LINE_CODING)
+ {
+ chopstx_mutex_lock (&usb_mtx);
+ if (line_coding.bitrate == NEUG_SPECIAL_BITRATE)
+ {
+ fsij_device_state = FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED;
+ chopstx_cond_signal (&usb_cnd);
+ }
+ else if (fsij_device_state == FSIJ_DEVICE_NEUG_FRAUCHEKY_REQUESTED)
+ fsij_device_state = FSIJ_DEVICE_RUNNING;
+ chopstx_mutex_unlock (&usb_mtx);
+ }
+ else if (!running_neug && arg->request == MSC_MASS_STORAGE_RESET_COMMAND)
+ fraucheky_setup_endpoints_for_interface (0);
+#endif
+ }
+}
+
+
+static int
+vcom_port_data_setup (struct usb_dev *dev)
+{
+ struct device_req *arg = &dev->dev_req;
+
+ if (USB_SETUP_GET (arg->type))
+ {
+ if (arg->request == USB_CDC_REQ_GET_LINE_CODING)
+ return usb_lld_ctrl_send (dev, &line_coding, sizeof(line_coding));
+ }
+ else /* USB_SETUP_SET (req) */
+ {
+ if (arg->request == USB_CDC_REQ_SET_LINE_CODING
+ && arg->len == sizeof (line_coding))
+ return usb_lld_ctrl_recv (dev, &line_coding, sizeof (line_coding));
+ else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+ return usb_lld_ctrl_ack (dev);
+ }
+
+ return -1;
+}
+
+static int
+usb_setup (struct usb_dev *dev)
+{
+ struct device_req *arg = &dev->dev_req;
+ uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT);
+
+ if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT))
+ {
+ if (USB_SETUP_GET (arg->type))
+ {
+ if (arg->request == USB_FSIJ_MEMINFO)
+ {
+ chopstx_mutex_lock (&usb_mtx);
+ if (fsij_device_state != FSIJ_DEVICE_EXITED)
+ {
+ chopstx_mutex_unlock (&usb_mtx);
+ return -1;
+ }
+ chopstx_mutex_unlock (&usb_mtx);
+ return usb_lld_ctrl_send (dev, mem_info, sizeof (mem_info));
+ }
+ else if (arg->request == USB_NEUG_GET_INFO)
+ {
+ if (arg->index == 0)
+ return usb_lld_ctrl_send (dev, &neug_mode, sizeof (uint8_t));
+ else if (arg->index == 1)
+ return usb_lld_ctrl_send (dev, &neug_err_cnt, sizeof (uint16_t));
+ else if (arg->index == 2)
+ return usb_lld_ctrl_send (dev, &neug_err_cnt_rc, sizeof (uint16_t));
+ else if (arg->index == 3)
+ return usb_lld_ctrl_send (dev, &neug_err_cnt_p64, sizeof (uint16_t));
+ else if (arg->index == 4)
+ return usb_lld_ctrl_send (dev, &neug_err_cnt_p4k, sizeof (uint16_t));
+ else if (arg->index == 5)
+ return usb_lld_ctrl_send (dev, &neug_rc_max, sizeof (uint16_t));
+ else if (arg->index == 6)
+ return usb_lld_ctrl_send (dev, &neug_p64_max, sizeof (uint16_t));
+ else if (arg->index == 7)
+ return usb_lld_ctrl_send (dev, &neug_p4k_max, sizeof (uint16_t));
+ else
+ return -1;
+ }
+ }
+ else /* SETUP_SET */
+ {
+ uint8_t *addr = (uint8_t *)(0x20000000 + arg->value * 0x100 + arg->index);
+
+ if (arg->request == USB_FSIJ_DOWNLOAD)
+ {
+ chopstx_mutex_lock (&usb_mtx);
+ if (fsij_device_state != FSIJ_DEVICE_EXITED)
+ {
+ chopstx_mutex_unlock (&usb_mtx);
+ return -1;
+ }
+ chopstx_mutex_unlock (&usb_mtx);
+
+ if (addr < &_regnual_start || addr + arg->len > &__heap_end__)
+ return -1;
+
+ if (arg->index + arg->len < 256)
+ memset (addr + arg->index + arg->len, 0, 256 - (arg->index + arg->len));
+
+ return usb_lld_ctrl_recv (dev, addr, arg->len);
+ }
+ else if (arg->request == USB_FSIJ_EXEC && arg->len == 0)
+ {
+ chopstx_mutex_lock (&usb_mtx);
+ if (fsij_device_state != FSIJ_DEVICE_EXITED)
+ {
+ chopstx_mutex_unlock (&usb_mtx);
+ return -1;
+ }
+ chopstx_mutex_unlock (&usb_mtx);
+
+ if (((uint32_t)addr & 0x03))
+ return -1;
+
+ return download_check_crc32 (dev, (uint32_t *)addr);
+ }
+ else if (arg->request == USB_NEUG_SET_PASSWD && arg->len <= 32)
+ {
+ usbbuf[0] = arg->len;
+ return usb_lld_ctrl_recv (dev, usbbuf + 1, arg->len);
+ }
+ else if (arg->request == USB_NEUG_EXIT && arg->len <= 32)
+ {
+ chopstx_mutex_lock (&usb_mtx);
+ if (fsij_device_state != FSIJ_DEVICE_RUNNING)
+ {
+ chopstx_mutex_unlock (&usb_mtx);
+ return -1;
+ }
+ chopstx_mutex_unlock (&usb_mtx);
+
+ usbbuf[0] = arg->len;
+ return usb_lld_ctrl_recv (dev, usbbuf + 1, arg->len);
+ }
+ }
+ }
+ else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)
+ && arg->index == 0)
+ {
+#ifdef FRAUCHEKY_SUPPORT
+ if (running_neug)
+ return vcom_port_data_setup (dev);
+ else
+ return fraucheky_setup (dev);
+#else
+ return vcom_port_data_setup (dev);
+#endif
+ }
+
+ return -1;
+}
+
+static int
+usb_get_descriptor (struct usb_dev *dev)
+{
+ struct device_req *arg = &dev->dev_req;
+ uint8_t rcp = arg->type & RECIPIENT;
+ uint8_t desc_type = (arg->value >> 8);
+ uint8_t desc_index = (arg->value & 0xff);
+
+#ifdef FRAUCHEKY_SUPPORT
+ if (!running_neug)
+ return fraucheky_get_descriptor (dev);
+#endif
+
+ if (rcp != DEVICE_RECIPIENT)
+ return -1;
+
+ if (desc_type == DEVICE_DESCRIPTOR)
+ return usb_lld_ctrl_send (dev,
+ vcom_device_desc, sizeof (vcom_device_desc));
+ else if (desc_type == CONFIG_DESCRIPTOR)
+ return usb_lld_ctrl_send (dev,
+ vcom_config_desc, sizeof (vcom_config_desc));
+ else if (desc_type == STRING_DESCRIPTOR)
+ {
+ const uint8_t *str;
+ int size;
+
+ switch (desc_index)
+ {
+ case 0:
+ str = vcom_string0;
+ size = sizeof (vcom_string0);
+ break;
+ case 1:
+ str = neug_string_vendor;
+ size = sizeof (neug_string_vendor);
+ break;
+ case 2:
+ str = neug_string_product;
+ size = sizeof (neug_string_product);
+ break;
+ case 3:
+ str = neug_string_serial;
+ size = sizeof (neug_string_serial);
+ break;
+ case 4:
+ str = neug_revision_detail;
+ size = sizeof (neug_revision_detail);
+ break;
+ case 5:
+ str = neug_config_options;
+ size = sizeof (neug_config_options);
+ break;
+ case 6:
+ str = sys_version;
+ size = sizeof (sys_version);
+ break;
+#ifdef USE_SYS3
+ 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] = STRING_DESCRIPTOR;
+ size = i*2 + 2;
+ }
+ break;
+#endif
+ default:
+ return -1;
+ }
+
+ return usb_lld_ctrl_send (dev, str, size);
+ }
+
+ return -1;
+}
+
+static void
+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)
+ {
+ if (!stop)
+ {
+ usb_lld_setup_endpoint (ENDP1, EP_BULK, 0, 0, ENDP1_TXADDR, 0);
+ usb_lld_setup_endpoint (ENDP3, EP_BULK, 0, ENDP3_RXADDR, 0, 64);
+ /* Start with no data receiving (ENDP3 not enabled) */
+ }
+ else
+ {
+ usb_lld_stall_tx (ENDP1);
+ usb_lld_stall_rx (ENDP3);
+ }
+ }
+}
+
+static int
+usb_set_configuration (struct usb_dev *dev)
+{
+ int i;
+ uint8_t current_conf;
+
+ current_conf = usb_lld_current_configuration (dev);
+ if (current_conf == 0)
+ {
+ if (dev->dev_req.value != 1)
+ return -1;
+
+ usb_lld_set_configuration (dev, 1);
+ for (i = 0; i < NUM_INTERFACES; i++)
+ neug_setup_endpoints_for_interface (i, 0);
+ chopstx_mutex_lock (&usb_mtx);
+ bDeviceState = CONFIGURED;
+ chopstx_mutex_unlock (&usb_mtx);
+ }
+ else if (current_conf != dev->dev_req.value)
+ {
+ if (dev->dev_req.value != 0)
+ return -1;
+
+ usb_lld_set_configuration (dev, 0);
+ for (i = 0; i < NUM_INTERFACES; i++)
+ neug_setup_endpoints_for_interface (i, 1);
+ chopstx_mutex_lock (&usb_mtx);
+ bDeviceState = ADDRESSED;
+ chopstx_cond_signal (&usb_cnd);
+ chopstx_mutex_unlock (&usb_mtx);
+ }
+
+ /* Do nothing when current_conf == value */
+ return usb_lld_ctrl_ack (dev);
+}
+
+
+static int
+usb_set_interface (struct usb_dev *dev)
+{
+ uint16_t interface = dev->dev_req.index;
+ uint16_t alt = dev->dev_req.value;
+
+ if (interface >= NUM_INTERFACES)
+ return -1;
+
+ if (alt != 0)
+ return -1;
+ else
+ {
+ neug_setup_endpoints_for_interface (interface, 0);
+ return usb_lld_ctrl_ack (dev);
+ }
+}
+
+static int
+usb_get_interface (struct usb_dev *dev)
+{
+ const uint8_t zero = 0;
+ uint16_t interface = dev->dev_req.index;
+
+ if (interface >= NUM_INTERFACES)
+ return -1;
+
+ return usb_lld_ctrl_send (dev, &zero, 1);
+}
+
+
+static int
+usb_get_status_interface (struct usb_dev *dev)
+{
+ const uint16_t status_info = 0;
+ uint16_t interface = dev->dev_req.index;
+
+ if (interface >= NUM_INTERFACES)
+ return -1;
+
+ return usb_lld_ctrl_send (dev, &status_info, 2);
+}
+
+static void usb_tx_done (uint8_t ep_num, uint16_t len);
+static void usb_rx_ready (uint8_t ep_num, uint16_t len);
+
+
+#define INTR_REQ_USB 20
+#define PRIO_USB 3
+
+static void *
+usb_main (void *arg)
+{
+ chopstx_intr_t interrupt;
+ struct usb_dev dev;
+ int e;
+
+ (void)arg;
+ usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
+ chopstx_claim_irq (&interrupt, INTR_REQ_USB);
+ goto event_handle; /* For old SYS < 3.0 */
+
+ while (1)
+ {
+ chopstx_intr_wait (&interrupt);
+
+ if (interrupt.ready)
+ {
+ uint8_t ep_num;
+
+ event_handle:
+ e = usb_lld_event_handler (&dev);
+ ep_num = USB_EVENT_ENDP (e);
+
+ if (ep_num != 0)
+ {
+ if (USB_EVENT_TXRX (e))
+ usb_tx_done (ep_num, USB_EVENT_LEN (e));
+ else
+ usb_rx_ready (ep_num, USB_EVENT_LEN (e));
+ }
+ else
+ switch (USB_EVENT_ID (e))
+ {
+ case USB_EVENT_DEVICE_RESET:
+ usb_device_reset (&dev);
+ continue;
+
+ case USB_EVENT_DEVICE_ADDRESSED:
+ chopstx_mutex_lock (&usb_mtx);
+ bDeviceState = ADDRESSED;
+ chopstx_cond_signal (&usb_cnd);
+ chopstx_mutex_unlock (&usb_mtx);
+ continue;
+
+ case USB_EVENT_GET_DESCRIPTOR:
+ if (usb_get_descriptor (&dev) < 0)
+ usb_lld_ctrl_error (&dev);
+ continue;
+
+ case USB_EVENT_SET_CONFIGURATION:
+ if (usb_set_configuration (&dev) < 0)
+ usb_lld_ctrl_error (&dev);
+ continue;
+
+ case USB_EVENT_SET_INTERFACE:
+ if (usb_set_interface (&dev) < 0)
+ usb_lld_ctrl_error (&dev);
+ continue;
+
+ case USB_EVENT_CTRL_REQUEST:
+ /* Device specific device request. */
+ if (usb_setup (&dev) < 0)
+ usb_lld_ctrl_error (&dev);
+ continue;
+
+ case USB_EVENT_GET_STATUS_INTERFACE:
+ if (usb_get_status_interface (&dev) < 0)
+ usb_lld_ctrl_error (&dev);
+ continue;
+
+ case USB_EVENT_GET_INTERFACE:
+ if (usb_get_interface (&dev) < 0)
+ usb_lld_ctrl_error (&dev);
+ continue;
+
+ case USB_EVENT_SET_FEATURE_DEVICE:
+ case USB_EVENT_SET_FEATURE_ENDPOINT:
+ case USB_EVENT_CLEAR_FEATURE_DEVICE:
+ case USB_EVENT_CLEAR_FEATURE_ENDPOINT:
+ usb_lld_ctrl_ack (&dev);
+ continue;
+
+ case USB_EVENT_CTRL_WRITE_FINISH:
+ /* Control WRITE transfer finished. */
+ usb_ctrl_write_finish (&dev);
+ continue;
+
+ case USB_EVENT_OK:
+ case USB_EVENT_DEVICE_SUSPEND:
+ default:
+ continue;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#define ID_OFFSET (2+SERIALNO_STR_LEN*2)