📄 avdtp.c
字号:
g_free(stream);}static gboolean stream_timeout(struct avdtp_stream *stream){ struct avdtp *session = stream->session; avdtp_close(session, stream); stream->idle_timer = 0; return FALSE;}static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, gpointer data){ struct avdtp_stream *stream = data; struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) sep->cfm->close(stream->session, sep, stream, NULL, sep->user_data); stream->io = 0; avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); return FALSE;}static void handle_transport_connect(struct avdtp *session, int sock, uint16_t imtu, uint16_t omtu){ struct avdtp_stream *stream = session->pending_open; struct avdtp_local_sep *sep = stream->lsep; GIOChannel *channel; session->pending_open = NULL; if (stream->timer) { g_source_remove(stream->timer); stream->timer = 0; } if (sock < 0) { if (!stream->open_acp && sep->cfm && sep->cfm->open) { struct avdtp_error err; avdtp_error_init(&err, AVDTP_ERROR_ERRNO, EIO); sep->cfm->open(session, sep, NULL, &err, sep->user_data); } return; } stream->sock = sock; stream->omtu = omtu; stream->imtu = imtu; if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, NULL, sep->user_data); channel = g_io_channel_unix_new(stream->sock); stream->io = g_io_add_watch(channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) transport_cb, stream); g_io_channel_unref(channel);}static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state){ struct avdtp_stream *stream = sep->stream; avdtp_state_t old_state; struct avdtp_error err, *err_ptr = NULL; if (sep->state == state) { avdtp_error_init(&err, AVDTP_ERROR_ERRNO, EIO); debug("stream state change failed: %s", avdtp_strerror(&err)); err_ptr = &err; } else { err_ptr = NULL; debug("stream state changed: %s -> %s", avdtp_statestr(sep->state), avdtp_statestr(state)); } old_state = sep->state; sep->state = state; if (stream) { GSList *l; for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) { struct stream_callback *cb = l->data; cb->cb(stream, old_state, state, err_ptr, cb->user_data); } } switch (state) { case AVDTP_STATE_OPEN: break; case AVDTP_STATE_STREAMING: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: if (stream->idle_timer) { g_source_remove(stream->idle_timer); stream->idle_timer = 0; } break; case AVDTP_STATE_IDLE: if (stream->idle_timer) { g_source_remove(stream->idle_timer); stream->idle_timer = 0; } session->streams = g_slist_remove(session->streams, stream); if (session->pending_open == stream) handle_transport_connect(session, -1, 0, 0); if (session->req && session->req->stream == stream) session->req->stream = NULL; stream_free(stream); if (session->ref == 1 && !session->streams) set_disconnect_timer(session); break; default: break; }}static void finalize_discovery(struct avdtp *session, int err){ struct avdtp_error avdtp_err; avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); if (!session->discov_cb) return; session->discov_cb(session, session->seps, err ? &avdtp_err : NULL, session->user_data); session->discov_cb = NULL; session->user_data = NULL;}static void release_stream(struct avdtp_stream *stream, struct avdtp *session){ struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->abort) sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);}static void connection_lost(struct avdtp *session, int err){ struct device *dev; dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, FALSE); if (dev) avrcp_disconnect(dev); if (session->state == AVDTP_SESSION_STATE_CONNECTED) { char address[18]; ba2str(&session->dst, address); debug("Disconnected from %s", address); } session->free_lock = 1; finalize_discovery(session, err); g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; session->free_lock = 0; if (session->sock >= 0) { close(session->sock); session->sock = -1; } session->state = AVDTP_SESSION_STATE_DISCONNECTED; if (session->io) { g_source_remove(session->io); session->io = 0; } if (session->ref != 1) error("connection_lost: ref count not 1 after all callbacks"); else avdtp_unref(session);}void avdtp_unref(struct avdtp *session){ if (!session) return; if (!g_slist_find(sessions, session)) { error("avdtp_unref: trying to unref a unknown session"); return; } session->ref--; debug("avdtp_unref(%p): ref=%d", session, session->ref); if (session->ref == 1) { if (session->state == AVDTP_SESSION_STATE_CONNECTING) { close(session->sock); session->sock = -1; } if (session->sock >= 0) set_disconnect_timer(session); else if (!session->free_lock) /* Drop the local ref if we aren't connected */ session->ref--; } if (session->ref > 0) return; debug("avdtp_unref(%p): freeing session and removing from list", session); if (session->dc_timer) remove_disconnect_timer(session); sessions = g_slist_remove(sessions, session); if (session->req) pending_req_free(session->req); g_slist_foreach(session->seps, (GFunc) g_free, NULL); g_slist_free(session->seps); g_free(session->buf); g_free(session);}struct avdtp *avdtp_ref(struct avdtp *session){ session->ref++; debug("avdtp_ref(%p): ref=%d", session, session->ref); if (session->dc_timer) remove_disconnect_timer(session); return session;}static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid){ GSList *l; for (l = local_seps; l != NULL; l = g_slist_next(l)) { struct avdtp_local_sep *sep = l->data; if (sep->info.seid == seid) return sep; } return NULL;}static struct avdtp_local_sep *find_local_sep(uint8_t type, uint8_t media_type, uint8_t codec){ GSList *l; for (l = local_seps; l != NULL; l = g_slist_next(l)) { struct avdtp_local_sep *sep = l->data; if (sep->info.inuse) continue; if (sep->info.type == type && sep->info.media_type == media_type && sep->codec == codec) return sep; } return NULL;}static GSList *caps_to_list(uint8_t *data, int size, struct avdtp_service_capability **codec){ GSList *caps; int processed; for (processed = 0, caps = NULL; processed + 2 < size;) { struct avdtp_service_capability *cap; uint8_t length, category; category = data[0]; length = data[1]; if (processed + 2 + length > size) { error("Invalid capability data in getcap resp"); break; } cap = g_malloc(sizeof(struct avdtp_service_capability) + length); memcpy(cap, data, 2 + length); processed += 2 + length; data += 2 + length; caps = g_slist_append(caps, cap); if (category == AVDTP_MEDIA_CODEC && length >= sizeof(struct avdtp_media_codec_capability)) *codec = cap; } return caps;}static void init_response(struct avdtp_header *rsp, struct avdtp_header *req, gboolean accept){ rsp->packet_type = AVDTP_PKT_TYPE_SINGLE; rsp->message_type = accept ? AVDTP_MSG_TYPE_ACCEPT : AVDTP_MSG_TYPE_REJECT; rsp->transaction = req->transaction; rsp->signal_id = req->signal_id; rsp->rfa0 = 0;}static gboolean avdtp_unknown_cmd(struct avdtp *session, struct avdtp_header *req, int size){ struct avdtp_general_rej rej; memset(&rej, 0, sizeof(rej)); rej.packet_type = AVDTP_PKT_TYPE_SINGLE; rej.message_type = AVDTP_MSG_TYPE_REJECT; rej.transaction = req->transaction; return avdtp_send(session, &rej, sizeof(rej));}static gboolean avdtp_discover_cmd(struct avdtp *session, struct gen_req *req, int size){ GSList *l; struct discover_resp *rsp = (struct discover_resp *) session->buf; struct seid_info *info; int rsp_size; init_response(&rsp->header, &req->header, TRUE); rsp_size = sizeof(struct discover_resp); info = rsp->seps; for (l = local_seps; l != NULL; l = l->next) { struct avdtp_local_sep *sep = l->data; if (rsp_size + sizeof(struct seid_info) > session->mtu) break; memcpy(info, &sep->info, sizeof(struct seid_info)); rsp_size += sizeof(struct seid_info); info++; } return avdtp_send(session, session->buf, rsp_size);}static gboolean avdtp_getcap_cmd(struct avdtp *session, struct seid_req *req, int size){ GSList *l, *caps; struct avdtp_local_sep *sep = NULL; struct seid_rej rej; struct getcap_resp *rsp = (struct getcap_resp *) session->buf; int rsp_size; unsigned char *ptr; uint8_t err; if (size < sizeof(struct seid_req)) { error("Too short getcap request"); return FALSE; } sep = find_local_sep_by_seid(req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (!sep->ind->get_capability(session, sep, &caps, &err, sep->user_data)) goto failed; init_response(&rsp->header, &req->header, TRUE); rsp_size = sizeof(struct getcap_resp); ptr = rsp->caps; for (l = caps; l != NULL; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; if (rsp_size + cap->length + 2 > session->mtu) break; memcpy(ptr, cap, cap->length + 2); rsp_size += cap->length + 2; ptr += cap->length + 2; g_free(cap); } g_slist_free(caps); return avdtp_send(session, session->buf, rsp_size);failed: init_response(&rej.header, &req->header, FALSE); rej.error = AVDTP_BAD_ACP_SEID; return avdtp_send(session, &rej, sizeof(rej));}static gboolean avdtp_setconf_cmd(struct avdtp *session, struct setconf_req *req, int size){ struct conf_rej rej; struct gen_resp *rsp = (struct gen_resp *) session->buf; struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err, category = 0x00; if (size < sizeof(struct setconf_req)) { error("Too short getcap request"); return FALSE; } sep = find_local_sep_by_seid(req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->stream) { err = AVDTP_SEP_IN_USE; goto failed; } stream = g_new0(struct avdtp_stream, 1); stream->session = session; stream->lsep = sep; stream->rseid = req->int_seid; stream->caps = caps_to_list(req->caps, size - sizeof(struct setconf_req), &stream->codec); stream->sock = -1; if (sep->ind && sep->ind->set_configuration) { if (!sep->ind->set_configuration(session, sep, stream, stream->caps, &err, &category, sep->user_data)) { stream_free(stream); goto failed; } } init_response(&rsp->header, &req->header, TRUE); if (!avdtp_send(session, rsp, sizeof(struct setconf_req))) { stream_free(stream); return FALSE; } sep->stream = stream; session->streams = g_slist_append(session->streams, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); return TRUE;failed: init_response(&rej.header, &req->header, FALSE); rej.error = err; rej.category = category; return avdtp_send(session, &rej, sizeof(rej));}static gboolean avdtp_getconf_cmd(struct avdtp *session, struct seid_req *req, int size){ return avdtp_unknown_cmd(session, (void *) req, size);}static gboolean avdtp_reconf_cmd(struct avdtp *session, struct seid_req *req, int size){ return avdtp_unknown_cmd(session, (void *) req, size);}static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, int size){ struct avdtp_local_sep *sep; struct avdtp_stream *stream; struct gen_resp *rsp = (struct gen_resp *) session->buf; struct seid_rej rej; uint8_t err; if (size < sizeof(struct seid_req)) { error("Too short abort request"); return FALSE; } sep = find_local_sep_by_seid(req->acp_seid); if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } if (sep->state != AVDTP_STATE_CONFIGURED) { err = AVDTP_BAD_STATE; goto failed; } stream = sep->stream; if (sep->ind && sep->ind->open) { if (!sep->ind->open(session, sep, stream, &err, sep->user_data)) goto failed; } init_response(&rsp->header, &req->header, TRUE); if (!avdtp_send(session, rsp, sizeof(struct gen_resp))) return FALSE; stream->open_acp = TRUE; session->pending_open = stream; avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); stream->timer = g_timeout_add(REQ_TIMEOUT, stream_open_timeout, stream); return TRUE;failed: init_response(&rej.header, &req->header, FALSE); rej.error = err; return avdtp_send(session, &rej, sizeof(rej));}static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, int size){ struct avdtp_local_sep *sep; struct avdtp_stream *stream; struct gen_resp *rsp = (struct gen_resp *) session->buf; struct stream_rej rej; struct seid *seid; uint8_t err, failed_seid; int seid_count, i; if (size < sizeof(struct start_req)) { error("Too short start request"); return FALSE; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -