USB-USART supports notification of serial error.
This commit is contained in:
@@ -172,9 +172,16 @@ usart_config (uint8_t dev_no, uint32_t config_bits)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits);
|
||||||
|
|
||||||
void
|
void
|
||||||
usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size)
|
usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size,
|
||||||
|
int (*cb) (uint8_t dev_no, uint16_t notify_bits))
|
||||||
{
|
{
|
||||||
|
ss_notify_callback = cb;
|
||||||
|
usart2_stat.dev_no = 2;
|
||||||
|
usart3_stat.dev_no = 3;
|
||||||
|
|
||||||
/* Enable USART2 and USART3 clocks, and strobe reset. */
|
/* Enable USART2 and USART3 clocks, and strobe reset. */
|
||||||
RCC->APB1ENR |= ((1 << 18) | (1 << 17));
|
RCC->APB1ENR |= ((1 << 18) | (1 << 17));
|
||||||
RCC->APB1RSTR = ((1 << 18) | (1 << 17));
|
RCC->APB1RSTR = ((1 << 18) | (1 << 17));
|
||||||
@@ -381,11 +388,20 @@ static struct chx_poll_head *usart_poll[4];
|
|||||||
static int usart2_tx_ready;
|
static int usart2_tx_ready;
|
||||||
static int usart3_tx_ready;
|
static int usart3_tx_ready;
|
||||||
|
|
||||||
|
#define UART_STATE_BITMAP_RX_CARRIER (1 << 0)
|
||||||
|
#define UART_STATE_BITMAP_TX_CARRIER (1 << 1)
|
||||||
|
#define UART_STATE_BITMAP_BREAK (1 << 2)
|
||||||
|
#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3)
|
||||||
|
#define UART_STATE_BITMAP_FRAMING (1 << 4)
|
||||||
|
#define UART_STATE_BITMAP_PARITY (1 << 5)
|
||||||
|
#define UART_STATE_BITMAP_OVERRUN (1 << 6)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
|
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
|
||||||
{
|
{
|
||||||
int tx_ready = 0;
|
int tx_ready = 0;
|
||||||
uint32_t r = USARTx->SR;
|
uint32_t r = USARTx->SR;
|
||||||
|
int notify_bits = 0;
|
||||||
|
|
||||||
if ((r & USART_SR_TXE))
|
if ((r & USART_SR_TXE))
|
||||||
{
|
{
|
||||||
@@ -406,15 +422,23 @@ handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
|
|||||||
stat->err_rx_noise++;
|
stat->err_rx_noise++;
|
||||||
else if ((r & USART_SR_FE))
|
else if ((r & USART_SR_FE))
|
||||||
{
|
{
|
||||||
|
/* NOTE: Noway to distinguish framing error and break */
|
||||||
|
|
||||||
stat->rx_break++;
|
stat->rx_break++;
|
||||||
/* XXX: break event report to upper layer? */
|
notify_bits |= UART_STATE_BITMAP_BREAK;
|
||||||
}
|
}
|
||||||
else if ((r & USART_SR_PE))
|
else if ((r & USART_SR_PE))
|
||||||
stat->err_rx_parity++;
|
{
|
||||||
|
stat->err_rx_parity++;
|
||||||
|
notify_bits |= UART_STATE_BITMAP_PARITY;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((r & USART_SR_ORE))
|
if ((r & USART_SR_ORE))
|
||||||
stat->err_rx_overrun++;
|
{
|
||||||
|
stat->err_rx_overrun++;
|
||||||
|
notify_bits |= UART_STATE_BITMAP_OVERRUN;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
|
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
|
||||||
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
|
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
|
||||||
@@ -428,6 +452,13 @@ handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
|
|||||||
uint32_t data = USARTx->DR;
|
uint32_t data = USARTx->DR;
|
||||||
asm volatile ("" : : "r" (data) : "memory");
|
asm volatile ("" : : "r" (data) : "memory");
|
||||||
stat->err_rx_overrun++;
|
stat->err_rx_overrun++;
|
||||||
|
notify_bits |= UART_STATE_BITMAP_OVERRUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify_bits)
|
||||||
|
{
|
||||||
|
if ((*ss_notify_callback) (stat->dev_no, notify_bits))
|
||||||
|
stat->err_notify_overflow++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx_ready;
|
return tx_ready;
|
||||||
@@ -552,3 +583,17 @@ usart_stat (uint8_t dev_no)
|
|||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
usart_send_break (uint8_t dev_no)
|
||||||
|
{
|
||||||
|
struct USART *USARTx = get_usart_dev (dev_no);
|
||||||
|
if (USARTx == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((USARTx->CR1 & 0x01))
|
||||||
|
return 1; /* Busy sending break, which was requested before. */
|
||||||
|
|
||||||
|
USARTx->CR1 |= 0x01;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,17 +44,23 @@ PAR_BITS 3
|
|||||||
DSR DCD RI */
|
DSR DCD RI */
|
||||||
|
|
||||||
struct usart_stat {
|
struct usart_stat {
|
||||||
|
uint8_t dev_no;
|
||||||
|
|
||||||
uint32_t tx;
|
uint32_t tx;
|
||||||
uint32_t rx;
|
uint32_t rx;
|
||||||
uint32_t rx_break;
|
uint32_t rx_break;
|
||||||
|
uint32_t err_notify_overflow;
|
||||||
uint32_t err_rx_overflow; /* software side */
|
uint32_t err_rx_overflow; /* software side */
|
||||||
uint32_t err_rx_overrun; /* hardware side */
|
uint32_t err_rx_overrun; /* hardware side */
|
||||||
uint32_t err_rx_noise;
|
uint32_t err_rx_noise;
|
||||||
uint32_t err_rx_parity;
|
uint32_t err_rx_parity;
|
||||||
};
|
};
|
||||||
|
|
||||||
void usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size);
|
|
||||||
|
void usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size,
|
||||||
|
int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits));
|
||||||
int usart_config (uint8_t dev_no, uint32_t config_bits);
|
int usart_config (uint8_t dev_no, uint32_t config_bits);
|
||||||
int usart_read (uint8_t dev_no, char *buf, uint16_t buflen);
|
int usart_read (uint8_t dev_no, char *buf, uint16_t buflen);
|
||||||
int usart_write (uint8_t dev_no, char *buf, uint16_t buflen);
|
int usart_write (uint8_t dev_no, char *buf, uint16_t buflen);
|
||||||
const struct usart_stat *usart_stat (uint8_t dev_no);
|
const struct usart_stat *usart_stat (uint8_t dev_no);
|
||||||
|
int usart_send_break (uint8_t dev_no);
|
||||||
|
|||||||
@@ -3,14 +3,21 @@ This is an application example using ST Nucleo F103 board.
|
|||||||
SB62 and SB63 should be soldered.
|
SB62 and SB63 should be soldered.
|
||||||
ST-Link/V2 is disconnected (SB13 and SB14).
|
ST-Link/V2 is disconnected (SB13 and SB14).
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
Using the USB CDC-ACM for serial communication is a kind of wrong,
|
||||||
|
because it's designed for modem; No way to control CTSRTS.
|
||||||
|
|
||||||
|
TIOCGICOUNT
|
||||||
|
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
* serial config setting
|
|
||||||
* CTSRTS? How by USB? By vendor specific control?
|
|
||||||
* stats report control
|
|
||||||
* USB communication class interface notification (interrupt ENDP2) support
|
|
||||||
SERIAL_STATE
|
|
||||||
* SEND_BREAK support
|
* SEND_BREAK support
|
||||||
* Use of DMA for serial communication
|
* Use of DMA for serial communication
|
||||||
* RS-232 support: GPIO with DTR (out), DCD (in), DSR (in), RI (in)
|
* RS-232 support: GPIO with DTR (out), DCD (in), DSR (in), RI (in)
|
||||||
|
* serial config setting of CTSRTS?
|
||||||
|
By vendor specific control?
|
||||||
|
* stats report control
|
||||||
|
By vendor specific control?
|
||||||
* Half-duplex support
|
* Half-duplex support
|
||||||
* Support of other communication mode: smartcard, IrDA, etc.
|
* Support of other communication mode: smartcard, IrDA, etc.
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ struct cdc *cdc_open (uint8_t num);
|
|||||||
void cdc_wait_connection (struct cdc *);
|
void cdc_wait_connection (struct cdc *);
|
||||||
int cdc_send (struct cdc *s, const char *buf, int count);
|
int cdc_send (struct cdc *s, const char *buf, int count);
|
||||||
int cdc_recv (struct cdc *s, char *buf, uint32_t *timeout);
|
int cdc_recv (struct cdc *s, char *buf, uint32_t *timeout);
|
||||||
|
int cdc_ss_notify (struct cdc *s, uint16_t state_bits);
|
||||||
|
|||||||
@@ -120,13 +120,28 @@ cdc_to_usart_loop (void *arg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct cdc_usart cdc_usart0;
|
||||||
|
static struct cdc_usart cdc_usart1;
|
||||||
|
|
||||||
|
static int
|
||||||
|
ss_notify (uint8_t dev_no, uint16_t state_bits)
|
||||||
|
{
|
||||||
|
struct cdc *s;
|
||||||
|
|
||||||
|
if (dev_no == cdc_usart0.dev_no)
|
||||||
|
s = cdc_usart0.cdc;
|
||||||
|
else if (dev_no == cdc_usart1.dev_no)
|
||||||
|
s = cdc_usart1.cdc;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return cdc_ss_notify (s, state_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc, const char *argv[])
|
main (int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
struct cdc_usart cdc_usart0;
|
|
||||||
struct cdc_usart cdc_usart1;
|
|
||||||
|
|
||||||
(void)argc;
|
(void)argc;
|
||||||
(void)argv;
|
(void)argv;
|
||||||
|
|
||||||
@@ -135,7 +150,7 @@ main (int argc, const char *argv[])
|
|||||||
cdc_init ();
|
cdc_init ();
|
||||||
cdc_wait_configured ();
|
cdc_wait_configured ();
|
||||||
|
|
||||||
usart_init (PRIO_USART, STACK_ADDR_USART, STACK_SIZE_USART);
|
usart_init (PRIO_USART, STACK_ADDR_USART, STACK_SIZE_USART, ss_notify);
|
||||||
|
|
||||||
cdc_usart0.cdc = cdc_open (0);
|
cdc_usart0.cdc = cdc_open (0);
|
||||||
cdc_usart0.dev_no = 2;
|
cdc_usart0.dev_no = 2;
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ struct cdc {
|
|||||||
uint32_t flag_connected : 1;
|
uint32_t flag_connected : 1;
|
||||||
uint32_t flag_output_ready: 1;
|
uint32_t flag_output_ready: 1;
|
||||||
uint32_t flag_input_avail : 1;
|
uint32_t flag_input_avail : 1;
|
||||||
uint32_t : 22;
|
uint32_t flag_notify_busy : 1;
|
||||||
|
uint32_t :21;
|
||||||
struct line_coding line_coding;
|
struct line_coding line_coding;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,16 +85,19 @@ cdc_get (int interface, uint8_t ep_num)
|
|||||||
#define ENDP0_TXADDR (0x80)
|
#define ENDP0_TXADDR (0x80)
|
||||||
#define ENDP1_TXADDR (0xc0)
|
#define ENDP1_TXADDR (0xc0)
|
||||||
#define ENDP2_TXADDR (0x100)
|
#define ENDP2_TXADDR (0x100)
|
||||||
#define ENDP3_RXADDR (0x108)
|
#define ENDP3_RXADDR (0x10A)
|
||||||
#define ENDP4_TXADDR (0x148)
|
#define ENDP4_TXADDR (0x14A)
|
||||||
#define ENDP5_TXADDR (0x188)
|
#define ENDP5_TXADDR (0x18A)
|
||||||
#define ENDP6_RXADDR (0x190)
|
#define ENDP6_RXADDR (0x194)
|
||||||
|
/* 0x1d4 = 468, 44-byte available */
|
||||||
|
|
||||||
#define USB_CDC_REQ_SET_LINE_CODING 0x20
|
#define USB_CDC_REQ_SET_LINE_CODING 0x20
|
||||||
#define USB_CDC_REQ_GET_LINE_CODING 0x21
|
#define USB_CDC_REQ_GET_LINE_CODING 0x21
|
||||||
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
|
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
|
||||||
#define USB_CDC_REQ_SEND_BREAK 0x23
|
#define USB_CDC_REQ_SEND_BREAK 0x23
|
||||||
|
|
||||||
|
#define USB_CDC_NOTIFY_SERIAL_STATE 0x20
|
||||||
|
|
||||||
/* USB Device Descriptor */
|
/* USB Device Descriptor */
|
||||||
static const uint8_t vcom_device_desc[18] = {
|
static const uint8_t vcom_device_desc[18] = {
|
||||||
18, /* bLength */
|
18, /* bLength */
|
||||||
@@ -169,7 +173,7 @@ static const uint8_t vcom_config_desc[] = {
|
|||||||
ENDPOINT_DESCRIPTOR,
|
ENDPOINT_DESCRIPTOR,
|
||||||
ENDP2|0x80, /* bEndpointAddress. */
|
ENDP2|0x80, /* bEndpointAddress. */
|
||||||
0x03, /* bmAttributes (Interrupt). */
|
0x03, /* bmAttributes (Interrupt). */
|
||||||
0x08, 0x00, /* wMaxPacketSize. */
|
0x0A, 0x00, /* wMaxPacketSize. */
|
||||||
0xFF, /* bInterval. */
|
0xFF, /* bInterval. */
|
||||||
/* Interface Descriptor.*/
|
/* Interface Descriptor.*/
|
||||||
9,
|
9,
|
||||||
@@ -242,7 +246,7 @@ static const uint8_t vcom_config_desc[] = {
|
|||||||
ENDPOINT_DESCRIPTOR,
|
ENDPOINT_DESCRIPTOR,
|
||||||
ENDP5|0x80, /* bEndpointAddress. */
|
ENDP5|0x80, /* bEndpointAddress. */
|
||||||
0x03, /* bmAttributes (Interrupt). */
|
0x03, /* bmAttributes (Interrupt). */
|
||||||
0x08, 0x00, /* wMaxPacketSize. */
|
0x0A, 0x00, /* wMaxPacketSize. */
|
||||||
0xFF, /* bInterval. */
|
0xFF, /* bInterval. */
|
||||||
/* Interface Descriptor.*/
|
/* Interface Descriptor.*/
|
||||||
9,
|
9,
|
||||||
@@ -675,20 +679,20 @@ usb_tx_done (uint8_t ep_num, uint16_t len)
|
|||||||
|
|
||||||
(void)len;
|
(void)len;
|
||||||
|
|
||||||
if (ep_num == ENDP1 || ep_num == ENDP4)
|
chopstx_mutex_lock (&s->mtx);
|
||||||
|
if (ep_num == s->endp1)
|
||||||
{
|
{
|
||||||
chopstx_mutex_lock (&s->mtx);
|
|
||||||
if (s->flag_output_ready == 0)
|
if (s->flag_output_ready == 0)
|
||||||
{
|
{
|
||||||
s->flag_output_ready = 1;
|
s->flag_output_ready = 1;
|
||||||
chopstx_cond_signal (&s->cnd_tx);
|
chopstx_cond_signal (&s->cnd_tx);
|
||||||
}
|
}
|
||||||
chopstx_mutex_unlock (&s->mtx);
|
|
||||||
}
|
}
|
||||||
else if (ep_num == ENDP2 || ep_num == ENDP5)
|
else if (ep_num == s->endp2)
|
||||||
{
|
{
|
||||||
/* Nothing */
|
s->flag_notify_busy = 0;
|
||||||
}
|
}
|
||||||
|
chopstx_mutex_unlock (&s->mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -697,7 +701,7 @@ usb_rx_ready (uint8_t ep_num, uint16_t len)
|
|||||||
{
|
{
|
||||||
struct cdc *s = cdc_get (-1, ep_num);
|
struct cdc *s = cdc_get (-1, ep_num);
|
||||||
|
|
||||||
if (ep_num == ENDP3 || ep_num == ENDP6)
|
if (ep_num == s->endp3)
|
||||||
{
|
{
|
||||||
usb_lld_rxcpy (s->input, ep_num, 0, len);
|
usb_lld_rxcpy (s->input, ep_num, 0, len);
|
||||||
s->flag_input_avail = 1;
|
s->flag_input_avail = 1;
|
||||||
@@ -1043,3 +1047,39 @@ cdc_recv (struct cdc *s, char *buf, uint32_t *timeout)
|
|||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
cdc_ss_notify (struct cdc *s, uint16_t state_bits)
|
||||||
|
{
|
||||||
|
int busy;
|
||||||
|
uint8_t notification[10];
|
||||||
|
int interface;
|
||||||
|
|
||||||
|
if (s == &cdc_table[0])
|
||||||
|
interface = 0;
|
||||||
|
else
|
||||||
|
interface = 2;
|
||||||
|
|
||||||
|
/* Little endian */
|
||||||
|
notification[0] = REQUEST_DIR | CLASS_REQUEST | INTERFACE_RECIPIENT;
|
||||||
|
notification[1] = USB_CDC_NOTIFY_SERIAL_STATE;
|
||||||
|
notification[2] = notification[3] = 0;
|
||||||
|
notification[4] = interface;
|
||||||
|
notification[5] = 0;
|
||||||
|
notification[6] = 2;
|
||||||
|
notification[7] = 0;
|
||||||
|
notification[8] = (state_bits & 0xff);
|
||||||
|
notification[9] = (state_bits >> 8);
|
||||||
|
|
||||||
|
chopstx_mutex_lock (&s->mtx);
|
||||||
|
busy = s->flag_notify_busy;
|
||||||
|
if (!busy)
|
||||||
|
{
|
||||||
|
usb_lld_write (s->endp2, notification, sizeof notification);
|
||||||
|
s->flag_notify_busy = 1;
|
||||||
|
}
|
||||||
|
chopstx_mutex_unlock (&s->mtx);
|
||||||
|
|
||||||
|
return busy;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user