/*
 * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*---------------------------------------------------------------------------*/
/**
 * \addtogroup srf06-common-peripherals
 * @{
 *
 * \file
 *  Driver for the SmartRF06EB buttons when a CC13xx/CC26xxEM is mounted on it
 */
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "lib/sensors.h"
#include "srf06/button-sensor.h"
#include "gpio-interrupt.h"
#include "sys/timer.h"
#include "lpm.h"

#include "ti-lib.h"

#include <stdint.h>
/*---------------------------------------------------------------------------*/
#ifdef BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN
#define BUTTON_SENSOR_ENABLE_SHUTDOWN BUTTON_SENSOR_CONF_ENABLE_SHUTDOWN
#else
#define BUTTON_SENSOR_ENABLE_SHUTDOWN 1
#endif
/*---------------------------------------------------------------------------*/
#define BUTTON_GPIO_CFG         (IOC_CURRENT_2MA  | IOC_STRENGTH_AUTO | \
                                 IOC_IOPULL_UP    | IOC_SLEW_DISABLE  | \
                                 IOC_HYST_DISABLE | IOC_BOTH_EDGES    | \
                                 IOC_INT_ENABLE   | IOC_IOMODE_NORMAL | \
                                 IOC_NO_WAKE_UP   | IOC_INPUT_ENABLE)
/*---------------------------------------------------------------------------*/
#define DEBOUNCE_DURATION (CLOCK_SECOND >> 5)

struct btn_timer {
  struct timer debounce;
  clock_time_t start;
  clock_time_t duration;
};

static struct btn_timer sel_timer, left_timer, right_timer, up_timer,
              down_timer;
/*---------------------------------------------------------------------------*/
/**
 * \brief Handler for SmartRF button presses
 */
static void
button_press_handler(uint8_t ioid)
{
  if(ioid == BOARD_IOID_KEY_SELECT) {
    if(!timer_expired(&sel_timer.debounce)) {
      return;
    }

    timer_set(&sel_timer.debounce, DEBOUNCE_DURATION);

    /*
     * Start press duration counter on press (falling), notify on release
     * (rising)
     */
    if(ti_lib_gpio_read_dio(BOARD_IOID_KEY_SELECT) == 0) {
      sel_timer.start = clock_time();
      sel_timer.duration = 0;
    } else {
      sel_timer.duration = clock_time() - sel_timer.start;
      sensors_changed(&button_select_sensor);
    }
  }

  if(ioid == BOARD_IOID_KEY_LEFT) {
    if(!timer_expired(&left_timer.debounce)) {
      return;
    }

    timer_set(&left_timer.debounce, DEBOUNCE_DURATION);

    /*
     * Start press duration counter on press (falling), notify on release
     * (rising)
     */
    if(ti_lib_gpio_read_dio(BOARD_IOID_KEY_LEFT) == 0) {
      left_timer.start = clock_time();
      left_timer.duration = 0;
    } else {
      left_timer.duration = clock_time() - left_timer.start;
      sensors_changed(&button_left_sensor);
    }
  }

  if(ioid == BOARD_IOID_KEY_RIGHT) {
    if(BUTTON_SENSOR_ENABLE_SHUTDOWN == 0) {
      if(!timer_expired(&right_timer.debounce)) {
        return;
      }

      timer_set(&right_timer.debounce, DEBOUNCE_DURATION);

      /*
       * Start press duration counter on press (falling), notify on release
       * (rising)
       */
      if(ti_lib_gpio_read_dio(BOARD_IOID_KEY_RIGHT) == 0) {
        right_timer.start = clock_time();
        right_timer.duration = 0;
      } else {
        right_timer.duration = clock_time() - right_timer.start;
        sensors_changed(&button_right_sensor);
      }
    } else {
      lpm_shutdown(BOARD_IOID_KEY_RIGHT, IOC_IOPULL_UP, IOC_WAKE_ON_LOW);
    }
  }

  if(ioid == BOARD_IOID_KEY_UP) {
    if(!timer_expired(&up_timer.debounce)) {
      return;
    }

    timer_set(&up_timer.debounce, DEBOUNCE_DURATION);

    /*
     * Start press duration counter on press (falling), notify on release
     * (rising)
     */
    if(ti_lib_gpio_read_dio(BOARD_IOID_KEY_UP) == 0) {
      up_timer.start = clock_time();
      up_timer.duration = 0;
    } else {
      up_timer.duration = clock_time() - up_timer.start;
      sensors_changed(&button_up_sensor);
    }
  }

  if(ioid == BOARD_IOID_KEY_DOWN) {
    if(!timer_expired(&down_timer.debounce)) {
      return;
    }

    timer_set(&down_timer.debounce, DEBOUNCE_DURATION);

    /*
     * Start press duration counter on press (falling), notify on release
     * (rising)
     */
    if(ti_lib_gpio_read_dio(BOARD_IOID_KEY_DOWN) == 0) {
      down_timer.start = clock_time();
      down_timer.duration = 0;
    } else {
      down_timer.duration = clock_time() - down_timer.start;
      sensors_changed(&button_down_sensor);
    }
  }
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the button sensor for all buttons.
 *
 * \param type This function does nothing unless type == SENSORS_ACTIVE
 * \param c 0: disable the button, non-zero: enable
 * \param key: One of BOARD_KEY_LEFT, BOARD_KEY_RIGHT etc
 */
static void
config_buttons(int type, int c, uint32_t key)
{
  switch(type) {
  case SENSORS_HW_INIT:
    ti_lib_gpio_clear_event_dio(key);
    ti_lib_rom_ioc_pin_type_gpio_input(key);
    ti_lib_rom_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG);
    gpio_interrupt_register_handler(key, button_press_handler);
    break;
  case SENSORS_ACTIVE:
    if(c) {
      ti_lib_gpio_clear_event_dio(key);
      ti_lib_rom_ioc_pin_type_gpio_input(key);
      ti_lib_rom_ioc_port_configure_set(key, IOC_PORT_GPIO, BUTTON_GPIO_CFG);
      ti_lib_rom_ioc_int_enable(key);
    } else {
      ti_lib_rom_ioc_int_disable(key);
    }
    break;
  default:
    break;
  }
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the select button.
 *
 * Parameters are passed onto config_buttons, which does the actual
 * configuration
 * Parameters are ignored. They have been included because the prototype is
 * dictated by the core sensor api. The return value is also required by
 * the API but otherwise ignored.
 *
 * \param type passed to config_buttons as-is
 * \param value passed to config_buttons as-is
 *
 * \return ignored
 */
static int
config_select(int type, int value)
{
  config_buttons(type, value, BOARD_IOID_KEY_SELECT);

  return 1;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the left button.
 *
 * Parameters are passed onto config_buttons, which does the actual
 * configuration
 * Parameters are ignored. They have been included because the prototype is
 * dictated by the core sensor api. The return value is also required by
 * the API but otherwise ignored.
 *
 * \param type passed to config_buttons as-is
 * \param value passed to config_buttons as-is
 *
 * \return ignored
 */
static int
config_left(int type, int value)
{
  config_buttons(type, value, BOARD_IOID_KEY_LEFT);

  return 1;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the right button.
 *
 * Parameters are passed onto config_buttons, which does the actual
 * configuration
 * Parameters are ignored. They have been included because the prototype is
 * dictated by the core sensor api. The return value is also required by
 * the API but otherwise ignored.
 *
 * \param type passed to config_buttons as-is
 * \param value passed to config_buttons as-is
 *
 * \return ignored
 */
static int
config_right(int type, int value)
{
  config_buttons(type, value, BOARD_IOID_KEY_RIGHT);

  return 1;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the up button.
 *
 * Parameters are passed onto config_buttons, which does the actual
 * configuration
 * Parameters are ignored. They have been included because the prototype is
 * dictated by the core sensor api. The return value is also required by
 * the API but otherwise ignored.
 *
 * \param type passed to config_buttons as-is
 * \param value passed to config_buttons as-is
 *
 * \return ignored
 */
static int
config_up(int type, int value)
{
  config_buttons(type, value, BOARD_IOID_KEY_UP);

  return 1;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Configuration function for the down button.
 *
 * Parameters are passed onto config_buttons, which does the actual
 * configuration
 * Parameters are ignored. They have been included because the prototype is
 * dictated by the core sensor api. The return value is also required by
 * the API but otherwise ignored.
 *
 * \param type passed to config_buttons as-is
 * \param value passed to config_buttons as-is
 *
 * \return ignored
 */
static int
config_down(int type, int value)
{
  config_buttons(type, value, BOARD_IOID_KEY_DOWN);

  return 1;
}
/*---------------------------------------------------------------------------*/
static int
value_select(int type)
{
  if(type == BUTTON_SENSOR_VALUE_STATE) {
    return ti_lib_gpio_read_dio(BOARD_IOID_KEY_SELECT) == 0 ?
           BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED;
  } else if(type == BUTTON_SENSOR_VALUE_DURATION) {
    return (int)sel_timer.duration;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
static int
value_left(int type)
{
  if(type == BUTTON_SENSOR_VALUE_STATE) {
    return ti_lib_gpio_read_dio(BOARD_IOID_KEY_LEFT) == 0 ?
           BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED;
  } else if(type == BUTTON_SENSOR_VALUE_DURATION) {
    return (int)left_timer.duration;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
static int
value_right(int type)
{
  if(type == BUTTON_SENSOR_VALUE_STATE) {
    return ti_lib_gpio_read_dio(BOARD_IOID_KEY_RIGHT) == 0 ?
           BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED;
  } else if(type == BUTTON_SENSOR_VALUE_DURATION) {
    return (int)right_timer.duration;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
static int
value_up(int type)
{
  if(type == BUTTON_SENSOR_VALUE_STATE) {
    return ti_lib_gpio_read_dio(BOARD_IOID_KEY_UP) == 0 ?
           BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED;
  } else if(type == BUTTON_SENSOR_VALUE_DURATION) {
    return (int)up_timer.duration;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
static int
value_down(int type)
{
  if(type == BUTTON_SENSOR_VALUE_STATE) {
    return ti_lib_gpio_read_dio(BOARD_IOID_KEY_DOWN) == 0 ?
           BUTTON_SENSOR_VALUE_PRESSED : BUTTON_SENSOR_VALUE_RELEASED;
  } else if(type == BUTTON_SENSOR_VALUE_DURATION) {
    return (int)down_timer.duration;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for all buttons
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \param key_io_id BOARD_IOID_KEY_LEFT, BOARD_IOID_KEY_RIGHT etc
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will only be called by status_left, status_right and the
 * called will pass the correct key_io_id
 */
static int
status(int type, uint32_t key_io_id)
{
  switch(type) {
  case SENSORS_ACTIVE:
  case SENSORS_READY:
    if(ti_lib_ioc_port_configure_get(key_io_id) & IOC_INT_ENABLE) {
      return 1;
    }
    break;
  default:
    break;
  }
  return 0;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for the select button.
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will call status. It will pass type verbatim and it will also
 * pass the correct key_io_id
 */
static int
status_select(int type)
{
  return status(type, BOARD_IOID_KEY_SELECT);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for the left button.
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will call status. It will pass type verbatim and it will also
 * pass the correct key_io_id
 */
static int
status_left(int type)
{
  return status(type, BOARD_IOID_KEY_LEFT);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for the right button.
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will call status. It will pass type verbatim and it will also
 * pass the correct key_io_id
 */
static int
status_right(int type)
{
  return status(type, BOARD_IOID_KEY_RIGHT);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for the up button.
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will call status. It will pass type verbatim and it will also
 * pass the correct key_io_id
 */
static int
status_up(int type)
{
  return status(type, BOARD_IOID_KEY_UP);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Status function for the down button.
 * \param type SENSORS_ACTIVE or SENSORS_READY
 * \return 1 if the button's port interrupt is enabled (edge detect)
 *
 * This function will call status. It will pass type verbatim and it will also
 * pass the correct key_io_id
 */
static int
status_down(int type)
{
  return status(type, BOARD_IOID_KEY_DOWN);
}
/*---------------------------------------------------------------------------*/
SENSORS_SENSOR(button_select_sensor, BUTTON_SENSOR, value_select,
               config_select, status_select);
SENSORS_SENSOR(button_left_sensor, BUTTON_SENSOR, value_left, config_left,
               status_left);
SENSORS_SENSOR(button_right_sensor, BUTTON_SENSOR, value_right, config_right,
               status_right);
SENSORS_SENSOR(button_up_sensor, BUTTON_SENSOR, value_up, config_up, status_up);
SENSORS_SENSOR(button_down_sensor, BUTTON_SENSOR, value_down, config_down,
               status_down);
/*---------------------------------------------------------------------------*/
/** @} */