Compare commits
5 Commits
master
...
stack-debu
Author | SHA1 | Date | |
---|---|---|---|
a81c04e485 | |||
bb82d588fb | |||
bfaf3010e6 | |||
75ef884e4f | |||
b9479b95f0 |
13
Makefile
13
Makefile
|
@ -1,9 +1,9 @@
|
||||||
MCU = at90usb1287
|
MCU = atmega32u2
|
||||||
ARCH = AVR8
|
ARCH = AVR8
|
||||||
BOARD = USER
|
BOARD = MINIMUS
|
||||||
F_CPU = 8000000
|
F_CPU = 16000000
|
||||||
F_USB = 8000000
|
F_USB = 16000000
|
||||||
OPTIMIZATION = 3
|
OPTIMIZATION = s
|
||||||
TARGET = main
|
TARGET = main
|
||||||
LUFA_PATH = lufa/LUFA
|
LUFA_PATH = lufa/LUFA
|
||||||
CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -Iconfig/ -Wall
|
CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -Iconfig/ -Wall
|
||||||
|
@ -13,6 +13,7 @@ SRC = \
|
||||||
main.c \
|
main.c \
|
||||||
ftdi.c \
|
ftdi.c \
|
||||||
reset.c \
|
reset.c \
|
||||||
|
stack.c \
|
||||||
$(LUFA_SRC_USB)
|
$(LUFA_SRC_USB)
|
||||||
|
|
||||||
# Default target
|
# Default target
|
||||||
|
@ -25,7 +26,7 @@ DEVICE = /dev/serial/by-id/usb-LUFA_FT232_*
|
||||||
|
|
||||||
# Open terminal on USB port
|
# Open terminal on USB port
|
||||||
term:
|
term:
|
||||||
terminal.py $(DEVICE)
|
python terminal.py $(DEVICE)
|
||||||
.PHONY: term
|
.PHONY: term
|
||||||
|
|
||||||
# Include LUFA build script makefiles
|
# Include LUFA build script makefiles
|
||||||
|
|
267
ftdi.c
267
ftdi.c
|
@ -8,8 +8,8 @@
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
/* TX and RX queues */
|
/* TX and RX queues */
|
||||||
volatile static uint8_t ftdi_txqueue_buf[FTDI_TX_QUEUE_LEN];
|
static uint8_t ftdi_txqueue_buf[FTDI_TX_QUEUE_LEN];
|
||||||
volatile static uint8_t ftdi_rxqueue_buf[FTDI_RX_QUEUE_LEN];
|
static uint8_t ftdi_rxqueue_buf[FTDI_RX_QUEUE_LEN];
|
||||||
struct queue ftdi_txqueue =
|
struct queue ftdi_txqueue =
|
||||||
QUEUE_INITIALIZER(ftdi_txqueue_buf, sizeof(ftdi_txqueue_buf));
|
QUEUE_INITIALIZER(ftdi_txqueue_buf, sizeof(ftdi_txqueue_buf));
|
||||||
struct queue ftdi_rxqueue =
|
struct queue ftdi_rxqueue =
|
||||||
|
@ -35,7 +35,7 @@ static const uint8_t ftdi_eeprom[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xa5
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0xa5
|
||||||
};
|
};
|
||||||
|
|
||||||
volatile static struct ftdi_status {
|
static struct ftdi_status {
|
||||||
/* modem status */
|
/* modem status */
|
||||||
unsigned reserved_1:1;
|
unsigned reserved_1:1;
|
||||||
unsigned reserved_0:3;
|
unsigned reserved_0:3;
|
||||||
|
@ -55,28 +55,13 @@ volatile static struct ftdi_status {
|
||||||
unsigned fifoerr:1;
|
unsigned fifoerr:1;
|
||||||
} ftdi_status = { 0 };
|
} ftdi_status = { 0 };
|
||||||
|
|
||||||
volatile static struct ctrl_status {
|
static struct ctrl_status {
|
||||||
unsigned dtr:1;
|
unsigned dtr:1;
|
||||||
unsigned rts:1;
|
unsigned rts:1;
|
||||||
} ctrl_status = { 0 };
|
} ctrl_status = { 0 };
|
||||||
|
|
||||||
static bool ftdi_onlcr = false; /* Translate \n to \r\n, like stty "onlcr" */
|
|
||||||
static bool ftdi_blocking_in = false;
|
|
||||||
static bool ftdi_blocking_out = false;
|
|
||||||
static bool ftdi_delay_init_tx = false;
|
|
||||||
|
|
||||||
volatile bool got_setbaudrate;
|
|
||||||
volatile bool got_first_rx;
|
|
||||||
#define WAIT_FRAMES 250
|
|
||||||
volatile uint8_t frames_since_setbaudrate;
|
|
||||||
|
|
||||||
volatile uint8_t latency_timer = 16;
|
|
||||||
volatile uint8_t latency_count = 0;
|
|
||||||
|
|
||||||
/* Fdev compatible wrappers */
|
/* Fdev compatible wrappers */
|
||||||
static int ftdi_fdev_write(char c, FILE *stream) {
|
static int ftdi_fdev_write(char c, FILE *stream) {
|
||||||
if (ftdi_onlcr && c == '\n')
|
|
||||||
ftdi_putchar('\r');
|
|
||||||
ftdi_putchar(c);
|
ftdi_putchar(c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -87,38 +72,22 @@ static FILE _ftdi_stream =
|
||||||
FDEV_SETUP_STREAM(ftdi_fdev_write, ftdi_fdev_read, _FDEV_SETUP_RW);
|
FDEV_SETUP_STREAM(ftdi_fdev_write, ftdi_fdev_read, _FDEV_SETUP_RW);
|
||||||
FILE *ftdi_stream = &_ftdi_stream;
|
FILE *ftdi_stream = &_ftdi_stream;
|
||||||
|
|
||||||
static void reset_internal_state(void)
|
/* Init */
|
||||||
|
static bool ftdi_blocking_in = false;
|
||||||
|
static bool ftdi_blocking_out = false;
|
||||||
|
void ftdi_init(int flags)
|
||||||
{
|
{
|
||||||
/* Disable endpoint interrupts */
|
|
||||||
UEIENX &= ~(1 << RXOUTE);
|
|
||||||
UEIENX &= ~(1 << TXINE);
|
|
||||||
|
|
||||||
/* Reset flags for ftdi_init_track_tx */
|
|
||||||
got_setbaudrate = false;
|
|
||||||
got_first_rx = false;
|
|
||||||
frames_since_setbaudrate = 0;
|
|
||||||
|
|
||||||
/* Latency timer */
|
|
||||||
latency_timer = 16;
|
|
||||||
latency_count = 0;
|
|
||||||
|
|
||||||
/* Reset line state */
|
/* Reset line state */
|
||||||
ftdi_status = (struct ftdi_status) { 0 };
|
memset(&ftdi_status, 0, sizeof(struct ftdi_status));
|
||||||
ftdi_status.reserved_1 = 1;
|
ftdi_status.reserved_1 = 1;
|
||||||
ftdi_status.cts = 1;
|
ftdi_status.cts = 1;
|
||||||
ftdi_status.dsr = 1;
|
ftdi_status.dsr = 1;
|
||||||
ftdi_status.ri = 0;
|
ftdi_status.ri = 0;
|
||||||
ftdi_status.cd = 1;
|
ftdi_status.cd = 1;
|
||||||
|
|
||||||
ctrl_status = (struct ctrl_status) { 0 };
|
memset(&ctrl_status, 0, sizeof(struct ctrl_status));
|
||||||
ctrl_status.dtr = 0;
|
ctrl_status.dtr = 0;
|
||||||
ctrl_status.rts = 0;
|
ctrl_status.rts = 0;
|
||||||
}
|
|
||||||
|
|
||||||
/* Init */
|
|
||||||
void ftdi_init(int flags)
|
|
||||||
{
|
|
||||||
reset_internal_state();
|
|
||||||
|
|
||||||
if (flags & FTDI_STDIO) {
|
if (flags & FTDI_STDIO) {
|
||||||
stdout = ftdi_stream;
|
stdout = ftdi_stream;
|
||||||
|
@ -129,17 +98,11 @@ void ftdi_init(int flags)
|
||||||
ftdi_blocking_in = true;
|
ftdi_blocking_in = true;
|
||||||
if (flags & FTDI_BLOCKING_OUT)
|
if (flags & FTDI_BLOCKING_OUT)
|
||||||
ftdi_blocking_out = true;
|
ftdi_blocking_out = true;
|
||||||
if (flags & FTDI_ONLCR)
|
|
||||||
ftdi_onlcr = true;
|
|
||||||
if (flags & FTDI_DELAY_INIT_TX)
|
|
||||||
ftdi_delay_init_tx = true;
|
|
||||||
|
|
||||||
USB_Device_EnableSOFEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ftdi_control_request(void)
|
static inline void ftdi_control_request(void)
|
||||||
{
|
{
|
||||||
uint16_t val;
|
uint16_t x;
|
||||||
|
|
||||||
if (!Endpoint_IsSETUPReceived())
|
if (!Endpoint_IsSETUPReceived())
|
||||||
return;
|
return;
|
||||||
|
@ -151,68 +114,50 @@ static inline void ftdi_control_request(void)
|
||||||
switch (USB_ControlRequest.bRequest) {
|
switch (USB_ControlRequest.bRequest) {
|
||||||
|
|
||||||
case SIO_RESET_REQUEST:
|
case SIO_RESET_REQUEST:
|
||||||
|
/* Reset */
|
||||||
switch (USB_ControlRequest.wValue) {
|
switch (USB_ControlRequest.wValue) {
|
||||||
case SIO_RESET_SIO:
|
case SIO_RESET_SIO:
|
||||||
ctrl_status.dtr = 0;
|
ftdi_init(0);
|
||||||
ctrl_status.rts = 0;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIO_RESET_PURGE_RX:
|
case SIO_RESET_PURGE_RX:
|
||||||
while (_queue_can_get(&ftdi_rxqueue))
|
while (_queue_can_get(&ftdi_rxqueue))
|
||||||
(void)_queue_get(&ftdi_rxqueue);
|
(void)_queue_get(&ftdi_rxqueue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIO_RESET_PURGE_TX:
|
case SIO_RESET_PURGE_TX:
|
||||||
while (_queue_can_get(&ftdi_txqueue))
|
while (_queue_can_get(&ftdi_txqueue))
|
||||||
(void)_queue_get(&ftdi_txqueue);
|
(void)_queue_get(&ftdi_txqueue);
|
||||||
ftdi_status.temt = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Endpoint_ClearSETUP();
|
Endpoint_ClearSETUP();
|
||||||
Endpoint_ClearStatusStage();
|
Endpoint_ClearStatusStage();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case SIO_READ_EEPROM_REQUEST:
|
case SIO_READ_EEPROM_REQUEST:
|
||||||
/* Return fake EEPROM data */
|
/* Return fake EEPROM data */
|
||||||
val = (USB_ControlRequest.wIndex & 0xff) * 2;
|
x = (USB_ControlRequest.wIndex & 0xff) * 2;
|
||||||
if (val + USB_ControlRequest.wLength > sizeof(ftdi_eeprom))
|
if (x + USB_ControlRequest.wLength > sizeof(ftdi_eeprom))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Endpoint_ClearSETUP();
|
Endpoint_ClearSETUP();
|
||||||
Endpoint_Write_Control_Stream_LE((uint8_t *)&ftdi_eeprom[val],
|
Endpoint_Write_Control_Stream_LE((uint8_t *)&ftdi_eeprom[x],
|
||||||
USB_ControlRequest.wLength);
|
USB_ControlRequest.wLength);
|
||||||
Endpoint_ClearStatusStage();
|
Endpoint_ClearStatusStage();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case SIO_SET_BAUDRATE_REQUEST:
|
|
||||||
got_setbaudrate = true;
|
|
||||||
Endpoint_ClearSETUP();
|
|
||||||
Endpoint_ClearStatusStage();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case SIO_WRITE_EEPROM_REQUEST:
|
case SIO_WRITE_EEPROM_REQUEST:
|
||||||
case SIO_ERASE_EEPROM_REQUEST:
|
case SIO_ERASE_EEPROM_REQUEST:
|
||||||
|
case SIO_SET_BAUDRATE_REQUEST:
|
||||||
case SIO_SET_DATA_REQUEST:
|
case SIO_SET_DATA_REQUEST:
|
||||||
case SIO_SET_FLOW_CTRL_REQUEST:
|
case SIO_SET_FLOW_CTRL_REQUEST:
|
||||||
case SIO_SET_EVENT_CHAR_REQUEST:
|
case SIO_SET_EVENT_CHAR_REQUEST:
|
||||||
case SIO_SET_ERROR_CHAR_REQUEST:
|
case SIO_SET_ERROR_CHAR_REQUEST:
|
||||||
|
case SIO_SET_LATENCY_TIMER_REQUEST:
|
||||||
case SIO_SET_BITMODE_REQUEST:
|
case SIO_SET_BITMODE_REQUEST:
|
||||||
/* Ignore these */
|
/* Ignore these */
|
||||||
Endpoint_ClearSETUP();
|
Endpoint_ClearSETUP();
|
||||||
Endpoint_ClearStatusStage();
|
Endpoint_ClearStatusStage();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case SIO_SET_LATENCY_TIMER_REQUEST:
|
|
||||||
val = USB_ControlRequest.wValue;
|
|
||||||
if (val >= 1 && val <= 255) {
|
|
||||||
latency_timer = val;
|
|
||||||
latency_count = 0;
|
|
||||||
}
|
|
||||||
Endpoint_ClearSETUP();
|
|
||||||
Endpoint_ClearStatusStage();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case SIO_SET_MODEM_CTRL_REQUEST:
|
case SIO_SET_MODEM_CTRL_REQUEST:
|
||||||
if (USB_ControlRequest.wValue & SIO_SET_DTR_HIGH)
|
if (USB_ControlRequest.wValue & SIO_SET_DTR_HIGH)
|
||||||
ctrl_status.dtr = 1;
|
ctrl_status.dtr = 1;
|
||||||
|
@ -227,11 +172,12 @@ static inline void ftdi_control_request(void)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case SIO_GET_LATENCY_TIMER_REQUEST:
|
case SIO_GET_LATENCY_TIMER_REQUEST:
|
||||||
|
/* Return dummy value */
|
||||||
if (USB_ControlRequest.wLength != 1)
|
if (USB_ControlRequest.wLength != 1)
|
||||||
return;
|
return;
|
||||||
Endpoint_ClearSETUP();
|
Endpoint_ClearSETUP();
|
||||||
val = latency_timer;
|
x = 100;
|
||||||
Endpoint_Write_Control_Stream_LE((uint8_t *)&val, 1);
|
Endpoint_Write_Control_Stream_LE((uint8_t *)&x, 1);
|
||||||
Endpoint_ClearStatusStage();
|
Endpoint_ClearStatusStage();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -240,8 +186,8 @@ static inline void ftdi_control_request(void)
|
||||||
if (USB_ControlRequest.wLength != 1)
|
if (USB_ControlRequest.wLength != 1)
|
||||||
return;
|
return;
|
||||||
Endpoint_ClearSETUP();
|
Endpoint_ClearSETUP();
|
||||||
val = 0;
|
x = 0;
|
||||||
Endpoint_Write_Control_Stream_LE((uint8_t *)&val, 1);
|
Endpoint_Write_Control_Stream_LE((uint8_t *)&x, 1);
|
||||||
Endpoint_ClearStatusStage();
|
Endpoint_ClearStatusStage();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -264,7 +210,7 @@ static const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
|
||||||
.Type = DTYPE_Device
|
.Type = DTYPE_Device
|
||||||
},
|
},
|
||||||
|
|
||||||
.USBSpecification = VERSION_BCD(2,0,0),
|
.USBSpecification = VERSION_BCD(02.00),
|
||||||
.Class = 0x00,
|
.Class = 0x00,
|
||||||
.SubClass = 0x00,
|
.SubClass = 0x00,
|
||||||
.Protocol = 0x00,
|
.Protocol = 0x00,
|
||||||
|
@ -359,7 +305,7 @@ static const USB_Descriptor_String_t *strings[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
|
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
|
||||||
const uint16_t wIndex,
|
const uint8_t wIndex,
|
||||||
const void** const DescriptorAddress)
|
const void** const DescriptorAddress)
|
||||||
{
|
{
|
||||||
const uint8_t DescriptorType = (wValue >> 8);
|
const uint8_t DescriptorType = (wValue >> 8);
|
||||||
|
@ -403,13 +349,6 @@ void EVENT_USB_Device_Reset(void)
|
||||||
{
|
{
|
||||||
Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP);
|
Endpoint_SelectEndpoint(ENDPOINT_CONTROLEP);
|
||||||
USB_INT_Enable(USB_INT_RXSTPI);
|
USB_INT_Enable(USB_INT_RXSTPI);
|
||||||
|
|
||||||
reset_internal_state();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EVENT_USB_Device_Disconnect(void)
|
|
||||||
{
|
|
||||||
reset_internal_state();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Configure endpoint with the best available number of banks. */
|
/* Configure endpoint with the best available number of banks. */
|
||||||
|
@ -422,86 +361,26 @@ static inline bool ftdi_config_ep(uint8_t address, uint8_t type, uint16_t size)
|
||||||
return Endpoint_ConfigureEndpoint(address, type, size, 1);
|
return Endpoint_ConfigureEndpoint(address, type, size, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void maybe_enable_tx(void)
|
|
||||||
{
|
|
||||||
/* Not configured? */
|
|
||||||
if (USB_DeviceState != DEVICE_STATE_Configured)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Transmitter empty, and latency timer hasn't timed out? */
|
|
||||||
if (ftdi_status.temt && latency_count < latency_timer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Delay the initial TX? */
|
|
||||||
if (ftdi_delay_init_tx)
|
|
||||||
{
|
|
||||||
if (!got_first_rx)
|
|
||||||
{
|
|
||||||
if (!got_setbaudrate)
|
|
||||||
return;
|
|
||||||
if (frames_since_setbaudrate < WAIT_FRAMES)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enable it */
|
|
||||||
Endpoint_SelectEndpoint(FTDI_TX_EPADDR);
|
|
||||||
UEIENX |= (1 << TXINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EVENT_USB_Device_StartOfFrame(void)
|
|
||||||
{
|
|
||||||
if (latency_count < latency_timer)
|
|
||||||
{
|
|
||||||
latency_count++;
|
|
||||||
if (latency_count == latency_timer)
|
|
||||||
maybe_enable_tx();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ftdi_delay_init_tx)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!got_setbaudrate)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (frames_since_setbaudrate < WAIT_FRAMES)
|
|
||||||
{
|
|
||||||
frames_since_setbaudrate++;
|
|
||||||
if (frames_since_setbaudrate == WAIT_FRAMES)
|
|
||||||
maybe_enable_tx();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EVENT_USB_Device_ConfigurationChanged(void)
|
void EVENT_USB_Device_ConfigurationChanged(void)
|
||||||
{
|
{
|
||||||
uint8_t PrevSelectedEndpoint = Endpoint_GetCurrentEndpoint();
|
uint8_t PrevSelectedEndpoint = Endpoint_GetCurrentEndpoint();
|
||||||
|
|
||||||
/* Configure endpoints */
|
/* Configure endpoints */
|
||||||
|
|
||||||
|
/* IN */
|
||||||
ftdi_config_ep(FTDI_TX_EPADDR, EP_TYPE_BULK, FTDI_TXRX_EPSIZE);
|
ftdi_config_ep(FTDI_TX_EPADDR, EP_TYPE_BULK, FTDI_TXRX_EPSIZE);
|
||||||
|
Endpoint_SelectEndpoint(FTDI_TX_EPADDR);
|
||||||
|
UEIENX = (1 << TXINE);
|
||||||
|
|
||||||
|
/* OUT */
|
||||||
ftdi_config_ep(FTDI_RX_EPADDR, EP_TYPE_BULK, FTDI_TXRX_EPSIZE);
|
ftdi_config_ep(FTDI_RX_EPADDR, EP_TYPE_BULK, FTDI_TXRX_EPSIZE);
|
||||||
|
|
||||||
/* Start interrupt on RX endpoint */
|
|
||||||
Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
|
Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
|
||||||
UEIENX |= (1 << RXOUTE);
|
UEIENX = (1 << RXOUTE);
|
||||||
|
|
||||||
/* If we have data to send, start interrupt on TX endpoint */
|
|
||||||
maybe_enable_tx();
|
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
Endpoint_SelectEndpoint(PrevSelectedEndpoint);
|
Endpoint_SelectEndpoint(PrevSelectedEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *fmt_ch(uint8_t ch) __attribute__((unused));
|
|
||||||
static const char *fmt_ch(uint8_t ch)
|
|
||||||
{
|
|
||||||
static char s[10];
|
|
||||||
if (ch > 32 && ch <= 127)
|
|
||||||
sprintf(s, "%c", ch);
|
|
||||||
else
|
|
||||||
sprintf(s, "<%d>", ch);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ftdi_bulk_in(void)
|
static inline void ftdi_bulk_in(void)
|
||||||
{
|
{
|
||||||
int len, i;
|
int len, i;
|
||||||
|
@ -509,38 +388,27 @@ static inline void ftdi_bulk_in(void)
|
||||||
/* Data format: ftdi_status structure (2 bytes) followed by the
|
/* Data format: ftdi_status structure (2 bytes) followed by the
|
||||||
serial data. */
|
serial data. */
|
||||||
len = _queue_get_len(&ftdi_txqueue);
|
len = _queue_get_len(&ftdi_txqueue);
|
||||||
if (len > FTDI_TXRX_EPSIZE - 2) {
|
|
||||||
/* There will be more data after this packet */
|
if (len >= FTDI_TXRX_EPSIZE - 2) {
|
||||||
|
/* more to send */
|
||||||
|
ftdi_status.thre = 0;
|
||||||
|
ftdi_status.temt = 0;
|
||||||
len = FTDI_TXRX_EPSIZE - 2;
|
len = FTDI_TXRX_EPSIZE - 2;
|
||||||
} else {
|
} else {
|
||||||
/* No more data after this */
|
/* transmitter empty */
|
||||||
ftdi_status.temt = 1;
|
ftdi_status.thre = 0;
|
||||||
|
ftdi_status.temt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we have data or the latency timer expired, send status
|
|
||||||
bytes and the data. Otherwise, this will just be a ZLP. */
|
|
||||||
if (len > 0 || latency_count == latency_timer)
|
|
||||||
{
|
|
||||||
Endpoint_Write_8(((uint8_t *)&ftdi_status)[0]);
|
Endpoint_Write_8(((uint8_t *)&ftdi_status)[0]);
|
||||||
Endpoint_Write_8(((uint8_t *)&ftdi_status)[1]);
|
Endpoint_Write_8(((uint8_t *)&ftdi_status)[1]);
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
Endpoint_Write_8(_queue_get(&ftdi_txqueue));
|
||||||
uint8_t ch = _queue_get(&ftdi_txqueue);
|
|
||||||
Endpoint_Write_8(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Latency timer can be reset, since we're sending
|
/* If that packet was empty, disable the IN interrupt. We'll
|
||||||
useful data to the host. */
|
reenable it when we have more data to send. */
|
||||||
latency_count = 0;
|
if (len == 0) {
|
||||||
}
|
|
||||||
|
|
||||||
if (ftdi_status.temt)
|
|
||||||
{
|
|
||||||
/* If we have no more data, and we wrote a packet that was
|
|
||||||
smaller than the full endpoint size, then this transfer is
|
|
||||||
done and we can disable the IN interrupt. We'll reenable
|
|
||||||
it when we have more data to send. */
|
|
||||||
if (len != FTDI_TXRX_EPSIZE - 2)
|
|
||||||
UEIENX &= ~(1 << TXINE);
|
UEIENX &= ~(1 << TXINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,28 +420,20 @@ static inline void ftdi_bulk_out(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int len;
|
int len;
|
||||||
bool overflow = false;
|
|
||||||
|
|
||||||
len = Endpoint_BytesInEndpoint();
|
len = Endpoint_BytesInEndpoint();
|
||||||
|
|
||||||
|
if (_queue_get_free(&ftdi_rxqueue) < len) {
|
||||||
|
/* We can't deal with this right now. Disable OUT
|
||||||
|
interrupt; we'll reenable after reading some bytes */
|
||||||
|
UEIENX &= ~(1 << RXOUTE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
/* This will discard incoming bytes if queue is full */
|
/* This will discard incoming bytes if queue is full */
|
||||||
uint8_t ch = Endpoint_Read_8();
|
_queue_put(&ftdi_rxqueue, Endpoint_Read_8());
|
||||||
got_first_rx = true;
|
|
||||||
/* Put into RX queue */
|
|
||||||
if (_queue_put(&ftdi_rxqueue, ch) < 0)
|
|
||||||
overflow = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overflow)
|
|
||||||
{
|
|
||||||
/* We lost data because our RX queue was full.
|
|
||||||
Disable OUT interrupt; we'll reenable after emptying
|
|
||||||
some bytes from the RX queue. */
|
|
||||||
UEIENX &= ~(1 << RXOUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Endpoint_ClearOUT();
|
Endpoint_ClearOUT();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,15 +478,10 @@ void ftdi_putchar(uint8_t c)
|
||||||
if (ftdi_blocking_out == false)
|
if (ftdi_blocking_out == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Protect against race conditions by double checking
|
/* Enable the IN endpoint interrupt, since we have data to send. */
|
||||||
that we still have data to send, while interrupts are
|
if (USB_DeviceState == DEVICE_STATE_Configured) {
|
||||||
disabled. */
|
Endpoint_SelectEndpoint(FTDI_TX_EPADDR);
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
UEIENX |= (1 << TXINE);
|
||||||
if (_queue_get_len(&ftdi_txqueue) > 0)
|
|
||||||
{
|
|
||||||
ftdi_status.temt = 0;
|
|
||||||
maybe_enable_tx();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,17 +494,11 @@ int ftdi_getchar(void)
|
||||||
if (ftdi_blocking_in == false)
|
if (ftdi_blocking_in == false)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Protect against race conditions by double checking
|
/* Reenable the OUT endpoint interrupt, since we read some data */
|
||||||
that we still have space for new data, while interrupts
|
|
||||||
are disabled. */
|
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
|
||||||
/* Reenable the OUT endpoint interrupt, since we read
|
|
||||||
some data */
|
|
||||||
if (USB_DeviceState == DEVICE_STATE_Configured) {
|
if (USB_DeviceState == DEVICE_STATE_Configured) {
|
||||||
Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
|
Endpoint_SelectEndpoint(FTDI_RX_EPADDR);
|
||||||
UEIENX |= (1 << RXOUTE);
|
UEIENX |= (1 << RXOUTE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
18
ftdi.h
18
ftdi.h
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
/* FTDI device-to-host data IN endpoint */
|
/* FTDI device-to-host data IN endpoint */
|
||||||
#define FTDI_TX_EPADDR (ENDPOINT_DIR_IN | 1)
|
#define FTDI_TX_EPADDR (ENDPOINT_DIR_IN | 1)
|
||||||
#define FTDI_TX_QUEUE_LEN 256
|
#define FTDI_TX_QUEUE_LEN 32
|
||||||
|
|
||||||
/* Endpoint address of FTDI host-to-device data OUT endpoint */
|
/* Endpoint address of FTDI host-to-device data OUT endpoint */
|
||||||
#define FTDI_RX_EPADDR (ENDPOINT_DIR_OUT | 2)
|
#define FTDI_RX_EPADDR (ENDPOINT_DIR_OUT | 2)
|
||||||
#define FTDI_RX_QUEUE_LEN 256 /* Must be at least FTDI_TXRX_EPSIZE + 1 */
|
#define FTDI_RX_QUEUE_LEN 32 /* Must be at least FTDI_TXRX_EPSIZE + 1 */
|
||||||
|
|
||||||
/* Endpoint size */
|
/* Endpoint size */
|
||||||
#define FTDI_TXRX_EPSIZE 16 /* e.g. 16 or 64 */
|
#define FTDI_TXRX_EPSIZE 16 /* e.g. 16 or 64 */
|
||||||
|
@ -63,18 +63,6 @@
|
||||||
#define FTDI_BLOCKING_OUT (1 << 2)
|
#define FTDI_BLOCKING_OUT (1 << 2)
|
||||||
#define FTDI_BLOCKING (FTDI_BLOCKING_IN | FTDI_BLOCKING_OUT)
|
#define FTDI_BLOCKING (FTDI_BLOCKING_IN | FTDI_BLOCKING_OUT)
|
||||||
|
|
||||||
/* Whether to translate \n to \r\n on output, like stty's onlcr flag */
|
|
||||||
#define FTDI_ONLCR (1 << 3)
|
|
||||||
|
|
||||||
/* Whether to delay the initial TX after USB configuration.
|
|
||||||
If set, wait for either:
|
|
||||||
- 250ms to pass after a SET_BAUDRATE command
|
|
||||||
- input to be received
|
|
||||||
before sending any data. This is to work around issues where
|
|
||||||
some hsots will discard (or even echo) input while the port
|
|
||||||
is still being opened. */
|
|
||||||
#define FTDI_DELAY_INIT_TX (1 << 4)
|
|
||||||
|
|
||||||
/* Initialize FTDI routines, with flags above */
|
/* Initialize FTDI routines, with flags above */
|
||||||
void ftdi_init(int flags);
|
void ftdi_init(int flags);
|
||||||
|
|
||||||
|
@ -92,7 +80,7 @@ void EVENT_USB_Device_ControlRequest(void);
|
||||||
void EVENT_USB_Device_Reset(void);
|
void EVENT_USB_Device_Reset(void);
|
||||||
void EVENT_USB_Device_ConfigurationChanged(void);
|
void EVENT_USB_Device_ConfigurationChanged(void);
|
||||||
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
|
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
|
||||||
const uint16_t wIndex,
|
const uint8_t wIndex,
|
||||||
const void** const DescriptorAddress)
|
const void** const DescriptorAddress)
|
||||||
ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
|
ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
|
||||||
|
|
||||||
|
|
2
lufa
2
lufa
|
@ -1 +1 @@
|
||||||
Subproject commit 737382aeda146ca793be452ac5790e12d1ca16ea
|
Subproject commit f59a34370cec33272412d9aee6e68871d7e8b5e0
|
6
main.c
6
main.c
|
@ -9,6 +9,7 @@
|
||||||
#include <LUFA/Drivers/USB/USB.h>
|
#include <LUFA/Drivers/USB/USB.h>
|
||||||
|
|
||||||
#include "ftdi.h"
|
#include "ftdi.h"
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
static void setup(void)
|
static void setup(void)
|
||||||
{
|
{
|
||||||
|
@ -36,7 +37,10 @@ int main(void)
|
||||||
c = getchar();
|
c = getchar();
|
||||||
/* If FTDI_NONBLOCKING was provided to ftdi_init,
|
/* If FTDI_NONBLOCKING was provided to ftdi_init,
|
||||||
c will be -1 if no data was available. */
|
c will be -1 if no data was available. */
|
||||||
if (c >= 0)
|
if (c >= 0) {
|
||||||
printf("You sent %d (%c)\n", c, isprint(c) ? c : '?');
|
printf("You sent %d (%c)\n", c, isprint(c) ? c : '?');
|
||||||
|
printf("Maximum stack space used: %d / %d bytes\n",
|
||||||
|
(stack_total() - stack_min()), stack_total());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
46
stack.c
Normal file
46
stack.c
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "stack.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
extern uint8_t _end;
|
||||||
|
extern uint8_t __stack;
|
||||||
|
|
||||||
|
void __stack_canary_init(void)
|
||||||
|
__attribute__ ((naked))
|
||||||
|
__attribute__ ((section (".init1")));
|
||||||
|
void __stack_canary_init(void)
|
||||||
|
{
|
||||||
|
__asm volatile (" ldi r30,lo8(_end)\n"
|
||||||
|
" ldi r31,hi8(_end)\n"
|
||||||
|
" ldi r24,lo8(0xaa)\n" // canary
|
||||||
|
" ldi r25,hi8(__stack)\n"
|
||||||
|
" rjmp 2f\n"
|
||||||
|
"1:\n"
|
||||||
|
" wdr\n"
|
||||||
|
" st Z+,r24\n"
|
||||||
|
"2:\n"
|
||||||
|
" cpi r30,lo8(__stack)\n"
|
||||||
|
" cpc r31,r25\n"
|
||||||
|
" brlo 1b\n"
|
||||||
|
" breq 1b"
|
||||||
|
:
|
||||||
|
:
|
||||||
|
: "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return low level mark of free stack space */
|
||||||
|
uint16_t stack_min(void)
|
||||||
|
{
|
||||||
|
const uint8_t *p;
|
||||||
|
uint16_t c = 0;
|
||||||
|
|
||||||
|
for (p = &_end; *p == 0xaa && p <= &__stack; p++)
|
||||||
|
c++;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return total amount of stack */
|
||||||
|
uint16_t stack_total(void)
|
||||||
|
{
|
||||||
|
return &__stack - &_end;
|
||||||
|
}
|
12
stack.h
Normal file
12
stack.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef STACK_H
|
||||||
|
#define STACK_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* return low level mark of free stack space */
|
||||||
|
uint16_t stack_min(void);
|
||||||
|
|
||||||
|
/* return total amount of stack */
|
||||||
|
uint16_t stack_total(void);
|
||||||
|
|
||||||
|
#endif
|
132
terminal.py
Executable file
132
terminal.py
Executable file
|
@ -0,0 +1,132 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# Based on miniterm sample in pyserial
|
||||||
|
|
||||||
|
import sys, os, serial, threading, traceback, time
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
import msvcrt
|
||||||
|
class Console:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
def cleanup(self):
|
||||||
|
pass
|
||||||
|
def getkey(self):
|
||||||
|
while 1:
|
||||||
|
z = msvcrt.getch()
|
||||||
|
if z == '\0' or z == '\xe0': # function keys
|
||||||
|
msvcrt.getch()
|
||||||
|
else:
|
||||||
|
if z == '\r':
|
||||||
|
return '\n'
|
||||||
|
return z
|
||||||
|
elif os.name == 'posix':
|
||||||
|
import termios, sys, os
|
||||||
|
class Console:
|
||||||
|
def __init__(self):
|
||||||
|
self.fd = sys.stdin.fileno()
|
||||||
|
try:
|
||||||
|
self.old = termios.tcgetattr(self.fd)
|
||||||
|
new = termios.tcgetattr(self.fd)
|
||||||
|
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
|
||||||
|
new[6][termios.VMIN] = 1
|
||||||
|
new[6][termios.VTIME] = 0
|
||||||
|
termios.tcsetattr(self.fd, termios.TCSANOW, new)
|
||||||
|
except termios.error:
|
||||||
|
# ignore errors, so we can pipe stuff to this script
|
||||||
|
pass
|
||||||
|
def cleanup(self):
|
||||||
|
try:
|
||||||
|
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old)
|
||||||
|
except:
|
||||||
|
# ignore errors, so we can pipe stuff to this script
|
||||||
|
pass
|
||||||
|
def getkey(self):
|
||||||
|
c = os.read(self.fd, 1)
|
||||||
|
return c
|
||||||
|
else:
|
||||||
|
raise ("Sorry, no terminal implementation for your platform (%s) "
|
||||||
|
"available." % sys.platform)
|
||||||
|
|
||||||
|
class Miniterm:
|
||||||
|
def __init__(self, serial):
|
||||||
|
self.serial = serial
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.alive = True
|
||||||
|
#start serial->console thread
|
||||||
|
self.receiver_thread = threading.Thread(target=self.reader)
|
||||||
|
self.receiver_thread.daemon = True
|
||||||
|
self.receiver_thread.start()
|
||||||
|
#enter console->serial loop
|
||||||
|
self.console = Console()
|
||||||
|
self.transmitter_thread = threading.Thread(target=self.writer)
|
||||||
|
self.transmitter_thread.daemon = True
|
||||||
|
self.transmitter_thread.start()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.alive = False
|
||||||
|
|
||||||
|
def join(self, transmit_only=False):
|
||||||
|
self.receiver_thread.join()
|
||||||
|
self.transmitter_thread.join()
|
||||||
|
|
||||||
|
def reader(self):
|
||||||
|
"""loop and copy serial->console"""
|
||||||
|
try:
|
||||||
|
while self.alive:
|
||||||
|
data = self.serial.read(1)
|
||||||
|
sys.stdout.write(data)
|
||||||
|
sys.stdout.flush()
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
self.console.cleanup()
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
def writer(self):
|
||||||
|
"""loop and copy console->serial until ^C"""
|
||||||
|
try:
|
||||||
|
while self.alive:
|
||||||
|
try:
|
||||||
|
c = self.console.getkey()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
c = '\x03'
|
||||||
|
if c == '\x03':
|
||||||
|
self.stop()
|
||||||
|
return
|
||||||
|
elif c == '':
|
||||||
|
# EOF on input. Wait a tiny bit so we can
|
||||||
|
# flush the remaining input, then stop.
|
||||||
|
time.sleep(0.25)
|
||||||
|
self.stop()
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.serial.write(c) # send character
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
self.console.cleanup()
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.serial.timeout = 0.1
|
||||||
|
self.start()
|
||||||
|
self.join()
|
||||||
|
print ""
|
||||||
|
self.console.cleanup()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Simple serial terminal")
|
||||||
|
parser.add_argument('device', metavar='DEVICE',
|
||||||
|
help='serial device')
|
||||||
|
parser.add_argument('baudrate', metavar='BAUDRATE', type=int, nargs='?',
|
||||||
|
help='baud rate', default=115200)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
dev = serial.Serial(args.device, args.baudrate)
|
||||||
|
except serial.serialutil.SerialException:
|
||||||
|
sys.stderr.write("error opening %s\n" % args.device)
|
||||||
|
raise SystemExit(1)
|
||||||
|
term = Miniterm(dev)
|
||||||
|
term.run()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user