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