keywords: bluetooth bluez A2DP, SINK, Sink_connect, Sink_disconnect, Sink_suspend, Sink_resume ,sink_is_connected, Sink_get_properties, AUDIO, DBUS
version: Based on android4.2 previous version BlueZ
Kernel: linux/linux3.08
System: android/android4.1.3.4
xubin341719 (Welcome reprint, please specify the author, please respect copyright thank you)
Welcome to correct mistakes, common learning, common progress!!
Android Bluetooth Introduction (a): Basic concepts and hardware interface
Android Bluetooth Introduction (ii): Android bluetooth® code Architecture and its UART to Rfcomm process
Android Bluetooth Introduction (III): Bluetooth scanning (scan) device analysis
Android Bluetooth Introduction (iv): A2DP Connect Process Analysis
First, A2dp_connect upper code flow
second, fromHCI Log SeeAVDTP Create a process
1,AVDTP L2cap Build Process
2,AVDTPthe relevant signaling processing processHCI the process in
DISCOVER \get_capabilities\set_configuration\open\start\suspend
Third, Audiosink function registration, and command processing flow
Avdtp_discover\avdtp_get_capabilities\avdtp_set_configuration\avdtp_open\avdtp_start: A series of control commands
(i), sink_connect create process
The overall flow is shown below
1, IDH.CODE\EXTERNAL\BLUETOOTH\BLUEZ\AUDIO\SINK.C
Static Dbusmessage *sink_connect (dbusconnection *conn,dbusmessage *msg, void *data) {...) if (!sink->session)//(1), If there is no AVDTP session, get AVDTP connection status; sink->session = Avdtp_get (&dev->src, &DEV->DST); if (!sink->session)// Related failed operation return btd_error_failed (MSG, "Unable to get a session"); if (Sink->connect | | sink->disconnect)//If you are connecting, disconnecting, Send busy message, return Btd_error_busy (msg), if (sink->stream_state >= avdtp_state_open)//If already open, send already connected message; return Btd_ Error_already_connected (msg), if (!sink_setup_stream (sink, NULL))//(2), create AVDTP stream, return btd_error_failed (MSG, " Failed to create a stream ");d Ev->auto_connect = false;pending = Sink->connect;pending->conn = Dbus_connection_ REF (conn);//(3), save client Dbus information; pending->msg = Dbus_message_ref (msg);D BG ("Stream creation in progress"); return NULL;}
(1), if there is no AVDTP session, get AVDTP connection status;
Sink->session = Avdtp_get (&dev->src, &DEV->DST); idh.code\external\bluetooth\hcidump\parser\ Avdtp.cstruct AVDTP *avdtp_get (bdaddr_t *src, bdaddr_t *dst) {..... session = Avdtp_get_internal (src, DST); .....................} Set session->state status in Avdtp_get_internal, session->state = avdtp_session_state_disconnected;
(2), create AVDTP stream;
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);//cannot be disconnected automatically; if (Avdtp_discover (sink->session, Discovery_complete, sink) < 0)//Call Avdtp_discover,discovery_ Complete is the callback function; 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);//Send Avdtp_discover command out 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 *buf Fer, 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);//This function is analyzed behind us;}
(3), Save the client Dbus information;
Pending->conn = DBUS_CONNECTION_REF (conn);p ending->msg = Dbus_message_ref (msg);
2, send_req create L2cap connection
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;i F (session->state = = avdtp_session_state_disconnected) {//If AVDTP is not connected, Session->io = L2cap_connect (session);// (1), create l2cap connection; 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) {//If AVDTP is not connected Queue_request (session, req, priority);//put the relevant parameters into the queue return 0;//back here, behind AVDTP Once the sock is established, the function is called again;}req->transaction = transaction++;transaction%= 16;/* fixme:should We retry to send if the buffer Was wasn't 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), send related commands err =-eio;goto failed;} ............}
(1), create L2CAP connection
The process of sink connect is essentially the process of establishing a AVDTP connection, AVDTP is based on L2cap, including the dispatch of control commands and the sending of data is l2cap, so this drawing represents a l2cap socket to set up a Send Control command, After the socket is set up, the request to send Avdpt_discover is started;
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; }
This function notice two points, 1), bt_io_connect;2), AVDTP_CONNECT_CB callback function;
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 (t ype) {Case bt_io_l2raw:err = l2cap_connect (sock, &OPTS.DST, 0, Opts.cid), break;//connections to different protocols, such as L2CPA, RFCOMM, Scocase 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;}
Implementation of L2cap_connect in Btio:
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); ELSEADDR.L2_PSM = Htobs (PSM); err = connect (sock, (struct sockaddr *) &addr, sizeof (addr));//Build Btproto_l2capif ( Err < 0 &&! (errno = = Eagain | | errno = = einprogress)) return Err;return 0;}
2), AVDTP_CONNECT_CB callback function
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) {//If in the connected state, DBG ("AVDTP imtu=%u, omtu=%u", SESSION->IMTU, SESSION->OMTU); session- >buf = g_malloc0 (SESSION->IMTU); Avdtp_set_state (session, avdtp_session_state_connected);//Set AVDTP status to connected state if (session->io_id) g_source_remove (session->io_id);/* This watch should is low priority since otherwise the * Conne CT callback might is dispatched before the session * callback if the kernel wakes us up at the same time for * them. This could happen if a headset are very quick in * sending the Start command after connecting the stream * Transport Channe L. */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);//Send Discoverreturn;.............}
3, Process_queue (session) send Discover command out
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);}
This function calls Send_req, this function has been called before, but now AVDTP state is different, the first call avdtp_session_state_disconnected State , the second call is
avdtp_session_state_connected State;
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;i F (session->state = = avdtp_session_state_disconnected) {//second call, do not go this function 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 | | The second call also crosses this segment of the function Session->req! = NULL) {queue_request (session, req, priority); return 0;} Req->transaction = transaction++;transaction%= 16;/* fixme:should We retry to send if the Bufferwas 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 is the main operation err =-eio;goto failed;}
4, implementation of 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) {......
5, the realization of try_sends function
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) ER r! = Len) {error ("Try_send:complete buffer not sent (%d/%zu bytes)", err, Len); return FALSE;} return TRUE;}
(ii), avdtp_discover the order of the sending process as shown;
AVDTP is based on the L2cap, including the control command and the sending of the data is l2cap, so the establishment of a command to send the L2CAP socket, and so on after the socket is established, to start sending Avdpt_discover request; |
' Avdtp_discover\avdtp_get_capabilities\avdtp_set_configuration\Avdtp_open\Avdtp_start:A series of CONTROL commands
Set up a l2cap connection, when there is data coming, it starts to trigger the logic, SESSION_CB is a very important function, here control the entire connection process, we will say below, the rest is to send a avdtp_ by avdtp_send Discover command, the function of this command is to check the remote device to see if it supports those Sep (stream end point), that is, whether to support source,sink, etc.;
iv. avdtp_get_capabilities Command Send (other code flow is similar)
As shown in the following:
After sending the AVDTP discover command, this figure will be executed by the previously established callback function, which will add the sep of the remote device to the SEPs of the session, and then start sending the avdtp_get_capabilities command;
The following logic is triggered when a reply message is received from the remote device:
After the series initialization, state setting, send Oh avdtp_set_configuration
V. avdtp_set_configuration command Send
Send Avdtp_open command;
Six, the Avdtp_open processing flow
This means that sep and caps have been established and the AVDTP has begun to open, as follows:
The number of stream_setup_complete inside will reply to the previous Dbus message;
Seven, Avdtp_start command send
Here send Avdtp_start command, its trigger is caused by the client, such as Aplay–dbluetooth 2.wav when the ALSA provided by the Bluetooth plug-in, Daemonbluetoothd-service-audio through Sockets (Pf_local, sock_stream,0), set up a socket to listen for client access, trigger SERVER_CB execution, Here accept the client, and set the Listener function CLIENT_CB, when receiving the client's start stream playback command, start calling the Avdtp_start function to send the command, notice here set a callback function A2dp_resume_complete, The following logic is executed when Bluetoothd-service-audio receives the response message for this command avdtp_start:
interprocess transfer file descriptor, the implementation of the kernel layer, through the socket to send this file descriptor, in the kernel to pass the struct file information to the peer of the socket, it then obtains an empty FD to associate it with the struct file, The file descriptor delivery is implemented.