📄 avdtp.c
字号:
if (omtu) *omtu = stream->omtu; if (imtu) *imtu = stream->imtu; if (caps) *caps = stream->caps; return TRUE;}static int process_queue(struct avdtp *session){ GSList **queue, *l; struct pending_req *req; if (session->req) return 0; if (session->prio_queue) queue = &session->prio_queue; else queue = &session->req_queue; if (!*queue) return 0; l = *queue; req = l->data; *queue = g_slist_remove(*queue, req); return send_req(session, FALSE, req);}struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep){ return sep->codec;}struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int length){ struct avdtp_service_capability *cap; if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_MEDIA_CODEC) return NULL; cap = g_malloc(sizeof(struct avdtp_service_capability) + length); cap->category = category; cap->length = length; memcpy(cap->data, data, length); return cap;}int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data){ struct gen_req req; int ret; if (session->discov_cb) return -EBUSY; if (session->seps) { cb(session, session->seps, NULL, user_data); return 0; } memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_DISCOVER); ret = send_request(session, FALSE, NULL, &req, sizeof(req)); if (ret == 0) { session->discov_cb = cb; session->user_data = user_data; } return ret;}int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type, uint8_t codec, struct avdtp_local_sep **lsep, struct avdtp_remote_sep **rsep){ GSList *l; uint8_t int_type; int_type = acp_type == AVDTP_SEP_TYPE_SINK ? AVDTP_SEP_TYPE_SOURCE : AVDTP_SEP_TYPE_SINK; *lsep = find_local_sep(int_type, media_type, codec); if (!*lsep) return -EINVAL; for (l = session->seps; l != NULL; l = g_slist_next(l)) { struct avdtp_remote_sep *sep = l->data; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_data; if (sep->type != acp_type) continue; if (sep->media_type != media_type) continue; if (!sep->codec) continue; cap = sep->codec; codec_data = (void *) cap->data; if (codec_data->media_codec_type != codec) continue; if (!sep->stream) { *rsep = sep; return 0; } } return -EINVAL;}gboolean avdtp_stream_remove_cb(struct avdtp *session, struct avdtp_stream *stream, unsigned int id){ GSList *l; struct stream_callback *cb; for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { struct stream_callback *tmp = l->data; if (tmp->id == id) { cb = tmp; break; } } if (!cb) return FALSE; stream->callbacks = g_slist_remove(stream->callbacks, cb); g_free(cb); return TRUE;}unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data){ struct stream_callback *stream_cb; static unsigned int id = 0; stream_cb = g_new(struct stream_callback, 1); stream_cb->cb = cb; stream_cb->user_data = data; stream_cb->id = ++id; stream->callbacks = g_slist_append(stream->callbacks, stream_cb);; return stream_cb->id;}int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream){ struct seid_req req; if (session->state < AVDTP_SESSION_STATE_CONNECTED) return -EINVAL; memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_GET_CONFIGURATION); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req));}static void copy_capabilities(gpointer data, gpointer user_data){ struct avdtp_service_capability *src_cap = data; struct avdtp_service_capability *dst_cap; GSList **l = user_data; dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, src_cap->length); *l = g_slist_append(*l, dst_cap);}int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, GSList *caps, struct avdtp_stream **stream){ struct setconf_req *req; struct avdtp_stream *new_stream; unsigned char *ptr; int ret, caps_len; struct avdtp_service_capability *cap; GSList *l; if (session->state != AVDTP_SESSION_STATE_CONNECTED) return -ENOTCONN; if (!(lsep && rsep)) return -EINVAL; debug("avdtp_set_configuration(%p): int_seid=%u, acp_seid=%u", session, lsep->info.seid, rsep->seid); new_stream = g_new0(struct avdtp_stream, 1); new_stream->session = session; new_stream->lsep = lsep; new_stream->rseid = rsep->seid; g_slist_foreach(caps, copy_capabilities, &new_stream->caps); /* Calculate total size of request */ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { cap = l->data; caps_len += cap->length + 2; } req = g_malloc0(sizeof(struct setconf_req) + caps_len); init_request(&req->header, AVDTP_SET_CONFIGURATION); req->int_seid = lsep->info.seid; req->acp_seid = rsep->seid; /* Copy the capabilities into the request */ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { cap = l->data; memcpy(ptr, cap, cap->length + 2); ptr += cap->length + 2; } ret = send_request(session, FALSE, new_stream, req, sizeof(struct setconf_req) + caps_len); if (ret < 0) stream_free(new_stream); else { lsep->info.inuse = 1; lsep->stream = new_stream; rsep->stream = new_stream; session->streams = g_slist_append(session->streams, new_stream); if (stream) *stream = new_stream; } g_free(req); return ret;}int avdtp_reconfigure(struct avdtp *session, GSList *caps, struct avdtp_stream *stream){ struct reconf_req *req; unsigned char *ptr; int caps_len; GSList *l; struct avdtp_service_capability *cap; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state != AVDTP_STATE_OPEN) return -EINVAL; /* Calculate total size of request */ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { cap = l->data; caps_len += cap->length + 2; } req = g_malloc0(sizeof(struct reconf_req) + caps_len); init_request(&req->header, AVDTP_RECONFIGURE); req->acp_seid = stream->rseid; /* Copy the capabilities into the request */ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { cap = l->data; memcpy(ptr, cap, cap->length + 2); ptr += cap->length + 2; } return send_request(session, FALSE, stream, req, sizeof(*req) + caps_len);}int avdtp_open(struct avdtp *session, struct avdtp_stream *stream){ struct seid_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state > AVDTP_STATE_CONFIGURED) return -EINVAL; memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_OPEN); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req));}int avdtp_start(struct avdtp *session, struct avdtp_stream *stream){ struct start_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state != AVDTP_STATE_OPEN) return -EINVAL; memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_START); req.first_seid.seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req));}int avdtp_close(struct avdtp *session, struct avdtp_stream *stream){ struct seid_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state < AVDTP_STATE_OPEN) return -EINVAL; memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_CLOSE); req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) stream->close_int = TRUE; return ret;}int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream){ struct seid_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state <= AVDTP_STATE_OPEN) return -EINVAL; memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_SUSPEND); req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req));}int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream){ struct seid_req req; int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; if (stream->lsep->state <= AVDTP_STATE_OPEN) return -EINVAL; memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_ABORT); req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_ABORTING); return 0;}struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data){ struct avdtp_local_sep *sep; if (free_seid > MAX_SEID) return NULL; sep = g_new0(struct avdtp_local_sep, 1); sep->state = AVDTP_STATE_IDLE; sep->info.seid = free_seid++; sep->info.type = type; sep->info.media_type = media_type; sep->ind = ind; sep->cfm = cfm; sep->user_data = user_data; local_seps = g_slist_append(local_seps, sep); return sep;}int avdtp_unregister_sep(struct avdtp_local_sep *sep){ if (!sep) return -EINVAL; if (sep->info.inuse) return -EBUSY; local_seps = g_slist_remove(local_seps, sep); g_free(sep); return 0;}static void auth_cb(DBusPendingCall *call, void *data){ GIOChannel *io; struct avdtp *session = data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; struct device *dev; dbus_pending_call_unref(session->pending_auth); session->pending_auth = NULL; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { error("Access denied: %s", err.message); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); manager_cancel_authorize(&session->dst, ADVANCED_AUDIO_UUID, NULL); } dbus_error_free(&err); connection_lost(session, -EACCES); dbus_message_unref(reply); return; } session->buf = g_malloc0(session->mtu); set_disconnect_timer(session); session->state = AVDTP_SESSION_STATE_CONNECTED; dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, FALSE); if (dev) avrcp_connect(dev); g_source_remove(session->io); io = g_io_channel_unix_new(session->sock); session->io = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); g_io_channel_unref(io);}static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data){ int srv_sk, cli_sk; socklen_t size; struct sockaddr_l2 addr; struct l2cap_options l2o; bdaddr_t src, dst; struct avdtp *session; GIOChannel *io; char address[18]; if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR)) { error("Hangup or error on AVDTP server socket"); g_io_channel_close(chan); raise(SIGTERM); return FALSE; } srv_sk = g_io_channel_unix_get_fd(chan); size = sizeof(struct sockaddr_l2); cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); if (cli_sk < 0) { error("AVDTP accept: %s (%d)", strerror(errno), errno); return TRUE; } bacpy(&dst, &addr.l2_bdaddr); ba2str(&dst, address); debug("AVDTP: incoming connect from %s", address); size = sizeof(struct sockaddr_l2); if (getsockname(cli_sk, (struct sockaddr *) &addr, &size) < 0) { error("getsockname: %s (%d)", strerror(errno), errno); close(cli_sk); return TRUE; } bacpy(&src, &addr.l2_bdaddr); memset(&l2o, 0, sizeof(l2o)); size = sizeof(l2o); if (getsockopt(cli_sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(errno), errno); close(cli_sk); return TRUE; } session = avdtp_get_internal(&src, &dst); if (session->pending_open && session->pending_open->open_acp) { handle_transport_connect(session, cli_sk, l2o.imtu, l2o.omtu); return TRUE; } if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); close(cli_sk); return TRUE; } if (!manager_authorize(&dst, ADVANCED_AUDIO_UUID, auth_cb, session, &session->pending_auth)) { close(cli_sk); avdtp_unref(session); return TRUE; } session->mtu = l2o.imtu; session->sock = cli_sk; io = g_io_channel_unix_new(session->sock); session->io = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); g_io_channel_unref(io); return TRUE;}static GIOChannel *avdtp_server_socket(void){ int sock, lm; struct sockaddr_l2 addr; GIOChannel *io; sock = socket(AF_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -