gboolean avrcp_connect(struct audio_device *dev) { struct control *control = dev->control; struct avctp *session; int err; if (control->session) return TRUE; session = avctp_get(&dev->src, &dev->dst); if (!session) { error("Unable to create new AVCTP session"); return FALSE; } device_remove_control_timer(dev); session->dev = dev; session->state = AVCTP_STATE_CONNECTING; err = bt_l2cap_connect(&dev->src, &dev->dst, AVCTP_PSM, 0, avctp_connect_cb, session); if (err < 0) { avctp_unref(session); error("Connect failed. %s(%d)", strerror(-err), -err); return FALSE; } control->session = session; return TRUE; }
static void state_changed(struct btd_device *dev, avctp_state_t old_state, avctp_state_t new_state, void *user_data) { struct control *control = user_data; DBusConnection *conn = btd_get_dbus_connection(); const char *path = device_get_path(dev); switch (new_state) { case AVCTP_STATE_DISCONNECTED: control->session = NULL; g_dbus_emit_property_changed(conn, path, AUDIO_CONTROL_INTERFACE, "Connected"); break; case AVCTP_STATE_CONNECTING: if (control->session) break; control->session = avctp_get(dev); break; case AVCTP_STATE_CONNECTED: g_dbus_emit_property_changed(conn, path, AUDIO_CONTROL_INTERFACE, "Connected"); break; default: return; } }
static void state_changed(struct audio_device *dev, avctp_state_t old_state, avctp_state_t new_state, void *user_data) { struct control *control = dev->control; gboolean value; switch (new_state) { case AVCTP_STATE_DISCONNECTED: control->session = NULL; if (old_state != AVCTP_STATE_CONNECTED) break; value = FALSE; g_dbus_emit_signal(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); emit_property_changed(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_BOOLEAN, &value); break; case AVCTP_STATE_CONNECTING: if (control->session) break; control->session = avctp_get(&dev->src, &dev->dst); break; case AVCTP_STATE_CONNECTED: value = TRUE; g_dbus_emit_signal(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_INVALID); emit_property_changed(dev->conn, dev->path, AUDIO_CONTROL_INTERFACE, "Connected", DBUS_TYPE_BOOLEAN, &value); break; default: return; } }
static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer data) { socklen_t size; struct l2cap_options l2o; struct avctp *session; GIOCondition flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; struct audio_device *dev; char address[18]; if (err < 0) { error("AVCTP server socket: %s (%d)", strerror(-err), -err); return; } session = avctp_get(src, dst); if (!session) { error("Unable to create new AVCTP session"); goto drop; } if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); goto drop; } dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, FALSE); if (!dev) { error("Unable to get audio device object for %s", address); goto drop; } if (!dev->control) dev->control = control_init(dev); device_remove_control_timer(dev); session->state = AVCTP_STATE_CONNECTING; session->sock = g_io_channel_unix_get_fd(chan); memset(&l2o, 0, sizeof(l2o)); size = sizeof(l2o); if (getsockopt(session->sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { err = errno; error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(err), err); avctp_unref(session); goto drop; } session->mtu = l2o.imtu; if (session->io) g_source_remove(session->io); session->io = g_io_add_watch(chan, flags, (GIOFunc) session_cb, session); g_io_channel_unref(chan); if (avdtp_is_connected(src, dst)) goto proceed; if (service_req_auth(src, dst, AVRCP_TARGET_UUID, auth_cb, session) < 0) goto drop; return; proceed: avctp_connect_session(session); return; drop: close(session->sock); }