Initialize Tizen 2.3
[framework/connectivity/bluez.git] / wearable / audio / control.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2006-2010  Nokia Corporation
6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7  *  Copyright (C) 2011  Texas Instruments, Inc.
8  *
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <assert.h>
35 #include <signal.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #include <bluetooth/bluetooth.h>
41 #include <bluetooth/sdp.h>
42 #include <bluetooth/sdp_lib.h>
43
44 #include <glib.h>
45 #include <dbus/dbus.h>
46 #include <gdbus.h>
47
48 #include "log.h"
49 #include "error.h"
50 #include "device.h"
51 #include "manager.h"
52 #include "avctp.h"
53 #include "control.h"
54 #include "sdpd.h"
55 #include "glib-helper.h"
56 #include "dbus-common.h"
57
58 static unsigned int avctp_id = 0;
59
60 struct control {
61         struct audio_device *dev;
62         struct avctp *session;
63
64         gboolean target;
65 };
66
67 static void state_changed(struct audio_device *dev, avctp_state_t old_state,
68                                 avctp_state_t new_state, void *user_data)
69 {
70         struct control *control = dev->control;
71         gboolean value;
72
73         switch (new_state) {
74         case AVCTP_STATE_DISCONNECTED:
75                 control->session = NULL;
76
77                 if (old_state != AVCTP_STATE_CONNECTED)
78                         break;
79
80                 value = FALSE;
81                 g_dbus_emit_signal(dev->conn, dev->path,
82                                         AUDIO_CONTROL_INTERFACE,
83                                         "Disconnected", DBUS_TYPE_INVALID);
84                 emit_property_changed(dev->conn, dev->path,
85                                         AUDIO_CONTROL_INTERFACE, "Connected",
86                                         DBUS_TYPE_BOOLEAN, &value);
87
88                 break;
89         case AVCTP_STATE_CONNECTING:
90                 if (control->session)
91                         break;
92
93                 control->session = avctp_get(&dev->src, &dev->dst);
94
95                 break;
96         case AVCTP_STATE_CONNECTED:
97                 value = TRUE;
98                 g_dbus_emit_signal(dev->conn, dev->path,
99                                 AUDIO_CONTROL_INTERFACE, "Connected",
100                                 DBUS_TYPE_INVALID);
101                 emit_property_changed(dev->conn, dev->path,
102                                 AUDIO_CONTROL_INTERFACE, "Connected",
103                                 DBUS_TYPE_BOOLEAN, &value);
104                 break;
105         default:
106                 return;
107         }
108 }
109
110 static DBusMessage *control_is_connected(DBusConnection *conn,
111                                                 DBusMessage *msg,
112                                                 void *data)
113 {
114         struct audio_device *device = data;
115         struct control *control = device->control;
116         DBusMessage *reply;
117         dbus_bool_t connected;
118
119         reply = dbus_message_new_method_return(msg);
120         if (!reply)
121                 return NULL;
122
123         connected = (control->session != NULL);
124
125         dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
126                                         DBUS_TYPE_INVALID);
127
128         return reply;
129 }
130
131 static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
132                                                                 void *data)
133 {
134         struct audio_device *device = data;
135         struct control *control = device->control;
136         int err;
137
138         if (!control->session)
139                 return btd_error_not_connected(msg);
140
141         if (!control->target)
142                 return btd_error_not_supported(msg);
143
144         err = avctp_send_passthrough(control->session, VOL_UP_OP);
145         if (err < 0)
146                 return btd_error_failed(msg, strerror(-err));
147
148         return dbus_message_new_method_return(msg);
149 }
150
151 static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
152                                                                 void *data)
153 {
154         struct audio_device *device = data;
155         struct control *control = device->control;
156         int err;
157
158         if (!control->session)
159                 return btd_error_not_connected(msg);
160
161         if (!control->target)
162                 return btd_error_not_supported(msg);
163
164         err = avctp_send_passthrough(control->session, VOL_DOWN_OP);
165         if (err < 0)
166                 return btd_error_failed(msg, strerror(-err));
167
168         return dbus_message_new_method_return(msg);
169 }
170
171 static DBusMessage *control_get_properties(DBusConnection *conn,
172                                         DBusMessage *msg, void *data)
173 {
174         struct audio_device *device = data;
175         DBusMessage *reply;
176         DBusMessageIter iter;
177         DBusMessageIter dict;
178         gboolean value;
179
180         reply = dbus_message_new_method_return(msg);
181         if (!reply)
182                 return NULL;
183
184         dbus_message_iter_init_append(reply, &iter);
185
186         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
187                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
188                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
189                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
190
191         /* Connected */
192         value = (device->control->session != NULL);
193         dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
194
195         dbus_message_iter_close_container(&iter, &dict);
196
197         return reply;
198 }
199
200 static const GDBusMethodTable control_methods[] = {
201         { GDBUS_ASYNC_METHOD("IsConnected",
202                                 NULL, GDBUS_ARGS({ "connected", "b" }),
203                                 control_is_connected) },
204         { GDBUS_METHOD("GetProperties",
205                                 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
206                                 control_get_properties) },
207         { GDBUS_METHOD("VolumeUp", NULL, NULL, volume_up) },
208         { GDBUS_METHOD("VolumeDown", NULL, NULL, volume_down) },
209         { }
210 };
211
212 static const GDBusSignalTable control_signals[] = {
213         { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
214         { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
215         { GDBUS_SIGNAL("PropertyChanged",
216                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
217         { }
218 };
219
220 static void path_unregister(void *data)
221 {
222         struct audio_device *dev = data;
223         struct control *control = dev->control;
224
225         DBG("Unregistered interface %s on path %s",
226                 AUDIO_CONTROL_INTERFACE, dev->path);
227
228         if (control->session)
229                 avctp_disconnect(control->session);
230
231         g_free(control);
232         dev->control = NULL;
233 }
234
235 void control_unregister(struct audio_device *dev)
236 {
237         g_dbus_unregister_interface(dev->conn, dev->path,
238                                                 AUDIO_CONTROL_INTERFACE);
239 }
240
241 void control_update(struct control *control, uint16_t uuid16)
242 {
243         if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
244                 control->target = TRUE;
245 }
246
247 struct control *control_init(struct audio_device *dev, uint16_t uuid16)
248 {
249         struct control *control;
250
251         if (!g_dbus_register_interface(dev->conn, dev->path,
252                                         AUDIO_CONTROL_INTERFACE,
253                                         control_methods, control_signals, NULL,
254                                         dev, path_unregister))
255                 return NULL;
256
257         DBG("Registered interface %s on path %s",
258                 AUDIO_CONTROL_INTERFACE, dev->path);
259
260         control = g_new0(struct control, 1);
261         control->dev = dev;
262
263         control_update(control, uuid16);
264
265         if (!avctp_id)
266                 avctp_id = avctp_add_state_cb(state_changed, NULL);
267
268         return control;
269 }
270
271 gboolean control_is_active(struct audio_device *dev)
272 {
273         struct control *control = dev->control;
274
275         if (control && control->session)
276                 return TRUE;
277
278         return FALSE;
279 }