From fdd09ffd2192b67ccd73ecac1fd7baf2f97705cf Mon Sep 17 00:00:00 2001 From: Nicolas Bertrand Date: Mon, 24 Sep 2012 18:09:05 +0200 Subject: [PATCH] Move set audio path api here --- CMakeLists.txt | 3 +- include/pr3_audio.h | 28 +++ packaging/tel-plugin-imc-pr3.spec | 1 + src/desc_imc_pr3.c | 9 + src/pr3_audio.c | 356 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 include/pr3_audio.h create mode 100644 src/pr3_audio.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 78556a2..2068376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ SET(CMAKE_INSTALL_PREFIX "${PREFIX}") # Set required packages INCLUDE(FindPkgConfig) -pkg_check_modules(pkgs REQUIRED glib-2.0 tcore dlog) +pkg_check_modules(pkgs REQUIRED glib-2.0 tcore dlog alsa) FOREACH(flag ${pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") @@ -30,6 +30,7 @@ MESSAGE(${CMAKE_EXE_LINKER_FLAGS}) SET(SRCS src/desc_imc_pr3.c src/tty_imc.c + src/pr3_audio.c ) diff --git a/include/pr3_audio.h b/include/pr3_audio.h new file mode 100644 index 0000000..063049b --- /dev/null +++ b/include/pr3_audio.h @@ -0,0 +1,28 @@ +/* + * + * oFono - Server + * + * Copyright (C) 2012 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 + * + */ + + +#include +#include + +TReturn pr3_audio_set_sound_path(TcoreHal *hal, int device); +gboolean pr3_audio_init(void); +gboolean pr3_audio_unload(void); diff --git a/packaging/tel-plugin-imc-pr3.spec b/packaging/tel-plugin-imc-pr3.spec index a8b7554..36e9830 100644 --- a/packaging/tel-plugin-imc-pr3.spec +++ b/packaging/tel-plugin-imc-pr3.spec @@ -11,6 +11,7 @@ BuildRequires: cmake BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(tcore) +BuildRequires: pkgconfig(alsa) %description PR3 plateform telephony plugin diff --git a/src/desc_imc_pr3.c b/src/desc_imc_pr3.c index c7f5673..7cc6bf7 100644 --- a/src/desc_imc_pr3.c +++ b/src/desc_imc_pr3.c @@ -35,6 +35,7 @@ #include #include "tty_imc.h" +#include "pr3_audio.h" #define BUF_LEN_MAX 512 @@ -96,6 +97,7 @@ static struct tcore_hal_operations hops = { .power = hal_power, .send = hal_send, + .set_sound_path = pr3_audio_set_sound_path, }; static gboolean on_recv_tty_message(GIOChannel *channel, GIOCondition condition, gpointer data) @@ -168,6 +170,10 @@ static gboolean on_init(TcorePlugin *plugin) dbg("tty_fd = %d, watch_id_tty=%d ", data->tty_fd, data->watch_id_tty); + if (pr3_audio_init() != TRUE) + err("Error in audio initialization") + + return TRUE; } @@ -176,6 +182,9 @@ static void on_unload(TcorePlugin *plugin) if (!plugin) return; + if (pr3_audio_unload() != TRUE) + err("Error in audio unload") + dbg("I'm unload"); } diff --git a/src/pr3_audio.c b/src/pr3_audio.c new file mode 100644 index 0000000..9cb23cb --- /dev/null +++ b/src/pr3_audio.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include + +#include "pr3_audio.h" + + +#define MODE_NORMAL 0 +#define MODE_RINGTONE 1 +#define MODE_IN_CALL 2 +#define MODE_IN_COMMUNICATION 3 + +#define DISCONNECT 0x00 +#define DEVICE_OUT_EARPIECE 0x1 +#define DEVICE_OUT_SPEAKER 0x2 +#define DEVICE_OUT_WIRED_HEADSET 0x4 +#define DEVICE_OUT_WIRED_HEADPHONE 0x8 + +#define A1026_IOCTL_MAGIC 'u' + +#define A1026_BOOTUP_INIT _IO(A1026_IOCTL_MAGIC, 0x01) +#define A1026_SUSPEND _IO(A1026_IOCTL_MAGIC, 0x02) +#define A1026_ENABLE_CLOCK _IO(A1026_IOCTL_MAGIC, 0x03) + +#define ES305B_DEVICE "/dev/audience_es305" + +#define FIRMWARE_NAME_MAX_LENGTH 64 + +static const unsigned char es305b_no_acoustic[] = { 0x80, 0x31, 0x00, 0x06 }; + +static int a1026_enabled; + +static snd_pcm_t *handle_playback = NULL; +static snd_pcm_t *handle_capture = NULL; + +static const char *playback_name(int mode, uint32_t device) +{ + switch (device) { + case DEVICE_OUT_EARPIECE: + return "VoicePlayback_Earpiece_incall"; + case DEVICE_OUT_SPEAKER: + return "VoicePlayback_Speaker_incall"; + case DEVICE_OUT_WIRED_HEADSET: + return "VoicePlayback_Headset_incall"; + case DEVICE_OUT_WIRED_HEADPHONE: + return "VoicePlayback_Headphone_incall"; + } + + return NULL; +} +static const char *capture_name(int mode, uint32_t device) +{ + /* No distinction as ALSA mixer control are set during playback + * path opening */ + return "VoiceCapture_incall"; +} + +static void pcm_init(void) +{ + int card = snd_card_get_index("medfieldaudio"); + + dbg("Card index: %d\n", card); +} + +static void pcm_enable(int mode, uint32_t device) +{ + const char *pcm_playback; + const char *pcm_capture; + int err; + + pcm_playback = playback_name(mode, device); + pcm_capture = capture_name(mode, device); + + dbg("Playback: %s\n", pcm_playback); + dbg("Capture: %s\n", pcm_capture); + + if (!pcm_playback || !pcm_capture) + return; + + if (handle_capture) { + snd_pcm_close(handle_capture); + handle_capture = NULL; + } + + if (handle_playback) { + snd_pcm_close(handle_playback); + handle_playback = NULL; + } + + err = snd_pcm_open(&handle_playback, pcm_playback, + SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) { + err("Failed to open %s\n", pcm_playback); + return; + } + + err = snd_pcm_open(&handle_capture, pcm_capture, + SND_PCM_STREAM_CAPTURE, 0); + if (err < 0) { + err("Failed to open %s\n", pcm_capture); + goto close_playback; + } + + err = snd_pcm_set_params(handle_playback, + SND_PCM_FORMAT_S16_LE, + SND_PCM_ACCESS_RW_INTERLEAVED, + 2, 48000, 0, 500000); /* 0.5sec */ + if (err < 0) { + err("Failed to set params for %s\n", + pcm_playback); + goto close_capture; + } + + err = snd_pcm_set_params(handle_capture, + SND_PCM_FORMAT_S16_LE, + SND_PCM_ACCESS_RW_INTERLEAVED, + 1, 48000, 1, 500000); /* 0.5sec */ + if (err < 0) { + err("Failed to set params for %s\n", + pcm_capture); + goto close_capture; + } + + return; + +close_capture: + snd_pcm_close(handle_capture); + +close_playback: + snd_pcm_close(handle_playback); +} + +static void pcm_disable(void) +{ + if (handle_capture) { + snd_pcm_close(handle_capture); + handle_capture = NULL; + } + + if (handle_playback) { + snd_pcm_close(handle_playback); + handle_playback = NULL; + } +} + +static void a1026_init(void) +{ + const unsigned char cmd1[] = { 0x80, 0x20, 0x00, 0x00 }; + const unsigned char cmd2[] = { 0x80, 0x21, 0x00, 0x00 }; + const char firmware_name[FIRMWARE_NAME_MAX_LENGTH] = { + "vpimg_es305b-NH.bin"}; + unsigned char buf[4]; + char label[100]; + ssize_t written, len; + unsigned int i; + int fd, err; + + dbg("A1026 init\n"); + + fd = open(ES305B_DEVICE, O_RDWR, 0); + if (fd < 0) { + err("Failed to open audience device\n"); + return; + } + + err = ioctl(fd, A1026_ENABLE_CLOCK); + if (err < 0) { + err("Failed to enable audience clock\n"); + goto done; + } + + err = ioctl(fd, A1026_BOOTUP_INIT, firmware_name); + if (err < 0) { + err("Failed to boot up audience\n"); + goto done; + } + + dbg("A1026 init done\n"); + + written = write(fd, cmd1, sizeof(cmd1)); + if (written < 0) + err("First command failed\n"); + + usleep(20000); + + for (i = 0; i < sizeof(label) - 1; i++) { + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + err("Read command failed\n"); + goto suspend; + } + + label[i] = buf[3]; + + written = write(fd, cmd2, sizeof(cmd2)); + if (written < 0) { + err("Second command failed\n"); + goto suspend; + } + + usleep(5000); + } + + err("Firmware: %s\n", label); + + a1026_enabled = 0; + +suspend: + err = ioctl(fd, A1026_SUSPEND); + if (err < 0) + err("Failed to suspend audience\n"); + +done: + close(fd); +} + +static void a1026_wakeup(void) +{ + ssize_t written; + int fd, err; + + dbg("A1026 wakeup\n"); + + if (a1026_enabled) + return; + + fd = open(ES305B_DEVICE, O_RDWR, 0); + if (fd < 0) { + err("Failed to open audience device\n"); + return; + } + + err = ioctl(fd, A1026_ENABLE_CLOCK); + if (err < 0) { + err("Failed to enable audience clock\n"); + goto done; + } + + written = write(fd, es305b_no_acoustic, sizeof(es305b_no_acoustic)); + if (written < 0) + err("Acoustic command failed\n"); + + a1026_enabled = 1; + +done: + close(fd); +} + +static void a1026_suspend(void) +{ + int fd, err; + + dbg("A1026 suspend\n"); + + if (!a1026_enabled) + return; + + fd = open(ES305B_DEVICE, O_RDWR, 0); + if (fd < 0) { + err("Failed to open audience device\n"); + return; + } + + err = ioctl(fd, A1026_SUSPEND); + if (err < 0) + err("Failed to suspend audience\n"); + + a1026_enabled = 0; + + close(fd); +} + +static void medfield_init(void) +{ + a1026_init(); + + pcm_init(); + + a1026_wakeup(); + + usleep(50000); + + /* Enable and disable voice device since otherwise it seems the + * first call stays silent */ + pcm_enable(MODE_IN_CALL, DEVICE_OUT_EARPIECE); + + usleep(100000); + + pcm_disable(); + + a1026_suspend(); +} + +static void medfield_cleanup(void) +{ + pcm_disable(); + + a1026_suspend(); +} + +static void medfield_enable(int mode, uint32_t device) +{ + a1026_wakeup(); + + pcm_enable(mode, device); +} + +static void medfield_disable(void) +{ + pcm_disable(); + + a1026_suspend(); +} + +static inline int audio_device_converter(int device) +{ + switch (device) { + case 0: + return DISCONNECT; + case CALL_SOUND_PATH_HANDSET: + return DEVICE_OUT_EARPIECE; + case CALL_SOUND_PATH_SPEAKER: + return DEVICE_OUT_SPEAKER; + default: + return DEVICE_OUT_EARPIECE; + } +} + +TReturn pr3_audio_set_sound_path(TcoreHal *hal, int device) +{ + int devId = audio_device_converter(device); + + dbg("device Id %d", devId); + + if (devId == DISCONNECT) + medfield_disable(); + else + medfield_enable(MODE_IN_CALL, devId); + + return TCORE_RETURN_SUCCESS; +} + +gboolean pr3_audio_init(void) +{ + medfield_init(); + + return TRUE; +} + +gboolean pr3_audio_unload(void) +{ + medfield_cleanup(); + + return TRUE; +} -- 2.7.4