diff options
author | NIIBE Yutaka <gniibe@fsij.org> | 2016-04-18 11:46:14 +0900 |
---|---|---|
committer | NIIBE Yutaka <gniibe@fsij.org> | 2016-04-18 11:46:14 +0900 |
commit | 82749ab97af6ec1de605f0638d44999c3b50eadf (patch) | |
tree | 214b4b33294e68d8f1853553f93f74b232a961d5 /example-fs-bb48 | |
parent | 92e17d3bdf2371031967f949659edb91b72fb63f (diff) |
Add support for FS-BB48
Diffstat (limited to 'example-fs-bb48')
-rw-r--r-- | example-fs-bb48/Makefile | 31 | ||||
l--------- | example-fs-bb48/board.h | 1 | ||||
-rw-r--r-- | example-fs-bb48/reset.c | 114 | ||||
-rw-r--r-- | example-fs-bb48/sample.c | 228 | ||||
-rw-r--r-- | example-fs-bb48/sample.ld | 135 | ||||
-rw-r--r-- | example-fs-bb48/stream.h | 24 | ||||
-rw-r--r-- | example-fs-bb48/usb-cdc.c | 506 | ||||
-rw-r--r-- | example-fs-bb48/usb_kl27z.c | 1035 | ||||
-rw-r--r-- | example-fs-bb48/usb_lld.h | 115 |
9 files changed, 2189 insertions, 0 deletions
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); |