text: /*
   * Copyright (c) 2006-2018, RT-Thread Development Team
   *
   * SPDX-License-Identifier: Apache-2.0
   *
   * Change Logs:
   * Date           Author       Notes
   * 2019-08-13     balanceTWK   the first version
   */
  
  #include <rtthread.h>
  #include <rtdevice.h>
  #include <rtdbg.h>
  
  static rt_err_t rt_inputcapture_init(struct rt_device *dev)
  {
      rt_err_t ret;
      struct rt_inputcapture_device *inputcapture;
  
      RT_ASSERT(dev != RT_NULL);
  
      ret = RT_EOK;
      inputcapture = (struct rt_inputcapture_device *)dev;
      inputcapture->watermark = RT_INPUT_CAPTURE_RB_SIZE / 2;
      if (inputcapture->ops->init)
      {
          ret = inputcapture->ops->init(inputcapture);
      }
  
      return ret;
  }
  
  static rt_err_t rt_inputcapture_open(struct rt_device *dev, rt_uint16_t oflag)
  {
      rt_err_t ret;
      struct rt_inputcapture_device *inputcapture;
  
      RT_ASSERT(dev != RT_NULL);
  
      ret = RT_EOK;
      inputcapture = (struct rt_inputcapture_device *)dev;
      if (inputcapture->ringbuff == RT_NULL)
      {
          inputcapture->ringbuff = rt_ringbuffer_create(sizeof(struct rt_inputcapture_data) * RT_INPUT_CAPTURE_RB_SIZE);
      }
      if (inputcapture->ops->open)
      {
          ret = inputcapture->ops->open(inputcapture);
      }
  
      return ret;
  }
  
  static rt_err_t rt_inputcapture_close(struct rt_device *dev)
  {
      rt_err_t ret;
      struct rt_inputcapture_device *inputcapture;
  
      RT_ASSERT(dev != RT_NULL);
  
      ret = -RT_ERROR;
      inputcapture = (struct rt_inputcapture_device *)dev;
  
      if (inputcapture->ops->close)
      {
          ret = inputcapture->ops->close(inputcapture);
      }
  
      if (ret != RT_EOK)
      {
          return ret;
      }
  
      if (inputcapture->ringbuff)
      {
          rt_ringbuffer_destroy(inputcapture->ringbuff);
          inputcapture->ringbuff = RT_NULL;
      }
      return ret;
  }
  
  static rt_size_t rt_inputcapture_read(struct rt_device *dev,
                                   rt_off_t          pos,
                                   void             *buffer,
                                   rt_size_t         size)
  {
      rt_size_t receive_size;
      struct rt_inputcapture_device *inputcapture;
  
      RT_ASSERT(dev != RT_NULL);
  
      inputcapture = (struct rt_inputcapture_device *)dev;
      receive_size = rt_ringbuffer_get(inputcapture->ringbuff, (rt_uint8_t *)buffer, sizeof(struct rt_inputcapture_data) * size);
  
      return receive_size / sizeof(struct rt_inputcapture_data);
  }
  
  static rt_err_t rt_inputcapture_control(struct rt_device *dev, int cmd, void *args)
  {
      rt_err_t result;
      struct rt_inputcapture_device *inputcapture;
  
      RT_ASSERT(dev != RT_NULL);
  
      result = RT_EOK;
      inputcapture = (struct rt_inputcapture_device *)dev;
      switch (cmd)
      {
      case INPUTCAPTURE_CMD_CLEAR_BUF:
          if (inputcapture->ringbuff)
          {
              rt_ringbuffer_reset(inputcapture->ringbuff);
          }
          break;
      case INPUTCAPTURE_CMD_SET_WATERMARK:
          inputcapture->watermark = *(rt_size_t *)args;
          break;
      default:
          result = -RT_ENOSYS;
          break;
      }
  
      return result;
  }
  
  #ifdef RT_USING_DEVICE_OPS
  const static struct rt_device_ops inputcapture_ops =
  {
      rt_inputcapture_init,
      rt_inputcapture_open,
      rt_inputcapture_close,
      rt_inputcapture_read,
      RT_NULL,
      rt_inputcapture_control
  };
  #endif
  
  rt_err_t rt_device_inputcapture_register(struct rt_inputcapture_device *inputcapture, const char *name, void *user_data)
  {
      struct rt_device *device;
  
      RT_ASSERT(inputcapture != RT_NULL);
      RT_ASSERT(inputcapture->ops != RT_NULL);
      RT_ASSERT(inputcapture->ops->get_pulsewidth != RT_NULL);
  
      device = &(inputcapture->parent);
  
      device->type        = RT_Device_Class_Miscellaneous;
      device->rx_indicate = RT_NULL;
      device->tx_complete = RT_NULL;
      inputcapture->ringbuff = RT_NULL;
  
  #ifdef RT_USING_DEVICE_OPS
      device->ops         = &inputcapture_ops;
  #else
      device->init        = rt_inputcapture_init;
      device->open        = rt_inputcapture_open;
      device->close       = rt_inputcapture_close;
      device->read        = rt_inputcapture_read;
      device->write       = RT_NULL;
      device->control     = rt_inputcapture_control;
  #endif
      device->user_data   = user_data;
  
      return rt_device_register(device, name, RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_STANDALONE);
  }
  
  /**
   * This function is ISR for inputcapture interrupt.
   * level: RT_TRUE denotes high level pulse, and RT_FALSE denotes low level pulse.
   */
  void rt_hw_inputcapture_isr(struct rt_inputcapture_device *inputcapture, rt_bool_t level)
  {
      struct rt_inputcapture_data data;
      rt_size_t receive_size;
      if (inputcapture->ops->get_pulsewidth(inputcapture, &data.pulsewidth_us) != RT_EOK)
      {
          return;
      }
  
      data.is_high = level;
      if (rt_ringbuffer_put(inputcapture->ringbuff, (rt_uint8_t *)&data, sizeof(struct rt_inputcapture_data)) == 0)
      {
          LOG_W("inputcapture ringbuffer doesn't have enough space.");
      }
  
      receive_size =  rt_ringbuffer_data_len(inputcapture->ringbuff) / sizeof(struct rt_inputcapture_data);
  
      if (receive_size >= inputcapture->watermark)
      {
          /* indicate to upper layer application */
          if (inputcapture->parent.rx_indicate != RT_NULL)
              inputcapture->parent.rx_indicate(&inputcapture->parent, receive_size);
      }
  }
  