Android bluetooth介绍(四): a2dp connect流程分析

来源:互联网 发布:淘宝交换友情链接 编辑:程序博客网 时间:2024/05/18 03:44

关键词:蓝牙blueZ  A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本:基于android4.2之前版本 bluez
内核:Linux/linux3.08
系统:Android/android4.1.3.4
作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)
欢迎指正错误,共同学习、共同进步!!

Android bluetooth介绍(一):基本概念及硬件接口
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
Android bluetooth介绍(四): a2dp connect流程分析

一、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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static DBusMessage *sink_connect(DBusConnection *conn,  
  2.                 DBusMessage *msg, void *data)  
  3. {  
  4. …………  
  5.     if (!sink->session)//(1)、如果没有AVDTP会话,获取AVDTP连接状态;  
  6.         sink->session = avdtp_get(&dev->src, &dev->dst);  
  7.   
  8.     if (!sink->session)//相关失败操作  
  9.         return btd_error_failed(msg, "Unable to get a session");  
  10.   
  11.     if (sink->connect || sink->disconnect)//如果正在连接、断开,发送busy消息;  
  12.         return btd_error_busy(msg);  
  13.   
  14.     if (sink->stream_state >= AVDTP_STATE_OPEN)//如果已经打开,发送已经连接消息;  
  15.         return btd_error_already_connected(msg);  
  16.   
  17.     if (!sink_setup_stream(sink, NULL))//(2)、创建AVDTP流;  
  18.         return btd_error_failed(msg, "Failed to create a stream");  
  19.   
  20.     dev->auto_connect = FALSE;  
  21.   
  22.     pending = sink->connect;  
  23.   
  24.     pending->conn = dbus_connection_ref(conn);//(3)、保存客户端dbus信息;  
  25.     pending->msg = dbus_message_ref(msg);  
  26.   
  27.     DBG("stream creation in progress");  
  28.   
  29.     return NULL;  
  30. }  

(1)、如果没有AVDTP会话,获取AVDTP连接状态;

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. sink->session = avdtp_get(&dev->src, &dev->dst);  
  2. idh.code\external\bluetooth\hcidump\parser\avdtp.c  
  3. struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst)  
  4. {  
  5. ………………  
  6.     session = avdtp_get_internal(src, dst);  
  7. ………………  
  8.   
  9. }  
  10. avdtp_get_internal 中设置 session->state状态,  
  11. session->state = AVDTP_SESSION_STATE_DISCONNECTED;  

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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)  
  2. {  
  3. …………  
  4.     avdtp_set_auto_disconnect(sink->session, FALSE);//不能自动断开;  
  5.   
  6.     if (avdtp_discover(sink->session, discovery_complete, sink) < 0)//调用avdtp_discover,  
  7. discovery_complete为回调函数;  
  8.         return FALSE;  
  9.   
  10.     sink->connect = g_new0(struct pending_request, 1);  
  11.   
  12.     return TRUE;  
  13. }  

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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,  
  2.             void *user_data)  
  3. {  
  4.     int err;  
  5.   
  6.     if (session->discov_cb)  
  7.         return -EBUSY;  
  8.   
  9.     if (session->seps) {  
  10.         session->discov_cb = cb;  
  11.         session->user_data = user_data;  
  12.         g_idle_add(process_discover, session);  
  13.         return 0;  
  14.     }  
  15.   
  16.     err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);  
  17. //发送AVDTP_DISCOVER命令出去  
  18.     if (err == 0) {  
  19.         session->discov_cb = cb;  
  20.         session->user_data = user_data;  
  21.     }  
  22.   
  23.     return err;  
  24. }  
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static int send_request(struct avdtp *session, gboolean priority,  
  2.             struct avdtp_stream *stream, uint8_t signal_id,  
  3.             void *buffer, size_t size)  
  4. {  
  5.     struct pending_req *req;  
  6.   
  7.     if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {  
  8.         DBG("Unable to send requests while aborting");  
  9.         return -EINVAL;  
  10.     }  
  11.   
  12.     req = g_new0(struct pending_req, 1);  
  13.     req->signal_id = signal_id;  
  14.     req->data = g_malloc(size);  
  15.     memcpy(req->data, buffer, size);  
  16.     req->data_size = size;  
  17.     req->stream = stream;  
  18.   
  19.     return send_req(session, priority, req);//这个函数我们后面分析;  
  20. }  

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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. pending->conn = dbus_connection_ref(conn);  
  2.     pending->msg = dbus_message_ref(msg);  

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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static int send_req(struct avdtp *session, gboolean priority,  
  2.             struct pending_req *req)  
  3. {  
  4.     static int transaction = 0;  
  5.     int err;  
  6.       
  7.     if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//如果AVDTP没有连接,  
  8.         session->io = l2cap_connect(session);//(1)、创建l2cap连接;  
  9.         if (!session->io) {  
  10.             err = -EIO;  
  11.             goto failed;  
  12.         }  
  13.         avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);  
  14.     }  
  15.   
  16.     if (session->state < AVDTP_SESSION_STATE_CONNECTED ||  
  17.             session->req != NULL) {//如果AVDTP没连接  
  18.         queue_request(session, req, priority);//把相关参数放入队列  
  19.         return 0;//在这里返回,后面AVDTP sock建立完成后,会再次调用这个函数;  
  20.     }  
  21.   
  22.     req->transaction = transaction++;  
  23.     transaction %= 16;  
  24.   
  25.     /* FIXME: Should we retry to send if the buffer  
  26.     was not totally sent or in case of EINTR? */  
  27.     if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,  
  28.                 req->signal_id, req->data, req->data_size)) {//(2)、发送相关命令  
  29.         err = -EIO;  
  30.         goto failed;  
  31.     }  
  32. …………  
  33. }  

(1)、创建l2cap连接
sink connect的过程本质上是建立一个avdtp 连接的过程,avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以这个图纸表示了建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送AVDPT_DISCOVER的请求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1.     session->io = l2cap_connect(session);  
  2. static GIOChannel *l2cap_connect(struct avdtp *session)  
  3. {  
  4.     GError *err = NULL;  
  5.     GIOChannel *io;  
  6.   
  7.     io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,  
  8.                 NULL, &err,  
  9.                 BT_IO_OPT_SOURCE_BDADDR, &session->server->src,  
  10.                 BT_IO_OPT_DEST_BDADDR, &session->dst,  
  11.                 BT_IO_OPT_PSM, AVDTP_PSM,  
  12.                 BT_IO_OPT_INVALID);  
  13.     if (!io) {  
  14.         error("%s", err->message);  
  15.         g_error_free(err);  
  16.         return NULL;  
  17.     }  
  18.   
  19.     return io;<strong>  
  20. }  
  21. </strong>  

这个函数中注意两点,1)、bt_io_connect;2)、avdtp_connect_cb回调函数;
1)、bt_io_connect
idh.code\external\bluetooth\bluez\btio\btio.c

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,  
  2.                 gpointer user_data, GDestroyNotify destroy,  
  3.                 GError **gerr, BtIOOption opt1, ...)  
  4. {  
  5.     …………  
  6.   
  7.     io = create_io(type, FALSE, &opts, gerr);  
  8.     if (io == NULL)  
  9.         return NULL;  
  10.     sock = g_io_channel_unix_get_fd(io);  
  11.     switch (type) {  
  12.     case BT_IO_L2RAW:  
  13.         err = l2cap_connect(sock, &opts.dst, 0, opts.cid);  
  14.         break;  
  15. //不同协议的连接,如L2CPA、RFCOMM、SCO  
  16.     case BT_IO_L2CAP:  
  17.         err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);  
  18.         break;  
  19.     case BT_IO_RFCOMM:  
  20.         err = rfcomm_connect(sock, &opts.dst, opts.channel);  
  21.         break;  
  22.     case BT_IO_SCO:  
  23.         err = sco_connect(sock, &opts.dst);  
  24.         break;  
  25. …………  
  26.     connect_add(io, connect, user_data, destroy);  
  27.   
  28.     return io;  
  29. }<strong>  
  30. </strong>  

Btio中l2cap_connect的实现:
idh.code\external\bluetooth\bluez\btio\btio.c

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static int l2cap_connect(int sock, const bdaddr_t *dst,  
  2.                     uint16_t psm, uint16_t cid)  
  3. {  
  4.     int err;  
  5.     struct sockaddr_l2 addr;  
  6.   
  7.     memset(&addr, 0, sizeof(addr));  
  8.     addr.l2_family = AF_BLUETOOTH;  
  9.     bacpy(&addr.l2_bdaddr, dst);  
  10.     if (cid)  
  11.         addr.l2_cid = htobs(cid);  
  12.     else  
  13.         addr.l2_psm = htobs(psm);  
  14.   
  15.     err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAP  
  16.     if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))  
  17.         return err;  
  18.   
  19.     return 0;  
  20. }  

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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)  
  2. {  
  3. ………………  
  4.     if (session->state == AVDTP_SESSION_STATE_CONNECTING) {//如果处于正在连接状态;  
  5.         DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);  
  6.   
  7.         session->buf = g_malloc0(session->imtu);  
  8.         avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);//设置AVDTP状态为已经连接状态;  
  9.   
  10.         if (session->io_id)  
  11.             g_source_remove(session->io_id);  
  12.   
  13.         /* This watch should be low priority since otherwise the  
  14.          * connect callback might be dispatched before the session  
  15.          * callback if the kernel wakes us up at the same time for  
  16.          * them. This could happen if a headset is very quick in  
  17.          * sending the Start command after connecting the stream  
  18.          * transport channel.  
  19.          */  
  20.         session->io_id = g_io_add_watch_full(chan,  
  21.                         G_PRIORITY_LOW,  
  22.                         G_IO_IN | G_IO_ERR | G_IO_HUP  
  23.                         | G_IO_NVAL,  
  24.                         (GIOFunc) session_cb, session,  
  25.                         NULL);  
  26.   
  27. ………………  
  28.     process_queue(session);//发送DISCOVER  
  29.   
  30.     return;  
  31. …………  
  32. }  

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

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static int process_queue(struct avdtp *session)  
  2. {  
  3. …………  
  4.     *queue = g_slist_remove(*queue, req);  
  5.   
  6.     return send_req(session, FALSE, req);  
  7. }<strong>  
  8. </strong>  

这个函数调用send_req,这个函数前面已经调用过,可是现在AVDTP的状态不同,第一次调用AVDTP_SESSION_STATE_DISCONNECTED状态,第二次调用为

AVDTP_SESSION_STATE_CONNECTED状态;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static int send_req(struct avdtp *session, gboolean priority,  
  2.             struct pending_req *req)  
  3. {  
  4.     static int transaction = 0;  
  5.     int err;  
  6.       
  7.     if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//第二次调用时,就不走这段函数  
  8.         session->io = l2cap_connect(session);  
  9.         if (!session->io) {  
  10.             err = -EIO;  
  11.             goto failed;  
  12.         }  
  13.         avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);  
  14.     }  
  15.   
  16.     if (session->state < AVDTP_SESSION_STATE_CONNECTED ||//第二次调用也越过这段函数  
  17.             session->req != NULL) {  
  18.         queue_request(session, req, priority);  
  19.         return 0;  
  20.     }  
  21.   
  22.     req->transaction = transaction++;  
  23.     transaction %= 16;  
  24.   
  25.     /* FIXME: Should we retry to send if the buffer  
  26.     was not totally sent or in case of EINTR? */  
  27.     if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,  
  28.                 req->signal_id, req->data, req->data_size)) {//avdtp_send就是主要的操作  
  29.         err = -EIO;  
  30.         goto failed;  
  31.     }  

4、avdtp_send的实现
idh.code\external\bluetooth\hcidump\parser\avdtp.c

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,  
  2.                 uint8_t message_type, uint8_t signal_id,  
  3.                 void *data, size_t len)  
  4. {  
  5.     …………       
  6.     /* Send the start packet */  
  7.     memset(&start, 0, sizeof(start));  
  8.     start.transaction = transaction;  
  9.     start.packet_type = AVDTP_PKT_TYPE_START;  
  10.     start.message_type = message_type;  
  11.     start.no_of_packets = cont_fragments + 1;  
  12.     start.signal_id = signal_id;  
  13.   
  14.     memcpy(session->buf, &start, sizeof(start));  
  15.     memcpy(session->buf + sizeof(start), data,  
  16.                     session->omtu - sizeof(start));  
  17.   
  18.     if (!try_send(sock, session->buf, session->omtu))  
  19.         return FALSE;  
  20.   
  21. ………………  
  22.         cont.message_type = message_type;  
  23.   
  24.         memcpy(session->buf, &cont, sizeof(cont));  
  25.         memcpy(session->buf + sizeof(cont), data + sent, to_copy);  
  26.   
  27.         if (!try_send(sock, session->buf, to_copy + sizeof(cont)))  
  28.             return FALSE;  
  29.   
  30.         sent += to_copy;  
  31.     }  
  32.   
  33.     return TRUE;  
  34. <strong>  
  35. </strong>  

5、Try_sends函数的实现

[html] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. static gboolean try_send(int sk, void *data, size_t len)  
  2. {  
  3.     int err;  
  4.     do {  
  5.         err = send(sk, data, len, 0);  
  6.     } while (err < 0 && errno == EINTR);  
  7.   
  8.     if (err < 0) {  
  9.         error("send: %s (%d)", strerror(errno), errno);  
  10.         return FALSE;  
  11.     } else if ((size_t) err != len) {  
  12.         error("try_send: complete buffer not sent (%d/%zu bytes)",  
  13.                                 err, len);  
  14.         return FALSE;  
  15.     }  
  16.   
  17.     return TRUE;  
  18. }<strong>  
  19. </strong>  

(二)、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关联起来,于是就实现了文件描述符传递。

0 0
原创粉丝点击