summaryrefslogtreecommitdiff
path: root/peripheral/light/mraa/lights.c
diff options
context:
space:
mode:
Diffstat (limited to 'peripheral/light/mraa/lights.c')
-rw-r--r--peripheral/light/mraa/lights.c518
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,
+};