shared: Replace device_notifier_type with the libsyscommon
[platform/core/system/deviced.git] / plugins / wearable / display / hbm.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdbool.h>
20 #include <fcntl.h>
21 #include <libsyscommon/ini-parser.h>
22
23 #include "auto-brightness-sensorhub.h"
24 #include "weaks.h"
25 #include "display-info.h"
26 #include "core.h"
27 #include "display-ops.h"
28 #include "display-panel.h"
29 #include "shared/common.h"
30 #include "shared/device-notifier.h"
31 #include "shared/log.h"
32
33 #define BOARD_CONF_FILE "/etc/deviced/display.conf"
34
35 #define ON              "on"
36 #define OFF             "off"
37
38 #define SIGNAL_HBM_ON   "HBMOn"
39 #define SIGNAL_HBM_OFF  "HBMOff"
40
41 #define HBM_LEVEL       120
42
43 #define LCD_PATH    "sys/class/lcd/"
44 #define HBM_PATH    "/hbm"
45 #define DEVICE_PATH "/device"
46 #define PATH_BUFFER_MAX    512
47
48 static guint timer;
49 static struct timespec offtime;
50 static char *hbm_path;
51
52 static void broadcast_hbm_state(char *state)
53 {
54         gdbus_signal_emit(NULL,
55                         DEVICED_PATH_DISPLAY,
56                         DEVICED_INTERFACE_DISPLAY,
57                         state,
58                         NULL);
59 }
60
61 static void hbm_set_offtime(int timeout)
62 {
63         struct timespec now;
64
65         if (timeout <= 0) {
66                 offtime.tv_sec = 0;
67                 return;
68         }
69
70         clock_gettime(CLOCK_REALTIME, &now);
71         offtime.tv_sec = now.tv_sec + timeout;
72 }
73
74 static gboolean hbm_off_cb(void *data)
75 {
76         timer = 0;
77
78         if (get_pm_cur_state() != DEVICED_DISPLAY_STATE_ON) {
79                 _D("Hbm timeout, but it's not display normal.");
80                 return G_SOURCE_REMOVE;
81         }
82
83         hbm_set_offtime(0);
84         auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
85
86         return G_SOURCE_REMOVE;
87 }
88
89 static void hbm_start_timer(int timeout)
90 {
91         if (timer) {
92                 g_source_remove(timer);
93                 timer = 0;
94         }
95         if (!timer) {
96                 timer = g_timeout_add_seconds(timeout, hbm_off_cb, NULL);
97         }
98 }
99
100 static void hbm_end_timer(void)
101 {
102         if (timer) {
103                 g_source_remove(timer);
104                 timer = 0;
105         }
106 }
107
108 int hbm_get_state(void)
109 {
110         char state[5];
111         int ret, hbm;
112
113         if (!hbm_path)
114                 return -ENODEV;
115
116         ret = sys_get_str(hbm_path, state, sizeof(state));
117         if (ret < 0)
118                 return ret;
119
120         if (!strncmp(state, ON, strlen(ON)))
121                 hbm = true;
122         else if (!strncmp(state, OFF, strlen(OFF)))
123                 hbm = false;
124         else
125                 hbm = -EINVAL;
126
127         return hbm;
128 }
129
130 int hbm_set_state(int hbm)
131 {
132         int ret;
133         if (!hbm_path)
134                 return -ENODEV;
135
136         if (hbm)
137                 broadcast_hbm_state(SIGNAL_HBM_ON);
138         else
139                 broadcast_hbm_state(SIGNAL_HBM_OFF);
140
141         ret = sys_set_str(hbm_path, (hbm ? ON : OFF));
142         if (ret < 0)
143                 _E("Failed to HBM %s.", hbm ? "ON" : " OFF");
144
145         return ret;
146 }
147
148 static int hbm_set_state_with_timeout(int hbm, int timeout)
149 {
150         int ret;
151
152         if (hbm && (timeout <= 0))
153                 return -EINVAL;
154
155         if (hbm)
156                 ret = auto_brightness_control(BR_HBM_ON, BR_IMPLICIT);
157         else
158                 ret = auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
159
160         if (ret < 0)
161                 return ret;
162
163         _D("timeout is %d", timeout);
164
165         if (hbm) {
166                 /*
167                  * hbm is turned off after timeout.
168                  */
169                 hbm_set_offtime(timeout);
170                 hbm_start_timer(timeout);
171         } else {
172                 hbm_set_offtime(0);
173                 hbm_end_timer();
174         }
175
176         return 0;
177 }
178
179 static void hbm_check_timeout(void)
180 {
181         struct timespec now;
182
183         if (timer) {
184                 g_source_remove(timer);
185                 timer = 0;
186         }
187
188         if (offtime.tv_sec == 0) {
189                 _E("It's invalid state. HBM is turned off.");
190                 auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
191                 return;
192         }
193
194         clock_gettime(CLOCK_REALTIME, &now);
195         _D("now %ld, offtime %ld", now.tv_sec, offtime.tv_sec);
196
197         /* check it's timeout */
198         if (now.tv_sec >= offtime.tv_sec) {
199                 hbm_set_offtime(0);
200                 auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
201         } else {
202                 _D("HBM state is restored.");
203                 auto_brightness_control(BR_HBM_ON, BR_IMPLICIT);
204                 hbm_start_timer(offtime.tv_sec - now.tv_sec);
205         }
206 }
207
208 static int display_state_changed(void *data)
209 {
210         int state;
211         int ret;
212
213         state = DATA_VALUE_INT(data);
214
215         if (get_outdoor_setting)
216                 if (get_outdoor_setting())
217                         return 0;
218
219         switch (state) {
220         case DEVICED_DISPLAY_STATE_ON:
221                 /*
222                  * outdoor-enhance-mode not supported
223                  *  : check & restore hbm always.
224                  * outdoor-enhance-mode supported
225                  *  : check & restore hbm when old state is dim only.
226                  */
227                 if (!get_outdoor_setting || (get_pm_old_state() == DEVICED_DISPLAY_STATE_DIM))
228                         hbm_check_timeout();
229                 break;
230         case DEVICED_DISPLAY_STATE_DIM:
231         case DEVICED_DISPLAY_STATE_OFF:
232         case DEVICED_DISPLAY_STATE_SLEEP:
233                 ret = auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
234                 if (ret < 0)
235                         _E("Failed to off hbm.");
236                 hbm_end_timer();
237                 break;
238         }
239
240         return 0;
241 }
242
243 static GVariant *dbus_gethbm(GDBusConnection *conn,
244         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
245         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
246 {
247         return g_variant_new("(i)", hbm_get_state());
248 }
249
250 static GVariant *dbus_sethbm_timeout(GDBusConnection *conn,
251         const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
252         GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
253 {
254         int hbm, timeout, ret;
255
256         g_variant_get(param, "(ii)", &hbm, &timeout);
257
258         if (timeout < 0) {
259                 _E("Timeout can not be setting(%d).", timeout);
260                 ret = -EINVAL;
261                 goto out;
262         }
263
264         ret = hbm_set_state_with_timeout(hbm, timeout);
265
266         if (ret < 0)
267                 _E("Failed to set HBM (ret=%d, hbm=%d, time=%d).", ret, hbm, timeout);
268         else
269                 _I("Set hbm (ret=%d, hbm=%d, time=%d).", ret, hbm, timeout);
270
271 out:
272         return g_variant_new("(i)", ret);
273 }
274
275 static const dbus_method_s dbus_methods[] = {
276         { "GetHBM",          NULL,   "i", dbus_gethbm },
277         { "SetHBMTimeout",   "ii",   "i", dbus_sethbm_timeout },
278 };
279
280 static const dbus_interface_u dbus_interface = {
281         .oh = NULL,
282         .name = DEVICED_INTERFACE_DISPLAY,
283         .methods = dbus_methods,
284         .nr_methods = ARRAY_SIZE(dbus_methods),
285 };
286
287 static int hbm_func(unsigned int cmd, void *arg)
288 {
289         int ret = 0;
290         struct hbmsetstate *hss;
291
292         switch (cmd) {
293         case HBM_GET_STATE:
294                 ret = hbm_get_state();
295                 break;
296         case HBM_SET_STATE:
297                 if ((int *)arg)
298                         ret = auto_brightness_control(BR_HBM_ON, BR_IMPLICIT);
299                 else
300                         ret = auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
301                 break;
302         case HBM_TURN_ON:
303                 ret = auto_brightness_control(BR_HBM_ON, BR_IMPLICIT);
304                 break;
305         case HBM_TURN_OFF:
306         case HBM_TURN_OFF_STATE:
307                 ret = auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
308                 break;
309         case HBM_SET_TIMEOUT_STATE:
310                 hss = (struct hbmsetstate *)arg;
311                 ret = hbm_set_state_with_timeout(hss->hbm, hss->timeout);
312                 break;
313         default:
314                 ret = -EINVAL;
315                 break;
316         }
317
318         return ret;
319 }
320
321 static char *check_and_copy_path(char *path)
322 {
323         int fd;
324         char *buf = NULL;
325
326         fd = open(path, O_RDONLY);
327         if (fd >= 0) {
328                 buf = strndup(path, strlen(path));
329                 close(fd);
330         } else {
331                 _E("Failed to open HBM node.");
332         }
333
334         return buf;
335 }
336
337 static char *find_hbm_node()
338 {
339         DIR *d;
340         struct dirent *dir;
341         char buf[PATH_BUFFER_MAX];
342         char *result = NULL;
343
344         d = opendir(LCD_PATH);
345         if (!d)
346                 return NULL;
347
348         while ((dir = readdir(d))) {
349                 if (dir->d_name[0] == '.')
350                         continue;
351
352                 snprintf(buf, sizeof(buf), "%s%s%s", LCD_PATH,
353                         dir->d_name, HBM_PATH);
354
355                 result = check_and_copy_path(buf);
356                 if (result)
357                         break;
358
359                 snprintf(buf, sizeof(buf), "%s%s%s%s", LCD_PATH,
360                         dir->d_name, DEVICE_PATH, HBM_PATH);
361
362                 result = check_and_copy_path(buf);
363                 if (result)
364                         break;
365         }
366         closedir(d);
367
368         return result;
369 }
370
371 static void dpms_check_hbm_off(void)
372 {
373         int real_hbm = hbm_get_state();
374
375         if (real_hbm == true) {
376                 _W("HBM still enabled right before DPMS OFF, turn off HBM.");
377                 auto_brightness_control(BR_HBM_OFF, BR_IMPLICIT);
378
379                 /* check once more whether the HBM is really OFFed by auto_brightness_control */
380                 real_hbm = hbm_get_state();
381
382                 /* hbm state, flag mismatch occured. Force HBMOFF */
383                 if (real_hbm == true) {
384                         _E("HBM state, flag mismatch occured. Force HBMOFF.");
385                         hbm_set_state(false);
386                 }
387         }
388 }
389
390 static void hbm_init(void *data)
391 {
392         int ret;
393
394         hbm_path = find_hbm_node();
395
396         if (!hbm_path) {
397                 _E("Failed to find HBM node.");
398                 return;
399         } else {
400                 _I("HBM node: %s.", hbm_path);
401         }
402
403         ret = gdbus_add_object(NULL, DEVICED_PATH_DISPLAY, &dbus_interface);
404         if (ret < 0)
405                 _E("Failed to register dbus object.");
406
407         /* register notifier */
408         syscommon_notifier_subscribe_notify(DEVICED_NOTIFIER_LCD, display_state_changed);
409
410         /* double check to guarantee HBMOFF before DPMS OFF */
411         display_panel_register_dpms_checklist(DPMS_OFF, dpms_check_hbm_off);
412 }
413
414 static void hbm_exit(void *data)
415 {
416         /* unregister notifier */
417         syscommon_notifier_unsubscribe_notify(DEVICED_NOTIFIER_LCD, display_state_changed);
418
419         /*
420          * set default brightness
421          * if display logic is stopped in hbm state.
422          */
423         if (hbm_get_state() == true) {
424                 hbm_set_offtime(0);
425                 _I("set brightness to default value!");
426         }
427 }
428
429 static const struct display_ops display_hbm_ops = {
430         .name     = "hbm",
431         .init     = hbm_init,
432         .exit     = hbm_exit,
433         .func     = hbm_func,
434 };
435
436 DISPLAY_OPS_REGISTER(&display_hbm_ops)
437