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

📄 btsco2.c

📁 蓝牙blue tooth sco协议栈
💻 C
字号:
/* *  Userspace management of snd-bt-sco * *  Copyright (c) 2003 by Jonathan Paisley <jp@dcs.gla.ac.uk> *  *  Daemon enhancements (c) 2004 by Lars Grunewaldt <lgw@dark-reality.de> * *  Based on sb16_csp/cspctl.c and hstest.c from bluez-utils/test. * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program 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 General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <assert.h>#include <string.h>#include <getopt.h>#include <sys/wait.h>#include <sys/time.h>#include <sys/poll.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <bluetooth/rfcomm.h>#include <bluetooth/sco.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <alsa/asoundlib.h>#define SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET _IOW ('H', 0x10, int)#ifndef SND_HWDEP_IFACE_EMUX_WAVETABLE#define SND_HWDEP_IFACE_EMUX_WAVETABLE (SND_HWDEP_IFACE_USX2Y + 1)#endif#ifndef SND_HWDEP_IFACE_BLUETOOTH#define SND_HWDEP_IFACE_BLUETOOTH (SND_HWDEP_IFACE_EMUX_WAVETABLE + 1)#endif#ifndef SNDRV_HWDEP_IFACE_BT_SCO#define SNDRV_HWDEP_IFACE_BT_SCO (SND_HWDEP_IFACE_BLUETOOTH + 1)#endifstatic volatile int terminate = 0;static void sig_term(int sig){	terminate = 1;}struct s_headset {	bdaddr_t local;	bdaddr_t bdaddr;	uint8_t channel;	int rfcomm_fd;	int sco_fd;	snd_hwdep_t *handle;	int volumes[2];	int last_volumes[2];	struct s_headset *next;};static struct s_headset *first = NULL;static int rfcomm_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t channel){	struct sockaddr_rc addr;	int sk;	sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);	if (sk < 0)		return -1;	memset(&addr, 0, sizeof(addr));	addr.rc_family = AF_BLUETOOTH;	bacpy(&addr.rc_bdaddr, src);	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		close(sk);		return -1;	}	memset(&addr, 0, sizeof(addr));	addr.rc_family = AF_BLUETOOTH;	bacpy(&addr.rc_bdaddr, dst);	addr.rc_channel = channel;	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		close(sk);		return -1;	}	return sk;}static int sco_connect(bdaddr_t *src, bdaddr_t *dst, uint16_t *handle, uint16_t *mtu){	struct sockaddr_sco addr;	struct sco_conninfo conn;	struct sco_options opts;	socklen_t size;	int sk;	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);	if (sk < 0)		return -1;	memset(&addr, 0, sizeof(addr));	addr.sco_family = AF_BLUETOOTH;	bacpy(&addr.sco_bdaddr, src);	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		close(sk);		return -1;	}	memset(&addr, 0, sizeof(addr));	addr.sco_family = AF_BLUETOOTH;	bacpy(&addr.sco_bdaddr, dst);	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {		close(sk);		return -1;	}	size = sizeof(conn);	if (getsockopt(sk, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {		close(sk);		return -1;	}	size = sizeof(opts);	if (getsockopt(sk, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {		close(sk);		return -1;	}	if (handle)		*handle = conn.hci_handle;	if (mtu)		*mtu = opts.mtu;	return sk;}static int bt_sco_set_fd(snd_hwdep_t * handle, int sco_fd){	if (snd_hwdep_ioctl(handle, SNDRV_BT_SCO_IOCTL_SET_SCO_SOCKET, (void *) sco_fd) < 0) {		perror("Unable to set SCO fd");		return -1;	}	return 0;}static int find_hwdep_device(int *cardP, int *devP){	snd_ctl_t *ctl_handle;	snd_ctl_card_info_t *card_info;	snd_hwdep_info_t *hwdep_info;	int card;	int dev;	int err;	char card_id[32];	ctl_handle = NULL;	snd_ctl_card_info_alloca(&card_info);	snd_hwdep_info_alloca(&hwdep_info);	for (card = 0; card < 7; card++) {		*cardP = card;		if (ctl_handle) {			snd_ctl_close(ctl_handle);			ctl_handle = NULL;		}		// Get control handle for selected card		sprintf(card_id, "hw:%i", card);		if ((err = snd_ctl_open(&ctl_handle, card_id, 0)) < 0) {			fprintf(stderr, "control open (%s): %s", card_id, snd_strerror(err));			return -1;		}		// Read control hardware info from card		if ((err = snd_ctl_card_info(ctl_handle, card_info)) < 0) {			fprintf(stderr, "control hardware info (%s): %s", card_id, snd_strerror(err));			continue;		}		//if (strcmp(snd_ctl_card_info_get_driver(card_info),"BT SCO (d)"))		//    continue;		dev = -1;		err = 1;		while (1) {			int if_type;			if (snd_ctl_hwdep_next_device(ctl_handle, &dev) < 0)				fprintf(stderr, "hwdep next device (%s): %s", card_id, snd_strerror(err));			if (dev < 0) 				break;			snd_hwdep_info_set_device(hwdep_info, dev);			if (snd_ctl_hwdep_info(ctl_handle, hwdep_info) < 0) {				if (err != -ENOENT)					fprintf(stderr, "control hwdep info (%s): %s", card_id, snd_strerror(err));				continue;			}			if_type = snd_hwdep_info_get_iface(hwdep_info);			if (if_type == SNDRV_HWDEP_IFACE_BT_SCO || if_type==12) {				snd_ctl_close(ctl_handle);				*devP = dev;				return 0;			}		}	}	if (ctl_handle)		snd_ctl_close(ctl_handle);	return -1;}static int detect_channel(bdaddr_t * bdaddr){	uuid_t group;	bdaddr_t interface;	sdp_list_t *attrid, *search, *seq, *next;	uint32_t range = 0x0000ffff;	sdp_session_t *sess;	int channel = 2;	int searchresult;	bacpy(&interface, BDADDR_ANY);	sdp_uuid16_create(&group, 0x1108);	sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);	if (!sess) {		fprintf(stderr, "Failed to connect to SDP server: %s\nAssuming channel %d\n",					strerror(errno), channel);		return channel;	}	attrid = sdp_list_append(0, &range);	search = sdp_list_append(0, &group);	searchresult = sdp_service_search_attr_req(sess, search,					SDP_ATTR_REQ_RANGE, attrid, &seq);	sdp_list_free(attrid, 0);	sdp_list_free(search, 0);	if (searchresult) {		fprintf(stderr, "Service Search failed: %s\nAssuming channel %d\n",					strerror(errno), channel);		sdp_close(sess);		return channel;	}	for (; seq; seq = next) {		sdp_record_t *rec = (sdp_record_t *) seq->data;		sdp_list_t *list = 0;		if (sdp_get_access_protos(rec, &list) == 0) {			channel = sdp_get_proto_port(list, RFCOMM_UUID);		}		next = seq->next;		free(seq);		sdp_record_free(rec);	}	sdp_close(sess);	return channel;}static int headset_button(struct s_headset *headset){	uint16_t sco_handle, sco_mtu;	if (headset == NULL)		return 0;	if (headset->sco_fd != -1) {		/* close bt_sco audio handle */		bt_sco_set_fd(headset->handle, -1);		/* disconnect SCO stream */		close(headset->sco_fd);		headset->sco_fd = -1;		fprintf(stderr, "disconnected SCO channel\n");		return 1;	}	fprintf(stderr, "opened hwdep\n");	/* connect sco stream */	if ((headset->sco_fd = sco_connect(&headset->local, &headset->bdaddr, &sco_handle, &sco_mtu)) < 0) {		perror("Can't connect SCO audio channel");		return 1;	}	fprintf(stderr, "connected SCO channel\n");	//      write(rd, "RING\r\n", 6);	fprintf(stderr, "Setting sco fd\n"); 	bt_sco_set_fd (headset->handle, headset->sco_fd);	fprintf(stderr, "Done setting sco fd\n"); 	return 1;}static struct s_headset *headset_new(void){	struct s_headset *headset;	headset = malloc (sizeof(struct s_headset));	if (headset == NULL)		return NULL;	headset->sco_fd = -1;	headset->rfcomm_fd = -1;	headset->handle = NULL;	headset->last_volumes[0] = 0;	headset->last_volumes[1] = 0;	headset->next = first;	first = headset;	return headset;}static int headset_volume_fromcard(struct s_headset *headset){	int len;	char line[100];	len = snd_hwdep_read(headset->handle, headset->volumes, sizeof(headset->volumes));	if (len != sizeof(headset->volumes)) 		return 0;	fprintf(stderr, "volume speaker: %d mic: %d\n", headset->volumes[0], headset->volumes[1]);	if (headset->volumes[0] != headset->last_volumes[0]) {		sprintf(line, "+VGS=%d\r", headset->volumes[0]);		write(headset->rfcomm_fd, line, strlen(line));		headset->last_volumes[0] = headset->last_volumes[0];	}	if (headset->volumes[1] != headset->last_volumes[1]) {		sprintf(line, "+VGM=%d\r", headset->volumes[1]);		write(headset->rfcomm_fd, line, strlen(line));		headset->last_volumes[1] = headset->last_volumes[1];	}	return 1;}static int headset_speaker(struct s_headset *headset){	fprintf(stderr, "Sending up speaker change %d\n", headset->volumes[0]);	snd_hwdep_write(headset->handle, headset->volumes, sizeof (headset->volumes));	return 1;}static int headset_micro(struct s_headset *headset){	fprintf(stderr, "Sending up microphone change %d\n", headset->volumes[1]);	snd_hwdep_write(headset->handle, headset->volumes, sizeof (headset->volumes));	return 1;}static int headset_from_bt(struct s_headset *headset){	unsigned char buf[2048];	int rlen;	int opdone;		opdone = 0;	rlen = read(headset->rfcomm_fd, buf, sizeof(buf) - 1);	if (rlen <= 0)		return 0;	buf [rlen] = 0;	fprintf(stderr, "recieved %s\n", buf);	if (strstr(buf, "AT+CKPD=200")) opdone = headset_button(headset);	else if (strstr(buf, "AT+CHUP"    )) opdone = headset_button(headset);	else if (strstr(buf, "AT+CIND=?"  )) opdone = headset_button(headset);	else if (sscanf (buf, "AT+VGS=%d", &headset->volumes[0]) == 1) opdone = headset_speaker (headset);	else if (sscanf (buf, "AT+VGM=%d", &headset->volumes[1]) == 1) opdone = headset_micro   (headset);	if (opdone == 1)		/* tell them we recieved */		write(headset->rfcomm_fd, "\r\nOK\r\n", 6);	else		write(headset->rfcomm_fd, "\r\nERROR\r\n", 9);	return 1;}static void headset_destroy(struct s_headset *headset){	if (headset == NULL)		return;	if (headset->sco_fd != -1) {		bt_sco_set_fd(headset->handle, -1);		close(headset->sco_fd);	}	sleep(1);	if (headset->rfcomm_fd != -1) 		close(headset->rfcomm_fd);	if (headset->handle != NULL) 		snd_hwdep_close(headset->handle);	headset->sco_fd = -1;	headset->rfcomm_fd = -1;	headset->handle = NULL;}static void cleanup(void){	struct s_headset *akt_headset;	akt_headset = first;	while (akt_headset != NULL) {		struct s_headset *next = akt_headset->next;		headset_destroy(akt_headset);		akt_headset = next;		}}static int check_bt_voice(int dev){	int dd;	uint16_t vs;	/* check voice settings. in this version we only support mu-law */	dd = hci_open_dev(dev);	hci_read_voice_setting(dd, &vs, 1000);	vs = htobs(vs);	fprintf(stderr, "Voice setting: 0x%04x\n", vs);	close(dd);	/*	   MU_LAW	   if (vs != 0x0140) {	   fprintf(stderr, "The voice setting must be 0x0140\n");	   return -1;	   }	 */	// 16bit	if (vs != 0x060) {		fprintf(stderr, "The voice setting must be 0x060\n");		return -1;	}	return 0;}static void usage(void){	printf("Usage:\n"		"\tbtsco2 <bdaddr> [channel]\n");}int main(int argc, char *argv[]){	int dev;	int card;	struct sigaction sa;#ifdef TEST	int rlen;#endif	int bt_dev = 0;	struct pollfd pfds[16];	int err;	char hwdep_name[16];	struct s_headset *akt_headset;	atexit(cleanup);	/* detect the audio device */	if (find_hwdep_device(&card, &dev)) {		perror("Can't find device. Bail");		return 1;	}	printf("Device is %d:%d\n", card, dev);	sprintf(hwdep_name, "hw:%i,%i", card, dev);	if (check_bt_voice(bt_dev) < 0)		return -1;	/* find bdaddr */	switch (argc) {	case 2:		akt_headset = headset_new();		hci_devba(bt_dev, &akt_headset->local);		str2ba(argv[1], &akt_headset->bdaddr);		akt_headset->channel = detect_channel(&akt_headset->bdaddr);		/* open hwdep on audio device */		if ((err = snd_hwdep_open(&akt_headset->handle, hwdep_name, O_RDWR)) < 0) {			fprintf(stderr, "btsco open (%i-%i): %s\n", card, dev, snd_strerror(err));			return -1;		}		break;	case 3:		akt_headset = headset_new();		hci_devba(bt_dev, &akt_headset->local);		str2ba(argv[1], &akt_headset->bdaddr);		akt_headset->channel = atoi(argv[2]);		/* open hwdep on audio device */		if ((err = snd_hwdep_open(&akt_headset->handle, hwdep_name, O_RDWR)) < 0) {			fprintf(stderr, "btsco open (%i-%i): %s\n", card, dev, snd_strerror(err));			return -1;		}		break;	default:		usage();		exit(-1);	}	/* setup sigterm handler. we must make sure to do a clean disconnect */	memset(&sa, 0, sizeof(sa));	sa.sa_flags = SA_NOCLDSTOP;	sa.sa_handler = sig_term;	sigaction(SIGTERM, &sa, NULL);	sigaction(SIGINT, &sa, NULL);	sa.sa_handler = SIG_IGN;	sigaction(SIGCHLD, &sa, NULL);	sigaction(SIGPIPE, &sa, NULL);	/* we are not yet connected */	while (!terminate) {		short revents;		int nfds;		nfds = 0;		/* set up data polling description */		for (akt_headset = first; akt_headset != NULL; akt_headset = akt_headset->next) { 			if (akt_headset->rfcomm_fd == -1) {				/* connect rfcomm control channel */				if ((akt_headset->rfcomm_fd = rfcomm_connect(						&akt_headset->local,						&akt_headset->bdaddr,						akt_headset->channel)) < 0)					fprintf(stderr, "Can't connect RFCOMM channel");				else					fprintf(stderr, "RFCOMM channel %i connected\n", akt_headset->channel);			}			if (akt_headset->rfcomm_fd != -1) {				pfds[nfds].fd = akt_headset->rfcomm_fd;				pfds[nfds++].events = POLLIN;			}			if (akt_headset->handle != NULL) {				/* polling data from hwdep interface */				nfds += snd_hwdep_poll_descriptors(akt_headset->handle, &pfds[nfds], 1);			}		} 		/*printf("outer loop\n"); */		if (nfds == 0) {			sleep(3);			continue;		}		if (poll(pfds, nfds, 1000) <= 0) 			continue;		for (akt_headset = first; akt_headset != NULL; akt_headset = akt_headset->next) {			int j;			for (j = 0; j < nfds; j++) {				if (pfds[j].fd == akt_headset->rfcomm_fd) {					if (pfds[j].revents & POLLIN)						headset_from_bt(akt_headset);					continue;				}#ifdef TEST				if (pfds[j].fd == akt_headset->sco_fd) {					/* Just for testing; handled by kernel driver */					fd_set rfds;					if (0 && FD_ISSET(akt_headset->sco_fd, &rfds)) {						int i;						unsigned char buf[2048];						memset(buf, 0, sizeof(buf));						rlen = read(akt_headset->sco_fd, buf, sizeof(buf));						write(akt_headset->sco_fd, buf, rlen);						i++;						if (i % 15 == 0) printf("rlen: %d\n", rlen);					}					continue;				}#endif				/* Volume polling (sound card) */				if (!snd_hwdep_poll_descriptors_revents (akt_headset->handle, &pfds[j], 1, &revents) && revents & POLLIN) 					headset_volume_fromcard (akt_headset);			}		}	}	return 0;}

⌨️ 快捷键说明

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