handsfree-audio: Initial DBUS code
RFC v2 1/8 handsfree-audio: Initial DBUS code
This code can register an handsfree audio agent and receive NewConnection and Release calls.
--- tools/handsfree-audio.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 tools/handsfree-audio.c
tools/handsfree-audio.c new file +332 -0
@@ -0,0 +1,332 @@ +/* +* +* oFono - Open Source Telephony +* +* Copyright (C) 2013 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program 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. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#include <gdbus.h> +#include <glib.h> + +#define OFONO_SERVICE "org.ofono" +#define HFP_AUDIO_MANAGER_PATH "/" +#define HFP_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" +#define HFP_AUDIO_AGENT_PATH "/hfpaudioagent" +#define HFP_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" + +#define HFP_AUDIO_CVSD 1 +#define HFP_AUDIO_MSBC 2 + +#define DBG(fmt, arg...) do {\ + g_print("%s: " fmt "\n", __FUNCTION__, ## arg);\ + } while (0) + +/* DBus related */ +static GMainLoop *main_loop =NULL ; +static DBusConnection *conn; +static GSList *hcons =NULL ; + +static gboolean option_nocvsd = FALSE; +static gboolean option_nomsbc = FALSE; + +struct hfp_audio_conn { +unsigned char codec; +int watch; +}; + +static void hfp_audio_conn_free (struct hfp_audio_conn *hcon) +{ +DBG ("Freeing audio connection %p" , hcon); + + hcons =g_slist_remove (hcons, hcon); +g_source_remove (hcon->watch); +g_free (hcon); +} + +static gboolean hfp_audio_cb (GIOChannel *io,GIOCondition cond,gpointer data) +{ +struct hfp_audio_conn *hcon = data; +gsize read; +gsize written; +char buf[60 ]; + +if (cond & (G_IO_HUP |G_IO_NVAL |G_IO_ERR )) + goto fail; + +if (g_io_channel_read_chars (io, buf,sizeof (buf), &read,NULL ) != +G_IO_STATUS_NORMAL ) + goto fail; + +g_io_channel_write_chars (io, buf+written, read, &written,NULL ); + +return TRUE; + +fail: +DBG ("Disconnected" ); +hfp_audio_conn_free (hcon); +return FALSE; +} + +static DBusMessage *agent_newconnection (DBusConnection *conn,DBusMessage *msg, +void *data) +{ +const char *card; +int fd; +unsigned char codec; +GIOChannel *io; +struct hfp_audio_conn *hcon; + +DBG ("New connection" ); + +if (dbus_message_get_args (msg,NULL ,DBUS_TYPE_OBJECT_PATH , &card, +DBUS_TYPE_UNIX_FD , &fd, +DBUS_TYPE_BYTE , &codec, +DBUS_TYPE_INVALID ) == FALSE) +return g_dbus_create_error (msg, +HFP_AUDIO_AGENT_INTERFACE ".InvalidArguments" , +"Invalid arguments" ); + +DBG ("New connection: card=%s fd=%d codec=%d" , card, fd, codec); + + io =g_io_channel_unix_new (fd); + + hcon =g_try_malloc0 (sizeof (struct hfp_audio_conn )); +if (hcon ==NULL ) +return NULL ; + + hcon->codec = codec; + hcon->watch =g_io_add_watch (io,G_IO_IN , hfp_audio_cb, hcon); + hcons =g_slist_prepend (hcons, hcon); + +return dbus_message_new_method_return (msg); +} + +static DBusMessage *agent_release (DBusConnection *conn,DBusMessage *msg, +void *data) +{ +DBG ("HFP audio agent released" ); +return dbus_message_new_method_return (msg); +} + +static const GDBusMethodTable agent_methods[] = { + {GDBUS_METHOD ("NewConnection" ,NULL ,NULL , agent_newconnection) }, + {GDBUS_METHOD ("Release" ,NULL ,NULL , agent_release) }, + { }, +}; + +static void hfp_audio_agent_register_reply (DBusPendingCall *call,void *data) +{ +DBusMessage *reply =dbus_pending_call_steal_reply (call); +DBusError err; + +dbus_error_init (&err); + +if (dbus_set_error_from_message (&err, reply) == TRUE) { +DBG ("Failed to register audio agent (%s: %s)" , err.name, + err.message); +dbus_error_free (&err); + }else { +DBG ("HFP audio agent registered" ); + } + +dbus_message_unref (reply); +} + +static void hfp_audio_agent_register (DBusConnection *conn) +{ +DBusMessage *msg; +DBusPendingCall *call; +const char *path =HFP_AUDIO_AGENT_PATH ; +unsigned char codecs[2 ]; +const unsigned char *pcodecs = codecs; +int ncodecs =0 ; + +DBG ("Registering audio agent" ); + + msg =dbus_message_new_method_call (OFONO_SERVICE , +HFP_AUDIO_MANAGER_PATH , +HFP_AUDIO_MANAGER_INTERFACE , +"Register" ); +if (msg ==NULL ) { +DBG ("Not enough memory" ); +return ; + } + +if (option_nocvsd == FALSE) + codecs[ncodecs++] =HFP_AUDIO_CVSD ; + +if (option_nomsbc == FALSE) + codecs[ncodecs++] =HFP_AUDIO_MSBC ; + +dbus_message_append_args (msg,DBUS_TYPE_OBJECT_PATH , &path, +DBUS_TYPE_ARRAY ,DBUS_TYPE_BYTE , + &pcodecs, ncodecs,DBUS_TYPE_INVALID ); + +if (!dbus_connection_send_with_reply (conn, msg, &call,-1 )) { +dbus_message_unref (msg); +DBG ("Unable to register agent" ); +return ; + } + +dbus_message_unref (msg); + +if (call ==NULL ) { +DBG ("Unable to register agent" ); +return ; + } + +dbus_pending_call_set_notify (call, hfp_audio_agent_register_reply, +NULL ,NULL ); + +dbus_pending_call_unref (call); +} + +static void hfp_audio_agent_create (DBusConnection *conn) +{ +DBG ("Creating audio agent" ); + +if (!g_dbus_register_interface (conn,HFP_AUDIO_AGENT_PATH , +HFP_AUDIO_AGENT_INTERFACE , + agent_methods,NULL ,NULL , +NULL ,NULL )) { +DBG ("Unable to create local agent" ); +g_main_loop_quit (main_loop); + } +} + +static void hfp_audio_agent_destroy (DBusConnection *conn) +{ +DBG ("Destroying audio agent" ); + +g_dbus_unregister_interface (conn,HFP_AUDIO_AGENT_PATH , +HFP_AUDIO_AGENT_INTERFACE ); +} + +static void ofono_connect (DBusConnection *conn,void *user_data) +{ +DBG ("oFono appeared" ); + +hfp_audio_agent_register (conn); +} + +static void ofono_disconnect (DBusConnection *conn,void *user_data) +{ +DBG ("oFono disappeared" ); +} + +static void disconnect_callback (DBusConnection *conn,void *user_data) +{ +DBG ("Disconnected from BUS" ); + +g_main_loop_quit (main_loop); +} + +static void sig_term (int sig) +{ +DBG ("Terminating" ); + +g_main_loop_quit (main_loop); +} + +static GOptionEntry options[] = { + {"nocvsd" ,'c' ,0 ,G_OPTION_ARG_NONE , &option_nocvsd, +"Disable CVSD support" }, + {"nomsbc" ,'m' ,0 ,G_OPTION_ARG_NONE , &option_nomsbc, +"Disable MSBC support" }, + {NULL }, +}; + +int main (int argc,char **argv) +{ +GOptionContext *context; +GError *error =NULL ; +DBusError err; +guint watch; +struct sigaction sa; + + context =g_option_context_new (NULL ); +g_option_context_add_main_entries (context, options,NULL ); + +if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) { +if (error !=NULL ) { +DBG ("%s" , error->message); +g_error_free (error); + }else +DBG ("An unknown error occurred" ); +exit (1 ); + } + +g_option_context_free (context); + +if (option_nocvsd == TRUE && option_nomsbc == TRUE) { +DBG ("At least one codec must be supported" ); +exit (2 ); + } + + main_loop =g_main_loop_new (NULL , FALSE); + +dbus_error_init (&err); + + conn =g_dbus_setup_bus (DBUS_BUS_SYSTEM ,NULL , &err); +if (conn ==NULL ) { +if (dbus_error_is_set (&err) == TRUE) { +DBG ("%s" , err.message); +dbus_error_free (&err); + }else +DBG ("Can't register with system bus" ); +exit (1 ); + } + +g_dbus_set_disconnect_function (conn, disconnect_callback,NULL ,NULL ); + +memset (&sa,0 ,sizeof (sa)); + sa.sa_handler = sig_term; +sigaction (SIGINT , &sa,NULL ); +sigaction (SIGTERM , &sa,NULL ); + +hfp_audio_agent_create (conn); + + watch =g_dbus_add_service_watch (conn,OFONO_SERVICE , + ofono_connect, ofono_disconnect,NULL ,NULL ); + +g_main_loop_run (main_loop); + +g_dbus_remove_watch (conn, watch); + +while (hcons !=NULL ) +hfp_audio_conn_free (hcons->data); + +hfp_audio_agent_destroy (conn); + +dbus_connection_unref (conn); + +g_main_loop_unref (main_loop); + +return 0 ; +}
Signature · 1 line
1.7.9.5
RFC v2 2/8 handsfree-audio: Build handsfree-audio command line tool
--- Makefile.am | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
Makefile.am +5 -1
@@ -718,11 +718,15 @@ TESTS = $(unit_tests) if TOOLS noinst_PROGRAMS += tools/huawei-audio tools/auto-enable \ tools/get-location tools/lookup-apn \ - tools/lookup-provider-name tools/tty-redirector + tools/lookup-provider-name tools/tty-redirector \ + tools/handsfree-audio tools_huawei_audio_SOURCES = $(gdbus_sources) tools/huawei-audio.c tools_huawei_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ +tools_handsfree_audio_SOURCES = $(gdbus_sources) tools/handsfree-audio.c +tools_handsfree_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ + tools_auto_enable_SOURCES = $(gdbus_sources) tools/auto-enable.c tools_auto_enable_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
Signature · 1 line
1.7.9.5
RFC v2 3/8 handsfree-audio: Add Alsa dependancy
In reply to Frédéric Dalleau--- configure.ac | 5 +++++ 1 file changed, 5 insertions(+)
configure.ac +5 -0
@@ -121,6 +121,11 @@ if (test "${enable_tools}" = "yes"); then AC_MSG_ERROR(USB library is required)) AC_SUBST(USB_CFLAGS) AC_SUBST(USB_LIBS) + + PKG_CHECK_MODULES(ALSA, alsa >= 1.0.0, dummy=yes, + AC_MSG_ERROR(Alsa library is required)) + AC_SUBST(ALSA_CFLAGS) + AC_SUBST(ALSA_LIBS) fi AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
Signature · 1 line
1.7.9.5
RFC v2 4/8 handsfree-audio: Link tool with Alsa
In reply to Frédéric Dalleau--- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Makefile.am +1 -1
@@ -725,7 +725,7 @@ tools_huawei_audio_SOURCES = $(gdbus_sources) tools/huawei-audio.c tools_huawei_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ tools_handsfree_audio_SOURCES = $(gdbus_sources) tools/handsfree-audio.c -tools_handsfree_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ +tools_handsfree_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @ALSA_LIBS@ -lpthread tools_auto_enable_SOURCES = $(gdbus_sources) tools/auto-enable.c tools_auto_enable_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
Signature · 1 line
1.7.9.5
RFC v2 5/8 handsfree-audio: Implement alsa playback
In reply to Frédéric Dalleau--- tools/handsfree-audio.c | 472 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 416 insertions(+), 56 deletions(-)
tools/handsfree-audio.c +416 -56
@@ -30,10 +30,17 @@#include <stdlib.h> #include <string.h> #include <signal.h> +#include <sys/time.h> +#include <poll.h> #include <gdbus.h> #include <glib.h> +#include <alsa/asoundlib.h> +#include <pthread.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/sco.h> +#define OFONO_SERVICE "org.ofono"#define HFP_AUDIO_MANAGER_PATH "/"#define HFP_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
@@ -50,47 +57,257 @@/* DBus related */ static GMainLoop *main_loop =NULL ;static DBusConnection *conn; -static GSList *hcons =NULL ; +static GSList *threads =NULL ;static gboolean option_nocvsd = FALSE;static gboolean option_nomsbc = FALSE; +static gboolean option_server = FALSE; +static gboolean option_defer = FALSE; +static gchar *option_client_addr =NULL ; -struct hfp_audio_conn { +struct hfp_audio_thread {unsigned char codec; -int watch; +int fd; +int running; +pthread_t thread; }; -static void hfp_audio_conn_free (struct hfp_audio_conn *hcon) +static int btstr2ba (const char *str,bdaddr_t *ba) +{ +int i; + +for (i =5 ; i >=0 ; i--, str +=3 ) + ba->b[i] =strtol (str,NULL ,16 ); + +return 0 ; +} + +static snd_pcm_t *hfp_audio_pcm_init (snd_pcm_stream_t stream) +{ +snd_pcm_t *pcm; +DBG ("Initializing pcm for %s" , (stream ==SND_PCM_STREAM_CAPTURE ) ? +"capture" :"playback" ); + +if (snd_pcm_open (&pcm,"default" , stream,SND_PCM_NONBLOCK ) <0 ) { +DBG ("Failed to open pcm" ); +return NULL ; + } + +/* 8000 khz, 16 bits, 128000 bytes/s, 48 bytes/frame, 6000 fps */ +if (snd_pcm_set_params (pcm,SND_PCM_FORMAT_S16_LE , +SND_PCM_ACCESS_RW_INTERLEAVED , +1 ,8000 ,1 ,20000 ) <0 ) { +DBG ("Failed to set pcm params" ); +snd_pcm_close (pcm); + pcm =NULL ; + } + +return pcm; +} + +static void hfp_audio_thread_free (struct hfp_audio_thread *hcon) {DBG ("Freeing audio connection %p" , hcon); +if (!hcon) +return ; + + hcon->running =0 ; +if (hcon->thread) +pthread_join (hcon->thread,NULL ); - hcons =g_slist_remove (hcons, hcon); -g_source_remove (hcon->watch); + threads =g_slist_remove (threads, hcon);g_free (hcon); +DBG ("freed %p" , hcon); } -static gboolean hfp_audio_cb (GIOChannel *io,GIOCondition cond,gpointer data) +/* Returns the number of data on sco socket */ +static int hfp_audio_playback (int fd,snd_pcm_t *playback) { -struct hfp_audio_conn *hcon = data; -gsize read; -gsize written; -char buf[60 ]; +char buf[800 ]; +snd_pcm_sframes_t frames; +int bytes; + + bytes =read (fd, buf,sizeof (buf)); +if (bytes <0 ) { +DBG ("Failed to read: bytes %d, errno %d" , bytes, errno); +switch (errno) { +case ENOTCONN : +return -ENOTCONN ; +case EAGAIN : +return 0 ; +default : +return -EINVAL ; + } + } -if (cond & (G_IO_HUP |G_IO_NVAL |G_IO_ERR )) - goto fail; + frames =snd_pcm_writei (playback, buf, bytes /2 ); +switch (frames) { +case -EPIPE : +DBG ("Playback underrun" ); +snd_pcm_prepare (playback); +return bytes; +case -EAGAIN : +DBG ("??? %d" , bytes /2 ); +return bytes; +case -EBADFD : +case -ESTRPIPE : +return -EINVAL ; + } -if (g_io_channel_read_chars (io, buf,sizeof (buf), &read,NULL ) != -G_IO_STATUS_NORMAL ) - goto fail; +if (frames < bytes /2 ) +DBG ("played %d < requested %d" , (int )frames, bytes /2 ); -g_io_channel_write_chars (io, buf+written, read, &written,NULL ); +return bytes; +} -return TRUE; +/* Returns the number of data on sco socket */ +static int hfp_audio_capture (int fd,snd_pcm_t *capture,GList **outq,int mtu) +{ +snd_pcm_sframes_t frames; +gchar *buf; + + buf =g_try_malloc (mtu); +if (!buf) +return -ENOMEM ; + + frames =snd_pcm_readi (capture, buf, mtu /2 ); +switch (frames) { +case -EPIPE : +DBG ("Capture overrun" ); +snd_pcm_prepare (capture); +g_free (buf); +return 0 ; +case -EAGAIN : +DBG ("No data to capture" ); +g_free (buf); +return 0 ; +case -EBADFD : +case -ESTRPIPE : +DBG ("Other error" ); +g_free (buf); +return -EINVAL ; + } + +if (frames < mtu /2 ) +DBG ("Small frame: %d" , (int ) frames); + +if (g_list_length (*outq) >32 ) +DBG ("Too many queued packets" ); + + *outq =g_list_append (*outq, buf); + +return frames *2 ; +} + +static void pop_outq (int fd,GList **outq,int mtu) +{ +GList *el; + + el =g_list_first (*outq); +if (!el) +return ; + + *outq =g_list_remove_link (*outq, el); +if (write (fd, el->data, mtu) <0 ) +DBG ("Failed to write: %d" , errno); + +g_free (el->data); +g_list_free (el); +} + +static void *thread_func (void *userdata) +{ +struct hfp_audio_thread *hcon = userdata; +snd_pcm_t *playback, *capture; +GList *outq =NULL ; +struct sco_options opts; +struct pollfd fds[8 ]; + +DBG ("thread started" ); + + capture =hfp_audio_pcm_init (SND_PCM_STREAM_CAPTURE ); +if (!capture) +return NULL ; + + playback =hfp_audio_pcm_init (SND_PCM_STREAM_PLAYBACK ); +if (!playback) { +snd_pcm_close (capture); +return NULL ; + } + +/* Force defered setup */ +if (read (hcon->fd, &opts,sizeof (opts)) <0 ) +DBG ("Defered setup failed: %d (%s)" , errno,strerror (errno)); + +/* Add SCO options +len = sizeof(opts); +if (getsockopt(hcon->fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) { +DBG("getsockopt failed %d", errno); +return NULL; +} +*/ + opts.mtu =48 ; +DBG ("mtu %d" , opts.mtu); + +while (hcon->running) { +/* Queue alsa captured data (snd_pcm_poll_descriptors failed) */ +if (hfp_audio_capture (hcon->fd, capture, &outq, opts.mtu) <0 ) { +DBG ("Failed to capture" ); +break ; + } + +memset (fds,0 ,sizeof (fds)); + fds[0 ].fd = hcon->fd; + fds[0 ].events =POLLIN |POLLERR |POLLHUP |POLLNVAL ; +if (poll (fds,1 ,200 ) ==0 ) +continue ; + +if (fds[0 ].revents & (POLLERR |POLLHUP |POLLNVAL )) { +DBG ("POLLERR | POLLHUP | POLLNVAL triggered" ); +break ; + } + +if (!fds[0 ].revents &POLLIN ) +continue ; + +if (hfp_audio_playback (hcon->fd, playback) <0 ) { +DBG ("POLLIN triggered, but read error" ); +break ; + } + +/* Dequeue in sync with readings */ +pop_outq (hcon->fd, &outq, opts.mtu); + } + +DBG ("thread terminating" ); +snd_pcm_close (playback); +snd_pcm_close (capture); +return NULL ; +} + +static int new_connection (int fd,int codec) +{ +struct hfp_audio_thread *hcon; +DBG ("New connection: fd=%d codec=%d" , fd, codec); + hcon =g_try_malloc0 (sizeof (struct hfp_audio_thread )); +if (hcon ==NULL ) +return -ENOMEM ; + + hcon->fd = fd; + hcon->codec = codec; + hcon->running =1 ; + +if (pthread_create (&hcon->thread,NULL , thread_func, hcon) <0 ) + goto fail; + +/* FIXME thread is not detached until we quit */ + + threads =g_slist_prepend (threads, hcon); +return 0 ; fail: -DBG ("Disconnected" ); -hfp_audio_conn_free (hcon); -return FALSE; +hfp_audio_thread_free (hcon); +return -EINVAL ; } static DBusMessage *agent_newconnection(DBusConnection *conn, DBusMessage *msg,
@@ -99,8 +316,7 @@ static DBusMessage *agent_newconnection(DBusConnection *conn, DBusMessage *msg,const char *card;int fd;unsigned char codec; -GIOChannel *io; -struct hfp_audio_conn *hcon; +DBusMessage *reply;DBG ("New connection" );
@@ -112,30 +328,32 @@ static DBusMessage *agent_newconnection(DBusConnection *conn, DBusMessage *msg,HFP_AUDIO_AGENT_INTERFACE ".InvalidArguments" , "Invalid arguments"); -DBG ("New connection: card=%s fd=%d codec=%d" , card, fd, codec); + reply =dbus_message_new_method_return (msg); +if (!reply) + goto fail; - io =g_io_channel_unix_new (fd); +if (new_connection (fd, codec) >=0 ) +return reply; - hcon =g_try_malloc0 (sizeof (struct hfp_audio_conn )); -if (hcon ==NULL ) -return NULL ; - - hcon->codec = codec; - hcon->watch =g_io_add_watch (io,G_IO_IN , hfp_audio_cb, hcon); - hcons =g_slist_prepend (hcons, hcon); +dbus_message_unref (reply); -return dbus_message_new_method_return (msg); +fail: +return g_dbus_create_error (msg, +HFP_AUDIO_AGENT_INTERFACE ".Failed" ,"Failed to start" ); }static DBusMessage *agent_release (DBusConnection *conn,DBusMessage *msg,void *data) {DBG ("HFP audio agent released" ); +/* agent will be registered on next oFono startup */ return dbus_message_new_method_return (msg); }static const GDBusMethodTable agent_methods[] = { - {GDBUS_METHOD ("NewConnection" ,NULL ,NULL , agent_newconnection) }, + {GDBUS_METHOD ("NewConnection" , + GDBUS_ARGS({ "path", "o" }, { "fd", "h" }, { "codec", "y" }), +NULL , agent_newconnection) }, {GDBUS_METHOD ("Release" ,NULL ,NULL , agent_release) }, { }, };
@@ -167,7 +385,7 @@ static void hfp_audio_agent_register(DBusConnection *conn)const unsigned char *pcodecs = codecs;int ncodecs =0 ; -DBG ("Registering audio agent" ); +DBG ("Registering audio agent in oFono" ); msg = dbus_message_new_method_call(OFONO_SERVICE, HFP_AUDIO_MANAGER_PATH,
@@ -196,20 +414,147 @@ static void hfp_audio_agent_register(DBusConnection *conn)dbus_message_unref (msg); -if (call ==NULL ) { -DBG ("Unable to register agent" ); -return ; - } -dbus_pending_call_set_notify (call, hfp_audio_agent_register_reply,NULL ,NULL );dbus_pending_call_unref (call); } +static gboolean sco_accept_cb (GIOChannel *io,GIOCondition cond,gpointer data) +{ +struct sockaddr_sco addr; +socklen_t optlen; +int sk, nsk; + +if (cond & (G_IO_HUP |G_IO_NVAL |G_IO_ERR )) + goto fail; + +DBG ("Incoming connection" ); + sk =g_io_channel_unix_get_fd (io); + nsk =accept (sk, (struct sockaddr *) &addr, &optlen); + +if (nsk >0 ) +new_connection (nsk,HFP_AUDIO_CVSD ); + +return TRUE; + +fail: +DBG ("Server disconnected" ); +return FALSE; +} + +static gboolean sco_connect_cb (GIOChannel *io,GIOCondition cond,gpointer data) +{ +int sk; + +if (cond & (G_IO_HUP |G_IO_NVAL |G_IO_ERR )) + goto fail; + +DBG ("Connected" ); + sk =g_io_channel_unix_get_fd (io); +if (sk >0 ) +new_connection (sk,HFP_AUDIO_CVSD ); + +return FALSE; + +fail: +DBG ("Connection failed" ); +return FALSE; +} + +static int sco_listen_watch () +{ +struct sockaddr_sco saddr; +int sk; +GIOChannel *io; + +/* Create socket */ + sk =socket (PF_BLUETOOTH ,SOCK_SEQPACKET ,BTPROTO_SCO ); +if (sk <0 ) { +DBG ("Can't create socket: %s (%d)" ,strerror (errno), errno); +return -1 ; + } + +/* Bind to local address */ +memset (&saddr,0 ,sizeof (saddr)); + saddr.sco_family =AF_BLUETOOTH ; + +if (bind (sk, (struct sockaddr *) &saddr,sizeof (saddr)) <0 ) { +DBG ("Can't bind socket: %s (%d)" ,strerror (errno), errno); + goto error; + } + +/* Enable deferred setup */ +if (option_defer &&setsockopt (sk,SOL_BLUETOOTH ,BT_DEFER_SETUP , + &option_defer,sizeof (option_defer)) <0 ) { +DBG ("Can't defer setup : %s (%d)" ,strerror (errno), errno); + goto error; + } + +/* Listen for connections */ +if (listen (sk,10 )) { +DBG ("Can not listen socket: %s (%d)" ,strerror (errno), errno); + goto error; + } + +DBG ("Waiting for connection ..." ); + io =g_io_channel_unix_new (sk); +if (!io) + goto error; + +return g_io_add_watch (io,G_IO_IN , sco_accept_cb,NULL ); + +error: +close (sk); +return -1 ; +} + +static int sco_connect_watch () +{ +struct sockaddr_sco saddr; +int sk; +GIOChannel *io; + +/* Create socket */ + sk =socket (PF_BLUETOOTH ,SOCK_SEQPACKET ,BTPROTO_SCO ); +if (sk <0 ) { +DBG ("Can't create socket: %s (%d)" ,strerror (errno), errno); +return -1 ; + } + +/* Bind to local address */ +memset (&saddr,0 ,sizeof (saddr)); + saddr.sco_family =AF_BLUETOOTH ; + +if (bind (sk, (struct sockaddr *) &saddr,sizeof (saddr)) <0 ) { +DBG ("Can't bind socket: %s (%d)" ,strerror (errno), errno); + goto error; + } + +/* Connect to remote address */ +memset (&saddr,0 ,sizeof (saddr)); + saddr.sco_family =AF_BLUETOOTH ; +btstr2ba (option_client_addr, &saddr.sco_bdaddr); +if (connect (sk, (struct sockaddr *) &saddr,sizeof (saddr))) { +DBG ("Can not connect socket: %s (%d)" ,strerror (errno), errno); + goto error; + } + +DBG ("Connecting to %s..." , option_client_addr); + io =g_io_channel_unix_new (sk); +if (!io) + goto error; + +return g_io_add_watch (io,G_IO_IN |G_IO_OUT , sco_connect_cb,NULL ); + +error: +close (sk); +return -1 ; +} + static void hfp_audio_agent_create(DBusConnection *conn) { - DBG("Creating audio agent"); + DBG("Registering audio agent on DBUS"); if (!g_dbus_register_interface(conn, HFP_AUDIO_AGENT_PATH, HFP_AUDIO_AGENT_INTERFACE,
@@ -222,7 +567,7 @@ static void hfp_audio_agent_create(DBusConnection *conn)static void hfp_audio_agent_destroy (DBusConnection *conn) { -DBG ("Destroying audio agent" ); +DBG ("Unregistering audio agent on DBUS" );g_dbus_unregister_interface (conn,HFP_AUDIO_AGENT_PATH ,HFP_AUDIO_AGENT_INTERFACE );
@@ -259,6 +604,12 @@ static GOptionEntry options[] = { "Disable CVSD support" }, {"nomsbc" ,'m' ,0 ,G_OPTION_ARG_NONE , &option_nomsbc, "Disable MSBC support" }, + { "defer", 'd', 0, G_OPTION_ARG_NONE, &option_defer, + "Defered socket support" }, + { "server", 'S', 0, G_OPTION_ARG_NONE, &option_server, + "Server" }, + { "client", 'C', 1, G_OPTION_ARG_STRING, &option_client_addr, + "Client addr" }, { NULL }, };
@@ -293,6 +644,11 @@ int main(int argc, char **argv)dbus_error_init (&err); +memset (&sa,0 ,sizeof (sa)); + sa.sa_handler = sig_term; +sigaction (SIGINT , &sa,NULL ); +sigaction (SIGTERM , &sa,NULL ); + conn =g_dbus_setup_bus (DBUS_BUS_SYSTEM ,NULL , &err);if (conn ==NULL ) {if (dbus_error_is_set (&err) == TRUE) {
@@ -305,24 +661,28 @@ int main(int argc, char **argv)g_dbus_set_disconnect_function (conn, disconnect_callback,NULL ,NULL ); -memset (&sa,0 ,sizeof (sa)); - sa.sa_handler = sig_term; -sigaction (SIGINT , &sa,NULL ); -sigaction (SIGTERM , &sa,NULL ); - -hfp_audio_agent_create (conn); - - watch =g_dbus_add_service_watch (conn,OFONO_SERVICE , +if (option_server) { + watch =sco_listen_watch (); + }else if (option_client_addr !=NULL ) { + watch =sco_connect_watch (); + }else { +hfp_audio_agent_create (conn); + watch =g_dbus_add_service_watch (conn,OFONO_SERVICE , ofono_connect, ofono_disconnect,NULL ,NULL ); - + }g_main_loop_run (main_loop); -g_dbus_remove_watch (conn, watch); - -while (hcons !=NULL ) -hfp_audio_conn_free (hcons->data); +while (threads !=NULL ) +hfp_audio_thread_free (threads->data); -hfp_audio_agent_destroy (conn); +if (option_server) { +g_source_remove (watch); + }else if (option_client_addr !=NULL ) { +g_source_remove (watch); + }else { +g_dbus_remove_watch (conn, watch); +hfp_audio_agent_destroy (conn); + }dbus_connection_unref (conn);
Signature · 1 line
1.7.9.5
RFC v2 6/8 handsfree-audio: Add SBC dependency
In reply to Frédéric Dalleau--- configure.ac | 5 +++++ 1 file changed, 5 insertions(+)
configure.ac +5 -0
@@ -126,6 +126,11 @@ if (test "${enable_tools}" = "yes"); then AC_MSG_ERROR(Alsa library is required)) AC_SUBST(ALSA_CFLAGS) AC_SUBST(ALSA_LIBS) + + PKG_CHECK_MODULES(SBC, sbc >= 1.1, dummy=yes, + AC_MSG_ERROR(SBC library v. 1.1 is required)) + AC_SUBST(SBC_CFLAGS) + AC_SUBST(SBC_LIBS) fi AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
Signature · 1 line
1.7.9.5
RFC v2 7/8 handsfree-audio: Link tool with SBC library
In reply to Frédéric Dalleau--- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Makefile.am +1 -1
@@ -725,7 +725,7 @@ tools_huawei_audio_SOURCES = $(gdbus_sources) tools/huawei-audio.c tools_huawei_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ tools_handsfree_audio_SOURCES = $(gdbus_sources) tools/handsfree-audio.c -tools_handsfree_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @ALSA_LIBS@ -lpthread +tools_handsfree_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @ALSA_LIBS@ @SBC_LIBS@ -lpthread tools_auto_enable_SOURCES = $(gdbus_sources) tools/auto-enable.c tools_auto_enable_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
Signature · 1 line
1.7.9.5
RFC v2 8/8 handsfree-audio: mSBC support
In reply to Frédéric Dalleau--- tools/handsfree-audio.c | 510 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 396 insertions(+), 114 deletions(-)
tools/handsfree-audio.c +396 -114
@@ -40,6 +40,7 @@#include <pthread.h> #include <bluetooth/bluetooth.h> #include <bluetooth/sco.h> +#include <sbc/sbc.h> #define OFONO_SERVICE "org.ofono"#define HFP_AUDIO_MANAGER_PATH "/"
@@ -65,11 +66,45 @@ static gboolean option_server = FALSE;static gboolean option_defer = FALSE;static gchar *option_client_addr =NULL ; -struct hfp_audio_thread { -unsigned char codec; +static const char sntable[4 ] = {0x08 ,0x38 ,0xC8 ,0xF8 }; +static const int audio_rates[3 ] = {0 ,8000 ,16000 }; + +struct msbc_parser { +int len; +uint8_t buffer[60 ]; +}; + +struct msbc_codec { +sbc_t sbcenc;/* Coder data */ +char *ebuffer;/* Codec transfer buffer */ +size_t ebuffer_size;/* Size of the buffer */ +size_t ebuffer_start;/* start of encoding data */ +size_t ebuffer_end;/* end of encoding data */ + +struct msbc_parser parser;/* mSBC parser for concatenating frames */ +sbc_t sbcdec;/* Decoder data */ + +size_t msbc_frame_size; +size_t decoded_frame_size; +}; + +struct hfp_thread {int fd;int running;pthread_t thread; +GSList *outq; + +int rate; +char *capture_buffer; +int capture_size; +int captured; +int mtu; +int (*init)(struct hfp_thread *); +int (*free)(struct hfp_thread *); +int (*encode)(struct hfp_thread *,char *data,int len); +int (*decode)(struct hfp_thread *,char *data,int len,char *out, +int outlen); +struct msbc_codec msbc; };static int btstr2ba (const char *str,bdaddr_t *ba)
@@ -82,21 +117,237 @@ static int btstr2ba(const char *str, bdaddr_t *ba)return 0 ; } -static snd_pcm_t *hfp_audio_pcm_init (snd_pcm_stream_t stream) +static void msbc_parser_reset (struct msbc_parser *p) +{ + p->len =0 ; +} + +static int msbc_state_machine (struct msbc_parser *p,uint8_t byte) +{ +switch (p->len) { +case 0 : +if (byte ==0x01 ) + goto copy; +return 0 ; +case 1 : +if (byte ==0x08 || byte ==0x38 || byte ==0xC8 + || byte ==0xF8 ) + goto copy; +break ; +case 2 : +if (byte ==0xAD ) + goto copy; +break ; +case 3 : +if (byte ==0x00 ) + goto copy; +break ; +case 4 : +if (byte ==0x00 ) + goto copy; +break ; +default : + goto copy; + } + + p->len =0 ; +return 0 ; + +copy: + p->buffer[p->len] = byte; + p->len++; + +return p->len; +} + +static size_t msbc_parse (sbc_t *sbcdec,struct msbc_parser *p,char *data, +int len,char *out,int outlen,int *bytes) +{ +size_t totalwritten =0 ; +size_t written =0 ; +int i; + *bytes =0 ; + +for (i =0 ; i < len; i++) { +if (msbc_state_machine (p, data[i]) ==60 ) { +int decoded; + decoded =sbc_decode (sbcdec, p->buffer +2 , + p->len -2 -1 , out, outlen, &written); +if (decoded >0 ) { + totalwritten += written; + *bytes += decoded; + }else { +DBG ("Error while decoding: %d" , decoded); + } +msbc_parser_reset (p); + } + } +return totalwritten; +} + +struct hack_sco_options { +uint16_t mtu; +uint8_t mode; +}; + +static int hfp_audio_cvsd_init (struct hfp_thread *thread) +{ + thread->rate =8000 ; + thread->capture_size =48 ; +return 0 ; +} + +static int hfp_audio_cvsd_free (struct hfp_thread *thread) +{ +return 0 ; +} + +static int hfp_audio_cvsd_encode (struct hfp_thread *thread,char *data, +int len) +{ +char *qbuf; + +if (len > thread->mtu) { +DBG ("Mtu too small: len %d, mtu %d" , len, thread->mtu); +return -EINVAL ; + } + + qbuf =g_try_malloc (thread->mtu); +if (!qbuf) +return -ENOMEM ; + +memcpy (qbuf, data, len); + + thread->outq =g_slist_insert (thread->outq, qbuf,-1 ); + +return len; +} + +static int hfp_audio_cvsd_decode (struct hfp_thread *thread,char *data, +int len,char *out,int outlen) +{ +int size = (len < outlen) ? len : outlen; +memcpy (out, data, size); +return size; +} + +/* Run from IO thread */ +static int hfp_audio_msbc_init (struct hfp_thread *thread) +{ +struct msbc_codec *codec = &thread->msbc; +struct hack_sco_options opts; + + thread->rate =16000 ; + thread->capture_size =240 ;/* decoded mSBC frame */ + +memset (&opts,0 ,sizeof (opts)); + opts.mode =1 ; +if (setsockopt (thread->fd,SOL_SCO ,SCO_OPTIONS , &opts,sizeof (opts)) + <0 ) { +DBG ("Can't set transparent mode: %s (%d)" , +strerror (errno), errno); +return -ENOTSUP ; + } + +sbc_init_msbc (&codec->sbcenc,0 ); +sbc_init_msbc (&codec->sbcdec,0 ); + + codec->msbc_frame_size =2 +sbc_get_frame_length (&codec->sbcenc) +1 ; + codec->decoded_frame_size =sbc_get_codesize (&codec->sbcenc); +msbc_parser_reset (&codec->parser); + +/* 5 * 48 == 10 * 24 == 4 * 60 */ + codec->ebuffer_size = codec->msbc_frame_size *4 ; + codec->ebuffer =g_try_malloc (codec->ebuffer_size); + codec->ebuffer_start =0 ; + codec->ebuffer_end =0 ; + +DBG ("codec->msbc_frame_size %d" , (int ) codec->msbc_frame_size); +DBG ("codec->ebuffer_size %d" , (int ) codec->ebuffer_size); +DBG ("codec->decoded_frame_size %d" , (int ) codec->decoded_frame_size); +return 0 ; +} + +/* Run from IO thread */ +static int hfp_audio_msbc_free (struct hfp_thread *thread) +{ +struct msbc_codec *codec = &thread->msbc; + +g_free (codec->ebuffer); +sbc_finish (&codec->sbcenc); +sbc_finish (&codec->sbcdec); + +return 0 ; +} + +/* Run from IO thread */ +static int hfp_audio_msbc_encode (struct hfp_thread *thread,char *data,int len) +{ +struct msbc_codec *codec = &thread->msbc; +char *h2 = codec->ebuffer + codec->ebuffer_end; +static int sn =0 ; +int written =0 ; +char *qbuf; + + h2[0 ] =0x01 ; + h2[1 ] = sntable[sn]; + h2[59 ] =0xff ; + sn = (sn +1 ) %4 ; + +sbc_encode (&codec->sbcenc, data, len, + codec->ebuffer + codec->ebuffer_end +2 , + codec->ebuffer_size - codec->ebuffer_end -2 , + (ssize_t *) &written); + + written +=2 /* H2 */ +1 /* 0xff */ ; + codec->ebuffer_end += written; + +/* Split into MTU sized chunks */ +while (codec->ebuffer_start + thread->mtu <= codec->ebuffer_end) { + qbuf =g_try_malloc (thread->mtu); +if (!qbuf) +return -ENOMEM ; + +/* DBG("Enqueuing %d bytes", thread->mtu); */ +memcpy (qbuf, codec->ebuffer + codec->ebuffer_start, thread->mtu); + + thread->outq =g_slist_insert (thread->outq, qbuf,-1 ); + + codec->ebuffer_start += thread->mtu; +if (codec->ebuffer_start >= codec->ebuffer_end) + codec->ebuffer_start = codec->ebuffer_end =0 ; + } + +return 0 ; +} + +/* Run from IO thread */ +static int hfp_audio_msbc_decode (struct hfp_thread *thread,char *data, +int len,char *out,int outlen) +{ +struct msbc_codec *codec = &thread->msbc; +int written, decoded; + + written =msbc_parse (&codec->sbcdec, &codec->parser, data, len, out, + outlen, &decoded); + +return written; +} + +static snd_pcm_t *hfp_audio_pcm_init (snd_pcm_stream_t stream,int rate) {snd_pcm_t *pcm;DBG ("Initializing pcm for %s" , (stream ==SND_PCM_STREAM_CAPTURE ) ? -"capture" :"playback" ); +"capture" :"playback" );if (snd_pcm_open (&pcm,"default" , stream,SND_PCM_NONBLOCK ) <0 ) {DBG ("Failed to open pcm" );return NULL ; } -/* 8000 khz, 16 bits, 128000 bytes/s, 48 bytes/frame, 6000 fps */ +/* 16 bits */ if (snd_pcm_set_params (pcm,SND_PCM_FORMAT_S16_LE , -SND_PCM_ACCESS_RW_INTERLEAVED , -1 ,8000 ,1 ,20000 ) <0 ) { +SND_PCM_ACCESS_RW_INTERLEAVED ,1 , rate,1 ,20000 ) <0 ) {DBG ("Failed to set pcm params" );snd_pcm_close (pcm); pcm =NULL ;
@@ -105,29 +356,35 @@ static snd_pcm_t *hfp_audio_pcm_init(snd_pcm_stream_t stream)return pcm; } -static void hfp_audio_thread_free (struct hfp_audio_thread *hcon) +static void hfp_audio_thread_free (struct hfp_thread *thread) { -DBG ("Freeing audio connection %p" , hcon); -if (!hcon) +DBG ("Freeing audio connection %p" , thread); +if (!thread)return ; - hcon->running =0 ; -if (hcon->thread) -pthread_join (hcon->thread,NULL ); + thread->running =0 ; +if (thread->thread) +pthread_join (thread->thread,NULL ); - threads =g_slist_remove (threads, hcon); -g_free (hcon); -DBG ("freed %p" , hcon); +if (shutdown (thread->fd,SHUT_RDWR ) <0 ) +DBG ("Failed to shutdown socket" ); +if (close (thread->fd) <0 ) +DBG ("Failed to close socket" ); + + threads =g_slist_remove (threads, thread); +g_free (thread); +DBG ("freed %p" , thread); }/* Returns the number of data on sco socket */ -static int hfp_audio_playback (int fd,snd_pcm_t *playback) +static int hfp_audio_playback (struct hfp_thread *thread, +snd_pcm_t *playback) { -char buf[800 ]; +char buf[512 ], out[512 ]; +int bytes, outlen;snd_pcm_sframes_t frames; -int bytes; - bytes =read (fd, buf,sizeof (buf)); + bytes =read (thread->fd, buf,sizeof (buf));if (bytes <0 ) {DBG ("Failed to read: bytes %d, errno %d" , bytes, errno);switch (errno) {
@@ -140,178 +397,204 @@ static int hfp_audio_playback(int fd, snd_pcm_t *playback) } } - frames =snd_pcm_writei (playback, buf, bytes /2 ); +/* DBG("received %d bytes", bytes); */ + outlen = thread->decode (thread, buf, bytes, out,sizeof (out)); + + frames =snd_pcm_writei (playback, out, outlen /2 );switch (frames) {case -EPIPE :DBG ("Playback underrun" );snd_pcm_prepare (playback);return bytes;case -EAGAIN : -DBG ("??? %d" , bytes /2 ); +DBG ("??? %d" , outlen /2 );return bytes;case -EBADFD :case -ESTRPIPE :return -EINVAL ; } -if (frames < bytes /2 ) -DBG ("played %d < requested %d" , (int )frames, bytes /2 ); +/* if (frames < outlen / 2) +DBG("played %d < requested %d", (int)frames, outlen / 2); */ return bytes; }/* Returns the number of data on sco socket */ -static int hfp_audio_capture (int fd,snd_pcm_t *capture,GList **outq,int mtu) +static int hfp_audio_capture (struct hfp_thread *thread,snd_pcm_t *capture) {snd_pcm_sframes_t frames; -gchar *buf; - - buf =g_try_malloc (mtu); -if (!buf) -return -ENOMEM ; - frames =snd_pcm_readi (capture, buf, mtu /2 ); + frames =snd_pcm_readi (capture, thread->capture_buffer+thread->captured, + (thread->capture_size - thread->captured) /2 );switch (frames) {case -EPIPE :DBG ("Capture overrun" );snd_pcm_prepare (capture); -g_free (buf);return 0 ;case -EAGAIN :DBG ("No data to capture" ); -g_free (buf);return 0 ;case -EBADFD :case -ESTRPIPE : -DBG ("Other error" ); -g_free (buf); +DBG ("Other error %s (%d)" ,strerror (frames), (int ) frames);return -EINVAL ; } -if (frames < mtu /2 ) -DBG ("Small frame: %d" , (int ) frames); + thread->captured += frames *2 ; +if (thread->captured < thread->capture_size) +return frames *2 ; -if (g_list_length (*outq) >32 ) -DBG ("Too many queued packets" ); - - *outq =g_list_append (*outq, buf); +/* DBG("Encoding %d bytes", (int) thread->captured); */ + thread->encode (thread, thread->capture_buffer, thread->captured);return frames *2 ; } -static void pop_outq (int fd,GList **outq,int mtu) +static void pop_outq (struct hfp_thread *thread) { -GList *el; - - el =g_list_first (*outq); -if (!el) -return ; +char *qbuf; - *outq =g_list_remove_link (*outq, el); +while (thread->outq !=NULL ) { + qbuf = thread->outq->data; + thread->outq =g_slist_remove (thread->outq, qbuf); -if (write (fd, el->data, mtu) <0 ) -DBG ("Failed to write: %d" , errno); +if (write (thread->fd, qbuf, thread->mtu) <0 ) +DBG ("Failed to write: %d" , errno); -g_free (el->data); -g_list_free (el); +g_free (qbuf); + } }static void *thread_func (void *userdata) { -struct hfp_audio_thread *hcon = userdata; +struct hfp_thread *thread = userdata;snd_pcm_t *playback, *capture; -GList *outq =NULL ; -struct sco_options opts; +struct sco_options opts;struct pollfd fds[8 ]; -DBG ("thread started" ); +DBG ("thread started: rate %d" , thread->rate); + +if (thread->init (thread) <0 ) +return NULL ; - capture =hfp_audio_pcm_init (SND_PCM_STREAM_CAPTURE ); + capture =hfp_audio_pcm_init (SND_PCM_STREAM_CAPTURE , thread->rate);if (!capture)return NULL ; - playback =hfp_audio_pcm_init (SND_PCM_STREAM_PLAYBACK ); + playback =hfp_audio_pcm_init (SND_PCM_STREAM_PLAYBACK , thread->rate);if (!playback) {snd_pcm_close (capture);return NULL ; } + thread->capture_buffer =g_try_malloc (thread->capture_size); +if (!thread->capture_buffer) { +snd_pcm_close (capture); +snd_pcm_close (playback); +return NULL ; + } +/* Force defered setup */ -if (read (hcon->fd, &opts,sizeof (opts)) <0 ) +if (read (thread->fd, &opts,sizeof (opts)) <0 )DBG ("Defered setup failed: %d (%s)" , errno,strerror (errno));/* Add SCO options -len = sizeof(opts); -if (getsockopt(hcon->fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) { -DBG("getsockopt failed %d", errno); -return NULL; -} -*/ +len = sizeof(opts); +if (getsockopt(thread->fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) { +DBG("getsockopt failed %d", errno); +return NULL; +} +*/ opts.mtu =48 ; -DBG ("mtu %d" , opts.mtu); + thread->mtu = opts.mtu; +DBG ("thread->mtu %d" , thread->mtu); -while (hcon->running) { +while (thread->running) {/* Queue alsa captured data (snd_pcm_poll_descriptors failed) */ -if (hfp_audio_capture (hcon->fd, capture, &outq, opts.mtu) <0 ) { +if (hfp_audio_capture (thread, capture) <0 ) {DBG ("Failed to capture" );break ; }memset (fds,0 ,sizeof (fds)); - fds[0 ].fd = hcon->fd; + fds[0 ].fd = thread->fd; fds[0 ].events =POLLIN |POLLERR |POLLHUP |POLLNVAL ;if (poll (fds,1 ,200 ) ==0 )continue ;if (fds[0 ].revents & (POLLERR |POLLHUP |POLLNVAL )) { -DBG ("POLLERR | POLLHUP | POLLNVAL triggered" ); +DBG ("POLLERR | POLLHUP | POLLNVAL triggered (%d)" , + fds[0 ].revents);break ; }if (!fds[0 ].revents &POLLIN )continue ; -if (hfp_audio_playback (hcon->fd, playback) <0 ) { +if (hfp_audio_playback (thread, playback) <0 ) {DBG ("POLLIN triggered, but read error" );break ; } +/* DBG("Received and send"); */ +/* Dequeue in sync with readings */ -pop_outq (hcon->fd, &outq, opts.mtu); +pop_outq (thread); }DBG ("thread terminating" ); +g_slist_free_full (thread->outq, g_free); +g_free (thread->capture_buffer);snd_pcm_close (playback);snd_pcm_close (capture); + + thread->free (thread); +return NULL ; }static int new_connection (int fd,int codec) { -struct hfp_audio_thread *hcon; +struct hfp_thread *thread; +DBG ("New connection: fd=%d codec=%d" , fd, codec); - hcon =g_try_malloc0 (sizeof (struct hfp_audio_thread )); -if (hcon ==NULL ) + thread =g_try_malloc0 (sizeof (struct hfp_thread )); +if (thread ==NULL )return -ENOMEM ; - hcon->fd = fd; - hcon->codec = codec; - hcon->running =1 ; + thread->fd = fd; + thread->running =1 ; + +switch (codec) { +case HFP_AUDIO_CVSD : + thread->init = hfp_audio_cvsd_init; + thread->free = hfp_audio_cvsd_free; + thread->decode = hfp_audio_cvsd_decode; + thread->encode = hfp_audio_cvsd_encode; +break ; +case HFP_AUDIO_MSBC : + thread->rate =16000 ; + thread->init = hfp_audio_msbc_init; + thread->free = hfp_audio_msbc_free; + thread->decode = hfp_audio_msbc_decode; + thread->encode = hfp_audio_msbc_encode; +break ; + } -if (pthread_create (&hcon->thread,NULL , thread_func, hcon) <0 ) - goto fail; +if (pthread_create (&thread->thread,NULL , thread_func, thread) <0 ) { +hfp_audio_thread_free (thread); +return -EINVAL ; + }/* FIXME thread is not detached until we quit */ - threads =g_slist_prepend (threads, hcon); + threads =g_slist_prepend (threads, thread);return 0 ; -fail: -hfp_audio_thread_free (hcon); -return -EINVAL ; }static DBusMessage *agent_newconnection (DBusConnection *conn,DBusMessage *msg, -void *data) +void *data) {const char *card;int fd;
@@ -321,9 +604,8 @@ static DBusMessage *agent_newconnection(DBusConnection *conn, DBusMessage *msg,DBG ("New connection" );if (dbus_message_get_args (msg,NULL ,DBUS_TYPE_OBJECT_PATH , &card, -DBUS_TYPE_UNIX_FD , &fd, -DBUS_TYPE_BYTE , &codec, -DBUS_TYPE_INVALID ) == FALSE) +DBUS_TYPE_UNIX_FD , &fd,DBUS_TYPE_BYTE , &codec, +DBUS_TYPE_INVALID ) == FALSE)return g_dbus_create_error (msg,HFP_AUDIO_AGENT_INTERFACE ".InvalidArguments" ,"Invalid arguments" );
@@ -343,7 +625,7 @@ fail: }static DBusMessage *agent_release (DBusConnection *conn,DBusMessage *msg, -void *data) +void *data) {DBG ("HFP audio agent released" );/* agent will be registered on next oFono startup */
@@ -366,8 +648,8 @@ static void hfp_audio_agent_register_reply(DBusPendingCall *call, void *data)dbus_error_init (&err);if (dbus_set_error_from_message (&err, reply) == TRUE) { -DBG ("Failed to register audio agent (%s: %s)" , err.name, - err.message); +DBG ("Failed to register audio agent (%s: %s)" , + err.name, err.message);dbus_error_free (&err); }else {DBG ("HFP audio agent registered" );
@@ -388,9 +670,8 @@ static void hfp_audio_agent_register(DBusConnection *conn)DBG ("Registering audio agent in oFono" ); msg =dbus_message_new_method_call (OFONO_SERVICE , -HFP_AUDIO_MANAGER_PATH , -HFP_AUDIO_MANAGER_INTERFACE , -"Register" ); +HFP_AUDIO_MANAGER_PATH ,HFP_AUDIO_MANAGER_INTERFACE , +"Register" );if (msg ==NULL ) {DBG ("Not enough memory" );return ;
@@ -403,8 +684,8 @@ static void hfp_audio_agent_register(DBusConnection *conn) codecs[ncodecs++] =HFP_AUDIO_MSBC ;dbus_message_append_args (msg,DBUS_TYPE_OBJECT_PATH , &path, -DBUS_TYPE_ARRAY ,DBUS_TYPE_BYTE , - &pcodecs, ncodecs,DBUS_TYPE_INVALID ); +DBUS_TYPE_ARRAY ,DBUS_TYPE_BYTE , &pcodecs, ncodecs, +DBUS_TYPE_INVALID );if (!dbus_connection_send_with_reply (conn, msg, &call,-1 )) {dbus_message_unref (msg);
@@ -414,8 +695,8 @@ static void hfp_audio_agent_register(DBusConnection *conn)dbus_message_unref (msg); -dbus_pending_call_set_notify (call, hfp_audio_agent_register_reply, -NULL ,NULL ); +dbus_pending_call_set_notify (call, hfp_audio_agent_register_reply,NULL , +NULL );dbus_pending_call_unref (call); }
@@ -481,30 +762,32 @@ static int sco_listen_watch()if (bind (sk, (struct sockaddr *) &saddr,sizeof (saddr)) <0 ) {DBG ("Can't bind socket: %s (%d)" ,strerror (errno), errno); - goto error; + goto fail; }/* Enable deferred setup */ -if (option_defer &&setsockopt (sk,SOL_BLUETOOTH ,BT_DEFER_SETUP , - &option_defer,sizeof (option_defer)) <0 ) { +if (option_defer + &&setsockopt (sk,SOL_BLUETOOTH ,BT_DEFER_SETUP , + &option_defer,sizeof (option_defer)) + <0 ) {DBG ("Can't defer setup : %s (%d)" ,strerror (errno), errno); - goto error; + goto fail; }/* Listen for connections */ if (listen (sk,10 )) {DBG ("Can not listen socket: %s (%d)" ,strerror (errno), errno); - goto error; + goto fail; }DBG ("Waiting for connection ..." ); io =g_io_channel_unix_new (sk);if (!io) - goto error; + goto fail;return g_io_add_watch (io,G_IO_IN , sco_accept_cb,NULL ); -error: +fail:close (sk);return -1 ; }
@@ -528,7 +811,7 @@ static int sco_connect_watch()if (bind (sk, (struct sockaddr *) &saddr,sizeof (saddr)) <0 ) {DBG ("Can't bind socket: %s (%d)" ,strerror (errno), errno); - goto error; + goto fail; }/* Connect to remote address */
@@ -537,17 +820,17 @@ static int sco_connect_watch()btstr2ba (option_client_addr, &saddr.sco_bdaddr);if (connect (sk, (struct sockaddr *) &saddr,sizeof (saddr))) {DBG ("Can not connect socket: %s (%d)" ,strerror (errno), errno); - goto error; + goto fail; }DBG ("Connecting to %s..." , option_client_addr); io =g_io_channel_unix_new (sk);if (!io) - goto error; + goto fail; -return g_io_add_watch (io,G_IO_IN |G_IO_OUT , sco_connect_cb,NULL ); +return g_io_add_watch (io,G_IO_IN |G_IO_OUT , sco_connect_cb,NULL ); -error: +fail:close (sk);return -1 ; }
@@ -557,9 +840,8 @@ static void hfp_audio_agent_create(DBusConnection *conn)DBG ("Registering audio agent on DBUS" );if (!g_dbus_register_interface (conn,HFP_AUDIO_AGENT_PATH , -HFP_AUDIO_AGENT_INTERFACE , - agent_methods,NULL ,NULL , -NULL ,NULL )) { +HFP_AUDIO_AGENT_INTERFACE , agent_methods,NULL ,NULL , +NULL ,NULL )) {DBG ("Unable to create local agent" );g_main_loop_quit (main_loop); }
@@ -570,7 +852,7 @@ static void hfp_audio_agent_destroy(DBusConnection *conn)DBG ("Unregistering audio agent on DBUS" );g_dbus_unregister_interface (conn,HFP_AUDIO_AGENT_PATH , -HFP_AUDIO_AGENT_INTERFACE ); +HFP_AUDIO_AGENT_INTERFACE ); }static void ofono_connect (DBusConnection *conn,void *user_data)
Signature · 1 line
1.7.9.5