Copied from example-cde (except README).
This commit is contained in:
36
example-usb-serial/Makefile
Normal file
36
example-usb-serial/Makefile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Makefile for example application of Chopstx
|
||||||
|
|
||||||
|
PROJECT = sample
|
||||||
|
|
||||||
|
CHOPSTX = ..
|
||||||
|
LDSCRIPT= sample.ld
|
||||||
|
CSRC = sample.c usb-cdc.c
|
||||||
|
|
||||||
|
CHIP=stm32f103
|
||||||
|
|
||||||
|
USE_SYS = yes
|
||||||
|
USE_USB = yes
|
||||||
|
|
||||||
|
###################################
|
||||||
|
CROSS = arm-none-eabi-
|
||||||
|
CC = $(CROSS)gcc
|
||||||
|
LD = $(CROSS)gcc
|
||||||
|
OBJCOPY = $(CROSS)objcopy
|
||||||
|
|
||||||
|
MCU = cortex-m3
|
||||||
|
CWARN = -Wall -Wextra -Wstrict-prototypes
|
||||||
|
DEFS = -DUSE_SYS3 -DFREE_STANDING
|
||||||
|
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
|
||||||
|
|
||||||
|
sys.c: board.h
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
rm -f board.h
|
||||||
1
example-usb-serial/README
Normal file
1
example-usb-serial/README
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is an application example using ST Nucleo F103 board.
|
||||||
167
example-usb-serial/sample.c
Normal file
167
example-usb-serial/sample.c
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <chopstx.h>
|
||||||
|
|
||||||
|
#include "usb_lld.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
|
/* For set_led */
|
||||||
|
#include "board.h"
|
||||||
|
#include "sys.h"
|
||||||
|
|
||||||
|
static chopstx_mutex_t mtx;
|
||||||
|
static chopstx_cond_t cnd0;
|
||||||
|
static chopstx_cond_t cnd1;
|
||||||
|
|
||||||
|
static uint8_t u, v;
|
||||||
|
static uint8_t m; /* 0..100 */
|
||||||
|
|
||||||
|
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);
|
||||||
|
chopstx_usec_wait (m);
|
||||||
|
set_led (0);
|
||||||
|
chopstx_usec_wait (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;
|
||||||
|
chopstx_usec_wait (200*1000);
|
||||||
|
v = 1;
|
||||||
|
chopstx_usec_wait (200*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PRIO_PWM 3
|
||||||
|
#define PRIO_BLK 2
|
||||||
|
|
||||||
|
#define STACK_MAIN
|
||||||
|
#define STACK_PROCESS_1
|
||||||
|
#define STACK_PROCESS_2
|
||||||
|
#include "stack-def.h"
|
||||||
|
#define STACK_ADDR_PWM ((uint32_t)process1_base)
|
||||||
|
#define STACK_SIZE_PWM (sizeof process1_base)
|
||||||
|
#define STACK_ADDR_BLK ((uint32_t)process2_base)
|
||||||
|
#define STACK_SIZE_BLK (sizeof process2_base)
|
||||||
|
|
||||||
|
|
||||||
|
static char hexchar (uint8_t x)
|
||||||
|
{
|
||||||
|
x &= 0x0f;
|
||||||
|
if (x <= 0x09)
|
||||||
|
return '0' + x;
|
||||||
|
else if (x <= 0x0f)
|
||||||
|
return 'a' + x - 10;
|
||||||
|
else
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
struct tty *tty;
|
||||||
|
uint8_t count;
|
||||||
|
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
chopstx_mutex_init (&mtx);
|
||||||
|
chopstx_cond_init (&cnd0);
|
||||||
|
chopstx_cond_init (&cnd1);
|
||||||
|
|
||||||
|
m = 10;
|
||||||
|
|
||||||
|
chopstx_create (PRIO_PWM, STACK_ADDR_PWM, STACK_SIZE_PWM, pwm, NULL);
|
||||||
|
chopstx_create (PRIO_BLK, STACK_ADDR_BLK, STACK_SIZE_BLK, blk, NULL);
|
||||||
|
|
||||||
|
chopstx_usec_wait (200*1000);
|
||||||
|
|
||||||
|
chopstx_mutex_lock (&mtx);
|
||||||
|
chopstx_cond_signal (&cnd0);
|
||||||
|
chopstx_cond_signal (&cnd1);
|
||||||
|
chopstx_mutex_unlock (&mtx);
|
||||||
|
|
||||||
|
u = 1;
|
||||||
|
|
||||||
|
tty = tty_open ();
|
||||||
|
tty_wait_configured (tty);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
m = 50;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
char s[LINEBUFSIZE];
|
||||||
|
|
||||||
|
u = 1;
|
||||||
|
tty_wait_connection (tty);
|
||||||
|
|
||||||
|
chopstx_usec_wait (50*1000);
|
||||||
|
|
||||||
|
/* Send ZLP at the beginning. */
|
||||||
|
tty_send (tty, s, 0);
|
||||||
|
|
||||||
|
memcpy (s, "xx: Hello, World with Chopstx!\r\n", 32);
|
||||||
|
s[0] = hexchar (count >> 4);
|
||||||
|
s[1] = hexchar (count & 0x0f);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (tty_send (tty, s, 32) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
uint32_t usec;
|
||||||
|
|
||||||
|
usec = 3000000; /* 3.0 seconds */
|
||||||
|
size = tty_recv (tty, s + 4, &usec);
|
||||||
|
if (size < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
{
|
||||||
|
size--;
|
||||||
|
|
||||||
|
s[0] = hexchar (size >> 4);
|
||||||
|
s[1] = hexchar (size & 0x0f);
|
||||||
|
s[2] = ':';
|
||||||
|
s[3] = ' ';
|
||||||
|
s[size + 4] = '\r';
|
||||||
|
s[size + 5] = '\n';
|
||||||
|
if (tty_send (tty, s, size + 6) < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
124
example-usb-serial/sample.ld
Normal file
124
example-usb-serial/sample.ld
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* ST32F103 memory setup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
flash0 : org = 0x08000000, len = 4k
|
||||||
|
flash : org = 0x08000000+0x1000, len = 60k
|
||||||
|
ram : org = 0x20000000, len = 20k
|
||||||
|
}
|
||||||
|
|
||||||
|
__ram_start__ = ORIGIN(ram);
|
||||||
|
__ram_size__ = 20k;
|
||||||
|
__ram_end__ = __ram_start__ + __ram_size__;
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0;
|
||||||
|
|
||||||
|
.sys : ALIGN(4) SUBALIGN(4)
|
||||||
|
{
|
||||||
|
_sys = .;
|
||||||
|
KEEP(*(.vectors))
|
||||||
|
. = ALIGN(16);
|
||||||
|
KEEP(*(.sys.version))
|
||||||
|
KEEP(*(.sys.board_id))
|
||||||
|
KEEP(*(.sys.board_name))
|
||||||
|
build/sys-*.o(.text)
|
||||||
|
build/sys-*.o(.text.*)
|
||||||
|
build/sys-*.o(.rodata)
|
||||||
|
build/sys-*.o(.rodata.*)
|
||||||
|
. = ALIGN(1024);
|
||||||
|
*(.sys.0)
|
||||||
|
*(.sys.1)
|
||||||
|
*(.sys.2)
|
||||||
|
} > flash0
|
||||||
|
|
||||||
|
_text = .;
|
||||||
|
|
||||||
|
.startup : ALIGN(128) SUBALIGN(128)
|
||||||
|
{
|
||||||
|
KEEP(*(.startup.vectors))
|
||||||
|
. = ALIGN (16);
|
||||||
|
} > flash =0xffffffff
|
||||||
|
|
||||||
|
.text : ALIGN(16) SUBALIGN(16)
|
||||||
|
{
|
||||||
|
*(.text.startup.*)
|
||||||
|
*(.text)
|
||||||
|
*(.text.*)
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata.*)
|
||||||
|
*(.glue_7t)
|
||||||
|
*(.glue_7)
|
||||||
|
*(.gcc*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
} > flash
|
||||||
|
|
||||||
|
.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 (NOLOAD) :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.process_stack.3)
|
||||||
|
*(.process_stack.2)
|
||||||
|
*(.process_stack.1)
|
||||||
|
*(.process_stack.0)
|
||||||
|
. = ALIGN(8);
|
||||||
|
} > ram
|
||||||
|
|
||||||
|
.main_stack (NOLOAD) :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
*(.main_stack)
|
||||||
|
. = ALIGN(8);
|
||||||
|
} > ram
|
||||||
|
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = 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__;
|
||||||
24
example-usb-serial/stack-def.h
Normal file
24
example-usb-serial/stack-def.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#if defined(STACK_MAIN)
|
||||||
|
/* Idle+Exception handlers */
|
||||||
|
char __main_stack_end__[0] __attribute__ ((section(".main_stack")));
|
||||||
|
char main_base[0x0100] __attribute__ ((section(".main_stack")));
|
||||||
|
|
||||||
|
/* Main program */
|
||||||
|
char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0")));
|
||||||
|
char process0_base[0x0400] __attribute__ ((section(".process_stack.0")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* First thread program */
|
||||||
|
#if defined(STACK_PROCESS_1)
|
||||||
|
char process1_base[0x0200] __attribute__ ((section(".process_stack.1")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Second thread program */
|
||||||
|
#if defined(STACK_PROCESS_2)
|
||||||
|
char process2_base[0x0200] __attribute__ ((section(".process_stack.2")));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Third thread program */
|
||||||
|
#if defined(STACK_PROCESS_3)
|
||||||
|
char process3_base[0x0200] __attribute__ ((section(".process_stack.3")));
|
||||||
|
#endif
|
||||||
9
example-usb-serial/tty.h
Normal file
9
example-usb-serial/tty.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#define LINEBUFSIZE 128
|
||||||
|
|
||||||
|
struct tty;
|
||||||
|
|
||||||
|
struct tty *tty_open (void);
|
||||||
|
void tty_wait_configured (struct tty *tty);
|
||||||
|
void tty_wait_connection (struct tty *tty);
|
||||||
|
int tty_send (struct tty *tty, const char *buf, int count);
|
||||||
|
int tty_recv (struct tty *tty, char *buf, uint32_t *timeout);
|
||||||
958
example-usb-serial/usb-cdc.c
Normal file
958
example-usb-serial/usb-cdc.c
Normal file
@@ -0,0 +1,958 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <chopstx.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "usb_lld.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
|
static chopstx_intr_t usb_intr;
|
||||||
|
|
||||||
|
struct line_coding
|
||||||
|
{
|
||||||
|
uint32_t bitrate;
|
||||||
|
uint8_t format;
|
||||||
|
uint8_t paritytype;
|
||||||
|
uint8_t datatype;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static const struct line_coding line_coding0 = {
|
||||||
|
115200, /* baud rate: 115200 */
|
||||||
|
0x00, /* stop bits: 1 */
|
||||||
|
0x00, /* parity: none */
|
||||||
|
0x08 /* bits: 8 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Currently, we only support a single TTY.
|
||||||
|
*
|
||||||
|
* It is possible to extend to support multiple TTYs, for multiple
|
||||||
|
* interfaces.
|
||||||
|
*
|
||||||
|
* In that case, add argument to TTY_OPEN function and
|
||||||
|
* modify TTY_GET function to get the TTY structure. Functions which
|
||||||
|
* directy accesses TTY0 (usb_device_reset and usb_set_configuration)
|
||||||
|
* should be modified, too.
|
||||||
|
*
|
||||||
|
* Modification of TTY_MAIN thread will be also needed to echo back
|
||||||
|
* input for each TTY, and the thread should run if one of TTY is
|
||||||
|
* opened.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct tty {
|
||||||
|
chopstx_mutex_t mtx;
|
||||||
|
chopstx_cond_t cnd;
|
||||||
|
uint8_t inputline[LINEBUFSIZE]; /* Line editing is supported */
|
||||||
|
uint8_t send_buf[LINEBUFSIZE]; /* Sending ring buffer for echo back */
|
||||||
|
uint32_t inputline_len : 8;
|
||||||
|
uint32_t send_head : 8;
|
||||||
|
uint32_t send_tail : 8;
|
||||||
|
uint32_t flag_connected : 1;
|
||||||
|
uint32_t flag_send_ready : 1;
|
||||||
|
uint32_t flag_input_avail : 1;
|
||||||
|
uint32_t : 2;
|
||||||
|
uint32_t device_state : 3; /* USB device status */
|
||||||
|
struct line_coding line_coding;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct tty tty0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Locate TTY structure from interface number or endpoint number.
|
||||||
|
* Currently, it always returns tty0, because we only have the one.
|
||||||
|
*/
|
||||||
|
static struct tty *
|
||||||
|
tty_get (int interface, uint8_t ep_num)
|
||||||
|
{
|
||||||
|
struct tty *t = &tty0;
|
||||||
|
|
||||||
|
if (interface >= 0)
|
||||||
|
{
|
||||||
|
if (interface == 0)
|
||||||
|
t = &tty0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ep_num == ENDP1 || ep_num == ENDP2 || ep_num == ENDP3)
|
||||||
|
t = &tty0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define ENDP0_RXADDR (0x40)
|
||||||
|
#define ENDP0_TXADDR (0x80)
|
||||||
|
#define ENDP1_TXADDR (0xc0)
|
||||||
|
#define ENDP2_TXADDR (0x100)
|
||||||
|
#define ENDP3_RXADDR (0x140)
|
||||||
|
|
||||||
|
#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. */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define VCOM_FEATURE_BUS_POWERED 0x80
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
VCOM_FEATURE_BUS_POWERED, /* bmAttributes. */
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_device_reset (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
usb_lld_reset (dev, VCOM_FEATURE_BUS_POWERED);
|
||||||
|
|
||||||
|
/* Initialize Endpoint 0 */
|
||||||
|
usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, 64);
|
||||||
|
|
||||||
|
chopstx_mutex_lock (&tty0.mtx);
|
||||||
|
tty0.inputline_len = 0;
|
||||||
|
tty0.send_head = tty0.send_tail = 0;
|
||||||
|
tty0.flag_connected = 0;
|
||||||
|
tty0.flag_send_ready = 1;
|
||||||
|
tty0.flag_input_avail = 0;
|
||||||
|
tty0.device_state = USB_DEVICE_STATE_ATTACHED;
|
||||||
|
memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding));
|
||||||
|
chopstx_mutex_unlock (&tty0.mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define CDC_CTRL_DTR 0x0001
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_ctrl_write_finish (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
struct device_req *arg = &dev->dev_req;
|
||||||
|
uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT);
|
||||||
|
|
||||||
|
if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0
|
||||||
|
&& USB_SETUP_SET (arg->type)
|
||||||
|
&& arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
|
||||||
|
{
|
||||||
|
struct tty *t = tty_get (arg->index, 0);
|
||||||
|
|
||||||
|
/* Open/close the connection. */
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
t->flag_connected = ((arg->value & CDC_CTRL_DTR) != 0);
|
||||||
|
chopstx_cond_signal (&t->cnd);
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The transaction was already finished. So, it is no use to call
|
||||||
|
* usb_lld_ctrl_error when the condition does not match.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
vcom_port_data_setup (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
struct device_req *arg = &dev->dev_req;
|
||||||
|
|
||||||
|
if (USB_SETUP_GET (arg->type))
|
||||||
|
{
|
||||||
|
struct tty *t = tty_get (arg->index, 0);
|
||||||
|
|
||||||
|
if (arg->request == USB_CDC_REQ_GET_LINE_CODING)
|
||||||
|
return usb_lld_ctrl_send (dev, &t->line_coding,
|
||||||
|
sizeof (struct line_coding));
|
||||||
|
}
|
||||||
|
else /* USB_SETUP_SET (req) */
|
||||||
|
{
|
||||||
|
if (arg->request == USB_CDC_REQ_SET_LINE_CODING
|
||||||
|
&& arg->len == sizeof (struct line_coding))
|
||||||
|
{
|
||||||
|
struct tty *t = tty_get (arg->index, 0);
|
||||||
|
|
||||||
|
return usb_lld_ctrl_recv (dev, &t->line_coding,
|
||||||
|
sizeof (struct line_coding));
|
||||||
|
}
|
||||||
|
else if (arg->request == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
|
||||||
|
return usb_lld_ctrl_ack (dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
usb_setup (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
struct device_req *arg = &dev->dev_req;
|
||||||
|
uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT);
|
||||||
|
|
||||||
|
if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT) && arg->index == 0)
|
||||||
|
return vcom_port_data_setup (dev);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
usb_get_descriptor (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
struct device_req *arg = &dev->dev_req;
|
||||||
|
uint8_t rcp = arg->type & RECIPIENT;
|
||||||
|
uint8_t desc_type = (arg->value >> 8);
|
||||||
|
uint8_t desc_index = (arg->value & 0xff);
|
||||||
|
|
||||||
|
if (rcp != DEVICE_RECIPIENT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (desc_type == DEVICE_DESCRIPTOR)
|
||||||
|
return usb_lld_ctrl_send (dev,
|
||||||
|
vcom_device_desc, sizeof (vcom_device_desc));
|
||||||
|
else if (desc_type == CONFIG_DESCRIPTOR)
|
||||||
|
return usb_lld_ctrl_send (dev,
|
||||||
|
vcom_config_desc, sizeof (vcom_config_desc));
|
||||||
|
else if (desc_type == STRING_DESCRIPTOR)
|
||||||
|
{
|
||||||
|
const uint8_t *str;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
switch (desc_index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
str = vcom_string0;
|
||||||
|
size = sizeof (vcom_string0);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
str = 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 -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return usb_lld_ctrl_send (dev, str, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vcom_setup_endpoints_for_interface (uint16_t interface, int stop)
|
||||||
|
{
|
||||||
|
if (interface == 0)
|
||||||
|
{
|
||||||
|
if (!stop)
|
||||||
|
usb_lld_setup_endpoint (ENDP2, EP_INTERRUPT, 0, 0, ENDP2_TXADDR, 0);
|
||||||
|
else
|
||||||
|
usb_lld_stall_tx (ENDP2);
|
||||||
|
}
|
||||||
|
else if (interface == 1)
|
||||||
|
{
|
||||||
|
if (!stop)
|
||||||
|
{
|
||||||
|
usb_lld_setup_endpoint (ENDP1, EP_BULK, 0, 0, ENDP1_TXADDR, 0);
|
||||||
|
usb_lld_setup_endpoint (ENDP3, EP_BULK, 0, ENDP3_RXADDR, 0, 64);
|
||||||
|
/* Start with no data receiving (ENDP3 not enabled)*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usb_lld_stall_tx (ENDP1);
|
||||||
|
usb_lld_stall_rx (ENDP3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
usb_set_configuration (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint8_t current_conf;
|
||||||
|
|
||||||
|
current_conf = usb_lld_current_configuration (dev);
|
||||||
|
if (current_conf == 0)
|
||||||
|
{
|
||||||
|
if (dev->dev_req.value != 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
usb_lld_set_configuration (dev, 1);
|
||||||
|
for (i = 0; i < NUM_INTERFACES; i++)
|
||||||
|
vcom_setup_endpoints_for_interface (i, 0);
|
||||||
|
chopstx_mutex_lock (&tty0.mtx);
|
||||||
|
tty0.device_state = USB_DEVICE_STATE_CONFIGURED;
|
||||||
|
chopstx_cond_signal (&tty0.cnd);
|
||||||
|
chopstx_mutex_unlock (&tty0.mtx);
|
||||||
|
}
|
||||||
|
else if (current_conf != dev->dev_req.value)
|
||||||
|
{
|
||||||
|
if (dev->dev_req.value != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
usb_lld_set_configuration (dev, 0);
|
||||||
|
for (i = 0; i < NUM_INTERFACES; i++)
|
||||||
|
vcom_setup_endpoints_for_interface (i, 1);
|
||||||
|
chopstx_mutex_lock (&tty0.mtx);
|
||||||
|
tty0.device_state = USB_DEVICE_STATE_ADDRESSED;
|
||||||
|
chopstx_cond_signal (&tty0.cnd);
|
||||||
|
chopstx_mutex_unlock (&tty0.mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_lld_ctrl_ack (dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
usb_set_interface (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
uint16_t interface = dev->dev_req.index;
|
||||||
|
uint16_t alt = dev->dev_req.value;
|
||||||
|
|
||||||
|
if (interface >= NUM_INTERFACES)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (alt != 0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vcom_setup_endpoints_for_interface (interface, 0);
|
||||||
|
usb_lld_ctrl_ack (dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
usb_get_interface (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
const uint8_t zero = 0;
|
||||||
|
uint16_t interface = dev->dev_req.index;
|
||||||
|
|
||||||
|
if (interface >= NUM_INTERFACES)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* We don't have alternate interface, so, always return 0. */
|
||||||
|
return usb_lld_ctrl_send (dev, &zero, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
usb_get_status_interface (struct usb_dev *dev)
|
||||||
|
{
|
||||||
|
const uint16_t status_info = 0;
|
||||||
|
uint16_t interface = dev->dev_req.index;
|
||||||
|
|
||||||
|
if (interface >= NUM_INTERFACES)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return usb_lld_ctrl_send (dev, &status_info, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put a character into the ring buffer to be send back.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
put_char_to_ringbuffer (struct tty *t, int c)
|
||||||
|
{
|
||||||
|
uint32_t next = (t->send_tail + 1) % LINEBUFSIZE;
|
||||||
|
|
||||||
|
if (t->send_head == next)
|
||||||
|
/* full */
|
||||||
|
/* All that we can do is ignore this char. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
t->send_buf[t->send_tail] = c;
|
||||||
|
t->send_tail = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get characters from ring buffer into S.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_chars_from_ringbuffer (struct tty *t, uint8_t *s, int len)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (t->send_head == t->send_tail)
|
||||||
|
/* Empty */
|
||||||
|
return i;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
s[i++] = t->send_buf[t->send_head];
|
||||||
|
t->send_head = (t->send_head + 1) % LINEBUFSIZE;
|
||||||
|
}
|
||||||
|
while (t->send_head != t->send_tail && i < len);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
tty_echo_char (struct tty *t, int c)
|
||||||
|
{
|
||||||
|
put_char_to_ringbuffer (t, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_tx_done (uint8_t ep_num, uint16_t len)
|
||||||
|
{
|
||||||
|
struct tty *t = tty_get (-1, ep_num);
|
||||||
|
|
||||||
|
(void)len;
|
||||||
|
|
||||||
|
if (ep_num == ENDP1)
|
||||||
|
{
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
if (t->flag_send_ready == 0)
|
||||||
|
{
|
||||||
|
t->flag_send_ready = 1;
|
||||||
|
chopstx_cond_signal (&t->cnd);
|
||||||
|
}
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
}
|
||||||
|
else if (ep_num == ENDP2)
|
||||||
|
{
|
||||||
|
/* Nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
tty_input_char (struct tty *t, int c)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
/* Process DEL, C-U, C-R, and RET as editing command. */
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 0x0d: /* Control-M */
|
||||||
|
t->inputline[t->inputline_len++] = '\n';
|
||||||
|
tty_echo_char (t, 0x0d);
|
||||||
|
tty_echo_char (t, 0x0a);
|
||||||
|
t->flag_input_avail = 1;
|
||||||
|
r = 1;
|
||||||
|
chopstx_cond_signal (&t->cnd);
|
||||||
|
break;
|
||||||
|
case 0x12: /* Control-R */
|
||||||
|
tty_echo_char (t, '^');
|
||||||
|
tty_echo_char (t, 'R');
|
||||||
|
tty_echo_char (t, 0x0d);
|
||||||
|
tty_echo_char (t, 0x0a);
|
||||||
|
for (i = 0; i < t->inputline_len; i++)
|
||||||
|
tty_echo_char (t, t->inputline[i]);
|
||||||
|
break;
|
||||||
|
case 0x15: /* Control-U */
|
||||||
|
for (i = 0; i < t->inputline_len; i++)
|
||||||
|
{
|
||||||
|
tty_echo_char (t, 0x08);
|
||||||
|
tty_echo_char (t, 0x20);
|
||||||
|
tty_echo_char (t, 0x08);
|
||||||
|
}
|
||||||
|
t->inputline_len = 0;
|
||||||
|
break;
|
||||||
|
case 0x7f: /* DEL */
|
||||||
|
if (t->inputline_len > 0)
|
||||||
|
{
|
||||||
|
tty_echo_char (t, 0x08);
|
||||||
|
tty_echo_char (t, 0x20);
|
||||||
|
tty_echo_char (t, 0x08);
|
||||||
|
t->inputline_len--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (t->inputline_len < sizeof (t->inputline) - 1)
|
||||||
|
{
|
||||||
|
tty_echo_char (t, c);
|
||||||
|
t->inputline[t->inputline_len++] = c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Beep */
|
||||||
|
tty_echo_char (t, 0x0a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_rx_ready (uint8_t ep_num, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t recv_buf[64];
|
||||||
|
struct tty *t = tty_get (-1, ep_num);
|
||||||
|
|
||||||
|
if (ep_num == ENDP3)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
usb_lld_rxcpy (recv_buf, ep_num, 0, len);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (tty_input_char (t, recv_buf[i]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
if (t->flag_input_avail == 0)
|
||||||
|
usb_lld_rx_enable (ENDP3);
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *tty_main (void *arg);
|
||||||
|
|
||||||
|
#define PRIO_TTY 4
|
||||||
|
|
||||||
|
#define STACK_PROCESS_3
|
||||||
|
#include "stack-def.h"
|
||||||
|
#define STACK_ADDR_TTY ((uint32_t)process3_base)
|
||||||
|
#define STACK_SIZE_TTY (sizeof process3_base)
|
||||||
|
|
||||||
|
struct tty *
|
||||||
|
tty_open (void)
|
||||||
|
{
|
||||||
|
chopstx_mutex_init (&tty0.mtx);
|
||||||
|
chopstx_cond_init (&tty0.cnd);
|
||||||
|
tty0.inputline_len = 0;
|
||||||
|
tty0.send_head = tty0.send_tail = 0;
|
||||||
|
tty0.flag_connected = 0;
|
||||||
|
tty0.flag_send_ready = 1;
|
||||||
|
tty0.flag_input_avail = 0;
|
||||||
|
tty0.device_state = USB_DEVICE_STATE_UNCONNECTED;
|
||||||
|
memcpy (&tty0.line_coding, &line_coding0, sizeof (struct line_coding));
|
||||||
|
|
||||||
|
chopstx_create (PRIO_TTY, STACK_ADDR_TTY, STACK_SIZE_TTY, tty_main, &tty0);
|
||||||
|
return &tty0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *
|
||||||
|
tty_main (void *arg)
|
||||||
|
{
|
||||||
|
struct tty *t = arg;
|
||||||
|
struct usb_dev dev;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
#if defined(OLDER_SYS_H)
|
||||||
|
/*
|
||||||
|
* Historically (before sys < 3.0), NVIC priority setting for USB
|
||||||
|
* interrupt was done in usb_lld_sys_init. Thus this code.
|
||||||
|
*
|
||||||
|
* When USB interrupt occurs between usb_lld_init (which assumes
|
||||||
|
* ISR) and chopstx_claim_irq (which clears pending interrupt),
|
||||||
|
* invocation of usb_lld_event_handler won't occur.
|
||||||
|
*
|
||||||
|
* Calling usb_lld_event_handler is no harm even if there were no
|
||||||
|
* interrupts, thus, we call it unconditionally here, just in case
|
||||||
|
* if there is a request.
|
||||||
|
*
|
||||||
|
* We can't call usb_lld_init after chopstx_claim_irq, as
|
||||||
|
* usb_lld_init does its own setting for NVIC. Calling
|
||||||
|
* chopstx_claim_irq after usb_lld_init overrides that.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
|
||||||
|
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
|
||||||
|
goto event_handle;
|
||||||
|
#else
|
||||||
|
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
|
||||||
|
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
chopstx_intr_wait (&usb_intr);
|
||||||
|
if (usb_intr.ready)
|
||||||
|
{
|
||||||
|
uint8_t ep_num;
|
||||||
|
#if defined(OLDER_SYS_H)
|
||||||
|
event_handle:
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* When interrupt is detected, call usb_lld_event_handler.
|
||||||
|
* The event may be one of following:
|
||||||
|
* (1) Transfer to endpoint (bulk or interrupt)
|
||||||
|
* In this case EP_NUM is encoded in the variable E.
|
||||||
|
* (2) "NONE" event: some trasfer was done, but all was
|
||||||
|
* done by lower layer, no other work is needed in
|
||||||
|
* upper layer.
|
||||||
|
* (3) Device events: Reset or Suspend
|
||||||
|
* (4) Device requests to the endpoint zero.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
e = usb_lld_event_handler (&dev);
|
||||||
|
ep_num = USB_EVENT_ENDP (e);
|
||||||
|
|
||||||
|
if (ep_num != 0)
|
||||||
|
{
|
||||||
|
if (USB_EVENT_TXRX (e))
|
||||||
|
usb_tx_done (ep_num, USB_EVENT_LEN (e));
|
||||||
|
else
|
||||||
|
usb_rx_ready (ep_num, USB_EVENT_LEN (e));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
switch (USB_EVENT_ID (e))
|
||||||
|
{
|
||||||
|
case USB_EVENT_DEVICE_RESET:
|
||||||
|
usb_device_reset (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_DEVICE_ADDRESSED:
|
||||||
|
/* The addres is assigned to the device. We don't
|
||||||
|
* need to do anything for this actually, but in this
|
||||||
|
* application, we maintain the USB status of the
|
||||||
|
* device. Usually, just "continue" as EVENT_OK is
|
||||||
|
* OK.
|
||||||
|
*/
|
||||||
|
chopstx_mutex_lock (&tty0.mtx);
|
||||||
|
tty0.device_state = USB_DEVICE_STATE_ADDRESSED;
|
||||||
|
chopstx_cond_signal (&tty0.cnd);
|
||||||
|
chopstx_mutex_unlock (&tty0.mtx);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_GET_DESCRIPTOR:
|
||||||
|
if (usb_get_descriptor (&dev) < 0)
|
||||||
|
usb_lld_ctrl_error (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_SET_CONFIGURATION:
|
||||||
|
if (usb_set_configuration (&dev) < 0)
|
||||||
|
usb_lld_ctrl_error (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_SET_INTERFACE:
|
||||||
|
if (usb_set_interface (&dev) < 0)
|
||||||
|
usb_lld_ctrl_error (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_CTRL_REQUEST:
|
||||||
|
/* Device specific device request. */
|
||||||
|
if (usb_setup (&dev) < 0)
|
||||||
|
usb_lld_ctrl_error (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_GET_STATUS_INTERFACE:
|
||||||
|
if (usb_get_status_interface (&dev) < 0)
|
||||||
|
usb_lld_ctrl_error (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_GET_INTERFACE:
|
||||||
|
if (usb_get_interface (&dev) < 0)
|
||||||
|
usb_lld_ctrl_error (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_SET_FEATURE_DEVICE:
|
||||||
|
case USB_EVENT_SET_FEATURE_ENDPOINT:
|
||||||
|
case USB_EVENT_CLEAR_FEATURE_DEVICE:
|
||||||
|
case USB_EVENT_CLEAR_FEATURE_ENDPOINT:
|
||||||
|
usb_lld_ctrl_ack (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_CTRL_WRITE_FINISH:
|
||||||
|
/* Control WRITE transfer finished. */
|
||||||
|
usb_ctrl_write_finish (&dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case USB_EVENT_OK:
|
||||||
|
case USB_EVENT_DEVICE_SUSPEND:
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
if (t->device_state == USB_DEVICE_STATE_CONFIGURED && t->flag_connected
|
||||||
|
&& t->flag_send_ready)
|
||||||
|
{
|
||||||
|
uint8_t line[32];
|
||||||
|
int len = get_chars_from_ringbuffer (t, line, sizeof (len));
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
usb_lld_txcpy (line, ENDP1, 0, len);
|
||||||
|
usb_lld_tx_enable (ENDP1, len);
|
||||||
|
t->flag_send_ready = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
tty_wait_configured (struct tty *t)
|
||||||
|
{
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
while (t->device_state != USB_DEVICE_STATE_CONFIGURED)
|
||||||
|
chopstx_cond_wait (&t->cnd, &t->mtx);
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
tty_wait_connection (struct tty *t)
|
||||||
|
{
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
while (t->flag_connected == 0)
|
||||||
|
chopstx_cond_wait (&t->cnd, &t->mtx);
|
||||||
|
t->flag_send_ready = 1;
|
||||||
|
t->flag_input_avail = 0;
|
||||||
|
t->send_head = t->send_tail = 0;
|
||||||
|
t->inputline_len = 0;
|
||||||
|
usb_lld_rx_enable (ENDP3); /* Accept input for line */
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_tx (struct tty *t)
|
||||||
|
{
|
||||||
|
if (t->flag_send_ready)
|
||||||
|
/* TX done */
|
||||||
|
return 1;
|
||||||
|
if (t->flag_connected == 0)
|
||||||
|
/* Disconnected */
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
tty_send (struct tty *t, const char *buf, int len)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
const char *p;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
p = buf;
|
||||||
|
count = len >= 64 ? 64 : len;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
while ((r = check_tx (t)) == 0)
|
||||||
|
chopstx_cond_wait (&t->cnd, &t->mtx);
|
||||||
|
if (r > 0)
|
||||||
|
{
|
||||||
|
usb_lld_txcpy (p, ENDP1, 0, count);
|
||||||
|
usb_lld_tx_enable (ENDP1, count);
|
||||||
|
t->flag_send_ready = 0;
|
||||||
|
}
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
|
||||||
|
len -= count;
|
||||||
|
p += count;
|
||||||
|
if (len == 0 && count != 64)
|
||||||
|
/*
|
||||||
|
* The size of the last packet should be != 0
|
||||||
|
* If 64, send ZLP (zelo length packet)
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
count = len >= 64 ? 64 : len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_rx (void *arg)
|
||||||
|
{
|
||||||
|
struct tty *t = arg;
|
||||||
|
|
||||||
|
if (t->flag_input_avail)
|
||||||
|
/* RX */
|
||||||
|
return 1;
|
||||||
|
if (t->flag_connected == 0)
|
||||||
|
/* Disconnected */
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns -1 on connection close
|
||||||
|
* 0 on timeout.
|
||||||
|
* >0 length of the inputline (including final \n)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
tty_recv (struct tty *t, char *buf, uint32_t *timeout)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
chopstx_poll_cond_t poll_desc;
|
||||||
|
|
||||||
|
poll_desc.type = CHOPSTX_POLL_COND;
|
||||||
|
poll_desc.ready = 0;
|
||||||
|
poll_desc.cond = &t->cnd;
|
||||||
|
poll_desc.mutex = &t->mtx;
|
||||||
|
poll_desc.check = check_rx;
|
||||||
|
poll_desc.arg = t;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
struct chx_poll_head *pd_array[1] = {
|
||||||
|
(struct chx_poll_head *)&poll_desc
|
||||||
|
};
|
||||||
|
chopstx_poll (timeout, 1, pd_array);
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
r = check_rx (t);
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
if (r || (timeout != NULL && *timeout == 0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chopstx_mutex_lock (&t->mtx);
|
||||||
|
if (t->flag_connected == 0)
|
||||||
|
r = -1;
|
||||||
|
else if (t->flag_input_avail)
|
||||||
|
{
|
||||||
|
r = t->inputline_len;
|
||||||
|
memcpy (buf, t->inputline, r);
|
||||||
|
t->flag_input_avail = 0;
|
||||||
|
usb_lld_rx_enable (ENDP3);
|
||||||
|
t->inputline_len = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
r = 0;
|
||||||
|
chopstx_mutex_unlock (&t->mtx);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user