diff options
Diffstat (limited to 'peripheral/light/mraa/lights.c')
-rw-r--r-- | peripheral/light/mraa/lights.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/peripheral/light/mraa/lights.c b/peripheral/light/mraa/lights.c new file mode 100644 index 0000000..00aa31e --- /dev/null +++ b/peripheral/light/mraa/lights.c @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Based on htc/flounder/lights/lights.h + */ + +#define LOG_TAG "lights" + +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <cutils/log.h> +#include <hardware/lights.h> +#include <hardware/hardware.h> +#include <gpio.h> + +/* List of supported lights */ +typedef enum { + NOTIFICATIONS_TYPE, + LIGHTS_TYPE_NUM +} light_type_t; + +/* Light device data structure */ +struct light_device_ext_t { + /* Base device */ + struct light_device_t base_dev; + /* Physical pin */ + int pin; + /* Current state of the light device */ + struct light_state_t state; + /* Number of device references */ + int refs; + /* Synchronization attributes */ + pthread_t flash_thread; + pthread_cond_t flash_cond; + pthread_mutex_t flash_signal_mutex; + pthread_mutex_t write_mutex; +}; + +static int64_t const ONE_MS_IN_NS = 1000000LL; +static int64_t const ONE_S_IN_NS = 1000000000LL; + +/* + * Raw pin number for identifying the TRI_STATE_ALL GPIO + * used to determine if we're on an Arduino board + */ +static int const TRI_STATE_ALL_GPIO_RAW_PIN = 214; + +/* + * Pin constants + * Please add a pin to EDISON_ARDUINO_PINS, EDISON_MINIBOARD_PINS & + * MINNOWBOARD_MAX_PINS when you add a new light type + */ +static int const EDISON_ARDUINO_PINS[LIGHTS_TYPE_NUM] = {13}; +static int const EDISON_MINIBOARD_PINS[LIGHTS_TYPE_NUM] = {31}; +static int const MINNOWBOARD_MAX_PINS[LIGHTS_TYPE_NUM] = {21}; + +/* + * Array of light devices with write_mutex statically initialized + * to be able to synchronize the open_lights & close_lights functions + */ +struct light_device_ext_t light_devices[] = { + [ 0 ... (LIGHTS_TYPE_NUM - 1) ] = { .write_mutex = PTHREAD_MUTEX_INITIALIZER } +}; + +/* + * Set the GPIO value + * @param pin physical pin of the GPIO + * @param value what value to set + * @return 0 if success, error code otherwise + */ +static int set_gpio_value(int pin, int value) +{ + mraa_gpio_context gpio = NULL; + int rc = 0; + + if ((value != 0) && (value != 1)) { + return EINVAL; + } + + gpio = mraa_gpio_init(pin); + if (gpio == NULL) { + return EPERM; + } + + if (mraa_gpio_dir(gpio, MRAA_GPIO_OUT) != MRAA_SUCCESS) { + rc = EPERM; + goto close_gpio; + } + + if (mraa_gpio_write(gpio, value) != MRAA_SUCCESS) { + rc = EPERM; + } + +close_gpio: + if (mraa_gpio_close(gpio) != MRAA_SUCCESS) { + rc = EPERM; + } + + return rc; +} + +/* + * Get current timestamp in nanoseconds + * @return time in nanoseconds + */ +int64_t get_timestamp_monotonic() +{ + struct timespec ts = {0}; + + if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { + return ONE_S_IN_NS * ts.tv_sec + ts.tv_nsec; + } + + return -1; +} + +/* + * Populates a timespec data structure from a int64_t timestamp + * @param out what timespec to populate + * @param target_ns timestamp in nanoseconds + */ +void set_timestamp(struct timespec *out, int64_t target_ns) +{ + out->tv_sec = target_ns / ONE_S_IN_NS; + out->tv_nsec = target_ns % ONE_S_IN_NS; +} + +/* + * pthread routine which flashes an LED + * @param flash_param light device pointer + */ +static void * flash_routine (void *flash_param) +{ + struct light_device_ext_t *dev = (struct light_device_ext_t *)flash_param; + struct light_state_t *flash_state; + int color = 0, rc = 0; + struct timespec target_time; + int64_t timestamp, period; + + if (dev == NULL) { + ALOGE("%s: Cannot flash a NULL light device", __func__); + return NULL; + } + + flash_state = &dev->state; + + pthread_mutex_lock(&dev->flash_signal_mutex); + + color = flash_state->color; + + /* Light flashing loop */ + while (flash_state->flashMode) { + rc = set_gpio_value(dev->pin, color); + if (rc != 0) { + ALOGE("%s: Cannot set light color", __func__); + goto mutex_unlock; + } + + timestamp = get_timestamp_monotonic(); + if (timestamp < 0) { + ALOGE("%s: Cannot get time from monotonic clock", __func__); + goto mutex_unlock; + } + + if (color) { + color = 0; + period = flash_state->flashOnMS * ONE_MS_IN_NS; + } else { + color = 1; + period = flash_state->flashOffMS * ONE_MS_IN_NS; + } + + /* check for overflow */ + if (timestamp > LLONG_MAX - period) { + ALOGE("%s: Timestamp overflow", __func__); + goto mutex_unlock; + } + + timestamp += period; + + /* sleep until target_time or the cond var is signaled */ + set_timestamp(&target_time, timestamp); + rc = pthread_cond_timedwait(&dev->flash_cond, &dev->flash_signal_mutex, &target_time); + if ((rc != 0) && (rc != ETIMEDOUT)) { + ALOGE("%s: pthread_cond_timedwait returned an error", __func__); + goto mutex_unlock; + } + } + +mutex_unlock: + pthread_mutex_unlock(&dev->flash_signal_mutex); + + return NULL; +} + +/* + * Check lights flash state + * @param state pointer to the state to check + * @return 0 if success, error code otherwise + */ +static int check_flash_state(struct light_state_t const *state) +{ + int64_t ns = 0; + + if ((state->flashOffMS < 0) || (state->flashOnMS < 0)) { + return EINVAL; + } + + if ((state->flashOffMS == 0) && (state->flashOnMS) == 0) { + return EINVAL; + } + + /* check for overflow in ns */ + ns = state->flashOffMS * ONE_MS_IN_NS; + if (ns / ONE_MS_IN_NS != state->flashOffMS) { + return EINVAL; + } + ns = state->flashOnMS * ONE_MS_IN_NS; + if (ns / ONE_MS_IN_NS != state->flashOnMS) { + return EINVAL; + } + + return 0; +} + +/* + * Generic function for setting the state of the light + * @param base_dev light device data structure + * @param state what state to set + * @return 0 if success, error code otherwise + */ +static int set_light_generic(struct light_device_t *base_dev, + struct light_state_t const *state) +{ + struct light_device_ext_t *dev = (struct light_device_ext_t *)base_dev; + struct light_state_t *current_state; + int rc = 0; + + if (dev == NULL) { + ALOGE("%s: Cannot set state for NULL device", __func__); + return EINVAL; + } + + current_state = &dev->state; + + pthread_mutex_lock(&dev->write_mutex); + + if (dev->refs == 0) { + ALOGE("%s: The light device is not opened", __func__); + pthread_mutex_unlock(&dev->write_mutex); + return EINVAL; + } + + ALOGV("%s: flashMode:%x, color:%x", __func__, state->flashMode, state->color); + + if (current_state->flashMode) { + /* destroy flashing thread */ + pthread_mutex_lock(&dev->flash_signal_mutex); + current_state->flashMode = LIGHT_FLASH_NONE; + pthread_cond_signal(&dev->flash_cond); + pthread_mutex_unlock(&dev->flash_signal_mutex); + pthread_join(dev->flash_thread, NULL); + } + + *current_state = *state; + + if (state->flashMode) { + /* start flashing thread */ + if (check_flash_state(current_state) == 0) { + rc = pthread_create(&dev->flash_thread, NULL, + flash_routine, (void *)dev); + if (rc != 0) { + ALOGE("%s: Cannot create flashing thread", __func__); + current_state->flashMode = LIGHT_FLASH_NONE; + } + } else { + ALOGE("%s: Flash state is invalid", __func__); + current_state->flashMode = LIGHT_FLASH_NONE; + } + } else { + rc = set_gpio_value(dev->pin, state->color); + if (rc != 0) { + ALOGE("%s: Cannot set light color.", __func__); + } + } + + pthread_mutex_unlock(&dev->write_mutex); + + return rc; +} + +/* + * Initialize light synchronization resources + * @param cond what condition variable to initialize + * @param signal_mutex what mutex (associated with the condvar) to initialize + * @return 0 if success, error code otherwise + */ +static int init_light_sync_resources(pthread_cond_t *cond, + pthread_mutex_t *signal_mutex) +{ + int rc = 0; + pthread_condattr_t condattr; + + rc = pthread_condattr_init(&condattr); + if (rc != 0) { + ALOGE("%s: Cannot initialize the pthread condattr", __func__); + return rc; + } + + rc = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); + if (rc != 0) { + ALOGE("%s: Cannot set the clock of condattr to monotonic", __func__); + goto destroy_condattr; + } + + rc = pthread_cond_init(cond, &condattr); + if (rc != 0) { + ALOGE("%s: Cannot intialize the pthread structure", __func__); + goto destroy_condattr; + } + + rc = pthread_mutex_init(signal_mutex, NULL); + if (rc != 0) { + ALOGE("%s: Cannot initialize the mutex associated with the pthread cond", __func__); + goto destroy_cond; + } + + pthread_condattr_destroy(&condattr); + return rc; + +destroy_cond: + pthread_cond_destroy(cond); +destroy_condattr: + pthread_condattr_destroy(&condattr); + return rc; +} + +/* + * Free light synchronization resources + * @param cond what condition variable to free + * @param signal_mutex what mutex (associated with the condvar) to free + */ +static void free_light_sync_resources(pthread_cond_t *cond, + pthread_mutex_t *signal_mutex) +{ + pthread_mutex_destroy(signal_mutex); + pthread_cond_destroy(cond); +} + +/* + * Close the lights module + * @param base_dev light device data structure + * @return 0 if success, error code otherwise + */ +static int close_lights(struct light_device_t *base_dev) +{ + struct light_device_ext_t *dev = (struct light_device_ext_t *)base_dev; + int rc = 0; + + if (dev == NULL) { + ALOGE("%s: Cannot deallocate a NULL light device", __func__); + return EINVAL; + } + + pthread_mutex_lock(&dev->write_mutex); + + if (dev->refs == 0) { + /* the light device is not open */ + rc = EINVAL; + goto mutex_unlock; + } else if (dev->refs > 1) { + goto dec_refs; + } + + if (dev->state.flashMode) { + /* destroy flashing thread */ + pthread_mutex_lock(&dev->flash_signal_mutex); + dev->state.flashMode = LIGHT_FLASH_NONE; + pthread_cond_signal(&dev->flash_cond); + pthread_mutex_unlock(&dev->flash_signal_mutex); + pthread_join(dev->flash_thread, NULL); + } + + free_light_sync_resources(&dev->flash_cond, + &dev->flash_signal_mutex); + +dec_refs: + dev->refs--; + +mutex_unlock: + pthread_mutex_unlock(&dev->write_mutex); + + return rc; +} + +/* + * Module initialization routine which detects the LEDs' GPIOs + * @param type light device type + * @return 0 if success, error code otherwise + */ +static int init_module(int type) +{ + mraa_gpio_context gpio = NULL; + + if (type < 0 || type >= LIGHTS_TYPE_NUM) { + return EINVAL; + } + + switch(mraa_get_platform_type()) { + case MRAA_INTEL_EDISON_FAB_C: + gpio = mraa_gpio_init_raw(TRI_STATE_ALL_GPIO_RAW_PIN); + if (gpio != NULL) { + /* Arduino board detected */ + mraa_gpio_close(gpio); + light_devices[type].pin = EDISON_ARDUINO_PINS[type]; + } else { + light_devices[type].pin = EDISON_MINIBOARD_PINS[type]; + } + break; + case MRAA_INTEL_MINNOWBOARD_MAX: + light_devices[type].pin = MINNOWBOARD_MAX_PINS[type]; + break; + default: + ALOGE("%s: Hardware platform not supported", __func__); + return EINVAL; + } + + return 0; +} + +/* + * Open a new lights device instance by name + * @param module associated hw module data structure + * @param name lights device name + * @param device where to store the pointer of the allocated device + * @return 0 if success, error code otherwise + */ +static int open_lights(const struct hw_module_t *module, char const *name, + struct hw_device_t **device) +{ + struct light_device_ext_t *dev; + int rc = 0, type = -1; + + ALOGV("%s: Opening %s lights module", __func__, name); + + if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) { + type = NOTIFICATIONS_TYPE; + } else { + return EINVAL; + } + + dev = (struct light_device_ext_t *)(light_devices + type); + + pthread_mutex_lock(&dev->write_mutex); + + if (dev->refs != 0) { + /* already opened; nothing to do */ + goto inc_refs; + } + + rc = init_module(type); + if (rc != 0) { + ALOGE("%s: Failed to initialize lights module", __func__); + goto mutex_unlock; + } + + rc = init_light_sync_resources(&dev->flash_cond, + &dev->flash_signal_mutex); + if (rc != 0) { + goto mutex_unlock; + } + + dev->base_dev.common.tag = HARDWARE_DEVICE_TAG; + dev->base_dev.common.version = 0; + dev->base_dev.common.module = (struct hw_module_t *)module; + dev->base_dev.common.close = (int (*)(struct hw_device_t *))close_lights; + dev->base_dev.set_light = set_light_generic; + +inc_refs: + dev->refs++; + *device = (struct hw_device_t *)dev; + +mutex_unlock: + pthread_mutex_unlock(&dev->write_mutex); + return rc; +} + +static struct hw_module_methods_t lights_methods = +{ + .open = open_lights, +}; + +struct hw_module_t HAL_MODULE_INFO_SYM = +{ + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = LIGHTS_HARDWARE_MODULE_ID, + .name = "Edison lights module", + .author = "Intel", + .methods = &lights_methods, +}; |