/**
 * \file
 *
 * \brief Atmel AVR and 32-bit AVR Common Sensor API.
 *
 * The sensor device service provides high-level interfaces and binary driver
 * libraries for sensor devices on systems built around UC3 and XMEGA
 * microcontrollers.
 *
 * Copyright (c) 2012 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * 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. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 *
 * \asf_license_stop
 *
 */



#include "sensor.h"



// Driver Call Wrapper

#define SENSOR_DRV_CALL(sns, fn, ...)   (((sns)->drv->func.fn) \
	? (((sns)->drv->func.fn)(sns, __VA_ARGS__)) : sensor_driver_unimpl (sns))



/**
 * @brief Get a range table index associated with a range value
 *
 * @internal
 *
 * This utility routine, provided for internal API and driver functions,
 * fetches the driver range table index associated with a device-specific
 * range value.  The table index returned in \c index is undefined in the event
 * of a \c false return value.
 *
 * @param  caps     Specifies an initialized driver capabilities descriptor.
 * @param  range    A device-specific operational range value.
 * @param  index    The driver range table index is returned to this location.
 * @return bool     true if the call succeeds, else false is returned.
 */
static inline bool sensor_range_index
	(const sensor_caps_t *caps, int16_t range, int *index)
{
	if (caps && caps->range_table) {
		for (int i = 0; i < caps->range_count; ++i) {
			if (caps->range_table[i].range_units == range) {
				*index = i;
				return true;
			}
		}
	}

	return false;
}

/**
 * @brief Get a bandwidth table index associated with a bandwidth value
 *
 * @internal
 *
 * This utility routine, provided for internal API and driver functions,
 * fetches the driver bandwidth table index associated with a specified
 * bandwidth value.  The table index returned in \c index is undefined in
 * the event of a \c false return value.
 *
 * @param  caps     Specifies an initialized driver capabilities descriptor.
 * @param  band     A bandwidth value (Hz).
 * @param  index    The driver range table index is returned to this location.
 * @return bool     true if the call succeeds, else false is returned.
 */
static inline bool sensor_band_index
	(const sensor_caps_t *caps, int16_t band, int *index)
{
	if (caps && caps->band_table) {
		for (int i = 0; i < caps->band_count; ++i) {
			if (caps->band_table[i].bandwidth_Hz == band) {
				*index = i;
				return true;
			}
		}
	}

	return false;
}

/**
 * @ingroup mems_sensor_api
 * @{
 */

/**
 * @brief Unimplemented driver function handler
 *
 * This routine is called in the API to set the sensor error status and return
 * a status indicating a function call failure when a driver has been invoked to
 * run an interface that's not implemented in the driver.
 *
 * @param  sensor   The address of an initialized sensor descriptor.
 * @return bool     Always returns \c false.
 */
static bool inline sensor_driver_unimpl (sensor_t *sensor)
{
	sensor->err = SENSOR_ERR_FUNCTION;
	return false;
}

/**
 * @brief Attach a specified sensor descriptor to a sensor device.
 *
 * Applications invoke this routine to attach a \c sensor_t instance to an
 * existing sensor device of a specified \c type.  Assuming the attach call
 * is successful, the \c sensor_t instance can then be used with other sensor
 * API functions to perform operations on the device.
 *
 * @param   sensor  Specifies a descriptor to attach to the sensor device.
 * @param   type    Specifies the sensor type to attach.
 * @param   num     Driver instance number; currently ignored.
 * @param   aux     API Reserved value; should always be set to zero.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_attach (sensor_t *sensor, sensor_type_t type, int num, void *aux)
{
	bool attached = false;

	sensor_hal_t * hal = sensor_find (type);

	sensor->hal  = hal;   // Set unconditionally for use in driver init.
	sensor->type = type;  // user-selected type (for multi-function device)
	sensor->aux  = aux;

	if ((hal != NULL) && /* sensor_bus_probe (hal, 0) && */
		((hal->sensor_init) (sensor, num))) {
		sensor->mod = SENSOR_STATE_NORMAL;
		sensor->err = SENSOR_ERR_NONE;
		attached = true;
	} else {
		sensor->err = SENSOR_ERR_DRIVER;
	}

	return attached;
}

/**
 * @brief Enumerate sensor devices.
 *
 * This routine is a sensor device enumeration function that will call a
 * user-defined callback \c func for each sensor of a specified \c type
 * configured into the system.  The user-callback function will be passed the
 * address of a \a sensor_hal_t \c HAL descriptor corresponding to the sensor
 * type along with a user-specified address \c arg storing any additional
 * function arguments.
 *
 * If the specified sensor \c type is \c SENSOR_TYPE_UNKNOWN, then the user
 * \c func will be called for every sensor device in the system.
 *
 * Once called, the user-callback function must return a boolean \a true
 * value to continue iterating the list, else \a false to cease device list
 * iteration.  This routine returns when there are not more sensors of the
 * requested \c type in the device list, or when the user function returns
 * zero (false).
 *
 * Note that this function will not be included in the API by default.  Build
 * a sensor system with \c INCLUDE_SENSOR_ENUM defined to include this routine.
 *
 * @param   type    Specifies the sensor type to enumerate.
 * @param   func    Specifies the address of a user callback function.
 * @param   arg     Specifies the address of a callback function argument.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_device_enum
    (sensor_type_t type, sensor_enum_callback func, void *arg)
{
#ifdef INCLUDE_SENSOR_ENUM

	size_t count;
	const  sensor_hal_t * hal = sensor_list (&count);

	if (func != 0) {
		for (int n = 0; n < count; ++n, ++hal) {
			if (((type == SENSOR_TYPE_UNKNOWN) || (type == hal->dev_type))
				&& (false == (func) (hal, arg))) {

				return true;
			}
		}
	}

#endif // INCLUDE_SENSOR_ENUM

	return false;
}

/**
 * @brief Get sensor hardware device ID.
 *
 * This routine returns device-specific sensor hardware identification
 * and, optionally, version values.  Unimplemented values will be set
 * to zero.  For example, devices supporting a dedicated ID register, but
 * not a version register, will set "id" to the ID value and "ver" to zero.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   id      An address where the device ID is returned.
 * @param   ver     An address where the device version is returned.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_device_id (sensor_t *sensor, uint32_t *id, uint8_t *ver)
{
	sensor_data_t   dev_data;
	bool status = false;

	status = sensor_read (sensor, SENSOR_READ_ID, &dev_data);

	if (status) {
		*id = dev_data.device.id;
		*ver = (uint8_t) dev_data.device.version;
	}

	return status;
}

/**
 * @brief Read sensor data
 *
 * This is a generic routine that will call a specified sensor function
 * returning sensor data.  It is used primarily by API routines
 * but may be called by special-case application or library code where
 * needed to extend the API semantics.
 *
 * As an example, consider a multiple-axis gyroscope driver implementing
 * the sensor_funcs_t.get_rotation() interface.  An application will usually
 * call the sensor_get_rotation() routine in this case, but the angular
 * rate might be obtained from the device as follows:
 *
 * @code
 *
 * sensor_t      gyroscope;
 * sensor_data_t omega;
 * ...
 *
 * sensor_read (&gyroscope, SENSOR_READ_ROTATION, &omega);
 * ...
 *
 * @endcode
 *
 * In the event of a false return from this routine, the contents stored
 * at the location specified by "data" are undefined.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   type    Type of sample to read from sensor
 * @param   data    Specifies an address where sensor data is stored.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_read (sensor_t *sensor, sensor_read_t type,
		sensor_data_t *data)
{
	bool result;

	result = SENSOR_DRV_CALL (sensor, read, type, data);

	if (result) {
		data->timestamp = sensor_timestamp();
	}

	return result;
}

/**
 * @brief Enumerate sensor bandwidth settings.
 *
 * This routine returns the address of a table, if any, holding device-specific
 * sensor bandwidth settings.  The use and context of the settings are specific
 * to the device and typically documented in the manufacturer's data sheets for
 * the device.
 *
 * @param   sensor  The address of a sensor descriptor.
 * @param   table   Location where the table address is returned.
 * @param   count   Location where the number of table elements is returned.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_enum_bandwidth
		(sensor_t *sensor, const sensor_band_t **table, size_t *count)
{
	*table = sensor->drv->caps.band_table;
	*count = sensor->drv->caps.band_count;

	return (*count) ? true : false;
}

/**
 * @brief Enumerate sensor range settings
 *
 * This routine returns the address of a table, if any, holding device-specific
 * sensor range settings.  The units of the settings are specific to the
 * device and typically documented in the manufacturer's data sheets for
 * the device.
 *
 * @param   sensor  The address of a sensor descriptor.
 * @param   table   Location where the table address is returned.
 * @param   count   Location where the number of table elements is returned.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_enum_range
		(sensor_t *sensor, const sensor_range_t **table, size_t *count)
{
	*table = sensor->drv->caps.range_table;
	*count = sensor->drv->caps.range_count;

	return (*count) ? true : false;
}

/**
 * @brief Install a sensor event handler.
 *
 * @param   event_desc  A specified sensor event descriptor.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_add_event (sensor_event_desc_t *event_desc)
{
	if (event_desc && event_desc->sensor && event_desc->event)
		return SENSOR_DRV_CALL (event_desc->sensor, event_callback, event_desc);

	return false;
}

/**
 * @brief Calibrate a sensor device.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   caltype The type of calibration to perform.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_calibrate (sensor_t *sensor, sensor_calibration_t caltype, 
		int code, void *caldata)
{
	return SENSOR_DRV_CALL (sensor, calibrate, caltype, code, caldata);
}

/**
 * @brief Set a sensor device to low-power or standby mode.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_sleep (sensor_t *sensor, int arg)
{
	return sensor_set_state (sensor, SENSOR_STATE_SLEEP);
}

/**
 * @brief Initiate a sensor device software reset.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_reset (sensor_t *sensor, int arg)
{
	return sensor_set_state (sensor, SENSOR_STATE_RESET);
}

/**
 * @brief Set a sensor mode.
 *
 * This routine sets a specified sensor execution state to one of the
 * following values:
 *
 *  - SENSOR_STATE_NORMAL
 *  - SENSOR_STATE_SLEEP
 *  - SENSOR_STATE_SUSPEND
 *  - SENSOR_STATE_LOWEST_POWER
 *  - SENSOR_STATE_LOW_POWER
 *  - SENSOR_STATE_HIGH_POWER
 *  - SENSOR_STATE_HIGHEST_POWER
 *  - SENSOR_STATE_RESET
 *
 * These execution states are not supported in all devices.  The function
 * return value will indicate whether or not the request could be processed.
 * The result of the request, when supported, is device dependent.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   mode    A specified sensor operational mode.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_set_state (sensor_t *sensor, sensor_state_t mode)
{
	bool result = false;

	if (mode != sensor->mod) {
		result = SENSOR_DRV_CALL (sensor, ioctl, SENSOR_SET_STATE, &mode);
	}

	if (result) {
		sensor->mod = (mode == SENSOR_STATE_RESET) ? SENSOR_STATE_NORMAL : mode;
	}

	return result;
}

/**
 * @brief Get a sensor mode.
 *
 * This routine gets the current sensor execution state which may be one
 * of the following values:
 *
 *  - SENSOR_STATE_NORMAL
 *  - SENSOR_STATE_SLEEP
 *  - SENSOR_STATE_SUSPEND
 *  - SENSOR_STATE_LOWEST_POWER
 *  - SENSOR_STATE_LOW_POWER
 *  - SENSOR_STATE_HIGH_POWER
 *  - SENSOR_STATE_HIGHEST_POWER
 *  - SENSOR_STATE_RESET
 *
 * These execution states are not supported in all devices.  The function
 * return value will indicate whether or not the request could be processed.
 * The result of the request, when supported, is device dependent.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   mode    The current sensor mode is returned to this location.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_get_state (sensor_t *sensor, sensor_state_t *mode)
{
	*mode = sensor->mod;
	return true;
}

/**
 * @brief Set a sensor operational threshold.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   type    A specified sensor operational threshold type.
 * @param   value   The value of the specified threshold.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_set_threshold
		(sensor_t *sensor, sensor_threshold_t type, int16_t value)
{
	sensor_threshold_desc_t threshold;

	threshold.type = type;
	threshold.value = value;

	return SENSOR_DRV_CALL (sensor, ioctl, SENSOR_SET_THRESHOLD, &threshold);
}

/**
 * @brief Get a sensor operational threshold.
 *
 * @param   sensor     The address of an initialized sensor descriptor.
 * @param   threshold  A specified sensor operational threshold type.
 * @param   value      Address of location to return threshold value
 * @return  bool       true if the call succeeds, else false is returned.
 */
bool sensor_get_threshold
		(sensor_t *sensor, sensor_threshold_t type, int16_t *value)
{
	sensor_threshold_desc_t threshold;
	bool status = false;

	threshold.type = type;

	if (SENSOR_DRV_CALL (sensor, ioctl, SENSOR_GET_THRESHOLD, &threshold)) {
		*value = threshold.value;
		status = true;
	}

	return status;
}

/**
 * @brief Execute a sensor device control function.
 *
 * @param   sensor  The address of an initialized sensor descriptor.
 * @param   cmd     Specifies the IOCTL command.
 * @param   arg     Specifies command paramters (varies by command).
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_ioctl (sensor_t *sensor, sensor_command_t cmd, void *arg)
{
	bool result = false;

	switch (cmd) {

		case SENSOR_GET_RANGE:
			*((int16_t *)arg) = sensor->hal->range;
			result = true;
			break;

		case SENSOR_SET_RANGE: {

			int16_t const range = *(int16_t *)arg;

			int index;
			result = sensor_range_index (&(sensor->drv->caps), range, &index);

			if (result &&
				(result = SENSOR_DRV_CALL (sensor, ioctl, cmd, &index))) {

				sensor->hal->range = range;

			} else { 
				sensor->err = SENSOR_ERR_PARAMS; 
			}
			break;
		}

		case SENSOR_GET_BANDWIDTH:
			*((int16_t *)arg) = sensor->hal->bandwidth;
			result = true;
			break;

		case SENSOR_SET_BANDWIDTH: {

			int16_t const bandwidth_Hz = *(int16_t *)arg;

			int index;
			result = sensor_band_index (&(sensor->drv->caps), bandwidth_Hz,
                                        &index);

			if (result &&
				(result = SENSOR_DRV_CALL (sensor, ioctl, cmd, &index))) {

				sensor->hal->bandwidth = bandwidth_Hz;
			} else {
				sensor->err = SENSOR_ERR_PARAMS;
			}
			break;
		}

		case SENSOR_GET_SAMPLE_RATE:
			*((int16_t *)arg) = sensor->hal->sample_rate;
			result = true;
			break;

		case SENSOR_SET_SAMPLE_RATE: {

			int16_t const sample_Hz = *(int16_t *)arg;

			if (SENSOR_DRV_CALL (sensor, ioctl, cmd, arg)) {
				sensor->hal->sample_rate = sample_Hz;
				result = true;
			} else if (SENSOR_ERR_FUNCTION != sensor->err) { 
				sensor->err = SENSOR_ERR_PARAMS; 
			}
			break;
		}

		default:
			result = SENSOR_DRV_CALL (sensor, ioctl, cmd, arg);
			break;

	}

	return result;
}

/**
 * @brief Set tap event parameters
 *
 * @param   sensor  Address of an initialized sensor device descriptor.
 * @param   params  Address of a tap paramter structure.
 * @return  bool    true if the call succeeds, else false is returned.
 */
bool sensor_set_tap (sensor_t *sensor, sensor_tap_params_t *params)
{
	if (params) {

		return SENSOR_DRV_CALL (sensor, ioctl, SENSOR_SET_TAP, params);
	}

	return false;
}

/**
 * @brief Activate a sensor self-test function.
 *
 * @param   sensor    Address of an initialized sensor device descriptor.
 * @param   test_code Address of a device-specific numeric test code.
 * @param   arg       Device-specific self-test argument options.
 * @return  bool      true if the test succeeds, else false is returned.
 */
bool sensor_selftest (sensor_t *sensor, int *test_code, void *arg)
{
	bool result = SENSOR_DRV_CALL (sensor, selftest, test_code, arg);

	if ((false == result) && (SENSOR_ERR_FUNCTION != sensor->err)) {
		sensor->err = SENSOR_ERR_SELFTEST;
	}

	return result;
}

/**
 * @brief AVR Software Framework Common Sensor API version
 *
 * This routine Gets the Sensor API version number and release date.
 *
 * @return The address of a null-terminated read-only version string.
 */
const char * sensor_api_version (void)
{
	static const char sensors_xplained_version [] =
		"$Id: sensor.c 28664 2012-02-16 15:47:17Z xleprevo $ $Revision: 28664 $ "
		"$Date: 2012-02-16 16:47:17 +0100 (Thu, 16 Feb 2012) $";

	return sensors_xplained_version;
}

/* @} */
