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


    关键词:蓝牙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_CAPABILITIESSET_CONFIGURATIONOPENSTARTSUSPEND
    三、audiosink函数注冊、及命令处理流程
    AVDTP_DISCOVERAVDTP_GET_CAPABILITIESAVDTP_SET_CONFIGURATIONAVDTP_OPENAVDTP_START:等一系列控制命令
    (一)、sink_connect创建流程
            总体流程例如以下所看到的


    1、idh.codeexternalluetoothluezaudiosink.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)、保存clientdbus信息。
    	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.codeexternalluetoothhcidumpparseravdtp.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.codeexternalluetoothhcidumpparseravdtp.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.codeexternalluetoothhcidumpparseravdtp.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.codeexternalluetoothhcidumpparseravdtp.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)、保存clientdbus信息;

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

    2、send_req 创建L2CAP连接
    idh.codeexternalluetoothhcidumpparseravdtp.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.codeexternalluetoothhcidumpparseravdtp.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;
    }
    

    这个函数中注意两点,1)、bt_io_connect;2)、avdtp_connect_cb回调函数。
    1)、bt_io_connect
    idh.codeexternalluetoothlueztiotio.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;
    }
    

    Btio中l2cap_connect的实现:
    idh.codeexternalluetoothlueztiotio.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.codeexternalluetoothhcidumpparseravdtp.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.codeexternalluetoothhcidumpparseravdtp.c

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

    这个函数调用send_req,这个函数前面已经调用过,但是如今AVDTP的状态不同。第一次调用AVDTP_SESSION_STATE_DISCONNECTED状态,第二次调用为

    AVDTP_SESSION_STATE_CONNECTED状态。
    idh.codeexternalluetoothhcidumpparseravdtp.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.codeexternalluetoothhcidumpparseravdtp.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_DISCOVERAVDTP_GET_CAPABILITIESAVDTP_SET_CONFIGURATIONAVDTP_OPENAVDTP_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的命令,它的触发是由client引起的,比方aplay –Dbluetooth 2.wav的时候通过alsa提供的bluetooth的插件。daemonbluetoothd-service-audio通过socket(PF_LOCAL, SOCK_STREAM,0);建立起一个socket来监听client的接入,触发server_cb的运行。在这里acceptclient,并设置监听函数client_cb。当收到client的启动流播放命令的时候就開始调用avdtp_start函数来发送命令。注意这里设置了一个回调函数a2dp_resume_complete,后面会被调用;当bluetoothd-service-audio收到了这个命令AVDTP_START的响应消息时运行以下的逻辑:

    进程间传递文件描写叙述符,内核层里面的实现,通过socket发送这个文件描写叙述符,在内核里面把struct file信息传递给socket的peer端。它再取得一个空的fd把它和struct file关联起来。于是就实现了文件描写叙述符传递。







    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    C#动态显示时间
    死锁问题
    TCP_NODELAY算法使用事项
    二叉搜索树的后序遍历
    从上到下打印二叉树
    栈的压入、弹出序列
    包含min函数的栈
    顺时针打印矩阵
    树的子结构
    合并两个排序链表
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4624639.html
Copyright © 2020-2023  润新知