From 85ad02b73626ee5caca6adb6e2890a7a588ecb38 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 28 Jan 2009 11:50:49 +0200 Subject: Implement AVDTP signal fragmentation support This patch implements AVDTP signal fragmentation. Even though it's a mandatory feature it in practice it never gets used with A2DP as it requires messages that exceed 48 bytes (the minimum L2CAP MTU). --- audio/avdtp.c | 780 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 487 insertions(+), 293 deletions(-) diff --git a/audio/avdtp.c b/audio/avdtp.c index bf3b79e0..86e30475 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -91,14 +91,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; @@ -115,7 +136,13 @@ 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; @@ -123,6 +150,21 @@ struct avdtp_header { 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; @@ -143,43 +185,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)); @@ -187,14 +215,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; @@ -204,15 +229,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; @@ -222,24 +244,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; @@ -249,15 +261,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; @@ -267,20 +276,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; }; @@ -363,7 +377,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; @@ -380,13 +398,16 @@ static GSList *servers = 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, @@ -427,16 +448,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, size_t 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; @@ -444,8 +460,99 @@ 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; @@ -455,7 +562,7 @@ 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); } @@ -940,67 +1047,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(session->server->seps); + rsp_size = sep_count * sizeof(struct seid_info); - for (l = session->server->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 = session->server->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"); @@ -1017,14 +1099,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); @@ -1036,19 +1114,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; @@ -1112,9 +1189,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; } @@ -1127,31 +1203,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)) { @@ -1178,9 +1252,8 @@ 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; @@ -1192,17 +1265,15 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, 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; @@ -1243,25 +1314,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_REJECT, + 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)) { @@ -1291,9 +1359,8 @@ 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_seconds(REQ_TIMEOUT, @@ -1303,17 +1370,15 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, 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; @@ -1354,24 +1419,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; @@ -1392,89 +1454,197 @@ 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; + + payload = session->buf + sizeof(*start); + payload_size = size - sizeof(*start); + + 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); + + break; + default: + error("Invalid AVDTP packet type 0x%02X", header->packet_type); + return PARSE_ERROR; + } - header->packet_type = AVDTP_PKT_TYPE_SINGLE; - header->message_type = AVDTP_MSG_TYPE_COMMAND; - header->transaction = transaction; - header->signal_id = request_id; + 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; + } - /* clear rfa bits */ - header->rfa0 = 0; + session->in.active = FALSE; - transaction = (transaction + 1) % 16; + 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 = (void *) session->buf; gsize size; debug("session_cb"); @@ -1485,21 +1655,31 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, 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)) { + 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; } @@ -1514,16 +1694,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; } @@ -1531,23 +1711,29 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, g_source_remove(session->req->timeout); session->req->timeout = 0; - switch(header->message_type) { + switch (header->message_type) { case AVDTP_MSG_TYPE_ACCEPT: if (!avdtp_parse_resp(session, session->req->stream, - header, size)) { + 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)) { + 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; } @@ -1608,8 +1794,12 @@ 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; session->io = g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP @@ -1685,7 +1875,7 @@ static gboolean request_timeout(gpointer user_data) avdtp_error_init(&err, AVDTP_ERROR_ERRNO, ETIMEDOUT); - seid = ((struct seid_req *) (req->msg))->acp_seid; + seid = ((struct seid_req *) (req->data))->acp_seid; stream = find_stream_by_rseid(session, seid); @@ -1694,7 +1884,7 @@ static gboolean request_timeout(gpointer user_data) else lsep = NULL; - switch (req->msg->signal_id) { + switch (req->signal_id) { case AVDTP_RECONFIGURE: error("Reconfigure request timed out"); if (lsep && lsep->cfm && lsep->cfm->reconfigure) @@ -1743,10 +1933,10 @@ 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; } @@ -1763,6 +1953,7 @@ done: 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) { @@ -1777,13 +1968,18 @@ 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; req->timeout = g_timeout_add_seconds(REQ_TIMEOUT, @@ -1792,20 +1988,22 @@ static int send_req(struct avdtp *session, gboolean priority, 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); @@ -1814,9 +2012,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; @@ -1844,10 +2042,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; @@ -1876,7 +2075,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); @@ -1898,7 +2097,7 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, 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; @@ -1914,7 +2113,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; } @@ -1967,8 +2166,7 @@ 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; @@ -1996,27 +2194,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); @@ -2024,31 +2220,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; } @@ -2099,28 +2292,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); @@ -2129,7 +2323,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); @@ -2138,7 +2332,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); @@ -2147,7 +2341,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); @@ -2156,7 +2350,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); @@ -2165,7 +2359,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); @@ -2174,7 +2368,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); @@ -2183,8 +2377,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; } } @@ -2364,7 +2557,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) @@ -2375,10 +2567,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; @@ -2482,10 +2671,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) @@ -2538,7 +2727,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; @@ -2549,7 +2737,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); @@ -2572,7 +2761,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; @@ -2590,7 +2779,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 */ @@ -2600,8 +2788,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) @@ -2615,10 +2806,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) @@ -2632,10 +2823,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) @@ -2650,10 +2841,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; @@ -2671,10 +2862,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) @@ -2689,10 +2880,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, TRUE, stream, AVDTP_ABORT, + &req, sizeof(req)); if (ret == 0) avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_ABORTING); @@ -2767,7 +2958,7 @@ static void auth_cb(DBusError *derr, void *user_data) return; } - session->buf = g_malloc0(session->mtu); + session->buf = g_malloc0(session->imtu); /* Here we set the disconnect timer so we don't stay in IDLE state * indefinitely but set auto_dc to FALSE so that when a stream is @@ -2837,9 +3028,12 @@ 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); + session->io = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); err = btd_request_authorization(src, dst, ADVANCED_AUDIO_UUID, -- cgit