📄 pcm_bluetooth.c
字号:
.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 + -