1 /*
2 * Copyright (C) 2022 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 #include <aidl/android/hardware/light/BnLights.h>
18 #include <aidl/com/google/hardware/pixel/display/IDisplay.h>
19 #include <android-base/logging.h>
20 #include <android/binder_manager.h>
21 #include <android/binder_process.h>
22 #include <fcntl.h>
23
24 #include "led_lut_calibrator.h"
25
26 using ::aidl::android::hardware::light::BnLights;
27 using ::aidl::android::hardware::light::HwLight;
28 using ::aidl::android::hardware::light::HwLightState;
29 using ::aidl::android::hardware::light::ILights;
30 using ::aidl::android::hardware::light::LightType;
31 using ::ndk::ScopedAStatus;
32 using ::ndk::SharedRefBase;
33 using ::ndk::SpAIBinder;
34
35 using aidl::com::google::hardware::pixel::display::IDisplay;
36 using PanelCalibrationStatus = aidl::com::google::hardware::pixel::display::PanelCalibrationStatus;
37
38 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
39
40 char const *const GREEN_LED_FILE = "/sys/class/leds/green/brightness";
41
42 enum {
43 ARGB_DAY = 0x0000ff00,
44 ARGB_DUSK = 0x0000c000,
45 ARGB_NIGHT = 0x00008000,
46 ARGB_OFF = 0x00000000
47 };
48
49 enum {
50 PWM_DAY_DEF = 0x300,
51 PWM_DUSK_DEF = 0x080,
52 PWM_NIGHT_DEF = 0x040,
53 PWM_OFF_DEF = 0x000
54 };
55
56 enum {
57 MODE_DAY = 0xfff,
58 MODE_NIGHT = 0x000
59 };
60
61 struct ARGBToPWM {
62 int argb;
63 int pwm;
64 };
65
66 static struct ARGBToPWM pwmIds[] = {
67 {ARGB_DAY, PWM_DAY_DEF},
68 {ARGB_DUSK, PWM_DUSK_DEF},
69 {ARGB_NIGHT, PWM_NIGHT_DEF},
70 {ARGB_OFF, PWM_OFF_DEF}
71 };
72
sys_write_int(int fd,int value)73 static int sys_write_int(int fd, int value) {
74 char buffer[16];
75 size_t bytes;
76 ssize_t amount;
77
78 bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
79 if (bytes >= sizeof(buffer)) {
80 return -EINVAL;
81 }
82 amount = write(fd, buffer, bytes);
83
84 return amount == -1 ? -errno : 0;
85 }
86
WaitForDisplayService()87 static SpAIBinder WaitForDisplayService() {
88 LOG(INFO) << "waiting for display service to appear";
89 static std::mutex svc_mutex;
90 static SpAIBinder svc;
91 std::unique_lock<std::mutex> l(svc_mutex);
92 uint8_t retrycount = 0;
93
94 if (svc != nullptr && AIBinder_isAlive(svc.get())) return svc;
95
96 while (true && retrycount < 20) {
97 svc = SpAIBinder(AServiceManager_waitForService(
98 "com.google.hardware.pixel.display.IDisplay/default"));
99 if (svc != nullptr) {
100 LOG(INFO) << "A wild display service has appeared!";
101 return svc;
102 }
103 retrycount++;
104 sleep(2);
105 }
106
107 LOG(ERROR) << "Failed to get the display service";
108 return NULL;
109 }
110
111 class Lights : public BnLights {
112 private:
113 std::vector<HwLight> availableLights;
114 LedLutCalibrator *calibrator;
115
addLight(LightType const type,int const ordinal)116 void addLight(LightType const type, int const ordinal) {
117 HwLight light{};
118 light.id = availableLights.size();
119 light.type = type;
120 light.ordinal = ordinal;
121 availableLights.emplace_back(light);
122 }
123
writeLed(const char * path,int color)124 void writeLed(const char *path, int color) {
125 int fd = open(path, O_WRONLY);
126 if (fd < 0) {
127 LOG(ERROR) << "Failed to open LED device " << path << strerror(errno);
128 return;
129 }
130 sys_write_int(fd, color);
131 close(fd);
132 }
133
134 public:
Lights()135 Lights() : BnLights() {
136 pthread_mutex_init(&g_lock, NULL);
137
138 addLight(LightType::BACKLIGHT, 0);
139 addLight(LightType::KEYBOARD, 0);
140 addLight(LightType::BUTTONS, 0);
141 addLight(LightType::BATTERY, 0);
142 addLight(LightType::NOTIFICATIONS, 0);
143 addLight(LightType::ATTENTION, 0);
144 addLight(LightType::BLUETOOTH, 0);
145 addLight(LightType::WIFI, 0);
146 addLight(LightType::MICROPHONE, 0);
147 addLight(LightType::CAMERA, 0);
148
149 SpAIBinder display_service = WaitForDisplayService();
150 auto ser = IDisplay::fromBinder(display_service);
151 PanelCalibrationStatus cal;
152 ser->getPanelCalibrationStatus(&cal);
153 calibrator = new LedLutCalibrator(cal);
154
155 int cal_color = 0;
156 cal_color = calibrator->GetByColorIntensity("green", MODE_DAY);
157 if (cal_color >= 0) {
158 pwmIds[0].pwm = cal_color;
159 }
160 cal_color = calibrator->GetByColorIntensity("green", MODE_NIGHT);
161 if (cal_color >= 0) {
162 pwmIds[1].pwm = cal_color << 1;
163 pwmIds[2].pwm = cal_color;
164 }
165 }
166
setLightState(int id,const HwLightState & state)167 ScopedAStatus setLightState(int id, const HwLightState &state) override {
168 if (!(0 <= id && id < availableLights.size())) {
169 LOG(ERROR) << "Light id " << (int32_t)id << " does not exist.";
170 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
171 }
172
173 HwLight const &light = availableLights[id];
174 switch (light.type) {
175 case LightType::CAMERA:
176 for (size_t i = 0; i < sizeof(pwmIds) / sizeof(pwmIds[0]); i++) {
177 if (state.color == pwmIds[i].argb) {
178 pthread_mutex_lock(&g_lock);
179 writeLed(GREEN_LED_FILE, pwmIds[i].pwm);
180 pthread_mutex_unlock(&g_lock);
181 break;
182 }
183 }
184 break;
185 default:
186 break;
187 }
188
189 return ScopedAStatus::ok();
190 }
191
getLights(std::vector<HwLight> * lights)192 ScopedAStatus getLights(std::vector<HwLight> *lights) override {
193 for (auto i = availableLights.begin(); i != availableLights.end(); i++) {
194 lights->push_back(*i);
195 }
196
197 return ScopedAStatus::ok();
198 }
199 };
200
main()201 int main() {
202 ABinderProcess_setThreadPoolMaxThreadCount(0);
203
204 SpAIBinder display_service = WaitForDisplayService();
205 if (display_service == nullptr) {
206 return -1;
207 }
208
209 std::shared_ptr<Lights> light = SharedRefBase::make<Lights>();
210
211 const std::string instance = std::string() + ILights::descriptor + "/default";
212 binder_status_t status = AServiceManager_addService(light->asBinder().get(), instance.c_str());
213
214 if (status != STATUS_OK) {
215 LOG(ERROR) << "Failed to register" << instance;
216 }
217
218 ABinderProcess_joinThreadPool();
219
220 return -1; // should not reach
221 }
222