mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-02 10:24:11 +01:00
b56f5206bd
CDC and USART device drivers were not all clearing their device structs before using them. This specifically caused crashes in the case where the upper COM layer was binding only a Tx path. The Rx path callback in the lower driver was uninitialized random data and would result in the lower driver faulting when it tried to call the callback. Conflicts: flight/PiOS/STM32F30x/pios_usart.c flight/PiOS/STM32F30x/pios_usb_cdc.c flight/PiOS/STM32F30x/pios_usb_hid.c
357 lines
10 KiB
C
357 lines
10 KiB
C
/**
|
|
******************************************************************************
|
|
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
|
* @{
|
|
* @addtogroup PIOS_USART USART Functions
|
|
* @brief PIOS interface for USART port
|
|
* @{
|
|
*
|
|
* @file pios_usart.c
|
|
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
|
|
* @brief USART commands. Inits USARTs, controls USARTs & Interupt handlers. (STM32 dependent)
|
|
* @see The GNU Public License (GPL) Version 3
|
|
*
|
|
*****************************************************************************/
|
|
/*
|
|
* 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, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* @todo This is virtually identical to the F1xx driver and should be merged.
|
|
*/
|
|
|
|
#include "pios.h"
|
|
|
|
#ifdef PIOS_INCLUDE_USART
|
|
|
|
#include <pios_usart_priv.h>
|
|
|
|
/* Provide a COM driver */
|
|
static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud);
|
|
static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context);
|
|
static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context);
|
|
static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail);
|
|
static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail);
|
|
|
|
const struct pios_com_driver pios_usart_com_driver = {
|
|
.set_baud = PIOS_USART_ChangeBaud,
|
|
.tx_start = PIOS_USART_TxStart,
|
|
.rx_start = PIOS_USART_RxStart,
|
|
.bind_tx_cb = PIOS_USART_RegisterTxCallback,
|
|
.bind_rx_cb = PIOS_USART_RegisterRxCallback,
|
|
};
|
|
|
|
enum pios_usart_dev_magic {
|
|
PIOS_USART_DEV_MAGIC = 0x4152834A,
|
|
};
|
|
|
|
struct pios_usart_dev {
|
|
enum pios_usart_dev_magic magic;
|
|
const struct pios_usart_cfg * cfg;
|
|
|
|
pios_com_callback rx_in_cb;
|
|
uint32_t rx_in_context;
|
|
pios_com_callback tx_out_cb;
|
|
uint32_t tx_out_context;
|
|
};
|
|
|
|
static bool PIOS_USART_validate(struct pios_usart_dev * usart_dev)
|
|
{
|
|
return (usart_dev->magic == PIOS_USART_DEV_MAGIC);
|
|
}
|
|
|
|
#if defined(PIOS_INCLUDE_FREERTOS)
|
|
static struct pios_usart_dev * PIOS_USART_alloc(void)
|
|
{
|
|
struct pios_usart_dev * usart_dev;
|
|
|
|
usart_dev = (struct pios_usart_dev *)pvPortMalloc(sizeof(*usart_dev));
|
|
if (!usart_dev) return(NULL);
|
|
|
|
memset(usart_dev, 0, sizeof(*usart_dev));
|
|
usart_dev->magic = PIOS_USART_DEV_MAGIC;
|
|
return(usart_dev);
|
|
}
|
|
#else
|
|
static struct pios_usart_dev pios_usart_devs[PIOS_USART_MAX_DEVS];
|
|
static uint8_t pios_usart_num_devs;
|
|
static struct pios_usart_dev * PIOS_USART_alloc(void)
|
|
{
|
|
struct pios_usart_dev * usart_dev;
|
|
|
|
if (pios_usart_num_devs >= PIOS_USART_MAX_DEVS) {
|
|
return (NULL);
|
|
}
|
|
|
|
usart_dev = &pios_usart_devs[pios_usart_num_devs++];
|
|
|
|
memset(usart_dev, 0, sizeof(*usart_dev));
|
|
usart_dev->magic = PIOS_USART_DEV_MAGIC;
|
|
|
|
return (usart_dev);
|
|
}
|
|
#endif
|
|
|
|
/* Bind Interrupt Handlers
|
|
*
|
|
* Map all valid USART IRQs to the common interrupt handler
|
|
* and provide storage for a 32-bit device id IRQ to map
|
|
* each physical IRQ to a specific registered device instance.
|
|
*/
|
|
static void PIOS_USART_generic_irq_handler(uint32_t usart_id);
|
|
|
|
static uint32_t PIOS_USART_1_id;
|
|
void USART1_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_1_irq_handler")));
|
|
static void PIOS_USART_1_irq_handler (void)
|
|
{
|
|
PIOS_USART_generic_irq_handler (PIOS_USART_1_id);
|
|
}
|
|
|
|
static uint32_t PIOS_USART_2_id;
|
|
void USART2_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_2_irq_handler")));
|
|
static void PIOS_USART_2_irq_handler (void)
|
|
{
|
|
PIOS_USART_generic_irq_handler (PIOS_USART_2_id);
|
|
}
|
|
|
|
static uint32_t PIOS_USART_3_id;
|
|
void USART3_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_3_irq_handler")));
|
|
static void PIOS_USART_3_irq_handler (void)
|
|
{
|
|
PIOS_USART_generic_irq_handler (PIOS_USART_3_id);
|
|
}
|
|
|
|
static uint32_t PIOS_USART_4_id;
|
|
void USART4_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_4_irq_handler")));
|
|
static void PIOS_USART_4_irq_handler (void)
|
|
{
|
|
PIOS_USART_generic_irq_handler (PIOS_USART_4_id);
|
|
}
|
|
|
|
static uint32_t PIOS_USART_5_id;
|
|
void USART5_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_5_irq_handler")));
|
|
static void PIOS_USART_5_irq_handler (void)
|
|
{
|
|
PIOS_USART_generic_irq_handler (PIOS_USART_5_id);
|
|
}
|
|
|
|
static uint32_t PIOS_USART_6_id;
|
|
void USART6_IRQHandler(void) __attribute__ ((alias ("PIOS_USART_6_irq_handler")));
|
|
static void PIOS_USART_6_irq_handler (void)
|
|
{
|
|
PIOS_USART_generic_irq_handler (PIOS_USART_6_id);
|
|
}
|
|
|
|
/**
|
|
* Initialise a single USART device
|
|
*/
|
|
int32_t PIOS_USART_Init(uint32_t * usart_id, const struct pios_usart_cfg * cfg)
|
|
{
|
|
PIOS_DEBUG_Assert(usart_id);
|
|
PIOS_DEBUG_Assert(cfg);
|
|
|
|
struct pios_usart_dev * usart_dev;
|
|
|
|
usart_dev = (struct pios_usart_dev *) PIOS_USART_alloc();
|
|
if (!usart_dev) goto out_fail;
|
|
|
|
/* Bind the configuration to the device instance */
|
|
usart_dev->cfg = cfg;
|
|
|
|
/* Map pins to USART function */
|
|
/* note __builtin_ctz() due to the difference between GPIO_PinX and GPIO_PinSourceX */
|
|
if (usart_dev->cfg->remap) {
|
|
GPIO_PinAFConfig(usart_dev->cfg->rx.gpio,
|
|
__builtin_ctz(usart_dev->cfg->rx.init.GPIO_Pin),
|
|
usart_dev->cfg->remap);
|
|
GPIO_PinAFConfig(usart_dev->cfg->tx.gpio,
|
|
__builtin_ctz(usart_dev->cfg->tx.init.GPIO_Pin),
|
|
usart_dev->cfg->remap);
|
|
}
|
|
|
|
/* Initialize the USART Rx and Tx pins */
|
|
GPIO_Init(usart_dev->cfg->rx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->rx.init);
|
|
GPIO_Init(usart_dev->cfg->tx.gpio, (GPIO_InitTypeDef *)&usart_dev->cfg->tx.init);
|
|
|
|
/* Configure the USART */
|
|
USART_Init(usart_dev->cfg->regs, (USART_InitTypeDef *)&usart_dev->cfg->init);
|
|
|
|
*usart_id = (uint32_t)usart_dev;
|
|
|
|
/* Configure USART Interrupts */
|
|
switch ((uint32_t)usart_dev->cfg->regs) {
|
|
case (uint32_t)USART1:
|
|
PIOS_USART_1_id = (uint32_t)usart_dev;
|
|
break;
|
|
case (uint32_t)USART2:
|
|
PIOS_USART_2_id = (uint32_t)usart_dev;
|
|
break;
|
|
case (uint32_t)USART3:
|
|
PIOS_USART_3_id = (uint32_t)usart_dev;
|
|
break;
|
|
case (uint32_t)UART4:
|
|
PIOS_USART_4_id = (uint32_t)usart_dev;
|
|
break;
|
|
case (uint32_t)UART5:
|
|
PIOS_USART_5_id = (uint32_t)usart_dev;
|
|
break;
|
|
case (uint32_t)USART6:
|
|
PIOS_USART_6_id = (uint32_t)usart_dev;
|
|
break;
|
|
}
|
|
NVIC_Init((NVIC_InitTypeDef *)&(usart_dev->cfg->irq.init));
|
|
USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
|
|
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
|
|
|
|
// FIXME XXX Clear / reset uart here - sends NUL char else
|
|
|
|
/* Enable USART */
|
|
USART_Cmd(usart_dev->cfg->regs, ENABLE);
|
|
|
|
return(0);
|
|
|
|
out_fail:
|
|
return(-1);
|
|
}
|
|
|
|
static void PIOS_USART_RxStart(uint32_t usart_id, __attribute__((unused)) uint16_t rx_bytes_avail)
|
|
{
|
|
struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id;
|
|
|
|
bool valid = PIOS_USART_validate(usart_dev);
|
|
PIOS_Assert(valid);
|
|
|
|
USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
|
|
}
|
|
static void PIOS_USART_TxStart(uint32_t usart_id, __attribute__((unused)) uint16_t tx_bytes_avail)
|
|
{
|
|
struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id;
|
|
|
|
bool valid = PIOS_USART_validate(usart_dev);
|
|
PIOS_Assert(valid);
|
|
|
|
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
|
|
}
|
|
|
|
/**
|
|
* Changes the baud rate of the USART peripheral without re-initialising.
|
|
* \param[in] usart_id USART name (GPS, TELEM, AUX)
|
|
* \param[in] baud Requested baud rate
|
|
*/
|
|
static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud)
|
|
{
|
|
struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id;
|
|
|
|
bool valid = PIOS_USART_validate(usart_dev);
|
|
PIOS_Assert(valid);
|
|
|
|
USART_InitTypeDef USART_InitStructure;
|
|
|
|
/* Start with a copy of the default configuration for the peripheral */
|
|
USART_InitStructure = usart_dev->cfg->init;
|
|
|
|
/* Adjust the baud rate */
|
|
USART_InitStructure.USART_BaudRate = baud;
|
|
|
|
/* Write back the new configuration */
|
|
USART_Init(usart_dev->cfg->regs, &USART_InitStructure);
|
|
}
|
|
|
|
static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context)
|
|
{
|
|
struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id;
|
|
|
|
bool valid = PIOS_USART_validate(usart_dev);
|
|
PIOS_Assert(valid);
|
|
|
|
/*
|
|
* Order is important in these assignments since ISR uses _cb
|
|
* field to determine if it's ok to dereference _cb and _context
|
|
*/
|
|
usart_dev->rx_in_context = context;
|
|
usart_dev->rx_in_cb = rx_in_cb;
|
|
}
|
|
|
|
static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context)
|
|
{
|
|
struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id;
|
|
|
|
bool valid = PIOS_USART_validate(usart_dev);
|
|
PIOS_Assert(valid);
|
|
|
|
/*
|
|
* Order is important in these assignments since ISR uses _cb
|
|
* field to determine if it's ok to dereference _cb and _context
|
|
*/
|
|
usart_dev->tx_out_context = context;
|
|
usart_dev->tx_out_cb = tx_out_cb;
|
|
}
|
|
|
|
static void PIOS_USART_generic_irq_handler(uint32_t usart_id)
|
|
{
|
|
struct pios_usart_dev * usart_dev = (struct pios_usart_dev *)usart_id;
|
|
|
|
bool valid = PIOS_USART_validate(usart_dev);
|
|
PIOS_Assert(valid);
|
|
|
|
/* Force read of dr after sr to make sure to clear error flags */
|
|
volatile uint16_t sr = usart_dev->cfg->regs->SR;
|
|
volatile uint8_t dr = usart_dev->cfg->regs->DR;
|
|
|
|
/* Check if RXNE flag is set */
|
|
bool rx_need_yield = false;
|
|
if (sr & USART_SR_RXNE) {
|
|
uint8_t byte = dr;
|
|
if (usart_dev->rx_in_cb) {
|
|
(void) (usart_dev->rx_in_cb)(usart_dev->rx_in_context, &byte, 1, NULL, &rx_need_yield);
|
|
}
|
|
}
|
|
|
|
/* Check if TXE flag is set */
|
|
bool tx_need_yield = false;
|
|
if (sr & USART_SR_TXE) {
|
|
if (usart_dev->tx_out_cb) {
|
|
uint8_t b;
|
|
uint16_t bytes_to_send;
|
|
|
|
bytes_to_send = (usart_dev->tx_out_cb)(usart_dev->tx_out_context, &b, 1, NULL, &tx_need_yield);
|
|
|
|
if (bytes_to_send > 0) {
|
|
/* Send the byte we've been given */
|
|
usart_dev->cfg->regs->DR = b;
|
|
} else {
|
|
/* No bytes to send, disable TXE interrupt */
|
|
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
|
|
}
|
|
} else {
|
|
/* No bytes to send, disable TXE interrupt */
|
|
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
|
|
}
|
|
}
|
|
|
|
#if defined(PIOS_INCLUDE_FREERTOS)
|
|
if (rx_need_yield || tx_need_yield) {
|
|
vPortYieldFromISR();
|
|
}
|
|
#endif /* PIOS_INCLUDE_FREERTOS */
|
|
}
|
|
|
|
#endif /* PIOS_INCLUDE_USART */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|