3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2006-2010 Nokia Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
7 * Copyright (C) 2011 Texas Instruments, Inc.
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.
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.
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
36 #include <sys/types.h>
40 #include <bluetooth/bluetooth.h>
41 #include <bluetooth/sdp.h>
42 #include <bluetooth/sdp_lib.h>
45 #include <dbus/dbus.h>
55 #include "glib-helper.h"
56 #include "dbus-common.h"
58 static unsigned int avctp_id = 0;
61 struct audio_device *dev;
62 struct avctp *session;
67 static void state_changed(struct audio_device *dev, avctp_state_t old_state,
68 avctp_state_t new_state, void *user_data)
70 struct control *control = dev->control;
74 case AVCTP_STATE_DISCONNECTED:
75 control->session = NULL;
77 if (old_state != AVCTP_STATE_CONNECTED)
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);
89 case AVCTP_STATE_CONNECTING:
93 control->session = avctp_get(&dev->src, &dev->dst);
96 case AVCTP_STATE_CONNECTED:
98 g_dbus_emit_signal(dev->conn, dev->path,
99 AUDIO_CONTROL_INTERFACE, "Connected",
101 emit_property_changed(dev->conn, dev->path,
102 AUDIO_CONTROL_INTERFACE, "Connected",
103 DBUS_TYPE_BOOLEAN, &value);
110 static DBusMessage *control_is_connected(DBusConnection *conn,
114 struct audio_device *device = data;
115 struct control *control = device->control;
117 dbus_bool_t connected;
119 reply = dbus_message_new_method_return(msg);
123 connected = (control->session != NULL);
125 dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
131 static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
134 struct audio_device *device = data;
135 struct control *control = device->control;
138 if (!control->session)
139 return btd_error_not_connected(msg);
141 if (!control->target)
142 return btd_error_not_supported(msg);
144 err = avctp_send_passthrough(control->session, VOL_UP_OP);
146 return btd_error_failed(msg, strerror(-err));
148 return dbus_message_new_method_return(msg);
151 static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
154 struct audio_device *device = data;
155 struct control *control = device->control;
158 if (!control->session)
159 return btd_error_not_connected(msg);
161 if (!control->target)
162 return btd_error_not_supported(msg);
164 err = avctp_send_passthrough(control->session, VOL_DOWN_OP);
166 return btd_error_failed(msg, strerror(-err));
168 return dbus_message_new_method_return(msg);
171 static DBusMessage *control_get_properties(DBusConnection *conn,
172 DBusMessage *msg, void *data)
174 struct audio_device *device = data;
176 DBusMessageIter iter;
177 DBusMessageIter dict;
180 reply = dbus_message_new_method_return(msg);
184 dbus_message_iter_init_append(reply, &iter);
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);
192 value = (device->control->session != NULL);
193 dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
195 dbus_message_iter_close_container(&iter, &dict);
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) },
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" })) },
220 static void path_unregister(void *data)
222 struct audio_device *dev = data;
223 struct control *control = dev->control;
225 DBG("Unregistered interface %s on path %s",
226 AUDIO_CONTROL_INTERFACE, dev->path);
228 if (control->session)
229 avctp_disconnect(control->session);
235 void control_unregister(struct audio_device *dev)
237 g_dbus_unregister_interface(dev->conn, dev->path,
238 AUDIO_CONTROL_INTERFACE);
241 void control_update(struct control *control, uint16_t uuid16)
243 if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
244 control->target = TRUE;
247 struct control *control_init(struct audio_device *dev, uint16_t uuid16)
249 struct control *control;
251 if (!g_dbus_register_interface(dev->conn, dev->path,
252 AUDIO_CONTROL_INTERFACE,
253 control_methods, control_signals, NULL,
254 dev, path_unregister))
257 DBG("Registered interface %s on path %s",
258 AUDIO_CONTROL_INTERFACE, dev->path);
260 control = g_new0(struct control, 1);
263 control_update(control, uuid16);
266 avctp_id = avctp_add_state_cb(state_changed, NULL);
271 gboolean control_is_active(struct audio_device *dev)
273 struct control *control = dev->control;
275 if (control && control->session)