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 #define LOG_TAG "lights" 18 #include <cutils/log.h> 19 #include <stdint.h> 20 #include <string.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <pthread.h> 24 #include <sys/ioctl.h> 25 #include <sys/types.h> 26 #include <hardware/lights.h> 27 #include <linux/leds-an30259a.h> 28 29 static pthread_once_t g_init = PTHREAD_ONCE_INIT; 30 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; 31 32 char const *const LCD_FILE = "/sys/class/backlight/s6e8aa0/brightness"; 33 char const *const LED_FILE = "/dev/an30259a_leds"; 34 35 #define IMAX 0 // 12.75mA power consumption 36 37 // Slope values, based on total blink of 1000ms 38 #define SLOPE_UP_1 450 39 #define SLOPE_UP_2 (500-SLOPE_UP_1) 40 #define SLOPE_DOWN_1 SLOPE_UP_2 41 #define SLOPE_DOWN_2 SLOPE_UP_1 42 // brightness at mid-slope, on 0 - 127 scale 43 #define MID_BRIGHTNESS 31 44 45 void init_g_lock(void) 46 { 47 pthread_mutex_init(&g_lock, NULL); 48 } 49 50 static int write_int(char const *path, int value) 51 { 52 int fd; 53 static int already_warned; 54 55 already_warned = 0; 56 57 ALOGV("write_int: path %s, value %d", path, value); 58 fd = open(path, O_RDWR); 59 60 if (fd >= 0) { 61 char buffer[20]; 62 int bytes = sprintf(buffer, "%d\n", value); 63 int amt = write(fd, buffer, bytes); 64 close(fd); 65 return amt == -1 ? -errno : 0; 66 } else { 67 if (already_warned == 0) { 68 ALOGE("write_int failed to open %s\n", path); 69 already_warned = 1; 70 } 71 return -errno; 72 } 73 } 74 75 static int rgb_to_brightness(struct light_state_t const *state) 76 { 77 int color = state->color & 0x00ffffff; 78 79 return ((77*((color>>16) & 0x00ff)) 80 + (150*((color>>8) & 0x00ff)) + (29*(color & 0x00ff))) >> 8; 81 } 82 83 static int set_light_backlight(struct light_device_t *dev, 84 struct light_state_t const *state) 85 { 86 int err = 0; 87 int brightness = rgb_to_brightness(state); 88 89 pthread_mutex_lock(&g_lock); 90 err = write_int(LCD_FILE, brightness); 91 92 pthread_mutex_unlock(&g_lock); 93 return err; 94 } 95 96 static int close_lights(struct light_device_t *dev) 97 { 98 ALOGV("close_light is called"); 99 if (dev) 100 free(dev); 101 102 return 0; 103 } 104 105 /* LEDs */ 106 static int write_leds(struct an30259a_pr_control *led) 107 { 108 int err = 0; 109 int imax = IMAX; 110 int fd; 111 112 pthread_mutex_lock(&g_lock); 113 114 fd = open(LED_FILE, O_RDWR); 115 if (fd >= 0) { 116 err = ioctl(fd, AN30259A_PR_SET_IMAX, &imax); 117 if (err) 118 ALOGE("failed to set imax"); 119 120 err = ioctl(fd, AN30259A_PR_SET_LED, led); 121 if (err < 0) 122 ALOGE("failed to set leds!"); 123 124 close(fd); 125 } else { 126 ALOGE("failed to open %s!", LED_FILE); 127 err = -errno; 128 } 129 130 pthread_mutex_unlock(&g_lock); 131 132 return err; 133 } 134 135 static int set_light_leds(struct light_state_t const *state, int type) 136 { 137 struct an30259a_pr_control led; 138 139 memset(&led, 0, sizeof(led)); 140 141 switch (state->flashMode) { 142 case LIGHT_FLASH_NONE: 143 led.state = LED_LIGHT_OFF; 144 break; 145 case LIGHT_FLASH_TIMED: 146 case LIGHT_FLASH_HARDWARE: 147 led.state = LED_LIGHT_SLOPE; 148 led.color = state->color & 0x00ffffff; 149 // tweak to eliminate purplish tint from white color 150 if (led.color == 0x00ffffff) 151 led.color = 0x80ff80; 152 // scale slope times based on flashOnMS 153 led.time_slope_up_1 = (SLOPE_UP_1 * state->flashOnMS) / 1000; 154 led.time_slope_up_2 = (SLOPE_UP_2 * state->flashOnMS) / 1000; 155 led.time_slope_down_1 = (SLOPE_DOWN_1 * state->flashOnMS) / 1000; 156 led.time_slope_down_2 = (SLOPE_DOWN_2 * state->flashOnMS) / 1000; 157 led.mid_brightness = MID_BRIGHTNESS; 158 led.time_off = state->flashOffMS; 159 break; 160 default: 161 return -EINVAL; 162 } 163 164 return write_leds(&led); 165 } 166 167 static int set_light_leds_notifications(struct light_device_t *dev, 168 struct light_state_t const *state) 169 { 170 return set_light_leds(state, 0); 171 } 172 173 static int set_light_leds_attention(struct light_device_t *dev, 174 struct light_state_t const *state) 175 { 176 return set_light_leds(state, 1); 177 } 178 179 static int open_lights(const struct hw_module_t *module, char const *name, 180 struct hw_device_t **device) 181 { 182 int (*set_light)(struct light_device_t *dev, 183 struct light_state_t const *state); 184 185 if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) 186 set_light = set_light_backlight; 187 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) 188 set_light = set_light_leds_notifications; 189 else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) 190 set_light = set_light_leds_attention; 191 else 192 return -EINVAL; 193 194 pthread_once(&g_init, init_g_lock); 195 196 struct light_device_t *dev = malloc(sizeof(struct light_device_t)); 197 memset(dev, 0, sizeof(*dev)); 198 199 dev->common.tag = HARDWARE_DEVICE_TAG; 200 dev->common.version = 0; 201 dev->common.module = (struct hw_module_t *)module; 202 dev->common.close = (int (*)(struct hw_device_t *))close_lights; 203 dev->set_light = set_light; 204 205 *device = (struct hw_device_t *)dev; 206 207 return 0; 208 } 209 210 static struct hw_module_methods_t lights_module_methods = { 211 .open = open_lights, 212 }; 213 214 struct hw_module_t HAL_MODULE_INFO_SYM = { 215 .tag = HARDWARE_MODULE_TAG, 216 .version_major = 1, 217 .version_minor = 0, 218 .id = LIGHTS_HARDWARE_MODULE_ID, 219 .name = "lights Module", 220 .author = "Google, Inc.", 221 .methods = &lights_module_methods, 222 }; 223