universal7885: fm-aidl: Move ioctl code to hal

This commit is contained in:
roynatech2544 2022-10-06 09:55:12 +00:00
commit 839dc3ae4c
8 changed files with 325 additions and 506 deletions

View file

@ -5,7 +5,6 @@ android_app {
],
platform_apis: true,
certificate: "platform",
jni_libs: ["libfmnative_jni"],
static_libs: [
"androidx.core_core",
"androidx.appcompat_appcompat",
@ -14,6 +13,7 @@ android_app {
"androidx.transition_transition",
"kotlinx_coroutines_android",
"androidx.coordinatorlayout_coordinatorlayout",
"vendor.eureka.hardware.fmradio-java",
],
required: [
"vendor.eureka.hardware.fmradio-service",

View file

@ -1,32 +0,0 @@
cc_library_shared {
name: "libfmnative_jni",
cflags: [
"-Wno-unused-parameter",
],
srcs: [
"fm_ioctl.cpp",
"fm_route.cpp",
],
defaults: ["eureka_defaults"],
shared_libs: [
"libhidlbase",
"liblog",
"libutils",
"vendor.eureka.hardware.fmradio@1.0",
"vendor.eureka.hardware.fmradio@1.1",
"vendor.eureka.hardware.fmradio@1.2",
"audioflinger-aidl-cpp",
"audiopolicy-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"libaudiofoundation",
"libaudioutils",
"libaudioclient",
"libutils",
"libaudiopolicy",
"libaudiomanager",
],
header_libs: [
"libaudioclient_headers",
"jni_headers",
],
}

View file

@ -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;
}

View file

@ -1,37 +0,0 @@
/*
* Copyright 2021 Soo Hwan Na "Royna"
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <media/AudioSystem.h>
#include <media/IAudioFlinger.h>
#define FM_FAILURE (-1)
#define FM_SUCCESS 0
#define IOHANDLE 13
using namespace android;
extern "C" JNIEXPORT jboolean JNICALL
Java_com_eurekateam_fmradio_NativeFMInterface_setAudioRoute(
__unused JNIEnv *env, __unused jobject thiz, jboolean speaker) {
const sp<IAudioFlinger> &af = AudioSystem::get_audio_flinger();
if (af == 0)
return PERMISSION_DENIED;
if (speaker) {
af->setParameters(IOHANDLE, String8("routing=2"));
} else {
af->setParameters(IOHANDLE, String8("routing=8"));
}
return FM_SUCCESS;
}

View file

@ -1,56 +0,0 @@
#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,
};

View file

@ -1,123 +0,0 @@
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;
uint64_t 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)