static void sink_free(struct audio_device *dev) { struct sink *sink = dev->sink; if (sink->cb_id) avdtp_stream_remove_cb(sink->session, sink->stream, sink->cb_id); if (sink->dc_id) device_remove_disconnect_watch(dev->btd_dev, sink->dc_id); if (sink->session) avdtp_unref(sink->session); if (sink->connect) pending_request_free(dev, sink->connect); if (sink->disconnect) pending_request_free(dev, sink->disconnect); if (sink->retry_id) g_source_remove(sink->retry_id); g_free(sink); dev->sink = NULL; }
static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct sink *sink = user_data; struct pending_request *pending; pending = sink->connect; if (stream) { DBusMessage *reply; sink->connect = NULL; reply = dbus_message_new_method_return(pending->msg); dbus_connection_send(pending->conn, reply, NULL); dbus_message_unref(reply); pending_request_free(pending); debug("Stream successfully created"); } else { avdtp_unref(sink->session); sink->session = NULL; if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO && avdtp_error_posix_errno(err) != EHOSTDOWN) { debug("connect:connect XCASE detected"); g_timeout_add(STREAM_SETUP_RETRY_TIMER, stream_setup_retry, sink); } else { sink->connect = NULL; error_failed(pending->conn, pending->msg, "Stream setup failed"); pending_request_free(pending); debug("Stream setup failed : %s", avdtp_strerror(err)); } } }
gboolean sink_shutdown(struct sink *sink) { if (!sink->session) return FALSE; avdtp_set_device_disconnect(sink->session, TRUE); /* cancel pending connect */ if (sink->connect) { struct pending_request *pending = sink->connect; if (pending->msg) error_failed(pending->conn, pending->msg, "Stream setup failed"); pending_request_free(sink->dev, pending); sink->connect = NULL; avdtp_unref(sink->session); sink->session = NULL; return TRUE; } /* disconnect already ongoing */ if (sink->disconnect) return TRUE; if (!sink->stream) return FALSE; if (avdtp_close(sink->session, sink->stream, FALSE) < 0) return FALSE; return TRUE; }
static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data) { struct audio_device *dev = user_data; struct source *source = dev->source; if (err) return; switch (new_state) { case AVDTP_STATE_IDLE: if (source->disconnect) { DBusMessage *reply; struct pending_request *p; p = source->disconnect; source->disconnect = NULL; reply = dbus_message_new_method_return(p->msg); g_dbus_send_message(p->conn, reply); pending_request_free(dev, p); } if (source->dc_id) { device_remove_disconnect_watch(dev->btd_dev, source->dc_id); source->dc_id = 0; } if (source->session) { avdtp_unref(source->session); source->session = NULL; } source->stream = NULL; source->cb_id = 0; break; case AVDTP_STATE_OPEN: if (old_state == AVDTP_STATE_CONFIGURED && source->state == SOURCE_STATE_CONNECTING) { source->dc_id = device_add_disconnect_watch(dev->btd_dev, disconnect_cb, dev, NULL); } source_set_state(dev, SOURCE_STATE_CONNECTED); break; case AVDTP_STATE_STREAMING: source_set_state(dev, SOURCE_STATE_PLAYING); break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: break; } source->stream_state = new_state; }
static void async_cb(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct pending_request *p = user_data; struct obc_session *session = p->session; GError *gerr = NULL; uint8_t code; p->req_id = 0; if (err != NULL) { if (p->func) p->func(p->session, NULL, err, p->data); goto done; } code = g_obex_packet_get_operation(rsp, NULL); if (code != G_OBEX_RSP_SUCCESS) g_set_error(&gerr, OBEX_IO_ERROR, code, "%s", g_obex_strerror(code)); if (p->func) p->func(p->session, NULL, gerr, p->data); if (gerr != NULL) g_clear_error(&gerr); done: pending_request_free(p); session->p = NULL; session_process_queue(session); }
static void session_terminate_transfer(struct obc_session *session, struct obc_transfer *transfer, GError *gerr) { struct pending_request *p = session->p; if (p == NULL || p->transfer != transfer) { GList *match; match = g_list_find_custom(session->queue->head, transfer, pending_transfer_cmptransfer); if (match == NULL) return; p = match->data; g_queue_delete_link(session->queue, match); } else session->p = NULL; obc_session_ref(session); if (p->func) p->func(session, p->transfer, gerr, p->data); pending_request_free(p); if (session->p == NULL) session_process_queue(session); obc_session_unref(session); }
guint obc_session_mkdir(struct obc_session *session, const char *folder, session_callback_t func, void *user_data, GError **err) { struct pending_request *p; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } if (session->p != NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy"); return 0; } p = pending_request_new(session, NULL, func, user_data); p->req_id = g_obex_mkdir(session->obex, folder, async_cb, p, err); if (*err != NULL) { pending_request_free(p); return 0; } session->p = p; return p->id; }
static void session_process_queue(struct obc_session *session) { struct pending_request *p; if (session->p != NULL) return; if (session->queue == NULL || g_queue_is_empty(session->queue)) return; obc_session_ref(session); while ((p = g_queue_pop_head(session->queue))) { GError *gerr = NULL; if (p->process(p, &gerr) == 0) break; if (p->func) p->func(session, p->transfer, gerr, p->data); g_clear_error(&gerr); pending_request_free(p); } obc_session_unref(session); }
static gboolean stream_setup_retry(gpointer user_data) { struct sink *sink = user_data; struct pending_request *pending = sink->connect; sink->retry_id = 0; if (sink->stream_state >= AVDTP_STATE_OPEN) { DBG("Stream successfully created, after XCASE connect:connect"); if (pending->msg) { DBusMessage *reply; reply = dbus_message_new_method_return(pending->msg); g_dbus_send_message(pending->conn, reply); } } else { DBG("Stream setup failed, after XCASE connect:connect"); if (pending->msg) error_failed(pending->conn, pending->msg, "Stream setup failed"); } sink->connect = NULL; pending_request_free(sink->dev, pending); return FALSE; }
static void select_complete(struct avdtp *session, struct a2dp_sep *sep, GSList *caps, void *user_data) { struct sink *sink = user_data; struct pending_request *pending; int id; pending = sink->connect; pending->id = 0; id = a2dp_config(session, sep, stream_setup_complete, caps, sink); if (id == 0) goto failed; pending->id = id; return; failed: if (pending->msg) error_failed(pending->conn, pending->msg, "Stream setup failed"); pending_request_free(sink->dev, pending); sink->connect = NULL; avdtp_unref(sink->session); sink->session = NULL; }
static void session_process_queue(struct obc_session *session) { struct pending_request *p; if (session->p != NULL) return; if (session->queue == NULL || g_queue_is_empty(session->queue)) return; obc_session_ref(session); while ((p = g_queue_pop_head(session->queue))) { GError *gerr = NULL; DBG("Transfer(%p) started", p->transfer); if (obc_transfer_start(p->transfer, session->obex, &gerr)) { session->p = p; break; } if (p->func) p->func(session, p->transfer, gerr, p->data); g_clear_error(&gerr); pending_request_free(p); } obc_session_unref(session); }
void obc_session_shutdown(struct obc_session *session) { struct pending_request *p; GError *err; DBG("%p", session); obc_session_ref(session); /* Unregister any pending transfer */ err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session closed by user"); if (session->p != NULL && session->p->id != 0) { p = session->p; session->p = NULL; if (p->func) p->func(session, p->transfer, err, p->data); pending_request_free(p); } while ((p = g_queue_pop_head(session->queue))) { if (p->func) p->func(session, p->transfer, err, p->data); pending_request_free(p); } g_error_free(err); /* Unregister interfaces */ if (session->path) session_unregistered(session); /* Disconnect transport */ if (session->id > 0 && session->transport != NULL) { session->transport->disconnect(session->id); session->id = 0; } obc_session_unref(session); }
static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct sink *sink = user_data; struct pending_request *pending; pending = sink->connect; pending->id = 0; if (stream) { DBG("Stream successfully created"); if (pending->msg) { DBusMessage *reply; reply = dbus_message_new_method_return(pending->msg); g_dbus_send_message(pending->conn, reply); } sink->connect = NULL; pending_request_free(sink->dev, pending); return; } avdtp_unref(sink->session); sink->session = NULL; if (avdtp_error_category(err) == AVDTP_ERRNO && avdtp_error_posix_errno(err) != EHOSTDOWN) { DBG("connect:connect XCASE detected"); sink->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, stream_setup_retry, sink); } else { if (pending->msg) error_failed(pending->conn, pending->msg, "Stream setup failed"); sink->connect = NULL; pending_request_free(sink->dev, pending); DBG("Stream setup failed : %s", avdtp_strerror(err)); } }
void sink_free(struct audio_device *dev) { struct sink *sink = dev->sink; if (sink->cb_id) avdtp_stream_remove_cb(sink->session, sink->stream, sink->cb_id); if (sink->session) avdtp_unref(sink->session); if (sink->connect) pending_request_free(sink->connect); if (sink->disconnect) pending_request_free(sink->disconnect); g_free(sink); dev->sink = NULL; }
static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { struct sink *sink = user_data; struct pending_request *pending; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; GSList *caps = NULL; int id; pending = sink->connect; if (err) { avdtp_unref(sink->session); sink->session = NULL; if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO && avdtp_error_posix_errno(err) != EHOSTDOWN) { debug("connect:connect XCASE detected"); g_timeout_add(STREAM_SETUP_RETRY_TIMER, stream_setup_retry, sink); } else goto failed; return; } debug("Discovery complete"); if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, &lsep, &rsep) < 0) { error("No matching ACP and INT SEPs found"); goto failed; } if (!select_capabilities(session, rsep, &caps)) { error("Unable to select remote SEP capabilities"); goto failed; } id = a2dp_source_config(sink->session, stream_setup_complete, caps, sink); if (id == 0) goto failed; pending->id = id; return; failed: pending_request_free(pending); sink->connect = NULL; avdtp_unref(sink->session); sink->session = NULL; error_failed(pending->conn, pending->msg, "Stream setup failed"); }
static void remove_request (ResolveClosure *closure, GrlTmdbRequest *request) { GList *it; for (it = closure->pending_requests->head; it; it = it->next) { PendingRequest *const pending_request = it->data; if (pending_request->request == request) { g_queue_delete_link (closure->pending_requests, it); pending_request_free (pending_request); break; } } }
static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { struct sink *sink = user_data; struct pending_request *pending; int id; if (!sink->connect) { avdtp_unref(sink->session); sink->session = NULL; return; } pending = sink->connect; if (err) { avdtp_unref(sink->session); sink->session = NULL; if (avdtp_error_category(err) == AVDTP_ERRNO && avdtp_error_posix_errno(err) != EHOSTDOWN) { DBG("connect:connect XCASE detected"); sink->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, stream_setup_retry, sink); } else goto failed; return; } DBG("Discovery complete"); id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL, select_complete, sink); if (id == 0) goto failed; pending->id = id; return; failed: if (pending->msg) error_failed(pending->conn, pending->msg, "Stream setup failed"); pending_request_free(sink->dev, pending); sink->connect = NULL; avdtp_unref(sink->session); sink->session = NULL; }
static void setpath_complete(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *p = user_data; if (p->func) p->func(session, NULL, err, p->data); if (session->p == p) session->p = NULL; pending_request_free(p); session_process_queue(session); }
guint obc_session_setpath(struct obc_session *session, const char *path, session_callback_t func, void *user_data, GError **err) { struct setpath_data *data; struct pending_request *p; const char *first = ""; if (session->obex == NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session disconnected"); return 0; } if (session->p != NULL) { g_set_error(err, OBEX_IO_ERROR, OBEX_IO_BUSY, "Session busy"); return 0; } data = g_new0(struct setpath_data, 1); data->func = func; data->user_data = user_data; data->remaining = g_strsplit(path, "/", 3); p = pending_request_new(session, NULL, setpath_complete, data); /* Relative path */ if (path[0] != '/') { first = data->remaining[data->index]; data->index++; } p->req_id = g_obex_setpath(session->obex, first, setpath_cb, p, err); if (*err != NULL) goto fail; session->p = p; return p->id; fail: g_strfreev(data->remaining); g_free(data); pending_request_free(p); return 0; }
static int session_process_delete(struct pending_request *p, GError **err) { struct file_data *req = p->data; p->req_id = g_obex_delete(p->session->obex, req->srcname, async_cb, p, err); if (*err != NULL) goto fail; p->session->p = p; return 0; fail: pending_request_free(p); return (*err)->code; }
static void disconnect_cb(GObex *obex, GError *err, GObexPacket *rsp, gpointer user_data) { struct pending_request *p = user_data; struct obc_session *session = p != NULL ? p->session : NULL; DBG("Finalizing disconnection. "); pending_request_free(p); if (session != NULL && session->id > 0 && session->transport != NULL) { session->transport->disconnect(session->id); session->id = 0; } obc_session_unref(session); }
static void setpath_complete(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *p = user_data; struct setpath_data *data = p->data; if (data->func) data->func(session, NULL, err, data->user_data); g_strfreev(data->remaining); g_free(data); if (session->p == p) session->p = NULL; pending_request_free(p); session_process_queue(session); }
void obc_session_cancel(struct obc_session *session, guint id, gboolean remove) { struct pending_request *p = session->p; if (p == NULL || p->id != id) return; if (p->req_id == 0) return; g_obex_cancel_req(session->obex, p->req_id, remove); if (!remove) return; pending_request_free(p); session->p = NULL; session_process_queue(session); }
static gboolean stream_setup_retry(gpointer user_data) { struct sink *sink = user_data; struct pending_request *pending = sink->connect; if (sink->state >= AVDTP_STATE_OPEN) { DBusMessage *reply; debug("Stream successfully created, after XCASE connect:connect"); reply = dbus_message_new_method_return(pending->msg); dbus_connection_send(pending->conn, reply, NULL); dbus_message_unref(reply); } else { debug("Stream setup failed, after XCASE connect:connect"); error_failed(pending->conn, pending->msg, "Stream setup failed"); } sink->connect = NULL; pending_request_free(pending); return FALSE; }
static void session_free(struct obc_session *session) { DBG("%p", session); if (session->process_id != 0) g_source_remove(session->process_id); if (session->queue) { g_queue_foreach(session->queue, (GFunc) pending_request_free, NULL); g_queue_free(session->queue); } if (session->watch) g_dbus_remove_watch(session->conn, session->watch); if (session->obex) { g_obex_set_disconnect_function(session->obex, NULL, NULL); g_obex_unref(session->obex); } if (session->id > 0 && session->transport != NULL) session->transport->disconnect(session->id); if (session->path) session_unregistered(session); if (session->conn) dbus_connection_unref(session->conn); if (session->p) pending_request_free(session->p); g_free(session->path); g_free(session->owner); g_free(session->source); g_free(session->destination); g_free(session->folder); g_free(session); }
static int session_process_setpath(struct pending_request *p, GError **err) { struct setpath_data *req = p->data; const char *first = ""; /* Relative path */ if (req->remaining[0][0] != '/') first = req->remaining[req->index]; req->index++; p->req_id = g_obex_setpath(p->session->obex, first, setpath_cb, p, err); if (*err != NULL) goto fail; p->session->p = p; return 0; fail: pending_request_free(p); return (*err)->code; }
void obc_session_shutdown(struct obc_session *session) { struct pending_request *p; GError *err; DBG("%p", session); if (session->disconnecting == TRUE) { DBG("%p already disconnecting", session); return; } session->disconnecting = TRUE; obc_session_ref(session); /* Unregister any pending transfer */ err = g_error_new(OBEX_IO_ERROR, OBEX_IO_DISCONNECTED, "Session closed by user"); if (session->p != NULL && session->p->id != 0) { p = session->p; session->p = NULL; if (p->func) p->func(session, p->transfer, err, p->data); pending_request_free(p); } while ((p = g_queue_pop_head(session->queue))) { if (p->func) p->func(session, p->transfer, err, p->data); pending_request_free(p); } g_error_free(err); /* Unregister interfaces */ if (session->path) session_unregistered(session); DBG("Checking the need for disconnect request"); /* Send a disconnect request and wait for reply */ if (session->id > 0 && session->transport != NULL && session->obex != NULL) { DBG("Generating disconnect request. "); err = NULL; p = pending_request_new(session, NULL, NULL, NULL); p->req_id = g_obex_disconnect(session->obex, disconnect_cb, p, &err); if (err != NULL) { DBG("Generating disconnect request failed. "); disconnect_cb(session->obex, NULL, NULL, p); } else { /* Finalize when reply arrives */ DBG("Generating disconnect request succeeded. "); } } else { DBG("Unreferring without disconnect request."); obc_session_unref(session); } }
static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data) { struct audio_device *dev = user_data; struct sink *sink = dev->sink; gboolean value; if (err) return; switch (new_state) { case AVDTP_STATE_IDLE: if (sink->disconnect) { DBusMessage *reply; struct pending_request *p; p = sink->disconnect; sink->disconnect = NULL; reply = dbus_message_new_method_return(p->msg); g_dbus_send_message(p->conn, reply); pending_request_free(dev, p); } if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; } sink->stream = NULL; sink->cb_id = 0; break; case AVDTP_STATE_OPEN: if (old_state == AVDTP_STATE_CONFIGURED && sink->state == SINK_STATE_CONNECTING) { value = TRUE; g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_INVALID); emit_property_changed(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_BOOLEAN, &value); } else if (old_state == AVDTP_STATE_STREAMING) { value = FALSE; g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Stopped", DBUS_TYPE_INVALID); emit_property_changed(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Playing", DBUS_TYPE_BOOLEAN, &value); } sink_set_state(dev, SINK_STATE_CONNECTED); break; case AVDTP_STATE_STREAMING: value = TRUE; g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Playing", DBUS_TYPE_INVALID); emit_property_changed(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Playing", DBUS_TYPE_BOOLEAN, &value); sink_set_state(dev, SINK_STATE_PLAYING); break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: break; } sink->stream_state = new_state; }
static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, struct avdtp_error *err, void *user_data) { struct audio_device *dev = user_data; struct sink *sink = dev->sink; if (err) return; switch (new_state) { case AVDTP_STATE_IDLE: g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); if (sink->disconnect) { DBusMessage *reply; struct pending_request *p; p = sink->disconnect; sink->disconnect = NULL; reply = dbus_message_new_method_return(p->msg); dbus_connection_send(p->conn, reply, NULL); dbus_message_unref(reply); pending_request_free(p); } if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; } sink->stream = NULL; sink->cb_id = 0; break; case AVDTP_STATE_OPEN: if (old_state == AVDTP_STATE_CONFIGURED) g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_INVALID); else if (old_state == AVDTP_STATE_STREAMING) g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Stopped", DBUS_TYPE_INVALID); break; case AVDTP_STATE_STREAMING: g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Playing", DBUS_TYPE_INVALID); break; case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: break; } sink->state = new_state; }