diff --git a/contrib/usart-stm32f103.c b/contrib/usart-stm32f103.c index cc9b498..e94d70d 100644 --- a/contrib/usart-stm32f103.c +++ b/contrib/usart-stm32f103.c @@ -172,9 +172,16 @@ usart_config (uint8_t dev_no, uint32_t config_bits) return 0; } +static int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits); + 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. */ RCC->APB1ENR |= ((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 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 handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat) { int tx_ready = 0; uint32_t r = USARTx->SR; + int notify_bits = 0; 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++; else if ((r & USART_SR_FE)) { + /* NOTE: Noway to distinguish framing error and break */ + stat->rx_break++; - /* XXX: break event report to upper layer? */ + notify_bits |= UART_STATE_BITMAP_BREAK; } else if ((r & USART_SR_PE)) - stat->err_rx_parity++; + { + stat->err_rx_parity++; + notify_bits |= UART_STATE_BITMAP_PARITY; + } else { 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 */ 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; asm volatile ("" : : "r" (data) : "memory"); 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; @@ -552,3 +583,17 @@ usart_stat (uint8_t dev_no) else 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; +} diff --git a/contrib/usart.h b/contrib/usart.h index d850644..281abf0 100644 --- a/contrib/usart.h +++ b/contrib/usart.h @@ -44,17 +44,23 @@ PAR_BITS 3 DSR DCD RI */ struct usart_stat { + uint8_t dev_no; + uint32_t tx; uint32_t rx; uint32_t rx_break; + uint32_t err_notify_overflow; uint32_t err_rx_overflow; /* software side */ uint32_t err_rx_overrun; /* hardware side */ uint32_t err_rx_noise; 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_read (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); +int usart_send_break (uint8_t dev_no); diff --git a/example-usb-serial/README b/example-usb-serial/README index a772945..cbd5a8e 100644 --- a/example-usb-serial/README +++ b/example-usb-serial/README @@ -3,14 +3,21 @@ This is an application example using ST Nucleo F103 board. SB62 and SB63 should be soldered. 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: -* 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 * Use of DMA for serial communication * 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 * Support of other communication mode: smartcard, IrDA, etc. diff --git a/example-usb-serial/cdc.h b/example-usb-serial/cdc.h index cbcd4f7..98f6d95 100644 --- a/example-usb-serial/cdc.h +++ b/example-usb-serial/cdc.h @@ -9,3 +9,4 @@ struct cdc *cdc_open (uint8_t num); void cdc_wait_connection (struct cdc *); int cdc_send (struct cdc *s, const char *buf, int count); int cdc_recv (struct cdc *s, char *buf, uint32_t *timeout); +int cdc_ss_notify (struct cdc *s, uint16_t state_bits); diff --git a/example-usb-serial/sample.c b/example-usb-serial/sample.c index 2a3e925..d4cb7e3 100644 --- a/example-usb-serial/sample.c +++ b/example-usb-serial/sample.c @@ -120,13 +120,28 @@ cdc_to_usart_loop (void *arg) 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 main (int argc, const char *argv[]) { - struct cdc_usart cdc_usart0; - struct cdc_usart cdc_usart1; - (void)argc; (void)argv; @@ -135,7 +150,7 @@ main (int argc, const char *argv[]) cdc_init (); 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.dev_no = 2; diff --git a/example-usb-serial/usb-cdc.c b/example-usb-serial/usb-cdc.c index b7f665d..c83586d 100644 --- a/example-usb-serial/usb-cdc.c +++ b/example-usb-serial/usb-cdc.c @@ -40,7 +40,8 @@ struct cdc { uint32_t flag_connected : 1; uint32_t flag_output_ready: 1; uint32_t flag_input_avail : 1; - uint32_t : 22; + uint32_t flag_notify_busy : 1; + uint32_t :21; struct line_coding line_coding; }; @@ -84,16 +85,19 @@ cdc_get (int interface, uint8_t ep_num) #define ENDP0_TXADDR (0x80) #define ENDP1_TXADDR (0xc0) #define ENDP2_TXADDR (0x100) -#define ENDP3_RXADDR (0x108) -#define ENDP4_TXADDR (0x148) -#define ENDP5_TXADDR (0x188) -#define ENDP6_RXADDR (0x190) +#define ENDP3_RXADDR (0x10A) +#define ENDP4_TXADDR (0x14A) +#define ENDP5_TXADDR (0x18A) +#define ENDP6_RXADDR (0x194) +/* 0x1d4 = 468, 44-byte available */ #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 +#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 + /* USB Device Descriptor */ static const uint8_t vcom_device_desc[18] = { 18, /* bLength */ @@ -169,7 +173,7 @@ static const uint8_t vcom_config_desc[] = { ENDPOINT_DESCRIPTOR, ENDP2|0x80, /* bEndpointAddress. */ 0x03, /* bmAttributes (Interrupt). */ - 0x08, 0x00, /* wMaxPacketSize. */ + 0x0A, 0x00, /* wMaxPacketSize. */ 0xFF, /* bInterval. */ /* Interface Descriptor.*/ 9, @@ -242,7 +246,7 @@ static const uint8_t vcom_config_desc[] = { ENDPOINT_DESCRIPTOR, ENDP5|0x80, /* bEndpointAddress. */ 0x03, /* bmAttributes (Interrupt). */ - 0x08, 0x00, /* wMaxPacketSize. */ + 0x0A, 0x00, /* wMaxPacketSize. */ 0xFF, /* bInterval. */ /* Interface Descriptor.*/ 9, @@ -675,20 +679,20 @@ usb_tx_done (uint8_t ep_num, uint16_t 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) { s->flag_output_ready = 1; 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); - if (ep_num == ENDP3 || ep_num == ENDP6) + if (ep_num == s->endp3) { usb_lld_rxcpy (s->input, ep_num, 0, len); s->flag_input_avail = 1; @@ -1043,3 +1047,39 @@ cdc_recv (struct cdc *s, char *buf, uint32_t *timeout) 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; +}