Initialize Tizen 2.3
[framework/system/deviced.git] / src / led / rgb.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 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
20 #include <stdio.h>
21 #include <errno.h>
22 #include <assert.h>
23 #include <device-node.h>
24 #include <Ecore.h>
25 #include <vconf.h>
26
27 #include "deviced/dd-led.h"
28 #include "xml.h"
29 #include "core/log.h"
30 #include "core/common.h"
31 #include "core/edbus-handler.h"
32 #include "core/devices.h"
33 #include "core/device-notifier.h"
34 #include "core/device-handler.h"
35 #include "display/core.h"
36
37 #define BOOT_ANIMATION_FINISHED         1
38
39 #define LED_VALUE(high, low)            (((high)<<16)|((low)&0xFFFF))
40
41 #define BG_COLOR                0x00000000
42 #define GET_ALPHA(x)    (((x)>>24) & 0xFF)
43 #define GET_RED(x)              (((x)>>16) & 0xFF)
44 #define GET_GREEN(x)    (((x)>> 8) & 0xFF)
45 #define GET_BLUE(x)             ((x) & 0xFF)
46 #define COMBINE_RGB(r,g,b)      ((((r) & 0xFF) << 16) | (((g) & 0xFF) << 8) | ((b) & 0xFF))
47
48 #define SET_WAVE_BIT    (0x6 << 24)
49 #define DUMPMODE_WAITING_TIME    600000
50
51 enum {
52         LED_CUSTOM_DUTY_ON = 1 << 0,    /* this enum doesn't blink on led */
53         LED_CUSTOM_DEFAULT = (LED_CUSTOM_DUTY_ON),
54 };
55
56 static bool charging_key;
57 static bool lowbat_key;
58 static bool charging_state;
59 static bool lowbat_state;
60 static bool full_state;
61 static bool badbat_state;
62 static bool ovp_state;
63 static bool rgb_blocked = false;
64 static bool rgb_dumpmode = false;
65 static Ecore_Timer *dumpmode_timer = NULL;
66 static enum state_t lcd_state = S_NORMAL;
67
68 static int led_prop(int mode, int on, int off, unsigned int color)
69 {
70         struct led_mode *led;
71
72         if (mode < 0 || mode > LED_MODE_MAX)
73                 return -EINVAL;
74
75         led = find_led_data(mode);
76         if (!led) {
77                 _E("no find data(%d)", mode);
78                 return -EINVAL;
79         }
80
81         switch (mode) {
82         case LED_MISSED_NOTI:
83         case LED_VOICE_RECORDING:
84         case LED_CUSTOM:
85                 if (on >= 0)
86                         led->data.on = on;
87                 if (off >= 0)
88                         led->data.off = off;
89                 if (color)
90                         led->data.color = color;
91                 break;
92         default:
93                 /* the others couldn't change any property */
94                 break;
95         }
96
97         _D("changed mode(%d) : on(%d), off(%d), color(%x)",
98                         mode, led->data.on, led->data.off, led->data.color);
99         return 0;
100 }
101
102 static int led_mode(int mode, bool enable)
103 {
104         struct led_mode *led;
105
106         if (mode < 0 || mode > LED_MODE_MAX)
107                 return -EINVAL;
108
109         led = find_led_data(mode);
110         if (!led) {
111                 _E("no find data(%d)", mode);
112                 return -EINVAL;
113         }
114
115         led->state = enable;
116         return 0;
117 }
118
119 static int get_led_mode_state(int mode, bool *state)
120 {
121         struct led_mode *led;
122
123         if (mode < 0 || mode > LED_MODE_MAX)
124                 return -EINVAL;
125
126         led = find_led_data(mode);
127         if (!led) {
128                 _E("no find data(%d)", mode);
129                 return -EINVAL;
130         }
131
132         if (led->state)
133                 *state = true;
134         else
135                 *state = false;
136         return 0;
137 }
138
139 static unsigned int led_blend(unsigned int before)
140 {
141         unsigned int alpha, alpha_inv;
142         unsigned char red, grn, blu;
143
144         alpha = GET_ALPHA(before) + 1;
145         alpha_inv = 256 - alpha;
146
147         red = ((alpha * GET_RED(before) + alpha_inv * GET_RED(BG_COLOR)) >> 8);
148         grn = ((alpha * GET_GREEN(before) + alpha_inv * GET_GREEN(BG_COLOR)) >> 8);
149         blu = ((alpha * GET_BLUE(before) + alpha_inv * GET_BLUE(BG_COLOR)) >> 8);
150         return COMBINE_RGB(red, grn, blu);
151 }
152
153 static int led_mode_to_device(struct led_mode *led)
154 {
155         int val, color;
156
157         if (led == NULL)
158                 return 0;
159
160         if (rgb_dumpmode)
161                 return 0;
162
163         if (rgb_blocked && led->mode != LED_POWER_OFF && led->mode != LED_OFF)
164                 return 0;
165
166         val = LED_VALUE(led->data.on, led->data.off);
167         color = led_blend(led->data.color);
168
169         /* if wave status is ON, color should be combined with WAVE_BIT */
170         if (led->data.wave)
171                 color |= SET_WAVE_BIT;
172
173         device_set_property(DEVICE_TYPE_LED, PROP_LED_COLOR, color);
174         device_set_property(DEVICE_TYPE_LED, PROP_LED_BLINK, val);
175         return 0;
176 }
177
178 static int led_display_changed_cb(void *data)
179 {
180         struct led_mode *led = NULL;
181         int val;
182
183         /* store last display condition */
184         lcd_state = (enum state_t)data;
185
186         /* charging error state */
187         if (badbat_state)
188                 return 0;
189
190         if (lcd_state == S_LCDOFF)
191                 led = get_valid_led_data();
192         else if (lcd_state == S_NORMAL || lcd_state == S_LCDDIM)
193                 led = find_led_data(LED_OFF);
194
195         return led_mode_to_device(led);
196 }
197
198 static int led_charging_changed_cb(void *data)
199 {
200         int v = (int)data;
201
202         if (v == CHARGER_CHARGING)
203                 charging_state = true;
204         else
205                 charging_state = false;
206
207         if (charging_key)
208                 led_mode(LED_CHARGING, charging_state);
209
210         return 0;
211 }
212
213 static int led_lowbat_changed_cb(void *data)
214 {
215         lowbat_state = (bool)data;
216
217         if (lowbat_key)
218                 led_mode(LED_LOW_BATTERY, lowbat_state);
219
220         return 0;
221 }
222
223 static int led_fullbat_changed_cb(void *data)
224 {
225         full_state = (bool)data;
226
227         if (charging_key)
228                 led_mode(LED_FULLY_CHARGED, full_state);
229
230         return 0;
231 }
232
233 static int led_battery_health_changed_cb(void *data)
234 {
235         struct led_mode *led;
236         bool cur;
237
238         cur = ((int)data == HEALTH_BAD) ? true : false;
239
240         /* do not enter below scenario in case of same state */
241         if (cur == badbat_state)
242                 return 0;
243
244         badbat_state = cur;
245
246         /* set charging error mode */
247         led_mode(LED_CHARGING_ERROR, badbat_state);
248
249         /* update the led state */
250         if (badbat_state) {     /* charging error */
251                 led = find_led_data(LED_CHARGING_ERROR);
252                 led_mode_to_device(led);
253         } else {
254                 led = find_led_data(LED_OFF);
255                 led_mode_to_device(led);
256                 if (lcd_state == S_LCDOFF)
257                         led_display_changed_cb((void*)S_LCDOFF);
258         }
259
260         return 0;
261 }
262
263 static int led_battery_ovp_changed_cb(void *data)
264 {
265         int cur = (int)data;
266
267         /* do not enter below flow in case of same state */
268         if (cur == ovp_state)
269                 return 0;
270
271         ovp_state = cur;
272
273         if (ovp_state == OVP_NORMAL && lcd_state == S_LCDOFF)
274                 led_display_changed_cb((void*)S_LCDOFF);
275
276         return 0;
277 }
278
279 static void led_poweron_changed_cb(keynode_t *key, void *data)
280 {
281         int state;
282         struct led_mode *led;
283
284         state = vconf_keynode_get_int(key);
285
286         if (state != BOOT_ANIMATION_FINISHED)
287                 return;
288
289         led = find_led_data(LED_OFF);
290         led_mode_to_device(led);
291
292         vconf_ignore_key_changed(VCONFKEY_BOOT_ANIMATION_FINISHED,
293                         led_poweron_changed_cb);
294 }
295
296 static void led_poweroff_changed_cb(keynode_t *key, void *data)
297 {
298         int state;
299         struct led_mode *led;
300
301         state = vconf_keynode_get_int(key);
302
303         if (state == VCONFKEY_SYSMAN_POWER_OFF_NONE ||
304                 state == VCONFKEY_SYSMAN_POWER_OFF_POPUP)
305                 return;
306
307         led = find_led_data(LED_POWER_OFF);
308         led_mode_to_device(led);
309
310         vconf_ignore_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS,
311                         led_poweroff_changed_cb);
312 }
313
314 static void led_vconf_charging_cb(keynode_t *key, void *data)
315 {
316         charging_key = vconf_keynode_get_bool(key);
317
318         if (charging_key) {
319                 led_mode(LED_CHARGING, charging_state);
320                 led_mode(LED_FULLY_CHARGED, full_state);
321         } else {
322                 led_mode(LED_CHARGING, false);
323                 led_mode(LED_FULLY_CHARGED, false);
324         }
325 }
326
327 static void led_vconf_lowbat_cb(keynode_t *key, void *data)
328 {
329         lowbat_key = vconf_keynode_get_bool(key);
330
331         if (lowbat_key)
332                 led_mode(LED_LOW_BATTERY, lowbat_state);
333         else
334                 led_mode(LED_LOW_BATTERY, false);
335 }
336
337 static void led_vconf_blocking_cb(keynode_t *key, void *data)
338 {
339         rgb_blocked = vconf_keynode_get_bool(key);
340         _I("rgbled blocking mode %s", (rgb_blocked ? "started" : "stopped"));
341 }
342
343 static DBusMessage *edbus_playcustom(E_DBus_Object *obj, DBusMessage *msg)
344 {
345         DBusMessageIter iter;
346         DBusMessage *reply;
347         int cmd, val, ret;
348         int on, off;
349         unsigned int color, flags;
350
351         ret = dbus_message_get_args(msg, NULL,
352                         DBUS_TYPE_INT32, &on,
353                         DBUS_TYPE_INT32, &off,
354                         DBUS_TYPE_UINT32, &color,
355                         DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID);
356         if (!ret) {
357                 _I("there is no message");
358                 ret = -EINVAL;
359                 goto error;
360         }
361
362         /* not to play blink, on and off value should be zero */
363         if (!(flags & LED_CUSTOM_DUTY_ON))
364                 on = off = 0;
365
366         /* set new value in case of LED_CUSTOM value */
367         ret = led_mode(LED_CUSTOM, true);
368         ret = led_prop(LED_CUSTOM, on, off, color);
369
370         _D("play custom %d, %d, %x, %d", on, off, color, ret);
371
372 error:
373         reply = dbus_message_new_method_return(msg);
374         dbus_message_iter_init_append(reply, &iter);
375         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
376         return reply;
377 }
378
379 static DBusMessage *edbus_stopcustom(E_DBus_Object *obj, DBusMessage *msg)
380 {
381         DBusMessageIter iter;
382         DBusMessage *reply;
383         int val, ret;
384
385         /* reset default value */
386         ret = led_mode(LED_CUSTOM, false);
387         ret = led_prop(LED_CUSTOM, 500, 5000, 255);
388
389         _D("stop custom %d", ret);
390
391         reply = dbus_message_new_method_return(msg);
392         dbus_message_iter_init_append(reply, &iter);
393         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
394         return reply;
395 }
396
397 static DBusMessage *edbus_set_mode(E_DBus_Object *obj, DBusMessage *msg)
398 {
399         DBusMessageIter iter;
400         DBusMessage *reply;
401         unsigned int color;
402         int mode, val, on, off, ret;
403         struct led_mode *led;
404         bool mode_enabled = false;
405
406         ret = dbus_message_get_args(msg, NULL,
407                         DBUS_TYPE_INT32, &mode,
408                         DBUS_TYPE_INT32, &val,
409                         DBUS_TYPE_INT32, &on,
410                         DBUS_TYPE_INT32, &off,
411                         DBUS_TYPE_UINT32, &color, DBUS_TYPE_INVALID);
412         if (!ret) {
413                 _I("there is no message");
414                 ret = -EINVAL;
415                 goto error;
416         }
417
418         ret = get_led_mode_state(mode, &mode_enabled);
419         if (ret) {
420                 _I("failed to get led mode state : %d", ret);
421                 goto error;
422         }
423
424         ret = led_mode(mode, val);
425         ret = led_prop(mode, on, off, color);
426
427         /* Exception case :
428            in case of display off condition,
429            who requests missed noti event, it should change the device value */
430         if (mode == LED_MISSED_NOTI && lcd_state == S_LCDOFF) {
431                 if (!mode_enabled || on == 0) {
432                         /* turn off previous setting */
433                         led = find_led_data(LED_OFF);
434                         led_mode_to_device(led);
435                         /* update the led state */
436                         led_display_changed_cb((void*)S_LCDOFF);
437                 }
438         }
439
440 error:
441         reply = dbus_message_new_method_return(msg);
442         dbus_message_iter_init_append(reply, &iter);
443         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
444         return reply;
445 }
446
447 static void set_dumpmode(bool on)
448 {
449         struct led_mode *led;
450         int val, color;
451
452         if (on) {
453                 val = LED_VALUE(200, 100);
454                 color = led_blend(0xFFFF0000);
455                 device_set_property(DEVICE_TYPE_LED, PROP_LED_COLOR, color);
456                 device_set_property(DEVICE_TYPE_LED, PROP_LED_BLINK, val);
457                 rgb_dumpmode = true;
458                 _I("dump_mode on");
459         } else {
460                 if (dumpmode_timer) {
461                         ecore_timer_del(dumpmode_timer);
462                         dumpmode_timer = NULL;
463                 }
464                 if (rgb_dumpmode)
465                         rgb_dumpmode = false;
466                 else
467                         return;
468                 led = find_led_data(LED_OFF);
469                 led_mode_to_device(led);
470                 _I("dump_mode off");
471         }
472 }
473
474 static void dumpmode_timer_cb(void *data)
475 {
476         set_dumpmode(false);
477 }
478
479 static DBusMessage *edbus_dump_mode(E_DBus_Object *obj, DBusMessage *msg)
480 {
481         DBusMessageIter iter;
482         DBusMessage *reply;
483         int ret = 0;
484         char *on;
485
486         ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &on,
487                         DBUS_TYPE_INVALID);
488
489         if (!ret) {
490                 _E("fail to get dumpmode state %d", ret);
491                 ret = -EINVAL;
492                 goto error;
493         }
494
495         if (!strcmp(on, "on")) {
496                 set_dumpmode(true);
497                 dumpmode_timer = ecore_timer_add(DUMPMODE_WAITING_TIME,
498                                 (Ecore_Task_Cb)dumpmode_timer_cb, NULL);
499         } else if (!strcmp(on, "off")) {
500                 set_dumpmode(false);
501         } else
502                 ret = -EINVAL;
503
504 error:
505         reply = dbus_message_new_method_return(msg);
506         dbus_message_iter_init_append(reply, &iter);
507         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
508
509         return reply;
510 }
511
512 static DBusMessage *edbus_print_mode(E_DBus_Object *obj, DBusMessage *msg)
513 {
514         print_all_data();
515         return dbus_message_new_method_return(msg);
516 }
517
518 static const struct edbus_method edbus_methods[] = {
519         { "playcustom",     "iiuu",  "i",  edbus_playcustom },
520         { "stopcustom",     NULL,    "i",  edbus_stopcustom },
521         { "SetMode",        "iiiiu", "i",  edbus_set_mode },
522         { "PrintMode",      NULL,    NULL, edbus_print_mode },
523         { "Dumpmode",       "s",     "i",  edbus_dump_mode },
524         /* Add methods here */
525 };
526
527 static void rgb_init(void *data)
528 {
529         int ta_connected, boot;
530         int ret;
531         struct led_mode *led;
532
533         /* init dbus interface */
534         ret = register_edbus_method(DEVICED_PATH_LED, edbus_methods, ARRAY_SIZE(edbus_methods));
535         if (ret < 0)
536                 _E("fail to init edbus method(%d)", ret);
537
538         /* get led mode data from xml */
539         get_led_data();
540
541         /* verify booting or restart */
542         ret = vconf_get_int(VCONFKEY_BOOT_ANIMATION_FINISHED, &boot);
543         if (ret != 0)
544                 boot = 0;       /* set booting state */
545
546         /* in case of restart */
547         if (boot)
548                 goto next;
549
550         /* when booting, led indicator will be work */
551         led = find_led_data(LED_POWER_OFF);
552         led_mode_to_device(led);
553         vconf_notify_key_changed(VCONFKEY_BOOT_ANIMATION_FINISHED,
554                         led_poweron_changed_cb, NULL);
555
556 next:
557         /* register power off callback */
558         vconf_notify_key_changed(VCONFKEY_SYSMAN_POWER_OFF_STATUS,
559                         led_poweroff_changed_cb, NULL);
560
561         /* register notifier for each event */
562         register_notifier(DEVICE_NOTIFIER_LCD, led_display_changed_cb);
563         register_notifier(DEVICE_NOTIFIER_BATTERY_CHARGING, led_charging_changed_cb);
564         register_notifier(DEVICE_NOTIFIER_LOWBAT, led_lowbat_changed_cb);
565         register_notifier(DEVICE_NOTIFIER_FULLBAT, led_fullbat_changed_cb);
566         register_notifier(DEVICE_NOTIFIER_BATTERY_HEALTH, led_battery_health_changed_cb);
567         register_notifier(DEVICE_NOTIFIER_BATTERY_OVP, led_battery_ovp_changed_cb);
568
569         /* initialize vconf value */
570         vconf_get_bool(VCONFKEY_SETAPPL_LED_INDICATOR_CHARGING, (int*)&charging_key);
571         vconf_get_bool(VCONFKEY_SETAPPL_LED_INDICATOR_LOW_BATT, (int*)&lowbat_key);
572
573         /* initialize led indicator blocking value */
574         vconf_get_bool(VCONFKEY_SETAPPL_BLOCKINGMODE_LED_INDICATOR, (int*)&rgb_blocked);
575
576         /* register vconf callback */
577         vconf_notify_key_changed(VCONFKEY_SETAPPL_LED_INDICATOR_CHARGING,
578                         led_vconf_charging_cb, NULL);
579         vconf_notify_key_changed(VCONFKEY_SETAPPL_LED_INDICATOR_LOW_BATT,
580                         led_vconf_lowbat_cb, NULL);
581
582         /* register led indicator blocking vconf callback */
583         vconf_notify_key_changed(VCONFKEY_SETAPPL_BLOCKINGMODE_LED_INDICATOR,
584                         led_vconf_blocking_cb, NULL);
585
586         ret = device_get_property(DEVICE_TYPE_POWER, PROP_POWER_CHARGE_NOW, &ta_connected);
587         if (!ret && ta_connected)
588                 led_charging_changed_cb((void*)ta_connected);
589 }
590
591 static void rgb_exit(void *data)
592 {
593         struct led_mode *led;
594
595         /* unregister vconf callback */
596         vconf_ignore_key_changed(VCONFKEY_SETAPPL_LED_INDICATOR_CHARGING,
597                         led_vconf_charging_cb);
598         vconf_ignore_key_changed(VCONFKEY_SETAPPL_LED_INDICATOR_LOW_BATT,
599                         led_vconf_lowbat_cb);
600
601         /* unregister led indicatore blocking vconf callback */
602         vconf_ignore_key_changed(VCONFKEY_SETAPPL_BLOCKINGMODE_LED_INDICATOR,
603                         led_vconf_blocking_cb);
604
605         /* unregister notifier for each event */
606         unregister_notifier(DEVICE_NOTIFIER_LCD, led_display_changed_cb);
607         unregister_notifier(DEVICE_NOTIFIER_BATTERY_CHARGING, led_charging_changed_cb);
608         unregister_notifier(DEVICE_NOTIFIER_LOWBAT, led_lowbat_changed_cb);
609         unregister_notifier(DEVICE_NOTIFIER_FULLBAT, led_fullbat_changed_cb);
610         unregister_notifier(DEVICE_NOTIFIER_BATTERY_HEALTH, led_battery_health_changed_cb);
611         unregister_notifier(DEVICE_NOTIFIER_BATTERY_OVP, led_battery_ovp_changed_cb);
612
613         set_dumpmode(false);
614         /* turn off led */
615         led = find_led_data(LED_OFF);
616         led_mode_to_device(led);
617
618         /* release led mode data */
619         release_led_data();
620 }
621
622 static int rgb_start(void)
623 {
624         _I("rgbled device will be started");
625         rgb_blocked = false;
626         return 0;
627 }
628
629 static int rgb_stop(void)
630 {
631         _I("rgbled device will be stopped");
632         rgb_blocked = true;
633         return 0;
634 }
635
636 static const struct device_ops rgbled_device_ops = {
637         .priority = DEVICE_PRIORITY_NORMAL,
638         .name     = "rgbled",
639         .init     = rgb_init,
640         .exit     = rgb_exit,
641         .start    = rgb_start,
642         .stop     = rgb_stop,
643 };
644
645 DEVICE_OPS_REGISTER(&rgbled_device_ops)