Apply next HAL architecture (hal api + backend)
[platform/adaptation/RPI3/device-manager-plugin-RPI3.git] / hw / led / led.c
1 /*
2  * device-manager-plugin-rpi3
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 <stdint.h>
20 #include <stdlib.h>
21 #include <glib.h>
22
23 #include <hal/device/hal-led-interface.h>
24 #include <hal/hal-common-interface.h>
25 #include "common.h"
26
27 #include <peripheral_io.h>
28
29 #define GET_BRIGHTNESS(val)      (((val) >> 24) & 0xFF)
30
31 #define GET_TYPE(a)        (((a) >> 24) & 0xFF)
32 #define GET_RED_ONLY(a)   ((a) & 0xFF0000)
33 #define GET_GREEN_ONLY(a) ((a) & 0x00FF00)
34 #define GET_BLUE_ONLY(a)  ((a) & 0x0000FF)
35 #define GET_RED_BRT(a)    (uint8_t)(((a) >> 16) & 0xFF)
36 #define GET_GREEN_BRT(a)  (uint8_t)(((a) >> 8) & 0xFF)
37 #define GET_BLUE_BRT(a)   (uint8_t)((a) & 0xFF)
38
39 /**
40  * GPIO Specific
41  */
42 #define GPIO_I2C_BUS_INDEX 1
43 #define BLINKM_DEFAULT_ADDR 0x09
44
45 /**
46  * LED operation command code
47  * Stop script: 0x6f('o')
48  * Set command: 0x6e('n')
49  * Change address command: 0x41('A')
50  */
51 #define SET_CMD_CODE    0x6e
52 #define STOP_SCRIPT_CMD 0x6f
53 #define CHANGE_ADDR_CMD 0x41
54
55 typedef enum _led_rgb_type {
56         LED_RED,
57         LED_GREEN,
58         LED_BLUE,
59 } led_rgb_type_e;
60
61 struct gpio_rgb_play_color_info {
62         unsigned int color;
63         int time;
64 };
65
66 struct gpio_rgb_play_info {
67         GList *play_list;
68         int nr_play;
69         int index;
70         guint timer;
71 } play_info;
72
73 static uint8_t off_cmd[4] = { SET_CMD_CODE, 0x00, 0x00, 0x00 };
74
75 static void blinkm_led_stop_script(peripheral_i2c_h handle)
76 {
77         uint8_t data[1] = {STOP_SCRIPT_CMD};
78         uint32_t length = 1;
79
80         peripheral_i2c_write(handle, data, length);
81 }
82
83 /**
84  * LED set to black
85  */
86 static void blinkm_led_off(peripheral_i2c_h handle)
87 {
88         peripheral_i2c_write(handle, off_cmd, sizeof(off_cmd));
89 }
90
91 static void blinkm_led_init(peripheral_i2c_h handle)
92 {
93         blinkm_led_stop_script(handle);
94         blinkm_led_off(handle);
95 }
96
97 static void blinkm_led_set_color(peripheral_i2c_h handle, uint8_t *pkt_data, int len)
98 {
99         peripheral_i2c_write(handle, pkt_data, len);
100 }
101
102 static int gpio_led_open_device(int device_index, peripheral_i2c_h *device_handle)
103 {
104         int ret;
105
106         if ((ret = peripheral_i2c_open(device_index, BLINKM_DEFAULT_ADDR, device_handle)) < PERIPHERAL_ERROR_NONE) {
107                 _E("Failed to open I2C");
108         }
109         return ret;
110 }
111
112 static int gpio_led_close_device(peripheral_i2c_h device_handle)
113 {
114         int ret;
115
116         if ((ret = peripheral_i2c_close(device_handle)) < PERIPHERAL_ERROR_NONE) {
117                 _E("Failed to close I2C");
118         }
119         return ret;
120 }
121
122 static int gpio_rgb_set_brightness(struct led_state *state)
123 {
124         uint8_t cmd_pkt[4] = { SET_CMD_CODE, };
125         peripheral_i2c_h handle;
126         unsigned int color;
127         int ret;
128
129         if (!state)
130                 return -EINVAL;
131
132         color = state->color;
133
134         cmd_pkt[1] = GET_RED_BRT(color);
135         cmd_pkt[2] = GET_GREEN_BRT(color);
136         cmd_pkt[3] = GET_BLUE_BRT(color);
137
138         _I("COLOR(%x) r(%x), g(%x), b(%x)", color, cmd_pkt[1], cmd_pkt[2], cmd_pkt[3]);
139
140         if ((ret = gpio_led_open_device(GPIO_I2C_BUS_INDEX, &handle)) < PERIPHERAL_ERROR_NONE)
141                 return -EIO;
142
143         blinkm_led_set_color(handle, cmd_pkt, sizeof(cmd_pkt));
144
145         if ((ret = gpio_led_close_device(handle)) < PERIPHERAL_ERROR_NONE)
146                 return -EIO;
147
148         return 0;
149 }
150
151 /* turn off led */
152 static int gpio_rgb_turn_off(struct led_state *state)
153 {
154         struct led_state st = { LED_TYPE_MANUAL, };
155         return gpio_rgb_set_brightness(&st);
156 }
157
158 /* release play list */
159 static void free_func(gpointer data)
160 {
161         struct gpio_rgb_play_color_info *color = data;
162         free(color);
163 }
164
165 static void release_play_info(void)
166 {
167         if (play_info.play_list) {
168                 g_list_free_full(play_info.play_list, free_func);
169                 play_info.play_list = NULL;
170         }
171         play_info.nr_play = 0;
172         play_info.index = 0;
173         if (play_info.timer) {
174                 g_source_remove(play_info.timer);
175                 play_info.timer = 0;
176         }
177
178         gpio_rgb_turn_off(NULL);
179 }
180
181 static int gpio_rgb_init_led()
182 {
183         peripheral_i2c_h handle;
184         int ret;
185
186         if ((ret = gpio_led_open_device(GPIO_I2C_BUS_INDEX, &handle)) < PERIPHERAL_ERROR_NONE)
187                 return ret;
188
189         blinkm_led_init(handle);
190
191         if ((ret = gpio_led_close_device(handle)) < PERIPHERAL_ERROR_NONE)
192                 return ret;
193
194         return ret;
195 }
196
197 /* timer callback to change colors which are stored in play_list */
198 static gboolean gpio_rgb_timer_expired(gpointer data)
199 {
200         struct gpio_rgb_play_color_info *color;
201         struct led_state state = {LED_TYPE_MANUAL, };
202         int ret;
203
204         if (play_info.timer) {
205                 g_source_remove(play_info.timer);
206                 play_info.timer = 0;
207         }
208
209         color = g_list_nth_data(play_info.play_list, play_info.index);
210         if (!color) {
211                 _E("Failed to get (%d)th item from the play list", play_info.index);
212                 goto out;
213         }
214
215         play_info.timer = g_timeout_add(color->time, gpio_rgb_timer_expired, data);
216         if (play_info.timer == 0) {
217                 _E("Failed to add timeout for LED blinking");
218                 goto out;
219         }
220
221         state.color = color->color;
222
223         ret = gpio_rgb_set_brightness(&state);
224         if (ret < 0) {
225                 _E("Failed to set brightness (%d)", ret);
226                 goto out;
227         }
228
229         play_info.index++;
230         if (play_info.index == play_info.nr_play)
231                 play_info.index = 0;
232
233         return G_SOURCE_CONTINUE;
234
235 out:
236         release_play_info();
237
238         return G_SOURCE_REMOVE;
239 }
240
241 /* insert color info to the play_list */
242 static int gpio_rgb_insert_play_list(unsigned color, int on, int off)
243 {
244         struct gpio_rgb_play_color_info *on_info, *off_info;
245
246         if (color == 0)
247                 return -EINVAL;
248         on_info = calloc(1, sizeof(struct gpio_rgb_play_color_info));
249         if (!on_info)
250                 return -ENOMEM;
251         off_info = calloc(1, sizeof(struct gpio_rgb_play_color_info));
252         if (!off_info) {
253                 free(on_info);
254                 return -ENOMEM;
255         }
256
257         on_info->color = color;
258         on_info->time = on;
259         play_info.play_list = g_list_append(play_info.play_list, on_info);
260
261         off_info->color = 0;
262         off_info->time = off;
263         play_info.play_list = g_list_append(play_info.play_list, off_info);
264
265         return 0;
266 }
267
268 /* insert color info to the play list and start to play */
269 static int gpio_rgb_set_brightness_blink(struct led_state *state)
270 {
271         unsigned int val;
272         int ret;
273
274         val = GET_RED_ONLY(state->color);
275         if (val) {
276                 ret = gpio_rgb_insert_play_list(val, state->duty_on, state->duty_off);
277                 if (ret < 0)
278                         _E("Failed to insert color info to list (%d)", ret);
279         }
280         val = GET_GREEN_ONLY(state->color);
281         if (val) {
282                 ret = gpio_rgb_insert_play_list(val, state->duty_on, state->duty_off);
283                 if (ret < 0)
284                         _E("Failed to insert color info to list (%d)", ret);
285         }
286         val = GET_BLUE_ONLY(state->color);
287         if (val) {
288                 ret = gpio_rgb_insert_play_list(val, state->duty_on, state->duty_off);
289                 if (ret < 0)
290                         _E("Failed to insert color info to list (%d)", ret);
291         }
292
293         play_info.nr_play = g_list_length(play_info.play_list);
294         play_info.index = 0;
295
296         gpio_rgb_timer_expired(NULL);
297         return 0;
298 }
299
300 static int gpio_rgb_turn_on(struct led_state *state)
301 {
302         if (state->type == LED_TYPE_MANUAL)
303                 return gpio_rgb_set_brightness(state);
304
305         return gpio_rgb_set_brightness_blink(state);
306 }
307
308 static int gpio_rgb_set_state(struct led_state *state)
309 {
310         if (!state)
311                 return -EINVAL;
312
313         switch (state->type) {
314         case LED_TYPE_BLINK:
315         case LED_TYPE_MANUAL:
316                 break;
317         default:
318                 _E("Not suppoted type (%d)", state->type);
319                 return -ENOTSUP;
320         }
321         release_play_info();
322
323         if (GET_TYPE(state->color) == 0)
324                 return gpio_rgb_turn_off(state);
325
326         return gpio_rgb_turn_on(state);
327 }
328
329 static int led_init(void **data)
330 {
331         hal_backend_led_funcs *led_funcs;
332
333         led_funcs = calloc(1, sizeof(hal_backend_led_funcs));
334         if (!led_funcs)
335                 return -ENOMEM;
336
337         if (gpio_rgb_init_led()) {
338                 free(led_funcs);
339                 return -EIO;
340         } else
341                 led_funcs->set_state = gpio_rgb_set_state;
342
343         *data = (void *)led_funcs;
344
345         return 0;
346 }
347
348 static int led_exit(void *data)
349 {
350         if (!data)
351                 return 0;
352
353         free(data);
354         return 0;
355 }
356
357 hal_backend EXPORT hal_backend_device_led_data = {
358         .name = "led",
359         .vendor = "rpi3",
360         .abi_version = HAL_ABI_VERSION_TIZEN_6_5,
361         .init = led_init,
362         .exit = led_exit,
363 };