Initialize Tizen 2.3
[framework/system/deviced.git] / src / haptic / haptic.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 <stdbool.h>
22 #include <dlfcn.h>
23 #include <vconf.h>
24
25 #include "core/log.h"
26 #include "core/list.h"
27 #include "core/common.h"
28 #include "core/devices.h"
29 #include "core/edbus-handler.h"
30 #include "core/device-notifier.h"
31 #include "haptic.h"
32
33 #ifndef DATADIR
34 #define DATADIR         "/usr/share/deviced"
35 #endif
36
37 /* hardkey vibration variable */
38 #define HARDKEY_VIB_RESOURCE            DATADIR"/HW_touch_30ms_sharp.ivt"
39 #define HARDKEY_VIB_ITERATION           1
40 #define HARDKEY_VIB_FEEDBACK            3
41 #define HARDKEY_VIB_PRIORITY            2
42 #define HAPTIC_FEEDBACK_STEP            20
43
44 /* power on, power off vibration variable */
45 #define POWER_ON_VIB_DURATION                   300
46 #define POWER_OFF_VIB_DURATION                  300
47 #define POWER_VIB_FEEDBACK                      100
48
49 #define MAX_EFFECT_BUFFER                       (64*1024)
50
51 #define RETRY_CNT                                       3
52
53 #define CHECK_VALID_OPS(ops, r)         ((ops) ? true : !(r = -ENODEV))
54
55 /* for playing */
56 static int g_handle;
57
58 /* haptic operation variable */
59 static dd_list *h_head;
60 static const struct haptic_plugin_ops *h_ops;
61 static bool haptic_disabled;
62
63 static int haptic_start(void);
64 static int haptic_stop(void);
65 static int haptic_internal_init(void);
66
67 void add_haptic(const struct haptic_ops *ops)
68 {
69         DD_LIST_APPEND(h_head, (void*)ops);
70 }
71
72 void remove_haptic(const struct haptic_ops *ops)
73 {
74         DD_LIST_REMOVE(h_head, (void*)ops);
75 }
76
77 static int haptic_module_load(void)
78 {
79         struct haptic_ops *ops;
80         dd_list *elem;
81         int r;
82
83         /* find valid plugin */
84         DD_LIST_FOREACH(h_head, elem, ops) {
85                 if (ops->is_valid && ops->is_valid()) {
86                         if (ops->load)
87                                 h_ops = ops->load();
88                         break;
89                 }
90         }
91
92         if (!CHECK_VALID_OPS(h_ops, r)) {
93                 _E("Can't find the valid haptic device");
94                 return r;
95         }
96
97         /* solution bug
98            we do not use internal vibration except power off
99            but module does not stop vibrating, although called terminate function */
100         haptic_internal_init();
101
102         return 0;
103 }
104
105 static DBusMessage *edbus_get_count(E_DBus_Object *obj, DBusMessage *msg)
106 {
107         DBusMessageIter iter;
108         DBusMessage *reply;
109         int ret, val;
110
111         if (!CHECK_VALID_OPS(h_ops, ret))
112                 goto exit;
113
114         ret = h_ops->get_device_count(&val);
115         if (ret >= 0)
116                 ret = val;
117
118 exit:
119         reply = dbus_message_new_method_return(msg);
120         dbus_message_iter_init_append(reply, &iter);
121         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
122         return reply;
123 }
124
125 static DBusMessage *edbus_open_device(E_DBus_Object *obj, DBusMessage *msg)
126 {
127         DBusMessageIter iter;
128         DBusMessage *reply;
129         int index, handle, ret;
130
131         if (!CHECK_VALID_OPS(h_ops, ret))
132                 goto exit;
133
134         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) {
135                 ret = -EINVAL;
136                 goto exit;
137         }
138
139         ret = h_ops->open_device(index, &handle);
140         if (ret >= 0)
141                 ret = handle;
142
143 exit:
144         reply = dbus_message_new_method_return(msg);
145         dbus_message_iter_init_append(reply, &iter);
146         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
147         return reply;
148 }
149
150 static DBusMessage *edbus_close_device(E_DBus_Object *obj, DBusMessage *msg)
151 {
152         DBusMessageIter iter;
153         DBusMessage *reply;
154         unsigned int handle;
155         int ret;
156
157         if (!CHECK_VALID_OPS(h_ops, ret))
158                 goto exit;
159
160         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID)) {
161                 ret = -EINVAL;
162                 goto exit;
163         }
164
165         ret = h_ops->close_device(handle);
166
167 exit:
168         reply = dbus_message_new_method_return(msg);
169         dbus_message_iter_init_append(reply, &iter);
170         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
171         return reply;
172 }
173
174 static DBusMessage *edbus_vibrate_monotone(E_DBus_Object *obj, DBusMessage *msg)
175 {
176         DBusMessageIter iter;
177         DBusMessage *reply;
178         unsigned int handle;
179         int duration, level, priority, e_handle, ret;
180
181         if (!CHECK_VALID_OPS(h_ops, ret))
182                 goto exit;
183
184         if (haptic_disabled) {
185                 ret = -EACCES;
186                 goto exit;
187         }
188
189         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
190                                 DBUS_TYPE_INT32, &duration,
191                                 DBUS_TYPE_INT32, &level,
192                                 DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) {
193                 ret = -EINVAL;
194                 goto exit;
195         }
196
197         ret = h_ops->vibrate_monotone(handle, duration, level, priority, &e_handle);
198         if (ret >= 0)
199                 ret = e_handle;
200
201 exit:
202         reply = dbus_message_new_method_return(msg);
203         dbus_message_iter_init_append(reply, &iter);
204         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
205         return reply;
206
207 }
208
209 static DBusMessage *edbus_vibrate_buffer(E_DBus_Object *obj, DBusMessage *msg)
210 {
211         DBusMessageIter iter;
212         DBusMessage *reply;
213         unsigned int handle;
214         unsigned char *data;
215         int size, iteration, level, priority, e_handle, ret;
216
217         if (!CHECK_VALID_OPS(h_ops, ret))
218                 goto exit;
219
220         if (haptic_disabled) {
221                 ret = -EACCES;
222                 goto exit;
223         }
224
225         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
226                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size,
227                                 DBUS_TYPE_INT32, &iteration,
228                                 DBUS_TYPE_INT32, &level,
229                                 DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) {
230                 ret = -EINVAL;
231                 goto exit;
232         }
233
234         ret = h_ops->vibrate_buffer(handle, data, iteration, level, priority, &e_handle);
235         if (ret >= 0)
236                 ret = e_handle;
237
238 exit:
239         reply = dbus_message_new_method_return(msg);
240         dbus_message_iter_init_append(reply, &iter);
241         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
242         return reply;
243 }
244
245 static DBusMessage *edbus_stop_device(E_DBus_Object *obj, DBusMessage *msg)
246 {
247         DBusMessageIter iter;
248         DBusMessage *reply;
249         unsigned int handle;
250         int ret;
251
252         if (!CHECK_VALID_OPS(h_ops, ret))
253                 goto exit;
254
255         if (haptic_disabled) {
256                 ret = -EACCES;
257                 goto exit;
258         }
259
260         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID)) {
261                 ret = -EINVAL;
262                 goto exit;
263         }
264
265         ret = h_ops->stop_device(handle);
266
267 exit:
268         reply = dbus_message_new_method_return(msg);
269         dbus_message_iter_init_append(reply, &iter);
270         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
271         return reply;
272 }
273
274 static DBusMessage *edbus_get_state(E_DBus_Object *obj, DBusMessage *msg)
275 {
276         DBusMessageIter iter;
277         DBusMessage *reply;
278         int index, state, ret;
279
280         if (!CHECK_VALID_OPS(h_ops, ret))
281                 goto exit;
282
283         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) {
284                 ret = -EINVAL;
285                 goto exit;
286         }
287
288         ret = h_ops->get_device_state(index, &state);
289         if (ret >= 0)
290                 ret = state;
291
292 exit:
293         reply = dbus_message_new_method_return(msg);
294         dbus_message_iter_init_append(reply, &iter);
295         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
296         return reply;
297 }
298
299 static DBusMessage *edbus_create_effect(E_DBus_Object *obj, DBusMessage *msg)
300 {
301         static unsigned char data[MAX_EFFECT_BUFFER];
302         static unsigned char *p = data;
303         DBusMessageIter iter, arr;
304         DBusMessage *reply;
305         haptic_module_effect_element *elem_arr;
306         int i, size, cnt, ret, bufsize = sizeof(data);
307
308         if (!CHECK_VALID_OPS(h_ops, ret))
309                 goto exit;
310
311         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &bufsize,
312                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &elem_arr, &size,
313                                 DBUS_TYPE_INT32, &cnt, DBUS_TYPE_INVALID)) {
314                 ret = -EINVAL;
315                 goto exit;
316         }
317
318         if (bufsize > MAX_EFFECT_BUFFER) {
319                 ret = -ENOMEM;
320                 goto exit;
321         }
322
323         for (i = 0; i < cnt; ++i)
324                 _D("[%2d] %d %d", i, elem_arr[i].haptic_duration, elem_arr[i].haptic_level);
325
326         memset(data, 0, MAX_EFFECT_BUFFER);
327         ret = h_ops->create_effect(data, bufsize, elem_arr, cnt);
328
329 exit:
330         reply = dbus_message_new_method_return(msg);
331         dbus_message_iter_init_append(reply, &iter);
332         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr);
333         dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &p, bufsize);
334         dbus_message_iter_close_container(&iter, &arr);
335         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
336         return reply;
337 }
338
339 static DBusMessage *edbus_get_duration(E_DBus_Object *obj, DBusMessage *msg)
340 {
341         DBusMessageIter iter;
342         DBusMessage *reply;
343         unsigned int handle;
344         unsigned char *data;
345         int size, duration, ret;
346
347         if (!CHECK_VALID_OPS(h_ops, ret))
348                 goto exit;
349
350         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
351                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size,
352                                 DBUS_TYPE_INVALID)) {
353                 ret = -EINVAL;
354                 goto exit;
355         }
356
357         ret = h_ops->get_buffer_duration(handle, data, &duration);
358         if (ret >= 0)
359                 ret = duration;
360
361 exit:
362         reply = dbus_message_new_method_return(msg);
363         dbus_message_iter_init_append(reply, &iter);
364         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
365         return reply;
366 }
367
368 static DBusMessage *edbus_save_binary(E_DBus_Object *obj, DBusMessage *msg)
369 {
370         DBusMessageIter iter;
371         DBusMessage *reply;
372         unsigned char *data;
373         unsigned char *path;
374         int size, ret;
375
376         if (!CHECK_VALID_OPS(h_ops, ret))
377                 goto exit;
378
379         if (!dbus_message_get_args(msg, NULL,
380                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size,
381                                 DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) {
382                 ret = -EINVAL;
383                 goto exit;
384         }
385
386         _D("file path : %s", path);
387         ret = h_ops->convert_binary(data, size, path);
388
389 exit:
390         reply = dbus_message_new_method_return(msg);
391         dbus_message_iter_init_append(reply, &iter);
392         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
393         return reply;
394 }
395
396 static unsigned char* convert_file_to_buffer(const char *file_name, int *size)
397 {
398         FILE *pf;
399         long file_size;
400         unsigned char *pdata = NULL;
401
402         if (!file_name)
403                 return NULL;
404
405         /* Get File Stream Pointer */
406         pf = fopen(file_name, "rb");
407         if (!pf) {
408                 _E("fopen failed : %s", strerror(errno));
409                 return NULL;
410         }
411
412         if (fseek(pf, 0, SEEK_END))
413                 goto error;
414
415         file_size = ftell(pf);
416         if (fseek(pf, 0, SEEK_SET))
417                 goto error;
418
419         if (file_size < 0)
420                 goto error;
421
422         pdata = (unsigned char*)malloc(file_size);
423         if (!pdata)
424                 goto error;
425
426         if (fread(pdata, 1, file_size, pf) != file_size)
427                 goto err_free;
428
429         fclose(pf);
430         *size = file_size;
431         return pdata;
432
433 err_free:
434         free(pdata);
435
436 error:
437         fclose(pf);
438
439         _E("failed to convert file to buffer (%s)", strerror(errno));
440         return NULL;
441 }
442
443 static int haptic_internal_init(void)
444 {
445         int r;
446         if (!CHECK_VALID_OPS(h_ops, r))
447                 return r;
448         return h_ops->open_device(HAPTIC_MODULE_DEVICE_ALL, &g_handle);
449 }
450
451 static int haptic_internal_exit(void)
452 {
453         int r;
454         if (!CHECK_VALID_OPS(h_ops, r))
455                 return r;
456         return h_ops->close_device(g_handle);
457 }
458
459 static int haptic_booting_done_cb(void *data)
460 {
461         return haptic_module_load();
462 }
463
464 static int haptic_hardkey_changed_cb(void *data)
465 {
466         int size, level, status, e_handle, ret, cnt = RETRY_CNT;
467         unsigned char *buf;
468
469         while (!CHECK_VALID_OPS(h_ops, ret) && cnt--) {
470                 haptic_module_load();
471                 if (!cnt)
472                         return ret;
473         }
474
475         if (!g_handle)
476                 haptic_internal_init();
477
478         if (vconf_get_bool(VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL, &status) < 0) {
479                 _E("fail to get VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL");
480                 status = 1;
481         }
482
483         /* when turn off haptic feedback option */
484         if (!status)
485                 return 0;
486
487         buf = convert_file_to_buffer(HARDKEY_VIB_RESOURCE, &size);
488         if (!buf)
489                 return -1;
490
491         ret = vconf_get_int(VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT, &level);
492         if (ret < 0) {
493                 _E("fail to get VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT");
494                 level = HARDKEY_VIB_FEEDBACK;
495         }
496
497         ret = h_ops->vibrate_buffer(g_handle, buf, HARDKEY_VIB_ITERATION,
498                         level*HAPTIC_FEEDBACK_STEP, HARDKEY_VIB_PRIORITY, &e_handle);
499         if (ret < 0)
500                 _E("fail to vibrate buffer : %d", ret);
501
502         free(buf);
503         return ret;
504 }
505
506 static int haptic_poweroff_cb(void *data)
507 {
508         int e_handle, ret, cnt = RETRY_CNT;
509
510         while (!CHECK_VALID_OPS(h_ops, ret) && cnt--) {
511                 haptic_module_load();
512                 if (!cnt)
513                         return ret;
514         }
515
516         if (!g_handle)
517                 haptic_internal_init();
518
519         /* power off vibration */
520         ret = h_ops->vibrate_monotone(g_handle, POWER_OFF_VIB_DURATION,
521                         POWER_VIB_FEEDBACK, HARDKEY_VIB_PRIORITY, &e_handle);
522         if (ret < 0) {
523                 _E("fail to vibrate_monotone : %d", ret);
524                 return ret;
525         }
526
527         /* sleep for vibration */
528         usleep(POWER_OFF_VIB_DURATION*1000);
529         return 0;
530 }
531
532 static int haptic_powersaver_cb(void *data)
533 {
534         int powersaver_on = (int)data;
535
536         if (powersaver_on)
537                 haptic_stop();
538         else
539                 haptic_start();
540
541         return 0;
542 }
543
544 static const struct edbus_method edbus_methods[] = {
545         { "GetCount",          NULL,   "i", edbus_get_count },
546         { "OpenDevice",         "i",   "i", edbus_open_device },
547         { "CloseDevice",        "u",   "i", edbus_close_device },
548         { "StopDevice",         "u",   "i", edbus_stop_device },
549         { "VibrateMonotone", "uiii",   "i", edbus_vibrate_monotone },
550         { "VibrateBuffer", "uayiii",   "i", edbus_vibrate_buffer },
551         { "GetState",           "i",   "i", edbus_get_state },
552         { "GetDuration",      "uay",   "i", edbus_get_duration },
553         { "CreateEffect",    "iayi", "ayi", edbus_create_effect },
554         { "SaveBinary",       "ays",   "i", edbus_save_binary },
555         /* Add methods here */
556 };
557
558 static void haptic_init(void *data)
559 {
560         int r;
561
562         /* init dbus interface */
563         r = register_edbus_method(DEVICED_PATH_HAPTIC, edbus_methods, ARRAY_SIZE(edbus_methods));
564         if (r < 0)
565                 _E("fail to init edbus method(%d)", r);
566
567         /* register notifier for below each event */
568         register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, haptic_booting_done_cb);
569         register_notifier(DEVICE_NOTIFIER_TOUCH_HARDKEY, haptic_hardkey_changed_cb);
570         register_notifier(DEVICE_NOTIFIER_POWEROFF_HAPTIC, haptic_poweroff_cb);
571         register_notifier(DEVICE_NOTIFIER_POWERSAVER, haptic_powersaver_cb);
572 }
573
574 static void haptic_exit(void *data)
575 {
576         struct haptic_ops *ops;
577         dd_list *elem;
578         int r;
579
580         /* unregister notifier for below each event */
581         unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, haptic_booting_done_cb);
582         unregister_notifier(DEVICE_NOTIFIER_TOUCH_HARDKEY, haptic_hardkey_changed_cb);
583         unregister_notifier(DEVICE_NOTIFIER_POWEROFF_HAPTIC, haptic_poweroff_cb);
584         unregister_notifier(DEVICE_NOTIFIER_POWERSAVER, haptic_powersaver_cb);
585
586         if (!CHECK_VALID_OPS(h_ops, r))
587                 return;
588
589         /* haptic exit for deviced */
590         haptic_internal_exit();
591
592         /* release plugin */
593         DD_LIST_FOREACH(h_head, elem, ops) {
594                 if (ops->is_valid && ops->is_valid()) {
595                         if (ops->release)
596                                 ops->release();
597                         h_ops = NULL;
598                         break;
599                 }
600         }
601 }
602
603 static int haptic_start(void)
604 {
605         _I("start");
606         haptic_disabled = false;
607         return 0;
608 }
609
610 static int haptic_stop(void)
611 {
612         _I("stop");
613         haptic_disabled = true;
614         return 0;
615 }
616
617 static const struct device_ops haptic_device_ops = {
618         .priority = DEVICE_PRIORITY_NORMAL,
619         .name     = "haptic",
620         .init     = haptic_init,
621         .exit     = haptic_exit,
622 };
623
624 DEVICE_OPS_REGISTER(&haptic_device_ops)