summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Raghavan <arun.raghavan@collabora.co.uk>2013-08-15 20:14:07 +0530
committerArun Raghavan <arun.raghavan@collabora.co.uk>2013-08-15 20:14:07 +0530
commite6770e10b91faa0dd63a98e891587f4b3e167130 (patch)
tree7bbc1555154014e914aced65fab8d8e506742e6f
parent7f465f17caccd48ab42f863c3d9814405471d8c9 (diff)
First pass at helper daemon for CSD setup on mako
Uses libcsd-client.so to talk to the modem firmware on mako. Needs to be in a separate process as we issue the commands from UCM via Exec commands, but the library needs to at least be open throughout the duration of the call.
-rw-r--r--tools/mako/Android.mk34
-rw-r--r--tools/mako/common.h45
-rw-r--r--tools/mako/csd-client.c95
-rw-r--r--tools/mako/csd-daemon.c325
4 files changed, 499 insertions, 0 deletions
diff --git a/tools/mako/Android.mk b/tools/mako/Android.mk
new file mode 100644
index 0000000..8d04f20
--- /dev/null
+++ b/tools/mako/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH:=$(call my-dir)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_MODULE := csd-daemon
+
+LOCAL_MODULE_TAGS:=eng debug
+
+LOCAL_SRC_FILES := \
+ csd-daemon.c
+
+LOCAL_SHARED_LIBRARIES:= \
+ libdl
+
+LOCAL_PRELINK_MODULE := false
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_MODULE := csd-client
+
+LOCAL_MODULE_TAGS:=eng debug
+
+LOCAL_SRC_FILES := \
+ csd-client.c
+
+LOCAL_PRELINK_MODULE := false
+include $(BUILD_EXECUTABLE)
diff --git a/tools/mako/common.h b/tools/mako/common.h
new file mode 100644
index 0000000..931a4b6
--- /dev/null
+++ b/tools/mako/common.h
@@ -0,0 +1,45 @@
+/* csd-daemon.c
+**
+** Copyright 2013, Mozilla Corporation
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Mozilla Corporation ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#ifndef csdcommonh
+#define csdcommonh
+
+#define ERR(...) fprintf(stderr, __VA_ARGS__)
+
+#ifdef DEBUG
+#define DBG ERR
+#else
+#define DBG(...) do { } while(0)
+#endif
+
+#define COMMAND_MAX 256
+
+#define DEFAULT_RUNTIME_DIR "/data/csd-daemon"
+#define DEFAULT_SOCKET_PATH DEFAULT_RUNTIME_DIR "/socket"
+
+#endif
diff --git a/tools/mako/csd-client.c b/tools/mako/csd-client.c
new file mode 100644
index 0000000..96ee78e
--- /dev/null
+++ b/tools/mako/csd-client.c
@@ -0,0 +1,95 @@
+/* csd-daemon.c
+**
+** Copyright 2013, Mozilla Corporation
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Mozilla Corporation ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "common.h"
+
+static void usage(void)
+{
+ ERR("csd-client disable-device | enable-(rx|tx)-device | volume | mic-mute | start-voice | stop-voice | quit\n");
+}
+
+int main(int argc, char **argv)
+{
+ char *command = argv[1];
+ struct sockaddr_un sa;
+ int fd, len;
+ int32_t ret = 0;
+
+ if (argc != 2) {
+ usage();
+ return -1;
+ }
+
+ len = strlen(command);
+
+ if (len > COMMAND_MAX) {
+ ERR("Argument too long\n");
+ return -1;
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ ERR("Could not create socket\n");
+ goto error;
+ }
+
+ sa.sun_family = AF_UNIX;
+ snprintf(sa.sun_path, sizeof(sa.sun_path), DEFAULT_SOCKET_PATH);
+
+ if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ ERR("Could not connect to daemon\n");
+ goto error;
+ }
+
+ /* len + 1 because we want to write out the NULL as well */
+ if (write(fd, command, len + 1) < len) {
+ ERR("Error writing to socket");
+ goto error;
+ }
+
+ if (read(fd, &ret, 4) < 4) {
+ ERR("Error getting return code");
+ goto error;
+ }
+
+out:
+ close(fd);
+
+ return ret;
+
+error:
+ ret = -1;
+ goto out;
+}
diff --git a/tools/mako/csd-daemon.c b/tools/mako/csd-daemon.c
new file mode 100644
index 0000000..da20a8e
--- /dev/null
+++ b/tools/mako/csd-daemon.c
@@ -0,0 +1,325 @@
+/* csd-daemon.c
+**
+** Copyright 2013, Mozilla Corporation
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Mozilla Corporation ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define DEBUG 1
+#include "common.h"
+
+/* FIXME: we're always setting dmic + tty-off, is that always correct? */
+#define ACDB_SETTINGS 0x12
+
+struct state {
+ int sockfd;
+ int rx_dev, tx_dev;
+ int start_voice;
+};
+
+static struct state state;
+
+/* Based on prototypes in hardware/qcom/audio/hal/audio_hw.h */
+
+int (*acdb_loader_init_ACDB)();
+void (*acdb_loader_deallocate_ACDB)();
+void (*acdb_loader_send_audio_cal)(int, int);
+void (*acdb_loader_send_voice_cal)(int, int);
+
+int (*csd_client_init)(void);
+int (*csd_client_deinit)(void);
+int (*csd_client_disable_device)(void);
+int (*csd_client_enable_device)(int, int, uint32_t);
+int (*csd_client_volume)(int);
+int (*csd_client_mic_mute)(int);
+int (*csd_client_start_voice)(void);
+int (*csd_client_stop_voice)(void);
+
+static int init_lib(void)
+{
+ void *handle;
+
+ handle = dlopen("/system/lib/libacdbloader.so", RTLD_NOW);
+
+ acdb_loader_init_ACDB = dlsym(handle, "acdb_loader_init_ACDB");
+ acdb_loader_deallocate_ACDB = dlsym(handle, "acdb_loader_deallocate_ACDB");
+ acdb_loader_send_audio_cal = dlsym(handle, "acdb_loader_send_audio_cal");
+ acdb_loader_send_voice_cal = dlsym(handle, "acdb_loader_send_voice_cal");
+
+ if (!acdb_loader_init_ACDB || !acdb_loader_deallocate_ACDB ||
+ !acdb_loader_send_audio_cal || !acdb_loader_send_voice_cal) {
+ ERR("Could not find all acdb_loader_* functions\n");
+ return -1;
+ }
+
+ handle = dlopen("/system/lib/libcsd-client.so", RTLD_NOW);
+
+ csd_client_init = dlsym(handle, "csd_client_init");
+ csd_client_deinit = dlsym(handle, "csd_client_deinit");
+ csd_client_disable_device = dlsym(handle, "csd_client_disable_device");
+ csd_client_enable_device = dlsym(handle, "csd_client_enable_device");
+ csd_client_volume = dlsym(handle, "csd_client_volume");
+ csd_client_mic_mute = dlsym(handle, "csd_client_mic_mute");
+ csd_client_start_voice = dlsym(handle, "csd_client_start_voice");
+ csd_client_stop_voice = dlsym(handle, "csd_client_stop_voice");
+
+ if (!csd_client_init || !csd_client_deinit || !csd_client_disable_device ||
+ !csd_client_enable_device || !csd_client_volume ||
+ !csd_client_mic_mute || !csd_client_start_voice ||
+ !csd_client_stop_voice) {
+ ERR("Could not find all csd_client_* functions\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int init_daemon(void)
+{
+ struct stat st;
+ struct sockaddr_un sa;
+
+ state.rx_dev = state.tx_dev = -1;
+
+ if (stat(DEFAULT_RUNTIME_DIR, &st) < 0) {
+ if (errno != -ENOENT) {
+ /* First run, try to create the directory */
+ if (mkdir(DEFAULT_RUNTIME_DIR, 0700) < 0) {
+ ERR("Could not create runtime directory\n");
+ return -1;
+ }
+ } else {
+ ERR("Could not access runtime directory\n");
+ return -1;
+ }
+ }
+
+ if ((state.sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ ERR("Could not create socket\n");
+ return -1;
+ }
+
+ sa.sun_family = AF_UNIX;
+ snprintf(sa.sun_path, sizeof(sa.sun_path), DEFAULT_SOCKET_PATH);
+
+ if (stat(DEFAULT_SOCKET_PATH, &st) == 0) {
+ /* Either a daemon is already running or this is a stale socket */
+ if (connect(state.sockfd, (struct sockaddr *) &sa, sizeof(sa)) < 0 &&
+ errno == ECONNREFUSED) {
+ DBG("Removing stale socket\n");
+ unlink(DEFAULT_SOCKET_PATH);
+ } else {
+ ERR("Found existing socket, daemon already running?\n");
+ close(state.sockfd);
+ return -1;
+ }
+ }
+
+ if (bind(state.sockfd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ ERR("Could not bind address\n");
+ return -1;
+ }
+
+ if (listen(state.sockfd, 1) < 0) {
+ ERR("Could not start listening for connections\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Return negative value to signal death (currently only returns 0 */
+static int process(const char *command)
+{
+ int32_t ret = 0;
+
+ if (strncmp(command, "enable-rx-device", 16) == 0) {
+ if (sscanf(command, "%*s %d", &state.rx_dev) != 1) {
+ ERR("Bad argument to enable-rx-device\n");
+ goto out;
+ }
+
+ DBG("enable rx device: %d\n", state.rx_dev);
+
+ /* Only enable device after we get both rx and tx device */
+ if (state.rx_dev != -1 && state.tx_dev != -1) {
+ ret = csd_client_enable_device(state.rx_dev, state.tx_dev, ACDB_SETTINGS);
+ if (ret == 0 && state.start_voice) {
+ state.start_voice = 0;
+ ret = csd_client_start_voice();
+ }
+ }
+
+ } else if (strncmp(command, "enable-tx-device", 16) == 0) {
+ if (sscanf(command, "%*s %d", &state.tx_dev) != 1) {
+ ERR("Bad argument to enable-tx-device\n");
+ goto out;
+ }
+
+ DBG("enable tx device: %d\n", state.tx_dev);
+
+ /* Only enable device after we get both rx and tx device */
+ if (state.rx_dev != -1 && state.tx_dev != -1) {
+ ret = csd_client_enable_device(state.rx_dev, state.tx_dev, ACDB_SETTINGS);
+
+ if (ret == 0 && state.start_voice) {
+ state.start_voice = 0;
+ ret = csd_client_start_voice();
+ }
+ }
+
+ } else if (strcmp(command, "disable-device") == 0) {
+ ret = csd_client_disable_device();
+
+ } else if (strncmp(command, "volume", 6) == 0) {
+ int volume;
+
+ if (sscanf(command, "%*s %d", &volume) != 1) {
+ ERR("Bad volume\n");
+ goto out;
+ }
+
+ ret = csd_client_volume(volume);
+
+ } else if (strncmp(command, "mic-mute", 8) == 0) {
+ int state;
+
+ if (sscanf(command, "%*s %d", &state) != 1 || (state != 0 && state != 1)) {
+ ERR("Bad volume\n");
+ goto out;
+ }
+
+ ret = csd_client_mic_mute(state);
+
+ } else if (strcmp(command, "start-voice") == 0) {
+ if (state.rx_dev != -1 && state.tx_dev != -1)
+ ret = csd_client_start_voice();
+ else {
+ DBG("Deferring start voice till devices are enabled\n");
+ state.start_voice = 1;
+ ret = 0;
+ }
+
+ } else if (strcmp(command, "stop-voice") == 0) {
+ ret = csd_client_stop_voice();
+ /* Reset all the things */
+ state.start_voice = 0;
+ state.rx_dev = state.tx_dev = -1;
+
+ } else {
+ ERR("Bad command: %s\n", command);
+ }
+
+ if (ret < 0)
+ ERR("Error running command\n");
+
+ /* For now, there are no fatal errors */
+
+out:
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ char command[COMMAND_MAX];
+ int fd, done = 0, ret = 0;
+ unsigned int i;
+
+ if (argc > 1) {
+ ERR("Bad arguments\n");
+ return -1;
+ }
+
+ if (init_lib() < 0)
+ return -1;
+
+ if (init_daemon() < 0)
+ return -1;
+
+ if ((ret = acdb_loader_init_ACDB()) < 0) {
+ ERR("Could not initialise acdb loader\n");
+ return ret;
+ }
+
+ if ((ret = csd_client_init()) < 0) {
+ ERR("Could not initialise csd client\n");
+ return ret;
+ }
+
+ while (!done) {
+ fd = accept(state.sockfd, NULL, NULL);
+ if (fd < 0) {
+ ERR("Could not accept incoming connection\n");
+ continue;
+ }
+
+ /* We now read one "command" which is a string terminated with a NULL */
+ for (i = 0; i < sizeof(command); i++) {
+ if (read(fd, &command[i], 1) != 1) {
+ ERR("Error reading command. Ignoring.\n");
+ goto next;
+ }
+
+ if (command[i] == '\0') {
+ DBG("Got command: %s\n", command);
+ break;
+ }
+ }
+
+ if (strcmp(command, "quit") == 0) {
+ DBG("Got quit message. Exiting.\n");
+ done = 1;
+ } else if (process(command) < 0) {
+ ERR("Fatal error while processing commands. Exiting.\n");
+ ret = -1;
+ done = 1;
+ }
+
+ /* Send back return code */
+ if (write(fd, &ret, 4) < 4)
+ ERR("Error sending return code");
+
+next:
+ close(fd);
+ }
+
+ close(state.sockfd);
+
+out:
+ csd_client_deinit();
+ acdb_loader_deallocate_ACDB();
+
+ return ret;
+}