static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { struct bluetooth_data *data = io->private_data; char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_open_req *open_req = (void *) buf; struct bt_open_rsp *open_rsp = (void *) buf; struct bt_set_configuration_req *req = (void *) buf; struct bt_set_configuration_rsp *rsp = (void *) buf; int err; DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", io->period_size, io->buffer_size); memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); open_req->h.type = BT_REQUEST; open_req->h.name = BT_OPEN; open_req->h.length = sizeof(*open_req); strncpy(open_req->destination, data->alsa_config.device, 18); open_req->seid = BT_A2DP_SEID_RANGE + 1; open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ? BT_WRITE_LOCK : BT_READ_LOCK); err = audioservice_send(data->server.fd, &open_req->h); if (err < 0) return err; open_rsp->h.length = sizeof(*open_rsp); err = audioservice_expect(data->server.fd, &open_rsp->h, BT_OPEN); if (err < 0) return err; memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); req->h.type = BT_REQUEST; req->h.name = BT_SET_CONFIGURATION; req->h.length = sizeof(*req); req->codec.transport = BT_CAPABILITIES_TRANSPORT_SCO; req->codec.seid = BT_A2DP_SEID_RANGE + 1; req->codec.length = sizeof(pcm_capabilities_t); req->h.length += req->codec.length - sizeof(req->codec); err = audioservice_send(data->server.fd, &req->h); if (err < 0) return err; rsp->h.length = sizeof(*rsp); err = audioservice_expect(data->server.fd, &rsp->h, BT_SET_CONFIGURATION); if (err < 0) return err; data->transport = BT_CAPABILITIES_TRANSPORT_SCO; data->link_mtu = rsp->link_mtu; return 0; }
static int bluetooth_configure(struct bluetooth_data *data) { char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_get_capabilities_req *getcaps_req = (void*) buf; struct bt_get_capabilities_rsp *getcaps_rsp = (void*) buf; int err; DBG("bluetooth_configure"); data->state = A2DP_STATE_CONFIGURING; memset(getcaps_req, 0, BT_SUGGESTED_BUFFER_SIZE); getcaps_req->h.type = BT_REQUEST; getcaps_req->h.name = BT_GET_CAPABILITIES; getcaps_req->flags = 0; getcaps_req->flags |= BT_FLAG_AUTOCONNECT; strncpy(getcaps_req->destination, data->address, 18); getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; getcaps_req->h.length = sizeof(*getcaps_req); err = audioservice_send(data, &getcaps_req->h); if (err < 0) { ERR("audioservice_send failed for BT_GETCAPABILITIES_REQ\n"); goto error; } getcaps_rsp->h.length = 0; err = audioservice_expect(data, &getcaps_rsp->h, BT_GET_CAPABILITIES); if (err < 0) { ERR("audioservice_expect failed for BT_GETCAPABILITIES_RSP\n"); goto error; } err = bluetooth_parse_capabilities(data, getcaps_rsp); if (err < 0) { ERR("bluetooth_parse_capabilities failed err: %d", err); goto error; } err = bluetooth_a2dp_hw_params(data); if (err < 0) { ERR("bluetooth_a2dp_hw_params failed err: %d", err); goto error; } set_state(data, A2DP_STATE_CONFIGURED); return 0; error: if (data->state == A2DP_STATE_CONFIGURING) { bluetooth_close(data); /* notify client that thread is ready for next command */ pthread_cond_signal(&data->client_wait); } return err; }
static int bluetooth_stop(struct bluetooth_data *data) { char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_stop_stream_req *stop_req = (void*) buf; struct bt_stop_stream_rsp *stop_rsp = (void*) buf; int err; DBG("bluetooth_stop"); data->state = A2DP_STATE_STOPPING; l2cap_set_flushable(data->stream.fd, 0); if (data->stream.fd >= 0) { close(data->stream.fd); data->stream.fd = -1; } /* send stop request */ memset(stop_req, 0, BT_SUGGESTED_BUFFER_SIZE); stop_req->h.type = BT_REQUEST; stop_req->h.name = BT_STOP_STREAM; stop_req->h.length = sizeof(*stop_req); err = audioservice_send(data, &stop_req->h); if (err < 0) goto error; stop_rsp->h.length = sizeof(*stop_rsp); err = audioservice_expect(data, &stop_rsp->h, BT_STOP_STREAM); if (err < 0) goto error; error: if (data->state == A2DP_STATE_STOPPING) set_state(data, A2DP_STATE_CONFIGURED); return err; }
static void bluetooth_close(struct bluetooth_data *data) { DBG("bluetooth_close"); if (data->server.fd >= 0) { // sending BT_CLOSE to cleanup unix socket. char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_close_req *close_req = (void*) buf; struct bt_close_rsp *close_rsp = (void*) buf; int err; memset(close_req, 0, BT_SUGGESTED_BUFFER_SIZE); close_req->h.type = BT_REQUEST; close_req->h.name = BT_CLOSE; close_req->h.length = sizeof(*close_req); err = audioservice_send(data, &close_req->h); if (err < 0) { ERR("audioservice_send failed for BT_CLOSE_REQ\n"); } else { close_rsp->h.length = 0; err = audioservice_expect(data, &close_rsp->h, BT_CLOSE); if (err < 0) { ERR("audioservice_expect failed for BT_CLOSE_RSP\n"); } } bt_audio_service_close(data->server.fd); data->server.fd = -1; } if (data->stream.fd >= 0) { close(data->stream.fd); data->stream.fd = -1; } data->state = A2DP_STATE_NONE; }
static int bluetooth_a2dp_hw_params(struct bluetooth_data *data) { char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_open_req *open_req = (void *) buf; struct bt_open_rsp *open_rsp = (void *) buf; struct bt_set_configuration_req *setconf_req = (void*) buf; struct bt_set_configuration_rsp *setconf_rsp = (void*) buf; int err; memset(open_req, 0, BT_SUGGESTED_BUFFER_SIZE); open_req->h.type = BT_REQUEST; open_req->h.name = BT_OPEN; open_req->h.length = sizeof(*open_req); strncpy(open_req->destination, data->address, 18); open_req->seid = data->sbc_capabilities.capability.seid; open_req->lock = BT_WRITE_LOCK; err = audioservice_send(data, &open_req->h); if (err < 0) return err; open_rsp->h.length = sizeof(*open_rsp); err = audioservice_expect(data, &open_rsp->h, BT_OPEN); if (err < 0) return err; err = bluetooth_a2dp_init(data); if (err < 0) return err; memset(setconf_req, 0, BT_SUGGESTED_BUFFER_SIZE); setconf_req->h.type = BT_REQUEST; setconf_req->h.name = BT_SET_CONFIGURATION; setconf_req->h.length = sizeof(*setconf_req); memcpy(&setconf_req->codec, &data->sbc_capabilities, sizeof(data->sbc_capabilities)); setconf_req->codec.transport = BT_CAPABILITIES_TRANSPORT_A2DP; setconf_req->codec.length = sizeof(data->sbc_capabilities); setconf_req->h.length += setconf_req->codec.length - sizeof(setconf_req->codec); DBG("bluetooth_a2dp_hw_params sending configuration:\n"); switch (data->sbc_capabilities.channel_mode) { case BT_A2DP_CHANNEL_MODE_MONO: DBG("\tchannel_mode: MONO\n"); break; case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: DBG("\tchannel_mode: DUAL CHANNEL\n"); break; case BT_A2DP_CHANNEL_MODE_STEREO: DBG("\tchannel_mode: STEREO\n"); break; case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: DBG("\tchannel_mode: JOINT STEREO\n"); break; default: DBG("\tchannel_mode: UNKNOWN (%d)\n", data->sbc_capabilities.channel_mode); } switch (data->sbc_capabilities.frequency) { case BT_SBC_SAMPLING_FREQ_16000: DBG("\tfrequency: 16000\n"); break; case BT_SBC_SAMPLING_FREQ_32000: DBG("\tfrequency: 32000\n"); break; case BT_SBC_SAMPLING_FREQ_44100: DBG("\tfrequency: 44100\n"); break; case BT_SBC_SAMPLING_FREQ_48000: DBG("\tfrequency: 48000\n"); break; default: DBG("\tfrequency: UNKNOWN (%d)\n", data->sbc_capabilities.frequency); } switch (data->sbc_capabilities.allocation_method) { case BT_A2DP_ALLOCATION_SNR: DBG("\tallocation_method: SNR\n"); break; case BT_A2DP_ALLOCATION_LOUDNESS: DBG("\tallocation_method: LOUDNESS\n"); break; default: DBG("\tallocation_method: UNKNOWN (%d)\n", data->sbc_capabilities.allocation_method); } switch (data->sbc_capabilities.subbands) { case BT_A2DP_SUBBANDS_4: DBG("\tsubbands: 4\n"); break; case BT_A2DP_SUBBANDS_8: DBG("\tsubbands: 8\n"); break; default: DBG("\tsubbands: UNKNOWN (%d)\n", data->sbc_capabilities.subbands); } switch (data->sbc_capabilities.block_length) { case BT_A2DP_BLOCK_LENGTH_4: DBG("\tblock_length: 4\n"); break; case BT_A2DP_BLOCK_LENGTH_8: DBG("\tblock_length: 8\n"); break; case BT_A2DP_BLOCK_LENGTH_12: DBG("\tblock_length: 12\n"); break; case BT_A2DP_BLOCK_LENGTH_16: DBG("\tblock_length: 16\n"); break; default: DBG("\tblock_length: UNKNOWN (%d)\n", data->sbc_capabilities.block_length); } DBG("\tmin_bitpool: %d\n", data->sbc_capabilities.min_bitpool); DBG("\tmax_bitpool: %d\n", data->sbc_capabilities.max_bitpool); err = audioservice_send(data, &setconf_req->h); if (err < 0) return err; err = audioservice_expect(data, &setconf_rsp->h, BT_SET_CONFIGURATION); if (err < 0) return err; data->link_mtu = setconf_rsp->link_mtu; #ifdef TN_JPN_NTT_BT_SCMS-T if (setconf_rsp->content_protection == CP_TYPE_SCMS_T) { data->sizeof_scms_t = 1; data->scms_t_cp_header = SCMS_T_COPY_NOT_ALLOWED; } else { data->sizeof_scms_t = 0; data->scms_t_cp_header = SCMS_T_COPY_ALLOWED; } DBG("MTU: %d -- SCMS-T Enabled: %d", data->link_mtu, setconf_rsp->content_protection); #else DBG("MTU: %d", data->link_mtu); #endif /* Setup SBC encoder now we agree on parameters */ bluetooth_a2dp_setup(data); DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", data->sbc.allocation, data->sbc.subbands, data->sbc.blocks, data->sbc.bitpool); return 0; }
static int bluetooth_start(struct bluetooth_data *data) { char c = 'w'; char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_start_stream_req *start_req = (void*) buf; struct bt_start_stream_rsp *start_rsp = (void*) buf; struct bt_new_stream_ind *streamfd_ind = (void*) buf; int opt_name, err, bytes; DBG("bluetooth_start"); data->state = A2DP_STATE_STARTING; /* send start */ memset(start_req, 0, BT_SUGGESTED_BUFFER_SIZE); start_req->h.type = BT_REQUEST; start_req->h.name = BT_START_STREAM; start_req->h.length = sizeof(*start_req); err = audioservice_send(data, &start_req->h); if (err < 0) goto error; start_rsp->h.length = sizeof(*start_rsp); err = audioservice_expect(data, &start_rsp->h, BT_START_STREAM); if (err < 0) goto error; streamfd_ind->h.length = sizeof(*streamfd_ind); err = audioservice_expect(data, &streamfd_ind->h, BT_NEW_STREAM); if (err < 0) goto error; data->stream.fd = bt_audio_service_get_data_fd(data->server.fd); if (data->stream.fd < 0) { ERR("bt_audio_service_get_data_fd failed, errno: %d", errno); err = -errno; goto error; } l2cap_set_flushable(data->stream.fd, 1); data->stream.events = POLLOUT; /* set our socket buffer to the size of PACKET_BUFFER_COUNT packets */ bytes = data->link_mtu * PACKET_BUFFER_COUNT; setsockopt(data->stream.fd, SOL_SOCKET, SO_SNDBUF, &bytes, sizeof(bytes)); #ifdef TN_JPN_NTT_BT_SCMS-T data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload) + data->sizeof_scms_t; #else data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); #endif data->frame_count = 0; data->samples = 0; data->nsamples = 0; data->seq_num = 0; data->frame_count = 0; data->next_write = 0; set_state(data, A2DP_STATE_STARTED); return 0; error: /* close bluetooth connection to force reinit and reconfiguration */ if (data->state == A2DP_STATE_STARTING) { bluetooth_close(data); /* notify client that thread is ready for next command */ pthread_cond_signal(&data->client_wait); } return err; }
static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { struct bluetooth_data *data = io->private_data; struct bluetooth_a2dp *a2dp = &data->a2dp; char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_open_req *open_req = (void *) buf; struct bt_open_rsp *open_rsp = (void *) buf; struct bt_set_configuration_req *req = (void *) buf; struct bt_set_configuration_rsp *rsp = (void *) buf; int err; DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", io->period_size, io->buffer_size); memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); open_req->h.type = BT_REQUEST; open_req->h.name = BT_OPEN; open_req->h.length = sizeof(*open_req); strncpy(open_req->destination, data->alsa_config.device, 18); open_req->seid = a2dp->sbc_capabilities.capability.seid; open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ? BT_WRITE_LOCK : BT_READ_LOCK); err = audioservice_send(data->server.fd, &open_req->h); if (err < 0) return err; open_rsp->h.length = sizeof(*open_rsp); err = audioservice_expect(data->server.fd, &open_rsp->h, BT_OPEN); if (err < 0) return err; err = bluetooth_a2dp_init(data, params); if (err < 0) return err; memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); req->h.type = BT_REQUEST; req->h.name = BT_SET_CONFIGURATION; req->h.length = sizeof(*req); memcpy(&req->codec, &a2dp->sbc_capabilities, sizeof(a2dp->sbc_capabilities)); req->codec.transport = BT_CAPABILITIES_TRANSPORT_A2DP; req->codec.length = sizeof(a2dp->sbc_capabilities); req->h.length += req->codec.length - sizeof(req->codec); err = audioservice_send(data->server.fd, &req->h); if (err < 0) return err; rsp->h.length = sizeof(*rsp); err = audioservice_expect(data->server.fd, &rsp->h, BT_SET_CONFIGURATION); if (err < 0) return err; data->transport = BT_CAPABILITIES_TRANSPORT_A2DP; data->link_mtu = rsp->link_mtu; /* Setup SBC encoder now we agree on parameters */ bluetooth_a2dp_setup(a2dp); 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_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; char c = 'w'; char buf[BT_SUGGESTED_BUFFER_SIZE]; struct bt_start_stream_req *req = (void *) buf; struct bt_start_stream_rsp *rsp = (void *) buf; struct bt_new_stream_ind *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 */ data->hw_ptr = 0; else /* ALSA library is really picky on the fact hw_ptr is not null. * If it is, capture won't start */ data->hw_ptr = io->period_size; /* send start */ memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); req->h.type = BT_REQUEST; req->h.name = BT_START_STREAM; req->h.length = sizeof(*req); err = audioservice_send(data->server.fd, &req->h); if (err < 0) return err; rsp->h.length = sizeof(*rsp); err = audioservice_expect(data->server.fd, &rsp->h, BT_START_STREAM); if (err < 0) return err; ind->h.length = sizeof(*ind); err = audioservice_expect(data->server.fd, &ind->h, BT_NEW_STREAM); 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; } if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDTIMEO : SO_RCVTIMEO; 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, sizeof(period_count)) == 0) return 0; opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDBUF : SO_RCVBUF; if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) return 0; /* FIXME : handle error codes */ } /* wake up any client polling at us */ if (write(data->pipefd[1], &c, 1) < 0) { err = -errno; return err; } return 0; }