📄 cal_avalon_uart.c
字号:
/******************************************************************************
* *
* License Agreement *
* *
* Copyright (c) 2003 Altera Corporation, San Jose, California, USA. *
* All rights reserved. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
* *
* This agreement shall be governed in all respects by the laws of the State *
* of California and by the laws of the United States of America. *
* *
******************************************************************************/
#include <fcntl.h>
#include "sys/alt_dev.h"
#include "sys/alt_irq.h"
#include "sys/ioctl.h"
#include "sys/alt_errno.h"
#include "cal_avalon_uart.h"
#include "cal_avalon_uart_regs.h"
/*
* CAL_AVALON_UART_READ_RDY and CAL_AVALON_UART_WRITE_RDY are the bitmasks
* that define uC/OS-II event flags that are releated to this device.
*
* CAL_AVALON_UART_READY_RDY indicates that there is read data in the buffer
* ready to be processed. ALT_UART_WRITE_RDY indicates that the transmitter is
* ready for more data.
*/
#define ALT_UART_READ_RDY 0x1
#define ALT_UART_WRITE_RDY 0x2
#if defined ALT_USE_SMALL_DRIVERS || CAL_AVALON_UART_SMALL
/*
* Implementation of the callback functions used for the
* "small", i.e. polled mode, version of the altera_avalon_uart device
* driver.
*/
/*
* cal_avalon_uart_read() is called by the system read() function in order to
* read a block of data from the UART. "len" is the maximum length of the data
* to read, and "ptr" indicates the destination address. "fd" is the file
* descriptor for the device to be read from.
*
* Permission checks are made before the call to cal_avalon_uart_read(), so we
* know that the file descriptor has been opened with the correct permissions
* for this operation.
*
* The return value is the number of bytes actually read.
*
* This implementation polls the device waiting for characters. At most it can
* only return one character, regardless of how many are requested. If the
* device is being accessed in non-blocking mode then it is possible for this
* function to return without reading any characters. In this case errno is
* set to EWOULDBLOCK.
*/
int cal_avalon_uart_read (alt_fd* fd, char* ptr, int len)
{
int block;
unsigned int status;
cal_avalon_uart_dev* dev = (cal_avalon_uart_dev*) fd->dev;
block = !(fd->fd_flags & O_NONBLOCK);
do
{
status = IORD_CAL_AVALON_UART_STATUS(dev->base);
/* clear any error flags */
IOWR_CAL_AVALON_UART_STATUS(dev->base, 0);
if (status & CAL_AVALON_UART_CONTROL_RRDY_MSK)
{
ptr[0] = IORD_CAL_AVALON_UART_RXDATA(dev->base);
if (!(status & (CAL_AVALON_UART_STATUS_PE_MSK |
CAL_AVALON_UART_STATUS_FE_MSK)))
{
return 1;
}
}
}
while (block);
ALT_ERRNO = EWOULDBLOCK;
return 0;
}
/*
* cal_avalon_uart_write() is called by the system write() function in order to
* write a block of data to the UART. "len" is the length of the data to write,
* and "ptr" indicates the source address. "fd" is the file descriptor for the
* device to be read from.
*
* Permission checks are made before the call to cal_avalon_uart_write(), so we
* know that the file descriptor has been opened with the correct permissions
* for this operation.
*
* The return value is the number of bytes actually written.
*
* This function will block on the devices transmit register, until all
* characters have been transmitted. This is unless the device is being
* accessed in non-blocking mode. In this case this function will return as
* soon as the device reports that it is not ready to transmit.
*
* Since this is the small footprint version of the UART driver, the value of
* CTS is ignored.
*/
int cal_avalon_uart_write (alt_fd* fd, const char* ptr, int len)
{
int block;
unsigned int status;
int count;
cal_avalon_uart_dev* dev = (cal_avalon_uart_dev*) fd->dev;
block = !(fd->fd_flags & O_NONBLOCK);
count = len;
do
{
status = IORD_CAL_AVALON_UART_STATUS(dev->base);
if (status & CAL_AVALON_UART_STATUS_TRDY_MSK)
{
IOWR_CAL_AVALON_UART_TXDATA(dev->base, *ptr++);
count--;
}
}
while (block && count);
if (count)
{
ALT_ERRNO = EWOULDBLOCK;
}
return (len - count);
}
#else /* Using the "fast" version of the driver */
/*
* Implementation of the callback functions used for the
* "fast", i.e. interrupt driven, version of the altera_avalon_uart device
* driver.
*/
/*
* cal_avalon_uart_read() is called by the system read() function in order to
* read a block of data from the UART. "len" is the maximum length of the data
* to read, and "ptr" indicates the destination address. "fd" is the file
* descriptor for the device to be read from.
*
* Permission checks are made before the call to cal_avalon_uart_read(), so we
* know that the file descriptor has been opened with the correct permissions
* for this operation.
*
* The return value is the number of bytes actually read.
*
* This function does not communicate with the device directly. Instead data is
* transfered from a circular buffer. The interrupt handler is then responsible
* for copying data from the device into this buffer.
*/
int cal_avalon_uart_read (alt_fd* fd, char* ptr, int len)
{
alt_irq_context context;
int block;
alt_u32 next;
cal_avalon_uart_dev* dev = (cal_avalon_uart_dev*) fd->dev;
int count = 0;
/*
* Construct a flag to indicate whether the device is being accessed in
* blocking or non-blocking mode.
*/
block = !(fd->fd_flags & O_NONBLOCK);
/*
* When running in a multi threaded environment, obtain the "read_lock"
* semaphore. This ensures that reading from the device is thread-safe.
*/
ALT_SEM_PEND (dev->read_lock, 0);
/*
* Calculate which slot in the circular buffer is the next one to read
* data from.
*/
next = (dev->rx_start + 1) & CAL_AVALON_UART_BUF_MSK;
/*
* Loop, copying data from the circular buffer to the destination address
* supplied in "ptr". This loop is terminated when the required number of
* bytes have been read. If the circular buffer is empty, and no data has
* been read, then the loop will block (when in blocking mode).
*
* If the circular buffer is empty, and some data has already been
* transferred, or the device is being accessed in non-blocking mode, then
* the loop terminates without necessarily reading all the requested data.
*/
do
{
/*
* Read the required amount of data, until the circular buffer runs
* empty
*/
while ((count < len) && (dev->rx_start != dev->rx_end))
{
count++;
*ptr++ = dev->rx_buf[dev->rx_start];
dev->rx_start = (++dev->rx_start) & CAL_AVALON_UART_BUF_MSK;
}
/*
* If no data has been transferred, the circular buffer is empty, and
* this is not a non-blocking access, block waiting for data to arrive.
*/
if (!count && (dev->rx_start == dev->rx_end))
{
if (!block)
{
/* Set errno to indicate the reason we're not returning any data */
ALT_ERRNO = EWOULDBLOCK;
break;
}
else
{
/* Block waiting for some data to arrive */
/* First, ensure read interrupts are enabled to avoid deadlock */
context = alt_irq_disable_all ();
dev->ctrl |= CAL_AVALON_UART_CONTROL_RRDY_MSK;
IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
alt_irq_enable_all (context);
/*
* When running in a multi-threaded mode, we pend on the read event
* flag set in the interrupt service routine. This avoids wasting CPU
* cycles waiting in this thread, when we could be doing something more
* profitable elsewhere.
*/
ALT_FLAG_PEND (dev->events,
ALT_UART_READ_RDY,
OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
0);
}
}
}
while (!count && len);
/*
* Now that access to the circular buffer is complete, release the read
* semaphore so that other threads can access the buffer.
*/
ALT_SEM_POST (dev->read_lock);
/*
* Ensure that interrupts are enabled, so that the circular buffer can
* re-fill.
*/
context = alt_irq_disable_all ();
dev->ctrl |= CAL_AVALON_UART_CONTROL_RRDY_MSK;
IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
alt_irq_enable_all (context);
/* Return the number of bytes read */
return count;
}
/*
* cal_avalon_uart_write() is called by the system write() function in order to
* write a block of data to the UART. "len" is the length of the data to write,
* and "ptr" indicates the source address. "fd" is the file descriptor for the
* device to be read from.
*
* Permission checks are made before the call to cal_avalon_uart_write(), so we
* know that the file descriptor has been opened with the correct permissions
* for this operation.
*
* The return value is the number of bytes actually written.
*
* This function does not communicate with the device directly. Instead data is
* transfered to a circular buffer. The interrupt handler is then responsible
* for copying data from this buffer into the device.
*/
int cal_avalon_uart_write (alt_fd* fd, const char* ptr, int len)
{
alt_irq_context context;
int no_block;
alt_u32 next;
cal_avalon_uart_dev* dev = (cal_avalon_uart_dev*) fd->dev;
int count = len;
/*
* Construct a flag to indicate whether the device is being accessed in
* blocking or non-blocking mode.
*/
no_block = (fd->fd_flags & O_NONBLOCK);
/*
* When running in a multi threaded environment, obtain the "write_lock"
* semaphore. This ensures that writing to the device is thread-safe.
*/
ALT_SEM_PEND (dev->write_lock, 0);
/*
* Loop transferring data from the input buffer to the transmit circular
* buffer. The loop is terminated once all the data has been transferred,
* or, (if in non-blocking mode) the buffer becomes full.
*/
while (count)
{
/* Determine the next slot in the buffer to access */
next = (dev->tx_end + 1) & CAL_AVALON_UART_BUF_MSK;
/* block waiting for space if necessary */
if (next == dev->tx_start)
{
if (no_block)
{
/* Set errno to indicate why this function returned early */
ALT_ERRNO = EWOULDBLOCK;
break;
}
else
{
/* Block waiting for space in the circular buffer */
/* First, ensure transmit interrupts are enabled to avoid deadlock */
context = alt_irq_disable_all ();
dev->ctrl |= (CAL_AVALON_UART_CONTROL_TRDY_MSK |
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -