1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * Based on htc/flounder/lights/lights.h 19 */ 20 21#define LOG_TAG "lights" 22 23#include <malloc.h> 24#include <string.h> 25#include <unistd.h> 26#include <errno.h> 27#include <cutils/log.h> 28#include <hardware/lights.h> 29#include <hardware/hardware.h> 30#include <gpio.h> 31 32/* List of supported lights */ 33typedef enum { 34 NOTIFICATIONS_TYPE, 35 LIGHTS_TYPE_NUM 36} light_type_t; 37 38/* Light device data structure */ 39struct light_device_ext_t { 40 /* Base device */ 41 struct light_device_t base_dev; 42 /* Physical pin */ 43 int pin; 44 /* Current state of the light device */ 45 struct light_state_t state; 46 /* Number of device references */ 47 int refs; 48 /* Synchronization attributes */ 49 pthread_t flash_thread; 50 pthread_cond_t flash_cond; 51 pthread_mutex_t flash_signal_mutex; 52 pthread_mutex_t write_mutex; 53 /* Transform function to apply on value */ 54 int (*transform)(int); 55}; 56 57static int64_t const ONE_MS_IN_NS = 1000000LL; 58static int64_t const ONE_S_IN_NS = 1000000000LL; 59 60/* 61 * Platform version strings used to identify board versions 62 */ 63static char * const EDISON_ARDUINO_PLATFORM_VERSION = "arduino"; 64static char * const MINNOWBOARD_TURBOT_PLATFORM_VERSION = "Turbot"; 65 66/* 67 * Pin constants 68 * Please add a pin to EDISON_ARDUINO_PINS, EDISON_MINIBOARD_PINS & 69 * MINNOWBOARD_MAX_PINS when you add a new light type 70 */ 71static int const EDISON_ARDUINO_PINS[LIGHTS_TYPE_NUM] = {13}; 72static int const EDISON_MINIBOARD_PINS[LIGHTS_TYPE_NUM] = {31}; 73static int const MINNOWBOARD_MAX_PINS[LIGHTS_TYPE_NUM] = {21}; 74static int const MINNOWBOARD_TURBOT_PINS[LIGHTS_TYPE_NUM] = {27}; 75 76/* 77 * Array of light devices with write_mutex statically initialized 78 * to be able to synchronize the open_lights & close_lights functions 79 */ 80struct light_device_ext_t light_devices[] = { 81 [ 0 ... (LIGHTS_TYPE_NUM - 1) ] = { .write_mutex = PTHREAD_MUTEX_INITIALIZER } 82}; 83 84/* 85 * Set the GPIO value 86 * @param pin physical pin of the GPIO 87 * @param value what value to set 88 * @return 0 if success, error code otherwise 89 */ 90static int set_gpio_value(int pin, int value) 91{ 92 mraa_gpio_context gpio = NULL; 93 int rc = 0; 94 95 if ((value != 0) && (value != 1)) { 96 return EINVAL; 97 } 98 99 gpio = mraa_gpio_init(pin); 100 if (gpio == NULL) { 101 return EPERM; 102 } 103 104 if (mraa_gpio_dir(gpio, MRAA_GPIO_OUT) != MRAA_SUCCESS) { 105 rc = EPERM; 106 goto close_gpio; 107 } 108 109 if (mraa_gpio_write(gpio, value) != MRAA_SUCCESS) { 110 rc = EPERM; 111 } 112 113close_gpio: 114 if (mraa_gpio_close(gpio) != MRAA_SUCCESS) { 115 rc = EPERM; 116 } 117 118 return rc; 119} 120 121/* 122 * Invert value 123 * @param value what value to invert 124 * @return value inverted 125 */ 126static int invert_value(int value) { 127 return value ? 0 : 1; 128} 129 130/* 131 * Get current timestamp in nanoseconds 132 * @return time in nanoseconds 133 */ 134int64_t get_timestamp_monotonic() 135{ 136 struct timespec ts = {0, 0}; 137 138 if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { 139 return ONE_S_IN_NS * ts.tv_sec + ts.tv_nsec; 140 } 141 142 return -1; 143} 144 145/* 146 * Populates a timespec data structure from a int64_t timestamp 147 * @param out what timespec to populate 148 * @param target_ns timestamp in nanoseconds 149 */ 150void set_timestamp(struct timespec *out, int64_t target_ns) 151{ 152 out->tv_sec = target_ns / ONE_S_IN_NS; 153 out->tv_nsec = target_ns % ONE_S_IN_NS; 154} 155 156/* 157 * pthread routine which flashes an LED 158 * @param flash_param light device pointer 159 */ 160static void * flash_routine (void *flash_param) 161{ 162 struct light_device_ext_t *dev = (struct light_device_ext_t *)flash_param; 163 struct light_state_t *flash_state; 164 int color = 0, rc = 0; 165 struct timespec target_time; 166 int64_t timestamp, period; 167 168 if (dev == NULL) { 169 ALOGE("%s: Cannot flash a NULL light device", __func__); 170 return NULL; 171 } 172 173 flash_state = &dev->state; 174 175 pthread_mutex_lock(&dev->flash_signal_mutex); 176 177 color = flash_state->color; 178 179 /* Light flashing loop */ 180 while (flash_state->flashMode) { 181 rc = set_gpio_value(dev->pin, color); 182 if (rc != 0) { 183 ALOGE("%s: Cannot set light color", __func__); 184 goto mutex_unlock; 185 } 186 187 timestamp = get_timestamp_monotonic(); 188 if (timestamp < 0) { 189 ALOGE("%s: Cannot get time from monotonic clock", __func__); 190 goto mutex_unlock; 191 } 192 193 if (color) { 194 color = 0; 195 period = flash_state->flashOnMS * ONE_MS_IN_NS; 196 } else { 197 color = 1; 198 period = flash_state->flashOffMS * ONE_MS_IN_NS; 199 } 200 201 /* check for overflow */ 202 if (timestamp > LLONG_MAX - period) { 203 ALOGE("%s: Timestamp overflow", __func__); 204 goto mutex_unlock; 205 } 206 207 timestamp += period; 208 209 /* sleep until target_time or the cond var is signaled */ 210 set_timestamp(&target_time, timestamp); 211 rc = pthread_cond_timedwait(&dev->flash_cond, &dev->flash_signal_mutex, &target_time); 212 if ((rc != 0) && (rc != ETIMEDOUT)) { 213 ALOGE("%s: pthread_cond_timedwait returned an error", __func__); 214 goto mutex_unlock; 215 } 216 } 217 218mutex_unlock: 219 pthread_mutex_unlock(&dev->flash_signal_mutex); 220 221 return NULL; 222} 223 224/* 225 * Check lights flash state 226 * @param state pointer to the state to check 227 * @return 0 if success, error code otherwise 228 */ 229static int check_flash_state(struct light_state_t const *state) 230{ 231 int64_t ns = 0; 232 233 if ((state->flashOffMS < 0) || (state->flashOnMS < 0)) { 234 return EINVAL; 235 } 236 237 if ((state->flashOffMS == 0) && (state->flashOnMS) == 0) { 238 return EINVAL; 239 } 240 241 /* check for overflow in ns */ 242 ns = state->flashOffMS * ONE_MS_IN_NS; 243 if (ns / ONE_MS_IN_NS != state->flashOffMS) { 244 return EINVAL; 245 } 246 ns = state->flashOnMS * ONE_MS_IN_NS; 247 if (ns / ONE_MS_IN_NS != state->flashOnMS) { 248 return EINVAL; 249 } 250 251 return 0; 252} 253 254/* 255 * Generic function for setting the state of the light 256 * @param base_dev light device data structure 257 * @param state what state to set 258 * @return 0 if success, error code otherwise 259 */ 260static int set_light_generic(struct light_device_t *base_dev, 261 struct light_state_t const *state) 262{ 263 struct light_device_ext_t *dev = (struct light_device_ext_t *)base_dev; 264 struct light_state_t *current_state; 265 int rc = 0; 266 267 if (dev == NULL) { 268 ALOGE("%s: Cannot set state for NULL device", __func__); 269 return EINVAL; 270 } 271 272 current_state = &dev->state; 273 274 pthread_mutex_lock(&dev->write_mutex); 275 276 if (dev->refs == 0) { 277 ALOGE("%s: The light device is not opened", __func__); 278 pthread_mutex_unlock(&dev->write_mutex); 279 return EINVAL; 280 } 281 282 ALOGV("%s: flashMode:%x, color:%x", __func__, state->flashMode, state->color); 283 284 if (current_state->flashMode) { 285 /* destroy flashing thread */ 286 pthread_mutex_lock(&dev->flash_signal_mutex); 287 current_state->flashMode = LIGHT_FLASH_NONE; 288 pthread_cond_signal(&dev->flash_cond); 289 pthread_mutex_unlock(&dev->flash_signal_mutex); 290 pthread_join(dev->flash_thread, NULL); 291 } 292 293 *current_state = *state; 294 if (dev->transform != NULL) { 295 current_state->color = dev->transform(current_state->color); 296 } 297 298 if (current_state->flashMode) { 299 /* start flashing thread */ 300 if (check_flash_state(current_state) == 0) { 301 rc = pthread_create(&dev->flash_thread, NULL, 302 flash_routine, (void *)dev); 303 if (rc != 0) { 304 ALOGE("%s: Cannot create flashing thread", __func__); 305 current_state->flashMode = LIGHT_FLASH_NONE; 306 } 307 } else { 308 ALOGE("%s: Flash state is invalid", __func__); 309 current_state->flashMode = LIGHT_FLASH_NONE; 310 } 311 } else { 312 rc = set_gpio_value(dev->pin, current_state->color); 313 if (rc != 0) { 314 ALOGE("%s: Cannot set light color.", __func__); 315 } 316 } 317 318 pthread_mutex_unlock(&dev->write_mutex); 319 320 return rc; 321} 322 323/* 324 * Initialize light synchronization resources 325 * @param cond what condition variable to initialize 326 * @param signal_mutex what mutex (associated with the condvar) to initialize 327 * @return 0 if success, error code otherwise 328 */ 329static int init_light_sync_resources(pthread_cond_t *cond, 330 pthread_mutex_t *signal_mutex) 331{ 332 int rc = 0; 333 pthread_condattr_t condattr; 334 335 rc = pthread_condattr_init(&condattr); 336 if (rc != 0) { 337 ALOGE("%s: Cannot initialize the pthread condattr", __func__); 338 return rc; 339 } 340 341 rc = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC); 342 if (rc != 0) { 343 ALOGE("%s: Cannot set the clock of condattr to monotonic", __func__); 344 goto destroy_condattr; 345 } 346 347 rc = pthread_cond_init(cond, &condattr); 348 if (rc != 0) { 349 ALOGE("%s: Cannot intialize the pthread structure", __func__); 350 goto destroy_condattr; 351 } 352 353 rc = pthread_mutex_init(signal_mutex, NULL); 354 if (rc != 0) { 355 ALOGE("%s: Cannot initialize the mutex associated with the pthread cond", __func__); 356 goto destroy_cond; 357 } 358 359 pthread_condattr_destroy(&condattr); 360 return rc; 361 362destroy_cond: 363 pthread_cond_destroy(cond); 364destroy_condattr: 365 pthread_condattr_destroy(&condattr); 366 return rc; 367} 368 369/* 370 * Free light synchronization resources 371 * @param cond what condition variable to free 372 * @param signal_mutex what mutex (associated with the condvar) to free 373 */ 374static void free_light_sync_resources(pthread_cond_t *cond, 375 pthread_mutex_t *signal_mutex) 376{ 377 pthread_mutex_destroy(signal_mutex); 378 pthread_cond_destroy(cond); 379} 380 381/* 382 * Close the lights module 383 * @param base_dev light device data structure 384 * @return 0 if success, error code otherwise 385 */ 386static int close_lights(struct light_device_t *base_dev) 387{ 388 struct light_device_ext_t *dev = (struct light_device_ext_t *)base_dev; 389 int rc = 0; 390 391 if (dev == NULL) { 392 ALOGE("%s: Cannot deallocate a NULL light device", __func__); 393 return EINVAL; 394 } 395 396 pthread_mutex_lock(&dev->write_mutex); 397 398 if (dev->refs == 0) { 399 /* the light device is not open */ 400 rc = EINVAL; 401 goto mutex_unlock; 402 } else if (dev->refs > 1) { 403 goto dec_refs; 404 } 405 406 if (dev->state.flashMode) { 407 /* destroy flashing thread */ 408 pthread_mutex_lock(&dev->flash_signal_mutex); 409 dev->state.flashMode = LIGHT_FLASH_NONE; 410 pthread_cond_signal(&dev->flash_cond); 411 pthread_mutex_unlock(&dev->flash_signal_mutex); 412 pthread_join(dev->flash_thread, NULL); 413 } 414 415 free_light_sync_resources(&dev->flash_cond, 416 &dev->flash_signal_mutex); 417 418dec_refs: 419 dev->refs--; 420 421mutex_unlock: 422 pthread_mutex_unlock(&dev->write_mutex); 423 424 return rc; 425} 426 427/* 428 * Module initialization routine which detects the LEDs' GPIOs 429 * @param type light device type 430 * @return 0 if success, error code otherwise 431 */ 432static int init_module(int type) 433{ 434 const char *platform_version = NULL; 435 436 if (type < 0 || type >= LIGHTS_TYPE_NUM) { 437 return EINVAL; 438 } 439 440 light_devices[type].transform = NULL; 441 442 switch(mraa_get_platform_type()) { 443 case MRAA_INTEL_EDISON_FAB_C: 444 platform_version = mraa_get_platform_version(MRAA_MAIN_PLATFORM_OFFSET); 445 if ((platform_version != NULL) && (strncmp(platform_version, 446 EDISON_ARDUINO_PLATFORM_VERSION, 447 strlen(EDISON_ARDUINO_PLATFORM_VERSION)) == 0)) { 448 light_devices[type].pin = EDISON_ARDUINO_PINS[type]; 449 } else { 450 light_devices[type].pin = EDISON_MINIBOARD_PINS[type]; 451 } 452 break; 453 case MRAA_INTEL_MINNOWBOARD_MAX: 454 platform_version = mraa_get_platform_version(MRAA_MAIN_PLATFORM_OFFSET); 455 if ((platform_version != NULL) && (strncmp(platform_version, 456 MINNOWBOARD_TURBOT_PLATFORM_VERSION, 457 strlen(MINNOWBOARD_TURBOT_PLATFORM_VERSION)) == 0)) { 458 light_devices[type].pin = MINNOWBOARD_TURBOT_PINS[type]; 459 light_devices[type].transform = invert_value; 460 } else { 461 light_devices[type].pin = MINNOWBOARD_MAX_PINS[type]; 462 } 463 break; 464 default: 465 ALOGE("%s: Hardware platform not supported", __func__); 466 return EINVAL; 467 } 468 469 return 0; 470} 471 472/* 473 * Open a new lights device instance by name 474 * @param module associated hw module data structure 475 * @param name lights device name 476 * @param device where to store the pointer of the allocated device 477 * @return 0 if success, error code otherwise 478 */ 479static int open_lights(const struct hw_module_t *module, char const *name, 480 struct hw_device_t **device) 481{ 482 struct light_device_ext_t *dev; 483 int rc = 0, type = -1; 484 485 ALOGV("%s: Opening %s lights module", __func__, name); 486 487 if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) { 488 type = NOTIFICATIONS_TYPE; 489 } else { 490 return EINVAL; 491 } 492 493 dev = (struct light_device_ext_t *)(light_devices + type); 494 495 pthread_mutex_lock(&dev->write_mutex); 496 497 if (dev->refs != 0) { 498 /* already opened; nothing to do */ 499 goto inc_refs; 500 } 501 502 rc = init_module(type); 503 if (rc != 0) { 504 ALOGE("%s: Failed to initialize lights module", __func__); 505 goto mutex_unlock; 506 } 507 508 rc = init_light_sync_resources(&dev->flash_cond, 509 &dev->flash_signal_mutex); 510 if (rc != 0) { 511 goto mutex_unlock; 512 } 513 514 dev->base_dev.common.tag = HARDWARE_DEVICE_TAG; 515 dev->base_dev.common.version = 0; 516 dev->base_dev.common.module = (struct hw_module_t *)module; 517 dev->base_dev.common.close = (int (*)(struct hw_device_t *))close_lights; 518 dev->base_dev.set_light = set_light_generic; 519 520inc_refs: 521 dev->refs++; 522 *device = (struct hw_device_t *)dev; 523 524mutex_unlock: 525 pthread_mutex_unlock(&dev->write_mutex); 526 return rc; 527} 528 529static struct hw_module_methods_t lights_methods = 530{ 531 .open = open_lights, 532}; 533 534struct hw_module_t HAL_MODULE_INFO_SYM = 535{ 536 .tag = HARDWARE_MODULE_TAG, 537 .version_major = 1, 538 .version_minor = 0, 539 .id = LIGHTS_HARDWARE_MODULE_ID, 540 .name = "Edison lights module", 541 .author = "Intel", 542 .methods = &lights_methods, 543}; 544