diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:23 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:23 -0800 |
commit | a8c8c4c1df055c15fdada72131a51bf8cb313408 (patch) | |
tree | 82f328e7f6b4b3cd76c7bbe737f1abda3b27233e | |
parent | b80c26081133bee33f876a665e1326d5d320dd16 (diff) | |
download | bluez-a8c8c4c1df055c15fdada72131a51bf8cb313408.tar.gz |
auto import from //depot/cupcake/@137055
-rw-r--r-- | utils/audio/a2dp.c | 38 | ||||
-rw-r--r-- | utils/audio/avdtp.c | 891 | ||||
-rw-r--r-- | utils/audio/control.c | 19 | ||||
-rw-r--r-- | utils/audio/headset.c | 2 | ||||
-rw-r--r-- | utils/audio/liba2dp.c | 71 | ||||
-rw-r--r-- | utils/audio/liba2dp.h | 3 | ||||
-rw-r--r-- | utils/audio/unix.c | 44 | ||||
-rw-r--r-- | utils/eglib/gmain.c | 6 | ||||
-rw-r--r-- | utils/hcid/adapter.c | 3 | ||||
-rw-r--r-- | utils/hcid/dbus-sdp.c | 5 |
10 files changed, 718 insertions, 364 deletions
diff --git a/utils/audio/a2dp.c b/utils/audio/a2dp.c index 7f4971bb..3490b81f 100644 --- a/utils/audio/a2dp.c +++ b/utils/audio/a2dp.c @@ -88,6 +88,7 @@ struct a2dp_setup { gboolean start; GSList *cb; int ref; + guint idle; }; static DBusConnection *connection = NULL; @@ -123,12 +124,20 @@ static void setup_free(struct a2dp_setup *s) static void setup_unref(struct a2dp_setup *setup) { + if (!g_slist_find(setups, setup)) { + error("setup_unref: trying to unref a unknown setup"); + return; + } + setup->ref--; debug("setup_unref(%p): ref=%d", setup, setup->ref); - if (setup->ref <= 0) + if (setup->ref <= 0) { + if (setup->idle) + g_source_remove(setup->idle); setup_free(setup); + } } static struct audio_device *a2dp_get_dev(struct avdtp *session) @@ -144,6 +153,8 @@ static gboolean finalize_config(struct a2dp_setup *s) { GSList *l; + /* we return false so this callback will be removed */ + s->idle = 0; setup_ref(s); for (l = s->cb; l != NULL; l = l->next) { struct a2dp_setup_cb *cb = l->data; @@ -174,6 +185,8 @@ static gboolean finalize_resume(struct a2dp_setup *s) { GSList *l; + /* we return false so this callback will be removed */ + s->idle = 0; setup_ref(s); for (l = s->cb; l != NULL; l = l->next) { struct a2dp_setup_cb *cb = l->data; @@ -203,6 +216,8 @@ static gboolean finalize_suspend(struct a2dp_setup *s) { GSList *l; + /* we return false so this callback will be removed */ + s->idle = 0; setup_ref(s); for (l = s->cb; l != NULL; l = l->next) { struct a2dp_setup_cb *cb = l->data; @@ -498,7 +513,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; - if (!setup) + if (!setup || !setup->stream) return; dev = a2dp_get_dev(session); @@ -1302,9 +1317,11 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, break; case AVDTP_STATE_OPEN: case AVDTP_STATE_STREAMING: - if (avdtp_stream_has_capabilities(setup->stream, caps)) - g_idle_add((GSourceFunc) finalize_config, setup); - else if (!setup->reconfigure) { + if (avdtp_stream_has_capabilities(setup->stream, caps)) { + if (setup->idle) + g_source_remove(setup->idle); + setup->idle = g_idle_add((GSourceFunc) finalize_config, setup); + } else if (!setup->reconfigure) { setup->reconfigure = TRUE; if (avdtp_close(session, sep->stream) < 0) { error("avdtp_close failed"); @@ -1367,8 +1384,11 @@ unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, } if (sep->suspending) setup->start = TRUE; - else - g_idle_add((GSourceFunc) finalize_resume, setup); + else { + if (setup->idle) + g_source_remove(setup->idle); + setup->idle = g_idle_add((GSourceFunc) finalize_resume, setup); + } break; default: error("SEP in bad state"); @@ -1412,7 +1432,9 @@ unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, goto failed; break; case AVDTP_STATE_OPEN: - g_idle_add((GSourceFunc) finalize_suspend, setup); + if (setup->idle) + g_source_remove(setup->idle); + setup->idle = g_idle_add((GSourceFunc) finalize_suspend, setup); break; case AVDTP_STATE_STREAMING: if (avdtp_suspend(session, sep->stream) < 0) { diff --git a/utils/audio/avdtp.c b/utils/audio/avdtp.c index 775c37b4..02e26951 100644 --- a/utils/audio/avdtp.c +++ b/utils/audio/avdtp.c @@ -88,14 +88,35 @@ typedef enum { #if __BYTE_ORDER == __LITTLE_ENDIAN -struct avdtp_header { +struct avdtp_common_header { uint8_t message_type:2; uint8_t packet_type:2; uint8_t transaction:4; +} __attribute__ ((packed)); + +struct avdtp_single_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct avdtp_start_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t no_of_packets; uint8_t signal_id:6; uint8_t rfa0:2; } __attribute__ ((packed)); +struct avdtp_continue_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; +} __attribute__ ((packed)); + struct seid_info { uint8_t rfa0:1; uint8_t inuse:1; @@ -112,14 +133,35 @@ struct seid { #elif __BYTE_ORDER == __BIG_ENDIAN -struct avdtp_header { +struct avdtp_common_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; +} __attribute__ ((packed)); + +struct avdtp_single_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct avdtp_start_header { uint8_t transaction:4; uint8_t packet_type:2; uint8_t message_type:2; + uint8_t no_of_packets; uint8_t rfa0:2; uint8_t signal_id:6; } __attribute__ ((packed)); +struct avdtp_continue_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; +} __attribute__ ((packed)); + struct seid_info { uint8_t seid:6; uint8_t inuse:1; @@ -140,43 +182,29 @@ struct seid { /* packets */ -struct gen_req { - struct avdtp_header header; -} __attribute__ ((packed)); - -struct gen_resp { - struct avdtp_header header; -} __attribute__ ((packed)); - struct discover_resp { - struct avdtp_header header; struct seid_info seps[0]; } __attribute__ ((packed)); struct getcap_resp { - struct avdtp_header header; uint8_t caps[0]; } __attribute__ ((packed)); struct start_req { - struct avdtp_header header; struct seid first_seid; struct seid other_seids[0]; } __attribute__ ((packed)); struct suspend_req { - struct avdtp_header header; struct seid first_seid; struct seid other_seids[0]; } __attribute__ ((packed)); struct seid_rej { - struct avdtp_header header; uint8_t error; } __attribute__ ((packed)); struct conf_rej { - struct avdtp_header header; uint8_t category; uint8_t error; } __attribute__ ((packed)); @@ -184,14 +212,11 @@ struct conf_rej { #if __BYTE_ORDER == __LITTLE_ENDIAN struct seid_req { - struct avdtp_header header; uint8_t rfa0:2; uint8_t acp_seid:6; } __attribute__ ((packed)); struct setconf_req { - struct avdtp_header header; - uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t rfa1:2; @@ -201,15 +226,12 @@ struct setconf_req { } __attribute__ ((packed)); struct stream_rej { - struct avdtp_header header; uint8_t rfa0:2; uint8_t acp_seid:6; uint8_t error; } __attribute__ ((packed)); struct reconf_req { - struct avdtp_header header; - uint8_t rfa0:2; uint8_t acp_seid:6; @@ -219,24 +241,14 @@ struct reconf_req { uint8_t caps[0]; } __attribute__ ((packed)); -struct avdtp_general_rej { - uint8_t message_type:2; - uint8_t packet_type:2; - uint8_t transaction:4; - uint8_t rfa0; -} __attribute__ ((packed)); - #elif __BYTE_ORDER == __BIG_ENDIAN struct seid_req { - struct avdtp_header header; uint8_t acp_seid:6; uint8_t rfa0:2; } __attribute__ ((packed)); struct setconf_req { - struct avdtp_header header; - uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t int_seid:6; @@ -246,15 +258,12 @@ struct setconf_req { } __attribute__ ((packed)); struct stream_rej { - struct avdtp_header header; uint8_t acp_seid:6; uint8_t rfa0:2; uint8_t error; } __attribute__ ((packed)); struct reconf_req { - struct avdtp_header header; - uint8_t acp_seid:6; uint8_t rfa0:2; @@ -264,20 +273,25 @@ struct reconf_req { uint8_t caps[0]; } __attribute__ ((packed)); -struct avdtp_general_rej { - uint8_t transaction:4; - uint8_t packet_type:2; - uint8_t message_type:2; - uint8_t rfa0; -} __attribute__ ((packed)); - #else #error "Unknown byte order" #endif +struct in_buf { + gboolean active; + int no_of_packets; + uint8_t transaction; + uint8_t message_type; + uint8_t signal_id; + uint8_t buf[1024]; + uint8_t data_size; +}; + struct pending_req { - struct avdtp_header *msg; - int msg_size; + uint8_t transaction; + uint8_t signal_id; + void *data; + size_t data_size; struct avdtp_stream *stream; /* Set if the request targeted a stream */ guint timeout; }; @@ -349,7 +363,11 @@ struct avdtp { struct avdtp_stream *pending_open; - uint16_t mtu; + uint16_t imtu; + uint16_t omtu; + + struct in_buf in; + char *buf; avdtp_discover_cb_t discov_cb; @@ -373,13 +391,16 @@ static GIOChannel *avdtp_server = NULL; static GSList *sessions = NULL; static int send_request(struct avdtp *session, gboolean priority, - struct avdtp_stream *stream, void *buffer, int size); + struct avdtp_stream *stream, uint8_t signal_id, + void *buffer, size_t size); static gboolean avdtp_parse_resp(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_header *header, int size); + uint8_t transaction, uint8_t signal_id, + void *buf, int size); static gboolean avdtp_parse_rej(struct avdtp *session, - struct avdtp_stream *stream, - struct avdtp_header *header, int size); + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size); static int process_queue(struct avdtp *session); static void connection_lost(struct avdtp *session, int err); static void avdtp_sep_set_state(struct avdtp *session, @@ -406,16 +427,11 @@ static const char *avdtp_statestr(avdtp_state_t state) } } -static gboolean avdtp_send(struct avdtp *session, void *data, int len) +static gboolean try_send(int sk, void *data, int len) { - int ret; - - if (session->sock < 0) { - error("avdtp_send: session is closed"); - return FALSE; - } + gboolean ret; - ret = send(session->sock, data, len, 0); + ret = send(sk, data, len, 0); if (ret < 0) ret = -errno; @@ -423,18 +439,109 @@ static gboolean avdtp_send(struct avdtp *session, void *data, int len) ret = -EIO; if (ret < 0) { - error("avdtp_send: %s (%d)", strerror(-ret), -ret); + error("try_send: %s (%d)", strerror(-ret), -ret); return FALSE; } return TRUE; } +static gboolean avdtp_send(struct avdtp *session, uint8_t transaction, + uint8_t message_type, uint8_t signal_id, + void *data, size_t len) +{ + int cont_fragments, sent; + struct avdtp_start_header start; + struct avdtp_continue_header cont; + + if (session->sock < 0) { + error("avdtp_send: session is closed"); + return FALSE; + } + + /* Single packet - no fragmentation */ + if (sizeof(struct avdtp_single_header) + len <= session->omtu) { + struct avdtp_single_header single; + + memset(&single, 0, sizeof(single)); + + single.transaction = transaction; + single.packet_type = AVDTP_PKT_TYPE_SINGLE; + single.message_type = message_type; + single.signal_id = signal_id; + + memcpy(session->buf, &single, sizeof(single)); + memcpy(session->buf + sizeof(single), data, len); + + return try_send(session->sock, session->buf, + sizeof(single) + len); + } + + /* Count the number of needed fragments */ + cont_fragments = (len - (session->omtu - sizeof(start))) / + (session->omtu - sizeof(cont)) + 1; + + debug("avdtp_send: %u bytes split into %d fragments", len, + cont_fragments + 1); + + /* Send the start packet */ + memset(&start, 0, sizeof(start)); + start.transaction = transaction; + start.packet_type = AVDTP_PKT_TYPE_START; + start.message_type = message_type; + start.no_of_packets = cont_fragments + 1; + start.signal_id = signal_id; + + memcpy(session->buf, &start, sizeof(start)); + memcpy(session->buf + sizeof(start), data, + session->omtu - sizeof(start)); + + if (!try_send(session->sock, session->buf, session->omtu)) + return FALSE; + + debug("avdtp_send: first packet with %d bytes sent", + session->omtu - sizeof(start)); + + sent = session->omtu - sizeof(start); + + /* Send the continue fragments and the end packet */ + while (sent < len) { + int left, to_copy; + + left = len - sent; + if (left + sizeof(cont) > session->omtu) { + cont.packet_type = AVDTP_PKT_TYPE_CONTINUE; + to_copy = session->omtu - sizeof(cont); + debug("avdtp_send: sending continue with %d bytes", + to_copy); + } else { + cont.packet_type = AVDTP_PKT_TYPE_END; + to_copy = left; + debug("avdtp_send: sending end with %d bytes", + to_copy); + } + + cont.transaction = transaction; + cont.message_type = message_type; + + memcpy(session->buf, &cont, sizeof(cont)); + memcpy(session->buf + sizeof(cont), data + sent, to_copy); + + if (!try_send(session->sock, session->buf, + to_copy + sizeof(cont))) + return FALSE; + + sent += to_copy; + } + + return TRUE; +} + static void pending_req_free(struct pending_req *req) { if (req->timeout) g_source_remove(req->timeout); - g_free(req->msg); + g_free(req->data); g_free(req); } @@ -628,6 +735,10 @@ static void handle_transport_connect(struct avdtp *session, int sock, GIOChannel *channel; session->pending_open = NULL; + if (!g_slist_find(session->streams, stream)) { + debug("handle_transport_connect: stream got removed"); + return; + } if (stream->timer) { g_source_remove(stream->timer); @@ -655,6 +766,8 @@ static void handle_transport_connect(struct avdtp *session, int sock, channel = g_io_channel_unix_new(stream->sock); + if (stream->io) + g_source_remove(stream->io); 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); @@ -683,11 +796,13 @@ static void avdtp_sep_set_state(struct avdtp *session, sep->state = state; if (stream) { - GSList *l; - for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) { + GSList *l = stream->callbacks; + while (l != NULL) { struct stream_callback *cb = l->data; + GSList *next = g_slist_next(l); cb->cb(stream, old_state, state, err_ptr, cb->user_data); + l = next; } } @@ -703,18 +818,20 @@ static void avdtp_sep_set_state(struct avdtp *session, } break; case AVDTP_STATE_IDLE: - if (stream->idle_timer) { - g_source_remove(stream->idle_timer); - stream->idle_timer = 0; + if (g_slist_find(session->streams, stream)) { + 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); } - 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; @@ -828,6 +945,8 @@ void avdtp_unref(struct avdtp *session) if (session->dc_timer) remove_disconnect_timer(session); + if (session->io) + g_source_remove(session->io); sessions = g_slist_remove(sessions, session); @@ -921,67 +1040,42 @@ static GSList *caps_to_list(uint8_t *data, int size, 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) +static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction, + void *buf, 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)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + 0, NULL, 0); } -static gboolean avdtp_discover_cmd(struct avdtp *session, - struct gen_req *req, int size) +static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction, + void *buf, int size) { GSList *l; - struct discover_resp *rsp = (struct discover_resp *) session->buf; - struct seid_info *info; - int rsp_size; + int rsp_size, sep_count, i; + struct seid_info *seps; - init_response(&rsp->header, &req->header, TRUE); - rsp_size = sizeof(struct discover_resp); - info = rsp->seps; + sep_count = g_slist_length(local_seps); + rsp_size = sep_count * sizeof(struct seid_info); - for (l = local_seps; l != NULL; l = l->next) { - struct avdtp_local_sep *sep = l->data; + seps = g_new0(struct seid_info, sep_count); - if (rsp_size + sizeof(struct seid_info) > session->mtu) - break; + for (l = local_seps, i = 0; l != NULL; l = l->next, i++) { + struct avdtp_local_sep *sep = l->data; - memcpy(info, &sep->info, sizeof(struct seid_info)); - rsp_size += sizeof(struct seid_info); - info++; + memcpy(&seps[i], &sep->info, sizeof(struct seid_info)); } - return avdtp_send(session, session->buf, rsp_size); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_DISCOVER, seps, rsp_size); } -static gboolean avdtp_getcap_cmd(struct avdtp *session, +static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, 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; + uint8_t err, buf[1024], *ptr = buf; if (size < sizeof(struct seid_req)) { error("Too short getcap request"); @@ -998,14 +1092,10 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, 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)) { + for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { struct avdtp_service_capability *cap = l->data; - if (rsp_size + cap->length + 2 > session->mtu) + if (rsp_size + cap->length + 2 > sizeof(buf)) break; memcpy(ptr, cap, cap->length + 2); @@ -1017,19 +1107,18 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, g_slist_free(caps); - return avdtp_send(session, session->buf, rsp_size); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_GET_CAPABILITIES, buf, rsp_size); failed: - init_response(&rej.header, &req->header, FALSE); - rej.error = AVDTP_BAD_ACP_SEID; - return avdtp_send(session, &rej, sizeof(rej)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_GET_CAPABILITIES, &err, sizeof(err)); } -static gboolean avdtp_setconf_cmd(struct avdtp *session, +static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, 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; @@ -1069,9 +1158,8 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, } } - init_response(&rsp->header, &req->header, TRUE); - - if (!avdtp_send(session, rsp, sizeof(struct setconf_req))) { + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SET_CONFIGURATION, NULL, 0)) { stream_free(stream); return FALSE; } @@ -1084,31 +1172,29 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, return TRUE; failed: - init_response(&rej.header, &req->header, FALSE); rej.error = err; rej.category = category; - return avdtp_send(session, &rej, sizeof(rej)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); } -static gboolean avdtp_getconf_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + return avdtp_unknown_cmd(session, transaction, (void *) req, size); } -static gboolean avdtp_reconf_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + return avdtp_unknown_cmd(session, transaction, (void *) req, size); } -static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction, + 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)) { @@ -1135,30 +1221,31 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, goto failed; } - init_response(&rsp->header, &req->header, TRUE); - - if (!avdtp_send(session, rsp, sizeof(struct gen_resp))) + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_OPEN, NULL, 0)) return FALSE; stream->open_acp = TRUE; session->pending_open = stream; - stream->timer = g_timeout_add(REQ_TIMEOUT, stream_open_timeout, - stream); + if (stream->timer) + g_source_remove(stream->timer); + 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)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_OPEN, &err, sizeof(err)); + } -static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, - int size) +static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction, + 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; @@ -1198,25 +1285,22 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); } - init_response(&rsp->header, &req->header, TRUE); - - return avdtp_send(session, rsp, sizeof(struct gen_resp)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_START, NULL, 0); failed: memset(&rej, 0, sizeof(rej)); - init_response(&rej.header, &req->header, FALSE); rej.acp_seid = failed_seid; rej.error = err; - return avdtp_send(session, &rej, sizeof(rej)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_START, &rej, sizeof(rej)); } -static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction, + 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)) { @@ -1246,28 +1330,28 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); - init_response(&rsp->header, &req->header, TRUE); - - if (!avdtp_send(session, rsp, sizeof(struct gen_resp))) + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_CLOSE, NULL, 0)) return FALSE; - stream->timer = g_timeout_add(REQ_TIMEOUT, stream_close_timeout, + if (stream->timer) + g_source_remove(stream->timer); + stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, + stream_close_timeout, stream); return TRUE; failed: - init_response(&rej.header, &req->header, FALSE); - rej.error = err; - return avdtp_send(session, &rej, sizeof(rej)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_CLOSE, &err, sizeof(err)); } -static gboolean avdtp_suspend_cmd(struct avdtp *session, +static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction, struct suspend_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; @@ -1307,24 +1391,21 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); } - init_response(&rsp->header, &req->header, TRUE); - - return avdtp_send(session, rsp, sizeof(struct gen_resp)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SUSPEND, NULL, 0); failed: memset(&rej, 0, sizeof(rej)); - init_response(&rej.header, &req->header, FALSE); rej.acp_seid = failed_seid; rej.error = err; - return avdtp_send(session, &rej, sizeof(rej)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_SUSPEND, &rej, sizeof(rej)); } -static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) { struct avdtp_local_sep *sep; - struct gen_resp *rsp = (struct gen_resp *) session->buf; - struct seid_rej rej; uint8_t err; gboolean ret; @@ -1345,114 +1426,241 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, goto failed; } - init_response(&rsp->header, &req->header, TRUE); - ret = avdtp_send(session, rsp, sizeof(struct gen_resp)); - + ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_ABORT, NULL, 0); if (ret) avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); return ret; failed: - init_response(&rej.header, &req->header, FALSE); - rej.error = err; - return avdtp_send(session, &rej, sizeof(rej)); + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_ABORT, &err, sizeof(err)); } -static gboolean avdtp_secctl_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + return avdtp_unknown_cmd(session, transaction, (void *) req, size); } -static gboolean avdtp_parse_cmd(struct avdtp *session, - struct avdtp_header *header, int size) +static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction, + uint8_t signal_id, void *buf, int size) { - switch (header->signal_id) { + switch (signal_id) { case AVDTP_DISCOVER: debug("Received DISCOVER_CMD"); - return avdtp_discover_cmd(session, (void *) header, size); + return avdtp_discover_cmd(session, transaction, buf, size); case AVDTP_GET_CAPABILITIES: debug("Received GET_CAPABILITIES_CMD"); - return avdtp_getcap_cmd(session, (void *) header, size); + return avdtp_getcap_cmd(session, transaction, buf, size); case AVDTP_SET_CONFIGURATION: debug("Received SET_CONFIGURATION_CMD"); - return avdtp_setconf_cmd(session, (void *) header, size); + return avdtp_setconf_cmd(session, transaction, buf, size); case AVDTP_GET_CONFIGURATION: debug("Received GET_CONFIGURATION_CMD"); - return avdtp_getconf_cmd(session, (void *) header, size); + return avdtp_getconf_cmd(session, transaction, buf, size); case AVDTP_RECONFIGURE: debug("Received RECONFIGURE_CMD"); - return avdtp_reconf_cmd(session, (void *) header, size); + return avdtp_reconf_cmd(session, transaction, buf, size); case AVDTP_OPEN: debug("Received OPEN_CMD"); - return avdtp_open_cmd(session, (void *) header, size); + return avdtp_open_cmd(session, transaction, buf, size); case AVDTP_START: debug("Received START_CMD"); - return avdtp_start_cmd(session, (void *) header, size); + return avdtp_start_cmd(session, transaction, buf, size); case AVDTP_CLOSE: debug("Received CLOSE_CMD"); - return avdtp_close_cmd(session, (void *) header, size); + return avdtp_close_cmd(session, transaction, buf, size); case AVDTP_SUSPEND: debug("Received SUSPEND_CMD"); - return avdtp_suspend_cmd(session, (void *) header, size); + return avdtp_suspend_cmd(session, transaction, buf, size); case AVDTP_ABORT: debug("Received ABORT_CMD"); - return avdtp_abort_cmd(session, (void *) header, size); + return avdtp_abort_cmd(session, transaction, buf, size); case AVDTP_SECURITY_CONTROL: debug("Received SECURITY_CONTROL_CMD"); - return avdtp_secctl_cmd(session, (void *) header, size); + return avdtp_secctl_cmd(session, transaction, buf, size); default: - debug("Received unknown request id %u", header->signal_id); - return avdtp_unknown_cmd(session, (void *) header, size); + debug("Received unknown request id %u", signal_id); + return avdtp_unknown_cmd(session, transaction, buf, size); } } -static void init_request(struct avdtp_header *header, int request_id) +enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS }; + +static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, + void *buf, size_t size) { - static int transaction = 0; + struct avdtp_common_header *header = buf; + struct avdtp_single_header *single = (void *) session->buf; + struct avdtp_start_header *start = (void *) session->buf; + void *payload; + gsize payload_size; + + switch (header->packet_type) { + case AVDTP_PKT_TYPE_SINGLE: + if (size < sizeof(*single)) { + error("Received too small single packet (%d bytes)", size); + return PARSE_ERROR; + } + if (session->in.active) { + error("SINGLE: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(*single); + payload_size = size - sizeof(*single); + + session->in.active = TRUE; + session->in.data_size = 0; + session->in.no_of_packets = 1; + session->in.transaction = header->transaction; + session->in.message_type = header->message_type; + session->in.signal_id = single->signal_id; + + break; + case AVDTP_PKT_TYPE_START: + if (size < sizeof(*start)) { + error("Received too small start packet (%d bytes)", size); + return PARSE_ERROR; + } + if (session->in.active) { + error("START: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + + session->in.active = TRUE; + session->in.data_size = 0; + session->in.transaction = header->transaction; + session->in.message_type = header->message_type; + session->in.no_of_packets = start->no_of_packets; + session->in.signal_id = start->signal_id; - header->packet_type = AVDTP_PKT_TYPE_SINGLE; - header->message_type = AVDTP_MSG_TYPE_COMMAND; - header->transaction = transaction; - header->signal_id = request_id; + payload = session->buf + sizeof(*start); + payload_size = size - sizeof(*start); - /* clear rfa bits */ - header->rfa0 = 0; + break; + case AVDTP_PKT_TYPE_CONTINUE: + if (size < sizeof(struct avdtp_continue_header)) { + error("Received too small continue packet (%d bytes)", + size); + return PARSE_ERROR; + } + if (!session->in.active) { + error("CONTINUE: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + if (session->in.transaction != header->transaction) { + error("Continue transaction id doesn't match"); + return PARSE_ERROR; + } + if (session->in.no_of_packets <= 1) { + error("Too few continue packets"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(struct avdtp_continue_header); + payload_size = size - sizeof(struct avdtp_continue_header); + + break; + case AVDTP_PKT_TYPE_END: + if (size < sizeof(struct avdtp_continue_header)) { + error("Received too small end packet (%d bytes)", size); + return PARSE_ERROR; + } + if (!session->in.active) { + error("END: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + if (session->in.transaction != header->transaction) { + error("End transaction id doesn't match"); + return PARSE_ERROR; + } + if (session->in.no_of_packets > 1) { + error("Got an end packet too early"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(struct avdtp_continue_header); + payload_size = size - sizeof(struct avdtp_continue_header); - transaction = (transaction + 1) % 16; + break; + default: + error("Invalid AVDTP packet type 0x%02X", header->packet_type); + return PARSE_ERROR; + } + + if (session->in.data_size + payload_size > + sizeof(session->in.buf)) { + error("Not enough incoming buffer space!"); + return PARSE_ERROR; + } + + memcpy(session->in.buf + session->in.data_size, payload, payload_size); + session->in.data_size += payload_size; + + if (session->in.no_of_packets > 1) { + session->in.no_of_packets--; + debug("Received AVDTP fragment. %d to go", + session->in.no_of_packets); + return PARSE_FRAGMENT; + } + + session->in.active = FALSE; + + return PARSE_SUCCESS; } static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct avdtp *session = data; - struct avdtp_header *header; + struct avdtp_common_header *header; + struct avdtp_stream *stream; gsize size; debug("session_cb"); - if (cond & G_IO_NVAL) + if (!g_slist_find(sessions, session)) { + error("session_cb called after session was freed"); + return FALSE; + } + + if (cond & G_IO_NVAL) { + session->io = 0; return FALSE; + } if (cond & (G_IO_HUP | G_IO_ERR)) goto failed; - if (g_io_channel_read(chan, session->buf, session->mtu, - &size) != G_IO_ERROR_NONE) { + if (g_io_channel_read(chan, session->buf, session->imtu, &size) + != G_IO_ERROR_NONE) { error("IO Channel read error"); goto failed; } - if (size < sizeof(struct avdtp_header)) { + header = (void *) session->buf; + if (size < sizeof(struct avdtp_common_header)) { error("Received too small packet (%d bytes)", size); goto failed; } - header = (struct avdtp_header *) session->buf; + switch (avdtp_parse_data(session, session->buf, size)) { + case PARSE_ERROR: + goto failed; + case PARSE_FRAGMENT: + return TRUE; + case PARSE_SUCCESS: + break; + } - if (header->message_type == AVDTP_MSG_TYPE_COMMAND) { - if (!avdtp_parse_cmd(session, header, size)) { + if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { + if (!avdtp_parse_cmd(session, session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { error("Unable to handle command. Disconnecting"); goto failed; } @@ -1467,16 +1675,16 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, } if (session->req == NULL) { - error("No pending request, rejecting message"); + error("No pending request, ignoring message"); return TRUE; } - if (header->transaction != session->req->msg->transaction) { + if (header->transaction != session->req->transaction) { error("Transaction label doesn't match"); return TRUE; } - if (header->signal_id != session->req->msg->signal_id) { + if (session->in.signal_id != session->req->signal_id) { error("Reponse signal doesn't match"); return TRUE; } @@ -1484,23 +1692,33 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, g_source_remove(session->req->timeout); session->req->timeout = 0; - switch(header->message_type) { + stream = session->req->stream; + if (!g_slist_find(session->streams, stream)) + stream = NULL; + + switch (header->message_type) { case AVDTP_MSG_TYPE_ACCEPT: - if (!avdtp_parse_resp(session, session->req->stream, - header, size)) { + if (!avdtp_parse_resp(session, stream, + session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { error("Unable to parse accept response"); goto failed; } break; case AVDTP_MSG_TYPE_REJECT: - if (!avdtp_parse_rej(session, session->req->stream, - header, size)) { + if (!avdtp_parse_rej(session, stream, + session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { error("Unable to parse reject response"); goto failed; } break; default: - error("Unknown message type"); + error("Unknown message type 0x%02X", header->message_type); break; } @@ -1561,9 +1779,15 @@ static void l2cap_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, if (session->state == AVDTP_SESSION_STATE_CONNECTING) { struct audio_device *dev; - session->mtu = l2o.imtu; - session->buf = g_malloc0(session->mtu); + session->imtu = l2o.imtu; + session->omtu = l2o.omtu; + + debug("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu); + + session->buf = g_malloc0(session->imtu); session->state = AVDTP_SESSION_STATE_CONNECTED; + if (session->io) + g_source_remove(session->io); session->io = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, @@ -1624,26 +1848,32 @@ static gboolean request_timeout(gpointer user_data) struct avdtp *session = user_data; struct pending_req *req; struct seid_req sreq; - struct avdtp_local_sep *lsep; - struct avdtp_stream *stream; - uint8_t seid; + struct avdtp_local_sep *lsep = NULL; + struct avdtp_stream *stream = NULL; + uint8_t seid = 0; + uint8_t signal_id; struct avdtp_error err; + if (!g_slist_find(sessions, session)) { + error("request_timeout called after session was freed"); + return FALSE; + } + req = session->req; session->req = NULL; avdtp_error_init(&err, AVDTP_ERROR_ERRNO, ETIMEDOUT); - seid = ((struct seid_req *) (req->msg))->acp_seid; - - stream = find_stream_by_rseid(session, seid); - - if (stream) - lsep = stream->lsep; - else - lsep = NULL; + if (req->data) { + seid = ((struct seid_req *) (req->data))->acp_seid; + stream = find_stream_by_rseid(session, seid); + if (stream) + lsep = stream->lsep; + } + signal_id = req->signal_id; + pending_req_free(req); - switch (req->msg->signal_id) { + switch (signal_id) { case AVDTP_RECONFIGURE: error("Reconfigure request timed out"); if (lsep && lsep->cfm && lsep->cfm->reconfigure) @@ -1692,26 +1922,25 @@ static gboolean request_timeout(gpointer user_data) } memset(&sreq, 0, sizeof(sreq)); - init_request(&sreq.header, AVDTP_ABORT); sreq.acp_seid = seid; - if (send_request(session, TRUE, stream, &sreq, sizeof(sreq)) < 0) { + if (send_request(session, TRUE, stream, AVDTP_ABORT, + &sreq, sizeof(sreq)) < 0) { error("Unable to send abort request"); goto failed; } - goto done; + return FALSE; failed: connection_lost(session, -ETIMEDOUT); -done: - pending_req_free(req); return FALSE; } static int send_req(struct avdtp *session, gboolean priority, struct pending_req *req) { + static int transaction = 0; int err; if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { @@ -1726,34 +1955,42 @@ static int send_req(struct avdtp *session, gboolean priority, return 0; } + req->transaction = transaction++; + transaction %= 16; + /* FIXME: Should we retry to send if the buffer was not totally sent or in case of EINTR? */ - if (!avdtp_send(session, req->msg, req->msg_size)) { + if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND, + req->signal_id, req->data, req->data_size)) { err = -EIO; goto failed; } session->req = req; + if (req->timeout) + g_source_remove(req->timeout); req->timeout = g_timeout_add(REQ_TIMEOUT, request_timeout, session); return 0; failed: - g_free(req->msg); + g_free(req->data); g_free(req); return err; } static int send_request(struct avdtp *session, gboolean priority, - struct avdtp_stream *stream, void *buffer, int size) + struct avdtp_stream *stream, uint8_t signal_id, + void *buffer, size_t size) { struct pending_req *req; req = g_new0(struct pending_req, 1); - req->msg = g_malloc(size); - memcpy(req->msg, buffer, size); - req->msg_size = size; + req->signal_id = signal_id; + req->data = g_malloc(size); + memcpy(req->data, buffer, size); + req->data_size = size; req->stream = stream; return send_req(session, priority, req); @@ -1762,9 +1999,9 @@ static int send_request(struct avdtp *session, gboolean priority, static gboolean avdtp_discover_resp(struct avdtp *session, struct discover_resp *resp, int size) { - int sep_count, i, isize = sizeof(struct seid_info); + int sep_count, i; - sep_count = (size - sizeof(struct avdtp_header)) / isize; + sep_count = size / sizeof(struct seid_info); for (i = 0; i < sep_count; i++) { struct avdtp_remote_sep *sep; @@ -1792,10 +2029,11 @@ static gboolean avdtp_discover_resp(struct avdtp *session, sep->media_type = resp->seps[i].media_type; memset(&req, 0, sizeof(req)); - init_request(&req.header, AVDTP_GET_CAPABILITIES); req.acp_seid = sep->seid; - ret = send_request(session, TRUE, NULL, &req, sizeof(req)); + ret = send_request(session, TRUE, NULL, + AVDTP_GET_CAPABILITIES, + &req, sizeof(req)); if (ret < 0) { finalize_discovery(session, ret); break; @@ -1824,7 +2062,7 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, return FALSE; } - seid = ((struct seid_req *) session->req->msg)->acp_seid; + seid = ((struct seid_req *) session->req->data)->acp_seid; sep = find_remote_sep(session->seps, seid); @@ -1840,13 +2078,12 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), &sep->codec); - return TRUE; } static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_header *resp, + struct avdtp_single_header *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; @@ -1862,7 +2099,7 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session, static gboolean avdtp_reconfigure_resp(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_header *resp, int size) + struct avdtp_single_header *resp, int size) { return TRUE; } @@ -1914,13 +2151,14 @@ static gboolean avdtp_close_resp(struct avdtp *session, static gboolean avdtp_suspend_resp(struct avdtp *session, struct avdtp_stream *stream, - struct gen_resp *resp, - int size) + void *data, int size) { struct avdtp_local_sep *sep = stream->lsep; avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + if (stream->idle_timer) + g_source_remove(stream->idle_timer); stream->idle_timer = g_timeout_add(STREAM_TIMEOUT, (GSourceFunc) stream_timeout, stream); @@ -1946,27 +2184,25 @@ static gboolean avdtp_abort_resp(struct avdtp *session, static gboolean avdtp_parse_resp(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_header *header, int size) + uint8_t transaction, uint8_t signal_id, + void *buf, int size) { - struct avdtp_header *next; + struct pending_req *next; if (session->prio_queue) - next = ((struct pending_req *) - (session->prio_queue->data))->msg; + next = session->prio_queue->data; else if (session->req_queue) - next = ((struct pending_req *) - (session->req_queue->data))->msg; + next = session->req_queue->data; else next = NULL; - switch (header->signal_id) { + switch (signal_id) { case AVDTP_DISCOVER: debug("DISCOVER request succeeded"); - return avdtp_discover_resp(session, (void *) header, size); + return avdtp_discover_resp(session, buf, size); case AVDTP_GET_CAPABILITIES: debug("GET_CAPABILITIES request succeeded"); - if (!avdtp_get_capabilities_resp(session, - (void *) header, size)) + if (!avdtp_get_capabilities_resp(session, buf, size)) return FALSE; if (!(next && next->signal_id == AVDTP_GET_CAPABILITIES)) finalize_discovery(session, 0); @@ -1974,31 +2210,28 @@ static gboolean avdtp_parse_resp(struct avdtp *session, case AVDTP_SET_CONFIGURATION: debug("SET_CONFIGURATION request succeeded"); return avdtp_set_configuration_resp(session, stream, - (void *) header, size); + buf, size); case AVDTP_RECONFIGURE: debug("RECONFIGURE request succeeded"); - return avdtp_reconfigure_resp(session, stream, - (void *) header, size); + return avdtp_reconfigure_resp(session, stream, buf, size); case AVDTP_OPEN: debug("OPEN request succeeded"); - return avdtp_open_resp(session, stream, (void *) header, size); + return avdtp_open_resp(session, stream, buf, size); case AVDTP_SUSPEND: debug("SUSPEND request succeeded"); - return avdtp_suspend_resp(session, stream, - (void *) header, size); + return avdtp_suspend_resp(session, stream, buf, size); case AVDTP_START: debug("START request succeeded"); - return avdtp_start_resp(session, stream, (void *) header, size); + return avdtp_start_resp(session, stream, buf, size); case AVDTP_CLOSE: debug("CLOSE request succeeded"); - return avdtp_close_resp(session, stream, (void *) header, size); + return avdtp_close_resp(session, stream, buf, size); case AVDTP_ABORT: debug("ABORT request succeeded"); - return avdtp_abort_resp(session, stream, (void *) header, size); + return avdtp_abort_resp(session, stream, buf, size); } - error("Unknown signal id in accept response: %u", header->signal_id); - + error("Unknown signal id in accept response: %u", signal_id); return TRUE; } @@ -2049,28 +2282,29 @@ static gboolean stream_rej_to_err(struct stream_rej *rej, int size, } static gboolean avdtp_parse_rej(struct avdtp *session, - struct avdtp_stream *stream, - struct avdtp_header *header, int size) + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size) { struct avdtp_error err; uint8_t acp_seid, category; struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; - switch (header->signal_id) { + switch (signal_id) { case AVDTP_DISCOVER: - if (!seid_rej_to_err((void *) header, size, &err)) + if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("DISCOVER request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); return TRUE; case AVDTP_GET_CAPABILITIES: - if (!seid_rej_to_err((void *) header, size, &err)) + if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("GET_CAPABILITIES request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); return TRUE; case AVDTP_OPEN: - if (!seid_rej_to_err((void *) header, size, &err)) + if (!seid_rej_to_err(buf, size, &err)) return FALSE; error("OPEN request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2079,7 +2313,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, sep->user_data); return TRUE; case AVDTP_SET_CONFIGURATION: - if (!conf_rej_to_err((void *) header, size, &err, &category)) + if (!conf_rej_to_err(buf, size, &err, &category)) return FALSE; error("SET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2088,7 +2322,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, &err, sep->user_data); return TRUE; case AVDTP_RECONFIGURE: - if (!conf_rej_to_err((void *) header, size, &err, &category)) + if (!conf_rej_to_err(buf, size, &err, &category)) return FALSE; error("RECONFIGURE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2097,7 +2331,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, sep->user_data); return TRUE; case AVDTP_START: - if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("START request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2106,7 +2340,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, sep->user_data); return TRUE; case AVDTP_SUSPEND: - if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("SUSPEND request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2115,7 +2349,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, sep->user_data); return TRUE; case AVDTP_CLOSE: - if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("CLOSE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2124,7 +2358,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, sep->user_data); return TRUE; case AVDTP_ABORT: - if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) return FALSE; error("ABORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); @@ -2133,8 +2367,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, sep->user_data); return TRUE; default: - error("Unknown reject response signal id: %u", - header->signal_id); + error("Unknown reject response signal id: %u", signal_id); return TRUE; } } @@ -2313,7 +2546,6 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data) { - struct gen_req req; int ret; if (session->discov_cb) @@ -2324,10 +2556,7 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, return 0; } - memset(&req, 0, sizeof(req)); - init_request(&req.header, AVDTP_DISCOVER); - - ret = send_request(session, FALSE, NULL, &req, sizeof(req)); + ret = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0); if (ret == 0) { session->discov_cb = cb; session->user_data = user_data; @@ -2431,10 +2660,10 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) 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)); + return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION, + &req, sizeof(req)); } static void copy_capabilities(gpointer data, gpointer user_data) @@ -2487,7 +2716,6 @@ int avdtp_set_configuration(struct avdtp *session, 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; @@ -2498,7 +2726,8 @@ int avdtp_set_configuration(struct avdtp *session, ptr += cap->length + 2; } - ret = send_request(session, FALSE, new_stream, req, + ret = send_request(session, FALSE, new_stream, + AVDTP_SET_CONFIGURATION, req, sizeof(struct setconf_req) + caps_len); if (ret < 0) stream_free(new_stream); @@ -2521,7 +2750,7 @@ int avdtp_reconfigure(struct avdtp *session, GSList *caps, { struct reconf_req *req; unsigned char *ptr; - int caps_len; + int caps_len, err; GSList *l; struct avdtp_service_capability *cap; @@ -2539,7 +2768,6 @@ int avdtp_reconfigure(struct avdtp *session, GSList *caps, 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 */ @@ -2549,8 +2777,11 @@ int avdtp_reconfigure(struct avdtp *session, GSList *caps, ptr += cap->length + 2; } - return send_request(session, FALSE, stream, req, sizeof(*req) - + caps_len); + err = send_request(session, FALSE, stream, AVDTP_RECONFIGURE, req, + sizeof(*req) + caps_len); + g_free(req); + + return err; } int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) @@ -2564,10 +2795,10 @@ int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) 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)); + return send_request(session, FALSE, stream, AVDTP_OPEN, + &req, sizeof(req)); } int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) @@ -2581,10 +2812,10 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) 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)); + return send_request(session, FALSE, stream, AVDTP_START, + &req, sizeof(req)); } int avdtp_close(struct avdtp *session, struct avdtp_stream *stream) @@ -2599,10 +2830,10 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream) 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)); + ret = send_request(session, FALSE, stream, AVDTP_CLOSE, + &req, sizeof(req)); if (ret == 0) stream->close_int = TRUE; @@ -2620,10 +2851,10 @@ int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) 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)); + return send_request(session, FALSE, stream, AVDTP_SUSPEND, + &req, sizeof(req)); } int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) @@ -2638,10 +2869,10 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) 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)); + ret = send_request(session, FALSE, stream, AVDTP_ABORT, + &req, sizeof(req)); if (ret == 0) avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_ABORTING); @@ -2710,7 +2941,7 @@ static void auth_cb(DBusError *derr, void *user_data) return; } - session->buf = g_malloc0(session->mtu); + session->buf = g_malloc0(session->imtu); session->stream_setup = TRUE; set_disconnect_timer(session); @@ -2722,7 +2953,8 @@ static void auth_cb(DBusError *derr, void *user_data) if (dev && dev->control) device_set_control_timer(dev); - g_source_remove(session->io); + if (session->io) + g_source_remove(session->io); io = g_io_channel_unix_new(session->sock); session->io = g_io_add_watch(io, @@ -2770,9 +3002,14 @@ static void avdtp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, goto drop; } - session->mtu = l2o.imtu; + session->imtu = l2o.imtu; + session->omtu = l2o.omtu; session->sock = sk; + debug("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu); + + if (session->io) + g_source_remove(session->io); session->io = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); diff --git a/utils/audio/control.c b/utils/audio/control.c index a0c1e403..267b1350 100644 --- a/utils/audio/control.c +++ b/utils/audio/control.c @@ -450,6 +450,11 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, if (!(cond | G_IO_IN)) goto failed; + if (!g_slist_find(sessions, session)) { + error("avctp session_cb: session no longer exists"); + return FALSE; + } + ret = read(session->sock, buf, sizeof(buf)); if (ret <= 0) goto failed; @@ -631,6 +636,11 @@ static void auth_cb(DBusError *derr, void *user_data) { struct avctp *session = user_data; + if (!g_slist_find(sessions, session)) { + error("avctp auth_cb: session no longer exists"); + return; + } + if (derr && dbus_error_is_set(derr)) { error("Access denied: %s", derr->message); if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { @@ -699,6 +709,8 @@ static void avctp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, 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); @@ -749,6 +761,11 @@ static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, int sk; char address[18]; + if (!g_slist_find(sessions, session)) { + error("avctp_connect_cb: session no longer exists"); + return; + } + if (err < 0) { avctp_unref(session); error("AVCTP connect(%s): %s (%d)", address, strerror(-err), @@ -781,6 +798,8 @@ static void avctp_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, session->state = AVCTP_STATE_CONNECTED; session->mtu = l2o.imtu; + if (session->io) + g_source_remove(session->io); session->io = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); diff --git a/utils/audio/headset.c b/utils/audio/headset.c index 0adc1e9d..6283133e 100644 --- a/utils/audio/headset.c +++ b/utils/audio/headset.c @@ -1820,6 +1820,8 @@ void headset_set_state(struct audio_device *dev, headset_state_t state) case HEADSET_STATE_PLAY_IN_PROGRESS: break; case HEADSET_STATE_PLAYING: + if (hs->sco_id) + g_source_remove(hs->sco_id); hs->sco_id = g_io_add_watch(hs->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) sco_cb, dev); diff --git a/utils/audio/liba2dp.c b/utils/audio/liba2dp.c index 9d2f54ad..25ebede8 100644 --- a/utils/audio/liba2dp.c +++ b/utils/audio/liba2dp.c @@ -87,6 +87,7 @@ struct bluetooth_data { int link_mtu; /* MTU for selected transport channel */ struct pollfd stream; /* Audio stream filedescriptor */ struct pollfd server; /* Audio daemon filedescriptor */ + int configured; /* true if we have a configured sink */ sbc_capabilities_t sbc_capabilities; sbc_t sbc; /* Codec data */ @@ -146,6 +147,7 @@ static void bluetooth_close(struct bluetooth_data *data) sbc_finish(&data->sbc); data->sbc_initialized = 0; + data->configured = 0; } static int bluetooth_start(struct bluetooth_data *data) @@ -182,7 +184,7 @@ static int bluetooth_start(struct bluetooth_data *data) data->stream.fd = bt_audio_service_get_data_fd(data->server.fd); if (data->stream.fd < 0) { - LOGE("bt_audio_service_get_data_fd failed, errno: %d\n", errno); + ERR("bt_audio_service_get_data_fd failed, errno: %d", errno); return -errno; } data->stream.events = POLLOUT; @@ -650,7 +652,8 @@ static int audioservice_send(struct bluetooth_data *data, err = -errno; ERR("Error sending data to audio service: %s(%d)", strerror(errno), errno); - bluetooth_close(data); + if (err == -EPIPE) + bluetooth_close(data); } return err; @@ -709,23 +712,31 @@ static int audioservice_expect(struct bluetooth_data *data, static int bluetooth_init(struct bluetooth_data *data) { int sk, err; - char buf[BT_AUDIO_IPC_PACKET_SIZE]; - bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; - struct bt_getcapabilities_req *getcaps_req = (void*) buf; - struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf; DBG("bluetooth_init"); sk = bt_audio_service_open(); if (sk <= 0) { ERR("bt_audio_service_open failed\n"); - err = -errno; - goto failed; + return -errno; } data->server.fd = sk; data->server.events = POLLIN; + return 0; +} + +static int bluetooth_configure(struct bluetooth_data *data) +{ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; + struct bt_getcapabilities_req *getcaps_req = (void*) buf; + struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf; + int err; + + DBG("bluetooth_configure"); + memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE); getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ; getcaps_req->flags = 0; @@ -736,20 +747,19 @@ static int bluetooth_init(struct bluetooth_data *data) err = audioservice_send(data, &getcaps_req->h); if (err < 0) { ERR("audioservice_send failed for BT_GETCAPABILITIES_REQ\n"); - goto failed; + return err; } err = audioservice_expect(data, &rsp_hdr->msg_h, BT_GETCAPABILITIES_RSP); if (err < 0) { ERR("audioservice_expect failed for BT_GETCAPABILITIES_RSP\n"); - goto failed; + return err; } if (rsp_hdr->posix_errno != 0) { ERR("BT_GETCAPABILITIES failed : %s(%d)", strerror(rsp_hdr->posix_errno), rsp_hdr->posix_errno); - err = -rsp_hdr->posix_errno; - goto failed; + return -rsp_hdr->posix_errno; } if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP) @@ -758,23 +768,18 @@ static int bluetooth_init(struct bluetooth_data *data) err = bluetooth_a2dp_hw_params(data); if (err < 0) { ERR("bluetooth_a2dp_hw_params failed err: %d", err); - goto failed; + return err; } + data->configured = 1; return 0; - -failed: - ERR("bluetooth_init failed, err: %d\n", err); - bt_audio_service_close(sk); - data->server.fd = -1; - return err; } -int a2dp_init(const char* address, int rate, int channels, a2dpData* dataPtr) +int a2dp_init(int rate, int channels, a2dpData* dataPtr) { int err; - DBG("a2dp_init %s rate: %d channels: %d", address, rate, channels); + DBG("a2dp_init rate: %d channels: %d", rate, channels); *dataPtr = NULL; struct bluetooth_data* data = malloc(sizeof(struct bluetooth_data)); if (!data) @@ -784,7 +789,7 @@ int a2dp_init(const char* address, int rate, int channels, a2dpData* dataPtr) data->server.fd = -1; data->stream.fd = -1; - strncpy(data->address, address, 18); + strncpy(data->address, "00:00:00:00:00:00", 18); data->rate = rate; data->channels = channels; @@ -802,6 +807,16 @@ error: return err; } +void a2dp_set_sink(a2dpData d, const char* address) +{ + struct bluetooth_data* data = (struct bluetooth_data*)d; + if (strncmp(data->address, address, 18)) { + strncpy(data->address, address, 18); + // force reconfiguration + data->configured = 0; + } +} + int a2dp_write(a2dpData d, const void* buffer, int count) { struct bluetooth_data* data = (struct bluetooth_data*)d; @@ -811,6 +826,7 @@ int a2dp_write(a2dpData d, const void* buffer, int count) long frames_left = count; int encoded, written; const char *buff; + int did_configure = 0; #ifdef ENABLE_TIMING uint64_t begin, end; DBG("********** a2dp_write **********"); @@ -823,10 +839,21 @@ int a2dp_write(a2dpData d, const void* buffer, int count) return err; } +configure: + if (!data->configured) { + err = bluetooth_configure(data); + if (err < 0) + return err; + did_configure = 1; + } + if (data->stream.fd == -1) { err = bluetooth_start(data); if (err < 0) { ERR("bluetooth_start failed err: %d", err); + data->configured = 0; + if (!did_configure) + goto configure; return err; } } diff --git a/utils/audio/liba2dp.h b/utils/audio/liba2dp.h index c135090f..5a9ef343 100644 --- a/utils/audio/liba2dp.h +++ b/utils/audio/liba2dp.h @@ -28,7 +28,8 @@ extern "C" { typedef void* a2dpData; -int a2dp_init(const char* address, int rate, int channels, a2dpData* dataPtr); +int a2dp_init(int rate, int channels, a2dpData* dataPtr); +void a2dp_set_sink(a2dpData data, const char* address); int a2dp_write(a2dpData data, const void* buffer, int count); int a2dp_stop(a2dpData data); void a2dp_cleanup(a2dpData data); diff --git a/utils/audio/unix.c b/utils/audio/unix.c index c3fd3683..31b5a9c0 100644 --- a/utils/audio/unix.c +++ b/utils/audio/unix.c @@ -110,6 +110,9 @@ static void client_free(struct unix_client *client) break; } + if (client->cancel && client->req_id > 0) + client->cancel(client->dev, client->req_id); + if (client->sock >= 0) close(client->sock); @@ -199,7 +202,14 @@ static void stream_state_changed(struct avdtp_stream *stream, void *user_data) { struct unix_client *client = user_data; - struct a2dp_data *a2dp = &client->d.a2dp; + struct a2dp_data *a2dp; + + if (!g_slist_find(clients, client)) { + debug("Client disconnected in stream_state_changed"); + return; + } + + a2dp = &client->d.a2dp; switch (new_state) { case AVDTP_STATE_IDLE: @@ -426,6 +436,11 @@ static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, uint16_t imtu, omtu; GSList *caps; + if (!g_slist_find(clients, client)) { + debug("Client disconnected in a2dp_config_complete"); + return; + } + if (err) goto failed; @@ -487,6 +502,11 @@ static void a2dp_resume_complete(struct avdtp *session, struct bt_streamfd_ind *ind = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; + if (!g_slist_find(clients, client)) { + debug("Client disconnected in a2dp_resume_complete"); + return; + } + if (err) goto failed; @@ -529,6 +549,11 @@ static void a2dp_suspend_complete(struct avdtp *session, struct bt_streamstart_rsp *rsp = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; + if (!g_slist_find(clients, client)) { + debug("Client disconnected in a2dp_suspend_complete"); + return; + } + if (err) goto failed; @@ -748,6 +773,11 @@ static void create_cb(struct audio_device *dev, void *user_data) { struct unix_client *client = user_data; + if (!g_slist_find(clients, client)) { + debug("Client disconnected during device creation"); + return; + } + if (!dev) unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); else @@ -992,13 +1022,21 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) bt_audio_msg_header_t *msghdr = (void *) buf; struct unix_client *client = data; int len; - struct a2dp_data *a2dp = &client->d.a2dp; - struct headset_data *hs = &client->d.hs; + struct a2dp_data *a2dp; + struct headset_data *hs; const char *type; if (cond & G_IO_NVAL) return FALSE; + if (!g_slist_find(clients, client)) { + debug("Client disconnected in client_cb"); + return TRUE; + } + + a2dp = &client->d.a2dp; + hs = &client->d.hs; + if (cond & (G_IO_HUP | G_IO_ERR)) { debug("Unix client disconnected (fd=%d)", client->sock); switch (client->type) { diff --git a/utils/eglib/gmain.c b/utils/eglib/gmain.c index 3475fdbf..0748a391 100644 --- a/utils/eglib/gmain.c +++ b/utils/eglib/gmain.c @@ -502,6 +502,10 @@ void g_main_loop_run(GMainLoop *loop) struct io_watch *w; for (nfds = 0, l = context->io_watches; l != NULL; l = l->next, nfds++) { + if (nfds == open_max) { + error("Number of io_watches exceeds open_max in g_main_loop_run. We probably have a leak."); + abort(); + } w = l->data; ufds[nfds].fd = w->channel->fd; ufds[nfds].events = w->condition; @@ -522,7 +526,7 @@ void g_main_loop_run(GMainLoop *loop) w = context->io_watches->data; - if (!*w->revents) { + if (!w || !w->revents || !*w->revents) { context->io_watches = g_slist_remove(context->io_watches, w); context->proc_io_watches = watch_list_add(context->proc_io_watches, w); continue; diff --git a/utils/hcid/adapter.c b/utils/hcid/adapter.c index 25bab32b..4ef4fe0e 100644 --- a/utils/hcid/adapter.c +++ b/utils/hcid/adapter.c @@ -2488,8 +2488,6 @@ static DBusMessage *remove_bonding(DBusConnection *conn, DBusMessage *msg, && msg){ int err = errno; error("Disconnect failed"); - hci_close_dev(dev); - return failed_strerror(msg, err); } } @@ -2600,6 +2598,7 @@ static gboolean create_bonding_conn_complete(GIOChannel *io, GIOCondition cond, if (!adapter->bonding) { /* If we come here it implies a bug somewhere */ debug("create_bonding_conn_complete: no pending bonding!"); + adapter->bonding->io_id = 0; g_io_channel_close(io); g_io_channel_unref(io); return FALSE; diff --git a/utils/hcid/dbus-sdp.c b/utils/hcid/dbus-sdp.c index b62c70a3..21146287 100644 --- a/utils/hcid/dbus-sdp.c +++ b/utils/hcid/dbus-sdp.c @@ -136,6 +136,8 @@ gboolean idle_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct cached_session *s = user_data; + s->io_id = 0; + if (cond & G_IO_NVAL) return FALSE; @@ -394,6 +396,7 @@ static gboolean search_process_cb(GIOChannel *chan, int err = 0; if (cond & G_IO_NVAL) { + ctxt->io_id = 0; g_io_channel_unref(chan); return FALSE; } @@ -852,6 +855,8 @@ static gboolean sdp_client_connect_cb(GIOChannel *chan, } /* set the callback responsible for update the transaction data */ + if (ctxt->io_id) + g_source_remove(ctxt->io_id); ctxt->io_id = g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, search_process_cb, ctxt); |