diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/Makefile.am | 14 | ||||
-rw-r--r-- | audio/ctl_bluetooth.c | 134 | ||||
-rw-r--r-- | audio/device.c | 2 | ||||
-rw-r--r-- | audio/gsta2dpsink.c | 746 | ||||
-rw-r--r-- | audio/gsta2dpsink.h | 8 | ||||
-rw-r--r-- | audio/gstsbcenc.c | 276 | ||||
-rw-r--r-- | audio/gstsbcenc.h | 4 | ||||
-rw-r--r-- | audio/gstsbcparse.c | 49 | ||||
-rw-r--r-- | audio/gstsbcutil.c | 174 | ||||
-rw-r--r-- | audio/gstsbcutil.h | 9 | ||||
-rw-r--r-- | audio/ipc.c | 119 | ||||
-rw-r--r-- | audio/ipc.h | 367 | ||||
-rw-r--r-- | audio/manager.c | 4 | ||||
-rw-r--r-- | audio/pcm_bluetooth.c | 786 | ||||
-rw-r--r-- | audio/unix.c | 433 |
15 files changed, 1843 insertions, 1282 deletions
diff --git a/audio/Makefile.am b/audio/Makefile.am index 48c2a80f..b04fc302 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -3,7 +3,7 @@ if AUDIOSERVICE if CONFIGFILES confdir = $(sysconfdir)/bluetooth -conf_DATA = audio.service +conf_DATA = audio.service audio.conf endif servicedir = $(libdir)/bluetooth @@ -11,7 +11,7 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ - manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \ + manager.h manager.c headset.h headset.c ipc.h ipc.c unix.h unix.c \ error.h error.c device.h device.c gateway.h gateway.c \ sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h control.c control.h @@ -23,12 +23,12 @@ alsadir = $(libdir)/alsa-lib alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la -libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h rtp.h +libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c rtp.h ipc.h ipc.c libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.* libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ -libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h +libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c rtp.h ipc.h ipc.c libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_ctl_.* libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @@ -39,12 +39,13 @@ gstreamerdir = $(libdir)/gstreamer-0.10 gstreamer_LTLIBRARIES = libgstbluetooth.la -libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h rtp.h \ +libgstbluetooth_la_SOURCES = gstbluetooth.c \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ gsta2dpsink.h gsta2dpsink.c \ - gstsbcutil.h gstsbcutil.c + gstsbcutil.h gstsbcutil.c \ + rtp.h ipc.h ipc.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ @@ -58,3 +59,4 @@ INCLUDES = -I$(top_srcdir)/common EXTRA_DIST = audio.service audio.conf audio-api.txt test-audio asound.conf MAINTAINERCLEANFILES = Makefile.in + diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 9e1c320c..5c198b1a 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -65,7 +65,7 @@ static void bluetooth_exit(struct bluetooth_data *data) return; if (data->sock >= 0) - close(data->sock); + bt_audio_service_close(data->sock); free(data); } @@ -141,32 +141,49 @@ static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, } static int bluetooth_send_ctl(struct bluetooth_data *data, - struct ipc_packet *pkt, int len) + uint8_t mode, uint8_t key, struct bt_control_rsp *ctl_rsp) { int ret; + struct bt_control_req *ctl_req = (void *) ctl_rsp; + const char *type; - ret = send(data->sock, pkt, len, MSG_NOSIGNAL); + memset(ctl_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + ctl_req->h.msg_type = BT_CONTROL_REQ; + ctl_req->mode = mode; + ctl_req->key = key; + + ret = send(data->sock, ctl_req, BT_AUDIO_IPC_PACKET_SIZE, MSG_NOSIGNAL); if (ret <= 0) { SYSERR("Unable to request new volume value to server"); return -errno; } - ret = recv(data->sock, pkt, len, 0); + ret = recv(data->sock, ctl_rsp, BT_AUDIO_IPC_PACKET_SIZE, 0); if (ret <= 0) { - SYSERR("Unable to receive new volume value from server"); + SNDERR("Unable to receive new volume value from server"); return -errno; } - if(pkt->type != PKT_TYPE_CTL_RSP) { - SNDERR("Unexpected packet type %d received", pkt->type); + type = bt_audio_strmsg(ctl_rsp->h.msg_type); + if (!type) { + SNDERR("Bogus message type %d " + "received from audio service", + ctl_rsp->h.msg_type); return -EINVAL; } - if(pkt->length != sizeof(struct ipc_data_ctl)) { - SNDERR("Unexpected packet length %d received", pkt->length); + if (ctl_rsp->h.msg_type != BT_CONTROL_RSP) { + SNDERR("Unexpected message %s received", type); return -EINVAL; } + if (ctl_rsp->posix_errno != 0) { + SNDERR("BT_CONTROL failed : %s (%d)", + strerror(ctl_rsp->posix_errno), + ctl_rsp->posix_errno); + return -ctl_rsp->posix_errno; + } + return 0; } @@ -174,28 +191,21 @@ static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { struct bluetooth_data *data = ext->private_data; - struct ipc_packet *pkt; - struct ipc_data_ctl *ctl; - int len, ret; + int ret; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_control_rsp *rsp = (void *) buf; DBG("ext %p key %ld", ext, key); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); - pkt = malloc(len); - memset(pkt, 0, len); + memset(buf, 0, sizeof(buf)); *value = 0; - pkt->type = PKT_TYPE_CTL_REQ; - pkt->length = sizeof(struct ipc_data_ctl); - ctl = (struct ipc_data_ctl *) pkt->data; - ctl->mode = key; - - if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0) + ret = bluetooth_send_ctl(data, key, 0, rsp); + if (ret < 0) goto done; - *value = ctl->key; + *value = rsp->key; done: - free(pkt); return ret; } @@ -203,38 +213,31 @@ static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { struct bluetooth_data *data = ext->private_data; - struct ipc_packet *pkt; - struct ipc_data_ctl *ctl; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_control_rsp *rsp = (void *) buf; long current; - int len, ret; + int ret, keyvalue; DBG("ext %p key %ld", ext, key); - if ((ret = bluetooth_read_integer(ext, key, ¤t)) < 0) + ret = bluetooth_read_integer(ext, key, ¤t); + if (ret < 0) return ret; if (*value == current) return 0; - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); - pkt = malloc(len); - memset(pkt, 0, len); - - pkt->length = sizeof(struct ipc_data_ctl); - ctl = (struct ipc_data_ctl *) pkt->data; - ctl->mode = key; - while (*value != current) { - pkt->type = PKT_TYPE_CTL_REQ; - ctl->key = (*value > current) ? CTL_KEY_VOL_UP : CTL_KEY_VOL_DOWN; + keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP : + BT_CONTROL_KEY_VOL_DOWN; - if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0) + ret = bluetooth_send_ctl(data, key, keyvalue, rsp); + if (ret < 0) break; - current = ctl->key; + current = keyvalue; } - free(pkt); return ret; } @@ -242,33 +245,31 @@ static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask) { struct bluetooth_data *data = ext->private_data; - struct ipc_packet *pkt; - struct ipc_data_ctl *ctl; - int len, ret; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_control_ind *ind = (void *) buf; + int ret; + const char *type; DBG("ext %p id %p", ext, id); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); - pkt = malloc(len); - memset(pkt, 0, len); - - ret = recv(data->sock, pkt, len, MSG_DONTWAIT); - if (ret <= 0) - return -errno; + memset(buf, 0, sizeof(buf)); - if(pkt->type != PKT_TYPE_CTL_NTFY) { - SNDERR("Unexpected packet type %d received!", pkt->type); + ret = recv(data->sock, ind, BT_AUDIO_IPC_PACKET_SIZE, MSG_DONTWAIT); + type = bt_audio_strmsg(ind->h.msg_type); + if (!type) { + SNDERR("Bogus message type %d " + "received from audio service", + ind->h.msg_type); return -EAGAIN; } - if(pkt->length != sizeof(struct ipc_data_ctl)) { - SNDERR("Unexpected packet length %d received", pkt->length); + if (ind->h.msg_type != BT_CONTROL_IND) { + SNDERR("Unexpected message %s received", type); return -EAGAIN; } - ctl = (struct ipc_data_ctl *) pkt->data; snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, ctl->mode == BLUETOOTH_PLAYBACK ? + snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ? vol_devices[BLUETOOTH_PLAYBACK] : vol_devices[BLUETOOTH_CAPTURE]); *event_mask = SND_CTL_EVENT_MASK_VALUE; @@ -290,10 +291,7 @@ static snd_ctl_ext_callback_t bluetooth_callback = { static int bluetooth_init(struct bluetooth_data *data) { - int sk, err, id; - struct sockaddr_un addr = { - AF_UNIX, IPC_SOCKET_NAME - }; + int sk; if (!data) return -EINVAL; @@ -302,21 +300,9 @@ static int bluetooth_init(struct bluetooth_data *data) data->sock = -1; - id = abs(getpid() * rand()); - - if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - err = -errno; - SNDERR("Can't open socket"); + sk = bt_audio_service_open(); + if (sk < 0) return -errno; - } - - DBG("Connecting to address: %s", addr.sun_path + 1); - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - SNDERR("Can't connect socket"); - close(sk); - return err; - } data->sock = sk; diff --git a/audio/device.c b/audio/device.c index c86f711d..a5b9b0a9 100644 --- a/audio/device.c +++ b/audio/device.c @@ -450,7 +450,6 @@ static headset_state_t ipc_to_hs_state(uint8_t ipc_state) return HEADSET_STATE_DISCONNECTED; } } -#endif static uint8_t avdtp_to_ipc_state(avdtp_state_t state) { @@ -504,6 +503,7 @@ uint8_t device_get_state(struct device *dev) return STATE_DISCONNECTED; } +#endif gboolean device_is_connected(struct device *dev, const char *interface) { diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index e605158f..535f6c5e 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -41,14 +41,6 @@ #include "gsta2dpsink.h" -enum { - NOT_CONFIGURED, - CONFIGURING_INIT, - CONFIGURING_SENT_CONF, - CONFIGURING_RCVD_DEV_CONF, - CONFIGURED -}; - GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); #define GST_CAT_DEFAULT a2dp_sink_debug @@ -62,24 +54,10 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); g_mutex_unlock (s->sink_lock); \ } G_STMT_END -#define GST_A2DP_SINK_WAIT_CON_END(s) G_STMT_START { \ - s->waiting_con_conf = TRUE; \ - g_cond_wait (s->con_conf_end, s->sink_lock); \ - s->waiting_con_conf = FALSE; \ - } G_STMT_END - -#define GST_A2DP_SINK_CONFIGURATION_FAIL(s) G_STMT_START { \ - s->con_state = NOT_CONFIGURED; \ - g_cond_signal (s->con_conf_end); \ - } G_STMT_END - -#define GST_A2DP_SINK_CONFIGURATION_SUCCESS(s) G_STMT_START { \ - s->con_state = CONFIGURED; \ - g_cond_signal (s->con_conf_end); \ - } G_STMT_END struct bluetooth_data { - struct ipc_data_cfg cfg; /* Bluetooth device config */ + struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */ + gint link_mtu; int samples; /* Number of encoded samples */ gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ gsize count; /* Codec transfer buffer counter */ @@ -121,6 +99,13 @@ static GstStaticPadTemplate a2dp_sink_factory = "rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ]")); +static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, + const bt_audio_msg_header_t *msg); +static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type); + + static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -137,8 +122,6 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) GST_INFO_OBJECT(self, "stop"); - self->con_state = NOT_CONFIGURED; - if (self->watch_id != 0) { g_source_remove(self->watch_id); self->watch_id = 0; @@ -152,7 +135,7 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) } if (self->server) { - g_io_channel_close(self->server); + bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); g_io_channel_unref(self->server); self->server = NULL; } @@ -162,9 +145,9 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) self->data = NULL; } - if (self->sbc) { - g_free(self->sbc); - self->sbc = NULL; + if (self->stream_caps) { + gst_caps_unref(self->stream_caps); + self->stream_caps = NULL; } if (self->dev_caps) { @@ -185,12 +168,6 @@ static void gst_a2dp_sink_finalize(GObject *object) if (self->device) g_free(self->device); - /* unlock any thread waiting for this signal */ - GST_A2DP_SINK_MUTEX_LOCK(self); - GST_A2DP_SINK_CONFIGURATION_FAIL(self); - GST_A2DP_SINK_MUTEX_UNLOCK(self); - - g_cond_free(self->con_conf_end); g_mutex_free(self->sink_lock); G_OBJECT_CLASS(parent_class)->finalize(object); @@ -232,19 +209,10 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) { - char cmsg_b[CMSG_SPACE(sizeof(int))], m; - int err, ret, stream_fd; - 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(g_io_channel_unix_get_fd(sink->server), &msgh, 0); + int err, ret; + + ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server)); + if (ret < 0) { err = errno; GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", @@ -252,76 +220,20 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) return -err; } - /* 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) { - stream_fd = (*(int *) CMSG_DATA(cmsg)); - sink->stream = g_io_channel_unix_new(stream_fd); - - GST_DEBUG_OBJECT(sink, "stream_fd=%d", stream_fd); - return 0; - } - } - - return -EINVAL; -} - -static void gst_a2dp_sink_check_dev_caps(GstA2dpSink *self) -{ - GstStructure *structure; - GstCaps *dev_caps; - gint channels; - - structure = gst_caps_get_structure(self->dev_caps, 0); - if (!gst_structure_get_int(structure, "channels", &channels)) - channels = 2; /* FIXME how to get channels */ - dev_caps = gst_sbc_caps_from_sbc(&(self->data->cfg), self->sbc, - channels); - - self->new_dev_caps = TRUE; - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_ref(dev_caps); - - -} - -static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self, - struct ipc_codec_sbc *sbc) -{ - struct bluetooth_data *data = self->data; - struct ipc_data_cfg *cfg = &data->cfg; - - if (cfg == NULL) { - GST_ERROR_OBJECT(self, "Error getting codec parameters"); - return -1; - } - - if (cfg->codec != CFG_CODEC_SBC) - return -1; - - data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - - GST_DEBUG_OBJECT(self, "Codec parameters: " - "\tallocation=%u\n\tsubbands=%u\n " - "\tblocks=%u\n\tbitpool=%u\n", - sbc->allocation, sbc->subbands, - sbc->blocks, sbc->bitpool); + sink->stream = g_io_channel_unix_new(ret); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); return 0; } static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, GstCaps *caps, - struct ipc_packet *pkt) + sbc_capabilities_t *pkt) { - - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; + sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities; const GValue *value = NULL; const char *pref, *name; - GstStructure *structure = gst_caps_get_structure(caps,0); + GstStructure *structure = gst_caps_get_structure(caps, 0); name = gst_structure_get_name(structure); /* FIXME only sbc supported here, should suport mp3 */ @@ -330,26 +242,21 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, return FALSE; } - if (sink->device) - strncpy(pkt->device, sink->device, 18); - - pkt->role = PKT_ROLE_HIFI; - value = gst_structure_get_value(structure, "rate"); - cfg->rate = g_value_get_int(value); + cfg->frequency = g_value_get_int(value); value = gst_structure_get_value(structure, "mode"); pref = g_value_get_string(value); if (strcmp(pref, "auto") == 0) - cfg->mode = CFG_MODE_AUTO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; else if (strcmp(pref, "mono") == 0) - cfg->mode = CFG_MODE_MONO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; else if (strcmp(pref, "dual") == 0) - cfg->mode = CFG_MODE_DUAL_CHANNEL; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; else if (strcmp(pref, "stereo") == 0) - cfg->mode = CFG_MODE_STEREO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; else if (strcmp(pref, "joint") == 0) - cfg->mode = CFG_MODE_JOINT_STEREO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; else { GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); return FALSE; @@ -358,107 +265,27 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, value = gst_structure_get_value(structure, "allocation"); pref = g_value_get_string(value); if (strcmp(pref, "auto") == 0) - sbc->allocation = CFG_ALLOCATION_AUTO; + cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; else if (strcmp(pref, "loudness") == 0) - sbc->allocation = CFG_ALLOCATION_LOUDNESS; + cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; else if (strcmp(pref, "snr") == 0) - sbc->allocation = CFG_ALLOCATION_SNR; + cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; else { GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); return FALSE; } value = gst_structure_get_value(structure, "subbands"); - sbc->subbands = g_value_get_int(value); + cfg->subbands = g_value_get_int(value); value = gst_structure_get_value(structure, "blocks"); - sbc->blocks = g_value_get_int(value); + cfg->block_length = g_value_get_int(value); + /* FIXME min and max ??? */ value = gst_structure_get_value(structure, "bitpool"); - sbc->bitpool = g_value_get_int(value); - - pkt->length = sizeof(*cfg) + sizeof(*sbc); - pkt->type = PKT_TYPE_CFG_REQ; - pkt->error = PKT_ERROR_NONE; - - return TRUE; -} - -static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) -{ - gchar buf[IPC_MTU]; - GIOError io_error; - gsize ret; - gint total; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; - - memset(buf, 0, sizeof(buf)); - - io_error = g_io_channel_read(sink->server, (gchar *) buf, - sizeof(*pkt) + sizeof(*cfg), &ret); - if (io_error != G_IO_ERROR_NONE && ret > 0) { - GST_ERROR_OBJECT(sink, "Error ocurred while receiving " - "configurarion packet answer"); - return FALSE; - } - - total = ret; - if (pkt->type != PKT_TYPE_CFG_RSP) { - GST_ERROR_OBJECT(sink, "Unexpected packet type %d " - "received", pkt->type); - return FALSE; - } - - if (pkt->error != PKT_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error %d while configuring " - "device", pkt->error); - return FALSE; - } - - if (cfg->codec != CFG_CODEC_SBC) { - GST_ERROR_OBJECT(sink, "Unsupported format"); - return FALSE; - } - - io_error = g_io_channel_read(sink->server, (gchar *) sbc, - sizeof(*sbc), &ret); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error while reading data from socket " - "%s (%d)", strerror(errno), errno); - return FALSE; - } else if (ret == 0) { - GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); - return FALSE; - } - - total += ret; - GST_DEBUG_OBJECT(sink, "OK - %d bytes received", total); - - if (pkt->length != (total - sizeof(struct ipc_packet))) { - GST_ERROR_OBJECT(sink, "Error while configuring device: " - "packet size doesn't match"); - return FALSE; - } - - memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); - memcpy(sink->sbc, sbc, sizeof(struct ipc_codec_sbc)); + cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); - gst_a2dp_sink_check_dev_caps(sink); - - GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t" - "fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - sink->stream, sink->data->cfg.fd_opt, - sink->data->cfg.pkt_len, sink->data->cfg.sample_size, - sink->data->cfg.rate); - - if (sink->data->cfg.codec == CFG_CODEC_SBC) { - /* FIXME is this necessary? */ - ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); - if (ret < 0) - return FALSE; - } + memcpy(pkt, cfg, sizeof(*pkt)); return TRUE; } @@ -504,7 +331,7 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) GST_LOG_OBJECT(self, "emptying stream pipe"); while (1) { err = g_io_channel_read(self->stream, data->buffer, - (gsize) data->cfg.pkt_len, + (gsize) data->cfg.link_mtu, &read); if (err != G_IO_ERROR_NONE || read <= 0) break; @@ -531,33 +358,6 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) return TRUE; } -static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) -{ - /* - * We hold the lock, since we can send a signal. - * It is a good practice, according to the glib api. - */ - GST_A2DP_SINK_MUTEX_LOCK(sink); - - switch (sink->con_state) { - case CONFIGURING_SENT_CONF: - if (gst_a2dp_sink_conf_resp(sink)) - sink->con_state = CONFIGURING_RCVD_DEV_CONF; - else - GST_A2DP_SINK_CONFIGURATION_FAIL(sink); - break; - case CONFIGURING_RCVD_DEV_CONF: - if (gst_a2dp_sink_conf_recv_stream_fd(sink)) - GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink); - else - GST_A2DP_SINK_CONFIGURATION_FAIL(sink); - break; - } - - GST_A2DP_SINK_MUTEX_UNLOCK(sink); -} - - static gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { @@ -568,16 +368,217 @@ static gboolean server_callback(GIOChannel *chan, else if (cond & G_IO_ERR) { sink = GST_A2DP_SINK(data); GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); - } else if (cond & G_IO_IN) { - sink = GST_A2DP_SINK(data); - if (sink->con_state != NOT_CONFIGURED && - sink->con_state != CONFIGURED) - gst_a2dp_sink_conf_recv_data(sink); - else - GST_WARNING_OBJECT(sink, "Unexpected data received"); + } + + return TRUE; +} + +static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) +{ + sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities; + GstStructure *structure; + GValue *value; + GValue *list; + gchar *tmp; + + GST_LOG_OBJECT(self, "updating device caps"); + + structure = gst_structure_empty_new("audio/x-sbc"); + value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); + + /* mode */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); } else { - sink = GST_A2DP_SINK(data); - GST_WARNING_OBJECT(sink, "Unexpected callback call"); + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "mode", list); + g_free(list); + list = NULL; + } + + /* subbands */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + value = g_value_init(value, G_TYPE_INT); + if (sbc->subbands & BT_A2DP_SUBBANDS_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + if (sbc->subbands & BT_A2DP_SUBBANDS_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "subbands", list); + g_free(list); + list = NULL; + } + + /* blocks */ + value = g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { + g_value_set_int(value, 16); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { + g_value_set_int(value, 12); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "blocks", list); + g_free(list); + list = NULL; + } + + /* allocation */ + g_value_init(value, G_TYPE_STRING); + list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); + if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + } + if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "allocation", list); + g_free(list); + list = NULL; + } + + /* rate */ + g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* bitpool */ + value = g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool); + gst_structure_set_value(structure, "bitpool", value); + + /* channels */ + gst_value_set_int_range(value, 1, 2); + gst_structure_set_value(structure, "channels", value); + + g_free(value); + + self->dev_caps = gst_caps_new_full(structure, NULL); + + tmp = gst_caps_to_string(self->dev_caps); + GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); + g_free(tmp); + + return TRUE; +} + +static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) +{ + gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_req *req = (void *) buf; + struct bt_getcapabilities_rsp *rsp = (void *) buf; + GIOError io_error; + + memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + + req->h.msg_type = BT_GETCAPABILITIES_REQ; + strncpy(req->device, self->device, 18); + + req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + io_error = gst_a2dp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while asking device caps"); + } + + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + BT_GETCAPABILITIES_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while getting device caps"); + return FALSE; + } + + if (rsp->posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", + strerror(rsp->posix_errno), + rsp->posix_errno); + return FALSE; + } + + if (rsp->transport != BT_CAPABILITIES_TRANSPORT_A2DP) { + GST_ERROR_OBJECT(self, "Non a2dp answer from device"); + return FALSE; + } + + memcpy(&self->data->cfg, rsp, sizeof(*rsp)); + if (!gst_a2dp_sink_update_caps(self)) { + GST_WARNING_OBJECT(self, "failed to update capabilities"); + return FALSE; } return TRUE; @@ -586,7 +587,6 @@ static gboolean server_callback(GIOChannel *chan, static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) { GstA2dpSink *self = GST_A2DP_SINK(basesink); - struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; gint sk; gint err; @@ -594,107 +594,146 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) self->watch_id = 0; - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { + sk = bt_audio_service_open(); + if (sk <= 0) { err = errno; - GST_ERROR_OBJECT(self, "Cannot open socket: %s (%d)", - strerror(err), err); - return FALSE; - } - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = errno; - GST_ERROR_OBJECT(self, "Connection fail %s (%d)", - strerror(err), err); - close(sk); - return FALSE; + GST_ERROR_OBJECT(self, "Cannot open connection to bt " + "audio service: %s %d", strerror(err), err); + goto failed; } self->server = g_io_channel_unix_new(sk); - - self->watch_id = g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | - G_IO_ERR | G_IO_NVAL, server_callback, self); + self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | + G_IO_NVAL, server_callback, self); self->data = g_new0(struct bluetooth_data, 1); memset(self->data, 0, sizeof(struct bluetooth_data)); - self->sbc = g_new0(struct ipc_codec_sbc, 1); - self->stream = NULL; - self->con_state = NOT_CONFIGURED; - self->new_dev_caps = FALSE; - self->dev_caps = NULL; + self->stream_caps = NULL; - self->waiting_con_conf = FALSE; + if (!gst_a2dp_sink_get_capabilities(self)) + goto failed; return TRUE; + +failed: + bt_audio_service_close(sk); + return FALSE; } -static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) +static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self) { - gchar buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - gboolean ret; - gsize bytes_sent; + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *req = (void *) buf; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_datafd_ind *ind = (void*) buf; GIOError io_error; - g_assert(sink->con_state == NOT_CONFIGURED); + GST_DEBUG_OBJECT(self, "stream start"); - memset (pkt, 0, sizeof(buf)); - ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt); - if (!ret) { - GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps " - "to packet configuration"); + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_STREAMSTART_REQ; + + io_error = gst_a2dp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "start packet"); return FALSE; } - sink->con_state = CONFIGURING_INIT; + GST_DEBUG_OBJECT(self, "stream start packet sent"); - io_error = g_io_channel_write(sink->server, (gchar *) pkt, - sizeof(*pkt) + pkt->length, &bytes_sent); + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + BT_STREAMSTART_RSP); if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error ocurred while sending " - "configurarion packet"); - sink->con_state = NOT_CONFIGURED; + GST_ERROR_OBJECT(self, "Error while stream start confirmation"); + return FALSE; + } + + if (rsp->posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", + strerror(rsp->posix_errno), + rsp->posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream started"); + + io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h, + BT_STREAMFD_IND); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving stream fd"); return FALSE; } - GST_DEBUG_OBJECT(sink, "%d bytes sent", bytes_sent); - sink->con_state = CONFIGURING_SENT_CONF; + if (!gst_a2dp_sink_conf_recv_stream_fd(self)) + return FALSE; return TRUE; } -static gboolean gst_a2dp_sink_start_dev_conf(GstA2dpSink *sink, GstCaps *caps) +static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) { + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *req = (void *) buf; + struct bt_setconfiguration_rsp *rsp = (void *) buf; gboolean ret; + GIOError io_error; + + GST_DEBUG_OBJECT(self, "configuring device"); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_SETCONFIGURATION_REQ; + strncpy(req->device, self->device, 18); + ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities); + if (!ret) { + GST_ERROR_OBJECT(self, "Couldn't parse caps " + "to packet configuration"); + return FALSE; + } - g_assert(sink->con_state == NOT_CONFIGURED); + io_error = gst_a2dp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "configurarion packet"); + return FALSE; + } - GST_DEBUG_OBJECT(sink, "starting device configuration"); + GST_DEBUG_OBJECT(self, "configuration packet sent"); - ret = gst_a2dp_sink_send_conf_pkt(sink, caps); + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + BT_SETCONFIGURATION_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving device confirmation"); + return FALSE; + } - return ret; + if (rsp->posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)", + strerror(rsp->posix_errno), + rsp->posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "configuration set"); + + return TRUE; } static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, GstBuffer *buffer) { GstA2dpSink *sink = GST_A2DP_SINK(basesink); + gboolean ret; GST_A2DP_SINK_MUTEX_LOCK(sink); - if (sink->con_state == NOT_CONFIGURED) - gst_a2dp_sink_start_dev_conf(sink, GST_BUFFER_CAPS(buffer)); - - /* wait for the connection process to finish */ - if (sink->con_state != CONFIGURED) - GST_A2DP_SINK_WAIT_CON_END(sink); + ret = gst_a2dp_sink_stream_start(sink); GST_A2DP_SINK_MUTEX_UNLOCK(sink); - if (sink->con_state != CONFIGURED) + if (!ret) return GST_FLOW_ERROR; return GST_FLOW_OK; @@ -745,7 +784,7 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, encoded = GST_BUFFER_SIZE(buffer); - if (data->count + encoded >= data->cfg.pkt_len) { + if (data->count + encoded >= data->cfg.link_mtu) { ret = gst_a2dp_sink_avdtp_write(self); if (ret < 0) return GST_FLOW_ERROR; @@ -758,24 +797,28 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, return GST_FLOW_OK; } +static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink) +{ + GstA2dpSink *self = GST_A2DP_SINK(basesink); + + return self->dev_caps ? gst_caps_ref(self->dev_caps): NULL; +} + static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) { GstA2dpSink *self = GST_A2DP_SINK(basesink); + gboolean ret; GST_A2DP_SINK_MUTEX_LOCK(self); - if (self->con_state == NOT_CONFIGURED) { - gst_a2dp_sink_start_dev_conf(self, caps); + ret = gst_a2dp_sink_configure(self, caps); - if (self->dev_caps) - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_ref(caps); + if (self->stream_caps) + gst_caps_unref(self->stream_caps); + self->stream_caps = gst_caps_ref(caps); - /* we suppose the device will accept this caps */ - self->new_dev_caps = FALSE; - } GST_A2DP_SINK_MUTEX_UNLOCK(self); - return TRUE; + return ret; } static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) @@ -800,12 +843,7 @@ static GstFlowReturn gst_a2dp_sink_buffer_alloc(GstBaseSink *basesink, return GST_FLOW_ERROR; } - if (self->new_dev_caps && self->dev_caps) { - GST_INFO_OBJECT(self, "new caps from device"); - gst_buffer_set_caps(*buf, self->dev_caps); - self->new_dev_caps = FALSE; - } else - gst_buffer_set_caps(*buf, caps); + gst_buffer_set_caps(*buf, caps); GST_BUFFER_OFFSET(*buf) = offset; @@ -830,6 +868,7 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render); basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll); basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); + basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps); basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR(gst_a2dp_sink_buffer_alloc); @@ -847,12 +886,79 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { self->device = NULL; self->data = NULL; - self->sbc = NULL; self->stream = NULL; - self->con_state = NOT_CONFIGURED; - self->con_conf_end = g_cond_new(); - self->waiting_con_conf = FALSE; + self->dev_caps = NULL; + self->sink_lock = g_mutex_new(); } + +static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, + const bt_audio_msg_header_t *msg) +{ + gint err; + GIOError error; + gsize written; + + GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type)); + + error = g_io_channel_write(self->server, (const gchar*) msg, + BT_AUDIO_IPC_PACKET_SIZE, &written); + if (error != G_IO_ERROR_NONE) { + err = errno; + GST_ERROR_OBJECT(self, "Error sending data to audio service:" + " %s(%d)", strerror(err), err); + } + + return error; +} + +static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self, + bt_audio_msg_header_t *inmsg) +{ + GIOError status; + gsize bytes_read; + const char *type; + + status = g_io_channel_read(self->server, (gchar*) inmsg, + BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); + if (status != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error receiving data from service"); + return status; + } + + type = bt_audio_strmsg(inmsg->msg_type); + if (!type) { + GST_ERROR_OBJECT(self, "Bogus message type %d " + "received from audio service", + inmsg->msg_type); + return G_IO_ERROR_INVAL; + } + + GST_DEBUG_OBJECT(self, "Received %s", type); + + return status; +} + +static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type) +{ + GIOError status; + + status = gst_a2dp_sink_audioservice_recv(self, outmsg); + if (status != G_IO_ERROR_NONE) + return status; + + if (outmsg->msg_type != expected_type) { + GST_ERROR_OBJECT(self, "Bogus message %s " + "received while %s was expected", + bt_audio_strmsg(outmsg->msg_type), + bt_audio_strmsg(expected_type)); + return G_IO_ERROR_INVAL; + } + + return status; +} + diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index ea750406..f5b9b69b 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -49,15 +49,13 @@ struct _GstA2dpSink { GIOChannel *stream; struct bluetooth_data *data; - struct ipc_codec_sbc *sbc; GIOChannel *server; - gint con_state; + /* stream connection data */ + GstCaps *stream_caps; + GstCaps *dev_caps; - gboolean new_dev_caps; - GCond *con_conf_end; - gboolean waiting_con_conf; GMutex *sink_lock; guint watch_id; diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 54bb7f8a..021ecacf 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -27,14 +27,17 @@ #include <string.h> +#include "ipc.h" #include "gstsbcenc.h" #include "gstsbcutil.h" -#define SBC_ENC_DEFAULT_MODE CFG_MODE_AUTO -#define SBC_ENC_DEFAULT_BLOCKS 16 -#define SBC_ENC_DEFAULT_SUB_BANDS 8 -#define SBC_ENC_DEFAULT_BITPOOL 53 -#define SBC_ENC_DEFAULT_ALLOCATION CFG_ALLOCATION_AUTO +#define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_AUTO +#define SBC_ENC_DEFAULT_BLOCKS 0 +#define SBC_ENC_DEFAULT_SUB_BANDS 0 +#define SBC_ENC_DEFAULT_BITPOOL 0 +#define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_AUTO +#define SBC_ENC_DEFAULT_RATE 0 +#define SBC_ENC_DEFAULT_CHANNELS 0 GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug @@ -65,9 +68,9 @@ static GType gst_sbc_allocation_get_type(void) { static GType sbc_allocation_type = 0; static GEnumValue sbc_allocations[] = { - { CFG_ALLOCATION_AUTO, "Auto", "auto" }, - { CFG_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, - { CFG_ALLOCATION_SNR, "SNR", "snr" }, + { BT_A2DP_ALLOCATION_AUTO, "Auto", "auto" }, + { BT_A2DP_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, + { BT_A2DP_ALLOCATION_SNR, "SNR", "snr" }, { -1, NULL, NULL} }; @@ -115,73 +118,193 @@ static GstStaticPadTemplate sbc_enc_src_factory = "allocation = (string) { snr, loudness }," "bitpool = (int) [ 2, 64 ]")); -static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) +gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps); + +static void sbc_enc_set_structure_int_param(GstSbcEnc *enc, + GstStructure *structure, const gchar* field, + gint field_value) +{ + GValue *value; + + value = g_new0(GValue,1); + value = g_value_init(value, G_TYPE_INT); + g_value_set_int(value, field_value); + gst_structure_set_value(structure, field, value); + g_free(value); +} + +static void sbc_enc_set_structure_string_param(GstSbcEnc *enc, + GstStructure *structure, const gchar* field, + const gchar* field_value) +{ + GValue *value; + + value = g_new0(GValue,1); + value = g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, field_value); + gst_structure_set_value(structure, field, value); + g_free(value); +} + +static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) { - gint rate; - gint channels; GstCaps* src_caps; GstStructure *structure; - const gchar *mode; - const gchar *allocation; + GEnumValue *enum_value; + GEnumClass *enum_class; + gchar* temp; + + src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad)); + structure = gst_caps_get_structure(src_caps, 0); + + if (enc->rate != 0) + sbc_enc_set_structure_int_param(enc, structure, "rate", + enc->rate); + + if (enc->channels != 0) + sbc_enc_set_structure_int_param(enc, structure, "channels", + enc->channels); + + if (enc->subbands != 0) + sbc_enc_set_structure_int_param(enc, structure, "subbands", + enc->subbands); + + if (enc->blocks != 0) + sbc_enc_set_structure_int_param(enc, structure, "blocks", + enc->blocks); + + if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) { + enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); + enum_value = g_enum_get_value(enum_class, enc->mode); + sbc_enc_set_structure_string_param(enc, structure, "mode", + enum_value->value_nick); + g_type_class_unref(enum_class); + } - structure = gst_caps_get_structure(caps, 0); + if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) { + enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); + enum_value = g_enum_get_value(enum_class, enc->allocation); + sbc_enc_set_structure_string_param(enc, structure, "allocation", + enum_value->value_nick); + g_type_class_unref(enum_class); + } - if (!gst_structure_get_int (structure, "rate", &rate)) - return NULL; - if (!gst_structure_get_int (structure, "channels", &channels)) + temp = gst_caps_to_string(src_caps); + GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp); + g_free(temp); + + return src_caps; +} + +static GstCaps* sbc_enc_src_getcaps (GstPad * pad) +{ + GstSbcEnc *enc; + + enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); + + return sbc_enc_generate_srcpad_caps(enc); +} + +static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps) +{ + GstCaps* srcpad_caps; + GstCaps* temp_caps; + gboolean res = TRUE; + GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); + + GST_LOG_OBJECT(enc, "setting srcpad caps"); + + srcpad_caps = sbc_enc_generate_srcpad_caps(enc); + temp_caps = gst_caps_intersect(srcpad_caps, caps); + if (temp_caps == GST_CAPS_NONE) + res = FALSE; + + gst_caps_unref(temp_caps); + gst_caps_unref(srcpad_caps); + + g_return_val_if_fail(res, FALSE); + + return gst_sbc_enc_fill_sbc_params(enc, caps); +} + +static GstCaps* sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps) +{ + + gchar *error_message = NULL; + GstCaps* result; + + result = gst_sbc_util_caps_fixate(caps, &error_message); + + if (!result) { + GST_ERROR_OBJECT (enc, "Invalid input caps caused parsing " + "error: %s", error_message); + g_free(error_message); return NULL; + } - enc->sbc.rate = rate; - enc->sbc.channels = channels; + return result; +} - if (enc->mode == CFG_MODE_AUTO) - enc->mode = CFG_MODE_JOINT_STEREO; +static GstCaps* sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc) +{ + GstCaps *peer_caps; + GstCaps *src_caps; + GstCaps *caps; + gboolean res = TRUE; + GstCaps *result_caps = NULL; + + peer_caps = gst_pad_peer_get_caps(enc->srcpad); + if (!peer_caps) + return NULL; - if (enc->mode == CFG_MODE_MONO || enc->mode == CFG_MODE_JOINT_STEREO) - enc->sbc.joint = 1; + src_caps = sbc_enc_generate_srcpad_caps(enc); + caps = gst_caps_intersect(src_caps, peer_caps); - enc->sbc.blocks = enc->blocks; - enc->sbc.subbands = enc->subbands; - if (enc->allocation == 0) - enc->sbc.allocation = CFG_ALLOCATION_LOUDNESS; - else - enc->sbc.allocation = enc->allocation; + if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) { + res = FALSE; + goto done; + } - enc->sbc.bitpool = SBC_ENC_DEFAULT_BITPOOL; + result_caps = sbc_enc_src_caps_fixate(enc, caps); - mode = gst_sbc_get_mode_string(enc->sbc.joint); - allocation = gst_sbc_get_allocation_string(enc->sbc.allocation); +done: - src_caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, enc->sbc.rate, - "channels", G_TYPE_INT, enc->sbc.channels, - "mode", G_TYPE_STRING, mode, - "subbands", G_TYPE_INT, enc->sbc.subbands, - "blocks", G_TYPE_INT, enc->sbc.blocks, - "allocation", G_TYPE_STRING, allocation, - "bitpool", G_TYPE_INT, enc->sbc.bitpool, - NULL); + gst_caps_unref(src_caps); + gst_caps_unref(peer_caps); + gst_caps_unref(caps); - return src_caps; + if (!res) + return NULL; + + return result_caps; } static gboolean sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps) { GstSbcEnc *enc; GstStructure *structure; - GstCaps *othercaps; + GstCaps *src_caps; + gint rate, channels; + gboolean res; enc = GST_SBC_ENC(GST_PAD_PARENT (pad)); structure = gst_caps_get_structure(caps, 0); - othercaps = sbc_enc_generate_srcpad_caps(enc, caps); - if (othercaps == NULL) + if (!gst_structure_get_int(structure, "rate", &rate)) + goto error; + if (!gst_structure_get_int(structure, "channels", &channels)) goto error; - gst_pad_set_caps (enc->srcpad, othercaps); - gst_caps_unref(othercaps); + enc->rate = rate; + enc->channels = channels; - return TRUE; + src_caps = sbc_enc_get_fixed_srcpad_caps(enc); + if (!src_caps) + goto error; + res = gst_pad_set_caps(enc->srcpad, src_caps); + gst_caps_unref(src_caps); + + return res; error: GST_ERROR_OBJECT (enc, "invalid input caps"); @@ -215,14 +338,13 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) if (!(allocation = gst_structure_get_string(structure, "allocation"))) return FALSE; - sbc_reinit(&enc->sbc, 0); - enc->sbc.rate = rate; - enc->sbc.channels = channels; - enc->blocks = blocks; - enc->sbc.subbands = subbands; + enc->rate = enc->sbc.rate = rate; + enc->channels = enc->sbc.channels = channels; + enc->blocks = enc->sbc.blocks = blocks; + enc->subbands = enc->sbc.subbands = subbands; enc->sbc.bitpool = bitpool; - enc->mode = gst_sbc_get_mode_int(mode); - enc->allocation = gst_sbc_get_allocation_mode_int(allocation); + enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode); + enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); return TRUE; } @@ -291,11 +413,8 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(enc->srcpad, output); - if (res != GST_FLOW_OK) { - GST_ERROR_OBJECT(enc, "pad pushing failed"); + if (res != GST_FLOW_OK) goto done; - } - } done: @@ -350,12 +469,30 @@ static void gst_sbc_enc_base_init(gpointer g_class) gst_element_class_set_details(element_class, &sbc_enc_details); } +static gboolean sbc_enc_set_blocks(GstSbcEnc *enc, gint value) +{ + if (value != 4 && value != 8 && value != 12 && + value != 16 && value != 0) + return FALSE; + enc->blocks = value; + return TRUE; +} + +static gboolean sbc_enc_set_subbands(GstSbcEnc *enc, gint value) +{ + if (value != 4 && value != 8 && value != 0) + return FALSE; + enc->subbands = value; + return TRUE; +} + static void gst_sbc_enc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstSbcEnc *enc = GST_SBC_ENC(object); - /* TODO - CAN ONLY BE CHANGED ON READY AND BELOW */ + /* changes to those properties will only happen on the next caps + * negotiation */ switch (prop_id) { case PROP_MODE: @@ -365,12 +502,14 @@ static void gst_sbc_enc_set_property(GObject *object, guint prop_id, enc->allocation = g_value_get_enum(value); break; case PROP_BLOCKS: - /* TODO - verify consistency */ - enc->blocks = g_value_get_int(value); + if (!sbc_enc_set_blocks(enc, g_value_get_int(value))) + GST_WARNING_OBJECT(enc, "invalid value %d for " + "blocks property", g_value_get_int(value)); break; case PROP_SUBBANDS: - /* TODO - verify consistency */ - enc->subbands = g_value_get_int(value); + if (!sbc_enc_set_subbands(enc, g_value_get_int(value))) + GST_WARNING_OBJECT(enc, "invalid value %d for " + "subbands property", g_value_get_int(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -428,7 +567,7 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) g_object_class_install_property(object_class, PROP_BLOCKS, g_param_spec_int("blocks", "Blocks", "Blocks", 0, G_MAXINT, - SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); + SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_SUBBANDS, g_param_spec_int("subbands", "Sub Bands", @@ -447,13 +586,18 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + gst_pad_set_getcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps)); + gst_pad_set_setcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + self->subbands = SBC_ENC_DEFAULT_SUB_BANDS; self->blocks = SBC_ENC_DEFAULT_BLOCKS; self->mode = SBC_ENC_DEFAULT_MODE; self->allocation = SBC_ENC_DEFAULT_ALLOCATION; + self->rate = SBC_ENC_DEFAULT_RATE; + self->channels = SBC_ENC_DEFAULT_CHANNELS; self->adapter = gst_adapter_new(); } diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index c5fc6bcc..f65bcc94 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -25,8 +25,6 @@ #include <gst/base/gstadapter.h> #include "sbc.h" -#include "ipc.h" - G_BEGIN_DECLS @@ -51,6 +49,8 @@ struct _GstSbcEnc { GstPad *srcpad; GstAdapter *adapter; + gint rate; + gint channels; gint mode; gint blocks; gint allocation; diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 185cda03..bae7d623 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -57,6 +57,7 @@ static GstStaticPadTemplate sbc_parse_src_factory = "bitpool = (int) [ 2, 64 ]")); /* Creates a fixed caps from the caps given. */ +/* FIXME use gstsbcutil caps fixating function */ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) { GstCaps *result; @@ -67,6 +68,11 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) const gchar* allocation = NULL; const gchar* mode = NULL; const gchar* error_message = NULL; + gchar* str; + + str = gst_caps_to_string(caps); + GST_DEBUG_OBJECT(parse, "Parsing caps: %s", str); + g_free(str); structure = gst_caps_get_structure(caps, 0); @@ -76,11 +82,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "rate"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) temp = gst_sbc_select_rate_from_list(value); - } else { + else temp = g_value_get_int(value); - } rate = temp; } @@ -90,11 +95,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "channels"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) { + if (GST_VALUE_HOLDS_INT_RANGE(value)) temp = gst_sbc_select_channels_from_range(value); - } else { + else temp = g_value_get_int(value); - } channels = temp; } @@ -104,11 +108,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "blocks"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) temp = gst_sbc_select_blocks_from_list(value); - } else { + else temp = g_value_get_int(value); - } blocks = temp; } @@ -118,11 +121,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "subbands"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) temp = gst_sbc_select_subbands_from_list(value); - } else { + else temp = g_value_get_int(value); - } subbands = temp; } @@ -132,11 +134,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "bitpool"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) { + if (GST_VALUE_HOLDS_INT_RANGE(value)) temp = gst_sbc_select_bitpool_from_range(value); - } else { + else temp = g_value_get_int(value); - } bitpool = temp; } @@ -146,11 +147,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "allocation"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) allocation = gst_sbc_get_allocation_from_list(value); - } else { + else allocation = g_value_get_string(value); - } } if (!gst_structure_has_field(structure, "mode")) { @@ -159,11 +159,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "mode"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) mode = gst_sbc_get_mode_from_list(value); - } else { + else mode = g_value_get_string(value); - } } error: @@ -205,9 +204,15 @@ static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) other = gst_caps_new_any(); inter = gst_caps_intersect(caps, other); + if (gst_caps_is_empty(inter)) { + gst_caps_unref(inter); + return FALSE; + } srccaps = sbc_parse_select_caps(parse, inter); - if (srccaps == NULL) + if (srccaps == NULL) { + gst_caps_unref(inter); return FALSE; + } gst_pad_set_caps(parse->srcpad, srccaps); diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 1d7a1227..f2351e6b 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -99,11 +99,11 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *value) gint gst_sbc_get_allocation_mode_int(const gchar *allocation) { if (g_ascii_strcasecmp(allocation, "loudness") == 0) - return CFG_ALLOCATION_LOUDNESS; + return BT_A2DP_ALLOCATION_LOUDNESS; else if (g_ascii_strcasecmp(allocation, "snr") == 0) - return CFG_ALLOCATION_SNR; + return BT_A2DP_ALLOCATION_SNR; else if (g_ascii_strcasecmp(allocation, "auto") == 0) - return CFG_ALLOCATION_AUTO; + return BT_A2DP_ALLOCATION_AUTO; else return -1; } @@ -111,15 +111,15 @@ gint gst_sbc_get_allocation_mode_int(const gchar *allocation) gint gst_sbc_get_mode_int(const gchar *mode) { if (g_ascii_strcasecmp(mode, "joint") == 0) - return CFG_MODE_JOINT_STEREO; + return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; else if (g_ascii_strcasecmp(mode, "stereo") == 0) - return CFG_MODE_STEREO; + return BT_A2DP_CHANNEL_MODE_STEREO; else if (g_ascii_strcasecmp(mode, "dual") == 0) - return CFG_MODE_DUAL_CHANNEL; + return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; else if (g_ascii_strcasecmp(mode, "mono") == 0) - return CFG_MODE_MONO; + return BT_A2DP_CHANNEL_MODE_MONO; else if (g_ascii_strcasecmp(mode, "auto") == 0) - return CFG_MODE_AUTO; + return BT_A2DP_CHANNEL_MODE_AUTO; else return -1; } @@ -127,15 +127,15 @@ gint gst_sbc_get_mode_int(const gchar *mode) const gchar *gst_sbc_get_mode_string(int joint) { switch (joint) { - case CFG_MODE_MONO: + case BT_A2DP_CHANNEL_MODE_MONO: return "mono"; - case CFG_MODE_DUAL_CHANNEL: + case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: return "dual"; - case CFG_MODE_STEREO: + case BT_A2DP_CHANNEL_MODE_STEREO: return "stereo"; - case CFG_MODE_JOINT_STEREO: + case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: return "joint"; - case CFG_MODE_AUTO: + case BT_A2DP_CHANNEL_MODE_AUTO: return NULL; /* TODO what should be selected here? */ default: return NULL; @@ -145,37 +145,165 @@ const gchar *gst_sbc_get_mode_string(int joint) const gchar *gst_sbc_get_allocation_string(int alloc) { switch (alloc) { - case CFG_ALLOCATION_LOUDNESS: + case BT_A2DP_ALLOCATION_LOUDNESS: return "loudness"; - case CFG_ALLOCATION_SNR: + case BT_A2DP_ALLOCATION_SNR: return "snr"; - case CFG_ALLOCATION_AUTO: + case BT_A2DP_ALLOCATION_AUTO: return "loudness"; /* TODO what should be selected here? */ default: return NULL; } } -GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, - struct ipc_codec_sbc *sbc, gint channels) +GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels) { GstCaps *caps; const gchar *mode_str; const gchar *allocation_str; - mode_str = gst_sbc_get_mode_string(cfg->mode); - allocation_str = gst_sbc_get_allocation_string(sbc->allocation); + mode_str = gst_sbc_get_mode_string(sbc->channel_mode); + allocation_str = gst_sbc_get_allocation_string(sbc->allocation_method); caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, cfg->rate, + "rate", G_TYPE_INT, sbc->frequency, "channels", G_TYPE_INT, channels, "mode", G_TYPE_STRING, mode_str, "subbands", G_TYPE_INT, sbc->subbands, - "blocks", G_TYPE_INT, sbc->blocks, + "blocks", G_TYPE_INT, sbc->block_length, "allocation", G_TYPE_STRING, allocation_str, - "bitpool", G_TYPE_INT, sbc->bitpool, + "bitpool", G_TYPE_INT, sbc->max_bitpool, NULL); return caps; } +/* + * Given a GstCaps, this will return a fixed GstCaps on sucessfull conversion. + * If an error occurs, it will return NULL and error_message will contain the + * error message. + * + * error_message must be passed NULL, if an error occurs, the caller has the + * ownership of the error_message, it must be freed after use. + */ +GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message) +{ + GstCaps *result; + GstStructure *structure; + const GValue *value; + gboolean error = FALSE; + gint temp, rate, channels, blocks, subbands, bitpool; + const gchar* allocation = NULL; + const gchar* mode = NULL; + + g_assert(*error_message == NULL); + + structure = gst_caps_get_structure(caps, 0); + + if (!gst_structure_has_field(structure, "rate")) { + error = TRUE; + *error_message = g_strdup("no rate"); + goto error; + } else { + value = gst_structure_get_value(structure, "rate"); + if (GST_VALUE_HOLDS_LIST(value)) + temp = gst_sbc_select_rate_from_list(value); + else + temp = g_value_get_int(value); + rate = temp; + } + + if (!gst_structure_has_field(structure, "channels")) { + error = TRUE; + *error_message = g_strdup("no channels"); + goto error; + } else { + value = gst_structure_get_value(structure, "channels"); + if (GST_VALUE_HOLDS_INT_RANGE(value)) + temp = gst_sbc_select_channels_from_range(value); + else + temp = g_value_get_int(value); + channels = temp; + } + + if (!gst_structure_has_field(structure, "blocks")) { + error = TRUE; + *error_message = g_strdup("no blocks."); + goto error; + } else { + value = gst_structure_get_value(structure, "blocks"); + if (GST_VALUE_HOLDS_LIST(value)) + temp = gst_sbc_select_blocks_from_list(value); + else + temp = g_value_get_int(value); + blocks = temp; + } + + if (!gst_structure_has_field(structure, "subbands")) { + error = TRUE; + *error_message = g_strdup("no subbands"); + goto error; + } else { + value = gst_structure_get_value(structure, "subbands"); + if (GST_VALUE_HOLDS_LIST(value)) + temp = gst_sbc_select_subbands_from_list(value); + else + temp = g_value_get_int(value); + subbands = temp; + } + + if (!gst_structure_has_field(structure, "bitpool")) { + error = TRUE; + *error_message = g_strdup("no bitpool"); + goto error; + } else { + value = gst_structure_get_value(structure, "bitpool"); + if (GST_VALUE_HOLDS_INT_RANGE(value)) + temp = gst_sbc_select_bitpool_from_range(value); + else + temp = g_value_get_int(value); + bitpool = temp; + } + + if (!gst_structure_has_field(structure, "allocation")) { + error = TRUE; + *error_message = g_strdup("no allocation"); + goto error; + } else { + value = gst_structure_get_value(structure, "allocation"); + if (GST_VALUE_HOLDS_LIST(value)) + allocation = gst_sbc_get_allocation_from_list(value); + else + allocation = g_value_get_string(value); + } + + if (!gst_structure_has_field(structure, "mode")) { + error = TRUE; + *error_message = g_strdup("no mode"); + goto error; + } else { + value = gst_structure_get_value(structure, "mode"); + if (GST_VALUE_HOLDS_LIST(value)) + mode = gst_sbc_get_mode_from_list(value); + else + mode = g_value_get_string(value); + } + +error: + if (error) + return NULL; + + result = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, rate, + "channels", G_TYPE_INT, channels, + "mode", G_TYPE_STRING, mode, + "blocks", G_TYPE_INT, blocks, + "subbands", G_TYPE_INT, subbands, + "allocation", G_TYPE_STRING, allocation, + "bitpool", G_TYPE_INT, bitpool, + NULL); + + return result; +} + + diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 0c91fe82..4581abf7 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -22,10 +22,9 @@ */ #include <gst/gst.h> -#include "sbc.h" -struct ipc_data_cfg; /* FIXME can't include ipc.h */ -struct ipc_codec_sbc; +#include "sbc.h" +#include "ipc.h" gint gst_sbc_select_rate_from_list(const GValue *value); @@ -47,6 +46,6 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *value); gint gst_sbc_get_mode_int(const gchar *mode); const gchar *gst_sbc_get_mode_string(int joint); -GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, struct ipc_codec_sbc *sbc, - gint channels); +GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); +GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); diff --git a/audio/ipc.c b/audio/ipc.c new file mode 100644 index 00000000..05920648 --- /dev/null +++ b/audio/ipc.c @@ -0,0 +1,119 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "ipc.h" + +/* This table contains the string representation for messages */ +static const char *strmsg[] = { + "BT_GETCAPABILITIES_REQ", + "BT_GETCAPABILITIES_RSP", + "BT_SETCONFIGURATION_REQ", + "BT_SETCONFIGURATION_RSP", + "BT_STREAMSTART_REQ", + "BT_STREAMSTART_RSP", + "BT_STREAMSTOP_REQ", + "BT_STREAMSTOP_RSP", + "BT_STREAMSUSPEND_IND", + "BT_STREAMRESUME_IND", + "BT_CONTROL_REQ", + "BT_CONTROL_RSP", + "BT_CONTROL_IND", + "BT_STREAMFD_IND", +}; + +int bt_audio_service_open() +{ + int sk; + int err; + struct sockaddr_un addr = { + AF_UNIX, BT_IPC_SOCKET_NAME + }; + + sk = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sk < 0) { + err = errno; + fprintf(stderr, "%s: Cannot open socket: %s (%d)\n", + __FUNCTION__, strerror(err), err); + errno = err; + return -1; + } + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + fprintf(stderr, "%s: connect() failed: %s (%d)\n", + __FUNCTION__, strerror(err), err); + close(sk); + errno = err; + return -1; + } + + return sk; +} + +int bt_audio_service_close(int sk) +{ + return close(sk); +} + +int bt_audio_service_get_data_fd(int sk) +{ + 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(sk, &msgh, 0); + if (ret < 0) { + err = errno; + fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n", + __FUNCTION__, strerror(err), err); + errno = err; + return -1; + } + + /* 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) + return (*(int *) CMSG_DATA(cmsg)); + } + + errno = EINVAL; + return -1; +} + +const char *bt_audio_strmsg(int type) +{ + if (type < 0 || type > (sizeof(strmsg) / sizeof(strmsg[0]))) + return NULL; + + return strmsg[type]; +} + diff --git a/audio/ipc.h b/audio/ipc.h index 1c26e304..0384cfd6 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -4,132 +4,285 @@ * * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * 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, + * This library 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#include <stdint.h> +/* + Message sequence chart of streaming sequence for A2DP transport + + Audio daemon User + on snd_pcm_open + <--BT_GETCAPABILITIES_REQ + + BT_GETCAPABILITIES_RSP--> + + on snd_pcm_hw_params + <--BT_SETCONFIGURATION_REQ + + BT_SETCONFIGURATION_RSP--> + + on snd_pcm_prepare + <--BT_STREAMSTART_REQ + + <Moves to streaming state> + BT_STREAMSTART_RSP--> -#define IPC_TYPE_CONNECT 0x0001 + BT_STREAMFD_IND --> -#define IPC_MTU 128 + < streams data > + .......... -#define IPC_SOCKET_NAME "\0/org/bluez/audio" + on snd_pcm_drop/snd_pcm_drain -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 + <--BT_STREAMSTOP_REQ + + <Moves to open state> + BT_STREAMSTOP_RSP--> + + on IPC close or appl crash + <Moves to idle> + + */ + +#ifndef BT_AUDIOCLIENT_H +#define BT_AUDIOCLIENT_H + +#ifdef __cplusplus +extern "C" { #endif -/* Supported roles */ -#define PKT_ROLE_AUTO 0 -#define PKT_ROLE_VOICE 1 -#define PKT_ROLE_HIFI 2 - -/* Packet types */ -#define PKT_TYPE_CFG_REQ 0 -#define PKT_TYPE_CFG_RSP 1 -#define PKT_TYPE_STATE_REQ 2 -#define PKT_TYPE_STATE_RSP 3 -#define PKT_TYPE_CTL_REQ 4 -#define PKT_TYPE_CTL_RSP 5 -#define PKT_TYPE_CTL_NTFY 6 - -/* Errors codes */ -#define PKT_ERROR_NONE 0 - -struct ipc_packet { - char device[18]; /* Address of the remote Device */ - uint8_t role; /* Audio role eg: voice, wifi, auto... */ - uint8_t type; /* Packet type */ - uint8_t error; /* Packet error code */ - uint8_t length; /* Payload length in bytes */ - uint8_t data[0]; /* Packet payload */ -} __attribute__ ((packed)); +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> + +#define BT_AUDIO_IPC_PACKET_SIZE 128 +#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio" -/* File descriptor options */ -#define CFG_FD_OPT_READ 0 -#define CFG_FD_OPT_WRITE 1 -#define CFG_FD_OPT_READWRITE 2 - -/* Audio channel mode */ -#define CFG_MODE_AUTO 0 -#define CFG_MODE_MONO 1 -#define CFG_MODE_DUAL_CHANNEL 2 -#define CFG_MODE_STEREO 3 -#define CFG_MODE_JOINT_STEREO 4 - -/* Allocation method */ -#define CFG_ALLOCATION_AUTO 0 -#define CFG_ALLOCATION_LOUDNESS 1 -#define CFG_ALLOCATION_SNR 2 - -/* Codec options */ -#define CFG_CODEC_NONE 0 -#define CFG_CODEC_SCO 1 -#define CFG_CODEC_SBC 2 - -struct ipc_data_cfg { - uint8_t fd_opt; /* Stream file descriptor options: read, - write or readwrite */ - uint16_t pkt_len; /* Stream packet length */ - uint8_t sample_size; /* Sample size in bytes */ - uint8_t mode; /* Audio channel mode */ - uint16_t rate; /* Stream sample rate */ - uint8_t codec; /* Stream codec */ - uint8_t data[0]; /* Codec payload */ +/* Generic message header definition */ +typedef struct { + uint8_t msg_type; +} __attribute__ ((packed)) bt_audio_msg_header_t; + +/* Messages list */ +#define BT_GETCAPABILITIES_REQ 0 +#define BT_GETCAPABILITIES_RSP 1 + +#define BT_SETCONFIGURATION_REQ 2 +#define BT_SETCONFIGURATION_RSP 3 + +#define BT_STREAMSTART_REQ 4 +#define BT_STREAMSTART_RSP 5 + +#define BT_STREAMSTOP_REQ 6 +#define BT_STREAMSTOP_RSP 7 + +#define BT_STREAMSUSPEND_IND 8 +#define BT_STREAMRESUME_IND 9 + +#define BT_CONTROL_REQ 10 +#define BT_CONTROL_RSP 11 +#define BT_CONTROL_IND 12 + +#define BT_STREAMFD_IND 13 + +/* BT_GETCAPABILITIES_REQ */ + +#define BT_CAPABILITIES_TRANSPORT_A2DP 0 +#define BT_CAPABILITIES_TRANSPORT_SCO 1 +#define BT_CAPABILITIES_TRANSPORT_ANY 2 + +#define BT_CAPABILITIES_ACCESS_MODE_READ 1 +#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2 +#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3 + +struct bt_getcapabilities_req { + bt_audio_msg_header_t h; + char device[18]; /* Address of the remote Device */ + uint8_t transport; /* Requested transport */ + uint8_t access_mode; /* Requested access mode */ } __attribute__ ((packed)); -struct ipc_codec_sbc { - uint8_t allocation; +/* BT_GETCAPABILITIES_RSP */ + +/** + * SBC Codec parameters as per A2DP profile 1.0 § 4.3 + */ + +#define BT_A2DP_SAMPLING_FREQ_16000 (1 << 3) +#define BT_A2DP_SAMPLING_FREQ_32000 (1 << 2) +#define BT_A2DP_SAMPLING_FREQ_44100 (1 << 1) +#define BT_A2DP_SAMPLING_FREQ_48000 1 + +#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3) +#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1) +#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1 +#define BT_A2DP_CHANNEL_MODE_AUTO 0 + +#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3) +#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2) +#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1) +#define BT_A2DP_BLOCK_LENGTH_16 1 + +#define BT_A2DP_SUBBANDS_4 (1 << 1) +#define BT_A2DP_SUBBANDS_8 1 + +#define BT_A2DP_ALLOCATION_SNR (1 << 1) +#define BT_A2DP_ALLOCATION_LOUDNESS 1 +#define BT_A2DP_ALLOCATION_AUTO 0 + +typedef struct { + uint8_t channel_mode; + uint8_t frequency; + uint8_t allocation_method; uint8_t subbands; - uint8_t blocks; - uint8_t bitpool; + uint8_t block_length; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)) sbc_capabilities_t; + +/* To be defined */ +typedef struct { +} __attribute__ ((packed)) mpeg_capabilities_t; + +struct bt_getcapabilities_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; + uint8_t transport; /* Granted transport */ + uint8_t access_mode; /* Granted access mode */ + uint16_t link_mtu; /* Max length that transport supports */ + sbc_capabilities_t sbc_capabilities; /* A2DP only */ + mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ + uint16_t sampling_rate; /* SCO only */ +} __attribute__ ((packed)); + +/* BT_SETCONFIGURATION_REQ */ +struct bt_setconfiguration_req { + bt_audio_msg_header_t h; + char device[18]; /* Address of the remote Device */ + sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field + and next one must be filled */ + mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ +} __attribute__ ((packed)); + +/* BT_SETCONFIGURATION_RSP */ +struct bt_setconfiguration_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; +} __attribute__ ((packed)); + +/* BT_STREAMSTART_REQ */ +#define BT_STREAM_ACCESS_READ 0 +#define BT_STREAM_ACCESS_WRITE 1 +#define BT_STREAM_ACCESS_READWRITE 2 +struct bt_streamstart_req { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); + +/* BT_STREAMSTART_RSP */ +struct bt_streamstart_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; +} __attribute__ ((packed)); + +/* BT_STREAMFD_IND */ +/* This message is followed by one byte of data containing the stream data fd + as ancilliary data */ +struct bt_datafd_ind { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); + +/* BT_STREAMSTOP_REQ */ +struct bt_streamstop_req { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); + +/* BT_STREAMSTOP_RSP */ +struct bt_streamstop_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; } __attribute__ ((packed)); -/* Device status */ -#define STATE_DISCONNECTED 0 -#define STATE_CONNECTING 1 -#define STATE_CONNECTED 2 -#define STATE_STREAM_STARTING 3 -#define STATE_STREAMING 4 +/* BT_STREAMSUSPEND_IND */ +struct bt_streamsuspend_ind { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); -struct ipc_data_state { - uint8_t state; /* Stream state */ +/* BT_STREAMRESUME_IND */ +struct bt_streamresume_ind { + bt_audio_msg_header_t h; } __attribute__ ((packed)); -#define CTL_MODE_PLAYBACK 0 -#define CTL_MODE_CAPTURE 1 -#define CTL_MODE_GENERAL 2 - -/* Supported control operations */ -#define CTL_KEY_POWER 0x40 -#define CTL_KEY_VOL_UP 0x41 -#define CTL_KEY_VOL_DOWN 0x42 -#define CTL_KEY_MUTE 0x43 -#define CTL_KEY_PLAY 0x44 -#define CTL_KEY_STOP 0x45 -#define CTL_KEY_PAUSE 0x46 -#define CTL_KEY_RECORD 0x47 -#define CTL_KEY_REWIND 0x48 -#define CTL_KEY_FAST_FORWARD 0x49 -#define CTL_KEY_EJECT 0x4A -#define CTL_KEY_FORWARD 0x4B -#define CTL_KEY_BACKWARD 0x4C - -struct ipc_data_ctl { - uint8_t mode; /* Control Mode */ - uint8_t key; /* Control Key */ -} __attribute__ ((packed)); +/* BT_CONTROL_REQ */ + +#define BT_CONTROL_KEY_POWER 0x40 +#define BT_CONTROL_KEY_VOL_UP 0x41 +#define BT_CONTROL_KEY_VOL_DOWN 0x42 +#define BT_CONTROL_KEY_MUTE 0x43 +#define BT_CONTROL_KEY_PLAY 0x44 +#define BT_CONTROL_KEY_STOP 0x45 +#define BT_CONTROL_KEY_PAUSE 0x46 +#define BT_CONTROL_KEY_RECORD 0x47 +#define BT_CONTROL_KEY_REWIND 0x48 +#define BT_CONTROL_KEY_FAST_FORWARD 0x49 +#define BT_CONTROL_KEY_EJECT 0x4A +#define BT_CONTROL_KEY_FORWARD 0x4B +#define BT_CONTROL_KEY_BACKWARD 0x4C + +struct bt_control_req { + bt_audio_msg_header_t h; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ +} __attribute__ ((packed)); + +/* BT_CONTROL_RSP */ +struct bt_control_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ +} __attribute__ ((packed)); + +/* BT_CONTROL_IND */ +struct bt_control_ind { + bt_audio_msg_header_t h; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ +} __attribute__ ((packed)); + +/* Function declaration */ + +/* Opens a connection to the audio service: return a socket descriptor */ +int bt_audio_service_open(); + +/* Closes a connection to the audio service */ +int bt_audio_service_close(int sk); + +/* Receives stream data file descriptor : must be called after a +BT_STREAMFD_IND message is returned */ +int bt_audio_service_get_data_fd(int sk); + +/* Human readable message type string */ +const char *bt_audio_strmsg(int type); + +#ifdef __cplusplus +} +#endif + +#endif /* BT_AUDIOCLIENT_H */ diff --git a/audio/manager.c b/audio/manager.c index b0cc3340..d80fb962 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1052,7 +1052,7 @@ static void parse_stored_devices(char *key, char *value, void *data) return; str2ba(key, &dst); - device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE); + device = manager_find_device(&dst, NULL, FALSE); if (device) return; @@ -1111,7 +1111,7 @@ static void register_devices_stored(const char *adapter) return; str2ba(addr, &dst); - device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE); + device = manager_find_device(&dst, NULL, FALSE); if (device) { info("Setting %s as default device", addr); 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); diff --git a/audio/unix.c b/audio/unix.c index 71eb570a..ea96bd4f 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -79,7 +79,8 @@ struct unix_client { struct headset_data hs; } d; int sock; - int fd_opt; + int access_mode; + int data_fd; /* To be deleted once two phase configuration is fully implemented */ unsigned int req_id; unsigned int cb_id; gboolean (*cancel_stream) (struct device *dev, unsigned int id); @@ -89,6 +90,11 @@ static GSList *clients = NULL; static int unix_sock = -1; +static void unix_ipc_sendmsg(struct unix_client *client, + const bt_audio_msg_header_t *msg); + +static void send_getcapabilities_rsp_error(struct unix_client *client, int err); + static void client_free(struct unix_client *client) { struct a2dp_data *a2dp; @@ -121,7 +127,7 @@ static void client_free(struct unix_client *client) /* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set -to SCM_RIGHTS and the data being an integer value equal to the handle of the +to SCM_RIGHTS and the data being an integer value equal to the handle of the file descriptor to be passed.*/ static int unix_sendmsg_fd(int sock, int fd) { @@ -193,77 +199,29 @@ static void stream_state_changed(struct avdtp_stream *stream, } } -static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) -{ - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - int len, codec_len; - - memset(buf, 0, sizeof(buf)); - - pkt->type = PKT_TYPE_CFG_RSP; - - if (!cfg) { - pkt->error = EINVAL; - len = send(sock, pkt, sizeof(struct ipc_packet), 0); - if (len < 0) - error("send: %s (%d)", strerror(errno), errno); - return len; - } - - debug("fd=%d, fd_opt=%u, pkt_len=%u, sample_size=%u, rate=%u", - fd, cfg->fd_opt, cfg->pkt_len, - cfg->sample_size, cfg->rate); - - if (cfg->codec == CFG_CODEC_SBC) - codec_len = sizeof(struct ipc_codec_sbc); - else - codec_len = 0; - - pkt->error = PKT_ERROR_NONE; - pkt->length = sizeof(struct ipc_data_cfg) + codec_len; - memcpy(pkt->data, cfg, pkt->length); - - len = sizeof(struct ipc_packet) + pkt->length; - len = send(sock, pkt, len, 0); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - - debug("%d bytes sent", len); - - if (fd != -1) { - len = unix_sendmsg_fd(sock, fd); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - debug("%d bytes sent", len); - } - - return 0; -} - static void headset_setup_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; - struct ipc_data_cfg cfg; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; struct headset_data *hs = &client->d.hs; - int fd; client->req_id = 0; if (!dev) { - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); client->dev = NULL; return; } - switch (client->fd_opt) { - case CFG_FD_OPT_READ: + switch (client->access_mode) { + case BT_CAPABILITIES_ACCESS_MODE_READ: hs->lock = HEADSET_LOCK_READ; break; - case CFG_FD_OPT_WRITE: + case BT_CAPABILITIES_ACCESS_MODE_WRITE: hs->lock = HEADSET_LOCK_WRITE; break; - case CFG_FD_OPT_READWRITE: + case BT_CAPABILITIES_ACCESS_MODE_READWRITE: hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; break; default: @@ -273,23 +231,22 @@ static void headset_setup_complete(struct device *dev, void *user_data) if (!headset_lock(dev, hs->lock)) { error("Unable to lock headset"); - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); client->dev = NULL; return; } - memset(&cfg, 0, sizeof(cfg)); + memset(buf, 0, sizeof(buf)); - cfg.fd_opt = client->fd_opt; - cfg.codec = CFG_CODEC_SCO; - cfg.mode = CFG_MODE_MONO; - cfg.pkt_len = 48; - cfg.sample_size = 2; - cfg.rate = 8000; + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; + rsp->access_mode = client->access_mode; + rsp->link_mtu = 48; + rsp->sampling_rate = 8000; - fd = headset_get_sco_fd(dev); + client->data_fd = headset_get_sco_fd(dev); - unix_send_cfg(client->sock, &cfg, fd); + unix_ipc_sendmsg(client, &rsp->h); } static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, @@ -297,17 +254,16 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, void *user_data, struct avdtp_error *err) { struct unix_client *client = user_data; - char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)]; - struct ipc_data_cfg *cfg = (void *) buf; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap; struct sbc_codec_cap *sbc_cap; - struct ipc_codec_sbc *sbc = (void *) cfg->data; struct a2dp_data *a2dp = &client->d.a2dp; - int fd; uint16_t imtu, omtu; GSList *caps; + memset(buf, 0, sizeof(buf)); client->req_id = 0; if (!stream) @@ -321,7 +277,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, a2dp->sep = sep; a2dp->stream = stream; - if (!avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, &caps)) { + if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu, &caps)) { error("Unable to get stream transport"); goto failed; } @@ -340,66 +296,27 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, goto failed; } + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + client->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + rsp->access_mode = client->access_mode; /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */ - cfg->pkt_len = omtu; - cfg->fd_opt = CFG_FD_OPT_WRITE; + rsp->link_mtu = omtu; sbc_cap = (void *) codec_cap; - cfg->sample_size = 2; - switch (sbc_cap->channel_mode) { - case A2DP_CHANNEL_MODE_MONO: - cfg->mode = CFG_MODE_MONO; - break; - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - cfg->mode = CFG_MODE_DUAL_CHANNEL; - break; - case A2DP_CHANNEL_MODE_STEREO: - cfg->mode = CFG_MODE_STEREO; - break; - case A2DP_CHANNEL_MODE_JOINT_STEREO: - cfg->mode = CFG_MODE_JOINT_STEREO; - break; - } + /* assignations below are ok as soon as newipc.h and a2dp.h are kept */ + /* in sync. However it is not possible to cast a struct to another */ + /* dues to endianess issues */ + rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode; + rsp->sbc_capabilities.frequency = sbc_cap->frequency; + rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method; + rsp->sbc_capabilities.subbands = sbc_cap->subbands; + rsp->sbc_capabilities.block_length = sbc_cap->block_length; + rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool; + rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; - switch (sbc_cap->frequency) { - case A2DP_SAMPLING_FREQ_16000: - cfg->rate = 16000; - break; - case A2DP_SAMPLING_FREQ_32000: - cfg->rate = 32000; - break; - case A2DP_SAMPLING_FREQ_44100: - cfg->rate = 44100; - break; - case A2DP_SAMPLING_FREQ_48000: - cfg->rate = 48000; - break; - } - - cfg->codec = CFG_CODEC_SBC; - sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? - 0x01 : 0x00; - sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; - - switch (sbc_cap->block_length) { - case A2DP_BLOCK_LENGTH_4: - sbc->blocks = 4; - break; - case A2DP_BLOCK_LENGTH_8: - sbc->blocks = 8; - break; - case A2DP_BLOCK_LENGTH_12: - sbc->blocks = 12; - break; - case A2DP_BLOCK_LENGTH_16: - sbc->blocks = 16; - break; - } - - sbc->bitpool = sbc_cap->max_bitpool; - - unix_send_cfg(client->sock, cfg, fd); + unix_ipc_sendmsg(client, &rsp->h); client->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, client); @@ -412,7 +329,7 @@ failed: a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); avdtp_unref(a2dp->session); @@ -469,7 +386,7 @@ static void create_stream(struct device *dev, struct unix_client *client) return; failed: - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); } static void create_cb(struct device *dev, void *user_data) @@ -477,135 +394,57 @@ static void create_cb(struct device *dev, void *user_data) struct unix_client *client = user_data; if (!dev) - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); else create_stream(dev, client); } -static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) +static void unix_ipc_sendmsg(struct unix_client *client, + const bt_audio_msg_header_t *msg) { - struct ipc_codec_sbc *sbc = (void *) cfg->data; - - memset(sbc_cap, 0, sizeof(struct sbc_codec_cap)); - - sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC; - - switch (cfg->rate) { - case 48000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000; - break; - case 44100: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; - break; - case 32000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000; - break; - case 16000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000; - break; - default: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; - break; - } - - switch (cfg->mode) { - case CFG_MODE_MONO: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_MONO; - break; - case CFG_MODE_DUAL_CHANNEL: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; - break; - case CFG_MODE_STEREO: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; - break; - case CFG_MODE_JOINT_STEREO: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - break; - default: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - break; - } - - switch (sbc->allocation) { - case CFG_ALLOCATION_LOUDNESS: - sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - break; - case CFG_ALLOCATION_SNR: - sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - break; - default: - sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - break; - } - - switch (sbc->subbands) { - case 8: - sbc_cap->subbands = A2DP_SUBBANDS_8; - break; - case 4: - sbc_cap->subbands = A2DP_SUBBANDS_4; - break; - default: - sbc_cap->subbands = A2DP_SUBBANDS_8; - break; - } - - switch (sbc->blocks) { - case 16: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; - break; - case 12: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_12; - break; - case 8: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_8; - break; - case 4: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_4; - break; - default: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; - break; - } + info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type)); + if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0) + error("Error %s(%d)", strerror(errno), errno); +} - if (sbc->bitpool != 0) { - if (sbc->bitpool > 250) - return -EINVAL; +static void send_getcapabilities_rsp_error(struct unix_client *client, int err) +{ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; - sbc_cap->min_bitpool = sbc->bitpool; - sbc_cap->max_bitpool = sbc->bitpool; - } + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->posix_errno = err; - return 0; + unix_ipc_sendmsg(client, &rsp->h); } -static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) +static void handle_getcapabilities_req(struct unix_client *client, + struct bt_getcapabilities_req *req) { struct device *dev; bdaddr_t bdaddr; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct sbc_codec_cap sbc_cap; - str2ba(pkt->device, &bdaddr); + str2ba(req->device, &bdaddr); + + if (!req->access_mode) { + send_getcapabilities_rsp_error(client, EINVAL); + return; + } - client->fd_opt = cfg->fd_opt; + client->access_mode = req->access_mode; if (client->interface) { g_free(client->interface); client->interface = NULL; } - if (pkt->role == PKT_ROLE_VOICE) + if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); - else if (pkt->role == PKT_ROLE_HIFI) + else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) client->interface = g_strdup(AUDIO_SINK_INTERFACE); - if (cfg_to_caps(cfg, &sbc_cap) < 0) - goto failed; - - client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, - &sbc_cap, sizeof(sbc_cap)); + client->media_codec = 0; if (!manager_find_device(&bdaddr, NULL, FALSE)) { if (!bacmp(&bdaddr, BDADDR_ANY)) @@ -627,59 +466,83 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int le return; failed: - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); } -static void ctl_event(struct unix_client *client, - struct ipc_packet *pkt, int len) +static void handle_setconfiguration_req(struct unix_client *client, + struct bt_setconfiguration_req *req) { + /* FIXME: for now we just blindly assume that we receive is the + only valid configuration sent.*/ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_rsp *rsp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_SETCONFIGURATION_RSP; + rsp->posix_errno = 0; + + unix_ipc_sendmsg(client, &rsp->h); } -static int reply_state(int sock, struct ipc_packet *pkt) +static void handle_streamstart_req(struct unix_client *client, + struct bt_streamstart_req *req) { - struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; - int len; + /* FIXME : to be really implemented */ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_datafd_ind *ind = (void *) buf; - info("status=%u", state->state); + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_STREAMSTART_RSP; + rsp->posix_errno = 0; + unix_ipc_sendmsg(client, &rsp->h); - pkt->type = PKT_TYPE_STATE_RSP; - pkt->length = sizeof(struct ipc_data_state); - pkt->error = PKT_ERROR_NONE; + memset(buf, 0, sizeof(buf)); + ind->h.msg_type = BT_STREAMFD_IND; + unix_ipc_sendmsg(client, &ind->h); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); - len = send(sock, pkt, len, 0); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); + if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) + error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); + +} - debug("%d bytes sent", len); +static void handle_streamstop_req(struct unix_client *client, + struct bt_streamstop_req *req) +{ + /* FIXME : to be implemented */ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstop_rsp *rsp = (void *) buf; - return 0; + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_STREAMSTOP_RSP; + rsp->posix_errno = 0; + + unix_ipc_sendmsg(client, &rsp->h); } -static void state_event(struct unix_client *client, - struct ipc_packet *pkt, int len) +static void handle_control_req(struct unix_client *client, + struct bt_control_req *req) { -#if 0 - struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; - struct device *dev = client->dev; + /* FIXME: really implement that */ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_rsp *rsp = (void *) buf; - if (len > sizeof(struct ipc_packet)) - device_set_state(dev, state->state); - else - state->state = device_get_state(dev); -#endif + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_CONTROL_RSP; + rsp->posix_errno = 0; - reply_state(client->sock, pkt); + unix_ipc_sendmsg(client, &rsp->h); } static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_msg_header_t *msghdr = (void *) buf; struct unix_client *client = data; - int len, len_check; + int len; struct a2dp_data *a2dp = &client->d.a2dp; struct headset_data *hs = &client->d.hs; + const char *type; if (cond & G_IO_NVAL) return FALSE; @@ -709,31 +572,39 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) memset(buf, 0, sizeof(buf)); - len = recv(client->sock, buf, sizeof(buf), 0); + len = recv(client->sock, buf, sizeof(buf), MSG_WAITALL); if (len < 0) { error("recv: %s (%d)", strerror(errno), errno); goto failed; } - len_check = pkt->length + sizeof(struct ipc_packet); - if (len != len_check) { - error("Packet lenght doesn't match"); - goto failed; - } + if ((type = bt_audio_strmsg(msghdr->msg_type))) + info("Audio API: received %s", type); - switch (pkt->type) { - case PKT_TYPE_CFG_REQ: - info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); - cfg_event(client, pkt, len); + switch (msghdr->msg_type) { + case BT_GETCAPABILITIES_REQ: + handle_getcapabilities_req(client, + (struct bt_getcapabilities_req *) msghdr); break; - case PKT_TYPE_STATE_REQ: - info("Package PKT_TYPE_STATE_REQ"); - state_event(client, pkt, len); + case BT_SETCONFIGURATION_REQ: + handle_setconfiguration_req(client, + (struct bt_setconfiguration_req *) msghdr); break; - case PKT_TYPE_CTL_REQ: - info("Package PKT_TYPE_CTL_REQ"); - ctl_event(client, pkt, len); + case BT_STREAMSTART_REQ: + handle_streamstart_req(client, + (struct bt_streamstart_req *) msghdr); break; + case BT_STREAMSTOP_REQ: + handle_streamstop_req(client, + (struct bt_streamstop_req *) msghdr); + break; + case BT_CONTROL_REQ: + handle_control_req(client, + (struct bt_control_req *) msghdr); + break; + default: + error("Audio API: received unexpected packet type %d", + msghdr->msg_type); } return TRUE; @@ -789,7 +660,7 @@ int unix_init(void) { GIOChannel *io; struct sockaddr_un addr = { - AF_UNIX, IPC_SOCKET_NAME + AF_UNIX, BT_IPC_SOCKET_NAME }; int sk, err; |