1 /* 2 * Copyright (C) 2014 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 19 #include <cutils/log.h> 20 21 #include <stdint.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 28 #include <sys/ioctl.h> 29 #include <sys/types.h> 30 31 #include <hardware/lights.h> 32 #include <hardware/hardware.h> 33 34 #define ALOG_ONCE(mask, op, ...) \ 35 do { \ 36 if (!(mask & op)) { \ 37 ALOGE(__VA_ARGS__); \ 38 mask |= op; \ 39 } \ 40 } while (0); 41 #define OP_WRITE_OPEN (1 << 0) 42 #define OP_BRIGHTNESS_PATH (1 << 1) 43 #define OP_BRIGHTNESS_VALUE (1 << 2) 44 #define OP_BRIGHTNESS_WRITE (1 << 3) 45 #define OP_MAX_BRIGHTNESS_PATH (1 << 4) 46 #define OP_MAX_BRIGHTNESS_OPEN (1 << 5) 47 #define OP_MAX_BRIGHTNESS_READ (1 << 6) 48 49 struct dragon_lights { 50 struct light_device_t base; 51 52 pthread_mutex_t lock; 53 char const *sysfs_path; 54 55 int max_brightness; 56 57 unsigned long logged_failures; 58 }; 59 60 static char const * kBacklightPath = 61 "/sys/class/backlight/lpm102a188a-backlight"; 62 static const int kNumBrightnessLevels = 16; 63 static const int kBrightnessLevels[] = 64 {8, 25, 30, 40, 55, 65, 75, 85, 95, 105, 120, 135, 160, 180, 200, 220}; 65 66 static struct dragon_lights *to_dragon_lights(struct light_device_t *dev) 67 { 68 return (struct dragon_lights *)dev; 69 } 70 71 static int write_brightness(struct dragon_lights *lights, int brightness) 72 { 73 char buffer[20], path[PATH_MAX]; 74 int fd, bytes, amt, ret = 0; 75 76 bytes = snprintf(path, sizeof(path), "%s/brightness", 77 lights->sysfs_path); 78 if (bytes < 0 || (size_t)bytes >= sizeof(path)) { 79 ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_PATH, 80 "failed to create brightness path %d\n", bytes); 81 return -EINVAL; 82 } 83 84 fd = open(path, O_RDWR); 85 if (fd < 0) { 86 ALOG_ONCE(lights->logged_failures, OP_WRITE_OPEN, 87 "write_int failed to open %s/%d\n", path, errno); 88 return -errno; 89 } 90 91 bytes = snprintf(buffer, sizeof(buffer), "%d\n", brightness); 92 if (bytes < 0 || (size_t)bytes >= sizeof(buffer)) { 93 ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_VALUE, 94 "failed to create brightness value %d/%d\n", 95 brightness, bytes); 96 ret = -EINVAL; 97 goto out; 98 } 99 amt = write(fd, buffer, bytes); 100 if (amt != bytes) { 101 ALOG_ONCE(lights->logged_failures, OP_BRIGHTNESS_WRITE, 102 "failed to write brightness value %d/%d\n", amt, 103 bytes); 104 ret = amt == -1 ? -errno : -EINVAL; 105 goto out; 106 } 107 108 out: 109 close(fd); 110 return ret; 111 } 112 113 static int read_max_brightness(struct dragon_lights *lights, int *value) 114 { 115 char buffer[20], path[PATH_MAX]; 116 int ret = 0, fd, bytes; 117 118 bytes = snprintf(path, sizeof(path), "%s/max_brightness", 119 lights->sysfs_path); 120 if (bytes < 0 || (size_t)bytes >= sizeof(path)) { 121 ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_PATH, 122 "failed to create max_brightness path %d\n", bytes); 123 return -EINVAL; 124 } 125 126 fd = open(path, O_RDONLY); 127 if (fd < 0) { 128 ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_OPEN, 129 "failed to open max_brightness %s/%d\n", path, errno); 130 return -errno; 131 } 132 133 bytes = read(fd, buffer, sizeof(buffer)); 134 if (bytes <= 0) { 135 ALOG_ONCE(lights->logged_failures, OP_MAX_BRIGHTNESS_READ, 136 "failed to read max_brightness %s/%d\n", path, errno); 137 ret = -errno; 138 goto out; 139 } 140 141 *value = atoi(buffer); 142 143 out: 144 close(fd); 145 return ret; 146 } 147 148 static int rgb_to_brightness(struct light_state_t const *state) 149 { 150 int color = state->color & 0x00ffffff; 151 return ((77 * ((color >> 16) & 0x00ff)) 152 + (150 * ((color >> 8) & 0x00ff)) + 153 (29 * (color & 0x00ff))) >> 8; 154 } 155 156 static int set_light_backlight(struct light_device_t *dev, 157 struct light_state_t const *state) 158 { 159 struct dragon_lights *lights = to_dragon_lights(dev); 160 int err, brightness_idx; 161 int brightness = rgb_to_brightness(state); 162 163 if (brightness > 0) { 164 // Get the bin number for brightness (0 to kNumBrightnessLevels - 1) 165 brightness_idx = (brightness - 1) * kNumBrightnessLevels / 0xff; 166 167 // Get brightness level 168 brightness = kBrightnessLevels[brightness_idx]; 169 } 170 171 pthread_mutex_lock(&lights->lock); 172 err = write_brightness(lights, brightness); 173 pthread_mutex_unlock(&lights->lock); 174 175 return err; 176 } 177 178 static int close_lights(struct hw_device_t *dev) 179 { 180 struct dragon_lights *lights = (struct dragon_lights *)dev; 181 if (lights) 182 free(lights); 183 return 0; 184 } 185 186 static int open_lights(const struct hw_module_t *module, char const *name, 187 struct hw_device_t **device) 188 { 189 struct dragon_lights *lights; 190 int ret; 191 192 // Only support backlight at the moment 193 if (strcmp(LIGHT_ID_BACKLIGHT, name)) 194 return -EINVAL; 195 196 lights = malloc(sizeof(*lights)); 197 if (lights == NULL) { 198 ALOGE("failed to allocate lights memory"); 199 return -ENOMEM; 200 } 201 memset(lights, 0, sizeof(*lights)); 202 203 pthread_mutex_init(&lights->lock, NULL); 204 205 lights->sysfs_path = kBacklightPath; 206 207 ret = read_max_brightness(lights, &lights->max_brightness); 208 if (ret) { 209 close_lights((struct hw_device_t *)lights); 210 return ret; 211 } 212 213 lights->base.common.tag = HARDWARE_DEVICE_TAG; 214 lights->base.common.version = 0; 215 lights->base.common.module = (struct hw_module_t *)module; 216 lights->base.common.close = close_lights; 217 lights->base.set_light = set_light_backlight; 218 219 *device = (struct hw_device_t *)lights; 220 return 0; 221 } 222 223 static struct hw_module_methods_t lights_methods = { 224 .open = open_lights, 225 }; 226 227 struct hw_module_t HAL_MODULE_INFO_SYM = { 228 .tag = HARDWARE_MODULE_TAG, 229 .version_major = 1, 230 .version_minor = 0, 231 .id = LIGHTS_HARDWARE_MODULE_ID, 232 .name = "dragon lights module", 233 .author = "Google, Inc.", 234 .methods = &lights_methods, 235 }; 236