⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 a2dpd_output_sco.c

📁 linux蓝牙剖面实现
💻 C
字号:
/***  BlueZ - Bluetooth protocol stack for Linux**  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.org>***  This library is free software; you can redistribute it and/or*  modify it under the terms of the GNU Lesser General Public*  License as published by the Free Software Foundation; either*  version 2.1 of the License, or (at your option) any later version.**  This library is distributed in the hope that it will be useful,*  but WITHOUT ANY WARRANTY; without even the implied warranty of*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU*  Lesser General Public License for more details.**  You should have received a copy of the GNU Lesser General Public*  License along with this library; if not, write to the Free Software*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA**/#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <malloc.h>#include <signal.h>#include <time.h>#include <fcntl.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/select.h>#include <sys/poll.h>#include <bluetooth/bluetooth.h>#include <bluetooth/rfcomm.h>#include <bluetooth/l2cap.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <bluetooth/sco.h>#include "a2dpd_output_sco.h"#include "a2dpd_protocol.h"#include "a2dpd_tools.h"#include "a2dpd_ipc.h"enum {	SCO_STATE_DISCONNECTED,	SCO_STATE_DISCONNECTING,	SCO_STATEX_SDP_CONNECTING,	SCO_STATEX_SDP_CONNECTING_WAIT,	SCO_STATE_RFC_CONNECTING,	SCO_STATE_RFC_CONNECTING_WAIT,	SCO_STATE_CONNECTED,	SCO_STATE_SCO_CONNECTING,	SCO_STATE_SCO_CONNECTED,};#define sco_set_state(new_state) do { DBG("State %s", #new_state); sco->state = new_state; } while (0)typedef struct sco {	int state;	int sk_rfc;	int sk_sco;	int sk_sdp;	bdaddr_t dst;	char bdaddr[18];	sdp_session_t *sdp_session;} sco_t;static int bw_in = 0;static int bw_out = 0;static struct timeval bandwith_timer = {0};int sco_transfer_raw(LPSCO sco, const char *pcm_buffer, int pcm_buffer_size){	int result = -1;	struct timeval now, interval;	now.tv_sec = now.tv_usec = interval.tv_sec = interval.tv_usec = 0;	if(bandwith_timer.tv_sec==0)		(void)gettimeofday(&bandwith_timer, 0);	(void)gettimeofday(&now, 0);	timersub(&now, &bandwith_timer, &interval);	if(interval.tv_sec>0) {		//DBG("bw(in=%d, out=%d)", bw_in, bw_out);		(void)gettimeofday(&bandwith_timer, 0);		bw_out=0;		bw_in=0;	}	bw_out += pcm_buffer_size;	if(sco->state == SCO_STATE_SCO_CONNECTED) {		result = send_socket(sco->sk_sco, (char*)pcm_buffer, pcm_buffer_size);		switch (result) {		default:			//DBG("result(%d)", result);			break;		}	} else {		result = pcm_buffer_size;		//DBG("SCO not connected");	}	return result;}static sco_t *sco_alloc(void){	sco_t *sco;	sco = malloc(sizeof(*sco));	if (!sco)		return NULL;	memset(sco, 0, sizeof(*sco));	return sco;}static void sco_disconnect(LPSCO sco){	if(sco->state != SCO_STATE_DISCONNECTED) {		DBG("Disconnecting");		// Free data		if(sco->sdp_session)			(void)sdp_close(sco->sdp_session);		sco->sdp_session = NULL;		sco->sk_sdp = 0;			close_socket(&sco->sk_rfc);		close_socket(&sco->sk_sco);			// Reset to init state		sco_set_state(SCO_STATE_DISCONNECTED);	}}static void sco_free(LPSCO sco){	DBG("Disconnecting");	sco_disconnect(sco);	DBG("(sco = %p)", sco);	free(sco);}void sco_init(void){	bandwith_timer.tv_sec = bandwith_timer.tv_usec = 0;}void sco_exit(void){}static uint8_t get_rfcomm_channel(LPSCO sco){	/* Retrieving sdp info */	uint32_t range = 0x0000ffff;	sdp_list_t *attrid=NULL, *search=NULL, *seq=NULL, *next=NULL;	uuid_t group;	int searchresult;	uint8_t channel = 0;	if(sco->sdp_session) {		DBG("Service search");		sdp_uuid16_create(&group, HEADSET_SVCLASS_ID);		// Remove non blocking attribute		(void)fcntl(sco->sdp_session->sock, F_SETFL, fcntl(sco->sdp_session->sock, F_GETFL, 0)&~O_NONBLOCK);		search = sdp_list_append(0, &group);		attrid = sdp_list_append(0, &range);		searchresult = sdp_service_search_attr_req(sco->sdp_session, search, SDP_ATTR_REQ_RANGE, attrid, &seq);		sdp_list_free(attrid, NULL);		sdp_list_free(search, NULL);		if ((searchresult==0) && (seq!=NULL)) {			DBG("Parsing results");			for (; seq; seq = next) {				sdp_record_t *rec = (sdp_record_t *) seq->data;				sdp_list_t *list = 0;				DBG("Record");				if (sdp_get_access_protos(rec, &list) == 0) {					channel = (uint8_t)sdp_get_proto_port(list, RFCOMM_UUID);					DBG("Found %d", (int)channel);				}				next = seq->next;				free(seq);				sdp_record_free(rec);			}		} else {			DBG("Service search failed (result=%d, seq=%p)", searchresult, seq);		}		(void)sdp_close(sco->sdp_session);		sco->sdp_session = NULL;	} else {		DBG("Invalid SDP Session");	}	return channel;}LPSCO sco_new(char *bdaddr){	LPSCO sco = NULL;	DBG("");	sco = sco_alloc();	if (!sco) {		DBG("Can't allocate");		return NULL;	}	sco_set_state(SCO_STATE_DISCONNECTED);	sco_set_dst_addr(sco, bdaddr);	return sco;}void sco_destroy(LPSCO *sco){	DBG("sco = %p", sco);	sco_free(*sco);	*sco = NULL;}int sco_state_connect( LPSCO sco){	if(strlen(sco->bdaddr)==0)		return -1;	switch(sco->state) {	case SCO_STATE_DISCONNECTED:	sco_set_state(SCO_STATEX_SDP_CONNECTING);					break;	default:                       return -1;	}	return 0;}int sco_is_connected(LPSCO sco){	return(sco->state == SCO_STATE_SCO_CONNECTED);}int sco_is_connecting(LPSCO sco){	return(sco->state != SCO_STATE_DISCONNECTED);}void sco_state_disconnect(LPSCO sco){	if(sco->state != SCO_STATE_DISCONNECTED) {		switch(sco->state) {		case SCO_STATE_DISCONNECTED:			break;		default:			DBG("Disconnection asked");			sco_set_state(SCO_STATE_DISCONNECTING);			break;		}	} else {		DBG("Filtering state : already disconnected");	}}void sco_state_startstream(LPSCO sco){	sco=sco;	//FIXME}void sco_state_suspend(LPSCO sco){	sco=sco;	//FIXME}void sco_set_dst_addr(LPSCO sco, char* bdaddr){	if((bdaddr!=NULL) && (bdaddr[0]!='\0')) {		strncpy(sco->bdaddr, bdaddr, sizeof(sco->bdaddr));		sco->bdaddr[sizeof(sco->bdaddr)-1] = '\0';		(void)str2ba(sco->bdaddr, &sco->dst);	} else {		sco->bdaddr[0] = '\0';	}	sco_state_disconnect(sco);}static int sco_connect_audio(LPSCO sco){	struct sockaddr_sco addr;	// Start async connection to channel	sco->sk_sco = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);	(void)fcntl(sco->sk_sco, F_SETFL, O_NONBLOCK);	memset(&addr, 0, sizeof(addr));	addr.sco_family = AF_BLUETOOTH;	bacpy(&addr.sco_bdaddr, &sco->dst);	if((connect(sco->sk_sco, (struct sockaddr *)&addr, (int)sizeof(addr)) < 0)  && ((errno == EAGAIN) || (errno == EINPROGRESS))) {		sco_set_state(SCO_STATE_SCO_CONNECTING);	}	return 0;}static int sco_handle_sco_stream(LPSCO sco, char* lpFrame, size_t iSize, ssize_t* received){	if(poll_accept(sco->sk_sco, 0)) {		*received = recv(sco->sk_sco, lpFrame, iSize, MSG_NOSIGNAL);		if(*received>=0) {			bw_in += *received;			// DBG("Received %d bytes", received);		} else {			DBG("Received Failed");		}	}	return 0;}static int sco_handle_rfcomm_commands(LPSCO sco){	char lpFrame[512];	if(poll_accept(sco->sk_rfc, 0)) {		ssize_t received = recv(sco->sk_rfc, lpFrame, sizeof(lpFrame), MSG_NOSIGNAL);		if(received>=0) {			lpFrame[received] = '\0';			DBG("Received %s", lpFrame);			if(strncmp(lpFrame, "AT+CLIP=1", 9)==0) {				(void)send_socket(sco->sk_rfc, "\r\nOK\r\n", 6);			} else if(strncmp(lpFrame, "AT+VGS", 6)==0) {				(void)send_socket(sco->sk_rfc, "\r\nOK\r\n", 6);			} else if(strncmp(lpFrame, "AT+CKPD", 7)==0) {				(void)send_socket(sco->sk_rfc, "\r\nOK\r\n", 6);			} else {				(void)send_socket(sco->sk_rfc, "\r\nERROR\r\n", 9);			}		} else {			DBG("Received Failed");		}	}	return 0;}int sco_state_machine(LPSCO sco, char* lpFrame, size_t iSize, ssize_t* received){	struct pollfd pollfds[1];	int errcode = 0;	size_t opt_size = sizeof(errcode);	uint8_t channel = 0;	switch(sco->state) {		case SCO_STATE_DISCONNECTED:			break;		case SCO_STATE_DISCONNECTING:			sco_disconnect(sco);			break;		case SCO_STATEX_SDP_CONNECTING:			if((sco->bdaddr[0] != '\0') && (sco->sdp_session==NULL))			{				sco->sdp_session = sdp_connect_async(&sco->dst);				if(sco->sdp_session != NULL) {					sco->sk_sdp = sdp_get_socket(sco->sdp_session);					sco_set_state(SCO_STATEX_SDP_CONNECTING_WAIT);				} else {					sco_set_state(SCO_STATE_DISCONNECTING);				}			} else {				sco_set_state(SCO_STATE_DISCONNECTING);			}			break;		case SCO_STATEX_SDP_CONNECTING_WAIT:			// If SDP connection is established			memset(pollfds, 0, sizeof(pollfds));			pollfds[0].fd = sco->sk_sdp;			pollfds[0].events = POLLOUT | POLLERR | POLLHUP;			pollfds[0].revents = 0;			if(poll(pollfds, 1, 0)>0) {				DBG("SDP connection terminated");				if((getsockopt(sco->sk_sdp, SOL_SOCKET, SO_ERROR, &errcode, &opt_size) == 0)				&& (errcode == 0)) {					sco_set_state(SCO_STATE_RFC_CONNECTING);				} else {					errno = errcode;					DBG("SDP connection failed");					sco_set_state(SCO_STATE_DISCONNECTING);				}			}			break;		case SCO_STATE_RFC_CONNECTING:			channel = get_rfcomm_channel(sco);			DBG("Found channel %d", (int)channel);			// Free sdp data			if(sco->sdp_session)				(void)sdp_close(sco->sdp_session);			sco->sdp_session = NULL;			// Retrieve channel			if(channel>0) {				struct sockaddr_rc addr;				// Start async connection to channel				sco->sk_rfc = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);				DBG("Socket is %d", sco->sk_rfc);				if(fcntl(sco->sk_rfc, F_SETFL, O_NONBLOCK)<0)					DBG("Failed to set non blocking socket %d", sco->sk_rfc);				memset(&addr, 0, sizeof(addr));				addr.rc_family = AF_BLUETOOTH;				bacpy(&addr.rc_bdaddr, &sco->dst);				addr.rc_channel = channel;				if((connect(sco->sk_rfc, (struct sockaddr *)&addr, (int)sizeof(addr)) < 0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) {					sco_set_state(SCO_STATE_RFC_CONNECTING_WAIT);				} else {					DBG("Connection failed");					sco_set_state(SCO_STATE_DISCONNECTING);				}			} else {				sco_set_state(SCO_STATE_DISCONNECTING);			}			break;		case SCO_STATE_RFC_CONNECTING_WAIT:			// Poll rfc socket			memset(pollfds, 0, sizeof(pollfds));			pollfds[0].fd = sco->sk_rfc;			pollfds[0].events = POLLOUT | POLLERR | POLLHUP;			pollfds[0].revents = 0;			if(poll(pollfds, 1, 0)>0) {				if((getsockopt(sco->sk_rfc, SOL_SOCKET, SO_ERROR, &errcode, &opt_size) == 0)				&& (errcode == 0)) {					sco_set_state(SCO_STATE_CONNECTED);				} else {					errno = errcode;					DBG("RFC connection failed");					sco_set_state(SCO_STATE_DISCONNECTING);				}			}			break;		case SCO_STATE_CONNECTED:			// Start accepting SCO connections?			(void)sco_connect_audio(sco);			(void)sco_handle_rfcomm_commands(sco);			break;		case SCO_STATE_SCO_CONNECTING:			memset(pollfds, 0, sizeof(pollfds));			pollfds[0].fd = sco->sk_sco;			// Poll rfc socket			pollfds[0].events = POLLOUT | POLLERR | POLLHUP;			pollfds[0].revents = 0;			if(poll(pollfds, 1, 0)>0) {				if((getsockopt(sco->sk_sco, SOL_SOCKET, SO_ERROR, &errcode, &opt_size) == 0)				&& (errcode == 0)) {					sco_set_state(SCO_STATE_SCO_CONNECTED);				} else {					errno = errcode;					DBG("SCO connection failed");					sco_set_state(SCO_STATE_CONNECTED);				}			}			(void)sco_handle_rfcomm_commands(sco);			break;		case SCO_STATE_SCO_CONNECTED:			(void)sco_handle_sco_stream(sco, lpFrame, iSize, received);			(void)sco_handle_rfcomm_commands(sco);			break;		default:			DBG("Unhandled state %d", sco->state);			break;	}	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -