⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cal_avalon_uart.c

📁 UART RS232 IPCORE for sopc builder
💻 C
📖 第 1 页 / 共 2 页
字号:
/******************************************************************************
*                                                                             *
* 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 + -