Platform drivers for Mbed.

Dependents:   EVAL-CN0535-FMCZ EVAL-CN0535-FMCZ EVAL-AD568x-AD569x EVAL-AD7606 ... more

src/uart.cpp

Committer:
mahphalke
Date:
2021-03-19
Revision:
16:61ad39564f45
Parent:
15:fd2c3c3038bf

File content as of revision 16:61ad39564f45:

/***************************************************************************//**
 * @file  uart.cpp
 * @brief Implementation of UART No-OS platform driver interfaces
********************************************************************************
 * Copyright (c) 2020 Analog Devices, Inc.
 *
 * All rights reserved.
 *
 * This software is proprietary to Analog Devices, Inc. and its licensors.
 * By using this software you agree to the terms of the associated
 * Analog Devices Software License Agreement.
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdio.h>
#include <mbed.h>
#include <USBCDC.h>

#include "platform_drivers.h"
#include "uart_extra.h"

/******************************************************************************/
/************************ Macros/Constants ************************************/
/******************************************************************************/

/* Max size for USB CDC packet during transmit/receive */
#define USB_CDC_MAX_PACKET_SIZE		64

/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/

/* Derived USBCDC class to access protected members of USBCDC class */
class platform_usbcdc :public USBCDC
{
public:
	/* Call parent class (USBCDC) constructor explicitly */
	platform_usbcdc(bool connect_blocking, uint16_t vendor_id, uint16_t product_id,
			uint16_t product_release)
		: USBCDC(connect_blocking, vendor_id, product_id, product_release)
	{

	}

public:
	void change_terminal_connected(bool connected);
	bool data_available(uint32_t rx_size);
	bool data_transmited(void);
};

/******************************************************************************/
/************************ Functions Definitions *******************************/
/******************************************************************************/

/**
 * @brief Change the terminal connection status (for non-terminal) based USB interface
 * @param status- new connection status
 * @note  This functions is used to change the terminal connection status of USB client
 *        interface which is different than the 'console terminal'. The console terminals acknowledge
 *        back to USB host when USB connection is opened on the console terminal and Mbed USBCDC
 *        class automatically changes the '_terminal_connected' status accordingly. However, for
 *        custom PC applications (non terminal), the terminal connection status needs to be changed
 *        manually. The '_terminal_connected' is protected member of USBCDC parent class and thus can
 *        be accessed through 'platform_usbcdc' derived class using below function.
 */
void platform_usbcdc::change_terminal_connected(bool status)
{
	_terminal_connected = status;
}


/**
 * @brief  Check if new USB data is available
 * @return true if new data available, else false
 */
bool platform_usbcdc::data_available(uint32_t rx_size)
{
	uint32_t size = 0;

	if (!_rx_in_progress) {
		size = _rx_size > 0xFF ? 0xFF : _rx_size;
	}

	if (size >= rx_size)
		return true;
	else
		return false;
}


/**
 * @brief  Check if old USB data is transmitted
 * @return true if transmit not in progress, else false
 */
bool platform_usbcdc::data_transmited(void)
{
	if (_tx_in_progress)
		return false;
	else
		return true;
}


/**
 * @brief Read data from UART device.
 * @param desc - Instance of UART.
 * @param data - Pointer to buffer containing data.
 * @param bytes_number - Number of bytes to read.
 * @return SUCCESS in case of success, FAILURE otherwise.
 */
int32_t uart_read(struct uart_desc *desc, uint8_t *data, uint32_t bytes_number)
{
	uint8_t cnt;
	mbed::BufferedSerial *uart;		// pointer to BufferedSerial/UART instance
	platform_usbcdc *usb_cdc_dev;	// Pointer to usb cdc device class instance
	uint32_t size_rd;

	if (desc && data) {
		if (((mbed_uart_desc *)(desc->extra))->uart_port) {
			if (((mbed_uart_desc *)desc->extra)->virtual_com_enable) {
				usb_cdc_dev = (platform_usbcdc *)((mbed_uart_desc *)(
						desc->extra))->uart_port;

				while (!usb_cdc_dev->data_available(bytes_number)) {
					/* Wait until new data is available */
				}

				/* Change terminal connection status manually */
				usb_cdc_dev->change_terminal_connected(true);

				usb_cdc_dev->receive_nb(data, bytes_number, &size_rd);
			} else {
				uart = (BufferedSerial *)(((mbed_uart_desc *)(desc->extra))->uart_port);

				for (cnt = 0; cnt < bytes_number; cnt++) {
					uart->read(data + cnt, 1);
				}
			}

			return bytes_number;
		}
	}

	return FAILURE;
}


/**
 * @brief Write data to UART device.
 * @param desc - Instance of UART.
 * @param data - Pointer to buffer containing data.
 * @param bytes_number - Number of bytes to read.
 * @return SUCCESS in case of success, FAILURE otherwise.
 */
int32_t uart_write(struct uart_desc *desc, const uint8_t *data,
		   uint32_t bytes_number)
{
	mbed::BufferedSerial *uart;		// pointer to BufferedSerial/UART instance
	platform_usbcdc *usb_cdc_dev;	// Pointer to usb cdc device class instance
	uint32_t d_sz;
	uint32_t indx = 0;

	if (desc && data) {
		if (((mbed_uart_desc *)(desc->extra))->uart_port) {
			if (((mbed_uart_desc *)desc->extra)->virtual_com_enable) {
				usb_cdc_dev = (platform_usbcdc *)((mbed_uart_desc *)(
						desc->extra))->uart_port;

				while (bytes_number) {
					while (!usb_cdc_dev->data_transmited()) {
						/* Wait until old data is transmitted */
					}

					/* Make sure packet size is less than max CDC packet size during data transmit */
					d_sz = (bytes_number > (USB_CDC_MAX_PACKET_SIZE - 1)) ?
					       (USB_CDC_MAX_PACKET_SIZE - 1) :
					       bytes_number;

					/* Change terminal connection status manually */
					usb_cdc_dev->change_terminal_connected(true);

					usb_cdc_dev->send_nb((uint8_t *)&data[indx], d_sz, &d_sz);

					bytes_number -= d_sz;
					indx += d_sz;
				}

				return bytes_number;
			} else {
				uart = (BufferedSerial *)(((mbed_uart_desc *)(desc->extra))->uart_port);
				return uart->write(data, bytes_number);
			}
		}
	}

	return FAILURE;
}


/**
 * @brief Submit reading buffer to the UART driver.
 *
 * Buffer is used until bytes_number bytes are read.
 * @param desc:	Descriptor of the UART device
 * @param data:	Buffer where data will be read
 * @param bytes_number:	Number of bytes to be read.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t uart_read_nonblocking(struct uart_desc *desc,
			      uint8_t *data,
			      uint32_t bytes_number)
{
	mbed::BufferedSerial *uart;		// pointer to BufferedSerial/UART instance

	if (desc) {
		if (((mbed_uart_desc *)(desc->extra))->uart_port) {
			uart = (BufferedSerial *)(((mbed_uart_desc *)(desc->extra))->uart_port);

			if (data) {
				for (size_t i = 0; i < bytes_number; i++) {
					if (uart->readable() > 0) {
						uart->read(&data[i], 1);
					}
				}

				return bytes_number;
			}
		}
	}

	return FAILURE;
}


/**
 * @brief Submit writting buffer to the UART driver.
 *
 * Data from the buffer is sent over the UART, the function returns imediatly.
 * @param desc:	Descriptor of the UART device
 * @param data:	Buffer where data will be written
 * @param bytes_number:	Number of bytes to be written.
 * @return \ref SUCCESS in case of success, \ref FAILURE otherwise.
 */
int32_t uart_write_nonblocking(struct uart_desc *desc,
			       const uint8_t *data,
			       uint32_t bytes_number)
{
	mbed::BufferedSerial *uart;		// pointer to BufferedSerial/UART instance

	if (desc) {
		if (((mbed_uart_desc *)(desc->extra))->uart_port) {
			uart = (BufferedSerial *)(((mbed_uart_desc *)(desc->extra))->uart_port);

			if (data) {
				for (size_t i = 0; i < bytes_number; i++) {
					uart->write(&data[i], 1);
				}

				return bytes_number;
			}
		}
	}

	return FAILURE;
}


/**
 * @brief Initialize the UART communication peripheral.
 * @param desc - The UART descriptor.
 * @param param - The structure that contains the UART parameters.
 * @return SUCCESS in case of success, FAILURE otherwise.
 */
int32_t uart_init(struct uart_desc **desc, struct uart_init_param *param)
{
	mbed::BufferedSerial *uart;		// Pointer to new BufferedSerial/UART instance
	platform_usbcdc *usb_cdc_dev;	// Pointer to usb cdc device class instance
	mbed_uart_desc *mbed_desc; 		// Pointer to mbed uart descriptor

	if (desc && param) {
		// Create the UART description object for the device
		uart_desc *new_desc = (uart_desc *)malloc(sizeof(uart_desc));
		if (new_desc == NULL) {
			return FAILURE;
		}

		new_desc->baud_rate = param->baud_rate;

		if (((mbed_uart_init_param *)param->extra)->virtual_com_enable) {
			// Create a new instance of platform_usbcdc class
			usb_cdc_dev = new platform_usbcdc(false,
							  ((mbed_uart_init_param *)param->extra)->vendor_id,
							  ((mbed_uart_init_param *)param->extra)->product_id,
							  ((mbed_uart_init_param *)param->extra)->product_release);
			if (!usb_cdc_dev)
				return FAILURE;
		} else {
			// Create and configure a new instance of BufferedSerial/UART port
			uart = new BufferedSerial(
				(PinName)(((mbed_uart_init_param *)param->extra)->uart_tx_pin),
				(PinName)(((mbed_uart_init_param *)param->extra)->uart_rx_pin),
				(int)param->baud_rate);

			if (!uart)
				return FAILURE;
		}

		// Create a new mbed descriptor to store new UART instances
		mbed_desc = (mbed_uart_desc *)malloc(sizeof(mbed_uart_desc));
		if (mbed_desc == NULL) {
			return FAILURE;
		}

		if (((mbed_uart_init_param *)param->extra)->virtual_com_enable) {
			mbed_desc->uart_port = (platform_usbcdc *)usb_cdc_dev;

			/* Establish connection with the USB CDC communication port */
			usb_cdc_dev->connect();
			mdelay(2000);

			/* Change terminal connection status manually */
			usb_cdc_dev->change_terminal_connected(true);
		} else {
			mbed_desc->uart_port = (BufferedSerial *)uart;
		}

		mbed_desc->virtual_com_enable = ((mbed_uart_init_param *)param->extra)->virtual_com_enable;
		new_desc->extra = (mbed_uart_desc *)mbed_desc;
		*desc = new_desc;

		return SUCCESS;
	}

	return FAILURE;
}


/**
 * @brief Free the resources allocated by uart_init().
 * @param desc - The UART descriptor.
 * @return SUCCESS in case of success, FAILURE otherwise.
 */
int32_t uart_remove(struct uart_desc *desc)
{
	if (desc) {
		// Free the UART port object
		if (((mbed_uart_desc *)desc->extra)->virtual_com_enable) {
			if ((platform_usbcdc *)((mbed_uart_desc *)desc->extra)->uart_port)
				delete((platform_usbcdc *)(platform_usbcdc *)((mbed_uart_desc *)
						desc->extra)->uart_port);
		} else {
			if ((BufferedSerial *)(((mbed_uart_desc *)(desc->extra))->uart_port)) {
				delete((BufferedSerial *)(((mbed_uart_desc *)(desc->extra))->uart_port));
			}
		}

		// Free the UART extra descriptor object
		if ((mbed_uart_desc *)(desc->extra)) {
			free((mbed_uart_desc *)(desc->extra));
		}

		// Free the UART descriptor object
		free(desc);

		return SUCCESS;
	}

	return FAILURE;
}


/**
 * @brief Get number of UART errors.
 * @param desc - The UART descriptor.
 * @return number of errors.
 */
uint32_t uart_get_errors(struct uart_desc *desc)
{
	if (desc) {
		// Unused variable - fix compiler warning
	}

	return SUCCESS;
}