Git init
[framework/connectivity/bluez.git] / 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 GDBusMethodTable control_methods[] = {
201         { "IsConnected",        "",     "b",    control_is_connected,
202                                                 G_DBUS_METHOD_FLAG_DEPRECATED },
203         { "GetProperties",      "",     "a{sv}",control_get_properties },
204         { "VolumeUp",           "",     "",     volume_up },
205         { "VolumeDown",         "",     "",     volume_down },
206         { NULL, NULL, NULL, NULL }
207 };
208
209 static GDBusSignalTable control_signals[] = {
210         { "Connected",                  "",     G_DBUS_SIGNAL_FLAG_DEPRECATED},
211         { "Disconnected",               "",     G_DBUS_SIGNAL_FLAG_DEPRECATED},
212         { "PropertyChanged",            "sv"    },
213         { NULL, NULL }
214 };
215
216 static void path_unregister(void *data)
217 {
218         struct audio_device *dev = data;
219         struct control *control = dev->control;
220
221         DBG("Unregistered interface %s on path %s",
222                 AUDIO_CONTROL_INTERFACE, dev->path);
223
224         if (control->session)
225                 avctp_disconnect(control->session);
226
227         g_free(control);
228         dev->control = NULL;
229 }
230
231 void control_unregister(struct audio_device *dev)
232 {
233         g_dbus_unregister_interface(dev->conn, dev->path,
234                                                 AUDIO_CONTROL_INTERFACE);
235 }
236
237 void control_update(struct control *control, uint16_t uuid16)
238 {
239         if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
240                 control->target = TRUE;
241 }
242
243 struct control *control_init(struct audio_device *dev, uint16_t uuid16)
244 {
245         struct control *control;
246
247         if (!g_dbus_register_interface(dev->conn, dev->path,
248                                         AUDIO_CONTROL_INTERFACE,
249                                         control_methods, control_signals, NULL,
250                                         dev, path_unregister))
251                 return NULL;
252
253         DBG("Registered interface %s on path %s",
254                 AUDIO_CONTROL_INTERFACE, dev->path);
255
256         control = g_new0(struct control, 1);
257         control->dev = dev;
258
259         control_update(control, uuid16);
260
261         if (!avctp_id)
262                 avctp_id = avctp_add_state_cb(state_changed, NULL);
263
264         return control;
265 }
266
267 gboolean control_is_active(struct audio_device *dev)
268 {
269         struct control *control = dev->control;
270
271         if (control && control->session)
272                 return TRUE;
273
274         return FALSE;
275 }