From 82749ab97af6ec1de605f0638d44999c3b50eadf Mon Sep 17 00:00:00 2001
From: NIIBE Yutaka <gniibe@fsij.org>
Date: Mon, 18 Apr 2016 11:46:14 +0900
Subject: Add support for FS-BB48

---
 example-fs-bb48/Makefile    |   31 ++
 example-fs-bb48/board.h     |    1 +
 example-fs-bb48/reset.c     |  114 +++++
 example-fs-bb48/sample.c    |  228 ++++++++++
 example-fs-bb48/sample.ld   |  135 ++++++
 example-fs-bb48/stream.h    |   24 +
 example-fs-bb48/usb-cdc.c   |  506 +++++++++++++++++++++
 example-fs-bb48/usb_kl27z.c | 1035 +++++++++++++++++++++++++++++++++++++++++++
 example-fs-bb48/usb_lld.h   |  115 +++++
 9 files changed, 2189 insertions(+)
 create mode 100644 example-fs-bb48/Makefile
 create mode 120000 example-fs-bb48/board.h
 create mode 100644 example-fs-bb48/reset.c
 create mode 100644 example-fs-bb48/sample.c
 create mode 100644 example-fs-bb48/sample.ld
 create mode 100644 example-fs-bb48/stream.h
 create mode 100644 example-fs-bb48/usb-cdc.c
 create mode 100644 example-fs-bb48/usb_kl27z.c
 create mode 100644 example-fs-bb48/usb_lld.h

(limited to 'example-fs-bb48')

diff --git a/example-fs-bb48/Makefile b/example-fs-bb48/Makefile
new file mode 100644
index 0000000..b60cfde
--- /dev/null
+++ b/example-fs-bb48/Makefile
@@ -0,0 +1,31 @@
+# Makefile for example application of Chopstx
+
+PROJECT = sample
+
+### Currently, it's for FS-BB48.
+
+CHOPSTX = ..
+LDSCRIPT= sample.ld
+CSRC = sample.c reset.c usb_kl27z.c usb-cdc.c
+
+###################################
+CROSS = arm-none-eabi-
+CC   = $(CROSS)gcc
+LD   = $(CROSS)gcc
+OBJCOPY   = $(CROSS)objcopy
+
+MCU   = cortex-m0plus
+CWARN = -Wall -Wextra -Wstrict-prototypes
+DEFS  = -DMAKE_ENTRY_PUBLIC -DFREE_STANDING -DMHZ=48
+OPT   = -O3 -Os -g
+LIBS  =
+
+####################
+include ../rules.mk
+
+board.h:
+	@echo Please make a symbolic link \'board.h\' to a file in ../board;
+	@exit 1
+
+distclean: clean
+	rm -f board.h
diff --git a/example-fs-bb48/board.h b/example-fs-bb48/board.h
new file mode 120000
index 0000000..e6caddc
--- /dev/null
+++ b/example-fs-bb48/board.h
@@ -0,0 +1 @@
+../board/board-fs-bb48.h
\ No newline at end of file
diff --git a/example-fs-bb48/reset.c b/example-fs-bb48/reset.c
new file mode 100644
index 0000000..9af6f96
--- /dev/null
+++ b/example-fs-bb48/reset.c
@@ -0,0 +1,114 @@
+/*
+ * sys.c - No system routines, but only RESET handler for MKL27Z256.
+ *
+ * Copyright (C) 2016 Flying Stone Technology
+ * Author: NIIBE Yutaka <gniibe@fsij.org>
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved.  This file is offered as-is,
+ * without any warranty.
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+static void __attribute__ ((naked))
+reset (void)
+{
+  asm volatile ("cpsid	i\n\t"		/* Mask all interrupts. */
+		"mov	r0, pc\n\t"	/* r0 = PC & ~0x0fff */
+		"mov	r1, #0x10\n\t"
+		"lsl	r1, #8\n\t"
+		"sub	r1, r1, #1\n\t"
+		"bic	r0, r0, r1\n\t"
+		"ldr	r2, [r0]\n\t"
+		"msr	MSP, r2\n\t"	/* Main (exception handler) stack. */
+		"b	entry\n\t"
+		: /* no output */ : /* no input */ : "memory");
+  /* Never reach here. */
+}
+
+extern uint8_t __main_stack_end__;
+extern void preempt (void);
+extern void chx_timer_expired (void);
+extern void chx_handle_intr (void);
+
+static void nmi (void)
+{
+  for (;;);
+}
+
+static void __attribute__ ((naked))
+hard_fault (void)
+{
+  for (;;);
+}
+
+static void mem_manage (void)
+{
+  for (;;);
+}
+
+static void bus_fault (void)
+{
+  for (;;);
+}
+
+static void usage_fault (void)
+{
+  for (;;);
+}
+
+static void none (void)
+{
+}
+
+
+typedef void (*handler)(void);
+extern uint8_t __main_stack_end__;
+
+handler vector[] __attribute__ ((section(".vectors"))) = {
+  (handler)(&__main_stack_end__ - 32),
+  reset,
+  nmi,		/* nmi */
+  hard_fault,		/* hard fault */
+  /* 0x10 */
+  mem_manage,		/* mem manage */
+  bus_fault,		/* bus fault */
+  usage_fault,		/* usage fault */
+  none,
+  /* 0x20 */
+  none, none, none,		/* reserved */
+  none,				/* SVCall */
+  none,				/* Debug */
+  none,				/* reserved */
+  preempt,			/* PendSV */
+  chx_timer_expired,		/* SysTick */
+  /* 0x40 */
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  /* 0x60 */
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  /* 0x80 */
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  /* 0xA0 */
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,  chx_handle_intr,
+  /* 0xc0 */
+};
+
+uint32_t flash_config[] __attribute__ ((section(".flash_config"))) = {
+  0xffffffff, 0xffffffff, /* Backdoor comparison key. */
+  0xffffffff, /* Protection bytes */
+  0xffff3ffe, /* FSEC=0xfe, FOPT=0x3f */
+  /* FOPT=0x3f:
+   * BOOTSRC_SEL=00: Boot from flash
+   */
+  /* FSEC=0xfe:
+   * unsecure
+   */
+};
diff --git a/example-fs-bb48/sample.c b/example-fs-bb48/sample.c
new file mode 100644
index 0000000..92ecc9b
--- /dev/null
+++ b/example-fs-bb48/sample.c
@@ -0,0 +1,228 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <chopstx.h>
+
+#include "usb_lld.h"
+#include "stream.h"
+#include "board.h"
+
+struct GPIO {
+  volatile uint32_t PDOR; /* Port Data Output Register    */
+  volatile uint32_t PSOR; /* Port Set Output Register     */
+  volatile uint32_t PCOR; /* Port Clear Output Register   */
+  volatile uint32_t PTOR; /* Port Toggle Output Register  */
+  volatile uint32_t PDIR; /* Port Data Input Register     */
+  volatile uint32_t PDDR; /* Port Data Direction Register */
+};
+static struct GPIO *const GPIOB = (struct GPIO *const)0x400FF040;
+static struct GPIO *const GPIOD = (struct GPIO *const)0x400FF0C0;
+static struct GPIO *const GPIOE = (struct GPIO *const)0x400FF100;
+
+static void
+set_led (int on)
+{
+  if (on)
+    GPIOB->PCOR = (1 << 0); /* PTB0: Clear: Light on  */
+  else
+    GPIOB->PSOR = (1 << 0); /* PTB0: Set  : Light off */
+}
+
+static chopstx_mutex_t mtx;
+static chopstx_cond_t cnd0;
+static chopstx_cond_t cnd1;
+
+uint8_t u;
+static uint8_t v;
+static uint8_t m;		/* 0..100 */
+
+static void
+wait_for (uint32_t usec)
+{
+#if defined(BUSY_LOOP)
+  uint32_t count = usec * 6;
+  uint32_t i;
+
+  for (i = 0; i < count; i++)
+    asm volatile ("" : : "r" (i) : "memory");
+#else
+  chopstx_usec_wait (usec);
+#endif
+}
+
+static void *
+pwm (void *arg)
+{
+  (void)arg;
+
+  chopstx_mutex_lock (&mtx);
+  chopstx_cond_wait (&cnd0, &mtx);
+  chopstx_mutex_unlock (&mtx);
+
+  while (1)
+    {
+      set_led (u&v);
+      wait_for (m);
+      set_led (0);
+      wait_for (100-m);
+    }
+
+  return NULL;
+}
+
+static void *
+blk (void *arg)
+{
+  (void)arg;
+
+  chopstx_mutex_lock (&mtx);
+  chopstx_cond_wait (&cnd1, &mtx);
+  chopstx_mutex_unlock (&mtx);
+
+  while (1)
+    {
+      v = 0;
+      wait_for (200*1000);
+      v = 1;
+      wait_for (200*1000);
+    }
+
+  return NULL;
+}
+
+#define INTR_REQ_USB 24
+
+static void *
+usb_intr (void *arg)
+{
+  extern void usb_lld_init (uint8_t feature);
+  extern void usb_interrupt_handler (void);
+
+  chopstx_intr_t interrupt;
+
+  (void)arg;
+  chopstx_claim_irq (&interrupt, INTR_REQ_USB);
+  usb_lld_init (0x80);		/* Bus powered. */
+
+  while (1)
+    {
+      chopstx_intr_wait (&interrupt);
+
+      /* Process interrupt. */
+      usb_interrupt_handler ();
+    }
+
+  chopstx_release_irq (&interrupt);
+  return NULL;
+}
+
+#if defined(BUSY_LOOP)
+#define PRIO_PWM (CHOPSTX_SCHED_RR|1)
+#define PRIO_BLK (CHOPSTX_SCHED_RR|1)
+#else
+#define PRIO_PWM 3
+#define PRIO_BLK 2
+#endif
+#define PRIO_INTR 4
+
+extern uint8_t __process1_stack_base__, __process1_stack_size__;
+extern uint8_t __process2_stack_base__, __process2_stack_size__;
+extern uint8_t __process3_stack_base__, __process3_stack_size__;
+
+const uint32_t __stackaddr_pwm = (uint32_t)&__process1_stack_base__;
+const size_t __stacksize_pwm = (size_t)&__process1_stack_size__;
+
+const uint32_t __stackaddr_blk = (uint32_t)&__process2_stack_base__;
+const size_t __stacksize_blk = (size_t)&__process2_stack_size__;
+
+const uint32_t __stackaddr_intr = (uint32_t)&__process3_stack_base__;
+const size_t __stacksize_intr = (size_t)&__process3_stack_size__;
+
+
+static char hexchar (uint8_t x)
+{
+  if (x <= 0x09)
+    return '0' + x;
+  else if (x <= 0x0f)
+    return 'a' + x - 10;
+  else
+    return '?';
+}
+
+int
+main (int argc, const char *argv[])
+{
+  struct stream *st;
+  uint8_t count;
+  extern uint32_t bDeviceState;
+
+  (void)argc;
+  (void)argv;
+
+  chopstx_mutex_init (&mtx);
+  chopstx_cond_init (&cnd0);
+  chopstx_cond_init (&cnd1);
+
+  st = stream_open ();
+
+  m = 10;
+
+  chopstx_create (PRIO_PWM, __stackaddr_pwm, __stacksize_pwm, pwm, NULL);
+  chopstx_create (PRIO_BLK, __stackaddr_blk, __stacksize_blk, blk, NULL);
+  chopstx_create (PRIO_INTR, __stackaddr_intr, __stacksize_intr,
+		  usb_intr, NULL);
+
+  chopstx_usec_wait (200*1000);
+
+  chopstx_mutex_lock (&mtx);
+  chopstx_cond_signal (&cnd0);
+  chopstx_cond_signal (&cnd1);
+  chopstx_mutex_unlock (&mtx);
+
+  u = 1;
+  while (bDeviceState != CONFIGURED)
+    chopstx_usec_wait (500*1000);
+
+  count = 0;
+  while (1)
+    {
+      uint8_t s[64];
+
+      if (stream_wait_connection (st) < 0)
+	{
+	  chopstx_usec_wait (1000*1000);
+	  continue;
+	}
+
+      chopstx_usec_wait (500*1000);
+
+      /* Send ZLP at the beginning.  */
+      stream_send (st, s, 0);
+
+      memcpy (s, "xx: Hello, World with Chopstx!\r\n\000", 32);
+      s[0] = hexchar (count >> 4);
+      s[1] = hexchar (count & 0x0f);
+      count++;
+
+      if (stream_send (st, s, 32) < 0)
+	continue;
+
+      while (1)
+	{
+	  int size = stream_recv (st, s);
+
+	  if (size < 0)
+	    break;
+
+	  if (size >= 0)
+	    {
+	      if (stream_send (st, s, size) < 0)
+		break;
+	    }
+
+	  u ^= 1;
+	}
+    }
+
+  return 0;
+}
diff --git a/example-fs-bb48/sample.ld b/example-fs-bb48/sample.ld
new file mode 100644
index 0000000..787bda9
--- /dev/null
+++ b/example-fs-bb48/sample.ld
@@ -0,0 +1,135 @@
+/*
+ * MK27Z memory setup.
+ */
+__main_stack_size__     = 0x0100;  /* Exception handlers     */
+__process0_stack_size__  = 0x0300; /* Main program           */
+__process1_stack_size__  = 0x0200; /* first thread program */
+__process2_stack_size__  = 0x0100; /* second thread program */
+__process3_stack_size__  = 0x0200; /* third thread program */
+
+MEMORY
+{
+    flash  : org = 0x00000000, len = 256k
+    ram : org = 0x1fffe000, len = 32k
+}
+
+__ram_start__           = ORIGIN(ram);
+__ram_size__            = 32k;
+__ram_end__             = __ram_start__ + __ram_size__;
+
+SECTIONS
+{
+    . = 0;
+
+    _text = .;
+
+    .text : ALIGN(16) SUBALIGN(16)
+    {
+	KEEP(*(.vectors))
+	. = ALIGN(1024);
+        KEEP(*(.flash_config))
+	. = ALIGN(16);
+        *(.text.svc)
+        *(.text.sched)
+        *(.text.preempt)
+        *(.text.startup.*)
+        *(.text)
+        *(.text.*)
+        *(.rodata)
+        *(.rodata.*)
+        *(.glue_7t)
+        *(.glue_7)
+        *(.gcc*)
+	. = ALIGN(8);
+    } > flash
+
+    /DISCARD/ :
+    {
+        *(.startup.vectors)
+        *(.bss.startup.0)
+    }
+
+    .ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash
+
+    .ARM.exidx : {
+        PROVIDE(__exidx_start = .);
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+        PROVIDE(__exidx_end = .);
+     } > flash
+
+    .eh_frame_hdr : {*(.eh_frame_hdr)} > flash
+
+    .eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash
+
+    .textalign : ONLY_IF_RO { . = ALIGN(8); } > flash
+
+    _etext = .;
+    _textdata = _etext;
+
+    .process_stack :
+    {
+        . = ALIGN(8);
+        __process3_stack_base__ = .;
+        . += __process3_stack_size__;
+        . = ALIGN(8);
+        __process3_stack_end__ = .;
+        __process2_stack_base__ = .;
+        . += __process2_stack_size__;
+        . = ALIGN(8);
+        __process2_stack_end__ = .;
+        __process1_stack_base__ = .;
+        . += __process1_stack_size__;
+        . = ALIGN(8);
+        __process1_stack_end__ = .;
+        __process0_stack_base__ = .;
+        . += __process0_stack_size__;
+        . = ALIGN(8);
+        __process0_stack_end__ = .;
+    } > ram
+
+    .main_stack :
+    {
+        . = ALIGN(8);
+        __main_stack_base__ = .;
+        . += __main_stack_size__;
+        . = ALIGN(8);
+        __main_stack_end__ = .;
+    } > ram
+
+    .data :
+    {
+        . = ALIGN(512);
+	__usb_bdt__ = .;
+        . += 512;
+	__usb_buf__ = .;
+        . += 8 /*control write*/ + 64 /*control read*/ + 64 + 64 + 8;
+        . = ALIGN(4);
+        PROVIDE(_data = .);
+        *(.data)
+        . = ALIGN(4);
+        *(.data.*)
+        . = ALIGN(4);
+        *(.ramtext)
+        . = ALIGN(4);
+        PROVIDE(_edata = .);
+    } > ram AT > flash
+
+    .bss :
+    {
+        . = ALIGN(4);
+        PROVIDE(_bss_start = .);
+        *(.bss)
+        . = ALIGN(4);
+        *(.bss.*)
+        . = ALIGN(4);
+        *(COMMON)
+        . = ALIGN(4);
+        PROVIDE(_bss_end = .);
+    } > ram
+
+    PROVIDE(end = .);
+    _end            = .;
+}
+
+__heap_base__   = _end;
+__heap_end__    = __ram_end__;
diff --git a/example-fs-bb48/stream.h b/example-fs-bb48/stream.h
new file mode 100644
index 0000000..15cbe63
--- /dev/null
+++ b/example-fs-bb48/stream.h
@@ -0,0 +1,24 @@
+#define BUFSIZE 128
+#define FLAG_CONNECTED   (1 << 0)
+#define FLAG_SEND_AVAIL  (1 << 1)
+#define FLAG_RECV_AVAIL  (1 << 2)
+
+/*
+ * Current implementation is synchronous and buffers are not yet used.
+ */
+struct stream {
+  chopstx_mutex_t mtx;
+  chopstx_cond_t cnd;
+  uint8_t buf_send[BUFSIZE];	/* Not yet used. */
+  uint8_t buf_recv[BUFSIZE];	/* Not yet used. */
+  uint8_t cnt_send_head;	/* Not yet used. */
+  uint8_t cnt_send_tail;	/* Not yet used. */
+  uint8_t cnt_recv_head;	/* Not yet used. */
+  uint8_t cnt_recv_tail;	/* Not yet used. */
+  uint32_t flags;
+};
+
+struct stream *stream_open (void);
+int stream_wait_connection (struct stream *st);
+int stream_send (struct stream *st, uint8_t *buf, uint8_t count);
+int stream_recv (struct stream *st, uint8_t *buf);
diff --git a/example-fs-bb48/usb-cdc.c b/example-fs-bb48/usb-cdc.c
new file mode 100644
index 0000000..290a4fd
--- /dev/null
+++ b/example-fs-bb48/usb-cdc.c
@@ -0,0 +1,506 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <chopstx.h>
+#include "usb_lld.h"
+#include "stream.h"
+
+static struct stream stream;
+
+#define USB_CDC_REQ_SET_LINE_CODING             0x20
+#define USB_CDC_REQ_GET_LINE_CODING             0x21
+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE      0x22
+#define USB_CDC_REQ_SEND_BREAK                  0x23
+
+/* USB Device Descriptor */
+static const uint8_t vcom_device_desc[18] = {
+  18,   /* bLength */
+  DEVICE_DESCRIPTOR,		/* bDescriptorType */
+  0x10, 0x01,			/* bcdUSB = 1.1 */
+  0x02,				/* bDeviceClass (CDC).              */
+  0x00,				/* bDeviceSubClass.                 */
+  0x00,				/* bDeviceProtocol.                 */
+  0x40,				/* bMaxPacketSize.                  */
+  0xFF, 0xFF, /* idVendor  */
+  0x01, 0x00, /* idProduct */
+  0x00, 0x01, /* bcdDevice  */
+  1,				/* iManufacturer.                   */
+  2,				/* iProduct.                        */
+  3,				/* iSerialNumber.                   */
+  1				/* bNumConfigurations.              */
+};
+
+/* Configuration Descriptor tree for a CDC.*/
+static const uint8_t vcom_config_desc[67] = {
+  9,
+  CONFIG_DESCRIPTOR,		/* bDescriptorType: Configuration */
+  /* Configuration Descriptor.*/
+  67, 0x00,			/* wTotalLength.                    */
+  0x02,				/* bNumInterfaces.                  */
+  0x01,				/* bConfigurationValue.             */
+  0,				/* iConfiguration.                  */
+  0x80,				/* bmAttributes (bus powered).      */
+  50,				/* bMaxPower (100mA).               */
+  /* Interface Descriptor.*/
+  9,
+  INTERFACE_DESCRIPTOR,
+  0x00,		   /* bInterfaceNumber.                */
+  0x00,		   /* bAlternateSetting.               */
+  0x01,		   /* bNumEndpoints.                   */
+  0x02,		   /* bInterfaceClass (Communications Interface Class,
+		      CDC section 4.2).  */
+  0x02,		   /* bInterfaceSubClass (Abstract Control Model, CDC
+		      section 4.3).  */
+  0x01,		   /* bInterfaceProtocol (AT commands, CDC section
+		      4.4).  */
+  0,	           /* iInterface.                      */
+  /* Header Functional Descriptor (CDC section 5.2.3).*/
+  5,	      /* bLength.                         */
+  0x24,	      /* bDescriptorType (CS_INTERFACE).  */
+  0x00,	      /* bDescriptorSubtype (Header Functional Descriptor). */
+  0x10, 0x01, /* bcdCDC.                          */
+  /* Call Management Functional Descriptor. */
+  5,            /* bFunctionLength.                 */
+  0x24,         /* bDescriptorType (CS_INTERFACE).  */
+  0x01,         /* bDescriptorSubtype (Call Management Functional
+		   Descriptor). */
+  0x03,         /* bmCapabilities (D0+D1).          */
+  0x01,         /* bDataInterface.                  */
+  /* ACM Functional Descriptor.*/
+  4,            /* bFunctionLength.                 */
+  0x24,         /* bDescriptorType (CS_INTERFACE).  */
+  0x02,         /* bDescriptorSubtype (Abstract Control Management
+		   Descriptor).  */
+  0x02,         /* bmCapabilities.                  */
+  /* Union Functional Descriptor.*/
+  5,            /* bFunctionLength.                 */
+  0x24,         /* bDescriptorType (CS_INTERFACE).  */
+  0x06,         /* bDescriptorSubtype (Union Functional
+		   Descriptor).  */
+  0x00,         /* bMasterInterface (Communication Class
+		   Interface).  */
+  0x01,         /* bSlaveInterface0 (Data Class Interface).  */
+  /* Endpoint 2 Descriptor.*/
+  7,
+  ENDPOINT_DESCRIPTOR,
+  ENDP2|0x80,    /* bEndpointAddress.    */
+  0x03,          /* bmAttributes (Interrupt).        */
+  0x08, 0x00,	 /* wMaxPacketSize.                  */
+  0xFF,		 /* bInterval.                       */
+  /* Interface Descriptor.*/
+  9,
+  INTERFACE_DESCRIPTOR, /* bDescriptorType: */
+  0x01,          /* bInterfaceNumber.                */
+  0x00,          /* bAlternateSetting.               */
+  0x02,          /* bNumEndpoints.                   */
+  0x0A,          /* bInterfaceClass (Data Class Interface, CDC section 4.5). */
+  0x00,          /* bInterfaceSubClass (CDC section 4.6). */
+  0x00,          /* bInterfaceProtocol (CDC section 4.7). */
+  0x00,		 /* iInterface.                      */
+  /* Endpoint 3 Descriptor.*/
+  7,
+  ENDPOINT_DESCRIPTOR,		/* bDescriptorType: Endpoint */
+  ENDP3,    /* bEndpointAddress. */
+  0x02,				/* bmAttributes (Bulk).             */
+  0x40, 0x00,			/* wMaxPacketSize.                  */
+  0x00,				/* bInterval.                       */
+  /* Endpoint 1 Descriptor.*/
+  7,
+  ENDPOINT_DESCRIPTOR,		/* bDescriptorType: Endpoint */
+  ENDP1|0x80,			/* bEndpointAddress. */
+  0x02,				/* bmAttributes (Bulk).             */
+  0x40, 0x00,			/* wMaxPacketSize.                  */
+  0x00				/* bInterval.                       */
+};
+
+
+/*
+ * U.S. English language identifier.
+ */
+static const uint8_t vcom_string0[4] = {
+  4,				/* bLength */
+  STRING_DESCRIPTOR,
+  0x09, 0x04			/* LangID = 0x0409: US-English */
+};
+
+static const uint8_t vcom_string1[] = {
+  23*2+2,			/* bLength */
+  STRING_DESCRIPTOR,		/* bDescriptorType */
+  /* Manufacturer: "Flying Stone Technology" */
+  'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0,
+  't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0,
+  'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0, 
+};
+
+static const uint8_t vcom_string2[] = {
+  14*2+2,			/* bLength */
+  STRING_DESCRIPTOR,		/* bDescriptorType */
+  /* Product name: "Chopstx Sample" */
+  'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0,
+  'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0,
+};
+
+/*
+ * Serial Number string.
+ */
+static const uint8_t vcom_string3[28] = {
+  28,				    /* bLength */
+  STRING_DESCRIPTOR,		    /* bDescriptorType */
+  '0', 0,  '.', 0,  '0', 0, '0', 0, /* Version number */
+};
+
+
+#define NUM_INTERFACES 2
+
+uint32_t bDeviceState = UNCONNECTED; /* USB device status */
+
+
+void
+usb_cb_device_reset (void)
+{
+  usb_lld_reset (vcom_config_desc[7]);
+
+  /* Initialize Endpoint 0 */
+  usb_lld_setup_endpoint (ENDP0, 1, 1);
+
+  chopstx_mutex_lock (&stream.mtx);
+  stream.flags = 0;
+  bDeviceState = ATTACHED;
+  chopstx_mutex_unlock (&stream.mtx);
+}
+
+
+#define CDC_CTRL_DTR            0x0001
+
+void
+usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no, struct req_args *arg)
+{
+  uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
+
+  if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT)
+      && USB_SETUP_SET (req) && req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+    {
+      /* Open/close the connection.  */
+      chopstx_mutex_lock (&stream.mtx);
+      stream.flags &= ~FLAG_CONNECTED;
+      stream.flags |= ((arg->value & CDC_CTRL_DTR) != 0)? FLAG_CONNECTED : 0;
+      chopstx_cond_signal (&stream.cnd);
+      chopstx_mutex_unlock (&stream.mtx);
+    }
+}
+
+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, struct req_args *arg)
+{
+  if (USB_SETUP_GET (req))
+    {
+      if (req_no == USB_CDC_REQ_GET_LINE_CODING)
+	return usb_lld_reply_request (&line_coding, sizeof(line_coding), arg);
+    }
+  else  /* USB_SETUP_SET (req) */
+    {
+      if (req_no == USB_CDC_REQ_SET_LINE_CODING
+	  && arg->len == sizeof (line_coding))
+	{
+	  usb_lld_set_data_to_recv (&line_coding, sizeof (line_coding));
+	  return USB_SUCCESS;
+	}
+      else if (req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
+	return USB_SUCCESS;
+    }
+
+  return USB_UNSUPPORT;
+}
+
+int
+usb_cb_setup (uint8_t req, uint8_t req_no, struct req_args *arg)
+{
+  uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
+
+  if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0)
+    return vcom_port_data_setup (req, req_no, arg);
+
+  return USB_UNSUPPORT;
+}
+
+int
+usb_cb_get_descriptor (uint8_t rcp, uint8_t desc_type, uint8_t desc_index,
+		       struct req_args *arg)
+{
+  if (rcp != DEVICE_RECIPIENT)
+    return USB_UNSUPPORT;
+
+  if (desc_type == DEVICE_DESCRIPTOR)
+    return usb_lld_reply_request (vcom_device_desc, sizeof (vcom_device_desc),
+				  arg);
+  else if (desc_type == CONFIG_DESCRIPTOR)
+    return usb_lld_reply_request (vcom_config_desc, sizeof (vcom_config_desc),
+				  arg);
+  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 = vcom_string1;
+	  size = sizeof (vcom_string1);
+	  break;
+	case 2:
+	  str = vcom_string2;
+	  size = sizeof (vcom_string2);
+	  break;
+	case 3:
+	  str = vcom_string3;
+	  size = sizeof (vcom_string3);
+	  break;
+	default:
+	  return USB_UNSUPPORT;
+	}
+
+      return usb_lld_reply_request (str, size, arg);
+    }
+
+  return USB_UNSUPPORT;
+}
+
+static void
+vcom_setup_endpoints_for_interface (uint16_t interface, int stop)
+{
+  if (interface == 0)
+    {
+      if (!stop)
+	usb_lld_setup_endpoint (ENDP2, 0, 1);
+      else
+	usb_lld_stall (ENDP2);
+    }
+  else if (interface == 1)
+    {
+      if (!stop)
+	{
+	  usb_lld_setup_endpoint (ENDP1, 0, 1);
+	  usb_lld_setup_endpoint (ENDP3, 1, 0);
+#if 0
+	  /* Start with no data receiving */
+	  usb_lld_stall (ENDP3);
+#endif
+	}
+      else
+	{
+	  usb_lld_stall (ENDP1);
+	  usb_lld_stall (ENDP3);
+	}
+    }
+}
+
+int
+usb_cb_handle_event (uint8_t event_type, uint16_t value)
+{
+  int i;
+  uint8_t current_conf;
+
+  switch (event_type)
+    {
+    case USB_EVENT_ADDRESS:
+      bDeviceState = ADDRESSED;
+      return USB_SUCCESS;
+    case USB_EVENT_CONFIG:
+      current_conf = usb_lld_current_configuration ();
+      if (current_conf == 0)
+	{
+	  if (value != 1)
+	    return USB_UNSUPPORT;
+
+	  usb_lld_set_configuration (1);
+	  for (i = 0; i < NUM_INTERFACES; i++)
+	    vcom_setup_endpoints_for_interface (i, 0);
+	  bDeviceState = CONFIGURED;
+	}
+      else if (current_conf != value)
+	{
+	  if (value != 0)
+	    return USB_UNSUPPORT;
+
+	  usb_lld_set_configuration (0);
+	  for (i = 0; i < NUM_INTERFACES; i++)
+	    vcom_setup_endpoints_for_interface (i, 1);
+	  bDeviceState = ADDRESSED;
+	}
+      /* Do nothing when current_conf == value */
+      return USB_SUCCESS;
+
+      return USB_SUCCESS;
+    default:
+      break;
+    }
+
+  return USB_UNSUPPORT;
+}
+
+
+int
+usb_cb_interface (uint8_t cmd, struct req_args *arg)
+{
+  const uint8_t zero = 0;
+  uint16_t interface = arg->index;
+  uint16_t alt = arg->value;
+
+  if (interface >= NUM_INTERFACES)
+    return USB_UNSUPPORT;
+
+  switch (cmd)
+    {
+    case USB_SET_INTERFACE:
+      if (alt != 0)
+	return USB_UNSUPPORT;
+      else
+	{
+	  vcom_setup_endpoints_for_interface (interface, 0);
+	  return USB_SUCCESS;
+	}
+
+    case USB_GET_INTERFACE:
+      return usb_lld_reply_request (&zero, 1, arg);
+
+    default:
+    case USB_QUERY_INTERFACE:
+      return USB_SUCCESS;
+    }
+}
+
+
+void
+usb_cb_tx_done (uint8_t ep_num)
+{
+  if (ep_num == ENDP1)
+    {
+      chopstx_mutex_lock (&stream.mtx);
+      if ((stream.flags & FLAG_SEND_AVAIL))
+	{
+	  stream.flags &= ~FLAG_SEND_AVAIL;
+	  chopstx_cond_signal (&stream.cnd);
+	}
+      chopstx_mutex_unlock (&stream.mtx);
+    }
+  else if (ep_num == ENDP2)
+    {
+    }
+}
+
+void
+usb_cb_rx_ready (uint8_t ep_num)
+{
+  if (ep_num == ENDP3)
+    {
+      chopstx_mutex_lock (&stream.mtx);
+      if ((stream.flags & FLAG_RECV_AVAIL) == 0)
+	{
+	  stream.flags |= FLAG_RECV_AVAIL;
+	  chopstx_cond_signal (&stream.cnd);
+	}
+      chopstx_mutex_unlock (&stream.mtx);
+    }
+}
+
+struct stream *
+stream_open (void)
+{
+  chopstx_mutex_init (&stream.mtx);
+  chopstx_cond_init (&stream.cnd);
+  return &stream;
+}
+
+int
+stream_wait_connection (struct stream *st)
+{
+  chopstx_mutex_lock (&st->mtx);
+  while ((stream.flags & FLAG_CONNECTED) == 0)
+    chopstx_cond_wait (&st->cnd, &st->mtx);
+  chopstx_mutex_unlock (&st->mtx);
+  stream.flags &= ~FLAG_SEND_AVAIL;
+  return 0;
+}
+
+
+int
+stream_send (struct stream *st, uint8_t *buf, uint8_t count)
+{
+  int r = 0;
+
+  chopstx_mutex_lock (&st->mtx);
+  if ((stream.flags & FLAG_CONNECTED) == 0)
+    r = -1;
+  else
+    {
+      usb_lld_tx_enable (ENDP1, buf, count);
+      stream.flags |= FLAG_SEND_AVAIL;
+      do
+	{
+	  chopstx_cond_wait (&st->cnd, &st->mtx);
+	  if ((stream.flags & FLAG_SEND_AVAIL) == 0)
+	    break;
+	  else if ((stream.flags & FLAG_CONNECTED) == 0)
+	    {
+	      r = -1;
+	      break;
+	    }
+	}
+      while (1);
+    }
+  chopstx_mutex_unlock (&st->mtx);
+  return r;
+}
+
+
+int
+stream_recv (struct stream *st, uint8_t *buf)
+{
+  int r;
+
+  chopstx_mutex_lock (&st->mtx);
+  if ((stream.flags & FLAG_CONNECTED) == 0)
+    r = -1;
+  else
+    {
+      usb_lld_rx_enable (ENDP3, buf, 64);
+      stream.flags &= ~FLAG_RECV_AVAIL;
+      do
+	{
+	  chopstx_cond_wait (&st->cnd, &st->mtx);
+	  if ((stream.flags & FLAG_RECV_AVAIL))
+	    {
+	      r = usb_lld_rx_data_len (ENDP3);
+	      break;
+	    }
+	  else if ((stream.flags & FLAG_CONNECTED) == 0)
+	    {
+	      r = -1;
+	      break;
+	    }
+	}
+      while (1);
+    }
+  chopstx_mutex_unlock (&st->mtx);
+
+  return r;
+}
diff --git a/example-fs-bb48/usb_kl27z.c b/example-fs-bb48/usb_kl27z.c
new file mode 100644
index 0000000..09a65f7
--- /dev/null
+++ b/example-fs-bb48/usb_kl27z.c
@@ -0,0 +1,1035 @@
+/*
+ * usb_kl27z.c - USB driver for KL27Z
+ *
+ * Copyright (C) 2016  Flying Stone Technology
+ * Author: NIIBE Yutaka <gniibe@fsij.org>
+ *
+ * This file is a part of Chopstx, a thread library for embedded.
+ *
+ * Chopstx is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chopstx 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As additional permission under GNU GPL version 3 section 7, you may
+ * distribute non-source form of the Program without the copy of the
+ * GNU GPL normally required by section 4, provided you inform the
+ * receipents of GNU GPL by a written offer.
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "usb_lld.h"
+
+struct endpoint_ctl {
+  uint32_t rx_odd: 1;
+  uint32_t tx_odd: 1;
+};
+static struct endpoint_ctl ep[16];
+
+struct USB_CONF {
+  const uint8_t PERID;       /* Peripheral ID register              */
+  uint8_t rsvd0[3];          /*                                     */
+  const uint8_t IDCOMP;      /* Peripheral ID Complement register   */
+  uint8_t rsvd1[3];          /*                                     */
+  const uint8_t REV;         /* Peripheral Revision register        */
+  uint8_t rsvd2[3];          /*                                     */
+  volatile uint8_t ADDINFO;  /* Peripheral Additional Info register */
+};
+static struct USB_CONF *const USB_CONF = (struct USB_CONF *const) 0x40072000;
+
+struct USB_CTRL0 {
+  volatile uint8_t OTGCTL;   /* OTG Control register                */
+};
+static struct USB_CTRL0 *const USB_CTRL0 = (struct USB_CTRL0 *const)0x4007201c;
+
+struct USB_CTRL1 {
+  volatile uint8_t ISTAT;    /* Interrupt Status register           */
+  uint8_t rsvd5[3];          /*                                     */
+  volatile uint8_t INTEN;    /* Interrupt Enable register           */
+  uint8_t rsvd6[3];          /*                                     */
+  volatile uint8_t ERRSTAT;  /* Error Interrupt Status register     */
+  uint8_t rsvd7[3];          /*                                     */
+  volatile uint8_t ERREN;    /* Error Interrupt Enable register     */
+  uint8_t rsvd8[3];          /*                                     */
+  volatile uint8_t STAT;     /* Status register                     */
+  uint8_t rsvd9[3];          /*                                     */
+  volatile uint8_t CTL;      /* Control register                    */
+  uint8_t rsvd10[3];         /*                                     */
+  volatile uint8_t ADDR;     /* Address register                    */
+  uint8_t rsvd11[3];         /*                                     */
+  volatile uint8_t BDTPAGE1; /* BDT Page register 1                 */
+  uint8_t rsvd12[3];         /*                                     */
+  volatile uint8_t FRMNUML;  /* Frame Number register Low           */
+  uint8_t rsvd13[3];         /*                                     */
+  volatile uint8_t FRMNUMH;  /* Frame Number register High          */
+  uint8_t rsvd14[11];        /*                                     */
+  volatile uint8_t BDTPAGE2; /* BDT Page Register 2                 */
+  uint8_t rsvd15[3];         /*                                     */
+  volatile uint8_t BDTPAGE3; /* BDT Page Register 3                 */
+};
+static struct USB_CTRL1 *const USB_CTRL1 = (struct USB_CTRL1 *const)0x40072080;
+
+/* Interrupt source bits */
+#define USB_IS_STALL  (1 << 7)
+#define USB_IS_RESUME (1 << 5)
+#define USB_IS_SLEEP  (1 << 4)
+#define USB_IS_TOKDNE (1 << 3)
+#define USB_IS_SOFTOK (1 << 2)
+#define USB_IS_ERROR  (1 << 1)
+#define USB_IS_USBRST (1 << 0)
+
+
+struct USB_ENDPT {
+  volatile uint8_t EP;  /* Endpoint Control register */
+  uint8_t rsvd17[3];
+};
+static struct USB_ENDPT *const USB_ENDPT = (struct USB_ENDPT *const)0x400720c0;
+
+struct USB_CTRL2 {
+  volatile uint8_t USBCTRL;  /* USB Control register                */
+  uint8_t rsvd33[3];         /*                                     */
+  volatile uint8_t OBSERVE;  /* USB OTG Observe register            */
+  uint8_t rsvd34[3];         /*                                     */
+  volatile uint8_t CONTROL;  /* USB OTG Control register            */
+  uint8_t rsvd35[3];         /*                                     */
+  volatile uint8_t USBTRC0;  /* USB Transceiver Control register 0  */
+  uint8_t rsvd36[7];         /*                                     */
+  volatile uint8_t USBFRMADJUST;           /* Frame Adjut Register  */
+};
+static struct USB_CTRL2 *const USB_CTRL2 = (struct USB_CTRL2 *const)0x40072100;
+
+/* Buffer Descriptor */
+struct BD {
+  volatile uint32_t ctrl;
+  volatile void *buf; 
+};
+/*
+  uint32_t rsvd0 : 2;
+  volatile uint32_t STALL: 1;
+  volatile uint32_t DTS: 1;
+
+  volatile uint32_t NINC: 1;
+  volatile uint32_t KEEP: 1;
+  volatile uint32_t DATA01: 1;
+  volatile uint32_t OWN: 1;
+
+  uint32_t rsvd1: 8;
+  volatile uint32_t BC: 10;
+  uint32_t rsvd2: 6;
+*/
+#define TOK_PID(ctrl)   ((ctrl >> 2) & 0x0f)
+
+extern uint8_t __usb_bdt__;
+extern uint8_t __usb_buf__;
+
+static struct BD *const BD_table = (struct BD *const)&__usb_bdt__;
+
+static uint8_t setup[8];
+/* bmRequestType, bRequest */
+/* Value: 2-byte  */
+/* Index: 2-byte  */
+/* Length: 2-byte */
+
+static void
+kl27z_usb_init (void)
+{
+  int i;
+
+  memset (ep, 0, sizeof (ep));
+  memset (BD_table, 0, 10 * sizeof (struct BD));
+
+  /* D+ pull up */
+  USB_CTRL0->OTGCTL = 0x80;
+
+  USB_CTRL1->ERREN = 0xff;
+
+  USB_CTRL1->BDTPAGE1 = ((uint32_t)BD_table) >> 8;
+  USB_CTRL1->BDTPAGE2 = ((uint32_t)BD_table) >> 16;
+  USB_CTRL1->BDTPAGE3 = ((uint32_t)BD_table) >> 24;
+
+  /* Not suspended, Pull-down disabled.  */
+  USB_CTRL2->USBCTRL = 0x00;
+  /* DP Pullup in non-OTG device mode.  */
+  USB_CTRL2->CONTROL = 0x10;
+
+  /* Disable all endpoints.  */
+  for (i = 0; i < 16; i++)
+    USB_ENDPT[i].EP = 0;
+
+  /* 
+   * Enable USB FS communication module, clearing all ODD-bits
+   * for BDT.
+   */
+  USB_CTRL1->CTL = 0x03;    
+
+  /* ??? How we can ask re-enumeration?  Is only hard RESET enough? */
+}
+
+static void
+kl27z_set_daddr (uint8_t daddr)
+{
+  USB_CTRL1->ADDR = daddr;
+}
+
+static void
+kl27z_prepare_ep0_setup (void)
+{
+  /* Endpoint 0, TX=0. */
+  BD_table[ep[0].rx_odd].ctrl = 0x00080088; /* Len=8, OWN=1, DATA01=0, DTS=1 */
+  BD_table[ep[0].rx_odd].buf = setup;
+
+  BD_table[!ep[0].rx_odd].ctrl = 0x0000; /* OWN=0 */
+  BD_table[!ep[0].rx_odd].buf = NULL;
+}
+
+static void
+kl27z_prepare_ep0_in (const void *buf, uint8_t len, int data01)
+{
+  /* Endpoint 0, TX=1 *//* OWN=1, DTS=1 */
+  BD_table[2+ep[0].tx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); 
+  BD_table[2+ep[0].tx_odd].buf = (void *)buf;
+}
+
+static void
+kl27z_prepare_ep0_out (void *buf, uint8_t len, int data01)
+{
+  /* Endpoint 0, TX=0 *//* OWN=1, DTS=1 */
+  BD_table[ep[0].rx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6);
+  BD_table[ep[0].rx_odd].buf = buf;
+}
+
+static int
+kl27z_ep_is_disabled (uint8_t n)
+{
+  return (USB_ENDPT[n].EP == 0);
+}
+
+static int
+kl27z_ep_is_stall (uint8_t n)
+{
+  return (USB_ENDPT[n].EP & 0x02) >> 1;
+}
+
+static void
+kl27z_ep_stall (uint8_t n)
+{
+  USB_ENDPT[n].EP |= 0x02;
+}
+
+static void
+kl27z_ep_clear_stall (uint8_t n)
+{
+  USB_ENDPT[n].EP &= ~0x02;
+}
+
+static void
+kl27z_ep_clear_dtog (int rx, uint8_t n)
+{
+  uint32_t config;
+
+  if (!kl27z_ep_is_stall (n))
+    /* Just in case, when the endpoint is active */
+    kl27z_ep_stall (n);
+
+  if (rx)
+    {
+      config = BD_table[4*n+ep[n].rx_odd].ctrl;
+
+      BD_table[4*n+!ep[n].rx_odd].ctrl &= ~(1 << 6);
+      if ((config & 0x0080)) /* OWN already? */
+	{
+	  /*
+	   * How to update BDT entry which is owned by USBFS seems to
+	   * be not clearly documented.  It would be just OK to update
+	   * it as long as the endpoint is stalled (BDT entry is
+	   * actually not in use).  We write 0 at first and then write
+	   * value with OWN, to avoid possible failure.
+	   */
+	  BD_table[4*n+ep[n].rx_odd].ctrl = 0;
+	  BD_table[4*n+ep[n].rx_odd].ctrl = (config & ~(1 << 6));
+	}
+    }
+  else
+    {
+      config = BD_table[4*n+2+ep[n].tx_odd].ctrl;
+
+      BD_table[4*n+2+!ep[n].tx_odd].ctrl &= ~(1 << 6);
+      if ((config & 0x0080)) /* OWN already? */
+	{
+	  BD_table[4*n+2+ep[n].tx_odd].ctrl = 0;
+	  BD_table[4*n+2+ep[n].tx_odd].ctrl = (config & ~(1 << 6));
+	}
+    }
+
+  kl27z_ep_clear_stall (n);
+}
+
+#define USB_MAX_PACKET_SIZE 64	/* For FS device */
+
+enum STANDARD_REQUESTS {
+  GET_STATUS = 0,
+  CLEAR_FEATURE,
+  RESERVED1,
+  SET_FEATURE,
+  RESERVED2,
+  SET_ADDRESS,
+  GET_DESCRIPTOR,
+  SET_DESCRIPTOR,
+  GET_CONFIGURATION,
+  SET_CONFIGURATION,
+  GET_INTERFACE,
+  SET_INTERFACE,
+  SYNCH_FRAME,
+  TOTAL_REQUEST  /* Total number of Standard request */
+};
+
+
+enum FEATURE_SELECTOR {
+  ENDPOINT_STALL,
+  DEVICE_REMOTE_WAKEUP
+};
+
+
+struct data_ctl {
+  uint8_t *addr;
+  uint16_t len;
+  uint8_t require_zlp;
+};
+
+/* The state machine states of a control pipe */
+enum {
+  WAIT_SETUP,
+  IN_DATA,
+  OUT_DATA,
+  LAST_IN_DATA,
+  WAIT_STATUS_IN,
+  WAIT_STATUS_OUT,
+  STALLED,
+  PAUSE
+};
+
+struct device_ctl {
+  /* control pipe state */
+  uint8_t state;		
+
+  uint32_t tkdone;
+  uint32_t reset;
+  uint32_t error;
+  uint32_t stall;
+
+  uint32_t send;
+  uint32_t recv;
+
+  /* Device specific settings */
+  uint8_t configuration;
+  uint8_t feature;
+};
+
+static struct device_ctl device_ctl;
+static struct data_ctl data_ctl;
+
+static struct device_ctl *const dev_p = &device_ctl;
+static struct data_ctl *const data_p = &data_ctl;
+
+static void handle_transaction (uint8_t stat);
+
+void
+usb_lld_stall (int n)
+{
+  kl27z_ep_stall (n);
+}
+
+
+void
+usb_lld_init (uint8_t feature)
+{
+  dev_p->state = WAIT_SETUP;
+  dev_p->tkdone = 0;
+  dev_p->reset = 0;
+  dev_p->error = 0;
+  dev_p->stall = 0;
+
+  usb_lld_set_configuration (0);
+  dev_p->feature = feature;
+
+  kl27z_set_daddr (0);
+  kl27z_usb_init ();
+
+  /* Enable the endpoint 0.  */
+  USB_ENDPT[0].EP = 0x0d;
+
+  /* Clear Interrupt Status Register, and enable interrupt for USB */
+  USB_CTRL1->ISTAT = 0xff;		     /* All clear */
+
+  USB_CTRL1->INTEN = USB_IS_STALL | USB_IS_TOKDNE
+                   | USB_IS_ERROR | USB_IS_USBRST;
+}
+
+void
+usb_interrupt_handler (void)
+{
+  uint8_t istat_value = USB_CTRL1->ISTAT;
+  uint8_t stat = USB_CTRL1->STAT;
+
+  if ((istat_value & USB_IS_USBRST))
+    {
+      USB_CTRL1->ISTAT = USB_IS_USBRST;
+      usb_cb_device_reset ();
+      dev_p->reset++;
+    }
+  else if ((istat_value & USB_IS_ERROR))
+    { /* Clear Errors.  */
+      USB_CTRL1->ERRSTAT = USB_CTRL1->ERRSTAT;
+      USB_CTRL1->ISTAT = USB_IS_ERROR;
+      /*reset???*/
+      dev_p->error++;
+    }
+  else if ((istat_value & USB_IS_TOKDNE))
+    {
+      handle_transaction (stat);
+      dev_p->tkdone++;
+    }
+  else if ((istat_value & USB_IS_STALL))
+    {
+      /* ??? stat includes ep_num in this case ???: No, it doesn't  */
+
+      if (kl27z_ep_is_stall (0))
+	{ /* It's endpoint 0, recover from erorr.  */
+	  dev_p->state = WAIT_SETUP;
+	  kl27z_ep_clear_stall (0);
+	  kl27z_prepare_ep0_setup ();
+	}
+
+      USB_CTRL1->ISTAT = USB_IS_STALL;
+      dev_p->stall++;
+    }
+}
+
+#define DATA0 0
+#define DATA1 1
+
+static void
+handle_datastage_out (uint8_t stat)
+{
+  int odd = (stat >> 2)&1;
+  int data01 = !((BD_table[odd].ctrl >> 6)&1);
+  uint32_t len = (BD_table[odd].ctrl >> 16)&0x3ff;
+
+  data_p->len -= len;
+  data_p->addr += len;
+
+  len = data_p->len;
+  if (len > USB_MAX_PACKET_SIZE)
+    len = USB_MAX_PACKET_SIZE;
+
+  if (data_p->len == 0)
+    {
+      /* No more data to receive, proceed to send acknowledge for IN.  */
+      dev_p->state = WAIT_STATUS_IN;
+      kl27z_prepare_ep0_in (setup, 0, DATA1);
+    }
+  else
+    {
+      dev_p->state = OUT_DATA;
+      kl27z_prepare_ep0_out (data_p->addr, len, data01);
+    }
+}
+
+static void
+handle_datastage_in (uint8_t stat)
+{
+  int odd = (stat >> 2)&1;
+  int data01 = !((BD_table[2+odd].ctrl >> 6)&1);
+  uint32_t len = USB_MAX_PACKET_SIZE;
+
+  if ((data_p->len == 0) && (dev_p->state == LAST_IN_DATA))
+    {
+      if (data_p->require_zlp)
+	{
+	  data_p->require_zlp = 0;
+
+	  /* No more data to send.  Send empty packet */
+	  kl27z_prepare_ep0_in (setup, 0, data01);
+	}
+      else
+	{
+	  /* No more data to send, proceed to receive OUT acknowledge.  */
+	  dev_p->state = WAIT_STATUS_OUT;
+	  kl27z_prepare_ep0_out (setup, 8, DATA1);
+	}
+
+      return;
+    }
+
+  dev_p->state = (data_p->len <= len) ? LAST_IN_DATA : IN_DATA;
+
+  if (len > data_p->len)
+    len = data_p->len;
+
+  kl27z_prepare_ep0_in (data_p->addr, len, data01);
+  data_p->len -= len;
+  data_p->addr += len;
+}
+
+typedef int (*HANDLER) (uint8_t req, struct req_args *arg);
+
+static int
+std_none (uint8_t req, struct req_args *arg)
+{
+  (void)req; (void)arg;
+  return USB_UNSUPPORT;
+}
+
+static int
+std_get_status (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+  uint16_t status_info = 0;
+
+  if (arg->value != 0 || arg->len != 2 || (arg->index >> 8) != 0
+      || USB_SETUP_SET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == DEVICE_RECIPIENT)
+    {
+      if (arg->index == 0)
+	{
+	  /* Get Device Status */
+	  uint8_t feature = dev_p->feature;
+
+	  /* Remote Wakeup enabled */
+	  if ((feature & (1 << 5)))
+	    status_info |= 2;
+	  else
+	    status_info &= ~2;
+
+	  /* Bus-powered */
+	  if ((feature & (1 << 6)))
+	    status_info |= 1;
+	  else /* Self-powered */
+	    status_info &= ~1;
+
+	  return usb_lld_reply_request (&status_info, 2, arg);
+	}
+    }
+  else if (rcp == INTERFACE_RECIPIENT)
+    {
+      int r;
+
+      if (dev_p->configuration == 0)
+	return USB_UNSUPPORT;
+
+      r = usb_cb_interface (USB_QUERY_INTERFACE, arg);
+      if (r != USB_SUCCESS)
+	return USB_UNSUPPORT;
+
+      return usb_lld_reply_request (&status_info, 2, arg);
+    }
+  else if (rcp == ENDPOINT_RECIPIENT)
+    {
+      uint8_t n = (arg->index & 0x0f);
+
+      if ((arg->index & 0x70) || n == ENDP0)
+	return USB_UNSUPPORT;
+
+      if (kl27z_ep_is_disabled (n))
+	return USB_UNSUPPORT;
+
+      status_info = kl27z_ep_is_stall (n);
+      return usb_lld_reply_request (&status_info, 2, arg);
+    }
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_clear_feature (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_GET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == DEVICE_RECIPIENT)
+    {
+      if (arg->len != 0 || arg->index != 0)
+	return USB_UNSUPPORT;
+
+      if (arg->value == DEVICE_REMOTE_WAKEUP)
+	{
+	  dev_p->feature &= ~(1 << 5);
+	  return USB_SUCCESS;
+	}
+    }
+  else if (rcp == ENDPOINT_RECIPIENT)
+    {
+      uint8_t n = (arg->index & 0x0f);
+
+      if (dev_p->configuration == 0)
+	return USB_UNSUPPORT;
+
+      if (arg->len != 0 || (arg->index >> 8) != 0
+	  || arg->value != ENDPOINT_STALL || n == ENDP0)
+	return USB_UNSUPPORT;
+
+      if (kl27z_ep_is_disabled (n))
+	return USB_UNSUPPORT;
+
+      kl27z_ep_clear_dtog ((arg->index & 0x80) == 0, n);
+
+      // event??
+      return USB_SUCCESS;
+    }
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_set_feature (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_GET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == DEVICE_RECIPIENT)
+    {
+      if (arg->len != 0 || arg->index != 0)
+	return USB_UNSUPPORT;
+
+      if (arg->value == DEVICE_REMOTE_WAKEUP)
+	{
+	  dev_p->feature |= 1 << 5;
+	  // event??
+	  return USB_SUCCESS;
+	}
+    }
+  else if (rcp == ENDPOINT_RECIPIENT)
+    {
+      uint8_t n = (arg->index & 0x0f);
+
+      if (dev_p->configuration == 0)
+	return USB_UNSUPPORT;
+
+      if (arg->len != 0 || (arg->index >> 8) != 0
+	  || arg->value != 0 || n == ENDP0)
+	return USB_UNSUPPORT;
+
+      if (kl27z_ep_is_disabled (n))
+	return USB_UNSUPPORT;
+
+      kl27z_ep_stall (n);
+
+      // event??
+      return USB_SUCCESS;
+    }
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_set_address (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_GET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == DEVICE_RECIPIENT && arg->len == 0 && arg->value <= 127
+      && arg->index == 0 && dev_p->configuration == 0)
+    return USB_SUCCESS;
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_get_descriptor (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_SET (req))
+    return USB_UNSUPPORT;
+
+  return usb_cb_get_descriptor (rcp, (arg->value >> 8),
+				(arg->value & 0xff), arg);
+}
+
+static int
+std_get_configuration (uint8_t req,  struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  (void)arg;
+  if (USB_SETUP_SET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == DEVICE_RECIPIENT)
+    return usb_lld_reply_request (&dev_p->configuration, 1, arg);
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_set_configuration (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_GET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == DEVICE_RECIPIENT && arg->index == 0 && arg->len == 0)
+    return usb_cb_handle_event (USB_EVENT_CONFIG, arg->value);
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_get_interface (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_SET (req))
+    return USB_UNSUPPORT;
+
+  if (rcp == INTERFACE_RECIPIENT)
+    {
+      if (arg->value != 0 || (arg->index >> 8) != 0 || arg->len != 1)
+	return USB_UNSUPPORT;
+
+      if (dev_p->configuration == 0)
+	return USB_UNSUPPORT;
+
+      return usb_cb_interface (USB_GET_INTERFACE, arg);
+    }
+
+  return USB_UNSUPPORT;
+}
+
+static int
+std_set_interface (uint8_t req, struct req_args *arg)
+{
+  uint8_t rcp = req & RECIPIENT;
+
+  if (USB_SETUP_GET (req) || rcp != INTERFACE_RECIPIENT
+      || arg->len != 0 || (arg->index >> 8) != 0
+      || (arg->value >> 8) != 0 || dev_p->configuration == 0)
+    return USB_UNSUPPORT;
+
+  return usb_cb_interface (USB_SET_INTERFACE, arg);
+}
+
+
+static void
+handle_setup0 (void)
+{
+  struct req_args *arg = (struct req_args *)&setup[2];
+  int r = USB_UNSUPPORT;
+  HANDLER handler;
+
+  data_p->addr = NULL;
+  data_p->len = 0;
+  data_p->require_zlp = 0;
+
+  if ((setup[0] & REQUEST_TYPE) == STANDARD_REQUEST)
+    {
+      if (setup[1] < TOTAL_REQUEST)
+	{
+	  switch (setup[1])
+	    {
+	    case 0: handler = std_get_status;  break;
+	    case 1: handler = std_clear_feature;  break;
+	    case 3: handler = std_set_feature;  break;
+	    case 5: handler = std_set_address;  break;
+	    case 6: handler = std_get_descriptor;  break;
+	    case 8: handler = std_get_configuration;  break;
+	    case 9: handler = std_set_configuration;  break;
+	    case 10: handler = std_get_interface;  break;
+	    case 11: handler = std_set_interface;  break;
+	    default: handler = std_none;  break;
+	    }
+
+	  r = (*handler) (setup[0], arg);
+	}
+    }
+  else
+    r = usb_cb_setup (setup[0], setup[1], arg);
+
+  if (r != USB_SUCCESS)
+    dev_p->state = STALLED;
+  else if (USB_SETUP_SET (setup[0]))
+    {
+      if (arg->len == 0)
+	{
+	  /* Zero length packet for ACK.  */
+	  kl27z_prepare_ep0_in (setup, 0, DATA1);
+	  dev_p->state = WAIT_STATUS_IN;
+	}
+    }
+}
+
+static void
+handle_in0 (uint8_t stat)
+{
+  if (dev_p->state == IN_DATA || dev_p->state == LAST_IN_DATA)
+    handle_datastage_in (stat);
+  else if (dev_p->state == WAIT_STATUS_IN)
+    { /* Control WRITE transfer done successfully.  */
+      uint16_t value = (setup[3]<<8) | setup[2];
+
+      if ((setup[1] == SET_ADDRESS) &&
+	  ((setup[0] & (REQUEST_TYPE | RECIPIENT))
+	   == (STANDARD_REQUEST | DEVICE_RECIPIENT)))
+	{
+	  kl27z_set_daddr (value);
+	  usb_cb_handle_event (USB_EVENT_ADDRESS, value);
+	  ep[0].rx_odd = 0;
+	}
+      else
+	usb_cb_ctrl_write_finish  (setup[0], setup[1],
+				   (struct req_args *)&setup[2]);
+
+      dev_p->state = WAIT_SETUP;
+      kl27z_prepare_ep0_setup ();
+    }
+  else
+    dev_p->state = STALLED;
+}
+
+static void
+handle_out0 (uint8_t stat)
+{
+  if (dev_p->state == IN_DATA || dev_p->state == LAST_IN_DATA)
+    /* Host aborts the control READ transfer before finish. */
+    dev_p->state = STALLED;
+  else if (dev_p->state == OUT_DATA)
+    /* It's normal control WRITE transfer.  */
+    handle_datastage_out (stat);
+  else if (dev_p->state == WAIT_STATUS_OUT)
+    { /* Control READ transfer done successfully.  */
+      dev_p->state = WAIT_SETUP;
+      kl27z_prepare_ep0_setup ();
+    }
+  else
+    dev_p->state = STALLED;
+}
+
+static void
+handle_transaction (uint8_t stat)
+{
+  int odd = (stat >> 2)&1;
+  uint8_t ep_num = (stat >> 4);
+
+  if (ep_num == 0)
+    {
+      if ((stat & 0x08) == 0)
+	{
+	  ep[0].rx_odd ^= 1;
+	  if (TOK_PID (BD_table[odd].ctrl) == 0x0d)
+	    {
+	      handle_setup0 ();
+	      USB_CTRL1->ISTAT = USB_IS_TOKDNE;
+	      USB_CTRL1->CTL = 0x01; /* Clear TXSUSPENDTOKENBUSY.  */
+	    }
+	  else
+	    {
+	      USB_CTRL1->ISTAT = USB_IS_TOKDNE;
+	      handle_out0 (stat);
+	    }
+	}
+      else
+	{
+	  ep[0].tx_odd ^= 1;
+	  USB_CTRL1->ISTAT = USB_IS_TOKDNE;
+	  handle_in0 (stat);
+	}
+
+      if (dev_p->state == STALLED)
+	kl27z_ep_stall (0);
+    }
+  else
+    {
+      if ((stat & 0x08) == 0)
+	{
+	  dev_p->recv++;
+	  usb_cb_rx_ready (ep_num);
+	  ep[ep_num].rx_odd ^= 1;
+	}
+      else
+	{
+	  /* XXX: Can be NAK.  Check BDT if it's NAK or not.  */
+
+	  dev_p->send++;
+	  ep[ep_num].tx_odd ^= 1;
+	  usb_cb_tx_done (ep_num);
+	}
+
+      USB_CTRL1->ISTAT = USB_IS_TOKDNE;
+    }
+}
+
+void
+usb_lld_reset (uint8_t feature)
+{
+  dev_p->feature = feature;
+  usb_lld_set_configuration (0);
+
+  /* Reset USB */
+  USB_CTRL2->USBTRC0 = 0xc0;
+
+  USB_CTRL1->CTL = 0x00;    /* Disable USB FS communication module */
+
+  dev_p->state = WAIT_SETUP;
+  dev_p->tkdone = 0;
+  dev_p->error = 0;
+  dev_p->stall = 0;
+
+  kl27z_set_daddr (0);
+  kl27z_usb_init ();
+
+  /* Clear Interrupt Status Register, and enable interrupt for USB */
+  USB_CTRL1->ISTAT = 0xff;		     /* All clear */
+
+  USB_CTRL1->INTEN = USB_IS_STALL | USB_IS_TOKDNE
+                   | USB_IS_ERROR | USB_IS_USBRST;
+}
+
+void
+usb_lld_setup_endpoint (int n, int rx_en, int tx_en)
+{
+  if (n == 0)
+    {
+      /* Enable the endpoint 0.  */
+      USB_ENDPT[0].EP = 0x0d;
+      kl27z_prepare_ep0_setup ();
+    }
+  else
+    {
+      /* Enable the endpoint.  */
+      USB_ENDPT[n].EP = (rx_en << 3)|(tx_en << 2)|0x11;
+
+      /* Configure BDT entry so that it starts with DATA0.  */
+
+      /* RX */
+      BD_table[4*n+ep[n].rx_odd].ctrl = 0x0000;
+      BD_table[4*n+ep[n].rx_odd].buf = NULL;
+      BD_table[4*n+!ep[n].rx_odd].ctrl = 0x0040;
+      BD_table[4*n+!ep[n].rx_odd].buf = NULL;
+
+      /* TX */
+      BD_table[4*n+2+ep[n].tx_odd].ctrl = 0x0000;
+      BD_table[4*n+2+ep[n].tx_odd].buf = NULL;
+      BD_table[4*n+2+!ep[n].tx_odd].ctrl = 0x0040;
+      BD_table[4*n+2+!ep[n].tx_odd].buf = NULL;
+    }
+}
+
+
+void
+usb_lld_set_configuration (uint8_t config)
+{
+  dev_p->configuration = config;
+}
+
+uint8_t
+usb_lld_current_configuration (void)
+{
+  return dev_p->configuration;
+}
+
+void
+usb_lld_set_data_to_recv (void *p, size_t len)
+{
+  data_p->addr = (uint8_t *)p;
+  data_p->len = len;
+  if (len > USB_MAX_PACKET_SIZE)
+    len = USB_MAX_PACKET_SIZE;
+
+  kl27z_prepare_ep0_out (p, len, DATA1);
+  dev_p->state = OUT_DATA;
+}
+
+/*
+ * BUF: Pointer to data memory.  Data memory should not be allocated
+ *      on stack when BUFLEN > USB_MAX_PACKET_SIZE.
+ *
+ * BUFLEN: size of the data.
+ */
+int
+usb_lld_reply_request (const void *buf, size_t buflen, struct req_args *a)
+{
+  uint32_t len_asked = a->len;
+  uint32_t len;
+
+  data_p->addr = (void *)buf;
+  data_p->len = buflen;
+
+  /* Restrict the data length to be the one host asks for */
+  if (data_p->len > len_asked)
+    data_p->len = len_asked;
+
+  data_p->require_zlp = (data_p->len != 0
+			 && (data_p->len % USB_MAX_PACKET_SIZE) == 0);
+
+  if (data_p->len < USB_MAX_PACKET_SIZE)
+    {
+      len = data_p->len;
+      dev_p->state = LAST_IN_DATA;
+    }
+  else
+    {
+      len = USB_MAX_PACKET_SIZE;
+      dev_p->state = IN_DATA;
+    }
+
+  if (len)
+    kl27z_prepare_ep0_in (data_p->addr, len, DATA1);
+
+  data_p->len -= len;
+  data_p->addr += len;
+
+  return USB_SUCCESS;
+}
+
+void
+usb_lld_rx_enable (int n, void *buf, size_t len)
+{
+  int data01 = !((BD_table[4*n+!ep[n].rx_odd].ctrl >> 6)&1);
+
+  BD_table[4*n+ep[n].rx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6);
+  BD_table[4*n+ep[n].rx_odd].buf = buf;
+}
+
+int
+usb_lld_rx_data_len (int n)
+{
+  return (BD_table[4*n+!ep[n].rx_odd].ctrl >> 16)&0x3ff;
+}
+
+
+void
+usb_lld_tx_enable (uint8_t n, const void *buf, size_t len)
+{
+  int data01 = !((BD_table[4*n+2+!ep[n].tx_odd].ctrl >> 6)&1);
+
+  BD_table[4*n+2+ep[n].tx_odd].ctrl = (len << 16) | 0x0088 | (data01 << 6); 
+  BD_table[4*n+2+ep[n].tx_odd].buf = (void *)buf;
+}
+
+int
+usb_lld_tx_result (int ep_num)
+{
+  (void)ep_num;
+  return 0; /* XXX: return -1 when NAK */
+}
diff --git a/example-fs-bb48/usb_lld.h b/example-fs-bb48/usb_lld.h
new file mode 100644
index 0000000..da694a6
--- /dev/null
+++ b/example-fs-bb48/usb_lld.h
@@ -0,0 +1,115 @@
+#define STANDARD_ENDPOINT_DESC_SIZE             0x09
+
+/* endpoints enumeration */
+#define ENDP0       ((uint8_t)0)
+#define ENDP1       ((uint8_t)1)
+#define ENDP2       ((uint8_t)2)
+#define ENDP3       ((uint8_t)3)
+#define ENDP4       ((uint8_t)4)
+#define ENDP5       ((uint8_t)5)
+#define ENDP6       ((uint8_t)6)
+#define ENDP7       ((uint8_t)7)
+
+/* EP_TYPE[1:0] EndPoint TYPE */
+#define EP_BULK        (0x0000) /* EndPoint BULK        */
+#define EP_CONTROL     (0x0200) /* EndPoint CONTROL     */
+#define EP_ISOCHRONOUS (0x0400) /* EndPoint ISOCHRONOUS */
+#define EP_INTERRUPT   (0x0600) /* EndPoint INTERRUPT   */
+
+enum RECIPIENT_TYPE
+{
+  DEVICE_RECIPIENT,     /* Recipient device    */
+  INTERFACE_RECIPIENT,  /* Recipient interface */
+  ENDPOINT_RECIPIENT,   /* Recipient endpoint  */
+  OTHER_RECIPIENT
+};
+
+enum DESCRIPTOR_TYPE
+{
+  DEVICE_DESCRIPTOR = 1,
+  CONFIG_DESCRIPTOR,
+  STRING_DESCRIPTOR,
+  INTERFACE_DESCRIPTOR,
+  ENDPOINT_DESCRIPTOR
+};
+
+#define REQUEST_DIR       0x80  /* Mask to get request dir  */
+#define REQUEST_TYPE      0x60  /* Mask to get request type */
+#define STANDARD_REQUEST  0x00  /* Standard request         */
+#define CLASS_REQUEST     0x20  /* Class request            */
+#define VENDOR_REQUEST    0x40  /* Vendor request           */
+#define RECIPIENT         0x1F  /* Mask to get recipient    */
+
+#define USB_SETUP_SET(req) ((req & REQUEST_DIR) == 0)
+#define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0)
+
+enum
+{
+  USB_UNSUPPORT = 0,
+  USB_SUCCESS = 1,
+};
+
+struct req_args {
+  uint16_t value;
+  uint16_t index;
+  uint16_t len;
+};
+
+void usb_cb_device_reset (void);
+int usb_cb_setup (uint8_t req, uint8_t req_no, struct req_args *arg);
+int usb_cb_interface (uint8_t cmd, struct req_args *arg);
+int usb_cb_get_descriptor (uint8_t rcp, uint8_t desc_type, uint8_t desc_index,
+			   struct req_args *arg);
+int usb_cb_handle_event (uint8_t event_type, uint16_t value);
+void usb_cb_ctrl_write_finish (uint8_t req, uint8_t req_no,
+			       struct req_args *arg);
+void usb_cb_tx_done (uint8_t ep_num);
+void usb_cb_rx_ready (uint8_t ep_num);
+
+enum {
+  USB_EVENT_ADDRESS,
+  USB_EVENT_CONFIG,
+  USB_EVENT_SUSPEND,
+  USB_EVENT_WAKEUP,
+  USB_EVENT_STALL,
+};
+
+enum {
+  USB_SET_INTERFACE,
+  USB_GET_INTERFACE,
+  USB_QUERY_INTERFACE,
+};
+
+enum DEVICE_STATE
+{
+  UNCONNECTED,
+  ATTACHED,
+  POWERED,
+  SUSPENDED,
+  ADDRESSED,
+  CONFIGURED
+};
+
+void usb_lld_init (uint8_t feature);
+
+int usb_lld_reply_request (const void *buf, size_t buflen,
+			   struct req_args *arg);
+void usb_lld_set_data_to_recv (void *p, size_t len);
+
+void usb_lld_tx_enable (uint8_t ep_num, const void *buf, size_t len);
+int usb_lld_tx_result (int ep_num);
+
+void usb_lld_rx_enable (int ep_num, void *buf, size_t len);
+int usb_lld_rx_data_len (int ep_num);
+
+void usb_lld_stall (int ep_num);
+
+void usb_lld_reset (uint8_t feature);
+void usb_lld_setup_endpoint (int n, int rx_en, int tx_en);
+void usb_lld_set_configuration (uint8_t config);
+uint8_t usb_lld_current_configuration (void);
+
+void usb_lld_prepare_shutdown (void);
+void usb_lld_shutdown (void);
+
+void usb_interrupt_handler (void);
-- 
cgit v1.2.3