📄 cal_avalon_uart.c
字号:
CAL_AVALON_UART_CONTROL_DCTS_MSK);
IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
alt_irq_enable_all (context);
/* wait for space to come free */
do
{
/*
* When running in a multi-threaded mode, we pend on the write 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_WRITE_RDY,
OS_FLAG_WAIT_SET_ANY + OS_FLAG_CONSUME,
0);
}
while ((next == dev->tx_start));
}
}
count--;
/* Add the next character to the transmit buffer */
dev->tx_buf[dev->tx_end] = *ptr++;
dev->tx_end = next;
}
/*
* Now that access to the circular buffer is complete, release the write
* semaphore so that other threads can access the buffer.
*/
ALT_SEM_POST (dev->write_lock);
/*
* Ensure that interrupts are enabled, so that the circular buffer can
* drain.
*/
context = alt_irq_disable_all ();
dev->ctrl |= CAL_AVALON_UART_CONTROL_TRDY_MSK |
CAL_AVALON_UART_CONTROL_DCTS_MSK;
IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
alt_irq_enable_all (context);
/* return the number of bytes written */
return (len - count);
}
/*
* cal_avalon_uart_rxirq() is called by cal_avalon_uart_irq() to process a
* receive interrupt. It transfers the incoming character into the receive
* circular buffer, and sets the apropriate flags to indicate that there is
* dat ready to be processed.
*/
static void cal_avalon_uart_rxirq (cal_avalon_uart_dev* dev,
alt_u32 status)
{
alt_u32 next;
/*
* In a multi-threaded environment, set the read event flag to indicate
* that there is data ready. This is only done if the circular buffer was
* previously empty.
*/
if (dev->rx_end == dev->rx_start)
{
ALT_FLAG_POST (dev->events, ALT_UART_READ_RDY, OS_FLAG_SET);
}
/* Determine which slot to use next in the circular buffer */
next = (dev->rx_end + 1) & CAL_AVALON_UART_BUF_MSK;
/* Transfer data from the device to the circular buffer */
dev->rx_buf[dev->rx_end] = IORD_CAL_AVALON_UART_RXDATA(dev->base);
/* If there was an error, discard the data */
if (status & (CAL_AVALON_UART_STATUS_PE_MSK |
CAL_AVALON_UART_STATUS_FE_MSK))
{
return;
}
dev->rx_end = next;
next = (dev->rx_end + 1) & CAL_AVALON_UART_BUF_MSK;
/*
* If the cicular buffer was full, disable interrupts. Interrupts will be
* re-enabled when data is removed from the buffer.
*/
if (next == dev->rx_start)
{
dev->ctrl &= ~CAL_AVALON_UART_CONTROL_RRDY_MSK;
IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
}
}
/*
* cal_avalon_uart_txirq() is called by cal_avalon_uart_irq() to process a
* transmit interrupt. It transfers data from the transmit buffer to the
* device, and sets the apropriate flags to indicate that there is
* data ready to be processed.
*/
static void cal_avalon_uart_txirq (cal_avalon_uart_dev* dev,
alt_u32 status)
{
/* Transfer data if there is some ready to be transfered */
if (dev->tx_start != dev->tx_end)
{
/*
* If the device is using flow control (i.e. RTS/CTS), then the
* transmitter is required to throttle if CTS is high.
*/
if (!(dev->flags & CAL_AVALON_UART_FC) ||
(status & CAL_AVALON_UART_STATUS_CTS_MSK))
{
/*
* In a multi-threaded environment, set the write event flag to indicate
* that there is space in the circular buffer. This is only done if the
* buffer was previously empty.
*/
if (dev->tx_start == ((dev->tx_end + 1) & CAL_AVALON_UART_BUF_MSK))
{
ALT_FLAG_POST (dev->events,
ALT_UART_WRITE_RDY,
OS_FLAG_SET);
}
/* Write the data to the device */
IOWR_CAL_AVALON_UART_TXDATA(dev->base, dev->tx_buf[dev->tx_start]);
dev->tx_start = (++dev->tx_start) & CAL_AVALON_UART_BUF_MSK;
/*
* In case the tranmit interrupt had previously been disabled by
* detecting a low value on CTS, it is reenabled here.
*/
dev->ctrl |= CAL_AVALON_UART_CONTROL_TRDY_MSK;
}
else
{
/*
* CTS is low and we are using flow control, so disable the transmit
* interrupt while we wait for CTS to go high again. This will be
* detected using the DCTS interrupt.
*
* There is a race condition here. "status" may indicate that
* CTS is low, but it actually went high before DCTS was cleared on
* the last write to the status register. To avoid this resulting in
* deadlock, it's necessary to re-check the status register here
* before throttling.
*/
status = IORD_CAL_AVALON_UART_STATUS(dev->base);
if (!(status & CAL_AVALON_UART_STATUS_CTS_MSK))
{
dev->ctrl &= ~CAL_AVALON_UART_CONTROL_TRDY_MSK;
}
}
}
/*
* If the circular buffer is empty, disable the interrupt. This will be
* re-enabled when new data is placed in the buffer.
*/
if (dev->tx_start == dev->tx_end)
{
dev->ctrl &= ~(CAL_AVALON_UART_CONTROL_TRDY_MSK |
CAL_AVALON_UART_CONTROL_DCTS_MSK);
}
IOWR_CAL_AVALON_UART_CONTROL(dev->base, dev->ctrl);
}
/*
* cal_avalon_uart_irq() is the interrupt handler registered at configuration
* time for processing UART interrupts. It vectors interrupt requests to
* either cal_avalon_uart_rxirq() (for incoming data), or
* cal_avalon_uart_txirq() (for outgoing data).
*/
static void cal_avalon_uart_irq (void* context, alt_u32 id)
{
alt_u32 status;
cal_avalon_uart_dev* dev = (cal_avalon_uart_dev*) context;
void* base = dev->base;
/*
* Read the status register in order to determine the cause of the
* interrupt.
*/
status = IORD_CAL_AVALON_UART_STATUS(base);
/* Clear any error flags set at the device */
IOWR_CAL_AVALON_UART_STATUS(base, 0);
/* process a read irq */
if (status & CAL_AVALON_UART_STATUS_RRDY_MSK)
{
cal_avalon_uart_rxirq (dev, status);
}
/* process a write irq */
if (status & (CAL_AVALON_UART_STATUS_TRDY_MSK |
CAL_AVALON_UART_STATUS_DCTS_MSK))
{
cal_avalon_uart_txirq (dev, status);
}
}
/*
* cal_avalon_uart_init() is called by the auto-generated function
* alt_sys_init() in order to initialise a particular instance of this device.
* It is responsible for configuring the device and associated software
* constructs.
*
* If no errors occur, then the device is register as available to the system
* through a call to alt_dev_reg().
*/
void cal_avalon_uart_init (cal_avalon_uart_dev* dev, void* base, alt_u32 irq)
{
int error;
/*
* Initialise the read and write flags and the semaphores used to
* protect access to the circular buffers when running in a multi-threaded
* environment.
*/
error = ALT_FLAG_CREATE (&dev->events, 0) ||
ALT_SEM_CREATE (dev->read_lock, 1) ||
ALT_SEM_CREATE (dev->write_lock, 1);
if (!error)
{
/* enable interrupts at the device */
dev->ctrl = CAL_AVALON_UART_CONTROL_RTS_MSK |
CAL_AVALON_UART_CONTROL_RRDY_MSK |
CAL_AVALON_UART_CONTROL_DCTS_MSK;
IOWR_CAL_AVALON_UART_CONTROL(base, dev->ctrl);
/* register the interrupt handler */
if (alt_irq_register (irq, dev, cal_avalon_uart_irq) >= 0)
{
/* make the device available to the system */
alt_dev_reg (&dev->dev);
}
}
}
/*
* To reduce the code footprint of this driver, the ioctl() function is not
* included by default. If you wish to use the ioctl features provided
* below, you can do so by adding the option : -DCAL_AVALON_UART_USE_IOCTL
* to CPPFLAGS in the Makefile (or through the Eclipse IDE).
*/
#ifdef CAL_AVALON_UART_USE_IOCTL
/*
* cal_avalon_uart_tiocmget() is used by cal_avalon_uart_ioctl() to fill in
* the input termios structure with the current device configuration.
*
* See termios.h for further details on the contents of the termios structure.
*/
static int cal_avalon_uart_tiocmget (cal_avalon_uart_dev* dev,
struct termios* term)
{
memcpy (term, &dev->termios, sizeof (struct termios));
return 0;
}
/*
* cal_avalon_uart_tiocmset() is used by cal_avalon_uart_ioctl() to configure
* the device according to the settings in the input termios structure. In
* practice the only configuration that can be changed is the baud rate, and
* then only if the hardware is configured to have a writable baud register.
*/
static int cal_avalon_uart_tiocmset (cal_avalon_uart_dev* dev,
struct termios* term)
{
alt_u32 divisor;
speed_t speed;
speed = dev->termios.c_ispeed;
/* Update the settings if the hardware supports it */
if (!(dev->flags & CAL_AVALON_UART_FB))
{
dev->termios.c_ispeed = dev->termios.c_ospeed = term->c_ispeed;
}
/*
* If the request was for an unsupported setting, return an error.
*/
if (memcmp(term, &dev->termios, sizeof (struct termios)))
{
dev->termios.c_ispeed = dev->termios.c_ospeed = speed;
return -EIO;
}
/*
* Otherwise, update the hardware.
*/
IOWR_CAL_AVALON_UART_DIVISOR(dev->base, ((dev->freq/speed) - 1));
return 0;
}
/*
* cal_avalon_uart_ioctl() is called by the system ioctl() function to handle
* ioctl requests for the UART. The only ioctl requests supported are TIOCMGET
* and TIOCMSET.
*
* TIOCMGET returns a termios structure that describes the current device
* configuration.
*
* TIOCMSET sets the device (if possible) to match the requested configuration.
* The requested configuration is described using a termios structure passed
* through the input argument "arg".
*/
int cal_avalon_uart_ioctl (alt_fd* fd, int req, void* arg)
{
int rc = -ENOTTY;
switch (req)
{
case TIOCMGET:
rc = cal_avalon_uart_tiocmget ((cal_avalon_uart_dev*) fd->dev,
(struct termios*) arg);
break;
case TIOCMSET:
rc = cal_avalon_uart_tiocmset ((cal_avalon_uart_dev*) fd->dev,
(struct termios*) arg);
break;
default:
break;
}
return rc;
}
#endif /* CAL_AVALON_UART_USE_IOCTL */
#endif /* defined ALT_USE_SMALL_DRIVERS || CAL_AVALON_UART_SMALL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -