OSDN Git Service

auto import from //depot/cupcake/@137055
authorThe Android Open Source Project <initial-contribution@android.com>
Tue, 3 Mar 2009 06:54:23 +0000 (22:54 -0800)
committerThe Android Open Source Project <initial-contribution@android.com>
Tue, 3 Mar 2009 06:54:23 +0000 (22:54 -0800)
utils/audio/a2dp.c
utils/audio/avdtp.c
utils/audio/control.c
utils/audio/headset.c
utils/audio/liba2dp.c
utils/audio/liba2dp.h
utils/audio/unix.c
utils/eglib/gmain.c
utils/hcid/adapter.c
utils/hcid/dbus-sdp.c

index 7f4971b..3490b81 100644 (file)
@@ -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) {
index 775c37b..02e2695 100644 (file)
@@ -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);
 
index a0c1e40..267b135 100644 (file)
@@ -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);
index 0adc1e9..6283133 100644 (file)
@@ -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);
index 9d2f54a..25ebede 100644 (file)
@@ -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;
                }
        }
index c135090..5a9ef34 100644 (file)
@@ -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);
index c3fd368..31b5a9c 100644 (file)
@@ -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) {
index 3475fdb..0748a39 100644 (file)
@@ -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;
index 25bab32..4ef4fe0 100644 (file)
@@ -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;
index b62c70a..2114628 100644 (file)
@@ -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);