diff --git a/universal7885-common/apps/FMRadio/Android.bp b/universal7885-common/apps/FMRadio/Android.bp new file mode 100644 index 0000000..132aa56 --- /dev/null +++ b/universal7885-common/apps/FMRadio/Android.bp @@ -0,0 +1,21 @@ +android_app { + name: "FMRadio", + srcs: [ + "src/**/*.kt", + ], + platform_apis: true, + privileged: true, + certificate: "platform", + jni_libs: ["libfmioctl_jni"], + static_libs: [ + "androidx.core_core", + "androidx.appcompat_appcompat", + ], + required: ["privapp_whitelist_com.eurekateam.fmradio.xml"], +} + +prebuilt_etc { + name: "privapp_whitelist_com.eurekateam.fmradio.xml", + src: "privapp_whitelist_com.eurekateam.fmradio.xml", + sub_dir: "permissions", +} diff --git a/universal7885-common/apps/FMRadio/AndroidManifest.xml b/universal7885-common/apps/FMRadio/AndroidManifest.xml new file mode 100644 index 0000000..e7337e2 --- /dev/null +++ b/universal7885-common/apps/FMRadio/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/universal7885-common/apps/FMRadio/jni/Android.bp b/universal7885-common/apps/FMRadio/jni/Android.bp new file mode 100644 index 0000000..560edd8 --- /dev/null +++ b/universal7885-common/apps/FMRadio/jni/Android.bp @@ -0,0 +1,12 @@ +cc_library_shared { + name: "libfmioctl_jni", + cflags: [ + "-Wall", + "-Wextra", + "-Wno-unused-parameter", + "-Werror", + ], + srcs: ["fm_ioctl.cpp"], + header_libs: ["jni_headers"], +} + diff --git a/universal7885-common/apps/FMRadio/jni/fm.h b/universal7885-common/apps/FMRadio/jni/fm.h new file mode 100644 index 0000000..f84660e --- /dev/null +++ b/universal7885-common/apps/FMRadio/jni/fm.h @@ -0,0 +1,65 @@ +#define FM_FAILURE -1 +#define FM_SUCCESS 0 + +#define V4L2_CID_USER_S610_BASE (0x00980900 + 0x1070) +enum s610_ctrl_id { + V4L2_CID_S610_CH_SPACING = (V4L2_CID_USER_S610_BASE + 0x01), + V4L2_CID_S610_CH_BAND = (V4L2_CID_USER_S610_BASE + 0x02), + V4L2_CID_S610_SOFT_STEREO_BLEND = (V4L2_CID_USER_S610_BASE + 0x03), + V4L2_CID_S610_SOFT_STEREO_BLEND_COEFF = (V4L2_CID_USER_S610_BASE+0x04), + V4L2_CID_S610_SOFT_MUTE_COEFF = (V4L2_CID_USER_S610_BASE + 0x5), + V4L2_CID_S610_RSSI_CURR = (V4L2_CID_USER_S610_BASE + 0x06), + V4L2_CID_S610_SNR_CURR = (V4L2_CID_USER_S610_BASE + 0x07), + V4L2_CID_S610_SEEK_CANCEL = (V4L2_CID_USER_S610_BASE + 0x08), + V4L2_CID_S610_SEEK_MODE = (V4L2_CID_USER_S610_BASE + 0x09), + V4L2_CID_S610_RDS_ON = (V4L2_CID_USER_S610_BASE + 0x0A), + V4L2_CID_S610_IF_COUNT1 = (V4L2_CID_USER_S610_BASE + 0x0B), + V4L2_CID_S610_IF_COUNT2 = (V4L2_CID_USER_S610_BASE + 0x0C), + V4L2_CID_S610_RSSI_TH = (V4L2_CID_USER_S610_BASE + 0x0D), + V4L2_CID_S610_KERNEL_VER = (V4L2_CID_USER_S610_BASE + 0x0E), + V4L2_CID_S610_SOFT_STEREO_BLEND_REF = (V4L2_CID_USER_S610_BASE+0x0F), + V4L2_CID_S610_REG_RW_ADDR = (V4L2_CID_USER_S610_BASE + 0x10), + V4L2_CID_S610_REG_RW = (V4L2_CID_USER_S610_BASE + 0x11), +}; + +/* Tunner modes */ +enum fm_tuner_mode { + FM_TUNER_STOP_SEARCH_MODE = 0, + FM_TUNER_PRESET_MODE = 1, + FM_TUNER_AUTONOMOUS_SEARCH_MODE = 2, + FM_TUNER_AUTONOMOUS_SEARCH_MODE_NEXT = 10 +}; + +/* channel spacing */ +enum fm_channel_spacing { + FM_CHANNEL_SPACING_50KHZ = 1, + FM_CHANNEL_SPACING_100KHZ = 2, + FM_CHANNEL_SPACING_200KHZ = 4 +}; + +/* Mute modes */ +enum fm_mute_mode { + FM_MUTE_ON = 0, + FM_MUTE_OFF = 1, + FM_MUTE_ATTENUATE = 2 +}; + +/* FM RDS modes */ +enum fm_rds_mode { + FM_RDS_DISABLE = 0, + FM_RDS_ENABLE = 1 +}; + +#define FM_RADIO_RDS_DATA_MAX 48 + +enum s610_freq_bands { + S610_BAND_FM = 0, + S610_BAND_AM = 1, +}; + +enum s610_aud_mode { + S610_AUD_ENABLE = 1, + S610_AUD_DISABLE = 0, +}; + + diff --git a/universal7885-common/apps/FMRadio/jni/fm_ioctl.cpp b/universal7885-common/apps/FMRadio/jni/fm_ioctl.cpp new file mode 100644 index 0000000..fbd7f6e --- /dev/null +++ b/universal7885-common/apps/FMRadio/jni/fm_ioctl.cpp @@ -0,0 +1,484 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fm.h" +#include "kernel_internal.h" +#include +#include + +//#define DEBUG +#define TRACK_SIZE 30 +long tracks[TRACK_SIZE] = { 0 }; +bool FMThread = false; + +int open_fm_device(){ + int fd; + if ((fd = open("/dev/radio0", O_RDWR)) < 0){ + printf("Cannot open /dev/radio0.\n"); + return -1; + } + return fd; +} +static int fm_radio_get_frequency(int fd, long *channel) +{ + struct v4l2_frequency freq{}; + int ret; + + freq.tuner = 0; + freq.type = V4L2_TUNER_RADIO; + + ret = ioctl(fd, VIDIOC_G_FREQUENCY, &freq); + if (ret < 0) { + printf("FmRadioController: failed to get frequency\n"); + return FM_FAILURE; + } + + *channel = (long)freq.frequency/16000; + + return FM_SUCCESS; +} + +static int fm_radio_set_frequency(int fd, long channel) +{ + struct v4l2_frequency freq{}; + int ret; + + freq.tuner = 0; + freq.type = V4L2_TUNER_RADIO; + freq.frequency = (unsigned int) channel * 16000; + + ret = ioctl(fd, VIDIOC_S_FREQUENCY, &freq); + if (ret < 0) { + printf("FmRadioController: failed to set frequency\n"); + return FM_FAILURE; + } + + return FM_SUCCESS; +} + +static int fm_radio_get_control(int fd, unsigned int id, long *val) +{ + struct v4l2_control ctrl{}; + int ret; +#ifdef DEBUG + printf("FmRadioController:fm_radio_get_control: id(%d)\n", id); +#endif + ctrl.id = id; + + ret = ioctl(fd, VIDIOC_G_CTRL, &ctrl); + if (ret < 0) { + printf("FmRadioController: failed to get control\n"); + return FM_FAILURE; + } + + *val = (long)ctrl.value; + + return FM_SUCCESS; +} + +static int fm_radio_set_control(int fd, unsigned int id, long val) +{ + struct v4l2_control ctrl{}; + int ret; +#ifdef DEBUG + printf("FmRadioController:fm_radio_set_control: id(%d) val(%ld)\n", id, val); +#endif + ctrl.id = id; + if (val) + ctrl.value = (unsigned int)val; + else + ctrl.value = 0; + + ret = ioctl(fd, VIDIOC_S_CTRL, &ctrl); + if (ret < 0) { + printf("FmRadioController: failed to set control\n"); + return FM_FAILURE; + } + + return FM_SUCCESS; +} + +static int fm_radio_seek_frequency(int fd, unsigned int upward, unsigned int wrap_around, unsigned int spacing) +{ + struct v4l2_hw_freq_seek seek{}; + int ret; + + seek.tuner = 0; + seek.type = V4L2_TUNER_RADIO; + seek.seek_upward = upward; + seek.wrap_around = wrap_around; + seek.spacing = spacing; + + ret = ioctl(fd, VIDIOC_S_HW_FREQ_SEEK, &seek); + if (ret < 0) { + printf("FmRadioController: failed to seek frequency\n"); + return FM_FAILURE; + } + + return FM_SUCCESS; +} +static int fm_radio_channel_searching(int fd, unsigned int upward, unsigned int wrap_around, unsigned int spacing, long *channel) +{ + int ret; + + ret = fm_radio_set_control(fd, V4L2_CID_S610_SEEK_MODE, FM_TUNER_AUTONOMOUS_SEARCH_MODE); + if (ret < 0) + return ret; + + ret = fm_radio_seek_frequency(fd, upward, wrap_around, spacing); + if (ret < 0) + return ret; + + ret = fm_radio_get_frequency(fd, channel); + if (ret < 0) + return ret; + + return ret; +} +static int fm_radio_set_tuner(int fd, unsigned int mode) +{ + struct v4l2_tuner tuner{}; + int ret; + + tuner.index = 0; + tuner.audmode = mode; + tuner.type = 1; + + ret = ioctl(fd, VIDIOC_S_TUNER, &tuner); + if (ret < 0) { + printf("FmRadioController: failed to set tuner\n"); + return FM_FAILURE; + } + + return FM_SUCCESS; +} +static int fm_radio_get_tuner(int fd) +{ + struct v4l2_tuner tuner{}; + int ret; + + tuner.index = 0; + tuner.type = 1; + ret = ioctl(fd, VIDIOC_G_TUNER, &tuner); + if (ret < 0) { + printf("FmRadioController: failed to set tuner\n"); + return FM_FAILURE; + } + + return tuner.audmode; +} + +static int fm_radio_set_mute(int fd, bool mute) +{ + int muteint = 1; + if (mute) muteint = 0; + int ret = fm_radio_set_control(fd, V4L2_CID_AUDIO_MUTE, muteint); + if (ret < 0) { + printf("FmRadioController: failed to set mute\n"); + return FM_FAILURE; + } + + return FM_SUCCESS; +} + +static int fm_radio_set_volume(int fd, int volume /* 1 ~ 15 */) +{ + int ret = fm_radio_set_control(fd, V4L2_CID_AUDIO_VOLUME, volume); + if (ret < 0) { + printf("FmRadioController: failed to set volume\n"); + return FM_FAILURE; + } + + return FM_SUCCESS; +} + +template +bool contains(C&& c, T e) { return std::find(std::begin(c), std::end(c), e) != std::end(c); } + +static long fm_radio_get_freqs(int fd){ + long ret = 0; + fm_radio_set_mute(fd, true); + for (long & track : tracks){ + fm_radio_channel_searching(fd, 1, 0, FM_CHANNEL_SPACING_50KHZ, &ret); + if (contains(tracks, ret)) break; + track = ret; + printf("Found Freq %ld\n", ret); + } + fm_radio_set_mute(fd, false); + return ret; +} + +static int fm_radio_poll(int fd, struct pollfd *poll_fd) +{ + int ret; + + poll_fd->fd = fd; + poll_fd->events = POLLIN; + poll_fd->revents = 0; + + ret = poll(poll_fd, 1, 360); + if (ret > 0) { + if (poll_fd->revents & POLLIN) { + printf("FmRadioController: ready to read\n"); + return FM_SUCCESS; + } + + printf("FmRadioController: cannot read yet\n"); + return FM_FAILURE; + } + + if (!ret) { + printf("FmRadioController: polling timeout\n"); + return FM_FAILURE; + } + + printf("FmRadioController: pollig fail: %d\n", ret); + return FM_FAILURE - 1; +} + +static int fm_radio_read(int fd, unsigned char *buf) +{ + int ret; + + ret = read(fd, buf, FM_RADIO_RDS_DATA_MAX); + if (ret < 0) { + printf("FmRadioController: failed to read\n"); + return FM_FAILURE; + } + + return ret; +} + +static int fm_radio_thread(int fd) +{ + struct pollfd radio_poll{}; + unsigned char read_buf[FM_RADIO_RDS_DATA_MAX]; + int ret; + + while (FMThread) { + ret = fm_radio_poll(fd, &radio_poll); + if (ret < 0) { + if (ret < FM_FAILURE) + break; + else + continue; + } + + ret = fm_radio_read(fd, read_buf); + if (ret < 0) + break; + } + return 0; +} +static unsigned int fm_radio_get_upperband_limit(int fd) +{ + int ret; + struct v4l2_tuner tuner{}; + unsigned int freq; + tuner.index = 0; + ret = ioctl(fd, VIDIOC_G_TUNER, &tuner); + if(ret < 0) { + return FM_FAILURE; + }else { + freq = (tuner.rangehigh / 16000); + return freq; + } +} + +static unsigned int fm_radio_get_lowerband_limit(int fd) +{ + int ret; + unsigned int freq; + struct v4l2_tuner tuner{}; + + tuner.index = 0; + ret = ioctl(fd, VIDIOC_G_TUNER, &tuner); + if(ret < 0) { + return FM_FAILURE; + }else { + freq = (tuner.rangelow / 16000); + return freq; + } +} +static long fm_radio_get_rmssi(int fd) +{ + struct v4l2_tuner tuner{}; + int ret; + long rmssi; + tuner.index = 0; + tuner.signal = 0; + ret = ioctl(fd, VIDIOC_G_TUNER, &tuner); + if(ret < 0) { + ret = FM_FAILURE; + }else { + rmssi = tuner.signal; + ret = rmssi; + } + return ret; +} +static int fm_radio_set_rssi(int fd, long rssi){ + int ret = fm_radio_set_control(fd, V4L2_CID_S610_RSSI_TH, rssi); + if (ret < 0){ + return FM_FAILURE; + } + return FM_SUCCESS; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_openFMDevice +(__unused JNIEnv *env, __unused jobject thiz) { + return open_fm_device(); +} +extern "C" +JNIEXPORT jlong JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getFMFreq +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + long freq; + fm_radio_get_frequency(fd, &freq); + return freq; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMFreq +(__unused JNIEnv *env, __unused jobject thiz, jint fd, jint freq) { + return fm_radio_set_frequency(fd, freq); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMVolume +(__unused JNIEnv *env, __unused jobject thiz, jint fd, jint volume) { + return fm_radio_set_volume(fd, volume); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMMute +(__unused JNIEnv *env, __unused jobject thiz, jint fd, jboolean mute) { + return fm_radio_set_mute(fd, mute); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getFmUpper +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + return fm_radio_get_upperband_limit(fd); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getFMLower +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + return fm_radio_get_lowerband_limit(fd); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getRMSSI +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + return fm_radio_get_rmssi(fd); +} +extern "C" +JNIEXPORT jlongArray JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getFMTracks +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + fm_radio_get_freqs(fd); + jlongArray result; + result = (*env).NewLongArray(TRACK_SIZE); + if (result == nullptr) { + return nullptr; /* out of memory error thrown */ + } + int i; + // fill a temp structure to use to populate the java int array + jlong fill[TRACK_SIZE]; + for (i = 0; i < TRACK_SIZE; i++) { + fill[i] = tracks[i]; // put whatever logic you want to populate the values here. + } + // move from the temp structure to the java structure + (*env).SetLongArrayRegion(result, 0, TRACK_SIZE, fill); + return result; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMStereo +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + return fm_radio_set_tuner(fd, 1); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMMono +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + return fm_radio_set_tuner(fd, 0); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMThread +(__unused JNIEnv *env, __unused jobject thiz, jint fd, jboolean run) { + if (run){ + FMThread = true; + fm_radio_thread(fd); + }else{ + FMThread = false; + } + return FM_SUCCESS; +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMBoot +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + fm_radio_set_control(fd, V4L2_CID_S610_IF_COUNT1, 4800); // SetIFCount 1 + fm_radio_set_control(fd, V4L2_CID_S610_IF_COUNT2, 5600); // SetIFCount 2 + fm_radio_set_control(fd, V4L2_CID_S610_SOFT_STEREO_BLEND, 3172); // Set Soft Stereo Blend + fm_radio_set_control(fd, V4L2_CID_S610_SOFT_MUTE_COEFF, 16); // SetSoftMuteCoeff + fm_radio_set_control(fd, V4L2_CID_S610_CH_BAND, S610_BAND_FM); // Set Band (To FM) + fm_radio_set_control(fd, V4L2_CID_S610_CH_SPACING, FM_CHANNEL_SPACING_50KHZ); // Spacing 5kHz + fm_radio_set_control(fd, V4L2_CID_S610_RDS_ON, FM_RDS_ENABLE); // RDS on +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getNextChannel +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + long ret; + fm_radio_channel_searching(fd, 1, 0, FM_CHANNEL_SPACING_100KHZ, &ret); + return ret; +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getBeforeChannel +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + long ret; + fm_radio_channel_searching(fd, 0, 0, FM_CHANNEL_SPACING_100KHZ, &ret); + return ret; +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_getAudioChannel +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + int channel = fm_radio_get_tuner(fd); + if (channel == V4L2_TUNER_MODE_STEREO){ + return true; // Is EarPhones + }else{ + return false; // Is Speaker + } +} +extern "C" +JNIEXPORT void JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_stopSearching +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + fm_radio_set_control(fd, V4L2_CID_S610_SEEK_CANCEL, 1); +} +extern "C" +JNIEXPORT jint JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_setFMRSSI +(__unused JNIEnv *env, __unused jobject thiz, jint fd, jlong rssi) { + return fm_radio_set_rssi(fd, rssi); +} +extern "C" +JNIEXPORT void JNICALL +Java_com_eurekateam_fmradio_NativeFMInterface_closeFMDevice +(__unused JNIEnv *env, __unused jobject thiz, jint fd) { + close(fd); +} \ No newline at end of file diff --git a/universal7885-common/apps/FMRadio/jni/kernel_internal.h b/universal7885-common/apps/FMRadio/jni/kernel_internal.h new file mode 100644 index 0000000..9f423ce --- /dev/null +++ b/universal7885-common/apps/FMRadio/jni/kernel_internal.h @@ -0,0 +1,125 @@ + +typedef u_int8_t __u8; +typedef int32_t __s32; +typedef u_int32_t __u32; + +struct v4l2_tuner { + __u32 index; + __u8 name[32]; + __u32 type; /* enum v4l2_tuner_type */ + __u32 capability; + __u32 rangelow; + __u32 rangehigh; + __u32 rxsubchans; + __u32 audmode; + __s32 signal; + __s32 afc; + __u32 reserved[4]; +}; + +struct v4l2_frequency { + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 frequency; + __u32 reserved[8]; +}; + + +struct v4l2_hw_freq_seek { + __u32 tuner; + __u32 type; /* enum v4l2_tuner_type */ + __u32 seek_upward; + __u32 wrap_around; + __u32 spacing; + __u32 rangelow; + __u32 rangehigh; + __u32 reserved[5]; +}; + +enum v4l2_tuner_type { + V4L2_TUNER_RADIO = 1, + V4L2_TUNER_ANALOG_TV = 2, + V4L2_TUNER_DIGITAL_TV = 3, + V4L2_TUNER_SDR = 4, + V4L2_TUNER_RF = 5, +}; +struct v4l2_control { + __u32 id; + __s32 value; +}; +/** + * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP + * + * @driver: name of the driver module (e.g. "bttv") + * @card: name of the card (e.g. "Hauppauge WinTV") + * @bus_info: name of the bus (e.g. "PCI:" + pci_name(pci_dev) ) + * @version: KERNEL_VERSION + * @capabilities: capabilities of the physical device as a whole + * @device_caps: capabilities accessed via this particular device (node) + * @reserved: reserved fields for future extensions + */ +struct v4l2_capability { + __u8 driver[16]; + __u8 card[32]; + __u8 bus_info[32]; + __u32 version; + __u32 capabilities; + __u32 device_caps; + __u32 reserved[3]; +}; + +/* + * T I M E C O D E + */ +struct v4l2_timecode { + __u32 type; + __u32 flags; + __u8 frames; + __u8 seconds; + __u8 minutes; + __u8 hours; + __u8 userbits[4]; +}; + +struct v4l2_buffer { + __u32 index; + __u32 type; + __u32 bytesused; + __u32 flags; + __u32 field; + struct timeval timestamp; + struct v4l2_timecode timecode; + __u32 sequence; + + /* memory location */ + __u32 memory; + union { + __u32 offset; + unsigned long userptr; + struct v4l2_plane *planes; + __s32 fd; + } m; + __u32 length; + __u32 reserved2; + __u32 reserved; +}; + +#define V4L2_TUNER_MODE_MONO 0x0000 +#define V4L2_TUNER_MODE_STEREO 0x0001 + +// Ioctl +#define VIDIOC_G_FREQUENCY _IOWR('V', 56, struct v4l2_frequency) +#define VIDIOC_S_FREQUENCY _IOW('V', 57, struct v4l2_frequency) +#define VIDIOC_G_TUNER _IOWR('V', 29, struct v4l2_tuner) +#define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) +#define VIDIOC_S_TUNER _IOW('V', 30, struct v4l2_tuner) +#define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) +#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) +#define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) + +#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */ +#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) +#define V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+5) +#define V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+9) + + diff --git a/universal7885-common/apps/FMRadio/privapp_whitelist_com.eurekateam.fmradio.xml b/universal7885-common/apps/FMRadio/privapp_whitelist_com.eurekateam.fmradio.xml new file mode 100644 index 0000000..3633515 --- /dev/null +++ b/universal7885-common/apps/FMRadio/privapp_whitelist_com.eurekateam.fmradio.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/universal7885-common/apps/FMRadio/res/drawable/left.png b/universal7885-common/apps/FMRadio/res/drawable/left.png new file mode 100644 index 0000000..73bd50a Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/left.png differ diff --git a/universal7885-common/apps/FMRadio/res/drawable/power.xml b/universal7885-common/apps/FMRadio/res/drawable/power.xml new file mode 100644 index 0000000..d816c7d --- /dev/null +++ b/universal7885-common/apps/FMRadio/res/drawable/power.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/universal7885-common/apps/FMRadio/res/drawable/radio.png b/universal7885-common/apps/FMRadio/res/drawable/radio.png new file mode 100644 index 0000000..3d087e5 Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/radio.png differ diff --git a/universal7885-common/apps/FMRadio/res/drawable/radio_text.png b/universal7885-common/apps/FMRadio/res/drawable/radio_text.png new file mode 100644 index 0000000..762332f Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/radio_text.png differ diff --git a/universal7885-common/apps/FMRadio/res/drawable/right.png b/universal7885-common/apps/FMRadio/res/drawable/right.png new file mode 100644 index 0000000..9108c07 Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/right.png differ diff --git a/universal7885-common/apps/FMRadio/res/drawable/start.png b/universal7885-common/apps/FMRadio/res/drawable/start.png new file mode 100644 index 0000000..b737620 Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/start.png differ diff --git a/universal7885-common/apps/FMRadio/res/drawable/stop.png b/universal7885-common/apps/FMRadio/res/drawable/stop.png new file mode 100644 index 0000000..3c7de6a Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/stop.png differ diff --git a/universal7885-common/apps/FMRadio/res/drawable/warning.jpg b/universal7885-common/apps/FMRadio/res/drawable/warning.jpg new file mode 100644 index 0000000..bff3977 Binary files /dev/null and b/universal7885-common/apps/FMRadio/res/drawable/warning.jpg differ diff --git a/universal7885-common/apps/FMRadio/res/layout/activity_main.xml b/universal7885-common/apps/FMRadio/res/layout/activity_main.xml new file mode 100644 index 0000000..fa07f6e --- /dev/null +++ b/universal7885-common/apps/FMRadio/res/layout/activity_main.xml @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +