Merge branch 'tizen_3.0' into tizen
[platform/core/multimedia/libmm-sound.git] / mm_sound_device.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Sangchul Lee <sc11.lee@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include <mm_debug.h>
26
27 #include "include/mm_sound.h"
28 #include "include/mm_sound_device.h"
29 #include "include/mm_sound_client.h"
30
31 #define VOLUME_TYPE_LEN 64
32
33 mm_sound_device_list_t g_device_list;
34 pthread_mutex_t g_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
35
36 static int _check_for_valid_mask (int flags)
37 {
38         int ret = MM_ERROR_NONE;
39         bool at_least_cond = false;
40
41         if (flags > 0 && flags <= MM_SOUND_DEVICE_ALL_FLAG) {
42                 if (flags & MM_SOUND_DEVICE_IO_DIRECTION_IN_FLAG)
43                         at_least_cond = true;
44                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_IO_DIRECTION_OUT_FLAG))
45                         at_least_cond = true;
46                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_IO_DIRECTION_BOTH_FLAG))
47                         at_least_cond = true;
48                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_TYPE_INTERNAL_FLAG))
49                         at_least_cond = true;
50                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_TYPE_EXTERNAL_FLAG))
51                         at_least_cond = true;
52                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_STATE_DEACTIVATED_FLAG))
53                         at_least_cond = true;
54                 if (!at_least_cond && (flags & MM_SOUND_DEVICE_STATE_ACTIVATED_FLAG))
55                         at_least_cond = true;
56         } else {
57                 ret = MM_ERROR_INVALID_ARGUMENT;
58         }
59         if (!at_least_cond) {
60                 ret = MM_ERROR_INVALID_ARGUMENT;
61         }
62         if (ret) {
63                 debug_error("flags[0x%x] is not valid\n", flags);
64         }
65         return ret;
66 }
67
68 static int __convert_device_type_to_enum (char *device_type, mm_sound_device_type_e *device_type_enum)
69 {
70         int ret = MM_ERROR_NONE;
71
72         if (!device_type || !device_type_enum) {
73                 return MM_ERROR_INVALID_ARGUMENT;
74         }
75
76         if (!strncmp(device_type, "builtin-speaker", VOLUME_TYPE_LEN)) {
77                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_SPEAKER;
78         } else if (!strncmp(device_type, "builtin-receiver", VOLUME_TYPE_LEN)) {
79                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_RECEIVER;
80         } else if (!strncmp(device_type, "builtin-mic", VOLUME_TYPE_LEN)) {
81                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BUILTIN_MIC;
82         } else if (!strncmp(device_type, "audio-jack", VOLUME_TYPE_LEN)) {
83                 *device_type_enum = MM_SOUND_DEVICE_TYPE_AUDIOJACK;
84         } else if (!strncmp(device_type, "bt-a2dp", VOLUME_TYPE_LEN)) {
85                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BLUETOOTH_A2DP;
86         } else if (!strncmp(device_type, "bt-sco", VOLUME_TYPE_LEN)) {
87                 *device_type_enum = MM_SOUND_DEVICE_TYPE_BLUETOOTH_SCO;
88         } else if (!strncmp(device_type, "hdmi", VOLUME_TYPE_LEN)) {
89                 *device_type_enum = MM_SOUND_DEVICE_TYPE_HDMI;
90         } else if (!strncmp(device_type, "forwarding", VOLUME_TYPE_LEN)) {
91                 *device_type_enum = MM_SOUND_DEVICE_TYPE_MIRRORING;
92         } else if (!strncmp(device_type, "usb-audio", VOLUME_TYPE_LEN)) {
93                 *device_type_enum = MM_SOUND_DEVICE_TYPE_USB_AUDIO;
94         } else {
95                 ret = MM_ERROR_INVALID_ARGUMENT;
96                 debug_error("not supported device_type(%s), err(0x%08x)", device_type, ret);
97         }
98
99         return ret;
100 }
101
102 static int __free_device_list(mm_sound_device_list_t *device_list_t)
103 {
104         if (!device_list_t)
105                 return MM_ERROR_INVALID_ARGUMENT;
106
107         debug_log("free device list %p", device_list_t);
108         g_list_free_full(device_list_t->list, g_free);
109         g_free(device_list_t);
110
111         return MM_ERROR_NONE;
112 }
113
114 EXPORT_API
115 int mm_sound_add_device_connected_callback(int flags, mm_sound_device_connected_cb func, void *user_data, unsigned int *id)
116 {
117         int ret = MM_ERROR_NONE;
118
119         if (func == NULL || id == NULL) {
120                 debug_error("argument is not valid\n");
121                 return MM_ERROR_INVALID_ARGUMENT;
122         }
123         ret = _check_for_valid_mask(flags);
124         if (ret == MM_ERROR_NONE) {
125                 ret = mm_sound_client_add_device_connected_callback(flags, func, user_data, id);
126                 if (ret < 0) {
127                         debug_error("Could not add device connected callback, ret = %x\n", ret);
128                 }
129         }
130
131         return ret;
132 }
133
134 EXPORT_API
135 int mm_sound_remove_device_connected_callback(unsigned int id)
136 {
137         int ret = MM_ERROR_NONE;
138
139         ret = mm_sound_client_remove_device_connected_callback(id);
140         if (ret < 0) {
141                 debug_error("Could not remove device connected callback, ret = %x\n", ret);
142         }
143
144         return ret;
145 }
146
147 EXPORT_API
148 int mm_sound_add_device_information_changed_callback(int flags, mm_sound_device_info_changed_cb func, void *user_data, unsigned int *id)
149 {
150         int ret = MM_ERROR_NONE;
151
152         if (func == NULL || id == NULL) {
153                 debug_error("argument is not valid\n");
154                 return MM_ERROR_INVALID_ARGUMENT;
155         }
156         ret = _check_for_valid_mask(flags);
157         if (ret == MM_ERROR_NONE) {
158                 ret = mm_sound_client_add_device_info_changed_callback(flags, func, user_data, id);
159                 if (ret < 0) {
160                         debug_error("Could not add device information changed callback, ret = %x\n", ret);
161                 }
162         }
163
164         return ret;
165 }
166
167 EXPORT_API
168 int mm_sound_remove_device_information_changed_callback(unsigned int id)
169 {
170         int ret = MM_ERROR_NONE;
171
172         ret = mm_sound_client_remove_device_info_changed_callback(id);
173         if (ret < 0) {
174                 debug_error("Could not remove device information changed callback, ret = %x\n", ret);
175         }
176
177         return ret;
178 }
179
180 EXPORT_API
181 int mm_sound_add_device_state_changed_callback(int flags, mm_sound_device_state_changed_cb func, void *user_data, unsigned int *id)
182 {
183         int ret = MM_ERROR_NONE;
184
185         if (func == NULL || id == NULL) {
186                 debug_error("argument is not valid\n");
187                 return MM_ERROR_INVALID_ARGUMENT;
188         }
189         ret = _check_for_valid_mask(flags);
190         if (ret == MM_ERROR_NONE) {
191                 ret = mm_sound_client_add_device_state_changed_callback(flags, func, user_data, id);
192                 if (ret < 0) {
193                         debug_error("Could not add device state changed callback, ret = %x\n", ret);
194                 }
195         }
196
197         return ret;
198 }
199
200 EXPORT_API
201 int mm_sound_remove_device_state_changed_callback(unsigned int id)
202 {
203         int ret = MM_ERROR_NONE;
204
205         ret = mm_sound_client_remove_device_state_changed_callback(id);
206         if (ret < 0) {
207                 debug_error("Could not remove device state changed callback, ret = %x\n", ret);
208         }
209
210         return ret;
211 }
212
213
214 EXPORT_API
215 int mm_sound_get_current_device_list(mm_sound_device_flags_e flags, MMSoundDeviceList_t *device_list)
216 {
217         int ret = MM_ERROR_NONE;
218
219         if (!device_list) {
220                 return MM_ERROR_INVALID_ARGUMENT;
221         }
222         ret = _check_for_valid_mask(flags);
223         if (ret != MM_ERROR_NONE) {
224                 debug_error("mask[0x%x] is invalid, ret=0x%x", flags, ret);
225                 return ret;
226         }
227
228         pthread_mutex_lock(&g_thread_mutex);
229
230         if (g_device_list.list != NULL) {
231                 g_list_free_full(g_device_list.list, g_free);
232                 g_device_list.list = NULL;
233         }
234
235         g_device_list.is_new_device_list = true;
236
237         ret = mm_sound_client_get_current_connected_device_list(flags, &g_device_list);
238         if (ret < 0) {
239                 debug_error("Could not get current connected device list, ret = %x\n", ret);
240                 g_device_list.list = NULL;
241         } else {
242                 *device_list = &g_device_list;
243         }
244
245         pthread_mutex_unlock(&g_thread_mutex);
246
247         return ret;
248 }
249
250 EXPORT_API
251 int mm_sound_get_device_list(int flags, MMSoundDeviceList_t *device_list)
252 {
253         int ret = MM_ERROR_NONE;
254         mm_sound_device_list_t *_device_list;
255
256         if (!device_list) {
257                 return MM_ERROR_INVALID_ARGUMENT;
258         }
259         ret = _check_for_valid_mask(flags);
260         if (ret != MM_ERROR_NONE) {
261                 debug_error("mask[0x%x] is invalid, ret=0x%x", flags, ret);
262                 return ret;
263         }
264
265         if (!(_device_list = g_malloc0(sizeof(mm_sound_device_list_t)))) {
266                 debug_error("[Client] Allocate device list failed");
267                 return MM_ERROR_SOUND_INTERNAL;
268         }
269
270         _device_list->is_new_device_list = true;
271
272         ret = mm_sound_client_get_current_connected_device_list(flags, _device_list);
273         if (ret < 0) {
274                 debug_error("Could not get current connected device list, ret = %x\n", ret);
275                 g_free(_device_list);
276         } else {
277                 *device_list = _device_list;
278         }
279
280         return ret;
281 }
282
283 EXPORT_API
284 int mm_sound_free_device_list(MMSoundDeviceList_t device_list)
285 {
286         return __free_device_list((mm_sound_device_list_t*) device_list);
287 }
288
289 EXPORT_API
290 int mm_sound_get_next_device (MMSoundDeviceList_t device_list, MMSoundDevice_t *device)
291 {
292         int ret = MM_ERROR_NONE;
293         mm_sound_device_list_t *device_list_t = NULL;
294         GList *node = NULL;
295         if (!device_list || !device) {
296                 return MM_ERROR_INVALID_ARGUMENT;
297         }
298         device_list_t = (mm_sound_device_list_t*) device_list;
299         if (device_list_t->is_new_device_list) {
300                 node = g_list_first(device_list_t->list);
301         } else {
302                 node = g_list_next(device_list_t->list);
303         }
304         if (!node) {
305                 ret = MM_ERROR_SOUND_NO_DATA;
306         } else {
307                 if (device_list_t->is_new_device_list) {
308                         device_list_t->is_new_device_list = false;
309                 } else {
310                         device_list_t->list = node;
311                 }
312                 *device = (mm_sound_device_t*)node->data;
313                 debug_log("next device[0x%x]\n", *device);
314         }
315         return ret;
316 }
317
318 EXPORT_API
319 int mm_sound_get_prev_device (MMSoundDeviceList_t device_list, MMSoundDevice_t *device)
320 {
321         int ret = MM_ERROR_NONE;
322         mm_sound_device_list_t *device_list_t = NULL;
323         GList *node = NULL;
324         if (!device_list || !device) {
325                 return MM_ERROR_INVALID_ARGUMENT;
326         }
327         device_list_t = (mm_sound_device_list_t*) device_list;
328         node = g_list_previous(device_list_t->list);
329         if (!node) {
330                 ret = MM_ERROR_SOUND_NO_DATA;
331                 debug_error("Could not get previous device, ret = %x\n", ret);
332         } else {
333                 device_list_t->list = node;
334                 *device = (mm_sound_device_t*)node->data;
335                 debug_log("previous device[0x%x]\n", *device);
336         }
337         return ret;
338 }
339
340 EXPORT_API
341 int mm_sound_get_device_type(MMSoundDevice_t device_h, mm_sound_device_type_e *type)
342 {
343         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
344         if(!device || !type) {
345                 debug_error("invalid argument\n");
346                 return MM_ERROR_INVALID_ARGUMENT;
347         }
348         __convert_device_type_to_enum(device->type, type);
349         debug_log("device_handle:0x%x, type:%d\n", device, *type);
350
351         return MM_ERROR_NONE;
352 }
353
354 EXPORT_API
355 int mm_sound_get_device_io_direction(MMSoundDevice_t device_h, mm_sound_device_io_direction_e *io_direction)
356 {
357         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
358         if(!device) {
359                 debug_error("invalid handle\n");
360                 return MM_ERROR_INVALID_ARGUMENT;
361         }
362         *io_direction = device->io_direction;
363         debug_log("device_handle:0x%x, io_direction:%d (1:IN,2:OUT,3:INOUT)\n", device, *io_direction);
364
365         return MM_ERROR_NONE;
366 }
367
368 EXPORT_API
369 int mm_sound_get_device_id(MMSoundDevice_t device_h, int *id)
370 {
371         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
372         if(!device) {
373                 debug_error("invalid handle\n");
374                 return MM_ERROR_INVALID_ARGUMENT;
375         }
376         *id = device->id;
377         debug_log("device_handle:0x%x, id:%d\n", device, *id);
378
379         return MM_ERROR_NONE;
380 }
381
382 EXPORT_API
383 int mm_sound_get_device_state(MMSoundDevice_t device_h, mm_sound_device_state_e *state)
384 {
385         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
386         if(!device) {
387                 debug_error("invalid handle\n");
388                 return MM_ERROR_INVALID_ARGUMENT;
389         }
390         *state = device->state;
391         debug_log("device_handle:0x%x, state:%d (0:INACTIVATED,1:ACTIVATED)\n", device, *state);
392
393         return MM_ERROR_NONE;
394 }
395
396 EXPORT_API
397 int mm_sound_get_device_name(MMSoundDevice_t device_h, char **name)
398 {
399         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
400         if(!device) {
401                 debug_error("invalid handle\n");
402                 return MM_ERROR_INVALID_ARGUMENT;
403         }
404         *name = device->name;
405         debug_log("device_handle:0x%x, name:%s\n", device, *name);
406
407         return MM_ERROR_NONE;
408 }
409
410 EXPORT_API
411 int mm_sound_is_stream_on_device(int stream_id, MMSoundDevice_t device_h, bool *is_on)
412 {
413         int ret = MM_ERROR_NONE;
414         int i;
415         mm_sound_device_t *device = (mm_sound_device_t*)device_h;
416         bool _is_on = false;
417
418         if(!device || !is_on) {
419                 debug_error("invalid argument\n");
420                 return MM_ERROR_INVALID_ARGUMENT;
421         }
422
423         if (device->stream_num >= 0) {
424                 debug_log("device_handle has stream id");
425                 for (i = 0; i < device->stream_num; i++) {
426                         if (device->stream_id[i] == stream_id) {
427                                 _is_on = true;
428                                 break;
429                         }
430                 }
431         } else {
432                 debug_log("device_handle dosn't have stream id");
433                 /* No information about stream in client-side, should ask to server-side */
434                 if ((ret = mm_sound_client_is_stream_on_device(stream_id, device->id, &_is_on)) < 0) {
435                         debug_error("Failed to query is stream on");
436                         return MM_ERROR_SOUND_INTERNAL;
437                 }
438         }
439
440         debug_log("device(%d) %s stream(%d)\n", device->id, _is_on ? "has" : "doesn't have", stream_id);
441         *is_on = _is_on;
442
443         return ret;
444 }