tizen 2.3 release
[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 <assert.h>
24 #include <vconf.h>
25
26 #include "core/log.h"
27 #include "core/list.h"
28 #include "core/common.h"
29 #include "core/devices.h"
30 #include "core/edbus-handler.h"
31 #include "core/device-notifier.h"
32 #include "core/config-parser.h"
33 #include "powersaver/powersaver.h"
34 #include "haptic.h"
35
36 #ifndef DATADIR
37 #define DATADIR         "/usr/share/deviced"
38 #endif
39
40 #define HAPTIC_CONF_PATH                        "/etc/deviced/haptic.conf"
41
42 /* hardkey vibration variable */
43 #define HARDKEY_VIB_ITERATION           1
44 #define HARDKEY_VIB_FEEDBACK            3
45 #define HARDKEY_VIB_PRIORITY            2
46 #define HARDKEY_VIB_DURATION            300
47 #define HAPTIC_FEEDBACK_STEP            20
48
49 /* power on, power off vibration variable */
50 #define POWER_ON_VIB_DURATION                   300
51 #define POWER_OFF_VIB_DURATION                  300
52 #define POWER_VIB_FEEDBACK                      100
53
54 #define MAX_EFFECT_BUFFER                       (64*1024)
55
56 #ifndef VCONFKEY_RECORDER_STATE
57 #define VCONFKEY_RECORDER_STATE "memory/recorder/state"
58 #define VCONFKEY_RECORDER_STATE_RECORDING       2
59 #endif
60
61 #define CHECK_VALID_OPS(ops, r)         ((ops) ? true : !(r = -ENODEV))
62
63 /* for playing */
64 static int g_handle;
65
66 /* haptic operation variable */
67 static dd_list *h_head;
68 static const struct haptic_plugin_ops *h_ops;
69 static enum haptic_type h_type;
70 static bool haptic_disabled;
71 static int powersaver_on = 0;
72
73 struct haptic_config {
74         int level;
75         int *level_arr;
76 };
77
78 static struct haptic_config haptic_conf;
79
80 static int haptic_start(void);
81 static int haptic_stop(void);
82 static int haptic_internal_init(void);
83
84 void add_haptic(const struct haptic_ops *ops)
85 {
86         DD_LIST_APPEND(h_head, (void*)ops);
87 }
88
89 void remove_haptic(const struct haptic_ops *ops)
90 {
91         DD_LIST_REMOVE(h_head, (void*)ops);
92 }
93
94 static int haptic_module_load(void)
95 {
96         struct haptic_ops *ops;
97         dd_list *elem;
98         int r;
99
100         /* find valid plugin */
101         DD_LIST_FOREACH(h_head, elem, ops) {
102                 if (ops->is_valid && ops->is_valid()) {
103                         if (ops->load)
104                                 h_ops = ops->load();
105                         h_type = ops->type;
106                         break;
107                 }
108         }
109
110         if (!CHECK_VALID_OPS(h_ops, r)) {
111                 _E("Can't find the valid haptic device");
112                 return r;
113         }
114
115         /* solution bug
116          * we do not use internal vibration except power off.
117          * if the last handle is closed during the playing of vibration,
118          * solution makes unlimited vibration.
119          * so we need at least one handle. */
120         haptic_internal_init();
121
122         return 0;
123 }
124
125 static int convert_magnitude_by_conf(int level)
126 {
127         int i, step;
128
129         assert(level >= 0 && level <= 100);
130
131         step = 100 / (haptic_conf.level-1);
132         for (i = 0; i < haptic_conf.level; ++i) {
133                 if (level <= i*step) {
134                         _D("level changed : %d -> %d", level, haptic_conf.level_arr[i]);
135                         return haptic_conf.level_arr[i];
136                 }
137         }
138
139         return -EINVAL;
140 }
141
142 static DBusMessage *edbus_get_count(E_DBus_Object *obj, DBusMessage *msg)
143 {
144         DBusMessageIter iter;
145         DBusMessage *reply;
146         int ret, val;
147
148         if (!CHECK_VALID_OPS(h_ops, ret))
149                 goto exit;
150
151         ret = h_ops->get_device_count(&val);
152         if (ret >= 0)
153                 ret = val;
154
155 exit:
156         reply = dbus_message_new_method_return(msg);
157         dbus_message_iter_init_append(reply, &iter);
158         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
159         return reply;
160 }
161
162 static DBusMessage *edbus_open_device(E_DBus_Object *obj, DBusMessage *msg)
163 {
164         DBusMessageIter iter;
165         DBusMessage *reply;
166         int index, handle, ret;
167
168         /* Load haptic module before booting done */
169         if (!CHECK_VALID_OPS(h_ops, ret)) {
170                 ret = haptic_module_load();
171                 if (ret < 0)
172                         goto exit;
173         }
174
175         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) {
176                 ret = -EINVAL;
177                 goto exit;
178         }
179
180         ret = h_ops->open_device(index, &handle);
181         if (ret >= 0)
182                 ret = handle;
183
184 exit:
185         reply = dbus_message_new_method_return(msg);
186         dbus_message_iter_init_append(reply, &iter);
187         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
188         return reply;
189 }
190
191 static DBusMessage *edbus_close_device(E_DBus_Object *obj, DBusMessage *msg)
192 {
193         DBusMessageIter iter;
194         DBusMessage *reply;
195         unsigned int handle;
196         int ret;
197
198         if (!CHECK_VALID_OPS(h_ops, ret))
199                 goto exit;
200
201         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID)) {
202                 ret = -EINVAL;
203                 goto exit;
204         }
205
206         ret = h_ops->close_device(handle);
207
208 exit:
209         reply = dbus_message_new_method_return(msg);
210         dbus_message_iter_init_append(reply, &iter);
211         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
212         return reply;
213 }
214
215 static DBusMessage *edbus_vibrate_monotone(E_DBus_Object *obj, DBusMessage *msg)
216 {
217         DBusMessageIter iter;
218         DBusMessage *reply;
219         unsigned int handle;
220         int duration, level, priority, e_handle, ret = 0;
221
222         if (!CHECK_VALID_OPS(h_ops, ret))
223                 goto exit;
224
225         if (haptic_disabled)
226                 goto exit;
227
228         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
229                                 DBUS_TYPE_INT32, &duration,
230                                 DBUS_TYPE_INT32, &level,
231                                 DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) {
232                 ret = -EINVAL;
233                 goto exit;
234         }
235
236         /* convert as per conf value */
237         level = convert_magnitude_by_conf(level);
238         if (level < 0) {
239                 ret = -EINVAL;
240                 goto exit;
241         }
242
243         ret = h_ops->vibrate_monotone(handle, duration, level, priority, &e_handle);
244         if (ret >= 0)
245                 ret = e_handle;
246
247 exit:
248         reply = dbus_message_new_method_return(msg);
249         dbus_message_iter_init_append(reply, &iter);
250         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
251         return reply;
252
253 }
254
255 static DBusMessage *edbus_vibrate_buffer(E_DBus_Object *obj, DBusMessage *msg)
256 {
257         DBusMessageIter iter;
258         DBusMessage *reply;
259         unsigned int handle;
260         unsigned char *data;
261         int size, iteration, level, priority, e_handle, ret = 0;
262
263         if (!CHECK_VALID_OPS(h_ops, ret))
264                 goto exit;
265
266         if (haptic_disabled)
267                 goto exit;
268
269         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
270                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size,
271                                 DBUS_TYPE_INT32, &iteration,
272                                 DBUS_TYPE_INT32, &level,
273                                 DBUS_TYPE_INT32, &priority, DBUS_TYPE_INVALID)) {
274                 ret = -EINVAL;
275                 goto exit;
276         }
277
278         /* convert as per conf value */
279         level = convert_magnitude_by_conf(level);
280         if (level < 0) {
281                 ret = -EINVAL;
282                 goto exit;
283         }
284
285         ret = h_ops->vibrate_buffer(handle, data, iteration, level, priority, &e_handle);
286         if (ret >= 0)
287                 ret = e_handle;
288
289 exit:
290         reply = dbus_message_new_method_return(msg);
291         dbus_message_iter_init_append(reply, &iter);
292         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
293         return reply;
294 }
295
296 static DBusMessage *edbus_stop_device(E_DBus_Object *obj, DBusMessage *msg)
297 {
298         DBusMessageIter iter;
299         DBusMessage *reply;
300         unsigned int handle;
301         int ret = 0;
302
303         if (!CHECK_VALID_OPS(h_ops, ret))
304                 goto exit;
305
306         if (haptic_disabled)
307                 goto exit;
308
309         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID)) {
310                 ret = -EINVAL;
311                 goto exit;
312         }
313
314         ret = h_ops->stop_device(handle);
315
316 exit:
317         reply = dbus_message_new_method_return(msg);
318         dbus_message_iter_init_append(reply, &iter);
319         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
320         return reply;
321 }
322
323 static DBusMessage *edbus_get_state(E_DBus_Object *obj, DBusMessage *msg)
324 {
325         DBusMessageIter iter;
326         DBusMessage *reply;
327         int index, state, ret;
328
329         if (!CHECK_VALID_OPS(h_ops, ret))
330                 goto exit;
331
332         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &index, DBUS_TYPE_INVALID)) {
333                 ret = -EINVAL;
334                 goto exit;
335         }
336
337         ret = h_ops->get_device_state(index, &state);
338         if (ret >= 0)
339                 ret = state;
340
341 exit:
342         reply = dbus_message_new_method_return(msg);
343         dbus_message_iter_init_append(reply, &iter);
344         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
345         return reply;
346 }
347
348 static DBusMessage *edbus_create_effect(E_DBus_Object *obj, DBusMessage *msg)
349 {
350         static unsigned char data[MAX_EFFECT_BUFFER];
351         static unsigned char *p = data;
352         DBusMessageIter iter, arr;
353         DBusMessage *reply;
354         haptic_module_effect_element *elem_arr;
355         int i, size, cnt, ret, bufsize = sizeof(data);
356
357         if (!CHECK_VALID_OPS(h_ops, ret))
358                 goto exit;
359
360         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &bufsize,
361                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &elem_arr, &size,
362                                 DBUS_TYPE_INT32, &cnt, DBUS_TYPE_INVALID)) {
363                 ret = -EINVAL;
364                 goto exit;
365         }
366
367         if (bufsize > MAX_EFFECT_BUFFER) {
368                 ret = -ENOMEM;
369                 goto exit;
370         }
371
372         for (i = 0; i < cnt; ++i)
373                 _D("[%2d] %d %d", i, elem_arr[i].haptic_duration, elem_arr[i].haptic_level);
374
375         memset(data, 0, MAX_EFFECT_BUFFER);
376         ret = h_ops->create_effect(data, bufsize, elem_arr, cnt);
377
378 exit:
379         reply = dbus_message_new_method_return(msg);
380         dbus_message_iter_init_append(reply, &iter);
381         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr);
382         dbus_message_iter_append_fixed_array(&arr, DBUS_TYPE_BYTE, &p, bufsize);
383         dbus_message_iter_close_container(&iter, &arr);
384         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
385         return reply;
386 }
387
388 static DBusMessage *edbus_get_duration(E_DBus_Object *obj, DBusMessage *msg)
389 {
390         DBusMessageIter iter;
391         DBusMessage *reply;
392         unsigned int handle;
393         unsigned char *data;
394         int size, duration, ret;
395
396         if (!CHECK_VALID_OPS(h_ops, ret))
397                 goto exit;
398
399         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
400                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size,
401                                 DBUS_TYPE_INVALID)) {
402                 ret = -EINVAL;
403                 goto exit;
404         }
405
406         ret = h_ops->get_buffer_duration(handle, data, &duration);
407         if (ret >= 0)
408                 ret = duration;
409
410 exit:
411         reply = dbus_message_new_method_return(msg);
412         dbus_message_iter_init_append(reply, &iter);
413         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
414         return reply;
415 }
416
417 static DBusMessage *edbus_save_binary(E_DBus_Object *obj, DBusMessage *msg)
418 {
419         DBusMessageIter iter;
420         DBusMessage *reply;
421         unsigned char *data;
422         unsigned char *path;
423         int size, ret;
424
425         if (!CHECK_VALID_OPS(h_ops, ret))
426                 goto exit;
427
428         if (!dbus_message_get_args(msg, NULL,
429                                 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, &size,
430                                 DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) {
431                 ret = -EINVAL;
432                 goto exit;
433         }
434
435         _D("file path : %s", path);
436         ret = h_ops->convert_binary(data, size, path);
437
438 exit:
439         reply = dbus_message_new_method_return(msg);
440         dbus_message_iter_init_append(reply, &iter);
441         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
442         return reply;
443 }
444
445 static unsigned char* convert_file_to_buffer(const char *file_name, int *size)
446 {
447         FILE *pf;
448         long file_size;
449         unsigned char *pdata = NULL;
450
451         if (!file_name)
452                 return NULL;
453
454         /* Get File Stream Pointer */
455         pf = fopen(file_name, "rb");
456         if (!pf) {
457                 _E("fopen failed : %s", strerror(errno));
458                 return NULL;
459         }
460
461         if (fseek(pf, 0, SEEK_END))
462                 goto error;
463
464         file_size = ftell(pf);
465         if (fseek(pf, 0, SEEK_SET))
466                 goto error;
467
468         if (file_size < 0)
469                 goto error;
470
471         pdata = (unsigned char*)malloc(file_size);
472         if (!pdata)
473                 goto error;
474
475         if (fread(pdata, 1, file_size, pf) != file_size)
476                 goto err_free;
477
478         fclose(pf);
479         *size = file_size;
480         return pdata;
481
482 err_free:
483         free(pdata);
484
485 error:
486         fclose(pf);
487
488         _E("failed to convert file to buffer (%s)", strerror(errno));
489         return NULL;
490 }
491
492 static int haptic_internal_init(void)
493 {
494         int r;
495         if (!CHECK_VALID_OPS(h_ops, r))
496                 return r;
497         return h_ops->open_device(HAPTIC_MODULE_DEVICE_ALL, &g_handle);
498 }
499
500 static int haptic_internal_exit(void)
501 {
502         int r;
503         if (!CHECK_VALID_OPS(h_ops, r))
504                 return r;
505         return h_ops->close_device(g_handle);
506 }
507
508 static int haptic_hardkey_changed_cb(void *data)
509 {
510         int size, level, status, e_handle, ret;
511         unsigned char *buf;
512
513         if (!CHECK_VALID_OPS(h_ops, ret)) {
514                 ret = haptic_module_load();
515                 if (ret < 0)
516                         return ret;
517         }
518
519         if (!g_handle)
520                 haptic_internal_init();
521
522         /* if haptic is stopped, do not play vibration */
523         if (haptic_disabled || powersaver_on)
524                 return 0;
525
526         if (vconf_get_bool(VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL, &status) < 0) {
527                 _E("fail to get VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL");
528                 status = 1;
529         }
530
531         /* when turn off haptic feedback option */
532         if (!status)
533                 return 0;
534
535         ret = vconf_get_int(VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT, &level);
536         if (ret < 0) {
537                 _E("fail to get VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT");
538                 level = HARDKEY_VIB_FEEDBACK;
539         }
540
541         ret = h_ops->vibrate_monotone(g_handle, HARDKEY_VIB_DURATION,
542                         level*HAPTIC_FEEDBACK_STEP, HARDKEY_VIB_PRIORITY, &e_handle);
543         if (ret < 0)
544                 _E("fail to vibrate buffer : %d", ret);
545
546         return ret;
547 }
548
549 static int haptic_poweroff_cb(void *data)
550 {
551         int e_handle, ret;
552
553         if (!CHECK_VALID_OPS(h_ops, ret)) {
554                 ret = haptic_module_load();
555                 if (ret < 0)
556                         return ret;
557         }
558
559         if (!g_handle)
560                 haptic_internal_init();
561
562         /* power off vibration */
563         ret = h_ops->vibrate_monotone(g_handle, POWER_OFF_VIB_DURATION,
564                         POWER_VIB_FEEDBACK, HARDKEY_VIB_PRIORITY, &e_handle);
565         if (ret < 0) {
566                 _E("fail to vibrate_monotone : %d", ret);
567                 return ret;
568         }
569
570         /* sleep for vibration */
571         usleep(POWER_OFF_VIB_DURATION*1000);
572         return 0;
573 }
574
575 static int haptic_powersaver_cb(void *data)
576 {
577         int mode = (int)data;
578
579         switch (mode) {
580         case POWERSAVER_OFF:
581                 powersaver_on = false;
582                 break;
583         case POWERSAVER_BASIC:
584         case POWERSAVER_ENHANCED:
585                 powersaver_on = true;
586                 break;
587         default:
588                 return -EINVAL;
589         }
590
591         _I("changed powersaver %d", powersaver_on);
592         return 0;
593 }
594
595 static void sound_capturing_cb(keynode_t *key, void *data)
596 {
597         int status;
598
599         status = vconf_keynode_get_int(key);
600
601         /* if sound capture is in use, this value is 1(true). */
602         if (status == VCONFKEY_RECORDER_STATE_RECORDING)
603                 haptic_stop();
604         else
605                 haptic_start();
606 }
607
608 static int parse_section(struct parse_result *result, void *user_data, int index)
609 {
610         struct haptic_config *conf = (struct haptic_config*)user_data;
611
612         assert(result);
613         assert(result->section && result->name && result->value);
614
615         if (MATCH(result->name, "level")) {
616                 conf->level = atoi(result->value);
617                 conf->level_arr = calloc(sizeof(int), conf->level);
618                 if (!conf->level_arr) {
619                         _E("failed to allocate memory for level");
620                         return -errno;
621                 }
622         } else if (MATCH(result->name, "value")) {
623                 if (index < 0)
624                         return -EINVAL;
625                 conf->level_arr[index] = atoi(result->value);
626         }
627
628         return 0;
629 }
630
631 static int haptic_load_config(struct parse_result *result, void *user_data)
632 {
633         struct haptic_config *conf = (struct haptic_config*)user_data;
634         char name[NAME_MAX];
635         int ret;
636         static int index = 0;
637
638         if (!result)
639                 return 0;
640
641         if (!result->section || !result->name || !result->value)
642                 return 0;
643
644         /* Parsing 'Haptic' section */
645         if (MATCH(result->section, "Haptic")) {
646                 ret = parse_section(result, user_data, -1);
647                 if (ret < 0) {
648                         _E("failed to parse [Haptic] section : %d", ret);
649                         return ret;
650                 }
651                 goto out;
652         }
653
654         /* Parsing 'Level' section */
655         for (index = 0; index < conf->level; ++index) {
656                 snprintf(name, sizeof(name), "level%d", index);
657                 if (MATCH(result->section, name)) {
658                         ret = parse_section(result, user_data, index);
659                         if (ret < 0) {
660                                 _E("failed to parse [level] section : %d", ret);
661                                 return ret;
662                         }
663                         goto out;
664                 }
665         }
666
667 out:
668         return 0;
669 }
670
671 static const struct edbus_method edbus_methods[] = {
672         { "GetCount",          NULL,   "i", edbus_get_count },
673         { "OpenDevice",         "i",   "i", edbus_open_device },
674         { "CloseDevice",        "u",   "i", edbus_close_device },
675         { "StopDevice",         "u",   "i", edbus_stop_device },
676         { "VibrateMonotone", "uiii",   "i", edbus_vibrate_monotone },
677         { "VibrateBuffer", "uayiii",   "i", edbus_vibrate_buffer },
678         { "GetState",           "i",   "i", edbus_get_state },
679         { "GetDuration",      "uay",   "i", edbus_get_duration },
680         { "CreateEffect",    "iayi", "ayi", edbus_create_effect },
681         { "SaveBinary",       "ays",   "i", edbus_save_binary },
682         /* Add methods here */
683 };
684
685 static void haptic_init(void *data)
686 {
687         int r;
688
689         /* Load haptic module */
690         haptic_module_load();
691
692         /* get haptic data from configuration file */
693         r = config_parse(HAPTIC_CONF_PATH, haptic_load_config, &haptic_conf);
694         if (r < 0) {
695                 _E("failed to load configuration file(%s) : %d", HAPTIC_CONF_PATH, r);
696                 safe_free(haptic_conf.level_arr);
697         }
698
699         /* init dbus interface */
700         r = register_edbus_method(DEVICED_PATH_HAPTIC, edbus_methods, ARRAY_SIZE(edbus_methods));
701         if (r < 0)
702                 _E("fail to init edbus method(%d)", r);
703
704         /* register notifier for below each event */
705         register_notifier(DEVICE_NOTIFIER_TOUCH_HARDKEY, haptic_hardkey_changed_cb);
706         register_notifier(DEVICE_NOTIFIER_POWEROFF_HAPTIC, haptic_poweroff_cb);
707         register_notifier(DEVICE_NOTIFIER_PMQOS_ULTRAPOWERSAVING,
708             haptic_powersaver_cb);
709
710         /* add watch for sound capturing value */
711         vconf_notify_key_changed(VCONFKEY_RECORDER_STATE, sound_capturing_cb, NULL);
712 }
713
714 static void haptic_exit(void *data)
715 {
716         struct haptic_ops *ops;
717         dd_list *elem;
718         int r;
719
720         /* remove watch */
721         vconf_ignore_key_changed(VCONFKEY_RECORDER_STATE, sound_capturing_cb);
722
723         /* unregister notifier for below each event */
724         unregister_notifier(DEVICE_NOTIFIER_TOUCH_HARDKEY, haptic_hardkey_changed_cb);
725         unregister_notifier(DEVICE_NOTIFIER_POWEROFF_HAPTIC, haptic_poweroff_cb);
726         unregister_notifier(DEVICE_NOTIFIER_PMQOS_ULTRAPOWERSAVING,
727             haptic_powersaver_cb);
728
729         /* release haptic data memory */
730         safe_free(haptic_conf.level_arr);
731
732         if (!CHECK_VALID_OPS(h_ops, r))
733                 return;
734
735         /* haptic exit for deviced */
736         haptic_internal_exit();
737
738         /* release plugin */
739         DD_LIST_FOREACH(h_head, elem, ops) {
740                 if (ops->is_valid && ops->is_valid()) {
741                         if (ops->release)
742                                 ops->release();
743                         h_ops = NULL;
744                         break;
745                 }
746         }
747 }
748
749 static int haptic_start(void)
750 {
751         _I("start");
752         haptic_disabled = false;
753         return 0;
754 }
755
756 static int haptic_stop(void)
757 {
758         _I("stop");
759         haptic_disabled = true;
760         return 0;
761 }
762
763 static const struct device_ops haptic_device_ops = {
764         .priority = DEVICE_PRIORITY_NORMAL,
765         .name     = "haptic",
766         .init     = haptic_init,
767         .exit     = haptic_exit,
768 };
769
770 DEVICE_OPS_REGISTER(&haptic_device_ops)