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

📄 pcm_bluetooth.c

📁 Linux的蓝牙操作工具。配合bluez-lib使用
💻 C
📖 第 1 页 / 共 3 页
字号:
	.prepare		= bluetooth_prepare,	.transfer		= bluetooth_hsp_write,	.poll_descriptors	= bluetooth_playback_poll_descriptors,	.poll_revents		= bluetooth_playback_poll_revents,	.delay			= bluetooth_playback_delay,};static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = {	.start			= bluetooth_start,	.stop			= bluetooth_stop,	.pointer		= bluetooth_pointer,	.close			= bluetooth_close,	.hw_params		= bluetooth_hsp_hw_params,	.prepare		= bluetooth_prepare,	.transfer		= bluetooth_hsp_read,	.poll_descriptors	= bluetooth_poll_descriptors,	.poll_revents		= bluetooth_poll_revents,};static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = {	.start			= bluetooth_playback_start,	.stop			= bluetooth_playback_stop,	.pointer		= bluetooth_pointer,	.close			= bluetooth_close,	.hw_params		= bluetooth_a2dp_hw_params,	.prepare		= bluetooth_prepare,	.transfer		= bluetooth_a2dp_write,	.poll_descriptors	= bluetooth_playback_poll_descriptors,	.poll_revents		= bluetooth_playback_poll_revents,	.delay			= bluetooth_playback_delay,};static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = {	.start			= bluetooth_start,	.stop			= bluetooth_stop,	.pointer		= bluetooth_pointer,	.close			= bluetooth_close,	.hw_params		= bluetooth_a2dp_hw_params,	.prepare		= bluetooth_prepare,	.transfer		= bluetooth_a2dp_read,	.poll_descriptors	= bluetooth_poll_descriptors,	.poll_revents		= bluetooth_poll_revents,};#define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0]))static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io){	struct bluetooth_data *data = io->private_data;	snd_pcm_access_t access_list[] = {		SND_PCM_ACCESS_RW_INTERLEAVED,		/* Mmap access is really useless fo this driver, but we		 * support it because some pieces of software out there		 * insist on using it */		SND_PCM_ACCESS_MMAP_INTERLEAVED	};	unsigned int format_list[] = {		SND_PCM_FORMAT_S16_LE	};	int err;	/* access type */	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,					ARRAY_NELEMS(access_list), access_list);	if (err < 0)		return err;	/* supported formats */	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,					ARRAY_NELEMS(format_list), format_list);	if (err < 0)		return err;	/* supported channels */	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,							1, 1);	if (err < 0)		return err;	/* supported rate */	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,							8000, 8000);	if (err < 0)		return err;	/* supported block size */	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,						data->link_mtu, data->link_mtu);	if (err < 0)		return err;	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,									2, 200);	if (err < 0)		return err;	return 0;}static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io){	struct bluetooth_data *data = io->private_data;	struct bluetooth_a2dp *a2dp = &data->a2dp;	snd_pcm_access_t access_list[] = {		SND_PCM_ACCESS_RW_INTERLEAVED,		/* Mmap access is really useless fo this driver, but we		 * support it because some pieces of software out there		 * insist on using it */		SND_PCM_ACCESS_MMAP_INTERLEAVED	};	unsigned int format_list[] = {		SND_PCM_FORMAT_S16_LE	};	unsigned int rate_list[4];	unsigned int rate_count;	int err, min_channels, max_channels;	unsigned int period_list[] = {		2048,		4096, /* e.g. 23.2msec/period (stereo 16bit at 44.1kHz) */		8192	};	/* access type */	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,					ARRAY_NELEMS(access_list), access_list);	if (err < 0)		return err;	/* supported formats */	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,					ARRAY_NELEMS(format_list), format_list);	if (err < 0)		return err;	/* supported channels */	if (a2dp->sbc_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)		min_channels = 1;	else		min_channels = 2;	if (a2dp->sbc_capabilities.channel_mode & (~BT_A2DP_CHANNEL_MODE_MONO))		max_channels = 2;	else		max_channels = 1;	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,							min_channels, max_channels);	if (err < 0)		return err;	/* supported buffer sizes	 * (can be used as 3*8192, 6*4096, 12*2048, ...) */	err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES,					      8192*3, 8192*3);	if (err < 0)		return err;	/* supported block sizes: */	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,				ARRAY_NELEMS(period_list), period_list);	if (err < 0)		return err;	/* supported rates */	rate_count = 0;	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) {		rate_list[rate_count] = 16000;		rate_count++;	}	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) {		rate_list[rate_count] = 32000;		rate_count++;	}	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) {		rate_list[rate_count] = 44100;		rate_count++;	}	if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) {		rate_list[rate_count] = 48000;		rate_count++;	}	err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE,						rate_count, rate_list);	if (err < 0)		return err;	return 0;}static int bluetooth_parse_config(snd_config_t *conf,				struct bluetooth_alsa_config *bt_config){	snd_config_iterator_t i, next;	const char *addr, *pref;	const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool;	memset(bt_config, 0, sizeof(struct bluetooth_alsa_config));	snd_config_for_each(i, next, conf) {		snd_config_t *n = snd_config_iterator_entry(i);		const char *id;		if (snd_config_get_id(n, &id) < 0)			continue;		if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)			continue;		if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) {			if (snd_config_get_string(n, &addr) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			bt_config->has_device = 1;			strncpy(bt_config->device, addr, 18);			continue;		}		if (strcmp(id, "profile") == 0) {			if (snd_config_get_string(n, &pref) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			if (strcmp(pref, "auto") == 0) {				bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY;				bt_config->has_transport = 1;			} else if (strcmp(pref, "voice") == 0 ||						strcmp(pref, "hfp") == 0) {				bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO;				bt_config->has_transport = 1;			} else if (strcmp(pref, "hifi") == 0 ||						strcmp(pref, "a2dp") == 0) {				bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP;				bt_config->has_transport = 1;			}			continue;		}		if (strcmp(id, "rate") == 0) {			if (snd_config_get_string(n, &rate) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			bt_config->rate = atoi(rate);			bt_config->has_rate = 1;			continue;		}		if (strcmp(id, "mode") == 0) {			if (snd_config_get_string(n, &mode) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			if (strcmp(pref, "auto") == 0) {				bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;				bt_config->has_channel_mode = 1;			} else if (strcmp(pref, "mono") == 0) {				bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;				bt_config->has_channel_mode = 1;			} else if (strcmp(pref, "dual") == 0) {				bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;				bt_config->has_channel_mode = 1;			} else if (strcmp(pref, "stereo") == 0) {				bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;				bt_config->has_channel_mode = 1;			} else if (strcmp(pref, "joint") == 0) {				bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;				bt_config->has_channel_mode = 1;			}			continue;		}		if (strcmp(id, "allocation") == 0) {			if (snd_config_get_string(n, &allocation) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			if (strcmp(pref, "auto") == 0) {				bt_config->allocation_method = BT_A2DP_ALLOCATION_AUTO;				bt_config->has_allocation_method = 1;			} else if (strcmp(pref, "loudness") == 0) {				bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;				bt_config->has_allocation_method = 1;			} else if (strcmp(pref, "snr") == 0) {				bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR;				bt_config->has_allocation_method = 1;			}			continue;		}		if (strcmp(id, "subbands") == 0) {			if (snd_config_get_string(n, &subbands) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			bt_config->subbands = atoi(subbands);			bt_config->has_subbands = 1;			continue;		}		if (strcmp(id, "blocks") == 0) {			if (snd_config_get_string(n, &blocks) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			bt_config->block_length = atoi(blocks);			bt_config->has_block_length = 1;			continue;		}		if (strcmp(id, "bitpool") == 0) {			if (snd_config_get_string(n, &bitpool) < 0) {				SNDERR("Invalid type for %s", id);				return -EINVAL;			}			bt_config->bitpool = atoi(bitpool);			bt_config->has_bitpool = 1;			continue;		}		SNDERR("Unknown field %s", id);		return -EINVAL;	}	return 0;}static int audioservice_send(int sk, const bt_audio_msg_header_t *msg){	int err;	DBG("sending %s", bt_audio_strmsg(msg->msg_type));	if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)		err = 0;	else {		err = -errno;		SNDERR("Error sending data to audio service: %s(%d)",			strerror(errno), errno);	}	return err;}static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg){	int err;	const char *type;	DBG("trying to receive msg from audio service...");	if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {		type = bt_audio_strmsg(inmsg->msg_type);		if (type) {			DBG("Received %s", type);			err = 0;		} else {			err = -EINVAL;			SNDERR("Bogus message type %d "					"received from audio service",					inmsg->msg_type);		}	} else {		err = -errno;		SNDERR("Error receiving data from audio service: %s(%d)",					strerror(errno), errno);	}	return err;}static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr,				int expected_type){	int err = audioservice_recv(sk, rsp_hdr);	if (err == 0) {		if (rsp_hdr->msg_type != expected_type) {			err = -EINVAL;			SNDERR("Bogus message %s received while "					"%s was expected",					bt_audio_strmsg(rsp_hdr->msg_type),					bt_audio_strmsg(expected_type));		}	}	return err;}static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,				snd_config_t *conf){	int sk, err;	struct bluetooth_alsa_config *alsa_conf = &data->alsa_config;	char buf[BT_AUDIO_IPC_PACKET_SIZE];	bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf;	struct bt_getcapabilities_req *getcaps_req = (void*) buf;	struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf;	memset(data, 0, sizeof(struct bluetooth_data));	err = bluetooth_parse_config(conf, alsa_conf);	if (err < 0)		return err;	data->server.fd = -1;	data->stream.fd = -1;	sk = bt_audio_service_open();	if(sk <= 0) {		err = -errno;		goto failed;	}	data->server.fd = sk;	data->server.events = POLLIN;	data->pipefd[0] = -1;	data->pipefd[1] = -1;	if (pipe(data->pipefd) < 0) {		err = -errno;		goto failed;	}	if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) {		err = -errno;		goto failed;	}	if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) {		err = -errno;		goto failed;	}	memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE);	getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ;	strncpy(getcaps_req->device, alsa_conf->device, 18);	if (alsa_conf->has_transport)		getcaps_req->transport = alsa_conf->transport;	else		getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_ANY;	err = audioservice_send(data->server.fd, &getcaps_req->h);	if (err < 0)		goto failed;	err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, BT_GETCAPABILITIES_RSP);	if (err < 0)		goto failed;	if (rsp_hdr->posix_errno != 0) {		SNDERR("BT_GETCAPABILITIES failed : %s(%d)",					strerror(rsp_hdr->posix_errno),					rsp_hdr->posix_errno);		return -rsp_hdr->posix_errno;	}	data->transport = getcaps_rsp->transport;	if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP)		data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities;	return 0;failed:	bt_audio_service_close(sk);	return err;}SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth){	struct bluetooth_data *data;	int err;	DBG("Bluetooth PCM plugin (%s)",		stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");	data = malloc(sizeof(struct bluetooth_data));	if (!data) {		err = -ENOMEM;		goto error;	}	err = bluetooth_init(data, stream, conf);	if (err < 0)		goto error;	data->io.version = SND_PCM_IOPLUG_VERSION;	data->io.name = "Bluetooth Audio Device";	data->io.mmap_rw = 0; /* No direct mmap communication */	data->io.private_data = data;	if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)		data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?			&bluetooth_a2dp_playback :			&bluetooth_a2dp_capture;	else		data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?			&bluetooth_hsp_playback :			&bluetooth_hsp_capture;	err = snd_pcm_ioplug_create(&data->io, name, stream, mode);	if (err < 0)		goto error;	if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)		err = bluetooth_a2dp_hw_constraint(&data->io);	else		err = bluetooth_hsp_hw_constraint(&data->io);	if (err < 0) {		snd_pcm_ioplug_delete(&data->io);		goto error;	}	*pcmp = data->io.pcm;	return 0;error:	bluetooth_exit(data);	return err;}SND_PCM_PLUGIN_SYMBOL(bluetooth);

⌨️ 快捷键说明

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