Android bluetooth介绍(四): a2dp connect流程分析
2014-11-13 16:55
621 查看
转自 http://blog.csdn.net/xubin341719/article/details/38640115
关键词:蓝牙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中看***DTP 创建过程
1、***DTP l2cap建立过程
2、***DTP相关信令处理流程在HCI 中的流程
DISCOVER \GET_CAPABILITIES\SET_CONFIGURATION\OPEN\START\SUSPEND
三、audiosink函数注册、及命令处理流程
***DTP_DISCOVER\***DTP_GET_CAPABILITIES\***DTP_SET_CONFIGURATION\***DTP_OPEN\***DTP_START:等一系列控制命令
(一)、sink_connect创建流程
整体流程如下所示
1、idh.code\external\bluetooth\bluez\audio\sink.c
[html] view
plaincopy
static DBusMessage *sink_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
…………
if (!sink->session)//(1)、如果没有***DTP会话,获取***DTP连接状态;
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 >= ***DTP_STATE_OPEN)//如果已经打开,发送已经连接消息;
return btd_error_already_connected(msg);
if (!sink_setup_stream(sink, NULL))//(2)、创建***DTP流;
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)、如果没有***DTP会话,获取***DTP连接状态;
[html] view
plaincopy
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 = ***DTP_SESSION_STATE_DISCONNECTED;
(2)、创建***DTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
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
[html] view
plaincopy
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, ***DTP_DISCOVER, NULL, 0);
//发送***DTP_DISCOVER命令出去
if (err == 0) {
session->discov_cb = cb;
session->user_data = user_data;
}
return err;
}
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
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 != ***DTP_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信息;
[html] view
plaincopy
pending->conn = dbus_connection_ref(conn);
pending->msg = dbus_message_ref(msg);
2、send_req 创建L2CAP连接
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
static int send_req(struct avdtp *session, gboolean priority,
struct pending_req *req)
{
static int transaction = 0;
int err;
if (session->state == ***DTP_SESSION_STATE_DISCONNECTED) {//如果***DTP没有连接,
session->io = l2cap_connect(session);//(1)、创建l2cap连接;
if (!session->io) {
err = -EIO;
goto failed;
}
avdtp_set_state(session, ***DTP_SESSION_STATE_CONNECTING);
}
if (session->state < ***DTP_SESSION_STATE_CONNECTED ||
session->req != NULL) {//如果***DTP没连接
queue_request(session, req, priority);//把相关参数放入队列
return 0;//在这里返回,后面***DTP 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, ***DTP_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建立起来以后,开始发送***DPT_DISCOVER的请求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
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, ***DTP_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
[html] view
plaincopy
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
[html] view
plaincopy
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
[html] view
plaincopy
static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
{
………………
if (session->state == ***DTP_SESSION_STATE_CONNECTING) {//如果处于正在连接状态;
DBG("***DTP imtu=%u, omtu=%u", session->imtu, session->omtu);
session->buf = g_malloc0(session->imtu);
avdtp_set_state(session, ***DTP_SESSION_STATE_CONNECTED);//设置***DTP状态为已经连接状态;
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
[html] view
plaincopy
static int process_queue(struct avdtp *session)
{
…………
*queue = g_slist_remove(*queue, req);
return send_req(session, FALSE, req);
}<strong>
</strong>
这个函数调用send_req,这个函数前面已经调用过,可是现在***DTP的状态不同,第一次调用***DTP_SESSION_STATE_DISCONNECTED状态,第二次调用为
***DTP_SESSION_STATE_CONNECTED状态;
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
static int send_req(struct avdtp *session, gboolean priority,
struct pending_req *req)
{
static int transaction = 0;
int err;
if (session->state == ***DTP_SESSION_STATE_DISCONNECTED) {//第二次调用时,就不走这段函数
session->io = l2cap_connect(session);
if (!session->io) {
err = -EIO;
goto failed;
}
avdtp_set_state(session, ***DTP_SESSION_STATE_CONNECTING);
}
if (session->state < ***DTP_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, ***DTP_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
[html] view
plaincopy
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 = ***DTP_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;
} <strong>
</strong>
5、Try_sends函数的实现
[html] view
plaincopy
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;
}<strong>
</strong>
(二)、***DTP_DISCOVER的命令发送流程如上图所示;
avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送***DPT_DISCOVER的请求;|
`***DTP_DISCOVER\***DTP_GET_CAPABILITIES\***DTP_SET_CONFIGURATION\***DTP_OPEN\***DTP_START:等一系列控制命令
建立了一个l2cap的连接,等有数据过来的时候,就开始触发逻辑,session_cb是一个非常重要的函数,这里控制了整个连接的流程,我们下面会讲,剩下的就是通过avdtp_send来发送一个***DTP_DISCOVER的命令,这个命令的作用就是查看远程设备看它支持那些sep(stream end point),也就是说是否支持source,sink等;
四、***DTP_GET_CAPABILITIES命令发送(其他代码流程比较类似)
如下图所示:
这个图在发送了avdtp discover命令以后,会被先前设立好的回调函数执行,里面会把远程设备的sep都加入到session的seps连边里面去,然后开始发送***DTP_GET_CAPABILITIES命令了;
当收到远端设备的回复消息后触发调用下面的逻辑:
在系列初始化、状态设定之后,发送哦***DTP_SET_CONFIGURATION
五、***DTP_SET_CONFIGURATION命令发送
发送***DTP_OPEN命令;
六、***DTP_OPEN的处理流程
到这里就表示已经确立了sep和caps,开始打开***DTP了,如下:
数stream_setup_complete里面会对先前的dbus消息进行回复;
七、***DTP_START命令发送
这里发送***DTP_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收到了这个命令***DTP_START的响应消息时执行下面的逻辑:
进程间传递文件描述符,内核层里面的实现,通过socket发送这个文件描述符,在内核里面把struct file信息传递给socket的peer端,它再取得一个空的fd把它和struct file关联起来,于是就实现了文件描述符传递。
关键词:蓝牙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中看***DTP 创建过程
1、***DTP l2cap建立过程
2、***DTP相关信令处理流程在HCI 中的流程
DISCOVER \GET_CAPABILITIES\SET_CONFIGURATION\OPEN\START\SUSPEND
三、audiosink函数注册、及命令处理流程
***DTP_DISCOVER\***DTP_GET_CAPABILITIES\***DTP_SET_CONFIGURATION\***DTP_OPEN\***DTP_START:等一系列控制命令
(一)、sink_connect创建流程
整体流程如下所示
1、idh.code\external\bluetooth\bluez\audio\sink.c
[html] view
plaincopy
static DBusMessage *sink_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
…………
if (!sink->session)//(1)、如果没有***DTP会话,获取***DTP连接状态;
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 >= ***DTP_STATE_OPEN)//如果已经打开,发送已经连接消息;
return btd_error_already_connected(msg);
if (!sink_setup_stream(sink, NULL))//(2)、创建***DTP流;
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)、如果没有***DTP会话,获取***DTP连接状态;
[html] view
plaincopy
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 = ***DTP_SESSION_STATE_DISCONNECTED;
(2)、创建***DTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
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
[html] view
plaincopy
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, ***DTP_DISCOVER, NULL, 0);
//发送***DTP_DISCOVER命令出去
if (err == 0) {
session->discov_cb = cb;
session->user_data = user_data;
}
return err;
}
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
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 != ***DTP_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信息;
[html] view
plaincopy
pending->conn = dbus_connection_ref(conn);
pending->msg = dbus_message_ref(msg);
2、send_req 创建L2CAP连接
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
static int send_req(struct avdtp *session, gboolean priority,
struct pending_req *req)
{
static int transaction = 0;
int err;
if (session->state == ***DTP_SESSION_STATE_DISCONNECTED) {//如果***DTP没有连接,
session->io = l2cap_connect(session);//(1)、创建l2cap连接;
if (!session->io) {
err = -EIO;
goto failed;
}
avdtp_set_state(session, ***DTP_SESSION_STATE_CONNECTING);
}
if (session->state < ***DTP_SESSION_STATE_CONNECTED ||
session->req != NULL) {//如果***DTP没连接
queue_request(session, req, priority);//把相关参数放入队列
return 0;//在这里返回,后面***DTP 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, ***DTP_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建立起来以后,开始发送***DPT_DISCOVER的请求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
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, ***DTP_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
[html] view
plaincopy
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
[html] view
plaincopy
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
[html] view
plaincopy
static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
{
………………
if (session->state == ***DTP_SESSION_STATE_CONNECTING) {//如果处于正在连接状态;
DBG("***DTP imtu=%u, omtu=%u", session->imtu, session->omtu);
session->buf = g_malloc0(session->imtu);
avdtp_set_state(session, ***DTP_SESSION_STATE_CONNECTED);//设置***DTP状态为已经连接状态;
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
[html] view
plaincopy
static int process_queue(struct avdtp *session)
{
…………
*queue = g_slist_remove(*queue, req);
return send_req(session, FALSE, req);
}<strong>
</strong>
这个函数调用send_req,这个函数前面已经调用过,可是现在***DTP的状态不同,第一次调用***DTP_SESSION_STATE_DISCONNECTED状态,第二次调用为
***DTP_SESSION_STATE_CONNECTED状态;
idh.code\external\bluetooth\hcidump\parser\avdtp.c
[html] view
plaincopy
static int send_req(struct avdtp *session, gboolean priority,
struct pending_req *req)
{
static int transaction = 0;
int err;
if (session->state == ***DTP_SESSION_STATE_DISCONNECTED) {//第二次调用时,就不走这段函数
session->io = l2cap_connect(session);
if (!session->io) {
err = -EIO;
goto failed;
}
avdtp_set_state(session, ***DTP_SESSION_STATE_CONNECTING);
}
if (session->state < ***DTP_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, ***DTP_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
[html] view
plaincopy
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 = ***DTP_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;
} <strong>
</strong>
5、Try_sends函数的实现
[html] view
plaincopy
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;
}<strong>
</strong>
(二)、***DTP_DISCOVER的命令发送流程如上图所示;
avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送***DPT_DISCOVER的请求;|
`***DTP_DISCOVER\***DTP_GET_CAPABILITIES\***DTP_SET_CONFIGURATION\***DTP_OPEN\***DTP_START:等一系列控制命令
建立了一个l2cap的连接,等有数据过来的时候,就开始触发逻辑,session_cb是一个非常重要的函数,这里控制了整个连接的流程,我们下面会讲,剩下的就是通过avdtp_send来发送一个***DTP_DISCOVER的命令,这个命令的作用就是查看远程设备看它支持那些sep(stream end point),也就是说是否支持source,sink等;
四、***DTP_GET_CAPABILITIES命令发送(其他代码流程比较类似)
如下图所示:
这个图在发送了avdtp discover命令以后,会被先前设立好的回调函数执行,里面会把远程设备的sep都加入到session的seps连边里面去,然后开始发送***DTP_GET_CAPABILITIES命令了;
当收到远端设备的回复消息后触发调用下面的逻辑:
在系列初始化、状态设定之后,发送哦***DTP_SET_CONFIGURATION
五、***DTP_SET_CONFIGURATION命令发送
发送***DTP_OPEN命令;
六、***DTP_OPEN的处理流程
到这里就表示已经确立了sep和caps,开始打开***DTP了,如下:
数stream_setup_complete里面会对先前的dbus消息进行回复;
七、***DTP_START命令发送
这里发送***DTP_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收到了这个命令***DTP_START的响应消息时执行下面的逻辑:
进程间传递文件描述符,内核层里面的实现,通过socket发送这个文件描述符,在内核里面把struct file信息传递给socket的peer端,它再取得一个空的fd把它和struct file关联起来,于是就实现了文件描述符传递。
相关文章推荐
- Android bluetooth介绍(四): a2dp connect流程分析
- Android bluetooth介绍(四): a2dp connect流程分析
- Android bluetooth介绍(四): a2dp connect流程分析
- Android bluetooth介绍(四): a2dp connect流程分析
- Android bluetooth介绍(四): a2dp connect流程分析
- android呼叫流程分析--整体介绍
- 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程
- Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
- Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
- Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
- Android bluetooth 流程分析(一)createBond
- [Network]Android N 新wifi connect&auto connect流程分析
- Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
- Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
- Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
- Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
- Android Wi-Fi connect & auto connect流程分析(Android 7.0)
- Android: Bluetooth Input 连接流程分析
- Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
- Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程