mirror of
https://github.com/chararomandroid/android_device_samsung_a20
synced 2026-08-22 12:05:32 -04:00
universal7885: fm-aidl: Move ioctl code to hal
This commit is contained in:
parent
983a25b4d2
commit
839dc3ae4c
8 changed files with 325 additions and 506 deletions
|
|
@ -5,7 +5,6 @@ android_app {
|
||||||
],
|
],
|
||||||
platform_apis: true,
|
platform_apis: true,
|
||||||
certificate: "platform",
|
certificate: "platform",
|
||||||
jni_libs: ["libfmnative_jni"],
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"androidx.core_core",
|
"androidx.core_core",
|
||||||
"androidx.appcompat_appcompat",
|
"androidx.appcompat_appcompat",
|
||||||
|
|
@ -14,6 +13,7 @@ android_app {
|
||||||
"androidx.transition_transition",
|
"androidx.transition_transition",
|
||||||
"kotlinx_coroutines_android",
|
"kotlinx_coroutines_android",
|
||||||
"androidx.coordinatorlayout_coordinatorlayout",
|
"androidx.coordinatorlayout_coordinatorlayout",
|
||||||
|
"vendor.eureka.hardware.fmradio-java",
|
||||||
],
|
],
|
||||||
required: [
|
required: [
|
||||||
"vendor.eureka.hardware.fmradio-service",
|
"vendor.eureka.hardware.fmradio-service",
|
||||||
|
|
|
||||||
|
|
@ -1,498 +0,0 @@
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <cstring>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <iostream>
|
|
||||||
#include "s610_radio.h"
|
|
||||||
#include "v4l2_api.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <jni.h>
|
|
||||||
#include <hardware/hardware.h>
|
|
||||||
#include <hidl/HidlSupport.h>
|
|
||||||
#include <hidl/LegacySupport.h>
|
|
||||||
#include <hidl/Status.h>
|
|
||||||
#include <vendor/eureka/hardware/fmradio/1.2/IFMRadio.h>
|
|
||||||
|
|
||||||
|
|
||||||
using android::sp;
|
|
||||||
using vendor::eureka::hardware::fmradio::V1_0::Direction;
|
|
||||||
using vendor::eureka::hardware::fmradio::V1_1::Status;
|
|
||||||
using vendor::eureka::hardware::fmradio::V1_2::IFMRadio;
|
|
||||||
|
|
||||||
// #define DEBUG
|
|
||||||
#define TRACK_SIZE 30
|
|
||||||
int64_t tracks[TRACK_SIZE] = {0};
|
|
||||||
bool FMThread = false;
|
|
||||||
|
|
||||||
int open_fm_device() {
|
|
||||||
int fd;
|
|
||||||
if ((fd = open("/dev/radio0", O_RDWR | O_CLOEXEC)) < 0) {
|
|
||||||
printf("Cannot open /dev/radio0.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
static int fm_radio_get_frequency(int fd, int64_t *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 = (int64_t)freq.frequency / 16000;
|
|
||||||
|
|
||||||
return FM_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fm_radio_set_frequency(int fd, int64_t 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_set_control(int fd, unsigned int id, int64_t 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, int64_t *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 <class C, typename T> bool contains(C &&c, T e) {
|
|
||||||
return std::find(std::begin(c), std::end(c), e) != std::end(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t fm_radio_get_freqs(int fd) {
|
|
||||||
int64_t ret = 0;
|
|
||||||
fm_radio_set_mute(fd, true);
|
|
||||||
sp<IFMRadio> service = IFMRadio::getService();
|
|
||||||
bool mSysfs = service->isAvailable() == Status::YES;
|
|
||||||
for (int64_t &track : tracks) {
|
|
||||||
if (mSysfs) {
|
|
||||||
service->adjustFreqByStep(Direction::UP);
|
|
||||||
ret = (int64_t)service->getFreqFromSysfs();
|
|
||||||
} else {
|
|
||||||
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 int64_t fm_radio_get_rmssi(int fd) {
|
|
||||||
struct v4l2_tuner tuner {};
|
|
||||||
int ret;
|
|
||||||
int64_t 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, int64_t 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) {
|
|
||||||
int64_t 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(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
|
|
||||||
int64_t 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) {
|
|
||||||
int64_t ret;
|
|
||||||
sp<IFMRadio> service = IFMRadio::getService();
|
|
||||||
bool mSysfs = service->isAvailable() == Status::YES;
|
|
||||||
if (!mSysfs) {
|
|
||||||
fm_radio_channel_searching(fd, 1, 0, FM_CHANNEL_SPACING_100KHZ, &ret);
|
|
||||||
} else {
|
|
||||||
service->adjustFreqByStep(Direction::UP);
|
|
||||||
ret = service->getFreqFromSysfs();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
extern "C" JNIEXPORT jint JNICALL
|
|
||||||
Java_com_eurekateam_fmradio_NativeFMInterface_getBeforeChannel(
|
|
||||||
__unused JNIEnv *env, __unused jobject thiz, jint fd) {
|
|
||||||
int64_t ret;
|
|
||||||
sp<IFMRadio> service = IFMRadio::getService();
|
|
||||||
bool mSysfs = service->isAvailable() == Status::YES;
|
|
||||||
if (!mSysfs) {
|
|
||||||
fm_radio_channel_searching(fd, 0, 0, FM_CHANNEL_SPACING_100KHZ, &ret);
|
|
||||||
} else {
|
|
||||||
service->adjustFreqByStep(Direction::DOWN);
|
|
||||||
ret = service->getFreqFromSysfs();
|
|
||||||
}
|
|
||||||
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, jint 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);
|
|
||||||
}
|
|
||||||
extern "C" JNIEXPORT jboolean JNICALL
|
|
||||||
Java_com_eurekateam_fmradio_NativeFMInterface_getSysfsSupport(
|
|
||||||
__unused JNIEnv *env, __unused jobject thiz) {
|
|
||||||
sp<IFMRadio> service = IFMRadio::getService();
|
|
||||||
return service->isAvailable() == Status::YES;
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
cc_library_shared {
|
cc_library_static {
|
||||||
name: "libfmnative_jni",
|
name: "libfm_slsi-impl",
|
||||||
cflags: [
|
cflags: [
|
||||||
"-Wno-unused-parameter",
|
"-Wno-unused-parameter",
|
||||||
],
|
],
|
||||||
srcs: [
|
srcs: [
|
||||||
"fm_ioctl.cpp",
|
"FM_Device_ctl.cpp",
|
||||||
"fm_route.cpp",
|
"FM_AudioRoute_ctl.cpp",
|
||||||
],
|
],
|
||||||
|
export_include_dirs: ["public"],
|
||||||
defaults: ["eureka_defaults"],
|
defaults: ["eureka_defaults"],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libhidlbase",
|
"libhidlbase",
|
||||||
"liblog",
|
"liblog",
|
||||||
"libutils",
|
"libutils",
|
||||||
"vendor.eureka.hardware.fmradio@1.0",
|
|
||||||
"vendor.eureka.hardware.fmradio@1.1",
|
|
||||||
"vendor.eureka.hardware.fmradio@1.2",
|
|
||||||
"audioflinger-aidl-cpp",
|
"audioflinger-aidl-cpp",
|
||||||
"audiopolicy-aidl-cpp",
|
"audiopolicy-aidl-cpp",
|
||||||
"audiopolicy-types-aidl-cpp",
|
"audiopolicy-types-aidl-cpp",
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#define FM_FAILURE (-1)
|
#define FM_FAILURE (-1)
|
||||||
#define FM_SUCCESS 0
|
#define FM_SUCCESS 0
|
||||||
#define IOHANDLE 13
|
#define IOHANDLE 13
|
||||||
|
|
||||||
using namespace android;
|
using namespace android;
|
||||||
|
|
||||||
extern "C" JNIEXPORT jboolean JNICALL
|
extern "C" JNIEXPORT jboolean JNICALL
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <iostream>
|
||||||
|
#include "S610_FMRadio.h"
|
||||||
|
#include "V4L2_4_4_API.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace fm_radio_slsi {
|
||||||
|
|
||||||
|
constexpr const int TRACK_SIZE = 30;
|
||||||
|
|
||||||
|
static bool FMThread = false;
|
||||||
|
|
||||||
|
int open_device(void) {
|
||||||
|
int fd;
|
||||||
|
if ((fd = open("/dev/radio0", O_RDWR | O_CLOEXEC)) < 0) {
|
||||||
|
printf("Cannot open /dev/radio0.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_frequency(const int fd, int64_t *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)
|
||||||
|
return FM_FAILURE;
|
||||||
|
|
||||||
|
*channel = static_cast<int64_t>(freq.frequency) / 16000;
|
||||||
|
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_frequency(const int fd, int64_t 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 set_control(const int fd, unsigned int id, int64_t val) {
|
||||||
|
struct v4l2_control ctrl {};
|
||||||
|
int ret;
|
||||||
|
ctrl.id = id;
|
||||||
|
if (val)
|
||||||
|
ctrl.value = static_cast<unsigned int>(val);
|
||||||
|
else
|
||||||
|
ctrl.value = 0;
|
||||||
|
|
||||||
|
ret = ioctl(fd, VIDIOC_S_CTRL, &ctrl);
|
||||||
|
if (ret < 0) {
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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) {
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int channel_search(const int fd, unsigned int upward,
|
||||||
|
unsigned int wrap_around,
|
||||||
|
unsigned int spacing, int64_t *channel) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = set_control(fd, V4L2_CID_S610_SEEK_MODE,
|
||||||
|
FM_TUNER_AUTONOMOUS_SEARCH_MODE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = seek_frequency(fd, upward, wrap_around, spacing);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = get_frequency(fd, channel);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_mute(const int fd, bool mute) {
|
||||||
|
int ret = set_control(fd, V4L2_CID_AUDIO_MUTE, !mute);
|
||||||
|
if (ret < 0) {
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_volume(int fd, int volume /* 1 ~ 15 */) {
|
||||||
|
int ret = set_control(fd, V4L2_CID_AUDIO_VOLUME, volume);
|
||||||
|
if (ret < 0) {
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<int64_t[]> get_freqs(const int fd) {
|
||||||
|
set_mute(fd, true);
|
||||||
|
|
||||||
|
auto map = std::vector<int64_t>();
|
||||||
|
for (int i = 0; i < TRACK_SIZE; i++) {
|
||||||
|
int64_t found = 0;
|
||||||
|
channel_search(fd, 1, 0, FM_CHANNEL_SPACING_50KHZ, &found);
|
||||||
|
for (auto k : map) {
|
||||||
|
if (k == found)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
map.push_back(found);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ptr = std::make_unique<int64_t[]>(map.size());
|
||||||
|
for (unsigned long i = 0; i < map.size(); i++) {
|
||||||
|
ptr[i] = map[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
set_mute(fd, false);
|
||||||
|
return std::move(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fm_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) {
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FM_FAILURE - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fm_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 void fm_thread(int fd) {
|
||||||
|
struct pollfd radio_poll {};
|
||||||
|
unsigned char read_buf[FM_RADIO_RDS_DATA_MAX];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (FMThread) {
|
||||||
|
ret = fm_poll(fd, &radio_poll);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (ret < FM_FAILURE)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fm_read(fd, read_buf);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fm_thread_set(const int fd, const bool enable) {
|
||||||
|
FMThread = enable;
|
||||||
|
if (enable) fm_thread(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_rmssi(int fd) {
|
||||||
|
struct v4l2_tuner tuner {};
|
||||||
|
int ret;
|
||||||
|
int64_t 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_rssi(int fd, int64_t rssi) {
|
||||||
|
int ret = set_control(fd, V4L2_CID_S610_RSSI_TH, rssi);
|
||||||
|
if (ret < 0) {
|
||||||
|
return FM_FAILURE;
|
||||||
|
}
|
||||||
|
return FM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootctrl(const int fd) {
|
||||||
|
set_control(fd, V4L2_CID_S610_IF_COUNT1, 4800); // SetIFCount 1
|
||||||
|
set_control(fd, V4L2_CID_S610_IF_COUNT2, 5600); // SetIFCount 2
|
||||||
|
set_control(fd, V4L2_CID_S610_SOFT_STEREO_BLEND,
|
||||||
|
3172); // Set Soft Stereo Blend
|
||||||
|
set_control(fd, V4L2_CID_S610_SOFT_MUTE_COEFF,
|
||||||
|
16); // SetSoftMuteCoeff
|
||||||
|
set_control(fd, V4L2_CID_S610_CH_BAND,
|
||||||
|
S610_BAND_FM); // Set Band (To FM)
|
||||||
|
set_control(fd, V4L2_CID_S610_CH_SPACING,
|
||||||
|
FM_CHANNEL_SPACING_50KHZ); // Spacing 5kHz
|
||||||
|
set_control(fd, V4L2_CID_S610_RDS_ON, FM_RDS_ENABLE); // RDS on
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_search(const int fd) {
|
||||||
|
set_control(fd, V4L2_CID_S610_SEEK_CANCEL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
typedef u_int8_t __u8;
|
typedef u_int8_t __u8;
|
||||||
typedef int32_t __s32;
|
typedef int32_t __s32;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace fm_radio_slsi {
|
||||||
|
|
||||||
|
int open_device(void);
|
||||||
|
int get_frequency(const int fd, int64_t *channel);
|
||||||
|
int set_frequency(const int fd, int64_t channel);
|
||||||
|
int seek_frequency(int fd, unsigned int upward, unsigned int wrap_around,
|
||||||
|
unsigned int spacing);
|
||||||
|
int channel_search(const int fd, unsigned int upward, unsigned int wrap_around,
|
||||||
|
unsigned int spacing, int64_t *channel);
|
||||||
|
int set_mute(const int fd, bool mute);
|
||||||
|
int set_volume(int fd, int volume);
|
||||||
|
std::unique_ptr<int64_t[]> get_freqs(const int fd);
|
||||||
|
void fm_thread_set(const int fd, const bool enable);
|
||||||
|
unsigned int get_upperband_limit(int fd);
|
||||||
|
unsigned int get_lowerband_limit(int fd);
|
||||||
|
int64_t get_rmssi(int fd);
|
||||||
|
int set_rssi(int fd, int64_t rssi);
|
||||||
|
void bootctrl(const int fd);
|
||||||
|
void stop_search(const int fd);
|
||||||
|
} // namespace fm_radio_slsi
|
||||||
Loading…
Reference in a new issue