tizen 2.3.1 release
[framework/system/libfeedback.git] / src / vibrator.c
1 /*
2  * libfeedback
3  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the License);
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18
19 #include <stdio.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <limits.h>
25 #include <vconf.h>
26 #include <sys/stat.h>
27
28 #include "feedback-ids.h"
29 #include "common.h"
30 #include "log.h"
31 #include "devices.h"
32 #include "xmlparser.h"
33 #include "dbus.h"
34
35 #define DEFAULT_VIB_LEVEL                       3
36 #define HAPTIC_FEEDBACK_STEP            20 /**< feedback max / slider step */
37
38 #define HAPTIC_DEVICE                           0
39
40 enum haptic_priority
41 {
42         HAPTIC_PRIORITY_MIN = 0,
43         HAPTIC_PRIORITY_MIDDLE,
44         HAPTIC_PRIORITY_HIGH,
45 };
46
47 enum haptic_iteration
48 {
49         HAPTIC_ITERATION_ONCE = 1,
50         HAPTIC_ITERATION_INFINITE = 256,
51 };
52
53 #define VIBRATION_XML                           "/usr/share/feedback/vibration.xml"
54
55 #define METHOD_OPEN                                     "OpenDevice"
56 #define METHOD_CLOSE                            "CloseDevice"
57 #define METHOD_VIBRATE_BUFFER           "VibrateBuffer"
58 #define METHOD_STOP                                     "StopDevice"
59
60 static int vibstatus;
61 static int noti_vibstatus;
62 static int vib_level;
63 static int noti_level;
64 static int feedbackstatus;
65
66 static unsigned int v_handle;
67 static xmlDocPtr v_doc;
68
69 static char haptic_file[FEEDBACK_PATTERN_END][NAME_MAX];
70
71 static int haptic_open(void)
72 {
73         char *arr[1];
74         char buf_index[32];
75
76         snprintf(buf_index, sizeof(buf_index), "%d", HAPTIC_DEVICE);
77         arr[0] = buf_index;
78
79         return dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_HAPTIC,
80                         DEVICED_INTERFACE_HAPTIC, METHOD_OPEN,
81                         "i", arr);
82 }
83
84 static int haptic_close(unsigned int handle)
85 {
86         char *arr[1];
87         char buf_handle[32];
88
89         snprintf(buf_handle, sizeof(buf_handle), "%u", handle);
90         arr[0] = buf_handle;
91
92         return dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_HAPTIC,
93                         DEVICED_INTERFACE_HAPTIC, METHOD_CLOSE,
94                         "u", arr);
95 }
96
97 static int haptic_vibrate_buffer(unsigned int handle,
98                                                                 const unsigned char *buffer,
99                                                                 int size,
100                                                                 int iteration,
101                                                                 int feedback,
102                                                                 int priority)
103 {
104         char *arr[6];
105         char buf_handle[32];
106         char buf_iteration[32];
107         char buf_feedback[32];
108         char buf_priority[32];
109         struct dbus_byte bytes;
110
111         snprintf(buf_handle, sizeof(buf_handle), "%u", handle);
112         arr[0] = buf_handle;
113         bytes.size = size;
114         bytes.data = buffer;
115         arr[2] = (char*)&bytes;
116         snprintf(buf_iteration, sizeof(buf_iteration), "%d", iteration);
117         arr[3] = buf_iteration;
118         snprintf(buf_feedback, sizeof(buf_feedback), "%d", feedback);
119         arr[4] = buf_feedback;
120         snprintf(buf_priority, sizeof(buf_priority), "%d", priority);
121         arr[5] = buf_priority;
122
123         return dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_HAPTIC,
124                         DEVICED_INTERFACE_HAPTIC, METHOD_VIBRATE_BUFFER,
125                         "uayiii", arr);
126 }
127
128 static int haptic_vibrate_stop(unsigned int handle)
129 {
130         char *arr[1];
131         char buf_handle[32];
132
133         snprintf(buf_handle, sizeof(buf_handle), "%u", handle);
134         arr[0] = buf_handle;
135
136         return dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_HAPTIC,
137                         DEVICED_INTERFACE_HAPTIC, METHOD_STOP,
138                         "u", arr);
139 }
140
141 static unsigned char* convert_file_to_buffer(const char *file_name, int *size)
142 {
143         FILE *pf;
144         long file_size;
145         unsigned char *pdata = NULL;
146
147         if (!file_name)
148                 return NULL;
149
150         /* Get File Stream Pointer */
151         pf = fopen(file_name, "rb");
152         if (!pf) {
153                 _E("fopen failed : %d", errno);
154                 return NULL;
155         }
156
157         if (fseek(pf, 0, SEEK_END))
158                 goto error;
159
160         file_size = ftell(pf);
161         if (fseek(pf, 0, SEEK_SET))
162                 goto error;
163
164         if (file_size < 0)
165                 goto error;
166
167         pdata = (unsigned char*)malloc(file_size);
168         if (!pdata)
169                 goto error;
170
171         if (fread(pdata, 1, file_size, pf) != file_size)
172                 goto err_free;
173
174         fclose(pf);
175         *size = file_size;
176         return pdata;
177
178 err_free:
179         free(pdata);
180
181 error:
182         fclose(pf);
183
184         _E("failed to convert file to buffer (%d)", errno);
185         return NULL;
186 }
187
188 static void feedback_noti_vibstatus_cb(keynode_t *key, void* data)
189 {
190         noti_vibstatus = vconf_keynode_get_bool(key);
191 }
192
193 static void feedback_vib_cb(keynode_t *key, void* data)
194 {
195         vib_level = vconf_keynode_get_int(key);
196 }
197
198 static void feedback_noti_cb(keynode_t *key, void* data)
199 {
200         noti_level = vconf_keynode_get_int(key);
201 }
202
203 static void feedback_feedbackstatus_cb(keynode_t *key, void* data)
204 {
205         feedbackstatus = vconf_keynode_get_bool(key);
206 }
207
208 static int get_priority(feedback_pattern_e pattern)
209 {
210         if (pattern >= FEEDBACK_PATTERN_TAP && pattern <= FEEDBACK_PATTERN_HW_HOLD)
211                 return HAPTIC_PRIORITY_MIN;
212
213         return HAPTIC_PRIORITY_MIDDLE;
214 }
215
216 static int get_haptic_level(feedback_pattern_e pattern)
217 {
218         int level;
219
220         if (pattern >= FEEDBACK_PATTERN_MESSAGE && pattern <= FEEDBACK_PATTERN_SMART_ALERT)
221                 level = noti_level * HAPTIC_FEEDBACK_STEP;
222         else
223                 level = vib_level * HAPTIC_FEEDBACK_STEP;
224
225         _D("Call status : %d, pattern : %s, level : %d", callstatus, str_pattern[pattern], level);
226         if (callstatus == VCONFKEY_CALL_VOICE_ACTIVE
227             || callstatus == VCONFKEY_CALL_VIDEO_ACTIVE) {
228                 // if call status is ON, vibration magnitude is 20%
229                 level = 20;
230                 _D("level changed : %d", level);
231         }
232
233         return level;
234 }
235
236 static bool get_always_alert_case(feedback_pattern_e pattern)
237 {
238         switch (pattern) {
239         case FEEDBACK_PATTERN_KEY0 ... FEEDBACK_PATTERN_KEY_BACK:
240         case FEEDBACK_PATTERN_HOLD:
241                 if (feedbackstatus)
242                         return true;
243                 break;
244         case FEEDBACK_PATTERN_SIP:
245         case FEEDBACK_PATTERN_SIP_BACKSPACE:
246         case FEEDBACK_PATTERN_SIP_FUNCTION:
247         case FEEDBACK_PATTERN_SIP_FJKEY:
248                 return true;
249         case FEEDBACK_PATTERN_TIMER:
250         case FEEDBACK_PATTERN_TIMER_ON_CALL:
251         case FEEDBACK_PATTERN_WAKEUP:
252         case FEEDBACK_PATTERN_WAKEUP_ON_CALL:
253                 return true;
254         case FEEDBACK_PATTERN_MESSAGE_ON_CALL:
255         case FEEDBACK_PATTERN_EMAIL_ON_CALL:
256         case FEEDBACK_PATTERN_GENERAL_ON_CALL:
257                 if (alert_callstatus)
258                         return true;
259                 break;
260         case FEEDBACK_PATTERN_MESSAGE:
261         case FEEDBACK_PATTERN_EMAIL:
262                 if (noti_vibstatus)
263                         return true;
264                 break;
265         case FEEDBACK_PATTERN_3RD_APPLICATION:
266         case FEEDBACK_PATTERN_SMART_ALERT:
267         case FEEDBACK_PATTERN_SEND_SOS_MESSAGE:
268         case FEEDBACK_PATTERN_END_SOS_MESSAGE:
269         case FEEDBACK_PATTERN_CMAS:
270         case FEEDBACK_PATTERN_OUTGOING_CALL:
271         case FEEDBACK_PATTERN_MMS:
272         case FEEDBACK_PATTERN_HOURLY_ALERT:
273                 return true;
274         case FEEDBACK_PATTERN_SPEED_UP:
275         case FEEDBACK_PATTERN_SLOW_DOWN:
276         case FEEDBACK_PATTERN_KEEP_THIS_PACE:
277         case FEEDBACK_PATTERN_GOAL_ACHIEVED:
278         case FEEDBACK_PATTERN_EXERCISE_COUNT:
279         case FEEDBACK_PATTERN_START_CUE:
280                 /* except mute case */
281                 if (is_sound_mode() ||  vibstatus)
282                         return true;
283                 break;
284         case FEEDBACK_PATTERN_CHARGERCONN_ON_CALL:
285         case FEEDBACK_PATTERN_CHARGING_ERROR_ON_CALL:
286         case FEEDBACK_PATTERN_LOWBATT_ON_CALL:
287                 /* no matter sound profile */
288                 return true;
289         default:
290                 break;
291         }
292         return false;
293 }
294
295 static bool get_always_off_case(feedback_pattern_e pattern)
296 {
297         switch (pattern) {
298         case FEEDBACK_PATTERN_KEY0 ... FEEDBACK_PATTERN_KEY_BACK:
299         case FEEDBACK_PATTERN_HOLD:
300                 if (!feedbackstatus)
301                         return true;
302                 break;
303         default:
304                 break;
305         }
306         return false;
307 }
308
309 static int get_xml_data(xmlDocPtr doc, feedback_pattern_e pattern, struct xmlData **data)
310 {
311         xmlNodePtr cur;
312         struct xmlData *retData;
313
314         cur = xml_find(doc, VIBRATION_STR, (const xmlChar*)str_pattern[pattern]);
315         /* This pattern does not have sound file to play */
316         if (cur == NULL)
317                 return -ENOENT;
318
319         retData = xml_parse(doc, cur);
320         if (retData == NULL) {
321                 _E("xml_parse fail");
322                 return -EPERM;
323         }
324
325         *data = retData;
326         return 0;
327 }
328
329 static void release_xml_data(struct xmlData *data)
330 {
331         if (data == NULL)
332                 return;
333
334         xml_free(data);
335 }
336
337 static void vibrator_init(void)
338 {
339         int ret;
340
341         /* xml Init */
342         v_doc = xml_open(VIBRATION_XML);
343         if (v_doc == NULL) {
344                 _E("xml_open(%s) fail", VIBRATION_XML);
345                 return;
346         }
347
348         /* Vibration Init */
349         ret = haptic_open();
350         if (ret < 0) {
351                 _E("haptic_open ==> FAIL!! : %d", ret);
352                 xml_close(v_doc);
353                 v_doc = NULL;
354                 return;
355         }
356
357         /* Set vibration handle */
358         v_handle = (unsigned int)ret;
359
360         /* check vibration status */
361         if (vconf_get_bool(VCONFKEY_SETAPPL_VIBRATE_WHEN_NOTIFICATION_BOOL, &noti_vibstatus) < 0)
362                 _W("VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL ==> FAIL!!");
363
364         /* check vib_level */
365         if (vconf_get_int(VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT, &vib_level) < 0)
366                 _W("VCONFKEY_FEEDBACK_VIBRATION_LEVEL_INT ==> FAIL!!");
367
368         /* check noti_level */
369         if (vconf_get_int(VCONFKEY_SETAPPL_NOTI_VIBRATION_LEVEL_INT, &noti_level) < 0)
370                 _W("VCONFKEY_SETAPPL_NOTI_VIBRATION_LEVEL_INT ==> FAIL!!");
371
372         /* feedback Init */
373         if(vconf_get_bool(VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL, &feedbackstatus) < 0)
374                 _W("VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL ==> FAIL!!");
375
376         /* add watch for status value */
377         vconf_notify_key_changed(VCONFKEY_SETAPPL_VIBRATE_WHEN_NOTIFICATION_BOOL, feedback_noti_vibstatus_cb, NULL);
378         vconf_notify_key_changed(VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT, feedback_vib_cb, NULL);
379         vconf_notify_key_changed(VCONFKEY_SETAPPL_NOTI_VIBRATION_LEVEL_INT, feedback_noti_cb, NULL);
380         vconf_notify_key_changed(VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL, feedback_feedbackstatus_cb, NULL);
381 }
382
383 static void vibrator_exit(void)
384 {
385         int ret;
386
387         /* remove watch */
388         vconf_ignore_key_changed(VCONFKEY_SETAPPL_VIBRATE_WHEN_NOTIFICATION_BOOL, feedback_noti_vibstatus_cb);
389         vconf_ignore_key_changed(VCONFKEY_SETAPPL_TOUCH_FEEDBACK_VIBRATION_LEVEL_INT, feedback_vib_cb);
390         vconf_ignore_key_changed(VCONFKEY_SETAPPL_NOTI_VIBRATION_LEVEL_INT, feedback_noti_cb);
391         vconf_ignore_key_changed(VCONFKEY_SETAPPL_HAPTIC_FEEDBACK_STATUS_BOOL, feedback_feedbackstatus_cb);
392
393         if (v_handle) {
394                 ret = haptic_close(v_handle);
395                 if (ret < 0)
396                         _E("haptic_close is failed : %d", ret);
397                 v_handle = 0;
398         }
399
400         if (v_doc) {
401                 xml_close(v_doc);
402                 v_doc = NULL;
403         }
404 }
405
406 static int vibrator_play(feedback_pattern_e pattern)
407 {
408         int ret, size;
409         struct xmlData *data;
410         char *path;
411         unsigned char *buf;
412
413         if (!v_handle || !v_doc) {
414                 _E("Not initialize");
415                 return -EPERM;
416         }
417
418         if (vconf_get_bool(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL, &vibstatus) < 0) {
419                 _D("fail to get vibration status, will work as turning off");
420                 vibstatus = 0;
421         }
422
423         if (vibstatus == 0 && !get_always_alert_case(pattern))  {
424                 _D("Vibration condition is OFF (vibstatus : %d)", vibstatus);
425                 return 0;
426         }
427
428         if (vibstatus && get_always_off_case(pattern)) {
429                 _D("Vibration always off condition");
430                 return 0;
431         }
432
433         /* if there is a file path user defined */
434         path = haptic_file[pattern];
435         if (*path) {
436                 buf = convert_file_to_buffer(path, &size);
437                 if (!buf) {
438                         _E("convert_file_to_buffer is failed");
439                         return -EPERM;
440                 }
441
442                 ret = haptic_vibrate_buffer(v_handle, buf, size, HAPTIC_ITERATION_ONCE,
443                                 get_haptic_level(pattern), get_priority(pattern));
444                 if (ret < 0) {
445                         _E("haptic_vibrate_buffer is failed");
446                         free(buf);
447                         return -EPERM;
448                 }
449
450                 free(buf);
451                 return 0;
452         }
453
454         ret = get_xml_data(v_doc, pattern, &data);
455         if (ret == -ENOENT) {
456                 _D("No vibration case(%s)", str_pattern[pattern]);
457                 return 0;
458         }
459
460         if (ret < 0) {
461                 _E("get_xml_data fail");
462                 return -EPERM;
463         }
464
465         if (data->data == NULL) {
466                 _D("No vibration case(%s)", str_pattern[pattern]);
467                 release_xml_data(data);
468                 return 0;
469         }
470
471         /* play haptic buffer */
472         ret = haptic_vibrate_buffer(v_handle, (unsigned char*)data->data, data->size, HAPTIC_ITERATION_ONCE,
473                                         get_haptic_level(pattern), get_priority(pattern));
474         if (ret < 0) {
475                 _E("haptic_vibrate_buffer is failed");
476                 release_xml_data(data);
477                 return -EPERM;
478         }
479
480         release_xml_data(data);
481         return 0;
482 }
483
484 static int vibrator_stop(void)
485 {
486         int ret;
487
488         if (!v_handle || !v_doc) {
489                 _E("Not initialize");
490                 return -EPERM;
491         }
492
493         /* stop haptic device */
494         ret = haptic_vibrate_stop(v_handle);
495         if (ret < 0) {
496                 _E("haptic_vibrate_stop is failed");
497                 return -EPERM;
498         }
499
500         return 0;
501 }
502
503 static int vibrator_get_path(feedback_pattern_e pattern, char *buf, unsigned int buflen)
504 {
505         const char *cur_path;
506
507         assert(buf != NULL && buflen > 0);
508
509         cur_path = haptic_file[pattern];
510         if (*cur_path) {
511                 _E("This pattern(%s) in vibrator type is not supported to play", str_pattern[pattern]);
512                 snprintf(buf, buflen, "NULL");
513                 return -ENOENT;
514         }
515
516         snprintf(buf, buflen, "%s", cur_path);
517         return 0;
518 }
519
520 static int vibrator_set_path(feedback_pattern_e pattern, char *path)
521 {
522         struct stat buf;
523         char *ppath;
524
525         /*
526          * check the path is valid
527          * if path is null, below operation is ignored
528          */
529         if (path && stat(path, &buf)) {
530                 _E("%s is not presents", path);
531                 return -errno;
532         }
533
534         ppath = haptic_file[pattern];
535
536         /* if path is NULL, this pattern set to default file */
537         if (path)
538                 snprintf(ppath, NAME_MAX, "%s", path);
539         else
540                 memset(ppath, 0, NAME_MAX);
541
542         _D("The file of pattern(%s) is changed to [%s]", str_pattern[pattern], path);
543         return 0;
544 }
545
546 static const struct device_ops vibrator_device_ops = {
547         .type = FEEDBACK_TYPE_VIBRATION,
548         .init = vibrator_init,
549         .exit = vibrator_exit,
550         .play = vibrator_play,
551         .stop = vibrator_stop,
552         .get_path = vibrator_get_path,
553         .set_path = vibrator_set_path,
554 };
555
556 DEVICE_OPS_REGISTER(&vibrator_device_ops);