diff options
author | Arun Raghavan <arun.raghavan@collabora.co.uk> | 2013-08-15 20:14:07 +0530 |
---|---|---|
committer | Arun Raghavan <arun.raghavan@collabora.co.uk> | 2013-08-15 20:14:07 +0530 |
commit | e6770e10b91faa0dd63a98e891587f4b3e167130 (patch) | |
tree | 7bbc1555154014e914aced65fab8d8e506742e6f | |
parent | 7f465f17caccd48ab42f863c3d9814405471d8c9 (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.mk | 34 | ||||
-rw-r--r-- | tools/mako/common.h | 45 | ||||
-rw-r--r-- | tools/mako/csd-client.c | 95 | ||||
-rw-r--r-- | tools/mako/csd-daemon.c | 325 |
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; +} |