Android bluetooth介紹—連接

一、A2DP_CONNECT上層代碼流程




二、從HCI log中看AVDTP 創建過程
1AVDTP l2cap建立過程



2AVDTP相關信令處理流程在HCI 中的流程



DISCOVER \GET_CAPABILITIES\SET_CONFIGURATION\OPEN\START\SUSPEND
三、audiosink函數註冊、及命令處理流程
AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
(一)、sink_connect創建流程
        整體流程如下所示.




1、idh.code\external\bluetooth\bluez\audio\sink.c

    static DBusMessage *sink_connect(DBusConnection *conn,  
                    DBusMessage *msg, void *data)  
    {  
    …………  
        if (!sink->session)//(1)、如果沒有AVDTP會話,獲取AVDTP連接狀態;  
            sink->session = avdtp_get(&dev->src, &dev->dst);  
      
        if (!sink->session)//相關失敗操作  
            return btd_error_failed(msg, "Unable to get a session");  
      
        if (sink->connect || sink->disconnect)//如果正在連接、斷開,發送busy消息;  
            return btd_error_busy(msg);  
      
        if (sink->stream_state >= AVDTP_STATE_OPEN)//如果已經打開,發送已經連接消息;  
            return btd_error_already_connected(msg);  
      
        if (!sink_setup_stream(sink, NULL))//(2)、創建AVDTP流;  
            return btd_error_failed(msg, "Failed to create a stream");  
      
        dev->auto_connect = FALSE;  
      
        pending = sink->connect;  
      
        pending->conn = dbus_connection_ref(conn);//(3)、保存客戶端dbus信息;  
        pending->msg = dbus_message_ref(msg);  
      
        DBG("stream creation in progress");  
      
        return NULL;  
    }  

(1)、如果沒有AVDTP會話,獲取AVDTP連接狀態;

    sink->session = avdtp_get(&dev->src, &dev->dst);  
    idh.code\external\bluetooth\hcidump\parser\avdtp.c  
    struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)  
    {  
    ………………  
        session = avdtp_get_internal(src, dst);  
    ………………  
      
    }  
    avdtp_get_internal 中設置 session->state狀態,  
    session->state = AVDTP_SESSION_STATE_DISCONNECTED;  

(2)、創建AVDTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c

    gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)  
    {  
    …………  
        avdtp_set_auto_disconnect(sink->session, FALSE);//不能自動斷開;  
      
        if (avdtp_discover(sink->session, discovery_complete, sink) < 0)//調用avdtp_discover,  
    discovery_complete爲回調函數;  
            return FALSE;  
      
        sink->connect = g_new0(struct pending_request, 1);  
      
        return TRUE;  
    }  

idh.code\external\bluetooth\hcidump\parser\avdtp.c

    int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,  
                void *user_data)  
    {  
        int err;  
      
        if (session->discov_cb)  
            return -EBUSY;  
      
        if (session->seps) {  
            session->discov_cb = cb;  
            session->user_data = user_data;  
            g_idle_add(process_discover, session);  
            return 0;  
        }  
      
        err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);  
    //發送AVDTP_DISCOVER命令出去  
        if (err == 0) {  
            session->discov_cb = cb;  
            session->user_data = user_data;  
        }  
      
        return err;  
    }  

idh.code\external\bluetooth\hcidump\parser\avdtp.c

    static int send_request(struct avdtp *session, gboolean priority,  
                struct avdtp_stream *stream, uint8_t signal_id,  
                void *buffer, size_t size)  
    {  
        struct pending_req *req;  
      
        if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {  
            DBG("Unable to send requests while aborting");  
            return -EINVAL;  
        }  
      
        req = g_new0(struct pending_req, 1);  
        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);//這個函數我們後面分析;  
    }  

(3)、保存客戶端dbus信息;

    pending->conn = dbus_connection_ref(conn);  
        pending->msg = dbus_message_ref(msg);  

2、send_req 創建L2CAP連接
idh.code\external\bluetooth\hcidump\parser\avdtp.c

    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) {//如果AVDTP沒有連接,  
            session->io = l2cap_connect(session);//(1)、創建l2cap連接;  
            if (!session->io) {  
                err = -EIO;  
                goto failed;  
            }  
            avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);  
        }  
      
        if (session->state < AVDTP_SESSION_STATE_CONNECTED ||  
                session->req != NULL) {//如果AVDTP沒連接  
            queue_request(session, req, priority);//把相關參數放入隊列  
            return 0;//在這裏返回,後面AVDTP sock建立完成後,會再次調用這個函數;  
        }  
      
        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->transaction, AVDTP_MSG_TYPE_COMMAND,  
                    req->signal_id, req->data, req->data_size)) {//(2)、發送相關命令  
            err = -EIO;  
            goto failed;  
        }  
    …………  
    }  

(1)、創建l2cap連接
sink connect的過程本質上是建立一個avdtp 連接的過程,avdtp是基於l2cap的,包括控制命令的發送和數據的發送都是l2cap的,所以這個圖紙表示了建立一個發送控制命令的l2cap的socket,等這個socket建立起來以後,開始發送AVDPT_DISCOVER的請求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

 session->io = l2cap_connect(session);  
static GIOChannel *l2cap_connect(struct avdtp *session)  
{  
    GError *err = NULL;  
    GIOChannel *io;  
  
    io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,  
                NULL, &err,  
                BT_IO_OPT_SOURCE_BDADDR, &session->server->src,  
                BT_IO_OPT_DEST_BDADDR, &session->dst,  
                BT_IO_OPT_PSM, AVDTP_PSM,  
                BT_IO_OPT_INVALID);  
    if (!io) {  
        error("%s", err->message);  
        g_error_free(err);  
        return NULL;  
    }  
  
    return io;<strong>  
}  
</strong>  

這個函數中注意兩點,1)、bt_io_connect;2)、avdtp_connect_cb回調函數;
1)、bt_io_connect
idh.code\external\bluetooth\bluez\btio\btio.c

    GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,  
                    gpointer user_data, GDestroyNotify destroy,  
                    GError **gerr, BtIOOption opt1, ...)  
    {  
        …………  
      
        io = create_io(type, FALSE, &opts, gerr);  
        if (io == NULL)  
            return NULL;  
        sock = g_io_channel_unix_get_fd(io);  
        switch (type) {  
        case BT_IO_L2RAW:  
            err = l2cap_connect(sock, &opts.dst, 0, opts.cid);  
            break;  
    //不同協議的連接,如L2CPA、RFCOMM、SCO  
        case BT_IO_L2CAP:  
            err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);  
            break;  
        case BT_IO_RFCOMM:  
            err = rfcomm_connect(sock, &opts.dst, opts.channel);  
            break;  
        case BT_IO_SCO:  
            err = sco_connect(sock, &opts.dst);  
            break;  
    …………  
        connect_add(io, connect, user_data, destroy);  
      
        return io;  
    }<strong>  
    </strong>  


Btio中l2cap_connect的實現:
idh.code\external\bluetooth\bluez\btio\btio.c

    static int l2cap_connect(int sock, const bdaddr_t *dst,  
                        uint16_t psm, uint16_t cid)  
    {  
        int err;  
        struct sockaddr_l2 addr;  
      
        memset(&addr, 0, sizeof(addr));  
        addr.l2_family = AF_BLUETOOTH;  
        bacpy(&addr.l2_bdaddr, dst);  
        if (cid)  
            addr.l2_cid = htobs(cid);  
        else  
            addr.l2_psm = htobs(psm);  
      
        err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAP  
        if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))  
            return err;  
      
        return 0;  
    }  

2)、avdtp_connect_cb回調函數
idh.code\external\bluetooth\hcidump\parser\avdtp.c

    static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)  
    {  
    ………………  
        if (session->state == AVDTP_SESSION_STATE_CONNECTING) {//如果處於正在連接狀態;  
            DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);  
      
            session->buf = g_malloc0(session->imtu);  
            avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);//設置AVDTP狀態爲已經連接狀態;  
      
            if (session->io_id)  
                g_source_remove(session->io_id);  
      
            /* This watch should be low priority since otherwise the  
             * connect callback might be dispatched before the session  
             * callback if the kernel wakes us up at the same time for  
             * them. This could happen if a headset is very quick in  
             * sending the Start command after connecting the stream  
             * transport channel.  
             */  
            session->io_id = g_io_add_watch_full(chan,  
                            G_PRIORITY_LOW,  
                            G_IO_IN | G_IO_ERR | G_IO_HUP  
                            | G_IO_NVAL,  
                            (GIOFunc) session_cb, session,  
                            NULL);  
      
    ………………  
        process_queue(session);//發送DISCOVER  
      
        return;  
    …………  
    }  

3、process_queue(session)發送DISCOVER命令出去
idh.code\external\bluetooth\hcidump\parser\avdtp.c

    static int process_queue(struct avdtp *session)  
    {  
    …………  
        *queue = g_slist_remove(*queue, req);  
      
        return send_req(session, FALSE, req);  
    }<strong>  
    </strong>  

這個函數調用send_req,這個函數前面已經調用過,可是現在AVDTP的狀態不同,第一次調用AVDTP_SESSION_STATE_DISCONNECTED狀態,第二次調用爲AVDTP_SESSION_STATE_CONNECTED狀態;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

    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) {//第二次調用時,就不走這段函數  
            session->io = l2cap_connect(session);  
            if (!session->io) {  
                err = -EIO;  
                goto failed;  
            }  
            avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);  
        }  
      
        if (session->state < AVDTP_SESSION_STATE_CONNECTED ||//第二次調用也越過這段函數  
                session->req != NULL) {  
            queue_request(session, req, 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->transaction, AVDTP_MSG_TYPE_COMMAND,  
                    req->signal_id, req->data, req->data_size)) {//avdtp_send就是主要的操作  
            err = -EIO;  
            goto failed;  
        }  

4、avdtp_send的實現
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,  
                uint8_t message_type, uint8_t signal_id,  
                void *data, size_t len)  
{  
    …………       
    /* 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(sock, session->buf, session->omtu))  
        return FALSE;  
  
………………  
        cont.message_type = message_type;  
  
        memcpy(session->buf, &cont, sizeof(cont));  
        memcpy(session->buf + sizeof(cont), data + sent, to_copy);  
  
        if (!try_send(sock, session->buf, to_copy + sizeof(cont)))  
            return FALSE;  
  
        sent += to_copy;  
    }  
  
    return TRUE;  
} 

5、Try_sends函數的實現

static gboolean try_send(int sk, void *data, size_t len)  
{  
    int err;  
    do {  
        err = send(sk, data, len, 0);  
    } while (err < 0 && errno == EINTR);  
  
    if (err < 0) {  
        error("send: %s (%d)", strerror(errno), errno);  
        return FALSE;  
    } else if ((size_t) err != len) {  
        error("try_send: complete buffer not sent (%d/%zu bytes)",  
                                err, len);  
        return FALSE;  
    }  
  
    return TRUE;  
}





(二)、AVDTP_DISCOVER的命令發送流程如上圖所示;
avdtp是基於l2cap的,包括控制命令的發送和數據的發送都是l2cap的,所以建立一個發送控制命令的l2cap的socket,等這個socket建立起來以後,開始發送AVDPT_DISCOVER的請求;|
`AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
建立了一個l2cap的連接,等有數據過來的時候,就開始觸發邏輯,session_cb是一個非常重要的函數,這裏控制了整個連接的流程,我們下面會講,剩下的就是通過avdtp_send來發送一個AVDTP_DISCOVER的命令,這個命令的作用就是查看遠程設備看它支持那些sep(stream end point),也就是說是否支持source,sink等;
四、AVDTP_GET_CAPABILITIES命令發送(其他代碼流程比較類似)
如下圖所示:





這個圖在發送了avdtp discover命令以後,會被先前設立好的回調函數執行,裏面會把遠程設備的sep都加入到session的seps連邊裏面去,然後開始發送AVDTP_GET_CAPABILITIES命令了;
當收到遠端設備的回覆消息後觸發調用下面的邏輯:







在系列初始化、狀態設定之後,發送哦AVDTP_SET_CONFIGURATION
五、AVDTP_SET_CONFIGURATION命令發送



發送AVDTP_OPEN命令;
六、AVDTP_OPEN的處理流程
到這裏就表示已經確立了sep和caps,開始打開AVDTP了,如下:

數stream_setup_complete裏面會對先前的dbus消息進行回覆;
七、AVDTP_START命令發送





這裏發送AVDTP_START的命令,它的觸發是由客戶端引起的,比如aplay –Dbluetooth 2.wav的時候通過alsa提供的bluetooth的插件,daemonbluetoothd-service-audio通過socket(PF_LOCAL, SOCK_STREAM,0);建立起一個socket來監聽客戶端的接入,觸發server_cb的執行,在這裏accept客戶端,並設置監聽函數client_cb,當收到客戶端的啓動流播放命令的時候就開始調用avdtp_start函數來發送命令,注意這裏設置了一個回調函數a2dp_resume_complete,後面會被調用;當bluetoothd-service-audio收到了這個命令AVDTP_START的響應消息時執行下面的邏輯:




進程間傳遞文件描述符,內核層裏面的實現,通過socket發送這個文件描述符,在內核裏面把struct file信息傳遞給socket的peer端,它再取得一個空的fd把它和struct file關聯起來,於是就實現了文件描述符傳遞。


發佈了74 篇原創文章 · 獲贊 26 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章