/* * This file is part of the Black Magic Debug project. * * Copyright (C) 2022 1BitSquared * Written by Rachel Mant * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) #include #include #include #include #elif defined(LM4F) #include #include #else #error "Unknown processor target" #endif #include #include #include #include "general.h" #include "usbuart.h" #include "usb.h" #include "aux_serial.h" static char aux_serial_receive_buffer[AUX_UART_BUFFER_SIZE]; /* Fifo in pointer, writes assumed to be atomic, should be only incremented within RX ISR */ static uint8_t aux_serial_receive_write_index; /* Fifo out pointer, writes assumed to be atomic, should be only incremented outside RX ISR */ static uint8_t aux_serial_receive_read_index; #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) static char aux_serial_transmit_buffer[2U][AUX_UART_BUFFER_SIZE]; static uint8_t aux_serial_transmit_buffer_index; static uint8_t aux_serial_transmit_buffer_consumed; static bool aux_serial_transmit_complete = true; #ifdef DMA_STREAM0 #define dma_channel_reset(dma, channel) dma_stream_reset(dma, channel) #define dma_enable_channel(dma, channel) dma_enable_stream(dma, channel) #define dma_disable_channel(dma, channel) dma_disable_stream(dma, channel) #define DMA_PSIZE_8BIT DMA_SxCR_PSIZE_8BIT #define DMA_MSIZE_8BIT DMA_SxCR_MSIZE_8BIT #define DMA_PL_HIGH DMA_SxCR_PL_HIGH #define DMA_CGIF DMA_ISR_FLAGS #else #define DMA_PSIZE_8BIT DMA_CCR_PSIZE_8BIT #define DMA_MSIZE_8BIT DMA_CCR_MSIZE_8BIT #define DMA_PL_HIGH DMA_CCR_PL_HIGH #define DMA_CGIF DMA_IFCR_CGIF_BIT #endif void aux_serial_init(void) { /* Enable clocks */ rcc_periph_clock_enable(USBUSART_CLK); rcc_periph_clock_enable(USBUSART_DMA_CLK); /* Setup UART parameters */ UART_PIN_SETUP(); usart_set_baudrate(USBUSART, 38400); usart_set_databits(USBUSART, 8); usart_set_stopbits(USBUSART, USART_STOPBITS_1); usart_set_mode(USBUSART, USART_MODE_TX_RX); usart_set_parity(USBUSART, USART_PARITY_NONE); usart_set_flow_control(USBUSART, USART_FLOWCONTROL_NONE); USART_CR1(USBUSART) |= USART_CR1_IDLEIE; /* Setup USART TX DMA */ #if !defined(USBUSART_TDR) && defined(USBUSART_DR) # define USBUSART_TDR USBUSART_DR #elif !defined(USBUSART_TDR) # define USBUSART_TDR USART_DR(USBUSART) #endif #if !defined(USBUSART_RDR) && defined(USBUSART_DR) # define USBUSART_RDR USBUSART_DR #elif !defined(USBUSART_RDR) # define USBUSART_RDR USART_DR(USBUSART) #endif dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uintptr_t)&USBUSART_TDR); dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); dma_set_peripheral_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_PSIZE_8BIT); dma_set_memory_size(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_MSIZE_8BIT); dma_set_priority(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_PL_HIGH); dma_enable_transfer_complete_interrupt(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); #ifdef DMA_STREAM0 dma_set_transfer_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); dma_channel_select(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, USBUSART_DMA_TRG); dma_set_dma_flow_control(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); dma_enable_direct_mode(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); #else dma_set_read_from_memory(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); #endif /* Setup USART RX DMA */ dma_channel_reset(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); dma_set_peripheral_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uintptr_t)&USBUSART_RDR); dma_set_memory_address(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, (uintptr_t)aux_serial_receive_buffer); dma_set_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, AUX_UART_BUFFER_SIZE); dma_enable_memory_increment_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); dma_enable_circular_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); dma_set_peripheral_size(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_PSIZE_8BIT); dma_set_memory_size(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_MSIZE_8BIT); dma_set_priority(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_PL_HIGH); dma_enable_half_transfer_interrupt(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); dma_enable_transfer_complete_interrupt(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); #ifdef DMA_STREAM0 dma_set_transfer_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_SxCR_DIR_PERIPHERAL_TO_MEM); dma_channel_select(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, USBUSART_DMA_TRG); dma_set_dma_flow_control(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); dma_enable_direct_mode(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); #else dma_set_read_from_peripheral(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); #endif dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); /* Enable interrupts */ nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); #if defined(USBUSART_DMA_RXTX_IRQ) nvic_set_priority(USBUSART_DMA_RXTX_IRQ, IRQ_PRI_USBUSART_DMA); #else nvic_set_priority(USBUSART_DMA_TX_IRQ, IRQ_PRI_USBUSART_DMA); nvic_set_priority(USBUSART_DMA_RX_IRQ, IRQ_PRI_USBUSART_DMA); #endif nvic_enable_irq(USBUSART_IRQ); #if defined(USBUSART_DMA_RXTX_IRQ) nvic_enable_irq(USBUSART_DMA_RXTX_IRQ); #else nvic_enable_irq(USBUSART_DMA_TX_IRQ); nvic_enable_irq(USBUSART_DMA_RX_IRQ); #endif /* Finally enable the USART */ usart_enable(USBUSART); usart_enable_tx_dma(USBUSART); usart_enable_rx_dma(USBUSART); } #elif defined(LM4F) static char aux_serial_transmit_buffer[AUX_UART_BUFFER_SIZE]; void aux_serial_init(void) { UART_PIN_SETUP(); periph_clock_enable(USBUART_CLK); __asm__("nop"); __asm__("nop"); __asm__("nop"); uart_disable(USBUART); /* Setup UART parameters. */ uart_clock_from_sysclk(USBUART); uart_set_baudrate(USBUART, 38400); uart_set_databits(USBUART, 8); uart_set_stopbits(USBUART, 1); uart_set_parity(USBUART, UART_PARITY_NONE); // Enable FIFO uart_enable_fifo(USBUART); // Set FIFO interrupt trigger levels to 1/8 full for RX buffer and // 7/8 empty (1/8 full) for TX buffer uart_set_fifo_trigger_levels(USBUART, UART_FIFO_RX_TRIG_1_8, UART_FIFO_TX_TRIG_7_8); uart_clear_interrupt_flag(USBUART, UART_INT_RX | UART_INT_RT); /* Enable interrupts */ uart_enable_interrupts(UART0, UART_INT_RX| UART_INT_RT); /* Finally enable the USART. */ uart_enable(USBUART); //nvic_set_priority(USBUSART_IRQ, IRQ_PRI_USBUSART); nvic_enable_irq(USBUART_IRQ); } #endif void aux_serial_set_encoding(struct usb_cdc_line_coding *coding) { usart_set_baudrate(USBUSART, coding->dwDTERate); #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) if (coding->bParityType) usart_set_databits(USBUSART, (coding->bDataBits + 1 <= 8 ? 8 : 9)); else usart_set_databits(USBUSART, (coding->bDataBits <= 8 ? 8 : 9)); #elif defined(LM4F) uart_set_databits(USBUART, coding->bDataBits); #endif switch(coding->bCharFormat) { case 0: usart_set_stopbits(USBUSART, USART_STOPBITS_1); break; case 1: usart_set_stopbits(USBUSART, USART_STOPBITS_1_5); break; case 2: default: usart_set_stopbits(USBUSART, USART_STOPBITS_2); break; } switch(coding->bParityType) { case 0: usart_set_parity(USBUSART, USART_PARITY_NONE); break; case 1: usart_set_parity(USBUSART, USART_PARITY_ODD); break; case 2: default: usart_set_parity(USBUSART, USART_PARITY_EVEN); break; } } #if defined(STM32F0) || defined(STM32F1) || defined(STM32F3) || defined(STM32F4) /* * Update led state atomically respecting RX anb TX states. */ void usbuart_set_led_state(uint8_t ledn, bool state) { CM_ATOMIC_CONTEXT(); static uint8_t led_state = 0; if (state) { led_state |= ledn; gpio_set(LED_PORT_UART, LED_UART); } else { led_state &= ~ledn; if (!led_state) gpio_clear(LED_PORT_UART, LED_UART); } } char *aux_serial_current_transmit_buffer(void) { return aux_serial_transmit_buffer[aux_serial_transmit_buffer_index]; } size_t aux_serial_transmit_buffer_fullness(void) { return aux_serial_transmit_buffer_consumed; } /* * Changes USBUSART TX buffer in which data is accumulated from USB. * Filled buffer is submitted to DMA for transfer. */ void aux_serial_switch_transmit_buffers(void) { /* Make the buffer we've been filling the active DMA buffer, and swap to the other */ dma_set_memory_address(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, (uintptr_t)aux_serial_current_transmit_buffer()); dma_set_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, aux_serial_transmit_buffer_consumed); dma_enable_channel(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN); /* Change active buffer */ aux_serial_transmit_buffer_consumed = 0; aux_serial_transmit_buffer_index ^= 1; } void aux_serial_send(const size_t len) { aux_serial_transmit_buffer_consumed += len; /* If DMA is idle, schedule new transfer */ if (len && aux_serial_transmit_complete) { aux_serial_transmit_complete = false; aux_serial_switch_transmit_buffers(); /* Enable LED */ usbuart_set_led_state(TX_LED_ACT, true); } } void aux_serial_update_receive_buffer_fullness(void) { aux_serial_receive_write_index = AUX_UART_BUFFER_SIZE - dma_get_number_of_data(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN); aux_serial_receive_write_index %= AUX_UART_BUFFER_SIZE; } bool aux_serial_receive_buffer_empty(void) { return aux_serial_receive_write_index == aux_serial_receive_read_index; } void aux_serial_drain_receive_buffer(void) { aux_serial_receive_read_index = aux_serial_receive_write_index; /* Turn off LED */ usbuart_set_led_state(RX_LED_ACT, false); } void aux_serial_stage_receive_buffer(void) { aux_serial_receive_read_index = debug_serial_fifo_send( aux_serial_receive_buffer, aux_serial_receive_read_index, aux_serial_receive_write_index); } static void aux_serial_receive_isr(const uint32_t usart, const uint8_t dma_irq) { nvic_disable_irq(dma_irq); /* Get IDLE flag and reset interrupt flags */ const bool is_idle = usart_get_flag(usart, USART_FLAG_IDLE); usart_recv(usart); /* If line is now idle, then transmit a packet */ if (is_idle) { #ifdef USART_ICR USART_ICR(usart) = USART_ICR_IDLECF; #endif debug_uart_run(); } nvic_enable_irq(dma_irq); } static void aux_serial_dma_transmit_isr(const uint8_t dma_tx_channel) { nvic_disable_irq(USB_IRQ); /* Stop DMA */ dma_disable_channel(USBUSART_DMA_BUS, dma_tx_channel); dma_clear_interrupt_flags(USBUSART_DMA_BUS, dma_tx_channel, DMA_CGIF); /* * If a new buffer is ready, continue transmission. * Otherwise we report the transfer has completed. */ if (aux_serial_transmit_buffer_fullness()) { aux_serial_switch_transmit_buffers(); usbd_ep_nak_set(usbdev, CDCACM_UART_ENDPOINT, 0); } else { usbuart_set_led_state(TX_LED_ACT, false); aux_serial_transmit_complete = true; } nvic_enable_irq(USB_IRQ); } static void aux_serial_dma_receive_isr(const uint8_t usart_irq, const uint8_t dma_rx_channel) { nvic_disable_irq(usart_irq); /* Clear flags and transmit a packet*/ dma_clear_interrupt_flags(USBUSART_DMA_BUS, dma_rx_channel, DMA_CGIF); debug_uart_run(); nvic_enable_irq(usart_irq); } #if defined(USBUSART_ISR) void USBUSART_ISR(void) { #if defined(USBUSART_DMA_RXTX_IRQ) aux_serial_receive_isr(USBUSART, USBUSART_DMA_RXTX_IRQ); #else aux_serial_receive_isr(USBUSART, USBUSART_DMA_RX_IRQ); #endif } #endif #if defined(USBUSART1_ISR) void USBUSART1_ISR(void) { #if defined(USBUSART1_DMA_RXTX_IRQ) aux_serial_receive_isr(USBUSART1, USBUSART1_DMA_RXTX_IRQ); #else aux_serial_receive_isr(USBUSART1, USBUSART1_DMA_RX_IRQ); #endif } #endif #if defined(USBUSART2_ISR) void USBUSART2_ISR(void) { #if defined(USBUSART2_DMA_RXTX_IRQ) aux_serial_receive_isr(USBUSART2, USBUSART2_DMA_RXTX_IRQ); #else aux_serial_receive_isr(USBUSART2, USBUSART2_DMA_RX_IRQ); #endif } #endif #if defined(USBUSART_DMA_TX_ISR) void USBUSART_DMA_TX_ISR(void) { aux_serial_dma_transmit_isr(USBUSART_DMA_TX_CHAN); } #endif #if defined(USBUSART1_DMA_TX_ISR) void USBUSART1_DMA_TX_ISR(void) { aux_serial_dma_transmit_isr(USBUSART1_DMA_TX_CHAN); } #endif #if defined(USBUSART2_DMA_TX_ISR) void USBUSART2_DMA_TX_ISR(void) { aux_serial_dma_transmit_isr(USBUSART2_DMA_TX_CHAN); } #endif #if defined(USBUSART_DMA_RX_ISR) void USBUSART_DMA_RX_ISR(void) { aux_serial_dma_receive_isr(USBUSART_IRQ, USBUSART_DMA_RX_CHAN); } #endif #if defined(USBUSART1_DMA_RX_ISR) void USBUSART1_DMA_RX_ISR(void) { aux_serial_dma_receive_isr(USBUSART1_IRQ, USBUSART1_DMA_RX_CHAN); } #endif #if defined(USBUSART2_DMA_RX_ISR) void USBUSART2_DMA_RX_ISR(void) { aux_serial_dma_receive_isr(USBUSART2_IRQ, USBUSART2_DMA_RX_CHAN); } #endif #if defined(USBUSART_DMA_RXTX_ISR) void USBUSART_DMA_RXTX_ISR(void) { if (dma_get_interrupt_flag(USBUSART_DMA_BUS, USBUSART_DMA_RX_CHAN, DMA_CGIF)) USBUSART_DMA_RX_ISR(); if (dma_get_interrupt_flag(USBUSART_DMA_BUS, USBUSART_DMA_TX_CHAN, DMA_CGIF)) USBUSART_DMA_TX_ISR(); } #endif #elif defined(LM4F) char *aux_serial_current_transmit_buffer(void) { return aux_serial_transmit_buffer; } size_t aux_serial_transmit_buffer_fullness(void) { return 0; } void aux_serial_send(const size_t len) { for(size_t i = 0; i < len; ++i) uart_send_blocking(USBUART, aux_serial_transmit_buffer[i]); } /* * Read a character from the UART RX and stuff it in a software FIFO. * Allowed to read from FIFO out pointer, but not write to it. * Allowed to write to FIFO in pointer. */ void USBUART_ISR(void) { bool flush = uart_is_interrupt_source(USBUART, UART_INT_RT); while (!uart_is_rx_fifo_empty(USBUART)) { const char c = uart_recv(USBUART); /* If the next increment of rx_in would put it at the same point * as rx_out, the FIFO is considered full. */ if (((aux_serial_receive_write_index + 1) % AUX_UART_BUFFER_SIZE) != aux_serial_receive_read_index) { /* insert into FIFO */ aux_serial_receive_buffer[aux_serial_receive_write_index++] = c; /* wrap out pointer */ if (aux_serial_receive_write_index >= AUX_UART_BUFFER_SIZE) aux_serial_receive_write_index = 0; } else flush = true; } if (flush) { /* forcibly empty fifo if no USB endpoint */ if (usb_get_config() != 1) { aux_serial_receive_read_index = aux_serial_receive_write_index; return; } char packet_buf[CDCACM_PACKET_SIZE]; uint8_t packet_size = 0; uint8_t buf_out = aux_serial_receive_read_index; /* copy from uart FIFO into local usb packet buffer */ while (aux_serial_receive_write_index != buf_out && packet_size < CDCACM_PACKET_SIZE) { packet_buf[packet_size++] = aux_serial_receive_buffer[buf_out++]; /* wrap out pointer */ if (buf_out >= AUX_UART_BUFFER_SIZE) buf_out = 0; } /* advance fifo out pointer by amount written */ aux_serial_receive_read_index += usbd_ep_write_packet(usbdev, CDCACM_UART_ENDPOINT, packet_buf, packet_size); aux_serial_receive_read_index %= AUX_UART_BUFFER_SIZE; } } #endif