diff options
author | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2007-11-21 20:24:11 +0000 |
---|---|---|
committer | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2007-11-21 20:24:11 +0000 |
commit | d4e24bf6a3d8af6479abce92fbbf1869a59669aa (patch) | |
tree | 8ba8c084bb1eb1a3e1dd598127ca33553dbc6d23 /audio/pcm_bluetooth.c | |
parent | 47e2c26cc95d761099c367593bbbd4bc581bf0ac (diff) |
Integrate new ipc API implementation.
Diffstat (limited to 'audio/pcm_bluetooth.c')
-rw-r--r-- | audio/pcm_bluetooth.c | 786 |
1 files changed, 418 insertions, 368 deletions
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index efe1d2aa..f4a20015 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -76,33 +76,60 @@ #endif struct bluetooth_a2dp { - sbc_t sbc; /* Codec data */ - int codesize; /* SBC codesize */ - int samples; /* Number of encoded samples */ - uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ - int count; /* Codec transfer buffer counter */ - - int nsamples; /* Cumulative number of codec samples */ - uint16_t seq_num; /* Cumulative packet sequence */ - int frame_count; /* Current frames in buffer*/ + sbc_capabilities_t sbc_capabilities; + sbc_t sbc; /* Codec data */ + int sbc_initialized; /* Keep track if the encoder is initialized */ + int codesize; /* SBC codesize */ + int samples; /* Number of encoded samples */ + uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + int count; /* Codec transfer buffer counter */ + + int nsamples; /* Cumulative number of codec samples */ + uint16_t seq_num; /* Cumulative packet sequence */ + int frame_count; /* Current frames in buffer*/ +}; + +struct bluetooth_alsa_config { + char device[18]; /* Address of the remote Device */ + int has_device; + uint8_t transport; /* Requested transport */ + int has_transport; + uint16_t rate; + int has_rate; + uint8_t channel_mode; /* A2DP only */ + int has_channel_mode; + uint8_t allocation_method; /* A2DP only */ + int has_allocation_method; + uint8_t subbands; /* A2DP only */ + int has_subbands; + uint8_t block_length; /* A2DP only */ + int has_block_length; + uint8_t bitpool; /* A2DP only */ + int has_bitpool; }; struct bluetooth_data { snd_pcm_ioplug_t io; + struct bluetooth_alsa_config alsa_config; /* ALSA resource file parameters */ volatile snd_pcm_sframes_t hw_ptr; - struct ipc_data_cfg cfg; /* Bluetooth device config */ - struct pollfd stream; /* Audio stream filedescriptor */ - struct pollfd server; /* Audio daemon filedescriptor */ - uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ - int count; /* Transfer buffer counter */ - struct bluetooth_a2dp a2dp; /* A2DP data */ - - pthread_t hw_thread; /* Makes virtual hw pointer move */ - int pipefd[2]; /* Inter thread communication */ + int transport; /* chosen transport SCO or AD2P */ + int link_mtu; /* MTU for selected transport channel */ + volatile struct pollfd stream; /* Audio stream filedescriptor */ + struct pollfd server; /* Audio daemon filedescriptor */ + uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ + int count; /* Transfer buffer counter */ + struct bluetooth_a2dp a2dp; /* A2DP data */ + + pthread_t hw_thread; /* Makes virtual hw pointer move */ + int pipefd[2]; /* Inter thread communication */ int stopped; sig_atomic_t reset; /* Request XRUN handling */ }; +static int audioservice_send(int sk, const bt_audio_msg_header_t *msg); +static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg, + int expected_type); + static int bluetooth_start(snd_pcm_ioplug_t *io) { DBG("bluetooth_start %p", io); @@ -215,62 +242,6 @@ iter_sleep: pthread_exit(NULL); } -#if 0 -static int bluetooth_state_init(struct ipc_packet *pkt, int newstate) -{ - struct ipc_data_state *state = (void *) pkt->data; - - pkt->length = sizeof(*state); - pkt->type = PKT_TYPE_STATE_REQ; - pkt->error = PKT_ERROR_NONE; - state->state = newstate; - - return 0; -} - -static int bluetooth_state(struct bluetooth_data *data, int newstate) -{ - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_state *state = (void *) pkt->data; - int ret; - - memset(buf, 0, sizeof(buf)); - - ret = bluetooth_state_init(pkt, newstate); - if (ret < 0) - return -ret; - - ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - DBG("OK - %d bytes sent. Waiting for response...", ret); - - memset(buf, 0, sizeof(buf)); - - ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*state), 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - if (pkt->type != PKT_TYPE_STATE_RSP) { - SNDERR("Unexpected packet type %d received", pkt->type); - return -EINVAL; - } - - if (pkt->error != PKT_ERROR_NONE) { - SNDERR("Error %d while configuring device", pkt->error); - return -pkt->error; - } - - return 0; -} -#endif - static int bluetooth_playback_start(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -278,9 +249,6 @@ static int bluetooth_playback_start(snd_pcm_ioplug_t *io) DBG("%p", io); -#if 0 - bluetooth_state(data, STATE_STREAMING); -#endif data->stopped = 0; if (data->hw_thread) @@ -297,9 +265,6 @@ static int bluetooth_playback_stop(snd_pcm_ioplug_t *io) DBG("%p", io); -#if 0 - bluetooth_state(data, STATE_CONNECTED); -#endif data->stopped = 1; return 0; @@ -317,7 +282,7 @@ static void bluetooth_exit(struct bluetooth_data *data) struct bluetooth_a2dp *a2dp = &data->a2dp; if (data->server.fd >= 0) - close(data->server.fd); + bt_audio_service_close(data->server.fd); if (data->stream.fd >= 0) close(data->stream.fd); @@ -327,7 +292,7 @@ static void bluetooth_exit(struct bluetooth_data *data) pthread_join(data->hw_thread, 0); } - if (data->cfg.codec == CFG_CODEC_SBC) + if (a2dp->sbc_initialized) sbc_finish(&a2dp->sbc); if (data->pipefd[0] > 0) @@ -354,12 +319,27 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; char c = 'w'; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *start_req = (void*) buf; + struct bt_streamstart_rsp *start_rsp = (void*) buf; + struct bt_datafd_ind *datafd_ind = (void*) buf; + uint32_t period_count = io->buffer_size / io->period_size; + int opt_name, err; + struct timeval t = { 0, period_count }; DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", io->period_size, io->buffer_size); data->reset = 0; + /* As we're gonna receive messages on the server socket, we have to stop the + hw thread that is polling on it, if any */ + if (data->hw_thread) { + pthread_cancel(data->hw_thread); + pthread_join(data->hw_thread, 0); + data->hw_thread = 0; + } + if (io->stream == SND_PCM_STREAM_PLAYBACK) /* If not null for playback, xmms doesn't display time * correctly */ @@ -369,63 +349,174 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - /* wake up any client polling at us */ - return write(data->pipefd[1], &c, 1); -} + /* send start */ + memset(start_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + start_req->h.msg_type = BT_STREAMSTART_REQ; -static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, - snd_pcm_hw_params_t *params) -{ - struct bluetooth_data *data = io->private_data; - uint32_t period_count = io->buffer_size / io->period_size; - int opt_name, err; + err = audioservice_send(data->server.fd, &start_req->h); + if (err < 0) + return err; + + err = audioservice_expect(data->server.fd, &start_rsp->h, + BT_STREAMSTART_RSP); + if (err < 0) + return err; + + if (start_rsp->posix_errno != 0) { + SNDERR("BT_START failed : %s(%d)", + strerror(start_rsp->posix_errno), + start_rsp->posix_errno); + return -start_rsp->posix_errno; + } + + err = audioservice_expect(data->server.fd, &datafd_ind->h, + BT_STREAMFD_IND); + if (err < 0) + return err; + + if (data->stream.fd >= 0) + close(data->stream.fd); + + data->stream.fd = bt_audio_service_get_data_fd(data->server.fd); + if (data->stream.fd < 0) { + return -errno; + } - DBG("fd=%d period_count=%d", data->stream.fd, period_count); + if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + SO_SNDTIMEO : SO_RCVTIMEO; - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t, + sizeof(t)) < 0) + return -errno; + } else { + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; - if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) - return 0; + return 0; - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDBUF : SO_RCVBUF; - if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) - return 0; - - err = errno; + return 0; - SNDERR("%s (%d)", strerror(err), err); + /* FIXME : handle error codes */ + } - /* FIXME: We should not ignores errors in the future. */ - return 0; + /* wake up any client polling at us */ + return write(data->pipefd[1], &c, 1); } static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { struct bluetooth_data *data = io->private_data; - uint32_t period_count = io->buffer_size / io->period_size; - int opt_name, err; - struct timeval t = { 0, period_count }; + struct bluetooth_a2dp *a2dp = &data->a2dp; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *setconf_req = (void*) buf; + struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; + int err; + sbc_capabilities_t active_capabilities; - DBG("fd=%d period_count=%d", data->stream.fd, period_count); + DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", + io->period_size, io->buffer_size); - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDTIMEO : SO_RCVTIMEO; + /* FIXME: this needs to be really implemented (take into account + real asoundrc settings + ALSA hw settings ) once server side sends us + more than one possible configuration */ + active_capabilities = a2dp->sbc_capabilities; - if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t, - sizeof(t)) == 0) - return 0; + memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; + setconf_req->sbc_capabilities = active_capabilities; - err = errno; + err = audioservice_send(data->server.fd, &setconf_req->h); + if (err < 0) + return err; - SNDERR("%s (%d)", strerror(err), err); + err = audioservice_expect(data->server.fd, &setconf_rsp->h, + BT_SETCONFIGURATION_RSP); + if (err < 0) + return err; - return -err; + if (setconf_rsp->posix_errno != 0) { + SNDERR("BT_SETCONFIGURATION failed : %s(%d)", + strerror(setconf_rsp->posix_errno), + setconf_rsp->posix_errno); + return -setconf_rsp->posix_errno; + } + + /* Setup SBC encoder now we agree on parameters */ + if (a2dp->sbc_initialized) + sbc_finish(&a2dp->sbc); + + /* FIXME: init using flags? */ + sbc_init(&a2dp->sbc, 0); + a2dp->sbc_initialized = 1; + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) + a2dp->sbc.rate = 16000; + + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) + a2dp->sbc.rate = 32000; + + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) + a2dp->sbc.rate = 44100; + + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) + a2dp->sbc.rate = 48000; + + if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + a2dp->sbc.channels = 1; + else + a2dp->sbc.channels = 2; + + if (active_capabilities.channel_mode & + (BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + a2dp->sbc.joint = 1; + else + a2dp->sbc.joint = 0; + + a2dp->sbc.allocation = active_capabilities.allocation_method + == BT_A2DP_ALLOCATION_SNR ? 0x01 : 0x00; + + switch (active_capabilities.subbands) { + case BT_A2DP_SUBBANDS_4: + a2dp->sbc.subbands = 4; + break; + case BT_A2DP_SUBBANDS_8: + a2dp->sbc.subbands = 8; + break; + } + + switch (active_capabilities.block_length) { + case BT_A2DP_BLOCK_LENGTH_4: + a2dp->sbc.blocks = 4; + break; + case BT_A2DP_BLOCK_LENGTH_8: + a2dp->sbc.blocks = 8; + break; + case BT_A2DP_BLOCK_LENGTH_12: + a2dp->sbc.blocks = 12; + break; + case BT_A2DP_BLOCK_LENGTH_16: + a2dp->sbc.blocks = 16; + break; + } + + a2dp->sbc.bitpool = active_capabilities.max_bitpool; + a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * + a2dp->sbc.channels * 2; + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + + DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", + a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, + a2dp->sbc.bitpool); + + return 0; } static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, @@ -504,7 +595,6 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; snd_pcm_uframes_t frames_to_write, ret; unsigned char *buff; int nrecv, frame_size = 0; @@ -517,7 +607,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, frame_size = areas->step / 8; - nrecv = recv(data->stream.fd, data->buffer, cfg.pkt_len, + nrecv = recv(data->stream.fd, data->buffer, data->link_mtu, MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); if (nrecv < 0) { @@ -525,28 +615,28 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, goto done; } - if (nrecv != cfg.pkt_len) { + if (nrecv != data->link_mtu) { ret = -EIO; SNDERR(strerror(-ret)); goto done; } /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % + data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) % io->buffer_size; proceed: buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; - if ((data->count + size * frame_size) <= cfg.pkt_len) + if ((data->count + size * frame_size) <= data->link_mtu) frames_to_write = size; else - frames_to_write = (cfg.pkt_len - data->count) / frame_size; + frames_to_write = (data->link_mtu - data->count) / frame_size; memcpy(buff, data->buffer + data->count, frame_size * frames_to_write); data->count += (frame_size * frames_to_write); - data->count %= cfg.pkt_len; + data->count %= data->link_mtu; /* Return written frames count */ ret = frames_to_write; @@ -562,7 +652,6 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read; uint8_t *buff; @@ -579,10 +668,10 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, } frame_size = areas->step / 8; - if ((data->count + size * frame_size) <= cfg.pkt_len) + if ((data->count + size * frame_size) <= data->link_mtu) frames_to_read = size; else - frames_to_read = (cfg.pkt_len - data->count) / frame_size; + frames_to_read = (data->link_mtu - data->count) / frame_size; DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); @@ -593,12 +682,12 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, /* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; - if (data->count != cfg.pkt_len) { + if (data->count != data->link_mtu) { ret = frames_to_read; goto done; } - rsend = send(data->stream.fd, data->buffer, cfg.pkt_len, + rsend = send(data->stream.fd, data->buffer, data->link_mtu, io->nonblock ? MSG_DONTWAIT : 0); if (rsend > 0) { /* Reset count pointer */ @@ -710,7 +799,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, frames_to_read = (a2dp->codesize - data->count) / frame_size; DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); - DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len); + DBG("a2dp.count=%d data.link_mtu=%d", a2dp->count, data->link_mtu); /* FIXME: If state is not streaming then return */ @@ -747,10 +836,11 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, written, a2dp->count); /* No space left for another frame then send */ - if (a2dp->count + written >= data->cfg.pkt_len) { + if (a2dp->count + written >= data->link_mtu) { avdtp_write(data); - DBG("sending packet %d, count %d, pkt_len %u", c, - old_count, data->cfg.pkt_len); + DBG("sending packet %d, count %d, link_mtu %u", + a2dp->seq_num, a2dp->count, + data->link_mtu); } ret += frames_to_read; @@ -791,7 +881,6 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .stop = bluetooth_playback_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_write, .poll_descriptors = bluetooth_playback_poll_descriptors, @@ -804,7 +893,6 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { .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, @@ -841,7 +929,6 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, /* Mmap access is really useless fo this driver, but we @@ -852,7 +939,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE }; - int err, channels; + int err; /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, @@ -867,21 +954,20 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ - channels = cfg.mode == CFG_MODE_MONO ? 1 : 2; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - channels, channels); + 1, 1); if (err < 0) return err; /* supported rate */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.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, - cfg.pkt_len, cfg.pkt_len); + data->link_mtu, data->link_mtu); if (err < 0) return err; @@ -896,7 +982,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; + 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 @@ -907,10 +993,12 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) 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[] = { 4096, /* 23/46ms (stereo/mono 16bit at 44.1kHz) */ }; - int err, channels; /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, @@ -925,15 +1013,18 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ - channels = cfg.mode == CFG_MODE_MONO ? 1 : 2; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - channels, channels); - if (err < 0) - return err; + if (a2dp->sbc_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + min_channels = 1; + else + min_channels = 2; - /* supported rate */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.rate); + 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; @@ -949,96 +1040,44 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; - return 0; -} - -static int bluetooth_recvmsg_fd(struct bluetooth_data *data) -{ - char cmsg_b[CMSG_SPACE(sizeof(int))], m; - int err, ret; - struct iovec iov = { &m, sizeof(m) }; - struct msghdr msgh; - struct cmsghdr *cmsg; - - memset(&msgh, 0, sizeof(msgh)); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - msgh.msg_control = &cmsg_b; - msgh.msg_controllen = CMSG_LEN(sizeof(int)); - - ret = recvmsg(data->server.fd, &msgh, 0); - if (ret < 0) { - err = errno; - SNDERR("Unable to receive fd: %s (%d)", strerror(err), err); - return -err; + /* supported rates */ + rate_count = 0; + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) { + rate_list[rate_count] = 16000; + rate_count++; } - /* Receive auxiliary data in msgh */ - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS) { - data->stream.fd = (*(int *) CMSG_DATA(cmsg)); - DBG("stream_fd=%d", data->stream.fd); - return 0; - } + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) { + rate_list[rate_count] = 32000; + rate_count++; } - return -EINVAL; -} - -static int bluetooth_a2dp_init(struct bluetooth_data *data, - struct ipc_codec_sbc *sbc) -{ - struct bluetooth_a2dp *a2dp = &data->a2dp; - struct ipc_data_cfg *cfg = &data->cfg; - - if (cfg == NULL) { - SNDERR("Error getting codec parameters"); - return -1; + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) { + rate_list[rate_count] = 44100; + rate_count++; } - if (cfg->codec != CFG_CODEC_SBC) - return -1; - - /* FIXME: init using flags? */ - sbc_init(&a2dp->sbc, 0); - a2dp->sbc.rate = cfg->rate; - a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; - if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) - a2dp->sbc.joint = 1; - a2dp->sbc.allocation = sbc->allocation; - a2dp->sbc.subbands = sbc->subbands; - a2dp->sbc.blocks = sbc->blocks; - a2dp->sbc.bitpool = sbc->bitpool; - a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; - a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) { + rate_list[rate_count] = 48000; + rate_count++; + } - DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", - a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, - a2dp->sbc.bitpool); + 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_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, - snd_config_t *conf) +static int bluetooth_parse_config(snd_config_t *conf, + struct bluetooth_alsa_config *bt_config) { - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; snd_config_iterator_t i, next; const char *addr, *pref; const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool; - switch (stream) { - case SND_PCM_STREAM_PLAYBACK: - cfg->fd_opt = CFG_FD_OPT_WRITE; - break; - case SND_PCM_STREAM_CAPTURE: - cfg->fd_opt = CFG_FD_OPT_READ; - break; - } + 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); @@ -1056,7 +1095,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - strncpy(pkt->device, addr, 18); + bt_config->has_device = 1; + strncpy(bt_config->device, addr, 18); continue; } @@ -1066,14 +1106,18 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - if (strcmp(pref, "auto") == 0) - pkt->role = PKT_ROLE_AUTO; - else if (strcmp(pref, "voice") == 0 || + 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) { - pkt->role = PKT_ROLE_VOICE; + bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO; + bt_config->has_transport = 1; } else if (strcmp(pref, "hifi") == 0 || - strcmp(pref, "a2dp") == 0) - pkt->role = PKT_ROLE_HIFI; + strcmp(pref, "a2dp") == 0) { + bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + bt_config->has_transport = 1; + } continue; } @@ -1083,7 +1127,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - cfg->rate = atoi(rate); + bt_config->rate = atoi(rate); + bt_config->has_rate = 1; continue; } @@ -1093,16 +1138,22 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - if (strcmp(pref, "auto") == 0) - cfg->mode = CFG_MODE_AUTO; - else if (strcmp(pref, "mono") == 0) - cfg->mode = CFG_MODE_MONO; - else if (strcmp(pref, "dual") == 0) - cfg->mode = CFG_MODE_DUAL_CHANNEL; - else if (strcmp(pref, "stereo") == 0) - cfg->mode = CFG_MODE_STEREO; - else if (strcmp(pref, "joint") == 0) - cfg->mode = CFG_MODE_JOINT_STEREO; + 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; } @@ -1112,12 +1163,16 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - if (strcmp(pref, "auto") == 0) - sbc->allocation = CFG_ALLOCATION_AUTO; - else if (strcmp(pref, "loudness") == 0) - sbc->allocation = CFG_ALLOCATION_LOUDNESS; - else if (strcmp(pref, "snr") == 0) - sbc->allocation = CFG_ALLOCATION_SNR; + 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; } @@ -1127,7 +1182,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - sbc->subbands = atoi(subbands); + bt_config->subbands = atoi(subbands); + bt_config->has_subbands = 1; continue; } @@ -1137,7 +1193,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - sbc->blocks = atoi(blocks); + bt_config->block_length = atoi(blocks); + bt_config->has_block_length = 1; continue; } @@ -1147,7 +1204,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - sbc->bitpool = atoi(bitpool); + bt_config->bitpool = atoi(bitpool); + bt_config->has_bitpool = 1; continue; } @@ -1155,139 +1213,89 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - pkt->length = sizeof(*cfg) + sizeof(*sbc); - pkt->type = PKT_TYPE_CFG_REQ; - pkt->error = PKT_ERROR_NONE; - return 0; } -static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream, - snd_config_t *conf) +static int audioservice_send(int sk, const bt_audio_msg_header_t *msg) { - int ret, total; - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; - - DBG("Sending PKT_TYPE_CFG_REQ..."); - - memset(buf, 0, sizeof(buf)); - - ret = bluetooth_cfg_init(pkt, stream, conf); - if (ret < 0) - return -ret; - - ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - DBG("OK - %d bytes sent. Waiting for response...", ret); - - memset(buf, 0, sizeof(buf)); - - ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*cfg), 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - total = ret; - - if (pkt->type != PKT_TYPE_CFG_RSP) { - SNDERR("Unexpected packet type %d received", pkt->type); - return -EINVAL; - } - - if (pkt->error != PKT_ERROR_NONE) { - SNDERR("Error %d while configuring device", pkt->error); - return -pkt->error; - } - - if (cfg->codec != CFG_CODEC_SBC) - goto done; - - ret = recv(data->server.fd, sbc, sizeof(*sbc), 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - total += ret; - -done: - DBG("OK - %d bytes received", total); + int err; - if (pkt->length != (total - sizeof(struct ipc_packet))) { - SNDERR("Error while configuring device: packet size doesn't match"); - return -EINVAL; + 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); } - memcpy(&data->cfg, cfg, sizeof(*cfg)); - - DBG("Device configuration:"); - - DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len, - data->cfg.sample_size, data->cfg.rate); + return err; +} - if (data->cfg.codec == CFG_CODEC_SBC) { - ret = bluetooth_a2dp_init(data, sbc); - if (ret < 0) - return ret; +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); } - ret = bluetooth_recvmsg_fd(data); - if (ret < 0) - return ret; + return err; +} - if (data->stream.fd == -1) { - SNDERR("Error while configuring device: could not acquire audio socket"); - return -EINVAL; +static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg, + int expected_type) +{ + int err = audioservice_recv(sk, outmsg); + if (err == 0) { + if (outmsg->msg_type != expected_type) { + err = -EINVAL; + SNDERR("Bogus message %s received while " + "%s was expected", + bt_audio_strmsg(outmsg->msg_type), + bt_audio_strmsg(expected_type)); + } } - - /* It is possible there is some outstanding - data in the pipe - we have to empty it */ - while (recv(data->stream.fd, data->buffer, data->cfg.pkt_len, - MSG_DONTWAIT) > 0); - - memset(data->buffer, 0, sizeof(data->buffer)); - - return 0; + return err; } static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, snd_config_t *conf) { int sk, err; - struct sockaddr_un addr = { - AF_UNIX, IPC_SOCKET_NAME - }; - - if (!data) - return -EINVAL; + struct bluetooth_alsa_config *alsa_conf = &data->alsa_config; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + 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 = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { - err = errno; - SNDERR("Cannot open socket: %s (%d)", strerror(err), err); - return -err; - } - - DBG("Connecting to address: %s", addr.sun_path + 1); - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = errno; - SNDERR("Connection fail", strerror(err), err); - close(sk); - return -err; + sk = bt_audio_service_open(); + if(sk <= 0) { + err = -errno; + goto failed; } data->server.fd = sk; @@ -1296,14 +1304,56 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, data->pipefd[0] = -1; data->pipefd[1] = -1; - if (pipe(data->pipefd) < 0) - return -errno; - if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) - return -errno; - if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) - return -errno; + 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; + + getcaps_req->access_mode = (stream == SND_PCM_STREAM_PLAYBACK ? + BT_CAPABILITIES_ACCESS_MODE_WRITE : + BT_CAPABILITIES_ACCESS_MODE_READ); + + err = audioservice_send(data->server.fd, &getcaps_req->h); + if (err < 0) + goto failed; + + err = audioservice_expect(data->server.fd, &getcaps_rsp->h, BT_GETCAPABILITIES_RSP); + if (err < 0) + goto failed; + + if (getcaps_rsp->posix_errno != 0) { + SNDERR("BT_GETCAPABILITIES failed : %s(%d)", + strerror(getcaps_rsp->posix_errno), + getcaps_rsp->posix_errno); + return -getcaps_rsp->posix_errno; + } + + data->transport = getcaps_rsp->transport; + data->link_mtu = getcaps_rsp->link_mtu; + if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP) + data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities; - return bluetooth_cfg(data, stream, conf); + return 0; + +failed: + bt_audio_service_close(sk); + return err; } SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) @@ -1329,7 +1379,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->io.mmap_rw = 0; /* No direct mmap communication */ data->io.private_data = data; - if (data->cfg.codec == CFG_CODEC_SBC) + if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? &bluetooth_a2dp_playback : &bluetooth_a2dp_capture; @@ -1342,7 +1392,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) if (err < 0) goto error; - if (data->cfg.codec == CFG_CODEC_SBC) + if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) err = bluetooth_a2dp_hw_constraint(&data->io); else err = bluetooth_hsp_hw_constraint(&data->io); |