summaryrefslogtreecommitdiffstats
path: root/firmware/target/hosted/ibasso
diff options
context:
space:
mode:
authorUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:44:29 +0100
committerUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:57:55 +0100
commitdbabd0d9c34a33bc0c51243ec37f230d117db955 (patch)
tree46de348929ce739702a230a2587fdb5539585753 /firmware/target/hosted/ibasso
parentcef17e3d59ad93f766e8ee23b1610540a33dfe5e (diff)
downloadrockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.tar.gz
rockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.zip
iBasso DX50/DX90: Major code cleanup and reorganization.
Reorganization - Separated iBasso devices from PLATFORM_ANDROID. These are now standlone hosted targets. Most device specific code is in the firmware/target/hosted/ibasso directory. - No dependency on Android SDK, only the Android NDK is needed. 32 bit Android NDK and Android API Level 16. - Separate implementation for each device where feasible. Code cleanup - Rewrite of existing code, from simple reformat to complete reimplementation. - New backlight interface, seperating backlight from touchscreen. - Rewrite of device button handler, removing unneeded code and fixing memory leaks. - New Debug messages interface logging to Android adb logcat (DEBUGF, panicf, logf). - Rewrite of lcd device handler, removing unneeded code and fixing memory leaks. - Rewrite of audiohw device handler/pcm interface, removing unneeded code and fixing memory leaks, enabling 44.1/48kHz pthreaded playback. - Rewrite of power and powermng, proper shutdown, using batterylog results (see http://gerrit.rockbox.org/r/#/c/1047/). - Rewrite of configure (Android NDK) and device specific config. - Rewrite of the Android NDK specific Makefile. Misc - All plugins/games/demos activated. - Update tinyalsa to latest from https://github.com/tinyalsa/tinyalsa. Includes - http://gerrit.rockbox.org/r/#/c/993/ - http://gerrit.rockbox.org/r/#/c/1010/ - http://gerrit.rockbox.org/r/#/c/1035/ Does not include http://gerrit.rockbox.org/r/#/c/1007/ due to new backlight interface and new option for hold switch, touchscreen, physical button interaction. Rockbox needs the iBasso DX50/DX90 loader for startup, see http://gerrit.rockbox.org/r/#/c/1099/ The loader expects Rockbox to be installed in /mnt/sdcard/.rockbox/. If /mnt/sdcard/ is accessed as USB mass storage device, Rockbox will exit gracefully and the loader will restart Rockbox on USB disconnect. Tested on iBasso DX50. Compiled (not tested) for iBasso DX90. Compiled (not tested) for PLATFORM_ANDROID. Change-Id: I5f5e22e68f5b4cf29c28e2b40b2c265f2beb7ab7
Diffstat (limited to 'firmware/target/hosted/ibasso')
-rw-r--r--firmware/target/hosted/ibasso/android_ndk.make49
-rw-r--r--firmware/target/hosted/ibasso/audiohw-ibasso.c81
-rw-r--r--firmware/target/hosted/ibasso/backlight-ibasso.c132
-rw-r--r--firmware/target/hosted/ibasso/backlight-target.h39
-rw-r--r--firmware/target/hosted/ibasso/button-ibasso.c420
-rw-r--r--firmware/target/hosted/ibasso/button-ibasso.h61
-rw-r--r--firmware/target/hosted/ibasso/button-target.h63
-rw-r--r--firmware/target/hosted/ibasso/debug-ibasso.c70
-rw-r--r--firmware/target/hosted/ibasso/debug-ibasso.h38
-rw-r--r--firmware/target/hosted/ibasso/dx50/audiohw-dx50.c68
-rw-r--r--firmware/target/hosted/ibasso/dx50/button-dx50.c96
-rw-r--r--firmware/target/hosted/ibasso/dx50/codec-dx50.h51
-rw-r--r--firmware/target/hosted/ibasso/dx90/audiohw-dx90.c63
-rw-r--r--firmware/target/hosted/ibasso/dx90/button-dx90.c104
-rw-r--r--firmware/target/hosted/ibasso/dx90/codec-dx90.h35
-rw-r--r--firmware/target/hosted/ibasso/hostfs-ibasso.c47
-rw-r--r--firmware/target/hosted/ibasso/lcd-ibasso.c195
-rw-r--r--firmware/target/hosted/ibasso/lcd-target.h44
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.c488
-rw-r--r--firmware/target/hosted/ibasso/pcm-ibasso.h33
-rw-r--r--firmware/target/hosted/ibasso/power-ibasso.c97
-rw-r--r--firmware/target/hosted/ibasso/powermgmt-ibasso.c122
-rw-r--r--firmware/target/hosted/ibasso/sysfs-ibasso.c404
-rw-r--r--firmware/target/hosted/ibasso/sysfs-ibasso.h111
-rw-r--r--firmware/target/hosted/ibasso/system-ibasso.c101
-rw-r--r--firmware/target/hosted/ibasso/system-target.h33
-rw-r--r--firmware/target/hosted/ibasso/tinyalsa/include/sound/asound.h820
-rw-r--r--firmware/target/hosted/ibasso/tinyalsa/include/tinyalsa/asoundlib.h260
-rw-r--r--firmware/target/hosted/ibasso/tinyalsa/mixer.c502
-rw-r--r--firmware/target/hosted/ibasso/tinyalsa/pcm.c1049
-rw-r--r--firmware/target/hosted/ibasso/usb-ibasso.c92
-rw-r--r--firmware/target/hosted/ibasso/usb-ibasso.h54
-rw-r--r--firmware/target/hosted/ibasso/vold-ibasso.c203
-rw-r--r--firmware/target/hosted/ibasso/vold-ibasso.h42
34 files changed, 6067 insertions, 0 deletions
diff --git a/firmware/target/hosted/ibasso/android_ndk.make b/firmware/target/hosted/ibasso/android_ndk.make
new file mode 100644
index 0000000000..e2f5c93db8
--- /dev/null
+++ b/firmware/target/hosted/ibasso/android_ndk.make
@@ -0,0 +1,49 @@
+# __________ __ ___
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+#
+# Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+# Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+# Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+# Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+#
+# 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+
+
+# This is a glibc compatibility hack to provide a get_nprocs() replacement.
+# The NDK ships cpu-features.c which has a compatible function android_getCpuCount()
+CPUFEAT = $(ANDROID_NDK_PATH)/sources/android/cpufeatures
+CPUFEAT_BUILD = $(BUILDDIR)/android-ndk/sources/android/cpufeatures
+INCLUDES += -I$(CPUFEAT)
+OTHER_SRC += $(CPUFEAT)/cpu-features.c
+CLEANOBJS += $(CPUFEAT_BUILD)/cpu-features.o
+$(CPUFEAT_BUILD)/cpu-features.o: $(CPUFEAT)/cpu-features.c
+ $(SILENT)mkdir -p $(dir $@)
+ $(call PRINTS,CC $(subst $(CPUFEAT)/,,$<))$(CC) -o $@ -c $(CPUFEAT)/cpu-features.c $(GCCOPTS) -Wno-unused
+
+.SECONDEXPANSION:
+.PHONY: clean dirs
+
+DIRS += $(CPUFEAT_BUILD)
+
+.PHONY:
+$(BUILDDIR)/$(BINARY): $$(OBJ) $(FIRMLIB) $(VOICESPEEXLIB) $(CORE_LIBS) $(CPUFEAT_BUILD)/cpu-features.o
+ $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS) -Wl,-Map,$(BUILDDIR)/rockbox.map
+ $(call PRINTS,OC $(@F))$(call objcopy,$@,$@)
+
+$(DIRS):
+ $(SILENT)mkdir -p $@
+
+dirs: $(DIRS)
+
+clean::
+ $(SILENT)rm -rf $(BUILDDIR)/android-ndk
diff --git a/firmware/target/hosted/ibasso/audiohw-ibasso.c b/firmware/target/hosted/ibasso/audiohw-ibasso.c
new file mode 100644
index 0000000000..447e133eba
--- /dev/null
+++ b/firmware/target/hosted/ibasso/audiohw-ibasso.c
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include "config.h"
+#include "debug.h"
+#include "pcm_sw_volume.h"
+#include "settings.h"
+
+#include "debug-ibasso.h"
+#include "pcm-ibasso.h"
+
+
+void audiohw_close(void)
+{
+ TRACE;
+
+ pcm_close_device();
+}
+
+
+void set_software_volume(void)
+{
+ /* -73dB (?) minimum software volume in decibels. See pcm-internal.h. */
+ static const int SW_VOLUME_MIN = 730;
+
+ int sw_volume_l = 0;
+ int sw_volume_r = 0;
+
+ if(global_settings.balance > 0)
+ {
+ if(global_settings.balance == 100)
+ {
+ sw_volume_l = PCM_MUTE_LEVEL;
+ }
+ else
+ {
+ sw_volume_l -= SW_VOLUME_MIN * global_settings.balance / 100;
+ }
+ }
+ else if(global_settings.balance < 0)
+ {
+ if(global_settings.balance == -100)
+ {
+ sw_volume_r = PCM_MUTE_LEVEL;
+ }
+ else
+ {
+ sw_volume_r = SW_VOLUME_MIN * global_settings.balance / 100;
+ }
+ }
+
+ DEBUGF("DEBUG %s: global_settings.balance: %d, sw_volume_l: %d, sw_volume_r: %d.",
+ __func__,
+ global_settings.balance,
+ sw_volume_l,
+ sw_volume_r);
+
+ /* Emulate balance with software volume. */
+ pcm_set_master_volume(sw_volume_l, sw_volume_r);
+}
diff --git a/firmware/target/hosted/ibasso/backlight-ibasso.c b/firmware/target/hosted/ibasso/backlight-ibasso.c
new file mode 100644
index 0000000000..907980e01a
--- /dev/null
+++ b/firmware/target/hosted/ibasso/backlight-ibasso.c
@@ -0,0 +1,132 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdbool.h>
+
+#include "config.h"
+#include "debug.h"
+#include "lcd.h"
+#include "panic.h"
+
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+/*
+ Prevent excessive backlight_hw_on usage.
+ Required for proper seeking.
+*/
+static bool _backlight_enabled = false;
+
+
+bool backlight_hw_init(void)
+{
+ TRACE;
+
+ /*
+ /sys/devices/platform/rk29_backlight/backlight/rk28_bl/bl_power
+ 0: backlight on
+ */
+ if(! sysfs_set_int(SYSFS_BACKLIGHT_POWER, 0))
+ {
+ DEBUGF("ERROR %s: Can not enable backlight.", __func__);
+ panicf("ERROR %s: Can not enable backlight.", __func__);
+ return false;
+ }
+
+ _backlight_enabled = true;
+
+ return true;
+}
+
+
+void backlight_hw_on(void)
+{
+ if(! _backlight_enabled)
+ {
+ backlight_hw_init();
+ lcd_enable(true);
+ }
+}
+
+
+void backlight_hw_off(void)
+{
+ TRACE;
+
+ /*
+ /sys/devices/platform/rk29_backlight/backlight/rk28_bl/bl_power
+ 1: backlight off
+ */
+ if(! sysfs_set_int(SYSFS_BACKLIGHT_POWER, 1))
+ {
+ DEBUGF("ERROR %s: Can not disable backlight.", __func__);
+ return;
+ }
+
+ lcd_enable(false);
+
+ _backlight_enabled = false;
+}
+
+
+/*
+ Prevent excessive backlight_hw_brightness usage.
+ Required for proper seeking.
+*/
+static int _current_brightness = -1;
+
+
+void backlight_hw_brightness(int brightness)
+{
+ if(brightness > MAX_BRIGHTNESS_SETTING)
+ {
+ DEBUGF("DEBUG %s: Adjusting brightness from %d to MAX.", __func__, brightness);
+ brightness = MAX_BRIGHTNESS_SETTING;
+ }
+ if(brightness < MIN_BRIGHTNESS_SETTING)
+ {
+ DEBUGF("DEBUG %s: Adjusting brightness from %d to MIN.", __func__, brightness);
+ brightness = MIN_BRIGHTNESS_SETTING;
+ }
+
+ if(_current_brightness == brightness)
+ {
+ return;
+ }
+
+ TRACE;
+
+ _current_brightness = brightness;
+
+ /*
+ /sys/devices/platform/rk29_backlight/backlight/rk28_bl/max_brightness
+ 0 ... 255
+ */
+ if(! sysfs_set_int(SYSFS_BACKLIGHT_BRIGHTNESS, _current_brightness))
+ {
+ DEBUGF("ERROR %s: Can not set brightness.", __func__);
+ return;
+ }
+}
diff --git a/firmware/target/hosted/ibasso/backlight-target.h b/firmware/target/hosted/ibasso/backlight-target.h
new file mode 100644
index 0000000000..aa8fafab04
--- /dev/null
+++ b/firmware/target/hosted/ibasso/backlight-target.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _BACKLIGHT_TARGET_H_
+#define _BACKLIGHT_TARGET_H_
+
+
+#include <stdbool.h>
+
+
+/* See backlight.c */
+bool backlight_hw_init(void);
+void backlight_hw_on(void);
+void backlight_hw_off(void);
+void backlight_hw_brightness(int brightness);
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/button-ibasso.c b/firmware/target/hosted/ibasso/button-ibasso.c
new file mode 100644
index 0000000000..1694992ea4
--- /dev/null
+++ b/firmware/target/hosted/ibasso/button-ibasso.c
@@ -0,0 +1,420 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <sys/inotify.h>
+#include <sys/limits.h>
+#include <sys/poll.h>
+
+#include "config.h"
+#include "backlight.h"
+#include "button.h"
+#include "debug.h"
+#include "panic.h"
+#include "settings.h"
+#include "touchscreen.h"
+
+#include "button-ibasso.h"
+#include "button-target.h"
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+#define EVENT_TYPE_BUTTON 1
+
+/* /dev/input/event0 */
+#define EVENT_CODE_BUTTON_LINEOUT 113
+#define EVENT_CODE_BUTTON_SPDIF 114
+#define EVENT_CODE_BUTTON_HOLD 115
+
+/* /dev/input/event1 */
+#define EVENT_CODE_BUTTON_SDCARD 143
+
+
+#define EVENT_TYPE_TOUCHSCREEN 3
+
+/* /dev/input/event2 */
+#define EVENT_CODE_TOUCHSCREEN_X 53
+#define EVENT_CODE_TOUCHSCREEN_Y 54
+#define EVENT_CODE_TOUCHSCREEN 57
+
+#define EVENT_VALUE_TOUCHSCREEN_PRESS 1
+#define EVENT_VALUE_TOUCHSCREEN_RELEASE -1
+
+
+/*
+ Changing bit, when hold switch is toggled.
+ Bit is off when hold switch is engaged.
+*/
+#define HOLD_SWITCH_BIT 16
+
+/*
+ Changing bit, when coaxial out is plugged.
+ Bit is off when coaxial out is plugged in.
+*/
+#define COAX_BIT 32
+
+/*
+ Changing bit, when line out is plugged.
+ Bit is off when line out is plugged in.
+*/
+#define SPDIF_BIT 64
+
+
+/* State of the hold switch; true: hold switch engaged. */
+static bool _hold = false;
+
+
+/* See button.h. */
+bool button_hold(void)
+{
+ char hold_state;
+ if(! sysfs_get_char(SYSFS_HOLDKEY, &hold_state))
+ {
+ DEBUGF("ERROR %s: Can not get hold switch state.", __func__);
+ hold_state = HOLD_SWITCH_BIT;
+ }
+
+ /*DEBUGF("%s: hold_state: %d, %c.", __func__, hold_state, hold_state);*/
+
+ /*bool coax_connected = ! (hold_state & COAX_BIT);
+ bool spdif_connected = ! (hold_state & SPDIF_BIT);*/
+
+ _hold = ! (hold_state & HOLD_SWITCH_BIT);
+
+ /*DEBUGF("%s: _hold: %d, coax_connected: %d, spdif_connected: %d.", __func__, _hold, coax_connected, spdif_connected);*/
+
+ return _hold;
+}
+
+
+/* Input devices monitored with poll API. */
+static struct pollfd* _fds = NULL;
+
+
+/* Number of input devices monitored with poll API. */
+static nfds_t _nfds = 0;
+
+
+/* The names of the devices in _fds. */
+static char** _device_names = NULL;
+
+
+/* Open device device_name and add it to the list of polled devices. */
+static bool open_device(const char* device_name)
+{
+ int fd = open(device_name, O_RDONLY);
+ if(fd == -1)
+ {
+ DEBUGF("ERROR %s: open failed on %s.", __func__, device_name);
+ return false;
+ }
+
+ struct pollfd* new_fds = realloc(_fds, sizeof(struct pollfd) * (_nfds + 1));
+ if(new_fds == NULL)
+ {
+ DEBUGF("ERROR %s: realloc for _fds failed.", __func__);
+ panicf("ERROR %s: realloc for _fds failed.", __func__);
+ return false;
+ }
+
+ _fds = new_fds;
+ _fds[_nfds].fd = fd;
+ _fds[_nfds].events = POLLIN;
+
+ char** new_device_names = realloc(_device_names, sizeof(char*) * (_nfds + 1));
+ if(new_device_names == NULL)
+ {
+ DEBUGF("ERROR %s: realloc for _device_names failed.", __func__);
+ panicf("ERROR %s: realloc for _device_names failed.", __func__);
+ return false;
+ }
+
+ _device_names = new_device_names;
+ _device_names[_nfds] = strdup(device_name);
+ if(_device_names[_nfds] == NULL)
+ {
+ DEBUGF("ERROR %s: strdup failed.", __func__);
+ panicf("ERROR %s: strdup failed.", __func__);
+ return false;
+ }
+
+ ++_nfds;
+
+ DEBUGF("DEBUG %s: Opened device %s.", __func__, device_name);
+
+ return true;
+}
+
+
+/* See button.h. */
+void button_init_device(void)
+{
+ TRACE;
+
+ if((_fds != NULL) || (_nfds != 0) || (_device_names != NULL))
+ {
+ DEBUGF("ERROR %s: Allready initialized.", __func__);
+ panicf("ERROR %s: Allready initialized.", __func__);
+ return;
+ }
+
+ /* The input device directory. */
+ static const char device_path[] = "/dev/input";
+
+ /* Path delimeter. */
+ static const char delimeter[] = "/";
+
+ /* Open all devices in device_path. */
+ DIR* dir = opendir(device_path);
+ if(dir == NULL)
+ {
+ DEBUGF("ERROR %s: opendir failed: errno: %d.", __func__, errno);
+ panicf("ERROR %s: opendir failed: errno: %d.", __func__, errno);
+ return;
+ }
+
+ char device_name[PATH_MAX];
+ strcpy(device_name, device_path);
+ strcat(device_name, delimeter);
+ char* device_name_idx = device_name + strlen(device_name);
+
+ struct dirent* dir_entry;
+ while((dir_entry = readdir(dir)))
+ {
+ if( ((dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '\0'))
+ || ((dir_entry->d_name[0] == '.') && (dir_entry->d_name[1] == '.') && (dir_entry->d_name[2] == '\0')))
+ {
+ continue;
+ }
+
+ strcpy(device_name_idx, dir_entry->d_name);
+
+ /* Open and add device to _fds. */
+ open_device(device_name);
+ }
+
+ closedir(dir);
+
+ /* Sanity check. */
+ if(_nfds < 2)
+ {
+ DEBUGF("ERROR %s: No input devices.", __func__);
+ panicf("ERROR %s: No input devices.", __func__);
+ return;
+ }
+
+ /*
+ Hold switch has a separate interface for its state.
+ Input events just report that it has been toggled, but not the state.
+ */
+ button_hold();
+}
+
+
+/* Last known touchscreen coordinates. */
+static int _last_x = 0;
+static int _last_y = 0;
+
+
+/* Last known touchscreen state. */
+static enum
+{
+ TOUCHSCREEN_STATE_UNKNOWN = 0,
+ TOUCHSCREEN_STATE_UP,
+ TOUCHSCREEN_STATE_DOWN
+} _last_touch_state = TOUCHSCREEN_STATE_UNKNOWN;
+
+
+static bool handle_touchscreen_event(__u16 code, __s32 value)
+{
+ bool read_more = false;
+
+ switch(code)
+ {
+ case EVENT_CODE_TOUCHSCREEN_X:
+ {
+ _last_x = value;
+
+ /* x -> next will be y. */
+ read_more = true;
+
+ break;
+ }
+
+ case EVENT_CODE_TOUCHSCREEN_Y:
+ {
+ _last_y = value;
+ break;
+ }
+
+ case EVENT_CODE_TOUCHSCREEN:
+ {
+ if(value == EVENT_VALUE_TOUCHSCREEN_PRESS)
+ {
+ _last_touch_state = TOUCHSCREEN_STATE_DOWN;
+
+ /* Press -> next will be x. */
+ read_more = true;
+ }
+ else
+ {
+ _last_touch_state = TOUCHSCREEN_STATE_UP;
+ }
+ break;
+ }
+ }
+
+ return read_more;
+}
+
+
+/* Last known hardware buttons pressed. */
+static int _last_btns = BUTTON_NONE;
+
+
+/* See button.h. */
+int button_read_device(int *data)
+{
+ bool read_more = true;
+ while(read_more)
+ {
+ read_more = false;
+
+ /* Poll all input devices. */
+ poll(_fds, _nfds, 0);
+
+ for(nfds_t fds_idx = 0; fds_idx < _nfds; ++fds_idx)
+ {
+ if(! (_fds[fds_idx].revents & POLLIN))
+ {
+ continue;
+ }
+
+ struct input_event event;
+ if(read(_fds[fds_idx].fd, &event, sizeof(event)) < (int) sizeof(event))
+ {
+ DEBUGF("ERROR %s: Read of input devices failed.", __func__);
+ continue;
+ }
+
+ /*DEBUGF("DEBUG %s: device: %s, event.type: %d, event.code: %d, event.value: %d", __func__, _device_names[fds_idx], event.type, event.code, event.value);*/
+
+ switch(event.type)
+ {
+ case EVENT_TYPE_BUTTON:
+ {
+ if(event.code == EVENT_CODE_BUTTON_HOLD)
+ {
+ /* Hold switch toggled, update hold switch state. */
+ button_hold();
+ backlight_hold_changed(_hold);
+
+ _last_btns = BUTTON_NONE;
+ break;
+ }
+
+ _last_btns = handle_button_event(event.code, event.value, _last_btns);
+
+ if(_hold)
+ {
+ /* Hold switch engaged. Ignore all button events. */
+ _last_btns = BUTTON_NONE;
+ }
+
+ /*DEBUGF("DEBUG %s: _last_btns: %#8.8x", __func__, _last_btns);*/
+ break;
+ }
+
+ case EVENT_TYPE_TOUCHSCREEN:
+ {
+ if(_hold)
+ {
+ /* Hold switch engaged, ignore all touchscreen events. */
+ _last_touch_state = TOUCHSCREEN_STATE_UNKNOWN;
+ _last_btns = BUTTON_NONE;
+ }
+ else
+ {
+ read_more = handle_touchscreen_event(event.code, event.value);
+ /*DEBUGF("DEBUG %s: _last_touch_state: %d, _last_x: %d, _last_y: %d, read_more: %s", __func__, _last_touch_state, _last_x, _last_y, read_more ? "true" : "false");*/
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ Get grid button/coordinates based on the current touchscreen mode
+ Caveat: The caller seemingly depends on *data always being filled with
+ the last known touchscreen position, so always call
+ touchscreen_to_pixels().
+ */
+ int touch = touchscreen_to_pixels(_last_x, _last_y, data);
+
+ if(_last_touch_state == TOUCHSCREEN_STATE_DOWN)
+ {
+ return _last_btns | touch;
+ }
+
+ /*DEBUGF("DEBUG %s: _last_btns: %#8.8x.", __func__, _last_btns);*/
+
+ return _last_btns;
+}
+
+
+void button_close_device(void)
+{
+ TRACE;
+
+ if(_fds)
+ {
+ for(nfds_t fds_idx = 0; fds_idx < _nfds; ++fds_idx)
+ {
+ close(_fds[fds_idx].fd);
+ }
+ free(_fds);
+ _fds = NULL;
+ }
+
+ if(_device_names)
+ {
+ for(nfds_t fds_idx = 0; fds_idx < _nfds; ++fds_idx)
+ {
+ free(_device_names[fds_idx]);
+ }
+ free(_device_names);
+ _device_names = NULL;
+ }
+
+ _nfds = 0;
+}
diff --git a/firmware/target/hosted/ibasso/button-ibasso.h b/firmware/target/hosted/ibasso/button-ibasso.h
new file mode 100644
index 0000000000..09c09e7c83
--- /dev/null
+++ b/firmware/target/hosted/ibasso/button-ibasso.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _BUTTON_IBASSO_H_
+#define _BUTTON_IBASSO_H_
+
+
+#include <sys/types.h>
+
+
+/* /dev/input/event0 */
+#define EVENT_CODE_BUTTON_PWR 116
+#define EVENT_CODE_BUTTON_PWR_LONG 117
+
+/* /dev/input/event1 */
+#define EVENT_CODE_BUTTON_VOLPLUS 158
+#define EVENT_CODE_BUTTON_VOLMINUS 159
+#define EVENT_CODE_BUTTON_REV 160
+#define EVENT_CODE_BUTTON_PLAY 161
+#define EVENT_CODE_BUTTON_NEXT 162
+
+#define EVENT_VALUE_BUTTON_PRESS 1
+#define EVENT_VALUE_BUTTON_RELEASE 0
+
+
+/*
+ Handle hardware button events.
+ code: Input event code.
+ value: Input event value.
+ last_btns: Last known pressed buttons.
+ Returns: Currently pressed buttons as bitmask (BUTTON_ values in button-target.h).
+*/
+int handle_button_event(__u16 code, __s32 value, int last_btns);
+
+
+/* Clean up the button device handler. */
+void button_close_device(void);
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/button-target.h b/firmware/target/hosted/ibasso/button-target.h
new file mode 100644
index 0000000000..d1b3c8a8de
--- /dev/null
+++ b/firmware/target/hosted/ibasso/button-target.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _BUTTON_TARGET_H_
+#define _BUTTON_TARGET_H_
+
+
+/* Hardware buttons. */
+#define BUTTON_LEFT 0x00000001
+#define BUTTON_RIGHT 0x00000002
+#define BUTTON_PLAY 0x00000004
+#define BUTTON_POWER 0x00000008
+#define BUTTON_VOL_UP 0x00000010
+#define BUTTON_VOL_DOWN 0x00000020
+#define BUTTON_POWER_LONG 0x00000040
+
+#define BUTTON_MAIN ( BUTTON_LEFT | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_RIGHT \
+ | BUTTON_PLAY | BUTTON_POWER | BUTTON_POWER_LONG)
+
+
+#define STATE_SPDIF_UNPLUGGED 32
+#define STATE_LINEOUT_UNPLUGGED 64
+
+
+/* Touchscreen area buttons 3x3 grid. */
+#define BUTTON_TOPLEFT 0x00001000
+#define BUTTON_TOPMIDDLE 0x00002000
+#define BUTTON_TOPRIGHT 0x00004000
+#define BUTTON_MIDLEFT 0x00008000
+#define BUTTON_CENTER 0x00010000
+#define BUTTON_MIDRIGHT 0x00020000
+#define BUTTON_BOTTOMLEFT 0x00040000
+#define BUTTON_BOTTOMMIDDLE 0x00080000
+#define BUTTON_BOTTOMRIGHT 0x00100000
+
+
+/* Power-off */
+#define POWEROFF_BUTTON BUTTON_POWER_LONG
+#define POWEROFF_COUNT 0
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/debug-ibasso.c b/firmware/target/hosted/ibasso/debug-ibasso.c
new file mode 100644
index 0000000000..6295de1f6c
--- /dev/null
+++ b/firmware/target/hosted/ibasso/debug-ibasso.c
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <android/log.h>
+
+#include "config.h"
+#include "debug.h"
+
+#include "debug-ibasso.h"
+
+
+static const char log_tag[] = "Rockbox";
+
+
+void debug_init(void)
+{}
+
+
+void debugf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ __android_log_vprint(ANDROID_LOG_DEBUG, log_tag, fmt, ap);
+ va_end(ap);
+}
+
+
+void ldebugf(const char* file, int line, const char *fmt, ...)
+{
+ va_list ap;
+ /* 13: 5 literal chars and 8 chars for the line number. */
+ char buf[strlen(file) + strlen(fmt) + 13];
+ snprintf(buf, sizeof(buf), "%s (%d): %s", file, line, fmt);
+ va_start(ap, fmt);
+ __android_log_vprint(ANDROID_LOG_DEBUG, log_tag, buf, ap);
+ va_end(ap);
+}
+
+
+void debug_trace(const char* function)
+{
+ static const char trace_tag[] = "TRACE: ";
+ char msg[strlen(trace_tag) + strlen(function) + 1];
+ snprintf(msg, sizeof(msg), "%s%s", trace_tag, function);
+ __android_log_write(ANDROID_LOG_DEBUG, log_tag, msg);
+}
diff --git a/firmware/target/hosted/ibasso/debug-ibasso.h b/firmware/target/hosted/ibasso/debug-ibasso.h
new file mode 100644
index 0000000000..456f189a5a
--- /dev/null
+++ b/firmware/target/hosted/ibasso/debug-ibasso.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _DEBUG_IBASSO_H_
+#define _DEBUG_IBASSO_H_
+
+
+void debug_trace(const char* function);
+
+
+#ifdef DEBUG
+#define TRACE debug_trace(__func__)
+#else
+#define TRACE
+#endif
+
+#endif \ No newline at end of file
diff --git a/firmware/target/hosted/ibasso/dx50/audiohw-dx50.c b/firmware/target/hosted/ibasso/dx50/audiohw-dx50.c
new file mode 100644
index 0000000000..5e61348c8d
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx50/audiohw-dx50.c
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include "config.h"
+#include "debug.h"
+
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+extern void set_software_volume(void);
+
+
+void audiohw_set_volume(int volume)
+{
+ set_software_volume();
+
+ /*
+ See codec-dx50.h.
+ -128db -> -1 (adjusted to 0, mute)
+ -127dB to 0dB -> 1 to 255 in steps of 2
+ volume is in centibels (tenth-decibels).
+ */
+ int volume_adjusted = (volume / 10) * 2 + 255;
+
+ DEBUGF("DEBUG %s: volume: %d, volume_adjusted: %d.", __func__, volume, volume_adjusted);
+
+ if(volume_adjusted > 255)
+ {
+ DEBUGF("DEBUG %s: Adjusting volume from %d to 255.", __func__, volume);
+ volume_adjusted = 255;
+ }
+ if(volume_adjusted < 0)
+ {
+ DEBUGF("DEBUG %s: Adjusting volume from %d to 0.", __func__, volume);
+ volume_adjusted = 0;
+ }
+
+ /*
+ /dev/codec_volume
+ 0 ... 255
+ */
+ if(! sysfs_set_int(SYSFS_DX50_CODEC_VOLUME, volume_adjusted))
+ {
+ DEBUGF("ERROR %s: Can not set volume.", __func__);
+ }
+}
diff --git a/firmware/target/hosted/ibasso/dx50/button-dx50.c b/firmware/target/hosted/ibasso/dx50/button-dx50.c
new file mode 100644
index 0000000000..b4f6952d44
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx50/button-dx50.c
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include "config.h"
+#include "button.h"
+
+#include "button-ibasso.h"
+
+
+int handle_button_event(__u16 code, __s32 value, int last_btns)
+{
+ int button = BUTTON_NONE;
+
+ switch(code)
+ {
+ case EVENT_CODE_BUTTON_PWR:
+ {
+ button = BUTTON_POWER;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_PWR_LONG:
+ {
+ button = BUTTON_POWER_LONG;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_VOLPLUS:
+ {
+ button = BUTTON_VOL_UP;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_VOLMINUS:
+ {
+ button = BUTTON_VOL_DOWN;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_REV:
+ {
+ button = BUTTON_LEFT;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_PLAY:
+ {
+ button = BUTTON_PLAY;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_NEXT:
+ {
+ button = BUTTON_RIGHT;
+ break;
+ }
+
+ default:
+ {
+ return BUTTON_NONE;
+ }
+ }
+
+ int buttons = last_btns;
+ if(value == EVENT_VALUE_BUTTON_PRESS)
+ {
+ buttons = (last_btns | button);
+ }
+ else
+ {
+ buttons = (last_btns & (~ button));
+ }
+
+ return buttons;
+}
diff --git a/firmware/target/hosted/ibasso/dx50/codec-dx50.h b/firmware/target/hosted/ibasso/dx50/codec-dx50.h
new file mode 100644
index 0000000000..89a1a3f1c4
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx50/codec-dx50.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _CODEC_DX50_H_
+#define _CODEC_DX50_H_
+
+
+#define AUDIOHW_CAPS MONO_VOL_CAP
+
+
+/*
+ http://www.wolfsonmicro.com/media/76425/WM8740.pdf
+
+ 0.5 * ( x - 255 ) = ydB 1 <= x <= 255
+ mute x = 0
+
+ x = 255 -> 0dB
+ .
+ .
+ .
+ x = 2 -> -126.5dB
+ x = 1 -> -127dB
+ x = 0 -> -128dB
+
+ See audiohw.h, sound.c.
+*/
+AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -128, 0, -30)
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/dx90/audiohw-dx90.c b/firmware/target/hosted/ibasso/dx90/audiohw-dx90.c
new file mode 100644
index 0000000000..ef18aae4bd
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx90/audiohw-dx90.c
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include "config.h"
+#include "debug.h"
+
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+extern void set_software_volume(void);
+
+
+void audiohw_set_volume(int volume)
+{
+ set_software_volume();
+
+ /* See codec-dx90.h. -2550 to 0 -> 0 to 255 */
+ int volume_adjusted = ((volume + 2550) / 10);
+
+ DEBUGF("DEBUG %s: volume: %d, volume_adjusted: %d.", __func__, volume, volume_adjusted);
+
+ if(volume_adjusted > 255)
+ {
+ DEBUGF("DEBUG %s: Adjusting volume from %d to 255.", __func__, volume);
+ volume_adjusted = 255;
+ }
+ if(volume_adjusted < 0)
+ {
+ DEBUGF("DEBUG %s: Adjusting volume from %d to 0.", __func__, volume);
+ volume_adjusted = 0;
+ }
+
+ /*
+ /sys/class/codec/es9018_volume
+ 0 ... 255
+ */
+ if(! sysfs_set_int(SYSFS_DX90_ES9018_VOLUME, volume_adjusted))
+ {
+ DEBUGF("ERROR %s: Can not set volume.", __func__);
+ }
+}
diff --git a/firmware/target/hosted/ibasso/dx90/button-dx90.c b/firmware/target/hosted/ibasso/dx90/button-dx90.c
new file mode 100644
index 0000000000..27e4be0c1e
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx90/button-dx90.c
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include "config.h"
+#include "button.h"
+
+#include "button-ibasso.h"
+
+
+int handle_button_event(__u16 code, __s32 value, int last_btns)
+{
+ int button = BUTTON_NONE;
+
+ switch(code)
+ {
+ case EVENT_CODE_BUTTON_PWR:
+ {
+ button = BUTTON_POWER;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_PWR_LONG:
+ {
+ button = BUTTON_POWER_LONG;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_VOLPLUS:
+ {
+ button = BUTTON_VOL_UP;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_VOLMINUS:
+ {
+ button = BUTTON_VOL_DOWN;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_REV:
+ {
+ button = BUTTON_LEFT;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_PLAY:
+ {
+ button = BUTTON_PLAY;
+ break;
+ }
+
+ case EVENT_CODE_BUTTON_NEXT:
+ {
+ button = BUTTON_RIGHT;
+ break;
+ }
+
+ default:
+ {
+ return BUTTON_NONE;
+ }
+ }
+
+ if( (button == BUTTON_RIGHT)
+ && ((last_btns & BUTTON_LEFT) == BUTTON_LEFT)
+ && (value == EVENT_VALUE_BUTTON_RELEASE))
+ {
+ /* Workaround for a wrong feedback, only present with DX90. */
+ button = BUTTON_LEFT;
+ }
+
+ int buttons = last_btns;
+ if(value == EVENT_VALUE_BUTTON_PRESS)
+ {
+ buttons = (last_btns | button);
+ }
+ else
+ {
+ buttons = (last_btns & (~button));
+ }
+
+ return buttons;
+}
diff --git a/firmware/target/hosted/ibasso/dx90/codec-dx90.h b/firmware/target/hosted/ibasso/dx90/codec-dx90.h
new file mode 100644
index 0000000000..b96377dfec
--- /dev/null
+++ b/firmware/target/hosted/ibasso/dx90/codec-dx90.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _CODEC_DX90_H_
+#define _CODEC_DX90_H_
+
+
+#define AUDIOHW_CAPS MONO_VOL_CAP
+
+
+AUDIOHW_SETTING(VOLUME, "", 0, 1, -255, 0, -128)
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/hostfs-ibasso.c b/firmware/target/hosted/ibasso/hostfs-ibasso.c
new file mode 100644
index 0000000000..3970d06987
--- /dev/null
+++ b/firmware/target/hosted/ibasso/hostfs-ibasso.c
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <unistd.h>
+
+#include "debug-ibasso.h"
+
+
+/* See hostfs.h. */
+
+
+int hostfs_init(void)
+{
+ TRACE;
+
+ return 0;
+}
+
+
+int hostfs_flush(void)
+{
+ TRACE;
+
+ sync();
+ return 0;
+}
diff --git a/firmware/target/hosted/ibasso/lcd-ibasso.c b/firmware/target/hosted/ibasso/lcd-ibasso.c
new file mode 100644
index 0000000000..4e03ba7e50
--- /dev/null
+++ b/firmware/target/hosted/ibasso/lcd-ibasso.c
@@ -0,0 +1,195 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <linux/fb.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "debug.h"
+#include "events.h"
+#include "panic.h"
+
+#include "debug-ibasso.h"
+#include "lcd-target.h"
+#include "sysfs-ibasso.h"
+
+
+fb_data *dev_fb = 0;
+
+
+/* Framebuffer device handle. */
+static int dev_fd = 0;
+
+
+void lcd_init_device(void)
+{
+ TRACE;
+
+ dev_fd = open("/dev/graphics/fb0", O_RDWR);
+ if(dev_fd == -1)
+ {
+ DEBUGF("ERROR %s: open failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ exit(errno);
+ }
+
+ /* Get the changeable information. */
+ struct fb_var_screeninfo vinfo;
+ if(ioctl(dev_fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
+ {
+ DEBUGF("ERROR %s: ioctl FBIOGET_VSCREENINFO failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ exit(errno);
+ }
+
+ DEBUGF("DEBUG %s: bits_per_pixel: %u, width: %u, height: %u.", __func__, vinfo.bits_per_pixel, vinfo.width, vinfo.height);
+
+ /*
+ Framebuffer does not fit the screen, a bug of iBassos Firmware, not Rockbox.
+ Cannot be solved with parameters.
+ */
+ /*vinfo.bits_per_pixel = LCD_DEPTH;
+ vinfo.xres = LCD_WIDTH;
+ vinfo.xres_virtual = LCD_WIDTH;
+ vinfo.width = LCD_WIDTH;
+ vinfo.yres = LCD_HEIGHT;
+ vinfo.yres_virtual = LCD_HEIGHT;
+ vinfo.height = LCD_HEIGHT;
+ vinfo.activate = FB_ACTIVATE_NOW;
+ if(ioctl(dev_fd, FBIOPUT_VSCREENINFO, &vinfo) == -1)
+ {
+ DEBUGF("ERROR %s: ioctl FBIOPUT_VSCREENINFO failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ exit(EXIT_FAILURE);
+ }*/
+
+
+ /* Sanity check: Does framebuffer config match Rockbox config? */
+ size_t screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
+ if(screensize != FRAMEBUFFER_SIZE)
+ {
+ DEBUGF("ERROR %s: Screen size does not match config: %d != %d.", __func__, screensize, FRAMEBUFFER_SIZE);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Map the device to memory. */
+ dev_fb = mmap(0, FRAMEBUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dev_fd, 0);
+ if(dev_fb == MAP_FAILED)
+ {
+ DEBUGF("ERROR %s: mmap failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ exit(errno);
+ }
+
+ /* Activate Rockbox LCD. */
+ lcd_enable(true);
+}
+
+
+void lcd_shutdown(void)
+{
+ TRACE;
+
+ lcd_set_active(false);
+ munmap(dev_fb, FRAMEBUFFER_SIZE);
+ close(dev_fd) ;
+}
+
+
+/*
+ Left as reference. Unblanking does not work as expected, will not enable LCD after a few
+ seconds of power down.
+ Instead the backlight power is toggled.
+*/
+/*void lcd_power_on(void)
+{
+ TRACE;
+
+ if(ioctl(dev_fd, FBIOBLANK, VESA_NO_BLANKING) == -1)
+ {
+ DEBUGF("ERROR %s: ioctl FBIOBLANK failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ panicf("ERROR %s: ioctl FBIOBLANK failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ return;
+ }
+
+ lcd_set_active(true);
+ send_event(LCD_EVENT_ACTIVATION, NULL);
+}
+
+
+void lcd_power_off(void)
+{
+ TRACE;
+
+ lcd_set_active(false);
+
+ if(ioctl(dev_fd, FBIOBLANK, VESA_POWERDOWN) == -1)
+ {
+ DEBUGF("ERROR %s: ioctl FBIOBLANK failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ panicf("ERROR %s: ioctl FBIOBLANK failed on /dev/graphics/fb0, errno: %d.", __func__, errno);
+ return;
+ }
+}*/
+
+
+void lcd_enable(bool on)
+{
+ TRACE;
+
+ lcd_set_active(on);
+
+ if(on)
+ {
+ /*
+ /sys/power/state
+ on: Cancel suspend.
+ */
+ if(! sysfs_set_string(SYSFS_POWER_STATE, "on"))
+ {
+ DEBUGF("ERROR %s: Can not set power state.", __func__);
+ }
+
+ send_event(LCD_EVENT_ACTIVATION, NULL);
+ }
+}
+
+
+void lcd_sleep(void)
+{
+ TRACE;
+
+ /*
+ See system_init(). Without suspend blocker und mute prevention this will interrupt playback.
+ Essentially, we are turning off the touch screen.
+ /sys/power/state
+ mem: Suspend to RAM.
+ */
+ if(! sysfs_set_string(SYSFS_POWER_STATE, "mem"))
+ {
+ DEBUGF("ERROR %s: Can not set power state.", __func__);
+ }
+}
diff --git a/firmware/target/hosted/ibasso/lcd-target.h b/firmware/target/hosted/ibasso/lcd-target.h
new file mode 100644
index 0000000000..50cc92599d
--- /dev/null
+++ b/firmware/target/hosted/ibasso/lcd-target.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef __LCD_TARGET_H__
+#define __LCD_TARGET_H__
+
+
+#include "lcd.h"
+
+
+/*
+ Framebuffer device and framebuffer access.
+ See lcd-memframe.c
+*/
+extern fb_data *dev_fb;
+#define LCD_FRAMEBUF_ADDR(col, row) (dev_fb + row * LCD_WIDTH + col)
+
+
+/* See lcd-memframe.c */
+extern void lcd_set_active(bool active);
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.c b/firmware/target/hosted/ibasso/pcm-ibasso.c
new file mode 100644
index 0000000000..14ef298af0
--- /dev/null
+++ b/firmware/target/hosted/ibasso/pcm-ibasso.c
@@ -0,0 +1,488 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "debug.h"
+#include "panic.h"
+#include "pcm.h"
+#include "pcm-internal.h"
+
+#include "sound/asound.h"
+#include "tinyalsa/asoundlib.h"
+
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+/* Tiny alsa handle. */
+static struct pcm* _alsa_handle = NULL;
+
+
+/* Bytes left in the Rockbox PCM frame buffer. */
+static size_t _pcm_buffer_size = 0;
+
+
+/* Rockbox PCM frame buffer. */
+static const void *_pcm_buffer = NULL;
+
+
+/*
+ 1: PCM thread suspended.
+ 0: PCM thread running.
+ These are used by pcm_play_[lock|unlock] or pcm_play_dma_[start|stop|pause]. These need to be
+ separated because of nested calls for locking and stopping.
+*/
+static volatile sig_atomic_t _dma_stopped = 1;
+static volatile sig_atomic_t _dma_locked = 1;
+
+
+/* Mutex for PCM thread suspend/unsuspend. */
+static pthread_mutex_t _dma_suspended_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+
+/* Signal condition for PCM thread suspend/unsuspend. */
+static pthread_cond_t _dma_suspended_cond = PTHREAD_COND_INITIALIZER;
+
+
+static void* pcm_thread_run(void* nothing)
+{
+ (void) nothing;
+
+ DEBUGF("DEBUG %s: Thread start.", __func__);
+
+ while(true)
+ {
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ while((_dma_stopped == 1) || (_dma_locked == 1))
+ {
+ DEBUGF("DEBUG %s: Playback suspended.", __func__);
+ pthread_cond_wait(&_dma_suspended_cond, &_dma_suspended_mtx);
+ DEBUGF("DEBUG %s: Playback resumed.", __func__);
+ }
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+
+ if(_pcm_buffer_size == 0)
+ {
+ /* Retrive a new PCM buffer from Rockbox. */
+ if(! pcm_play_dma_complete_callback(PCM_DMAST_OK, &_pcm_buffer, &_pcm_buffer_size))
+ {
+ DEBUGF("DEBUG %s: No new buffer.", __func__);
+
+ usleep( 10000 );
+ continue;
+ }
+ }
+ pcm_play_dma_status_callback(PCM_DMAST_STARTED);
+
+ /* This relies on Rockbox PCM frame buffer size == ALSA PCM frame buffer size. */
+ if(pcm_write(_alsa_handle, _pcm_buffer, _pcm_buffer_size) != 0)
+ {
+ DEBUGF("ERROR %s: pcm_write failed: %s.", __func__, pcm_get_error(_alsa_handle));
+
+ usleep( 10000 );
+ continue;
+ }
+
+ _pcm_buffer_size = 0;
+
+ /*DEBUGF("DEBUG %s: Thread running.", __func__);*/
+ }
+
+ DEBUGF("DEBUG %s: Thread end.", __func__);
+
+ return 0;
+}
+
+
+#ifdef DEBUG
+
+/* https://github.com/tinyalsa/tinyalsa/blob/master/tinypcminfo.c */
+
+static const char* format_lookup[] =
+{
+ /*[0] =*/ "S8",
+ "U8",
+ "S16_LE",
+ "S16_BE",
+ "U16_LE",
+ "U16_BE",
+ "S24_LE",
+ "S24_BE",
+ "U24_LE",
+ "U24_BE",
+ "S32_LE",
+ "S32_BE",
+ "U32_LE",
+ "U32_BE",
+ "FLOAT_LE",
+ "FLOAT_BE",
+ "FLOAT64_LE",
+ "FLOAT64_BE",
+ "IEC958_SUBFRAME_LE",
+ "IEC958_SUBFRAME_BE",
+ "MU_LAW",
+ "A_LAW",
+ "IMA_ADPCM",
+ "MPEG",
+ /*[24] =*/ "GSM",
+ [31] = "SPECIAL",
+ "S24_3LE",
+ "S24_3BE",
+ "U24_3LE",
+ "U24_3BE",
+ "S20_3LE",
+ "S20_3BE",
+ "U20_3LE",
+ "U20_3BE",
+ "S18_3LE",
+ "S18_3BE",
+ "U18_3LE",
+ /*[43] =*/ "U18_3BE"
+};
+
+
+static const char* pcm_get_format_name(unsigned int bit_index)
+{
+ return(bit_index < 43 ? format_lookup[bit_index] : NULL);
+}
+
+#endif
+
+
+/* Thread that copies the Rockbox PCM buffer to ALSA. */
+static pthread_t _pcm_thread;
+
+
+/* ALSA card and device. */
+static const unsigned int CARD = 0;
+static const unsigned int DEVICE = 0;
+
+
+/* ALSA config. */
+static struct pcm_config _config;
+
+
+void pcm_play_dma_init(void)
+{
+ TRACE;
+
+#ifdef DEBUG
+
+ /*
+ DEBUG pcm_play_dma_init: Access: 0x000009
+ DEBUG pcm_play_dma_init: Format[0]: 0x000044
+ DEBUG pcm_play_dma_init: Format[1]: 0x000010
+ DEBUG pcm_play_dma_init: Format: S16_LE
+ DEBUG pcm_play_dma_init: Format: S24_LE
+ DEBUG pcm_play_dma_init: Format: S20_3LE
+ DEBUG pcm_play_dma_init: Subformat: 0x000001
+ DEBUG pcm_play_dma_init: Rate: min = 8000Hz, max = 192000Hz
+ DEBUG pcm_play_dma_init: Channels: min = 2, max = 2
+ DEBUG pcm_play_dma_init: Sample bits: min=16, max=32
+ DEBUG pcm_play_dma_init: Period size: min=8, max=10922
+ DEBUG pcm_play_dma_init: Period count: min=3, max=128
+ DEBUG pcm_play_dma_init: 0 mixer controls.
+ */
+
+ struct pcm_params* params = pcm_params_get(CARD, DEVICE, PCM_OUT);
+ if(params == NULL)
+ {
+ DEBUGF("ERROR %s: Card/device does not exist.", __func__);
+ panicf("ERROR %s: Card/device does not exist.", __func__);
+ return;
+ }
+
+ struct pcm_mask* m = pcm_params_get_mask(params, PCM_PARAM_ACCESS);
+ if(m)
+ {
+ DEBUGF("DEBUG %s: Access: %#08x", __func__, m->bits[0]);
+ }
+
+ m = pcm_params_get_mask(params, PCM_PARAM_FORMAT);
+ if(m)
+ {
+ DEBUGF("DEBUG %s: Format[0]: %#08x", __func__, m->bits[0]);
+ DEBUGF("DEBUG %s: Format[1]: %#08x", __func__, m->bits[1]);
+
+ unsigned int j;
+ unsigned int k;
+ const unsigned int bitcount = sizeof(m->bits[0]) * 8;
+ for(k = 0; k < 2; ++k)
+ {
+ for(j = 0; j < bitcount; ++j)
+ {
+ const char* name;
+ if(m->bits[k] & (1 << j))
+ {
+ name = pcm_get_format_name(j + (k * bitcount));
+ if(name)
+ {
+ DEBUGF("DEBUG %s: Format: %s", __func__, name);
+ }
+ }
+ }
+ }
+ }
+
+ m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT);
+ if(m)
+ {
+ DEBUGF("DEBUG %s: Subformat: %#08x", __func__, m->bits[0]);
+ }
+
+ unsigned int min = pcm_params_get_min(params, PCM_PARAM_RATE);
+ unsigned int max = pcm_params_get_max(params, PCM_PARAM_RATE) ;
+ DEBUGF("DEBUG %s: Rate: min = %uHz, max = %uHz", __func__, min, max);
+
+ min = pcm_params_get_min(params, PCM_PARAM_CHANNELS);
+ max = pcm_params_get_max(params, PCM_PARAM_CHANNELS);
+ DEBUGF("DEBUG %s: Channels: min = %u, max = %u", __func__, min, max);
+
+ min = pcm_params_get_min(params, PCM_PARAM_SAMPLE_BITS);
+ max = pcm_params_get_max(params, PCM_PARAM_SAMPLE_BITS);
+ DEBUGF("DEBUG %s: Sample bits: min=%u, max=%u", __func__, min, max);
+
+ min = pcm_params_get_min(params, PCM_PARAM_PERIOD_SIZE);
+ max = pcm_params_get_max(params, PCM_PARAM_PERIOD_SIZE);
+ DEBUGF("DEBUG %s: Period size: min=%u, max=%u", __func__, min, max);
+
+ min = pcm_params_get_min(params, PCM_PARAM_PERIODS);
+ max = pcm_params_get_max(params, PCM_PARAM_PERIODS);
+ DEBUGF("DEBUG %s: Period count: min=%u, max=%u", __func__, min, max);
+
+ pcm_params_free(params);
+
+ struct mixer* mixer = mixer_open(CARD);
+ if(! mixer)
+ {
+ DEBUGF("ERROR %s: Failed to open mixer.", __func__);
+ }
+ else
+ {
+ int num_ctls = mixer_get_num_ctls(mixer);
+
+ DEBUGF("DEBUG %s: %d mixer controls.", __func__, num_ctls);
+
+ mixer_close(mixer);
+ }
+
+#endif
+
+ if(_alsa_handle != NULL)
+ {
+ DEBUGF("ERROR %s: Allready initialized.", __func__);
+ panicf("ERROR %s: Allready initialized.", __func__);
+ return;
+ }
+
+ /*
+ Rockbox outputs 16 Bit/44.1kHz stereo by default.
+
+ ALSA frame buffer size = config.period_count * config.period_size * config.channels * (16 \ 8)
+ = 4 * 256 * 2 * 2
+ = 4096
+ = Rockbox PCM buffer size
+ pcm_thread_run relies on this size match. See pcm_mixer.h.
+ */
+ _config.channels = 2;
+ _config.rate = 44100;
+ _config.period_size = 256;
+ _config.period_count = 4;
+ _config.format = PCM_FORMAT_S16_LE;
+ _config.start_threshold = 0;
+ _config.stop_threshold = 0;
+ _config.silence_threshold = 0;
+
+ _alsa_handle = pcm_open(CARD, DEVICE, PCM_OUT, &_config);
+ if(! pcm_is_ready(_alsa_handle))
+ {
+ DEBUGF("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
+ panicf("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
+ return;
+ }
+
+ DEBUGF("DEBUG %s: ALSA PCM frame buffer size: %d.", __func__, pcm_frames_to_bytes(_alsa_handle, pcm_get_buffer_size(_alsa_handle)));
+
+ /* Create pcm thread in the suspended state. */
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_stopped = 1;
+ _dma_locked = 1;
+ pthread_create(&_pcm_thread, NULL, pcm_thread_run, NULL);
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+}
+
+
+void pcm_play_dma_start(const void *addr, size_t size)
+{
+ TRACE;
+
+ /*
+ DX50
+ /sys/class/codec/mute
+ Mute: echo 'A' > /sys/class/codec/mute
+ Unmute: echo 'B' > /sys/class/codec/mute
+
+ DX90?
+ */
+ if(! sysfs_set_char(SYSFS_MUTE, 'B'))
+ {
+ DEBUGF("ERROR %s: Could not unmute.", __func__);
+ panicf("ERROR %s: Could not unmute.", __func__);
+ }
+
+ _pcm_buffer = addr;
+ _pcm_buffer_size = size;
+
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_stopped = 0;
+ pthread_cond_signal(&_dma_suspended_cond);
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+}
+
+
+/* TODO: Why is this in the API if it gets never called? */
+void pcm_play_dma_pause(bool pause)
+{
+ TRACE;
+
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_stopped = pause ? 1 : 0;
+ if(_dma_stopped == 0)
+ {
+ pthread_cond_signal(&_dma_suspended_cond);
+ }
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+}
+
+
+void pcm_play_dma_stop(void)
+{
+ TRACE;
+
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_stopped = 1;
+ pcm_stop(_alsa_handle);
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+}
+
+
+/* Unessecary play locks before pcm_play_dma_postinit. */
+static int _play_lock_recursion_count = -10000;
+
+
+void pcm_play_dma_postinit(void)
+{
+ TRACE;
+
+ _play_lock_recursion_count = 0;
+}
+
+
+void pcm_play_lock(void)
+{
+ TRACE;
+
+ ++_play_lock_recursion_count;
+
+ if(_play_lock_recursion_count == 1)
+ {
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_locked = 1;
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+ }
+}
+
+
+void pcm_play_unlock(void)
+{
+ TRACE;
+
+ --_play_lock_recursion_count;
+
+ if(_play_lock_recursion_count == 0)
+ {
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_locked = 0;
+ pthread_cond_signal(&_dma_suspended_cond);
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+ }
+}
+
+
+void pcm_dma_apply_settings(void)
+{
+ unsigned int rate = pcm_get_frequency();
+
+ DEBUGF("DEBUG %s: Current sample rate: %u, next sampe rate: %u.", __func__, _config.rate, rate);
+
+ if(( _config.rate != rate) && (rate >= 8000) && (rate <= 192000))
+ {
+ _config.rate = rate;
+
+ pcm_close(_alsa_handle);
+ _alsa_handle = pcm_open(CARD, DEVICE, PCM_OUT, &_config);
+
+ if(! pcm_is_ready(_alsa_handle))
+ {
+ DEBUGF("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
+ panicf("ERROR %s: pcm_open failed: %s.", __func__, pcm_get_error(_alsa_handle));
+ }
+ }
+}
+
+
+size_t pcm_get_bytes_waiting(void)
+{
+ TRACE;
+
+ return _pcm_buffer_size;
+}
+
+
+/* TODO: WTF */
+const void* pcm_play_dma_get_peak_buffer(int* count)
+{
+ TRACE;
+
+ uintptr_t addr = (uintptr_t) _pcm_buffer;
+ *count = _pcm_buffer_size / 4;
+ return (void*) ((addr + 3) & ~3);
+}
+
+
+void pcm_close_device(void)
+{
+ TRACE;
+
+ pthread_mutex_lock(&_dma_suspended_mtx);
+ _dma_stopped = 1;
+ pthread_mutex_unlock(&_dma_suspended_mtx);
+
+ pcm_close(_alsa_handle);
+ _alsa_handle = NULL;
+}
diff --git a/firmware/target/hosted/ibasso/pcm-ibasso.h b/firmware/target/hosted/ibasso/pcm-ibasso.h
new file mode 100644
index 0000000000..588c4dfb9b
--- /dev/null
+++ b/firmware/target/hosted/ibasso/pcm-ibasso.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef __PCM_IBASSO_H__
+#define __PCM_IBASSO_H__
+
+
+/* Clean up the audio device handler. */
+void pcm_close_device(void);
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/power-ibasso.c b/firmware/target/hosted/ibasso/power-ibasso.c
new file mode 100644
index 0000000000..8257de5f33
--- /dev/null
+++ b/firmware/target/hosted/ibasso/power-ibasso.c
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/reboot.h>
+
+#include "config.h"
+#include "debug.h"
+#include "power.h"
+
+#include "button-ibasso.h"
+#include "debug-ibasso.h"
+#include "pcm-ibasso.h"
+#include "sysfs-ibasso.h"
+#include "vold-ibasso.h"
+
+
+unsigned int power_input_status(void)
+{
+ /*TRACE;*/
+
+ /*
+ /sys/class/power_supply/usb/present
+ 0: No external power supply connected.
+ 1: External power supply connected.
+ */
+ int val = 0;
+ if(! sysfs_get_int(SYSFS_USB_POWER_PRESENT, &val))
+ {
+ DEBUGF("ERROR %s: Can not get power supply status.", __func__);
+ return POWER_INPUT_NONE;
+ }
+
+ return val ? POWER_INPUT_USB_CHARGER : POWER_INPUT_NONE;
+}
+
+
+void power_off(void)
+{
+ TRACE;
+
+ button_close_device();
+
+ if(vold_monitor_forced_close_imminent())
+ {
+ /*
+ We are here, because Android Vold is going to kill Rockbox. Instead of powering off,
+ we exit into the loader.
+ */
+ DEBUGF("DEBUG %s: Exit Rockbox.", __func__);
+ exit(42);
+ }
+
+ reboot(RB_POWER_OFF);
+}
+
+
+/* Returns true, if battery is charging, false else. */
+bool charging_state(void)
+{
+ /*TRACE;*/
+
+ /*
+ /sys/class/power_supply/battery/status
+ "Full", "Charging", "Discharging"
+ */
+ char state[9];
+ if(! sysfs_get_string(SYSFS_BATTERY_STATUS, state, 9))
+ {
+ DEBUGF("ERROR %s: Can not get battery charging state.", __func__);
+ return false;
+ }
+
+ return(strcmp(state, "Charging") == 0);;
+}
diff --git a/firmware/target/hosted/ibasso/powermgmt-ibasso.c b/firmware/target/hosted/ibasso/powermgmt-ibasso.c
new file mode 100644
index 0000000000..7df0064097
--- /dev/null
+++ b/firmware/target/hosted/ibasso/powermgmt-ibasso.c
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdio.h>
+
+#include "config.h"
+#include "debug.h"
+#include "panic.h"
+
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+/* Based on batterymonitor with PISEN and Samsung SIII battery. */
+
+
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3600
+};
+
+
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3500
+};
+
+
+/*
+ Averages at percent of running time from five measuremnts with PISEN and Samsung SIII battery
+ during normal usage.
+
+ Mongo default values (?)
+ < 3660 (0%), < 3730 (1% - 10%), < 3780 (11% - 20%), < 3830 (21% - 40%), < 3950 (41% - 60%),
+ < 4080 (61% - 80%), > 4081 (81% - 100%)
+*/
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ { 3522, 3660, 3720, 3752, 3784, 3827, 3896, 3978, 4072, 4168, 4255 }
+};
+
+
+/* Copied from percent_to_volt_discharge. */
+const unsigned short percent_to_volt_charge[11] =
+{
+ 3500, 3544, 3578, 3623, 3660, 3773, 3782, 3853, 3980, 4130, 4360
+};
+
+
+static int _battery_present = -1;
+
+
+int _battery_voltage(void)
+{
+ /*TRACE;*/
+
+ if( (_battery_present == -1)
+ && (! sysfs_get_int(SYSFS_BATTERY_PRESENT, &_battery_present)))
+ {
+ /* This check is only done once at startup. */
+
+ DEBUGF("ERROR %s: Can not get current battery availabilty.", __func__);
+ _battery_present = 1;
+ }
+
+ int val;
+
+ if(_battery_present == 1)
+ {
+ /* Battery is present. */
+
+ /*
+ /sys/class/power_supply/battery/voltage_now
+ Voltage in microvolt.
+ */
+ if(! sysfs_get_int(SYSFS_BATTERY_VOLTAGE_NOW, &val))
+ {
+ DEBUGF("ERROR %s: Can not get current battery voltage.", __func__);
+ return 0;
+ }
+ }
+ else
+ {
+ /*
+ No battery, so we have to be running solely from USB power.
+ This will prevent Rockbox from forcing shutdown due to low power.
+ */
+
+ /*
+ /sys/class/power_supply/usb/voltage_now
+ Voltage in microvolt.
+ */
+ if(! sysfs_get_int(SYSFS_USB_POWER_VOLTAGE_NOW, &val))
+ {
+ DEBUGF("ERROR %s: Can not get current USB voltage.", __func__);
+ return 0;
+ }
+ }
+
+ return(val / 1000);
+}
diff --git a/firmware/target/hosted/ibasso/sysfs-ibasso.c b/firmware/target/hosted/ibasso/sysfs-ibasso.c
new file mode 100644
index 0000000000..8ca3edf387
--- /dev/null
+++ b/firmware/target/hosted/ibasso/sysfs-ibasso.c
@@ -0,0 +1,404 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "debug.h"
+
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+
+
+static const char* SYSFS_PATHS[] =
+{
+ /* SYSFS_DX50_CODEC_VOLUME */
+ "/dev/codec_volume",
+
+ /* SYSFS_HOLDKEY */
+ "/sys/class/axppower/holdkey",
+
+ /* SYSFS_DX90_ES9018_VOLUME */
+ "/sys/class/codec/es9018_volume",
+
+ /* SYSFS_MUTE */
+ "/sys/class/codec/mute",
+
+ /* SYSFS_WM8740_MUTE */
+ "/sys/class/codec/wm8740_mute",
+
+ /* SYSFS_BATTERY_CAPACITY */
+ "/sys/class/power_supply/battery/capacity",
+
+ /* SYSFS_BATTERY_CURRENT_NOW */
+ "/sys/class/power_supply/battery/current_now",
+
+ /* SYSFS_BATTERY_ENERGY_FULL_DESIGN */
+ "/sys/class/power_supply/battery/energy_full_design",
+
+ /* SYSFS_BATTERY_HEALTH */
+ "/sys/class/power_supply/battery/health",
+
+ /* SYSFS_BATTERY_MODEL_NAME */
+ "/sys/class/power_supply/battery/model_name",
+
+ /* SYSFS_BATTERY_ONLINE */
+ "/sys/class/power_supply/battery/online",
+
+ /* SYSFS_BATTERY_PRESENT */
+ "/sys/class/power_supply/battery/present",
+
+ /* SYSFS_BATTERY_STATUS */
+ "/sys/class/power_supply/battery/status",
+
+ /* SYSFS_BATTERY_TECHNOLOGY */
+ "/sys/class/power_supply/battery/technology",
+
+ /* SYSFS_BATTERY_TEMP */
+ "/sys/class/power_supply/battery/temp",
+
+ /* SYSFS_BATTERY_TYPE */
+ "/sys/class/power_supply/battery/type",
+
+ /* SYSFS_BATTERY_VOLTAGE_MAX_DESIGN */
+ "/sys/class/power_supply/battery/voltage_max_design",
+
+ /* SYSFS_BATTERY_VOLTAGE_MIN_DESIGN */
+ "/sys/class/power_supply/battery/voltage_min_design",
+
+ /* SYSFS_BATTERY_VOLTAGE_NOW */
+ "/sys/class/power_supply/battery/voltage_now",
+
+ /* SYSFS_USB_POWER_CURRENT_NOW */
+ "/sys/class/power_supply/usb/current_now",
+
+ /* SYSFS_USB_POWER_ONLINE */
+ "/sys/class/power_supply/usb/online",
+
+ /* SYSFS_USB_POWER_PRESENT */
+ "/sys/class/power_supply/usb/present",
+
+ /* SYSFS_USB_POWER_VOLTAGE_NOW */
+ "/sys/class/power_supply/usb/voltage_now",
+
+ /* SYSFS_BACKLIGHT_POWER */
+ "/sys/devices/platform/rk29_backlight/backlight/rk28_bl/bl_power",
+
+ /* SYSFS_BACKLIGHT_BRIGHTNESS */
+ "/sys/devices/platform/rk29_backlight/backlight/rk28_bl/brightness",
+
+ /* SYSFS_POWER_STATE */
+ "/sys/power/state",
+
+ /* SYSFS_POWER_WAKE_LOCK */
+ "/sys/power/wake_lock"
+};
+
+
+static FILE* open_read(const char* file_name)
+{
+ FILE *f = fopen(file_name, "r");
+ if(f == NULL)
+ {
+ DEBUGF("ERROR %s: Can not open %s for reading.", __func__, file_name);
+ }
+
+ return f;
+}
+
+
+static FILE* open_write(const char* file_name)
+{
+ FILE *f = fopen(file_name, "w");
+ if(f == NULL)
+ {
+ DEBUGF("ERROR %s: Can not open %s for writing.", __func__, file_name);
+ }
+
+ return f;
+}
+
+
+bool sysfs_get_int(enum sys_fs_interface_id id, int* value)
+{
+ *value = -1;
+
+ switch(id)
+ {
+ case SYSFS_BATTERY_CAPACITY:
+ case SYSFS_BATTERY_CURRENT_NOW:
+ case SYSFS_BATTERY_ENERGY_FULL_DESIGN:
+ case SYSFS_BATTERY_ONLINE:
+ case SYSFS_BATTERY_PRESENT:
+ case SYSFS_BATTERY_TEMP:
+ case SYSFS_BATTERY_VOLTAGE_MAX_DESIGN:
+ case SYSFS_BATTERY_VOLTAGE_MIN_DESIGN:
+ case SYSFS_BATTERY_VOLTAGE_NOW:
+ case SYSFS_USB_POWER_CURRENT_NOW:
+ case SYSFS_USB_POWER_VOLTAGE_NOW:
+ case SYSFS_USB_POWER_ONLINE:
+ case SYSFS_USB_POWER_PRESENT:
+ {
+ break;
+ }
+
+ default:
+ {
+ DEBUGF("ERROR %s: Unknown interface id: %d.", __func__, id);
+ return false;
+ }
+ }
+
+ const char* interface = SYSFS_PATHS[id];
+
+ /*DEBUGF("%s: interface: %s.", __func__, interface);*/
+
+ FILE *f = open_read(interface);
+ if(f == NULL)
+ {
+ return false;
+ }
+
+ bool success = true;
+ if(fscanf(f, "%d", value) == EOF)
+ {
+ DEBUGF("ERROR %s: Read failed for %s.", __func__, interface);
+ success = false;
+ }
+
+ fclose(f);
+ return success;
+}
+
+
+bool sysfs_set_int(enum sys_fs_interface_id id, int value)
+{
+ switch(id)
+ {
+ case SYSFS_BACKLIGHT_POWER:
+ case SYSFS_BACKLIGHT_BRIGHTNESS:
+ case SYSFS_DX50_CODEC_VOLUME:
+ case SYSFS_DX90_ES9018_VOLUME:
+ {
+ break;
+ }
+
+ default:
+ {
+ DEBUGF("ERROR %s: Unknown interface id: %d.", __func__, id);
+ return false;
+ }
+ }
+
+ const char* interface = SYSFS_PATHS[id];
+
+ /*DEBUGF("%s: interface: %s, value: %d.", __func__, interface, value);*/
+
+ FILE *f = open_write(interface);
+ if(f == NULL)
+ {
+ return false;
+ }
+
+ bool success = true;
+ if(fprintf(f, "%d", value) < 1)
+ {
+ DEBUGF("ERROR %s: Write failed for %s.", __func__, interface);
+ success = false;
+ }
+
+ fclose(f);
+ return success;
+}
+
+
+bool sysfs_get_char(enum sys_fs_interface_id id, char* value)
+{
+ *value = '\0';
+
+ switch(id)
+ {
+ case SYSFS_HOLDKEY:
+ {
+ break;
+ }
+
+ default:
+ {
+ DEBUGF("ERROR %s: Unknown interface id: %d.", __func__, id);
+ return false;
+ }
+ }
+
+ const char* interface = SYSFS_PATHS[id];
+
+ /*DEBUGF("%s: interface: %s.", __func__, interface);*/
+
+ FILE *f = open_read(interface);
+ if(f == NULL)
+ {
+ return false;
+ }
+
+ bool success = true;
+ if(fscanf(f, "%c", value) == EOF)
+ {
+ DEBUGF("ERROR %s: Read failed for %s.", __func__, interface);
+ success = false;
+ }
+
+ fclose(f);
+ return success;
+}
+
+
+bool sysfs_set_char(enum sys_fs_interface_id id, char value)
+{
+ switch(id)
+ {
+ case SYSFS_MUTE:
+ case SYSFS_WM8740_MUTE:
+ {
+ break;
+ }
+
+ default:
+ {
+ DEBUGF("ERROR %s: Unknown interface id: %d.", __func__, id);
+ return false;
+ }
+ }
+
+ const char* interface = SYSFS_PATHS[id];
+
+ /*DEBUGF("%s: interface: %s, value: %c.", __func__, interface, value);*/
+
+ FILE *f = open_write(interface);
+ if(f == NULL)
+ {
+ return false;
+ }
+
+ bool success = true;
+ if(fprintf(f, "%c", value) < 1)
+ {
+ DEBUGF("ERROR %s: Write failed for %s.", __func__, interface);
+ success = false;
+ }
+
+ fclose(f);
+ return success;
+}
+
+
+bool sysfs_get_string(enum sys_fs_interface_id id, char* value, int size)
+{
+ value[0] = '\0';
+
+ switch(id)
+ {
+ case SYSFS_BATTERY_STATUS:
+ case SYSFS_BATTERY_HEALTH:
+ case SYSFS_BATTERY_MODEL_NAME:
+ case SYSFS_BATTERY_TECHNOLOGY:
+ case SYSFS_BATTERY_TYPE:
+ {
+ break;
+ }
+
+ default:
+ {
+ DEBUGF("ERROR %s: Unknown interface id: %d.", __func__, id);
+ return false;
+ }
+ }
+
+ const char* interface = SYSFS_PATHS[id];
+
+ /*DEBUGF("%s: interface: %s, size: %d.", __func__, interface, size);*/
+
+ FILE *f = open_read(interface);
+ if(f == NULL)
+ {
+ return false;
+ }
+
+ bool success = true;
+ if(fgets(value, size, f) == NULL)
+ {
+ DEBUGF("ERROR %s: Read failed for %s.", __func__, interface);
+ success = false;
+ }
+ else
+ {
+ size_t length = strlen(value);
+ if((length > 0) && value[length - 1] == '\n')
+ {
+ value[length - 1] = '\0';
+ }
+ }
+
+ fclose(f);
+ return success;
+}
+
+
+bool sysfs_set_string(enum sys_fs_interface_id id, char* value)
+{
+ switch(id)
+ {
+ case SYSFS_POWER_STATE:
+ case SYSFS_POWER_WAKE_LOCK:
+ {
+ break;
+ }
+
+ default:
+ {
+ DEBUGF("ERROR %s: Unknown interface id: %d.", __func__, id);
+ return false;
+ }
+ }
+
+ const char* interface = SYSFS_PATHS[id];
+
+ /*DEBUGF("%s: interface: %s, value: %s.", __func__, interface, value);*/
+
+ FILE *f = open_write(interface);
+ if(f == NULL)
+ {
+ return false;
+ }
+
+ bool success = true;
+ if(fprintf(f, "%s", value) < 1)
+ {
+ DEBUGF("ERROR %s: Write failed for %s.", __func__, interface);
+ success = false;
+ }
+
+ fclose(f);
+ return success;
+}
diff --git a/firmware/target/hosted/ibasso/sysfs-ibasso.h b/firmware/target/hosted/ibasso/sysfs-ibasso.h
new file mode 100644
index 0000000000..fec8a082f9
--- /dev/null
+++ b/firmware/target/hosted/ibasso/sysfs-ibasso.h
@@ -0,0 +1,111 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _SYSFS_IBASSO_H_
+#define _SYSFS_IBASSO_H_
+
+
+#include <stdbool.h>
+
+
+/*
+ Sys FS path identifiers.
+ See SYSFS_PATHS in sysfs-ibasso.c.
+*/
+enum sys_fs_interface_id
+{
+ SYSFS_DX50_CODEC_VOLUME = 0,
+ SYSFS_HOLDKEY,
+ SYSFS_DX90_ES9018_VOLUME,
+ SYSFS_MUTE,
+ SYSFS_WM8740_MUTE,
+ SYSFS_BATTERY_CAPACITY,
+ SYSFS_BATTERY_CURRENT_NOW,
+ SYSFS_BATTERY_ENERGY_FULL_DESIGN,
+ SYSFS_BATTERY_HEALTH,
+ SYSFS_BATTERY_MODEL_NAME,
+ SYSFS_BATTERY_ONLINE,
+ SYSFS_BATTERY_PRESENT,
+ SYSFS_BATTERY_STATUS,
+ SYSFS_BATTERY_TECHNOLOGY,
+ SYSFS_BATTERY_TEMP,
+ SYSFS_BATTERY_TYPE,
+ SYSFS_BATTERY_VOLTAGE_MAX_DESIGN,
+ SYSFS_BATTERY_VOLTAGE_MIN_DESIGN,
+ SYSFS_BATTERY_VOLTAGE_NOW,
+ SYSFS_USB_POWER_CURRENT_NOW,
+ SYSFS_USB_POWER_ONLINE,
+ SYSFS_USB_POWER_PRESENT,
+ SYSFS_USB_POWER_VOLTAGE_NOW,
+ SYSFS_BACKLIGHT_POWER,
+ SYSFS_BACKLIGHT_BRIGHTNESS,
+ SYSFS_POWER_STATE,
+ SYSFS_POWER_WAKE_LOCK
+};
+
+
+/*
+ Read a integer value from the sys fs interface given by id.
+ Returns true on success, false else.
+*/
+bool sysfs_get_int(enum sys_fs_interface_id id, int* value);
+
+
+/*
+ Write a integer value to the sys fs interface given by id.
+ Returns true on success, false else.
+*/
+bool sysfs_set_int(enum sys_fs_interface_id id, int value);
+
+
+/*
+ Read a char value from the sys fs interface given by id.
+ Returns true on success, false else.
+*/
+bool sysfs_get_char(enum sys_fs_interface_id id, char* value);
+
+
+/*
+ Write a char value to the sys fs interface given by id.
+ Returns true on success, false else.
+*/
+bool sysfs_set_char(enum sys_fs_interface_id id, char value);
+
+/*
+ Read a single line of text from the sys fs interface given by id.
+ A newline will be discarded.
+ size: The size of value.
+ Returns true on success, false else.
+*/
+bool sysfs_get_string(enum sys_fs_interface_id id, char* value, int size);
+
+
+/*
+ Write text to the sys fs interface given by id.
+ Returns true on success, false else.
+*/
+bool sysfs_set_string(enum sys_fs_interface_id id, char* value);
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/system-ibasso.c b/firmware/target/hosted/ibasso/system-ibasso.c
new file mode 100644
index 0000000000..00f8669ae0
--- /dev/null
+++ b/firmware/target/hosted/ibasso/system-ibasso.c
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/reboot.h>
+
+#include "config.h"
+#include "cpufreq-linux.h"
+#include "debug.h"
+
+#include "button-ibasso.h"
+#include "debug-ibasso.h"
+#include "sysfs-ibasso.h"
+#include "usb-ibasso.h"
+#include "vold-ibasso.h"
+
+
+/* Fake stack. */
+uintptr_t* stackbegin;
+uintptr_t* stackend;
+
+
+void system_init(void)
+{
+ TRACE;
+
+ /* Fake stack. */
+ volatile uintptr_t stack = 0;
+ stackbegin = stackend = (uintptr_t*) &stack;
+
+ cpufreq_set_governor("powersave", CPUFREQ_ALL_CPUS);
+ vold_monitor_start();
+ ibasso_set_usb_mode(USB_MODE_MASS_STORAGE);
+
+ /*
+ Prevent device from deep sleeping, which will interrupt playback.
+ /sys/power/wake_lock
+ */
+ if(! sysfs_set_string(SYSFS_POWER_WAKE_LOCK, "rockbox"))
+ {
+ DEBUGF("ERROR %s: Can not set suspend blocker.", __func__);
+ }
+
+ /*
+ Prevent device to mute, which will cause tinyalsa pcm_writes to fail.
+ /sys/class/codec/wm8740_mute
+ */
+ if(! sysfs_set_char(SYSFS_WM8740_MUTE, '0'))
+ {
+ DEBUGF("ERROR %s: Can not set WM8740 lock.", __func__);
+ }
+}
+
+
+void system_reboot(void)
+{
+ TRACE;
+
+ button_close_device();
+
+ if(vold_monitor_forced_close_imminent())
+ {
+ /*
+ We are here, because Android Vold is going to kill Rockbox. Instead of powering off,
+ we exit into the loader.
+ */
+ exit(42);
+ }
+
+ reboot(RB_AUTOBOOT);
+}
+
+
+void system_exception_wait(void)
+{
+ TRACE;
+
+ while(1) {};
+}
diff --git a/firmware/target/hosted/ibasso/system-target.h b/firmware/target/hosted/ibasso/system-target.h
new file mode 100644
index 0000000000..17b1238380
--- /dev/null
+++ b/firmware/target/hosted/ibasso/system-target.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef __SYSTEM_TARGET_H__
+#define __SYSTEM_TARGET_H__
+
+
+#include "kernel-unix.h"
+#include "system-hosted.h"
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/tinyalsa/include/sound/asound.h b/firmware/target/hosted/ibasso/tinyalsa/include/sound/asound.h
new file mode 100644
index 0000000000..9dd66fe169
--- /dev/null
+++ b/firmware/target/hosted/ibasso/tinyalsa/include/sound/asound.h
@@ -0,0 +1,820 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __SOUND_ASOUND_H
+#define __SOUND_ASOUND_H
+
+#include <linux/types.h>
+
+#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor))
+#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff)
+#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff)
+#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff)
+#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) (SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion)))
+
+struct snd_aes_iec958 {
+ unsigned char status[24];
+ unsigned char subcode[147];
+ unsigned char pad;
+ unsigned char dig_subframe[4];
+};
+
+#define SNDRV_HWDEP_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 1)
+
+enum {
+ SNDRV_HWDEP_IFACE_OPL2 = 0,
+ SNDRV_HWDEP_IFACE_OPL3,
+ SNDRV_HWDEP_IFACE_OPL4,
+ SNDRV_HWDEP_IFACE_SB16CSP,
+ SNDRV_HWDEP_IFACE_EMU10K1,
+ SNDRV_HWDEP_IFACE_YSS225,
+ SNDRV_HWDEP_IFACE_ICS2115,
+ SNDRV_HWDEP_IFACE_SSCAPE,
+ SNDRV_HWDEP_IFACE_VX,
+ SNDRV_HWDEP_IFACE_MIXART,
+ SNDRV_HWDEP_IFACE_USX2Y,
+ SNDRV_HWDEP_IFACE_EMUX_WAVETABLE,
+ SNDRV_HWDEP_IFACE_BLUETOOTH,
+ SNDRV_HWDEP_IFACE_USX2Y_PCM,
+ SNDRV_HWDEP_IFACE_PCXHR,
+ SNDRV_HWDEP_IFACE_SB_RC,
+ SNDRV_HWDEP_IFACE_HDA,
+ SNDRV_HWDEP_IFACE_USB_STREAM,
+
+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
+};
+
+struct snd_hwdep_info {
+ unsigned int device;
+ int card;
+ unsigned char id[64];
+ unsigned char name[80];
+ int iface;
+ unsigned char reserved[64];
+};
+
+struct snd_hwdep_dsp_status {
+ unsigned int version;
+ unsigned char id[32];
+ unsigned int num_dsps;
+ unsigned int dsp_loaded;
+ unsigned int chip_ready;
+ unsigned char reserved[16];
+};
+
+struct snd_hwdep_dsp_image {
+ unsigned int index;
+ unsigned char name[64];
+ unsigned char __user *image;
+ size_t length;
+ unsigned long driver_data;
+};
+
+#define SNDRV_HWDEP_IOCTL_PVERSION _IOR ('H', 0x00, int)
+#define SNDRV_HWDEP_IOCTL_INFO _IOR ('H', 0x01, struct snd_hwdep_info)
+#define SNDRV_HWDEP_IOCTL_DSP_STATUS _IOR('H', 0x02, struct snd_hwdep_dsp_status)
+#define SNDRV_HWDEP_IOCTL_DSP_LOAD _IOW('H', 0x03, struct snd_hwdep_dsp_image)
+
+#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10)
+
+typedef unsigned long snd_pcm_uframes_t;
+typedef signed long snd_pcm_sframes_t;
+
+enum {
+ SNDRV_PCM_CLASS_GENERIC = 0,
+ SNDRV_PCM_CLASS_MULTI,
+ SNDRV_PCM_CLASS_MODEM,
+ SNDRV_PCM_CLASS_DIGITIZER,
+
+ SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER,
+};
+
+enum {
+ SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0,
+ SNDRV_PCM_SUBCLASS_MULTI_MIX,
+
+ SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX,
+};
+
+enum {
+ SNDRV_PCM_STREAM_PLAYBACK = 0,
+ SNDRV_PCM_STREAM_CAPTURE,
+ SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE,
+};
+
+typedef int __bitwise snd_pcm_access_t;
+#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ((__force snd_pcm_access_t) 0)
+#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED ((__force snd_pcm_access_t) 1)
+#define SNDRV_PCM_ACCESS_MMAP_COMPLEX ((__force snd_pcm_access_t) 2)
+#define SNDRV_PCM_ACCESS_RW_INTERLEAVED ((__force snd_pcm_access_t) 3)
+#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ((__force snd_pcm_access_t) 4)
+#define SNDRV_PCM_ACCESS_LAST SNDRV_PCM_ACCESS_RW_NONINTERLEAVED
+
+typedef int __bitwise snd_pcm_format_t;
+#define SNDRV_PCM_FORMAT_S8 ((__force snd_pcm_format_t) 0)
+#define SNDRV_PCM_FORMAT_U8 ((__force snd_pcm_format_t) 1)
+#define SNDRV_PCM_FORMAT_S16_LE ((__force snd_pcm_format_t) 2)
+#define SNDRV_PCM_FORMAT_S16_BE ((__force snd_pcm_format_t) 3)
+#define SNDRV_PCM_FORMAT_U16_LE ((__force snd_pcm_format_t) 4)
+#define SNDRV_PCM_FORMAT_U16_BE ((__force snd_pcm_format_t) 5)
+#define SNDRV_PCM_FORMAT_S24_LE ((__force snd_pcm_format_t) 6)
+#define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7)
+#define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8)
+#define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9)
+#define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10)
+#define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11)
+#define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12)
+#define SNDRV_PCM_FORMAT_U32_BE ((__force snd_pcm_format_t) 13)
+#define SNDRV_PCM_FORMAT_FLOAT_LE ((__force snd_pcm_format_t) 14)
+#define SNDRV_PCM_FORMAT_FLOAT_BE ((__force snd_pcm_format_t) 15)
+#define SNDRV_PCM_FORMAT_FLOAT64_LE ((__force snd_pcm_format_t) 16)
+#define SNDRV_PCM_FORMAT_FLOAT64_BE ((__force snd_pcm_format_t) 17)
+#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((__force snd_pcm_format_t) 18)
+#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((__force snd_pcm_format_t) 19)
+#define SNDRV_PCM_FORMAT_MU_LAW ((__force snd_pcm_format_t) 20)
+#define SNDRV_PCM_FORMAT_A_LAW ((__force snd_pcm_format_t) 21)
+#define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22)
+#define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23)
+#define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24)
+#define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31)
+#define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32)
+#define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33)
+#define SNDRV_PCM_FORMAT_U24_3LE ((__force snd_pcm_format_t) 34)
+#define SNDRV_PCM_FORMAT_U24_3BE ((__force snd_pcm_format_t) 35)
+#define SNDRV_PCM_FORMAT_S20_3LE ((__force snd_pcm_format_t) 36)
+#define SNDRV_PCM_FORMAT_S20_3BE ((__force snd_pcm_format_t) 37)
+#define SNDRV_PCM_FORMAT_U20_3LE ((__force snd_pcm_format_t) 38)
+#define SNDRV_PCM_FORMAT_U20_3BE ((__force snd_pcm_format_t) 39)
+#define SNDRV_PCM_FORMAT_S18_3LE ((__force snd_pcm_format_t) 40)
+#define SNDRV_PCM_FORMAT_S18_3BE ((__force snd_pcm_format_t) 41)
+#define SNDRV_PCM_FORMAT_U18_3LE ((__force snd_pcm_format_t) 42)
+#define SNDRV_PCM_FORMAT_U18_3BE ((__force snd_pcm_format_t) 43)
+#define SNDRV_PCM_FORMAT_G723_24 ((__force snd_pcm_format_t) 44)
+#define SNDRV_PCM_FORMAT_G723_24_1B ((__force snd_pcm_format_t) 45)
+#define SNDRV_PCM_FORMAT_G723_40 ((__force snd_pcm_format_t) 46)
+#define SNDRV_PCM_FORMAT_G723_40_1B ((__force snd_pcm_format_t) 47)
+#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_G723_40_1B
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE
+#define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_LE
+#define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_LE
+#define SNDRV_PCM_FORMAT_U24 SNDRV_PCM_FORMAT_U24_LE
+#define SNDRV_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32_LE
+#define SNDRV_PCM_FORMAT_U32 SNDRV_PCM_FORMAT_U32_LE
+#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_LE
+#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_LE
+#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
+#endif
+#ifdef SNDRV_BIG_ENDIAN
+#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE
+#define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_BE
+#define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_BE
+#define SNDRV_PCM_FORMAT_U24 SNDRV_PCM_FORMAT_U24_BE
+#define SNDRV_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32_BE
+#define SNDRV_PCM_FORMAT_U32 SNDRV_PCM_FORMAT_U32_BE
+#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_BE
+#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_BE
+#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
+#endif
+
+typedef int __bitwise snd_pcm_subformat_t;
+#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0)
+#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD
+
+#define SNDRV_PCM_INFO_MMAP 0x00000001
+#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002
+#define SNDRV_PCM_INFO_DOUBLE 0x00000004
+#define SNDRV_PCM_INFO_BATCH 0x00000010
+#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100
+#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200
+#define SNDRV_PCM_INFO_COMPLEX 0x00000400
+#define SNDRV_PCM_INFO_BLOCK_TRANSFER 0x00010000
+#define SNDRV_PCM_INFO_OVERRANGE 0x00020000
+#define SNDRV_PCM_INFO_RESUME 0x00040000
+#define SNDRV_PCM_INFO_PAUSE 0x00080000
+#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000
+#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000
+#define SNDRV_PCM_INFO_SYNC_START 0x00400000
+#define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000
+#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000
+
+typedef int __bitwise snd_pcm_state_t;
+#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0)
+#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1)
+#define SNDRV_PCM_STATE_PREPARED ((__force snd_pcm_state_t) 2)
+#define SNDRV_PCM_STATE_RUNNING ((__force snd_pcm_state_t) 3)
+#define SNDRV_PCM_STATE_XRUN ((__force snd_pcm_state_t) 4)
+#define SNDRV_PCM_STATE_DRAINING ((__force snd_pcm_state_t) 5)
+#define SNDRV_PCM_STATE_PAUSED ((__force snd_pcm_state_t) 6)
+#define SNDRV_PCM_STATE_SUSPENDED ((__force snd_pcm_state_t) 7)
+#define SNDRV_PCM_STATE_DISCONNECTED ((__force snd_pcm_state_t) 8)
+#define SNDRV_PCM_STATE_LAST SNDRV_PCM_STATE_DISCONNECTED
+
+enum {
+ SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000,
+ SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000,
+ SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000,
+};
+
+union snd_pcm_sync_id {
+ unsigned char id[16];
+ unsigned short id16[8];
+ unsigned int id32[4];
+};
+
+struct snd_pcm_info {
+ unsigned int device;
+ unsigned int subdevice;
+ int stream;
+ int card;
+ unsigned char id[64];
+ unsigned char name[80];
+ unsigned char subname[32];
+ int dev_class;
+ int dev_subclass;
+ unsigned int subdevices_count;
+ unsigned int subdevices_avail;
+ union snd_pcm_sync_id sync;
+ unsigned char reserved[64];
+};
+
+typedef int snd_pcm_hw_param_t;
+#define SNDRV_PCM_HW_PARAM_ACCESS 0
+#define SNDRV_PCM_HW_PARAM_FORMAT 1
+#define SNDRV_PCM_HW_PARAM_SUBFORMAT 2
+#define SNDRV_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_ACCESS
+#define SNDRV_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_SUBFORMAT
+
+#define SNDRV_PCM_HW_PARAM_SAMPLE_BITS 8
+#define SNDRV_PCM_HW_PARAM_FRAME_BITS 9
+#define SNDRV_PCM_HW_PARAM_CHANNELS 10
+#define SNDRV_PCM_HW_PARAM_RATE 11
+#define SNDRV_PCM_HW_PARAM_PERIOD_TIME 12
+#define SNDRV_PCM_HW_PARAM_PERIOD_SIZE 13
+#define SNDRV_PCM_HW_PARAM_PERIOD_BYTES 14
+#define SNDRV_PCM_HW_PARAM_PERIODS 15
+#define SNDRV_PCM_HW_PARAM_BUFFER_TIME 16
+#define SNDRV_PCM_HW_PARAM_BUFFER_SIZE 17
+#define SNDRV_PCM_HW_PARAM_BUFFER_BYTES 18
+#define SNDRV_PCM_HW_PARAM_TICK_TIME 19
+#define SNDRV_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_SAMPLE_BITS
+#define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME
+
+#define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0)
+#define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1)
+#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
+
+struct snd_interval {
+ unsigned int min, max;
+ unsigned int openmin:1,
+ openmax:1,
+ integer:1,
+ empty:1;
+};
+
+#define SNDRV_MASK_MAX 256
+
+struct snd_mask {
+ __u32 bits[(SNDRV_MASK_MAX+31)/32];
+};
+
+struct snd_pcm_hw_params {
+ unsigned int flags;
+ struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK + 1];
+ struct snd_mask mres[5];
+ struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL -
+ SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+ struct snd_interval ires[9];
+ unsigned int rmask;
+ unsigned int cmask;
+ unsigned int info;
+ unsigned int msbits;
+ unsigned int rate_num;
+ unsigned int rate_den;
+ snd_pcm_uframes_t fifo_size;
+ unsigned char reserved[64];
+};
+
+enum {
+ SNDRV_PCM_TSTAMP_NONE = 0,
+ SNDRV_PCM_TSTAMP_ENABLE,
+ SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_ENABLE,
+};
+
+struct snd_pcm_sw_params {
+ int tstamp_mode;
+ unsigned int period_step;
+ unsigned int sleep_min;
+ snd_pcm_uframes_t avail_min;
+ snd_pcm_uframes_t xfer_align;
+ snd_pcm_uframes_t start_threshold;
+ snd_pcm_uframes_t stop_threshold;
+ snd_pcm_uframes_t silence_threshold;
+ snd_pcm_uframes_t silence_size;
+ snd_pcm_uframes_t boundary;
+ unsigned char reserved[64];
+};
+
+struct snd_pcm_channel_info {
+ unsigned int channel;
+ __kernel_off_t offset;
+ unsigned int first;
+ unsigned int step;
+};
+
+struct snd_pcm_status {
+ snd_pcm_state_t state;
+ struct timespec trigger_tstamp;
+ struct timespec tstamp;
+ snd_pcm_uframes_t appl_ptr;
+ snd_pcm_uframes_t hw_ptr;
+ snd_pcm_sframes_t delay;
+ snd_pcm_uframes_t avail;
+ snd_pcm_uframes_t avail_max;
+ snd_pcm_uframes_t overrange;
+ snd_pcm_state_t suspended_state;
+ unsigned char reserved[60];
+};
+
+struct snd_pcm_mmap_status {
+ snd_pcm_state_t state;
+ int pad1;
+ snd_pcm_uframes_t hw_ptr;
+ struct timespec tstamp;
+ snd_pcm_state_t suspended_state;
+};
+
+struct snd_pcm_mmap_control {
+ snd_pcm_uframes_t appl_ptr;
+ snd_pcm_uframes_t avail_min;
+};
+
+#define SNDRV_PCM_SYNC_PTR_HWSYNC (1<<0)
+#define SNDRV_PCM_SYNC_PTR_APPL (1<<1)
+#define SNDRV_PCM_SYNC_PTR_AVAIL_MIN (1<<2)
+
+struct snd_pcm_sync_ptr {
+ unsigned int flags;
+ union {
+ struct snd_pcm_mmap_status status;
+ unsigned char reserved[64];
+ } s;
+ union {
+ struct snd_pcm_mmap_control control;
+ unsigned char reserved[64];
+ } c;
+};
+
+struct snd_xferi {
+ snd_pcm_sframes_t result;
+ void __user *buf;
+ snd_pcm_uframes_t frames;
+};
+
+struct snd_xfern {
+ snd_pcm_sframes_t result;
+ void __user * __user *bufs;
+ snd_pcm_uframes_t frames;
+};
+
+enum {
+ SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0,
+ SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
+ SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
+};
+
+#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int)
+#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info)
+#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int)
+#define SNDRV_PCM_IOCTL_TTSTAMP _IOW('A', 0x03, int)
+#define SNDRV_PCM_IOCTL_HW_REFINE _IOWR('A', 0x10, struct snd_pcm_hw_params)
+#define SNDRV_PCM_IOCTL_HW_PARAMS _IOWR('A', 0x11, struct snd_pcm_hw_params)
+#define SNDRV_PCM_IOCTL_HW_FREE _IO('A', 0x12)
+#define SNDRV_PCM_IOCTL_SW_PARAMS _IOWR('A', 0x13, struct snd_pcm_sw_params)
+#define SNDRV_PCM_IOCTL_STATUS _IOR('A', 0x20, struct snd_pcm_status)
+#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t)
+#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22)
+#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr)
+#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info)
+#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40)
+#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41)
+#define SNDRV_PCM_IOCTL_START _IO('A', 0x42)
+#define SNDRV_PCM_IOCTL_DROP _IO('A', 0x43)
+#define SNDRV_PCM_IOCTL_DRAIN _IO('A', 0x44)
+#define SNDRV_PCM_IOCTL_PAUSE _IOW('A', 0x45, int)
+#define SNDRV_PCM_IOCTL_REWIND _IOW('A', 0x46, snd_pcm_uframes_t)
+#define SNDRV_PCM_IOCTL_RESUME _IO('A', 0x47)
+#define SNDRV_PCM_IOCTL_XRUN _IO('A', 0x48)
+#define SNDRV_PCM_IOCTL_FORWARD _IOW('A', 0x49, snd_pcm_uframes_t)
+#define SNDRV_PCM_IOCTL_WRITEI_FRAMES _IOW('A', 0x50, struct snd_xferi)
+#define SNDRV_PCM_IOCTL_READI_FRAMES _IOR('A', 0x51, struct snd_xferi)
+#define SNDRV_PCM_IOCTL_WRITEN_FRAMES _IOW('A', 0x52, struct snd_xfern)
+#define SNDRV_PCM_IOCTL_READN_FRAMES _IOR('A', 0x53, struct snd_xfern)
+#define SNDRV_PCM_IOCTL_LINK _IOW('A', 0x60, int)
+#define SNDRV_PCM_IOCTL_UNLINK _IO('A', 0x61)
+
+#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0)
+
+enum {
+ SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT,
+};
+
+#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001
+#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002
+#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004
+
+struct snd_rawmidi_info {
+ unsigned int device;
+ unsigned int subdevice;
+ int stream;
+ int card;
+ unsigned int flags;
+ unsigned char id[64];
+ unsigned char name[80];
+ unsigned char subname[32];
+ unsigned int subdevices_count;
+ unsigned int subdevices_avail;
+ unsigned char reserved[64];
+};
+
+struct snd_rawmidi_params {
+ int stream;
+ size_t buffer_size;
+ size_t avail_min;
+ unsigned int no_active_sensing: 1;
+ unsigned char reserved[16];
+};
+
+struct snd_rawmidi_status {
+ int stream;
+ struct timespec tstamp;
+ size_t avail;
+ size_t xruns;
+ unsigned char reserved[16];
+};
+
+#define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int)
+#define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info)
+#define SNDRV_RAWMIDI_IOCTL_PARAMS _IOWR('W', 0x10, struct snd_rawmidi_params)
+#define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status)
+#define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int)
+#define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int)
+
+#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6)
+
+enum {
+ SNDRV_TIMER_CLASS_NONE = -1,
+ SNDRV_TIMER_CLASS_SLAVE = 0,
+ SNDRV_TIMER_CLASS_GLOBAL,
+ SNDRV_TIMER_CLASS_CARD,
+ SNDRV_TIMER_CLASS_PCM,
+ SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM,
+};
+
+enum {
+ SNDRV_TIMER_SCLASS_NONE = 0,
+ SNDRV_TIMER_SCLASS_APPLICATION,
+ SNDRV_TIMER_SCLASS_SEQUENCER,
+ SNDRV_TIMER_SCLASS_OSS_SEQUENCER,
+ SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER,
+};
+
+#define SNDRV_TIMER_GLOBAL_SYSTEM 0
+#define SNDRV_TIMER_GLOBAL_RTC 1
+#define SNDRV_TIMER_GLOBAL_HPET 2
+#define SNDRV_TIMER_GLOBAL_HRTIMER 3
+
+#define SNDRV_TIMER_FLG_SLAVE (1<<0)
+
+struct snd_timer_id {
+ int dev_class;
+ int dev_sclass;
+ int card;
+ int device;
+ int subdevice;
+};
+
+struct snd_timer_ginfo {
+ struct snd_timer_id tid;
+ unsigned int flags;
+ int card;
+ unsigned char id[64];
+ unsigned char name[80];
+ unsigned long reserved0;
+ unsigned long resolution;
+ unsigned long resolution_min;
+ unsigned long resolution_max;
+ unsigned int clients;
+ unsigned char reserved[32];
+};
+
+struct snd_timer_gparams {
+ struct snd_timer_id tid;
+ unsigned long period_num;
+ unsigned long period_den;
+ unsigned char reserved[32];
+};
+
+struct snd_timer_gstatus {
+ struct snd_timer_id tid;
+ unsigned long resolution;
+ unsigned long resolution_num;
+ unsigned long resolution_den;
+ unsigned char reserved[32];
+};
+
+struct snd_timer_select {
+ struct snd_timer_id id;
+ unsigned char reserved[32];
+};
+
+struct snd_timer_info {
+ unsigned int flags;
+ int card;
+ unsigned char id[64];
+ unsigned char name[80];
+ unsigned long reserved0;
+ unsigned long resolution;
+ unsigned char reserved[64];
+};
+
+#define SNDRV_TIMER_PSFLG_AUTO (1<<0)
+#define SNDRV_TIMER_PSFLG_EXCLUSIVE (1<<1)
+#define SNDRV_TIMER_PSFLG_EARLY_EVENT (1<<2)
+
+struct snd_timer_params {
+ unsigned int flags;
+ unsigned int ticks;
+ unsigned int queue_size;
+ unsigned int reserved0;
+ unsigned int filter;
+ unsigned char reserved[60];
+};
+
+struct snd_timer_status {
+ struct timespec tstamp;
+ unsigned int resolution;
+ unsigned int lost;
+ unsigned int overrun;
+ unsigned int queue;
+ unsigned char reserved[64];
+};
+
+#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int)
+#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id)
+#define SNDRV_TIMER_IOCTL_TREAD _IOW('T', 0x02, int)
+#define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo)
+#define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams)
+#define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus)
+#define SNDRV_TIMER_IOCTL_SELECT _IOW('T', 0x10, struct snd_timer_select)
+#define SNDRV_TIMER_IOCTL_INFO _IOR('T', 0x11, struct snd_timer_info)
+#define SNDRV_TIMER_IOCTL_PARAMS _IOW('T', 0x12, struct snd_timer_params)
+#define SNDRV_TIMER_IOCTL_STATUS _IOR('T', 0x14, struct snd_timer_status)
+
+#define SNDRV_TIMER_IOCTL_START _IO('T', 0xa0)
+#define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1)
+#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2)
+#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3)
+
+struct snd_timer_read {
+ unsigned int resolution;
+ unsigned int ticks;
+};
+
+enum {
+ SNDRV_TIMER_EVENT_RESOLUTION = 0,
+ SNDRV_TIMER_EVENT_TICK,
+ SNDRV_TIMER_EVENT_START,
+ SNDRV_TIMER_EVENT_STOP,
+ SNDRV_TIMER_EVENT_CONTINUE,
+ SNDRV_TIMER_EVENT_PAUSE,
+ SNDRV_TIMER_EVENT_EARLY,
+ SNDRV_TIMER_EVENT_SUSPEND,
+ SNDRV_TIMER_EVENT_RESUME,
+
+ SNDRV_TIMER_EVENT_MSTART = SNDRV_TIMER_EVENT_START + 10,
+ SNDRV_TIMER_EVENT_MSTOP = SNDRV_TIMER_EVENT_STOP + 10,
+ SNDRV_TIMER_EVENT_MCONTINUE = SNDRV_TIMER_EVENT_CONTINUE + 10,
+ SNDRV_TIMER_EVENT_MPAUSE = SNDRV_TIMER_EVENT_PAUSE + 10,
+ SNDRV_TIMER_EVENT_MSUSPEND = SNDRV_TIMER_EVENT_SUSPEND + 10,
+ SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10,
+};
+
+struct snd_timer_tread {
+ int event;
+ struct timespec tstamp;
+ unsigned int val;
+};
+
+#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6)
+
+struct snd_ctl_card_info {
+ int card;
+ int pad;
+ unsigned char id[16];
+ unsigned char driver[16];
+ unsigned char name[32];
+ unsigned char longname[80];
+ unsigned char reserved_[16];
+ unsigned char mixername[80];
+ unsigned char components[128];
+};
+
+typedef int __bitwise snd_ctl_elem_type_t;
+#define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0)
+#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1)
+#define SNDRV_CTL_ELEM_TYPE_INTEGER ((__force snd_ctl_elem_type_t) 2)
+#define SNDRV_CTL_ELEM_TYPE_ENUMERATED ((__force snd_ctl_elem_type_t) 3)
+#define SNDRV_CTL_ELEM_TYPE_BYTES ((__force snd_ctl_elem_type_t) 4)
+#define SNDRV_CTL_ELEM_TYPE_IEC958 ((__force snd_ctl_elem_type_t) 5)
+#define SNDRV_CTL_ELEM_TYPE_INTEGER64 ((__force snd_ctl_elem_type_t) 6)
+#define SNDRV_CTL_ELEM_TYPE_LAST SNDRV_CTL_ELEM_TYPE_INTEGER64
+
+typedef int __bitwise snd_ctl_elem_iface_t;
+#define SNDRV_CTL_ELEM_IFACE_CARD ((__force snd_ctl_elem_iface_t) 0)
+#define SNDRV_CTL_ELEM_IFACE_HWDEP ((__force snd_ctl_elem_iface_t) 1)
+#define SNDRV_CTL_ELEM_IFACE_MIXER ((__force snd_ctl_elem_iface_t) 2)
+#define SNDRV_CTL_ELEM_IFACE_PCM ((__force snd_ctl_elem_iface_t) 3)
+#define SNDRV_CTL_ELEM_IFACE_RAWMIDI ((__force snd_ctl_elem_iface_t) 4)
+#define SNDRV_CTL_ELEM_IFACE_TIMER ((__force snd_ctl_elem_iface_t) 5)
+#define SNDRV_CTL_ELEM_IFACE_SEQUENCER ((__force snd_ctl_elem_iface_t) 6)
+#define SNDRV_CTL_ELEM_IFACE_LAST SNDRV_CTL_ELEM_IFACE_SEQUENCER
+
+#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0)
+#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1)
+#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
+#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2)
+#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6)
+#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8)
+#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9)
+#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28)
+#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29)
+
+#define SNDRV_CTL_POWER_D0 0x0000
+#define SNDRV_CTL_POWER_D1 0x0100
+#define SNDRV_CTL_POWER_D2 0x0200
+#define SNDRV_CTL_POWER_D3 0x0300
+#define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000)
+#define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001)
+
+struct snd_ctl_elem_id {
+ unsigned int numid;
+ snd_ctl_elem_iface_t iface;
+ unsigned int device;
+ unsigned int subdevice;
+ unsigned char name[44];
+ unsigned int index;
+};
+
+struct snd_ctl_elem_list {
+ unsigned int offset;
+ unsigned int space;
+ unsigned int used;
+ unsigned int count;
+ struct snd_ctl_elem_id __user *pids;
+ unsigned char reserved[50];
+};
+
+struct snd_ctl_elem_info {
+ struct snd_ctl_elem_id id;
+ snd_ctl_elem_type_t type;
+ unsigned int access;
+ unsigned int count;
+ __kernel_pid_t owner;
+ union {
+ struct {
+ long min;
+ long max;
+ long step;
+ } integer;
+ struct {
+ long long min;
+ long long max;
+ long long step;
+ } integer64;
+ struct {
+ unsigned int items;
+ unsigned int item;
+ char name[64];
+ } enumerated;
+ unsigned char reserved[128];
+ } value;
+ union {
+ unsigned short d[4];
+ unsigned short *d_ptr;
+ } dimen;
+ unsigned char reserved[64-4*sizeof(unsigned short)];
+};
+
+struct snd_ctl_elem_value {
+ struct snd_ctl_elem_id id;
+ unsigned int indirect: 1;
+ union {
+ union {
+ long value[128];
+ long *value_ptr;
+ } integer;
+ union {
+ long long value[64];
+ long long *value_ptr;
+ } integer64;
+ union {
+ unsigned int item[128];
+ unsigned int *item_ptr;
+ } enumerated;
+ union {
+ unsigned char data[512];
+ unsigned char *data_ptr;
+ } bytes;
+ struct snd_aes_iec958 iec958;
+ } value;
+ struct timespec tstamp;
+ unsigned char reserved[128-sizeof(struct timespec)];
+};
+
+struct snd_ctl_tlv {
+ unsigned int numid;
+ unsigned int length;
+ unsigned int tlv[0];
+};
+
+#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)
+#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info)
+#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list)
+#define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info)
+#define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value)
+#define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value)
+#define SNDRV_CTL_IOCTL_ELEM_LOCK _IOW('U', 0x14, struct snd_ctl_elem_id)
+#define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id)
+#define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int)
+#define SNDRV_CTL_IOCTL_ELEM_ADD _IOWR('U', 0x17, struct snd_ctl_elem_info)
+#define SNDRV_CTL_IOCTL_ELEM_REPLACE _IOWR('U', 0x18, struct snd_ctl_elem_info)
+#define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id)
+#define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE _IOWR('U', 0x20, int)
+#define SNDRV_CTL_IOCTL_HWDEP_INFO _IOR('U', 0x21, struct snd_hwdep_info)
+#define SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE _IOR('U', 0x30, int)
+#define SNDRV_CTL_IOCTL_PCM_INFO _IOWR('U', 0x31, struct snd_pcm_info)
+#define SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE _IOW('U', 0x32, int)
+#define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int)
+#define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info)
+#define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int)
+#define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int)
+#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int)
+
+enum sndrv_ctl_event_type {
+ SNDRV_CTL_EVENT_ELEM = 0,
+ SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM,
+};
+
+#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0)
+#define SNDRV_CTL_EVENT_MASK_INFO (1<<1)
+#define SNDRV_CTL_EVENT_MASK_ADD (1<<2)
+#define SNDRV_CTL_EVENT_MASK_TLV (1<<3)
+#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U)
+
+struct snd_ctl_event {
+ int type;
+ union {
+ struct {
+ unsigned int mask;
+ struct snd_ctl_elem_id id;
+ } elem;
+ unsigned char data8[60];
+ } data;
+};
+
+#define SNDRV_CTL_NAME_NONE ""
+#define SNDRV_CTL_NAME_PLAYBACK "Playback "
+#define SNDRV_CTL_NAME_CAPTURE "Capture "
+
+#define SNDRV_CTL_NAME_IEC958_NONE ""
+#define SNDRV_CTL_NAME_IEC958_SWITCH "Switch"
+#define SNDRV_CTL_NAME_IEC958_VOLUME "Volume"
+#define SNDRV_CTL_NAME_IEC958_DEFAULT "Default"
+#define SNDRV_CTL_NAME_IEC958_MASK "Mask"
+#define SNDRV_CTL_NAME_IEC958_CON_MASK "Con Mask"
+#define SNDRV_CTL_NAME_IEC958_PRO_MASK "Pro Mask"
+#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream"
+#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what
+
+#endif
diff --git a/firmware/target/hosted/ibasso/tinyalsa/include/tinyalsa/asoundlib.h b/firmware/target/hosted/ibasso/tinyalsa/include/tinyalsa/asoundlib.h
new file mode 100644
index 0000000000..ba58bdcdfc
--- /dev/null
+++ b/firmware/target/hosted/ibasso/tinyalsa/include/tinyalsa/asoundlib.h
@@ -0,0 +1,260 @@
+/* asoundlib.h
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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 The Android Open Source Project ``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 ASOUNDLIB_H
+#define ASOUNDLIB_H
+
+#include <sys/time.h>
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * PCM API
+ */
+
+struct pcm;
+
+#define PCM_OUT 0x00000000
+#define PCM_IN 0x10000000
+#define PCM_MMAP 0x00000001
+#define PCM_NOIRQ 0x00000002
+#define PCM_NORESTART 0x00000004 /* PCM_NORESTART - when set, calls to
+ * pcm_write for a playback stream will not
+ * attempt to restart the stream in the case
+ * of an underflow, but will return -EPIPE
+ * instead. After the first -EPIPE error, the
+ * stream is considered to be stopped, and a
+ * second call to pcm_write will attempt to
+ * restart the stream.
+ */
+#define PCM_MONOTONIC 0x00000008 /* see pcm_get_htimestamp */
+
+/* PCM runtime states */
+#define PCM_STATE_RUNNING 3
+#define PCM_STATE_XRUN 4
+#define PCM_STATE_DRAINING 5
+#define PCM_STATE_SUSPENDED 7
+#define PCM_STATE_DISCONNECTED 8
+
+/* Bit formats */
+enum pcm_format {
+ PCM_FORMAT_S16_LE = 0,
+ PCM_FORMAT_S32_LE,
+ PCM_FORMAT_S8,
+ PCM_FORMAT_S24_LE,
+
+ PCM_FORMAT_MAX,
+};
+
+/* Bitmask has 256 bits (32 bytes) in asound.h */
+struct pcm_mask {
+ unsigned int bits[32 / sizeof(unsigned int)];
+};
+
+/* Configuration for a stream */
+struct pcm_config {
+ unsigned int channels;
+ unsigned int rate;
+ unsigned int period_size;
+ unsigned int period_count;
+ enum pcm_format format;
+
+ /* Values to use for the ALSA start, stop and silence thresholds. Setting
+ * any one of these values to 0 will cause the default tinyalsa values to be
+ * used instead. Tinyalsa defaults are as follows.
+ *
+ * start_threshold : period_count * period_size
+ * stop_threshold : period_count * period_size
+ * silence_threshold : 0
+ */
+ unsigned int start_threshold;
+ unsigned int stop_threshold;
+ unsigned int silence_threshold;
+};
+
+/* PCM parameters */
+enum pcm_param
+{
+ /* mask parameters */
+ PCM_PARAM_ACCESS,
+ PCM_PARAM_FORMAT,
+ PCM_PARAM_SUBFORMAT,
+ /* interval parameters */
+ PCM_PARAM_SAMPLE_BITS,
+ PCM_PARAM_FRAME_BITS,
+ PCM_PARAM_CHANNELS,
+ PCM_PARAM_RATE,
+ PCM_PARAM_PERIOD_TIME,
+ PCM_PARAM_PERIOD_SIZE,
+ PCM_PARAM_PERIOD_BYTES,
+ PCM_PARAM_PERIODS,
+ PCM_PARAM_BUFFER_TIME,
+ PCM_PARAM_BUFFER_SIZE,
+ PCM_PARAM_BUFFER_BYTES,
+ PCM_PARAM_TICK_TIME,
+};
+
+/* Mixer control types */
+enum mixer_ctl_type {
+ MIXER_CTL_TYPE_BOOL,
+ MIXER_CTL_TYPE_INT,
+ MIXER_CTL_TYPE_ENUM,
+ MIXER_CTL_TYPE_BYTE,
+ MIXER_CTL_TYPE_IEC958,
+ MIXER_CTL_TYPE_INT64,
+ MIXER_CTL_TYPE_UNKNOWN,
+
+ MIXER_CTL_TYPE_MAX,
+};
+
+/* Open and close a stream */
+struct pcm *pcm_open(unsigned int card, unsigned int device,
+ unsigned int flags, struct pcm_config *config);
+int pcm_close(struct pcm *pcm);
+int pcm_is_ready(struct pcm *pcm);
+
+/* Obtain the parameters for a PCM */
+struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
+ unsigned int flags);
+void pcm_params_free(struct pcm_params *pcm_params);
+
+struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
+ enum pcm_param param);
+unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
+ enum pcm_param param);
+unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
+ enum pcm_param param);
+
+/* Returns a human readable reason for the last error */
+const char *pcm_get_error(struct pcm *pcm);
+
+/* Returns the sample size in bits for a PCM format.
+ * As with ALSA formats, this is the storage size for the format, whereas the
+ * format represents the number of significant bits. For example,
+ * PCM_FORMAT_S24_LE uses 32 bits of storage.
+ */
+unsigned int pcm_format_to_bits(enum pcm_format format);
+
+/* Returns the buffer size (int frames) that should be used for pcm_write. */
+unsigned int pcm_get_buffer_size(struct pcm *pcm);
+unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);
+unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);
+
+/* Returns available frames in pcm buffer and corresponding time stamp.
+ * The clock is CLOCK_MONOTONIC if flag PCM_MONOTONIC was specified in pcm_open,
+ * otherwise the clock is CLOCK_REALTIME.
+ * For an input stream, frames available are frames ready for the
+ * application to read.
+ * For an output stream, frames available are the number of empty frames available
+ * for the application to write.
+ */
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+ struct timespec *tstamp);
+
+/* Write data to the fifo.
+ * Will start playback on the first write or on a write that
+ * occurs after a fifo underrun.
+ */
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count);
+int pcm_read(struct pcm *pcm, void *data, unsigned int count);
+
+/*
+ * mmap() support.
+ */
+int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
+int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);
+int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
+ unsigned int *frames);
+int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);
+
+/* Prepare the PCM substream to be triggerable */
+int pcm_prepare(struct pcm *pcm);
+/* Start and stop a PCM channel that doesn't transfer data */
+int pcm_start(struct pcm *pcm);
+int pcm_stop(struct pcm *pcm);
+
+/* Interrupt driven API */
+int pcm_wait(struct pcm *pcm, int timeout);
+
+
+/*
+ * MIXER API
+ */
+
+struct mixer;
+struct mixer_ctl;
+
+/* Open and close a mixer */
+struct mixer *mixer_open(unsigned int card);
+void mixer_close(struct mixer *mixer);
+
+/* Get info about a mixer */
+const char *mixer_get_name(struct mixer *mixer);
+
+/* Obtain mixer controls */
+unsigned int mixer_get_num_ctls(struct mixer *mixer);
+struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id);
+struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name);
+
+/* Get info about mixer controls */
+const char *mixer_ctl_get_name(struct mixer_ctl *ctl);
+enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl);
+const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl);
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl);
+unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl);
+const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
+ unsigned int enum_id);
+
+/* Some sound cards update their controls due to external events,
+ * such as HDMI EDID byte data changing when an HDMI cable is
+ * connected. This API allows the count of elements to be updated.
+ */
+void mixer_ctl_update(struct mixer_ctl *ctl);
+
+/* Set and get mixer controls */
+int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id);
+int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent);
+
+int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);
+int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count);
+int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);
+int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count);
+int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);
+
+/* Determe range of integer mixer controls */
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl);
+int mixer_ctl_get_range_max(struct mixer_ctl *ctl);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/firmware/target/hosted/ibasso/tinyalsa/mixer.c b/firmware/target/hosted/ibasso/tinyalsa/mixer.c
new file mode 100644
index 0000000000..24e94f4f1d
--- /dev/null
+++ b/firmware/target/hosted/ibasso/tinyalsa/mixer.c
@@ -0,0 +1,502 @@
+/* mixer.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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 The Android Open Source Project ``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 <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <sys/ioctl.h>
+
+#include <linux/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+
+struct mixer_ctl {
+ struct mixer *mixer;
+ struct snd_ctl_elem_info *info;
+ char **ename;
+};
+
+struct mixer {
+ int fd;
+ struct snd_ctl_card_info card_info;
+ struct snd_ctl_elem_info *elem_info;
+ struct mixer_ctl *ctl;
+ unsigned int count;
+};
+
+void mixer_close(struct mixer *mixer)
+{
+ unsigned int n,m;
+
+ if (!mixer)
+ return;
+
+ if (mixer->fd >= 0)
+ close(mixer->fd);
+
+ if (mixer->ctl) {
+ for (n = 0; n < mixer->count; n++) {
+ if (mixer->ctl[n].ename) {
+ unsigned int max = mixer->ctl[n].info->value.enumerated.items;
+ for (m = 0; m < max; m++)
+ free(mixer->ctl[n].ename[m]);
+ free(mixer->ctl[n].ename);
+ }
+ }
+ free(mixer->ctl);
+ }
+
+ if (mixer->elem_info)
+ free(mixer->elem_info);
+
+ free(mixer);
+
+ /* TODO: verify frees */
+}
+
+struct mixer *mixer_open(unsigned int card)
+{
+ struct snd_ctl_elem_list elist;
+ struct snd_ctl_elem_info tmp;
+ struct snd_ctl_elem_id *eid = NULL;
+ struct mixer *mixer = NULL;
+ unsigned int n, m;
+ int fd;
+ char fn[256];
+
+ snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
+ fd = open(fn, O_RDWR);
+ if (fd < 0)
+ return 0;
+
+ memset(&elist, 0, sizeof(elist));
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ goto fail;
+
+ mixer = calloc(1, sizeof(*mixer));
+ if (!mixer)
+ goto fail;
+
+ mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
+ mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
+ if (!mixer->ctl || !mixer->elem_info)
+ goto fail;
+
+ if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
+ goto fail;
+
+ eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
+ if (!eid)
+ goto fail;
+
+ mixer->count = elist.count;
+ mixer->fd = fd;
+ elist.space = mixer->count;
+ elist.pids = eid;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ goto fail;
+
+ for (n = 0; n < mixer->count; n++) {
+ struct snd_ctl_elem_info *ei = mixer->elem_info + n;
+ ei->id.numid = eid[n].numid;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
+ goto fail;
+ mixer->ctl[n].info = ei;
+ mixer->ctl[n].mixer = mixer;
+ if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
+ char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
+ if (!enames)
+ goto fail;
+ mixer->ctl[n].ename = enames;
+ for (m = 0; m < ei->value.enumerated.items; m++) {
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.id.numid = ei->id.numid;
+ tmp.value.enumerated.item = m;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
+ goto fail;
+ enames[m] = strdup(tmp.value.enumerated.name);
+ if (!enames[m])
+ goto fail;
+ }
+ }
+ }
+
+ free(eid);
+ return mixer;
+
+fail:
+ /* TODO: verify frees in failure case */
+ if (eid)
+ free(eid);
+ if (mixer)
+ mixer_close(mixer);
+ else if (fd >= 0)
+ close(fd);
+ return 0;
+}
+
+const char *mixer_get_name(struct mixer *mixer)
+{
+ return (const char *)mixer->card_info.name;
+}
+
+unsigned int mixer_get_num_ctls(struct mixer *mixer)
+{
+ if (!mixer)
+ return 0;
+
+ return mixer->count;
+}
+
+struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
+{
+ if (mixer && (id < mixer->count))
+ return mixer->ctl + id;
+
+ return NULL;
+}
+
+struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
+{
+ unsigned int n;
+
+ if (!mixer)
+ return NULL;
+
+ for (n = 0; n < mixer->count; n++)
+ if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
+ return mixer->ctl + n;
+
+ return NULL;
+}
+
+void mixer_ctl_update(struct mixer_ctl *ctl)
+{
+ ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
+}
+
+const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return NULL;
+
+ return (const char *)ctl->info->id.name;
+}
+
+enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return MIXER_CTL_TYPE_UNKNOWN;
+
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
+ case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
+ case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
+ default: return MIXER_CTL_TYPE_UNKNOWN;
+ };
+}
+
+const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return "";
+
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
+ case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
+ case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
+ case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
+ default: return "Unknown";
+ };
+}
+
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return 0;
+
+ return ctl->info->count;
+}
+
+static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
+{
+ if ((percent > 100) || (percent < 0)) {
+ return -EINVAL;
+ }
+
+ int range = (ei->value.integer.max - ei->value.integer.min);
+
+ return ei->value.integer.min + (range * percent) / 100;
+}
+
+static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
+{
+ int range = (ei->value.integer.max - ei->value.integer.min);
+
+ if (range == 0)
+ return 0;
+
+ return ((value - ei->value.integer.min) / range) * 100;
+}
+
+int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ return -EINVAL;
+
+ return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
+}
+
+int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ return -EINVAL;
+
+ return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
+}
+
+int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
+{
+ struct snd_ctl_elem_value ev;
+ int ret;
+
+ if (!ctl || (id >= ctl->info->count))
+ return -EINVAL;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.id.numid = ctl->info->id.numid;
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ if (ret < 0)
+ return ret;
+
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ return !!ev.value.integer.value[id];
+
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ return ev.value.integer.value[id];
+
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ return ev.value.enumerated.item[id];
+
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ return ev.value.bytes.data[id];
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
+{
+ struct snd_ctl_elem_value ev;
+ int ret;
+ size_t size;
+ void *source;
+
+ if (!ctl || (count > ctl->info->count) || !count || !array)
+ return -EINVAL;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.id.numid = ctl->info->id.numid;
+
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ if (ret < 0)
+ return ret;
+
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ size = sizeof(ev.value.integer.value[0]);
+ source = ev.value.integer.value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ size = sizeof(ev.value.bytes.data[0]);
+ source = ev.value.bytes.data;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ memcpy(array, source, size * count);
+
+ return 0;
+}
+
+int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
+{
+ struct snd_ctl_elem_value ev;
+ int ret;
+
+ if (!ctl || (id >= ctl->info->count))
+ return -EINVAL;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.id.numid = ctl->info->id.numid;
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ if (ret < 0)
+ return ret;
+
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ ev.value.integer.value[id] = !!value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ if ((value < mixer_ctl_get_range_min(ctl)) ||
+ (value > mixer_ctl_get_range_max(ctl))) {
+ return -EINVAL;
+ }
+
+ ev.value.integer.value[id] = value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ ev.value.enumerated.item[id] = value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ ev.value.bytes.data[id] = value;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+}
+
+int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
+{
+ struct snd_ctl_elem_value ev;
+ size_t size;
+ void *dest;
+
+ if (!ctl || (count > ctl->info->count) || !count || !array)
+ return -EINVAL;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.id.numid = ctl->info->id.numid;
+
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ size = sizeof(ev.value.integer.value[0]);
+ dest = ev.value.integer.value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ size = sizeof(ev.value.bytes.data[0]);
+ dest = ev.value.bytes.data;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ memcpy(dest, array, size * count);
+
+ return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+}
+
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ return -EINVAL;
+
+ return ctl->info->value.integer.min;
+}
+
+int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ return -EINVAL;
+
+ return ctl->info->value.integer.max;
+}
+
+unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return 0;
+
+ return ctl->info->value.enumerated.items;
+}
+
+const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
+ unsigned int enum_id)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
+ (enum_id >= ctl->info->value.enumerated.items))
+ return NULL;
+
+ return (const char *)ctl->ename[enum_id];
+}
+
+int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
+{
+ unsigned int i, num_enums;
+ struct snd_ctl_elem_value ev;
+ int ret;
+
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
+ return -EINVAL;
+
+ num_enums = ctl->info->value.enumerated.items;
+ for (i = 0; i < num_enums; i++) {
+ if (!strcmp(string, ctl->ename[i])) {
+ memset(&ev, 0, sizeof(ev));
+ ev.value.enumerated.item[0] = i;
+ ev.id.numid = ctl->info->id.numid;
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/firmware/target/hosted/ibasso/tinyalsa/pcm.c b/firmware/target/hosted/ibasso/tinyalsa/pcm.c
new file mode 100644
index 0000000000..0d2f0adf08
--- /dev/null
+++ b/firmware/target/hosted/ibasso/tinyalsa/pcm.c
@@ -0,0 +1,1049 @@
+/* pcm.c
+**
+** Copyright 2011, The Android Open Source Project
+**
+** 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 The Android Open Source Project ``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 <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <limits.h>
+
+#include <linux/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
+#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline int param_is_interval(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
+}
+
+static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->min = val;
+ }
+}
+
+static unsigned int param_get_min(struct snd_pcm_hw_params *p, int n)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ return i->min;
+ }
+ return 0;
+}
+
+static unsigned int param_get_max(struct snd_pcm_hw_params *p, int n)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ return i->max;
+ }
+ return 0;
+}
+
+static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->min = val;
+ i->max = val;
+ i->integer = 1;
+ }
+}
+
+static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ if (i->integer)
+ return i->max;
+ }
+ return 0;
+}
+
+static void param_init(struct snd_pcm_hw_params *p)
+{
+ int n;
+
+ memset(p, 0, sizeof(*p));
+ for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
+ n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = ~0;
+ m->bits[1] = ~0;
+ }
+ for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
+ n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->min = 0;
+ i->max = ~0;
+ }
+ p->rmask = ~0U;
+ p->cmask = 0;
+ p->info = ~0U;
+}
+
+#define PCM_ERROR_MAX 128
+
+struct pcm {
+ int fd;
+ unsigned int flags;
+ int running:1;
+ int prepared:1;
+ int underruns;
+ unsigned int buffer_size;
+ unsigned int boundary;
+ char error[PCM_ERROR_MAX];
+ struct pcm_config config;
+ struct snd_pcm_mmap_status *mmap_status;
+ struct snd_pcm_mmap_control *mmap_control;
+ struct snd_pcm_sync_ptr *sync_ptr;
+ void *mmap_buffer;
+ unsigned int noirq_frames_per_msec;
+};
+
+unsigned int pcm_get_buffer_size(struct pcm *pcm)
+{
+ return pcm->buffer_size;
+}
+
+const char* pcm_get_error(struct pcm *pcm)
+{
+ return pcm->error;
+}
+
+static int oops(struct pcm *pcm, int e, const char *fmt, ...)
+{
+ va_list ap;
+ int sz;
+
+ va_start(ap, fmt);
+ vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
+ va_end(ap);
+ sz = strlen(pcm->error);
+
+ if (errno)
+ snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
+ ": %s", strerror(e));
+ return -1;
+}
+
+static unsigned int pcm_format_to_alsa(enum pcm_format format)
+{
+ switch (format) {
+ case PCM_FORMAT_S32_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+ case PCM_FORMAT_S8:
+ return SNDRV_PCM_FORMAT_S8;
+ case PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S24_LE;
+ default:
+ case PCM_FORMAT_S16_LE:
+ return SNDRV_PCM_FORMAT_S16_LE;
+ };
+}
+
+unsigned int pcm_format_to_bits(enum pcm_format format)
+{
+ switch (format) {
+ case PCM_FORMAT_S32_LE:
+ case PCM_FORMAT_S24_LE:
+ return 32;
+ default:
+ case PCM_FORMAT_S16_LE:
+ return 16;
+ };
+}
+
+unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
+{
+ return bytes / (pcm->config.channels *
+ (pcm_format_to_bits(pcm->config.format) >> 3));
+}
+
+unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
+{
+ return frames * pcm->config.channels *
+ (pcm_format_to_bits(pcm->config.format) >> 3);
+}
+
+static int pcm_sync_ptr(struct pcm *pcm, int flags) {
+ if (pcm->sync_ptr) {
+ pcm->sync_ptr->flags = flags;
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static int pcm_hw_mmap_status(struct pcm *pcm) {
+
+ if (pcm->sync_ptr)
+ return 0;
+
+ int page_size = sysconf(_SC_PAGE_SIZE);
+ pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
+ pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
+ if (pcm->mmap_status == MAP_FAILED)
+ pcm->mmap_status = NULL;
+ if (!pcm->mmap_status)
+ goto mmap_error;
+
+ pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
+ if (pcm->mmap_control == MAP_FAILED)
+ pcm->mmap_control = NULL;
+ if (!pcm->mmap_control) {
+ munmap(pcm->mmap_status, page_size);
+ pcm->mmap_status = NULL;
+ goto mmap_error;
+ }
+ pcm->mmap_control->avail_min = 1;
+
+ return 0;
+
+mmap_error:
+
+ pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
+ if (!pcm->sync_ptr)
+ return -ENOMEM;
+ pcm->mmap_status = &pcm->sync_ptr->s.status;
+ pcm->mmap_control = &pcm->sync_ptr->c.control;
+ pcm->mmap_control->avail_min = 1;
+ pcm_sync_ptr(pcm, 0);
+
+ return 0;
+}
+
+static void pcm_hw_munmap_status(struct pcm *pcm) {
+ if (pcm->sync_ptr) {
+ free(pcm->sync_ptr);
+ pcm->sync_ptr = NULL;
+ } else {
+ int page_size = sysconf(_SC_PAGE_SIZE);
+ if (pcm->mmap_status)
+ munmap(pcm->mmap_status, page_size);
+ if (pcm->mmap_control)
+ munmap(pcm->mmap_control, page_size);
+ }
+ pcm->mmap_status = NULL;
+ pcm->mmap_control = NULL;
+}
+
+static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
+ char *buf, unsigned int src_offset,
+ unsigned int frames)
+{
+ int size_bytes = pcm_frames_to_bytes(pcm, frames);
+ int pcm_offset_bytes = pcm_frames_to_bytes(pcm, pcm_offset);
+ int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
+
+ /* interleaved only atm */
+ if (pcm->flags & PCM_IN)
+ memcpy(buf + src_offset_bytes,
+ (char*)pcm->mmap_buffer + pcm_offset_bytes,
+ size_bytes);
+ else
+ memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
+ buf + src_offset_bytes,
+ size_bytes);
+ return 0;
+}
+
+static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
+ unsigned int offset, unsigned int size)
+{
+ void *pcm_areas;
+ int commit;
+ unsigned int pcm_offset, frames, count = 0;
+
+ while (size > 0) {
+ frames = size;
+ pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+ pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
+ commit = pcm_mmap_commit(pcm, pcm_offset, frames);
+ if (commit < 0) {
+ oops(pcm, commit, "failed to commit %d frames\n", frames);
+ return commit;
+ }
+
+ offset += commit;
+ count += commit;
+ size -= commit;
+ }
+ return count;
+}
+
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+ struct timespec *tstamp)
+{
+ int frames;
+ int rc;
+ snd_pcm_uframes_t hw_ptr;
+
+ if (!pcm_is_ready(pcm))
+ return -1;
+
+ rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
+ if (rc < 0)
+ return -1;
+
+ if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+ (pcm->mmap_status->state != PCM_STATE_DRAINING))
+ return -1;
+
+ *tstamp = pcm->mmap_status->tstamp;
+ if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
+ return -1;
+
+ hw_ptr = pcm->mmap_status->hw_ptr;
+ if (pcm->flags & PCM_IN)
+ frames = hw_ptr - pcm->mmap_control->appl_ptr;
+ else
+ frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+ if (frames < 0)
+ return -1;
+
+ *avail = (unsigned int)frames;
+
+ return 0;
+}
+
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+ struct snd_xferi x;
+
+ if (pcm->flags & PCM_IN)
+ return -EINVAL;
+
+ x.buf = (void*)data;
+ x.frames = count / (pcm->config.channels *
+ pcm_format_to_bits(pcm->config.format) / 8);
+
+ for (;;) {
+ if (!pcm->running) {
+ int prepare_error = pcm_prepare(pcm);
+ if (prepare_error)
+ return prepare_error;
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
+ return oops(pcm, errno, "cannot write initial data");
+ pcm->running = 1;
+ return 0;
+ }
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) {
+ pcm->prepared = 0;
+ pcm->running = 0;
+ if (errno == EPIPE) {
+ /* we failed to make our window -- try to restart if we are
+ * allowed to do so. Otherwise, simply allow the EPIPE error to
+ * propagate up to the app level */
+ pcm->underruns++;
+ if (pcm->flags & PCM_NORESTART)
+ return -EPIPE;
+ continue;
+ }
+ return oops(pcm, errno, "cannot write stream data");
+ }
+ return 0;
+ }
+}
+
+int pcm_read(struct pcm *pcm, void *data, unsigned int count)
+{
+ struct snd_xferi x;
+
+ if (!(pcm->flags & PCM_IN))
+ return -EINVAL;
+
+ x.buf = data;
+ x.frames = count / (pcm->config.channels *
+ pcm_format_to_bits(pcm->config.format) / 8);
+
+ for (;;) {
+ if (!pcm->running) {
+ if (pcm_start(pcm) < 0) {
+ fprintf(stderr, "start error");
+ return -errno;
+ }
+ }
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
+ pcm->prepared = 0;
+ pcm->running = 0;
+ if (errno == EPIPE) {
+ /* we failed to make our window -- try to restart */
+ pcm->underruns++;
+ continue;
+ }
+ return oops(pcm, errno, "cannot read stream data");
+ }
+ return 0;
+ }
+}
+
+static struct pcm bad_pcm = {
+ .fd = -1,
+};
+
+struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
+ unsigned int flags)
+{
+ struct snd_pcm_hw_params *params;
+ char fn[256];
+ int fd;
+
+ snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+ flags & PCM_IN ? 'c' : 'p');
+
+ fd = open(fn, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "cannot open device '%s'\n", fn);
+ goto err_open;
+ }
+
+ params = calloc(1, sizeof(struct snd_pcm_hw_params));
+ if (!params)
+ goto err_calloc;
+
+ param_init(params);
+ if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
+ fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
+ goto err_hw_refine;
+ }
+
+ close(fd);
+
+ return (struct pcm_params *)params;
+
+err_hw_refine:
+ free(params);
+err_calloc:
+ close(fd);
+err_open:
+ return NULL;
+}
+
+void pcm_params_free(struct pcm_params *pcm_params)
+{
+ struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+
+ if (params)
+ free(params);
+}
+
+static int pcm_param_to_alsa(enum pcm_param param)
+{
+ switch (param) {
+ case PCM_PARAM_ACCESS:
+ return SNDRV_PCM_HW_PARAM_ACCESS;
+ case PCM_PARAM_FORMAT:
+ return SNDRV_PCM_HW_PARAM_FORMAT;
+ case PCM_PARAM_SUBFORMAT:
+ return SNDRV_PCM_HW_PARAM_SUBFORMAT;
+ case PCM_PARAM_SAMPLE_BITS:
+ return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
+ break;
+ case PCM_PARAM_FRAME_BITS:
+ return SNDRV_PCM_HW_PARAM_FRAME_BITS;
+ break;
+ case PCM_PARAM_CHANNELS:
+ return SNDRV_PCM_HW_PARAM_CHANNELS;
+ break;
+ case PCM_PARAM_RATE:
+ return SNDRV_PCM_HW_PARAM_RATE;
+ break;
+ case PCM_PARAM_PERIOD_TIME:
+ return SNDRV_PCM_HW_PARAM_PERIOD_TIME;
+ break;
+ case PCM_PARAM_PERIOD_SIZE:
+ return SNDRV_PCM_HW_PARAM_PERIOD_SIZE;
+ break;
+ case PCM_PARAM_PERIOD_BYTES:
+ return SNDRV_PCM_HW_PARAM_PERIOD_BYTES;
+ break;
+ case PCM_PARAM_PERIODS:
+ return SNDRV_PCM_HW_PARAM_PERIODS;
+ break;
+ case PCM_PARAM_BUFFER_TIME:
+ return SNDRV_PCM_HW_PARAM_BUFFER_TIME;
+ break;
+ case PCM_PARAM_BUFFER_SIZE:
+ return SNDRV_PCM_HW_PARAM_BUFFER_SIZE;
+ break;
+ case PCM_PARAM_BUFFER_BYTES:
+ return SNDRV_PCM_HW_PARAM_BUFFER_BYTES;
+ break;
+ case PCM_PARAM_TICK_TIME:
+ return SNDRV_PCM_HW_PARAM_TICK_TIME;
+ break;
+
+ default:
+ return -1;
+ }
+}
+
+struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
+ enum pcm_param param)
+{
+ int p;
+ struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+ if (params == NULL) {
+ return NULL;
+ }
+
+ p = pcm_param_to_alsa(param);
+ if (p < 0 || !param_is_mask(p)) {
+ return NULL;
+ }
+
+ return (struct pcm_mask *)param_to_mask(params, p);
+}
+
+unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
+ enum pcm_param param)
+{
+ struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+ int p;
+
+ if (!params)
+ return 0;
+
+ p = pcm_param_to_alsa(param);
+ if (p < 0)
+ return 0;
+
+ return param_get_min(params, p);
+}
+
+unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
+ enum pcm_param param)
+{
+ struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+ int p;
+
+ if (!params)
+ return 0;
+
+ p = pcm_param_to_alsa(param);
+ if (p < 0)
+ return 0;
+
+ return param_get_max(params, p);
+}
+
+int pcm_close(struct pcm *pcm)
+{
+ if (pcm == &bad_pcm)
+ return 0;
+
+ pcm_hw_munmap_status(pcm);
+
+ if (pcm->flags & PCM_MMAP) {
+ pcm_stop(pcm);
+ munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+ }
+
+ if (pcm->fd >= 0)
+ close(pcm->fd);
+ pcm->prepared = 0;
+ pcm->running = 0;
+ pcm->buffer_size = 0;
+ pcm->fd = -1;
+ free(pcm);
+ return 0;
+}
+
+struct pcm *pcm_open(unsigned int card, unsigned int device,
+ unsigned int flags, struct pcm_config *config)
+{
+ struct pcm *pcm;
+ struct snd_pcm_info info;
+ struct snd_pcm_hw_params params;
+ struct snd_pcm_sw_params sparams;
+ char fn[256];
+ int rc;
+
+ pcm = calloc(1, sizeof(struct pcm));
+ if (!pcm || !config)
+ return &bad_pcm; /* TODO: could support default config here */
+
+ pcm->config = *config;
+
+ snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+ flags & PCM_IN ? 'c' : 'p');
+
+ pcm->flags = flags;
+ pcm->fd = open(fn, O_RDWR);
+ if (pcm->fd < 0) {
+ oops(pcm, errno, "cannot open device '%s'", fn);
+ return pcm;
+ }
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
+ oops(pcm, errno, "cannot get info");
+ goto fail_close;
+ }
+
+ param_init(&params);
+ param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
+ pcm_format_to_alsa(config->format));
+ param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_SUBFORMAT_STD);
+ param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
+ param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ pcm_format_to_bits(config->format));
+ param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ pcm_format_to_bits(config->format) * config->channels);
+ param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
+ config->channels);
+ param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
+ param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, config->rate);
+
+ if (flags & PCM_NOIRQ) {
+
+ if (!(flags & PCM_MMAP)) {
+ oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
+ goto fail;
+ }
+
+ params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
+ pcm->noirq_frames_per_msec = config->rate / 1000;
+ }
+
+ if (flags & PCM_MMAP)
+ param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+ else
+ param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
+ oops(pcm, errno, "cannot set hw params");
+ goto fail_close;
+ }
+
+ /* get our refined hw_params */
+ config->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ config->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
+ pcm->buffer_size = config->period_count * config->period_size;
+
+ if (flags & PCM_MMAP) {
+ pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
+ PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
+ if (pcm->mmap_buffer == MAP_FAILED) {
+ oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
+ pcm_frames_to_bytes(pcm, pcm->buffer_size));
+ goto fail_close;
+ }
+ }
+
+
+ memset(&sparams, 0, sizeof(sparams));
+ sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
+ sparams.period_step = 1;
+ sparams.avail_min = 1;
+
+ if (!config->start_threshold) {
+ if (pcm->flags & PCM_IN)
+ pcm->config.start_threshold = sparams.start_threshold = 1;
+ else
+ pcm->config.start_threshold = sparams.start_threshold =
+ config->period_count * config->period_size / 2;
+ } else
+ sparams.start_threshold = config->start_threshold;
+
+ /* pick a high stop threshold - todo: does this need further tuning */
+ if (!config->stop_threshold) {
+ if (pcm->flags & PCM_IN)
+ pcm->config.stop_threshold = sparams.stop_threshold =
+ config->period_count * config->period_size * 10;
+ else
+ pcm->config.stop_threshold = sparams.stop_threshold =
+ config->period_count * config->period_size;
+ }
+ else
+ sparams.stop_threshold = config->stop_threshold;
+
+ sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
+ sparams.silence_size = 0;
+ sparams.silence_threshold = config->silence_threshold;
+ pcm->boundary = sparams.boundary = pcm->buffer_size;
+
+ while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
+ pcm->boundary *= 2;
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
+ oops(pcm, errno, "cannot set sw params");
+ goto fail;
+ }
+
+ rc = pcm_hw_mmap_status(pcm);
+ if (rc < 0) {
+ oops(pcm, rc, "mmap status failed");
+ goto fail;
+ }
+
+#ifdef SNDRV_PCM_IOCTL_TTSTAMP
+ if (pcm->flags & PCM_MONOTONIC) {
+ int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
+ rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
+ if (rc < 0) {
+ oops(pcm, rc, "cannot set timestamp type");
+ goto fail;
+ }
+ }
+#endif
+
+ pcm->underruns = 0;
+ return pcm;
+
+fail:
+ if (flags & PCM_MMAP)
+ munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+fail_close:
+ close(pcm->fd);
+ pcm->fd = -1;
+ return pcm;
+}
+
+int pcm_is_ready(struct pcm *pcm)
+{
+ return pcm->fd >= 0;
+}
+
+int pcm_prepare(struct pcm *pcm)
+{
+ if (pcm->prepared)
+ return 0;
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
+ return oops(pcm, errno, "cannot prepare channel");
+
+ pcm->prepared = 1;
+ return 0;
+}
+
+int pcm_start(struct pcm *pcm)
+{
+ int prepare_error = pcm_prepare(pcm);
+ if (prepare_error)
+ return prepare_error;
+
+ if (pcm->flags & PCM_MMAP)
+ pcm_sync_ptr(pcm, 0);
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
+ return oops(pcm, errno, "cannot start channel");
+
+ pcm->running = 1;
+ return 0;
+}
+
+int pcm_stop(struct pcm *pcm)
+{
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
+ return oops(pcm, errno, "cannot stop channel");
+
+ pcm->prepared = 0;
+ pcm->running = 0;
+ return 0;
+}
+
+static inline int pcm_mmap_playback_avail(struct pcm *pcm)
+{
+ int avail;
+
+ avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+ if (avail < 0)
+ avail += pcm->boundary;
+ else if (avail > (int)pcm->boundary)
+ avail -= pcm->boundary;
+
+ return avail;
+}
+
+static inline int pcm_mmap_capture_avail(struct pcm *pcm)
+{
+ int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
+ if (avail < 0)
+ avail += pcm->boundary;
+ return avail;
+}
+
+static inline int pcm_mmap_avail(struct pcm *pcm)
+{
+ pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
+ if (pcm->flags & PCM_IN)
+ return pcm_mmap_capture_avail(pcm);
+ else
+ return pcm_mmap_playback_avail(pcm);
+}
+
+static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
+{
+ unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
+ appl_ptr += frames;
+
+ /* check for boundary wrap */
+ if (appl_ptr > pcm->boundary)
+ appl_ptr -= pcm->boundary;
+ pcm->mmap_control->appl_ptr = appl_ptr;
+}
+
+int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
+ unsigned int *frames)
+{
+ unsigned int continuous, copy_frames, avail;
+
+ /* return the mmap buffer */
+ *areas = pcm->mmap_buffer;
+
+ /* and the application offset in frames */
+ *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
+
+ avail = pcm_mmap_avail(pcm);
+ if (avail > pcm->buffer_size)
+ avail = pcm->buffer_size;
+ continuous = pcm->buffer_size - *offset;
+
+ /* we can only copy frames if the are availabale and continuos */
+ copy_frames = *frames;
+ if (copy_frames > avail)
+ copy_frames = avail;
+ if (copy_frames > continuous)
+ copy_frames = continuous;
+ *frames = copy_frames;
+
+ return 0;
+}
+
+int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
+{
+ (void) offset;
+ /* update the application pointer in userspace and kernel */
+ pcm_mmap_appl_forward(pcm, frames);
+ pcm_sync_ptr(pcm, 0);
+
+ return frames;
+}
+
+int pcm_avail_update(struct pcm *pcm)
+{
+ pcm_sync_ptr(pcm, 0);
+ return pcm_mmap_avail(pcm);
+}
+
+int pcm_state(struct pcm *pcm)
+{
+ int err = pcm_sync_ptr(pcm, 0);
+ if (err < 0)
+ return err;
+
+ return pcm->mmap_status->state;
+}
+
+int pcm_wait(struct pcm *pcm, int timeout)
+{
+ struct pollfd pfd;
+ int err;
+
+ pfd.fd = pcm->fd;
+ pfd.events = POLLOUT | POLLERR | POLLNVAL;
+
+ do {
+ /* let's wait for avail or timeout */
+ err = poll(&pfd, 1, timeout);
+ if (err < 0)
+ return -errno;
+
+ /* timeout ? */
+ if (err == 0)
+ return 0;
+
+ /* have we been interrupted ? */
+ if (errno == -EINTR)
+ continue;
+
+ /* check for any errors */
+ if (pfd.revents & (POLLERR | POLLNVAL)) {
+ switch (pcm_state(pcm)) {
+ case PCM_STATE_XRUN:
+ return -EPIPE;
+ case PCM_STATE_SUSPENDED:
+ return -ESTRPIPE;
+ case PCM_STATE_DISCONNECTED:
+ return -ENODEV;
+ default:
+ return -EIO;
+ }
+ }
+ /* poll again if fd not ready for IO */
+ } while (!(pfd.revents & (POLLIN | POLLOUT)));
+
+ return 1;
+}
+
+int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
+{
+ int err = 0, frames, avail;
+ unsigned int offset = 0, count;
+
+ if (bytes == 0)
+ return 0;
+
+ count = pcm_bytes_to_frames(pcm, bytes);
+
+ while (count > 0) {
+
+ /* get the available space for writing new frames */
+ avail = pcm_avail_update(pcm);
+ if (avail < 0) {
+ fprintf(stderr, "cannot determine available mmap frames");
+ return err;
+ }
+
+ /* start the audio if we reach the threshold */
+ if (!pcm->running &&
+ (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
+ if (pcm_start(pcm) < 0) {
+ fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
+ (unsigned int)pcm->mmap_status->hw_ptr,
+ (unsigned int)pcm->mmap_control->appl_ptr,
+ avail);
+ return -errno;
+ }
+ }
+
+ /* sleep until we have space to write new frames */
+ if (pcm->running &&
+ (unsigned int)avail < pcm->mmap_control->avail_min) {
+ int time = -1;
+
+ if (pcm->flags & PCM_NOIRQ)
+ time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
+ / pcm->noirq_frames_per_msec;
+
+ err = pcm_wait(pcm, time);
+ if (err < 0) {
+ pcm->prepared = 0;
+ pcm->running = 0;
+ fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n",
+ (unsigned int)pcm->mmap_status->hw_ptr,
+ (unsigned int)pcm->mmap_control->appl_ptr,
+ avail);
+ pcm->mmap_control->appl_ptr = 0;
+ return err;
+ }
+ continue;
+ }
+
+ frames = count;
+ if (frames > avail)
+ frames = avail;
+
+ if (!frames)
+ break;
+
+ /* copy frames from buffer */
+ frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
+ if (frames < 0) {
+ fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
+ (unsigned int)pcm->mmap_status->hw_ptr,
+ (unsigned int)pcm->mmap_control->appl_ptr,
+ avail);
+ return frames;
+ }
+
+ offset += frames;
+ count -= frames;
+ }
+
+ return 0;
+}
+
+int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+ if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
+ return -ENOSYS;
+
+ return pcm_mmap_transfer(pcm, (void *)data, count);
+}
+
+int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
+{
+ if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
+ return -ENOSYS;
+
+ return pcm_mmap_transfer(pcm, data, count);
+}
diff --git a/firmware/target/hosted/ibasso/usb-ibasso.c b/firmware/target/hosted/ibasso/usb-ibasso.c
new file mode 100644
index 0000000000..e1b134e545
--- /dev/null
+++ b/firmware/target/hosted/ibasso/usb-ibasso.c
@@ -0,0 +1,92 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <stdlib.h>
+
+#include "config.h"
+#include "debug.h"
+
+#include "debug-ibasso.h"
+#include "usb-ibasso.h"
+
+
+static void usb_enable_adb(void)
+{
+ TRACE;
+
+ if(system(NULL))
+ {
+ system("setprop persist.sys.usb.config adb");
+ system("setprop persist.usb.debug 1");
+ return;
+ }
+
+ DEBUGF("ERROR %s: No command processor available.", __func__);
+}
+
+
+static void usb_enable_mass_storage(void)
+{
+ TRACE;
+
+ if(system(NULL))
+ {
+ system("setprop persist.sys.usb.config mass_storage");
+ system("setprop persist.usb.debug 0");
+ return;
+ }
+
+ DEBUGF("ERROR %s: No command processor available.", __func__);
+}
+
+
+/* Default at boot not known. */
+static int _last_usb_mode = -1;
+
+
+void ibasso_set_usb_mode(int mode)
+{
+ DEBUGF("DEBUG %s: _last_usb_mode: %d, mode: %d.", __func__, _last_usb_mode, mode);
+
+ if(_last_usb_mode != mode)
+ {
+ switch(mode)
+ {
+ case USB_MODE_MASS_STORAGE:
+ {
+ _last_usb_mode = mode;
+ usb_enable_mass_storage();
+ break;
+ }
+
+ case USB_MODE_CHARGE: /* Work around. */
+ case USB_MODE_ADB:
+ {
+ _last_usb_mode = mode;
+ usb_enable_adb();
+ break;
+ }
+ }
+ }
+}
diff --git a/firmware/target/hosted/ibasso/usb-ibasso.h b/firmware/target/hosted/ibasso/usb-ibasso.h
new file mode 100644
index 0000000000..f509d43038
--- /dev/null
+++ b/firmware/target/hosted/ibasso/usb-ibasso.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef _USB_DX50_H_
+#define _USB_DX50_H_
+
+
+/* Supported usb modes. */
+enum ibasso_usb_mode
+{
+ /*
+ USB mass storage mode. On USB connection, Rockbox will terminate and the internel and
+ external storage gets exported to the connected client.
+ */
+ USB_MODE_MASS_STORAGE = 0,
+
+ /*
+ Actually the same, since we to not have proper USB detection.
+ Starts the adb server and enables adb connection over USB. Rockbox will continue to run.
+ */
+ USB_MODE_CHARGE,
+ USB_MODE_ADB
+};
+
+
+/*
+ Set the usb mode.
+ mode: ibasso_usb_mode
+*/
+void ibasso_set_usb_mode(int mode);
+
+
+#endif
diff --git a/firmware/target/hosted/ibasso/vold-ibasso.c b/firmware/target/hosted/ibasso/vold-ibasso.c
new file mode 100644
index 0000000000..c92b86d364
--- /dev/null
+++ b/firmware/target/hosted/ibasso/vold-ibasso.c
@@ -0,0 +1,203 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include "config.h"
+#include "debug.h"
+#include "powermgmt.h"
+
+#include "debug-ibasso.h"
+
+
+/*
+ Without this socket iBasso Vold will not start.
+ iBasso Vold uses this to send status messages about storage devices.
+*/
+static const char VOLD_MONITOR_SOCKET_NAME[] = "UNIX_domain";
+static int _vold_monitor_socket_fd = -1;
+
+
+static void vold_monitor_open_socket(void)
+{
+ TRACE;
+
+ unlink(VOLD_MONITOR_SOCKET_NAME);
+
+ _vold_monitor_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if(_vold_monitor_socket_fd < 0)
+ {
+ _vold_monitor_socket_fd = -1;
+ return;
+ }
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, VOLD_MONITOR_SOCKET_NAME, sizeof(addr.sun_path) -1);
+
+ if(bind(_vold_monitor_socket_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
+ {
+ close(_vold_monitor_socket_fd);
+ unlink(VOLD_MONITOR_SOCKET_NAME);
+ _vold_monitor_socket_fd = -1;
+ return;
+ }
+
+ if(listen(_vold_monitor_socket_fd, 1) < 0)
+ {
+ close(_vold_monitor_socket_fd);
+ unlink(VOLD_MONITOR_SOCKET_NAME);
+ _vold_monitor_socket_fd = -1;
+ return;
+ }
+}
+
+
+/*
+ bionic does not have pthread_cancel.
+ 0: Vold monitor thread stopped/ending.
+ 1: Vold monitor thread started/running.
+*/
+static volatile sig_atomic_t _vold_monitor_active = 0;
+
+
+/*
+ 1: /mnt/sdcard is unmounting
+ 0: else
+*/
+static volatile sig_atomic_t _vold_monitor_forced_close_imminent = 0;
+
+
+static void* vold_monitor_run(void* nothing)
+{
+ _vold_monitor_active = 1;
+
+ (void) nothing;
+
+ DEBUGF("DEBUG %s: Thread start.", __func__);
+
+ vold_monitor_open_socket();
+ if(_vold_monitor_socket_fd < 0)
+ {
+ DEBUGF("ERROR %s: Thread end: No socket.", __func__);
+
+ _vold_monitor_active = 0;
+ return 0;
+ }
+
+ struct pollfd fds[1];
+ fds[0].fd = _vold_monitor_socket_fd;
+ fds[0].events = POLLIN;
+
+ while(_vold_monitor_active == 1)
+ {
+ poll(fds, 1, 10);
+ if(! (fds[0].revents & POLLIN))
+ {
+ continue;
+ }
+
+ int socket_fd = accept(_vold_monitor_socket_fd, NULL, NULL);
+
+ if(socket_fd < 0)
+ {
+ DEBUGF("ERROR %s: accept failed.", __func__);
+
+ continue;
+ }
+
+ while(true)
+ {
+ char msg[1024];
+ memset(msg, 0, sizeof(msg));
+ int length = read(socket_fd, msg, sizeof(msg));
+
+ if(length <= 0)
+ {
+ close(socket_fd);
+ break;
+ }
+
+ DEBUGF("%s: msg: %s", __func__, msg);
+
+ if(strcmp(msg, "Volume flash /mnt/sdcard state changed from 4 (Mounted) to 5 (Unmounting)") == 0)
+ {
+ /* We are losing /mnt/sdcard, shutdown Rockbox before it is forced closed. */
+
+ _vold_monitor_forced_close_imminent = 1;
+ sys_poweroff();
+ _vold_monitor_active = 0;
+ }
+ else if(strcmp(msg, "Volume sdcard /mnt/external_sd state changed from 4 (Mounted) to 5 (Unmounting)") == 0)
+ {
+ /* We are loosing the external sdcard, inform Rockbox. */
+ }
+ else if(strcmp(msg, "Volume sdcard /mnt/external_sd state changed from 3 (Checking) to 4 (Mounted)") == 0)
+ {
+ /* The external sdcard is back, inform Rockbox. */
+ }
+ }
+ }
+
+ close(_vold_monitor_socket_fd);
+ unlink(VOLD_MONITOR_SOCKET_NAME);
+ _vold_monitor_socket_fd = -1;
+
+ DEBUGF("%s: Thread end.", __func__);
+
+ _vold_monitor_active = 0;
+ return 0;
+}
+
+
+/* Vold monitor thread. */
+static pthread_t _vold_monitor_thread;
+
+
+void vold_monitor_start(void)
+{
+ TRACE;
+
+ if(_vold_monitor_active == 0)
+ {
+ pthread_create(&_vold_monitor_thread, NULL, vold_monitor_run, NULL);
+ }
+}
+
+
+bool vold_monitor_forced_close_imminent(void)
+{
+ TRACE;
+
+ return(_vold_monitor_forced_close_imminent == 1);
+}
diff --git a/firmware/target/hosted/ibasso/vold-ibasso.h b/firmware/target/hosted/ibasso/vold-ibasso.h
new file mode 100644
index 0000000000..18012b7e16
--- /dev/null
+++ b/firmware/target/hosted/ibasso/vold-ibasso.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * __________ __ ___
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Ilia Sergachev: Initial Rockbox port to iBasso DX50
+ * Copyright (C) 2014 by Mario Basister: iBasso DX90 port
+ * Copyright (C) 2014 by Simon Rothen: Initial Rockbox repository submission, additional features
+ * Copyright (C) 2014 by Udo Schläpfer: Code clean up, additional features
+ *
+ * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+
+#ifndef __VOLD_IBASSO_H__
+#define __VOLD_IBASSO_H__
+
+
+/* Start the vold monitor thread. */
+void vold_monitor_start(void);
+
+
+/*
+ Used to change Rockbox shutdown from reboot/power off to program exit.
+ true: vold monitor has detected, that vold is remounting /mnt/sdcard for USB mass storage
+ access.
+ false: else.
+*/
+bool vold_monitor_forced_close_imminent(void);
+
+
+#endif