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