/*
* TI FM kernel driver's sample application.
*
* Copyright (C) 2010 Texas Instruments
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <math.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <tinyalsa/asoundlib.h>
#include <poll.h>
#include "kfmapp.h"
static unsigned int pdevice = 0; /* playback device */
static unsigned int cdevice = 1; /* capture device */
static int fm_aud_enable;
struct pcm *pcm_p = NULL;
struct pcm *pcm_c = NULL;
struct mixer *mixer;
/* #define V4L2_TUNER_SUB_RDS 0x10 */
static char *g_mutemodes[]={"Mute ON","Mute OFF","Attenuate Voice"};
/*
static char *g_bands[]={"Europe/US","Japan"};
static char *g_sm_modes[]={"Stereo","Mono"};
static char *g_rx_deemphasis_modes[]={"50 usec","75 usec"};
static char *g_rds_opmodes[]={"RDS","RBDS"};
static char *g_af_switch_mode[]={"Off","On"};
*/
static char *g_rds_modes[]={"Off","On"};
static int g_vol_to_set;
static pthread_t g_rds_thread_ptr;
volatile char g_rds_thread_terminate,g_rds_thread_running;
static int g_radio_fd;
/* Program Type */
static char *pty_str[]= {"None", "News", "Current Affairs",
"Information","Sport", "Education",
"Drama", "Culture","Science",
"Varied Speech", "Pop Music",
"Rock Music","Easy Listening",
"Light Classic Music", "Serious Classics",
"other Music","Weather", "Finance",
"Childrens Progs","Social Affairs",
"Religion", "Phone In", "Travel",
"Leisure & Hobby","Jazz", "Country",
"National Music","Oldies","Folk",
"Documentary", "Alarm Test", "Alarm"};
void fmapp_display_tx_menu(void)
{
printf("Available FM TX Commands:\n");
printf("f <freq> tune to freq(in MHz)\n");
printf("gf get frequency(MHz)\n");
printf("e <val> set pre-emphasis filter value"
"(0 = OFF, 1 = 50 usec and 2 = 75 usec)\n");
/* printf("ge get pre-emphasis filter\n");*/
printf("p <val> set FM TX powerlevel (91 - 122)\n");
/* printf("gp get deemphasis filter\n");
printf("i <val> set FM TX antenna impedance value (0 = 50, 1 = 200 and 2 = 500)\n");
printf("gi get FM TX antenna impedance value\n");*/
printf("1 to set RDS Radio Text\n");
printf("2 to set RDS Radio PS Name\n");
printf("3 <value> to set RDS Radio PI code\n");
printf("4 <value> to set RDS Radio PTY\n");
printf("5 <AF Freq in KHz> to set RDS Radio Alternate Frequency\n");
}
void fmapp_display_rx_menu(void)
{
printf("Available FM RX Commands:\n");
/* printf("p power on/off\n"); */
printf("f <freq> tune to freq(in MHz)\n");
printf("gf get frequency(MHz)\n");
printf("gr get rssi level\n");
printf("t turns RDS on/off\n");
printf("gt get RDS on/off\n");
printf("+ increases the volume\n");
printf("- decreases the volume\n");
printf("v <0-65535> sets the volume\n");
printf("gv get volume\n");
printf("b<value> switches Japan / Eur-Us (0=US/Eur & 1=Japan)\n");
printf("gb get band\n");
printf("s switches stereo / mono\n");
printf("gs get stereo/mono mode\n");
printf("m changes mute mode\n");
printf("gm get mute mode\n");
/* printf("e set deemphasis filter\n");
printf("ge get deemphasis filter\n");
printf("d set rf dependent mute\n");
printf("gd get rf dependent mute\n");
printf("z set rds system\n");
printf("gz get rds system\n"); */
printf("c<value> set rds af switch(0-OFF & 1=ON)\n");
printf("gc get rds af switch\n");
printf("< seek down\n");
printf("> seek up\n");
printf("? <(0)-(127)> set RSSI threshold\n");
printf("g? get rssi threshold\n");
printf("ga get tuner attributes\n");
/* printf("gn auto scan\n"); */
printf("A Start FM RX Audio Routing\n");
printf("q quit rx menu\n");
}
int fmapp_get_tx_ant_imp(void)
{
struct v4l2_control vctrl;
int res;
vctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR;
res = ioctl(g_radio_fd,VIDIOC_G_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to get FM Tx antenna impedence value\n");
return res;
}
printf("FM Tx antenna impedence value is --> %d\n",vctrl.value);
return 0;
}
int fmapp_get_tx_power_level(void)
{
struct v4l2_control vctrl;
int res;
vctrl.id = V4L2_CID_TUNE_POWER_LEVEL;
res = ioctl(g_radio_fd,VIDIOC_G_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to get FM Tx power level\n");
return res;
}
printf("FM Tx Power level is --> %d\n",vctrl.value);
return 0;
}
int fmapp_get_premphasis_filter_mode(void)
{
struct v4l2_control vctrl;
int res;
vctrl.id = V4L2_CID_TUNE_PREEMPHASIS;
res = ioctl(g_radio_fd,VIDIOC_G_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to get preemphasis filter val\n");
return res;
}
printf("Preemphasis filter val is --> %d\n",vctrl.value);
return 0;
}
int fmapp_get_tx_frequency(void)
{
struct v4l2_frequency vf;
struct v4l2_modulator vm;
int res, div;
vm.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_MODULATOR, &vm);
if(res < 0)
{
printf("Failed to get modulator capabilities\n");
return res;
}
res = ioctl(g_radio_fd, VIDIOC_G_FREQUENCY,&vf);
if(res < 0)
{
printf("Failed to read current frequency\n");
return res;
}
div = (vm.capability & V4L2_TUNER_CAP_LOW) ? 1000 : 1;
printf("Transmitting at Frequency %3.2f MHz\n",vf.frequency /
( 16000.0 * div));
return 0;
}
int fmapp_get_rx_frequency(void)
{
struct v4l2_frequency vf;
struct v4l2_tuner vt;
int res, div;
vt.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vt);
if(res < 0)
{
printf("Failed to get tuner capabilities\n");
return res;
}
res = ioctl(g_radio_fd, VIDIOC_G_FREQUENCY,&vf);
if(res < 0)
{
printf("Failed to read current frequency\n");
return res;
}
div = (vt.capability & V4L2_TUNER_CAP_LOW) ? 1000 : 1;
printf("Tuned to frequency %3.2f MHz \n",vf.frequency / ( 16.0 * div));
return 0;
}
int fmapp_set_tx_rds_radio_text(void)
{
struct v4l2_ext_controls_kfmapp vec;
struct v4l2_ext_control_kfmapp vctrls;
int res;
char rds_text[100];
vec.ctrl_class = V4L2_CTRL_CLASS_FM_TX;
vec.count = 1;
vctrls.id = V4L2_CID_RDS_TX_RADIO_TEXT;
printf("Enter RDS text to transmit\n");
scanf("%s", rds_text);
vctrls.string = rds_text;
vctrls.size = strlen(rds_text) + 1;
vec.controls = &vctrls;
printf("Entered RDS text is - %s and strlen = %d\n",vctrls.string, vctrls.size);
res = ioctl(g_radio_fd, VIDIOC_S_EXT_CTRLS, &vec);
if(res < 0)
{
printf("Failed to set FM Tx RDS Radio text\n");
return res;
}
printf("FM Modulator RDS Radio text is set and transmitted\n");
return res;
}
int fmapp_set_tx_rds_radio_ps_name(void)
{
struct v4l2_ext_controls_kfmapp vec;
struct v4l2_ext_control_kfmapp vctrls;
int res;
char rds_text[100];
vec.ctrl_class = V4L2_CTRL_CLASS_FM_TX;
vec.count = 1;
vctrls.id = V4L2_CID_RDS_TX_PS_NAME;
printf("Enter RDS PS Name to transmit\n");
scanf("%s", rds_text);
vctrls.string = rds_text;
vctrls.size = strlen(rds_text) + 1;
vec.controls = &vctrls;
printf("Entered RDS text is - %s\n",vctrls.string);
res = ioctl(g_radio_fd, VIDIOC_S_EXT_CTRLS, &vec);
if(res < 0)
{
printf("Failed to set FM Tx RDS Radio PS Name\n");
return res;
}
printf("FM Modulator RDS Radio PS Name set and transmitted\n");
return res;
}
int fmapp_set_tx_rds_radio_pi_code(char *cmd)
{
struct v4l2_ext_controls_kfmapp vec;
struct v4l2_ext_control_kfmapp vctrls;
int user_val;
int res;
sscanf(cmd, "%d", &user_val);
vec.ctrl_class = V4L2_CTRL_CLASS_FM_TX;
vec.count = 1;
vctrls.id = V4L2_CID_RDS_TX_PI;
vctrls.value = user_val;
vctrls.size = 0;
vec.controls = &vctrls;
res = ioctl(g_radio_fd, VIDIOC_S_EXT_CTRLS, &vec);
if(res < 0)
{
printf("Failed to set FM Tx RDS PI Code\n");
return res;
}
printf("Setting FM Tx RDS PI Code is Succesful\n");
return res;
}
int fmapp_set_tx_rds_radio_af(char *cmd)
{
int fd, res, af_freq;
fd = open(FMTX_RDS_AF_SYSFS_ENTRY, O_RDWR);
if (fd < 0) {
printf("Can't open %s", FMTX_RDS_AF_SYSFS_ENTRY);
return -1;
}
res = write(fd, cmd, FMAPP_AF_MAX_FREQ_RANGE);
if(res <= 0){
printf("Failed to set FM TX RDS Alternate Frequency\n");
goto exit;
}
printf("FM RDS Alternate Frequency is to %s Succesfully\n", cmd);
exit:
close(fd);
return res;
}
int fmapp_set_tx_rds_radio_pty(char *cmd)
{
struct v4l2_ext_controls_kfmapp vec;
struct v4l2_ext_control_kfmapp vctrls;
int user_val;
int res;
sscanf(cmd, "%d", &user_val);
vec.ctrl_class = V4L2_CTRL_CLASS_FM_TX;
vec.count = 1;
vctrls.id = V4L2_CID_RDS_TX_PTY;
vctrls.value = user_val;
vctrls.size = 0;
vec.controls = &vctrls;
res = ioctl(g_radio_fd, VIDIOC_S_EXT_CTRLS, &vec);
if(res < 0)
{
printf("Failed to set FM Tx RDS PTY\n");
return res;
}
printf("Setting FM Tx RDS PTY is Succesful\n");
return res;
}
int fmapp_set_tx_ant_imp(char *cmd)
{
int user_val;
struct v4l2_control vctrl;
int res;
sscanf(cmd, "%d", &user_val);
vctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR;
vctrl.value = user_val;
res = ioctl(g_radio_fd,VIDIOC_S_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to set FM Tx antenna impedence value\n");
return res;
}
printf("Setting FM Tx antenna impedence value to ---> %d\n",vctrl.value);
return 0;
}
int fmapp_set_tx_power_level(char *cmd)
{
struct v4l2_ext_controls_kfmapp vec;
struct v4l2_ext_control_kfmapp vctrls;
int user_val;
int res;
sscanf(cmd, "%d", &user_val);
vec.ctrl_class = V4L2_CTRL_CLASS_FM_TX;
vec.count = 1;
vctrls.id = V4L2_CID_TUNE_POWER_LEVEL;
vctrls.value = user_val;
vctrls.size = 0;
vec.controls = &vctrls;
res = ioctl(g_radio_fd, VIDIOC_S_EXT_CTRLS, &vec);
if(res < 0)
{
printf("Failed to set FM Tx power level\n");
return res;
}
printf("Setting FM Tx Power level to ---> %d\n", vctrls.value);
return res;
}
int fmapp_set_premphasis_filter_mode(char *cmd)
{
struct v4l2_ext_controls_kfmapp vec;
struct v4l2_ext_control_kfmapp vctrls;
int user_val;
int res;
sscanf(cmd, "%d", &user_val);
vec.ctrl_class = V4L2_CTRL_CLASS_FM_TX;
vec.count = 1;
vctrls.id = V4L2_CID_TUNE_PREEMPHASIS;
vctrls.value = user_val;
vctrls.size = 0;
vec.controls = &vctrls;
res = ioctl(g_radio_fd, VIDIOC_S_EXT_CTRLS, &vec);
if(res < 0)
{
printf("Failed to set preemphasis filter val\n");
return res;
}
printf("Setting preemphasis filter val success\n");
return res;
}
int fmapp_set_tx_frequency(char *cmd)
{
float user_freq;
struct v4l2_frequency vf;
struct v4l2_modulator vm;
int res, div;
sscanf(cmd, "%f", &user_freq);
vm.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_MODULATOR, &vm);
if(res < 0)
{
printf("Failed to get modulator capabilities\n");
return res;
}
vf.tuner = 0;
vf.frequency = rint(user_freq * 16000 + 0.5);
div = (vm.capability & V4L2_TUNER_CAP_LOW) ? 1000 : 1;
if (div == 1)
vf.frequency /= 1000;
res = ioctl(g_radio_fd, VIDIOC_S_FREQUENCY, &vf);
if(res < 0)
{
printf("Failed to set frequency %f\n",user_freq);
return res;
}
printf("Started Transmitting at %3.2f MHz Frequency\n", vf.frequency /
(16.0 * div));
return res;
}
int fmapp_set_rx_frequency(char *cmd)
{
float user_freq;
struct v4l2_frequency vf;
struct v4l2_tuner vt;
int res, div;
sscanf(cmd, "%f", &user_freq);
vf.tuner = 0;
/* As per V4L2 specifications VIDIOC_S_FREQUENCY ioctl expects tuning
* frequency in units of 62.5 KHz, or if the struct v4l2_tuner or struct
* v4l2_modulator capabilities flag V4L2_TUNER_CAP_LOW is set, in units
* of 62.5 Hz. But FM ST v4l2 driver presently handling the frequency in
* units of 1 KHz
*/
vf.frequency = rint(user_freq * 16000 + 0.5);
vt.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vt);
if(res < 0)
{
printf("Failed to get tuner capabilities\n");
return res;
}
div = (vt.capability & V4L2_TUNER_CAP_LOW) ? 1000 : 1;
if (div == 1)
vf.frequency /= 1000;
if(vf.frequency < vt.rangelow || vf.frequency > vt.rangehigh){
printf("Failed to set frequency: Frequency is not in range"
"(%3.2f MHz to %3.2f MHz)\n", (vt.rangelow/(16.0 * div)),
(vt.rangehigh/(16.0 * div)));
return -EINVAL;
}
res = ioctl(g_radio_fd, VIDIOC_S_FREQUENCY, &vf);
if(res < 0)
{
printf("Failed to set frequency %f\n",user_freq);
return res;
}
printf("Tuned to frequency %3.2f MHz\n", vf.frequency / (16.0 * div));
return 0;
}
inline void display_volume_bar(void)
{
int index;
printf("\nVolume: ");
for(index=1; index<g_vol_to_set; index = index*1000)
printf("#");
printf("\nVolume is : %d\n",g_vol_to_set);
}
int fmapp_set_rx_volume(char *cmd,int interactive,int vol_to_set)
{
struct v4l2_control vctrl;
int res;
if(interactive == FMAPP_INTERACTIVE)
sscanf(cmd, "%d", &g_vol_to_set);
else
g_vol_to_set = vol_to_set;
vctrl.id = V4L2_CID_AUDIO_VOLUME;
vctrl.value = g_vol_to_set;
res = ioctl(g_radio_fd,VIDIOC_S_CTRL,&vctrl);
if(res < 0)
{
g_vol_to_set = 0;
printf("Failed to set volume\n");
return res;
}
printf("Setting volume to %d \n",g_vol_to_set);
return 0;
}
int fmapp_get_rx_volume(void)
{
struct v4l2_control vctrl;
int res;
vctrl.id = V4L2_CID_AUDIO_VOLUME;
res = ioctl(g_radio_fd,VIDIOC_G_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to get volume\n");
return res;
}
g_vol_to_set = vctrl.value;
printf("Radio Volume is set to %d\n",g_vol_to_set);
// display_volume_bar();
return 0;
}
int fmapp_rx_increase_volume(void)
{
int ret;
g_vol_to_set +=1;
if(g_vol_to_set > 70)
g_vol_to_set = 70;
ret = fmapp_set_rx_volume(NULL,FMAPP_BATCH,g_vol_to_set);
if(ret < 0)
return ret;
display_volume_bar();
return 0;
}
int fmapp_rx_decrease_volume(void)
{
int ret;
g_vol_to_set -=1;
if(g_vol_to_set < 0)
g_vol_to_set = 0;
ret = fmapp_set_rx_volume(NULL,FMAPP_BATCH,g_vol_to_set);
if(ret < 0)
return ret;
display_volume_bar();
return 0;
}
int fmapp_set_rx_mute_mode(void)
{
struct v4l2_control vctrl;
static short int mute_mode = FM_MUTE_OFF;
int res;
vctrl.value = 0;
printf("Mutemode = %d\n",mute_mode);
switch (mute_mode)
{
case FM_MUTE_OFF:
mute_mode = FM_MUTE_ON;
break;
case FM_MUTE_ON:
mute_mode = FM_MUTE_OFF;
break;
}
vctrl.id = V4L2_CID_AUDIO_MUTE;
vctrl.value = mute_mode;
res = ioctl(g_radio_fd,VIDIOC_S_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to set mute mode\n");
return res;
}
printf("Setting to \"%s\" \n",g_mutemodes[mute_mode]);
return 0;
}
int fmapp_get_rx_mute_mode(void)
{
struct v4l2_control vctrl;
int res;
vctrl.id = V4L2_CID_AUDIO_MUTE;
res = ioctl(g_radio_fd,VIDIOC_G_CTRL,&vctrl);
if(res < 0)
{
printf("Failed to get mute mode\n");
return res;
}
printf("%s\n",g_mutemodes[vctrl.value]);
return 0;
}
int fmapp_rx_seek(int seek_direction)
{
struct ti_v4l2_hw_freq_seek frq_seek;
int res;
printf("Seeking %s..\n",seek_direction?"up":"down");
frq_seek.type = 1;
frq_seek.seek_upward = seek_direction;
frq_seek.spacing = 200000;
frq_seek.wrap_around = 0;
errno = 0;
res = ioctl(g_radio_fd,VIDIOC_S_HW_FREQ_SEEK,&frq_seek);
if(errno == EAGAIN)
{
printf("Band limit reached\n");
}
else if(res <0)
{
printf("Seek operation failed\n");
return res;
}
/* Display seeked freq */
fmapp_get_rx_frequency();
return 0;
}
int fmapp_set_rx_af_switch(char *cmd)
{
int fd, res;
fd = open(FMRX_RDS_AF_SYSFS_ENTRY, O_RDWR);
if (fd < 0) {
printf("Can't open %s", FMRX_RDS_AF_SYSFS_ENTRY);
return -1;
}
res = write(fd, cmd, sizeof(char));
if(res <= 0){
printf("Failed to set FM RDS AF Switch\n");
goto exit;
}
printf("FM RDS Alternate Frequency is %s\n",
atoi(cmd) == 0 ? "OFF":"ON");
exit:
close(fd);
return res;
}
int fmapp_get_rx_af_switch(void)
{
unsigned char fm_rds_af;
int fd, res;
fd = open(FMRX_RDS_AF_SYSFS_ENTRY, O_RDONLY);
if (fd < 0) {
printf("Can't open %s", FMRX_RDS_AF_SYSFS_ENTRY);
return -1;
}
res = read(fd, &fm_rds_af, 1);
if(res < 0){
printf("reading %s failed %s\n",
FMRX_RDS_AF_SYSFS_ENTRY,strerror(res));
goto exit;
}
printf("FM RDS Alternate Frequency is %s \n",
(atoi((char *) &fm_rds_af)) == 0?"OFF":"ON");
exit:
close(fd);
return 0;
}
int fmapp_get_rx_rssi_threshold(void)
{
unsigned char fm_rssi_threshhold;
int fd, res;
fd = open(FMRX_RSSI_LVL_SYSFS_ENTRY, O_RDONLY);
if (fd < 0) {
printf("Can't open %s", FMRX_RSSI_LVL_SYSFS_ENTRY);
return -1;
}
res = read(fd, &fm_rssi_threshhold, 3);
if(res < 0){
printf("reading %s failed %s\n",
FMRX_RSSI_LVL_SYSFS_ENTRY,strerror(res));
goto exit;
}
printf("Current FM RSSI threshold level is %d \n",
atoi((char *) &fm_rssi_threshhold));
exit:
close(fd);
return res;
}
int fmapp_set_rx_rssi_threshold(char *cmd)
{
int fd, res;
fd = open(FMRX_RSSI_LVL_SYSFS_ENTRY, O_RDWR);
if (fd < 0) {
printf("Can't open %s", FMRX_RSSI_LVL_SYSFS_ENTRY);
return -1;
}
res = write(fd, cmd, sizeof(char) * 3);
if(res <= 0){
printf("Failed to set FM RSSI threshold level\n");
goto exit;
}
printf("FM RSSI threshold level is set to %d\n", atoi(cmd));
exit:
close(fd);
return res;
}
int fmapp_set_band(char *cmd)
{
int fd, res;
fd = open(FMRX_BAND_SYSFS_ENTRY, O_RDWR);
if (fd < 0) {
printf("Can't open %s", FMRX_BAND_SYSFS_ENTRY);
return -1;
}
res = write(fd, cmd, sizeof(char));
if(res <= 0){
printf("Failed to set FM Band\n");
goto exit;
}
printf("FM Band is set to %s\n", atoi(cmd) == 0?"US/EUROPE":"JAPAN");
exit:
close(fd);
return res;
}
int fmapp_get_band(void)
{
unsigned char fm_band;
int fd, res;
fd = open(FMRX_BAND_SYSFS_ENTRY, O_RDONLY);
if (fd < 0) {
printf("Can't open %s", FMRX_BAND_SYSFS_ENTRY);
return -1;
}
res = read(fd, &fm_band, 1);
if(res < 0){
printf("reading %s failed %s\n",FMRX_BAND_SYSFS_ENTRY,strerror(res));
goto exit;
}
printf("Present FM Band is %s \n",
(atoi((char *) &fm_band)) == 0?"US/EUROPE":"JAPAN");
exit:
close(fd);
return res;
}
static void tinymix_set_value(struct mixer *mixer, unsigned int id,
int value)
{
struct mixer_ctl *ctl;
enum mixer_ctl_type type;
unsigned int i, num_values;
ctl = mixer_get_ctl(mixer, id);
type = mixer_ctl_get_type(ctl);
num_values = mixer_ctl_get_num_values(ctl);
for(i=0; i<num_values; i++) {
if (mixer_ctl_set_value(ctl, i, value)) {
fprintf(stderr, "Error: invalid value\n");
return;
}
}
}
int fmapp_start_audio()
{
struct pcm_config config;
mixer = mixer_open(0);
if (!mixer) {
fprintf(stderr, "Failed to open mixer\n");
return EXIT_FAILURE;
}
config.channels = 2;
config.rate = 48000;
config.period_size = 1024;
config.period_count = 4;
config.format = PCM_FORMAT_S16_LE;
config.silence_threshold = 0;
config.stop_threshold = -1;
if (fm_aud_enable == 0){
/* Set Tinymix controles */
tinymix_set_value(mixer, 77, 2);
tinymix_set_value(mixer, 76, 2);
tinymix_set_value(mixer, 64, 1);
tinymix_set_value(mixer, 65, 4);
tinymix_set_value(mixer, 55, 12);
tinymix_set_value(mixer, 54, 11);
tinymix_set_value(mixer, 51, 1);
tinymix_set_value(mixer, 9, 120);
tinymix_set_value(mixer, 72, 1);
tinymix_set_value(mixer, 73, 1);
tinymix_set_value(mixer, 34, 1);
tinymix_set_value(mixer, 50, 1);
pcm_p = pcm_open(0, pdevice, PCM_OUT, &config);
if (!pcm_p || !pcm_is_ready(pcm_p)) {
fprintf(stderr, "Unable to open PCM device (%s)\n",
pcm_get_error(pcm_p));
return 0;
}
printf("Playback device opened successfully");
pcm_c = pcm_open(0, cdevice, PCM_IN, &config);
if (!pcm_c || !pcm_is_ready(pcm_c)) {
fprintf(stderr, "Unable to open PCM device (%s)\n",
pcm_get_error(pcm_c));
return 0;
}
printf("Capture device opened successfully");
pcm_start(pcm_c);
pcm_start(pcm_p);
printf(" Trigered the loopback");
fm_aud_enable = 1;
}
else {
/* Set Tinymix controls to Normal*/
tinymix_set_value(mixer, 77, 0);
tinymix_set_value(mixer, 76, 0);
tinymix_set_value(mixer, 64, 0);
tinymix_set_value(mixer, 65, 0);
tinymix_set_value(mixer, 55, 0);
tinymix_set_value(mixer, 54, 0);
tinymix_set_value(mixer, 51, 0);
tinymix_set_value(mixer, 9, 0);
tinymix_set_value(mixer, 72, 0);
tinymix_set_value(mixer, 73, 0);
tinymix_set_value(mixer, 34, 0);
tinymix_set_value(mixer, 50, 0);
/* close the device */
pcm_stop(pcm_p);
pcm_stop(pcm_c);
pcm_close(pcm_p);
pcm_close(pcm_c);
fm_aud_enable = 0;
}
printf("FM RX Audio Routing Done\n");
return 0;
}
int fmapp_get_rx_rssi_lvl(void)
{
struct v4l2_tuner vtun;
float rssi_lvl;
int res;
vtun.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun);
if(res < 0)
{
printf("Failed to get tunner attributes\n");
return res;
}
rssi_lvl = ((float)vtun.signal / 0xFFFF) * 100;
printf("Signal Strength: %d%%\n",(unsigned int)rssi_lvl);
return 0;
}
int fmapp_set_stereo_mono_mode(void)
{
struct v4l2_tuner vtun;
int res = 0;
vtun.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun);
if(res < 0)
{
printf("Failed to set stereo-mono mode\n");
return res;
}
if(V4L2_TUNER_MODE_STEREO == vtun.audmode)
vtun.audmode = V4L2_TUNER_MODE_MONO;
else
vtun.audmode = V4L2_TUNER_MODE_STEREO;
res = ioctl(g_radio_fd, VIDIOC_S_TUNER, &vtun);
if(res < 0)
{
printf("Failed to set stereo-mono mode\n");
return res;
}
printf("Audio Mode set to: %s\n",(vtun.audmode == V4L2_TUNER_MODE_STEREO) ? "STEREO":"MONO");
return 0;
}
int fmapp_get_stereo_mono_mode(void)
{
struct v4l2_tuner vtun;
int res;
vtun.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun);
if(res < 0)
{
printf("Failed to get tunner attributes\n");
return res;
}
printf("Audio Mode: %s\n",(vtun.audmode == V4L2_TUNER_MODE_STEREO) ? "STEREO":"MONO");
return 0;
}
int fmapp_get_rx_tunner_attributes(void)
{
struct v4l2_tuner vtun;
float sigstrength_percentage;
int res;
vtun.index = 0;
res = ioctl(g_radio_fd,VIDIOC_G_TUNER,&vtun);
if(res < 0)
{
printf("Failed to get tunner attributes\n");
return res;
}
printf("-----------------------\n");
printf("Tuner Name: %s\n",vtun.name);
/* TODO: FM driver is not setting V4L2_TUNER_CAP_LOW flag , but its returning vtun.rangelow
* and vtun.rangehigh ranges in HZ . This needs to be corrected in FM driver */
printf(" Low Freq: %d KHz\n",
(unsigned int )((float)vtun.rangelow * 0.0625));
printf(" High Freq: %d KHz\n",
(unsigned int) ((float)vtun.rangehigh * 0.0625));
printf("Audio Mode: %s\n",(vtun.audmode == V4L2_TUNER_MODE_STEREO) ? "STEREO":"MONO");
sigstrength_percentage = ((float)vtun.signal /0xFFFF) * 100;
printf("Signal Strength: %d%%\n",(unsigned int)sigstrength_percentage);
printf("-----------------------\n");
return 0;
}
int fmapp_get_scan_valid_frequencies(void)
{
int ret;
struct v4l2_tuner vtun;
struct v4l2_frequency vf;
struct v4l2_control vctrl;
float freq_multiplicator,start_frq,end_frq,
freq,perc,threshold,divide_by;
long totsig;
unsigned char index;
vtun.index = 0;
ret = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun); /* get frequency range */
if (ret < 0) {
printf("Failed to get frequency range");
return ret;
}
freq_multiplicator = (62.5 * ((vtun.capability & V4L2_TUNER_CAP_LOW)
? 1 : 1000));
divide_by = (vtun.capability & V4L2_TUNER_CAP_LOW) ? 1000000 : 1000;
start_frq = ((float)vtun.rangelow * freq_multiplicator)/divide_by;
end_frq = ((float)vtun.rangehigh * freq_multiplicator)/divide_by;
threshold = FMAPP_ASCAN_SIGNAL_THRESHOLD_PER;
/* Enable Mute */
vctrl.id = V4L2_CID_AUDIO_MUTE;
vctrl.value = FM_MUTE_ON;
ret = ioctl(g_radio_fd,VIDIOC_S_CTRL,&vctrl);
if(ret < 0)
{
printf("Failed to set mute mode\n");
return ret;
}
printf("Auto Scanning..\n");
for(freq=start_frq;freq<=end_frq;freq+=0.1)
{
vf.tuner = 0;
vf.frequency = rint(freq*1000);
ret = ioctl(g_radio_fd, VIDIOC_S_FREQUENCY, &vf); /* tune */
if (ret < 0) {
printf("failed to set freq");
return ret;
}
totsig = 0;
for(index=0;index<FMAPP_ASCAN_NO_OF_SIGNAL_SAMPLE;index++)
{
vtun.index = 0;
ret = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun); /* get info */
if (ret < 0) {
printf("Failed to get frequency range");
return ret;
}
totsig += vtun.signal;
perc = (totsig / (65535.0 * index));
usleep(1);
}
perc = (totsig / (65535.0 * FMAPP_ASCAN_NO_OF_SIGNAL_SAMPLE));
if ((perc*100.0) > threshold)
printf("%2.1f MHz(%d%%)\n",freq,((unsigned short)(perc * 100.0)));
}
/* Disable Mute */
vctrl.id = V4L2_CID_AUDIO_MUTE;
vctrl.value = FM_MUTE_OFF;
ret = ioctl(g_radio_fd,VIDIOC_S_CTRL,&vctrl);
if(ret < 0)
{
printf("Failed to set mute mode\n");
return ret;
}
printf("Scan Completed\n");
return 0;
}
int fmapp_get_rds_onoff(void)
{
struct v4l2_tuner vtun;
int res = 0;
vtun.index = 0;
res = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun);
if(res < 0)
{
printf("Failed to read RDS state\n");
return res;
}
printf("RDS is: %s\n",(vtun.rxsubchans & V4L2_TUNER_SUB_RDS) ? "ON":"OFF");
return 0;
}
void fmapp_rds_decode(int blkno, int byte1, int byte2)
{
static char rds_psn[9];
static char rds_txt[65];
static int rds_pty,ms_code;
static int group,spare,blkc_byte1,blkc_byte2;
switch (blkno) {
case 0: /* Block A */
printf("----------------------------------------\n");
printf("block A - id=%d\n",(byte1 << 8) | byte2);
break;
case 1: /* Block B */
printf("block B - group=%d%c tp=%d pty=%d spare=%d\n",
(byte1 >> 4) & 0x0f,
((byte1 >> 3) & 0x01) + 'A',
(byte1 >> 2) & 0x01,
((byte1 << 3) & 0x18) | ((byte2 >> 5) & 0x07),
byte2 & 0x1f);
group = (byte1 >> 3) & 0x1f;
spare = byte2 & 0x1f;
rds_pty = ((byte1 << 3) & 0x18) | ((byte2 >> 5) & 0x07);
ms_code = (byte2 >> 3)& 0x1;
break;
case 2: /* Block C */
printf("block C - 0x%02x 0x%02x\n",byte1,byte2);
blkc_byte1 = byte1;
blkc_byte2 = byte2;
break;
case 3 : /* Block D */
printf("block D - 0x%02x 0x%02x\n",byte1,byte2);
switch (group) {
case 0: /* Group 0A */
rds_psn[2*(spare & 0x03)+0] = byte1;
rds_psn[2*(spare & 0x03)+1] = byte2;
if ((spare & 0x03) == 0x03)
printf("PSN: %s, PTY: %s, MS: %s\n",rds_psn,
pty_str[rds_pty],ms_code?"Music":"Speech");
break;
case 4: /* Group 2A */
rds_txt[4*(spare & 0x0f)+0] = blkc_byte1;
rds_txt[4*(spare & 0x0f)+1] = blkc_byte2;
rds_txt[4*(spare & 0x0f)+2] = byte1;
rds_txt[4*(spare & 0x0f)+3] = byte2;
/* Display radio text once we get 16 characters */
// if ((spare & 0x0f) == 0x0f)
if (spare > 16)
{
printf("Radio Text: %s\n",rds_txt);
// memset(&rds_txt,0,sizeof(rds_txt));
}
break;
}
printf("----------------------------------------\n");
break;
default:
printf("unknown block [%d]\n",blkno);
}
}
void *rds_thread(void *data)
{
unsigned char buf[600];
int radio_fd;
int ret,index;
struct pollfd pfd;
radio_fd = (int)data;
while(!g_rds_thread_terminate)
{
while(1){
memset(&pfd, 0, sizeof(pfd));
pfd.fd = radio_fd;
pfd.events = POLLIN;
ret = poll(&pfd, 1, 10);
if (ret == 0){
/* Break the poll after RDS data available */
break;
}
}
ret = read(radio_fd,buf,500);
if(ret < 0) {
break;
}
else if( ret > 0)
{
for(index=0;index<ret;index+=3)
fmapp_rds_decode(buf[index+2] & 0x7,buf[index+1],buf[index]);
}
}
/* TODO: Need to conform thread termination.
* below msg is not coming ,have a doubt on thread termination.
* Fix this later. */
printf("RDS thread exiting..\n");
return NULL;
}
int fmapp_set_rds_onoff(unsigned char fmapp_mode)
{
struct v4l2_tuner vtun;
int ret;
static unsigned char rds_mode = FM_RDS_DISABLE;
vtun.index = 0;
ret = ioctl(g_radio_fd, VIDIOC_G_TUNER, &vtun);
if(ret < 0)
{
printf("Failed to get tuner capabilities\n");
return ret;
}
if(rds_mode == FM_RDS_DISABLE) {
vtun.rxsubchans |= V4L2_TUNER_SUB_RDS;
rds_mode = FM_RDS_ENABLE;
} else {
vtun.rxsubchans &= ~V4L2_TUNER_SUB_RDS;
rds_mode = FM_RDS_DISABLE;
}
ret = ioctl(g_radio_fd, VIDIOC_S_TUNER, &vtun);
if(ret < 0)
{
printf("Failed to set rds on/off status\n");
return ret;
}
/* Create rds receive thread once */
if(fmapp_mode == FM_MODE_RX && rds_mode == FM_RDS_ENABLE &&
g_rds_thread_running == 0)
{
g_rds_thread_running = 1;
pthread_create(&g_rds_thread_ptr,NULL,rds_thread,(void *)g_radio_fd);
}
printf("RDS %s\n",g_rds_modes[rds_mode]);
return 0;
}
void fmapp_execute_tx_get_command(char *cmd)
{
switch(cmd[0])
{
case 'f':
fmapp_get_tx_frequency();
break;
case 'e':
fmapp_get_premphasis_filter_mode();
break;
case 'p':
fmapp_get_tx_power_level();
break;
case 'i':
fmapp_get_tx_ant_imp();
break;
default:
printf("unknown command; type 'h' for help\n");
}
}
void fmapp_execute_rx_get_command(char *cmd)
{
switch(cmd[0])
{
case 'f':
fmapp_get_rx_frequency();
break;
case 'r':
fmapp_get_rx_rssi_lvl();
break;
case 't':
fmapp_get_rds_onoff();
break;
case 'v':
fmapp_get_rx_volume();
break;
case 'm':
fmapp_get_rx_mute_mode();
break;
case 'b':
fmapp_get_band();
break;
case 'c':
fmapp_get_rx_af_switch();
break;
case '?':
fmapp_get_rx_rssi_threshold();
break;
#if 0
case 'd':
fmapp_get_rfmute(fm_snd_ctrl);
break;
case 'z':
fmapp_get_rds_operation_mode(fm_snd_ctrl);
break;
#endif
case 's':
fmapp_get_stereo_mono_mode();
break;
#if 0
case 'e':
fmapp_get_rx_deemphasis_filter_mode(fm_snd_ctrl);
break;
#endif
case 'a':
fmapp_get_rx_tunner_attributes();
break;
#if 0
case 'n':
fmapp_get_scan_valid_frequencies();
break;
#endif
default:
printf("unknown command; type 'h' for help\n");
}
}
void fmapp_execute_rx_other_command(char *cmd)
{
switch(cmd[0])
{
#if 0
case 'p':
fmapp_change_rx_power_mode(fm_snd_ctrl);
break;
#endif
case 'f':
fmapp_set_rx_frequency(cmd+1);
break;
case 't':
fmapp_set_rds_onoff(FM_MODE_RX);
break;
case '+':
fmapp_rx_increase_volume();
break;
case '-':
fmapp_rx_decrease_volume();
break;
case 'v':
fmapp_set_rx_volume(cmd+1,FMAPP_INTERACTIVE,0);
break;
case 'm':
fmapp_set_rx_mute_mode();
break;
case '<':
fmapp_rx_seek(FM_SEARCH_DIRECTION_DOWN);
break;
case '>':
fmapp_rx_seek(FM_SEARCH_DIRECTION_UP);
break;
case 'b':
fmapp_set_band(cmd+1);
break;
case 'h':
fmapp_display_rx_menu();
break;
case 'c':
fmapp_set_rx_af_switch(cmd+1);
break;
case '?':
fmapp_set_rx_rssi_threshold(cmd+1);
break;
#if 0
case 'd':
fmapp_set_rfmute(fm_snd_ctrl);
break;
case 'z':
fmapp_set_rds_operation_mode(fm_snd_ctrl);
break;
#endif
case 's':
fmapp_set_stereo_mono_mode();
break;
#if 0
case 'e':
fmapp_set_rx_deemphasis_filter_mode(fm_snd_ctrl);
break;
#endif
case 'A':
fmapp_start_audio();
break;
}
}
void fmapp_execute_tx_other_command(char *cmd)
{
switch(cmd[0])
{
case 'f':
fmapp_set_tx_frequency(cmd+1);
break;
case 'e':
fmapp_set_premphasis_filter_mode(cmd+1);
break;
case 'p':
fmapp_set_tx_power_level(cmd+1);
break;
case 'i':
fmapp_set_tx_ant_imp(cmd+1);
break;
case '1':
fmapp_set_tx_rds_radio_text();
break;
case '2':
fmapp_set_tx_rds_radio_ps_name();
break;
case '3':
fmapp_set_tx_rds_radio_pi_code(cmd+1);
break;
case '4':
fmapp_set_tx_rds_radio_pty(cmd+1);
break;
case '5':
fmapp_set_tx_rds_radio_af(cmd+1);
break;
case 'h':
fmapp_display_tx_menu();
break;
}
}
/* Switch to RX mode before accepting user commands for RX */
void fmapp_execute_rx_command(void)
{
char cmd[100];
struct v4l2_tuner vtun;
int ret;
vtun.index = 0;
vtun.audmode = V4L2_TUNER_MODE_STEREO;
vtun.rxsubchans = V4L2_TUNER_SUB_RDS;
ret = ioctl(g_radio_fd, VIDIOC_S_TUNER, &vtun);
if(ret < 0)
{
printf("Failed to set RX mode\n");
return;
}
printf("Switched to RX menu\n");
printf("type 'h' for help\n");
while(1)
{
fgets(cmd, sizeof(cmd), stdin);
switch(cmd[0]) {
case 'g':
fmapp_execute_rx_get_command(cmd+1);
break;
case 'q':
printf("quiting RX menu\n");
if (pcm_p != NULL && pcm_c != NULL)
fmapp_start_audio();
return;
default:
fmapp_execute_rx_other_command(cmd);
break;
}
}
}
void fmapp_execute_tx_command(void)
{
char cmd[100];
struct v4l2_modulator vmod;
int ret;
vmod.index = 0;
vmod.txsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS;
ret = ioctl(g_radio_fd, VIDIOC_S_MODULATOR, &vmod);
if(ret < 0)
{
printf("Failed to set TX mode\n");
return;
}
printf("Switched to TX menu\n");
printf("type 'h' for help\n");
while(1)
{
fgets(cmd, sizeof(cmd), stdin);
switch(cmd[0]) {
case 'g':
fmapp_execute_tx_get_command(cmd+1);
break;
case 'q':
printf("quiting TX menu\n");
return;
default:
fmapp_execute_tx_other_command(cmd);
break;
}
}
}
int fmapp_read_anddisplay_capabilities(void)
{
struct v4l2_capability cap;
int res;
res = ioctl(g_radio_fd,VIDIOC_QUERYCAP,&cap);
if(res < 0)
{
printf("Failed to read %s capabilities\n",DEFAULT_RADIO_DEVICE);
return res;
}
if((cap.capabilities & V4L2_CAP_RADIO) == 0)
{
printf("%s is not radio devcie",DEFAULT_RADIO_DEVICE);
return -1;
}
printf("\n***%s Info ****\n",DEFAULT_RADIO_DEVICE);
printf("Driver : %s\n",cap.driver);
printf("Card : %s\n",cap.card);
printf("Bus : %s\n",cap.bus_info);
printf("Capabilities : 0x%x\n",cap.capabilities);
return 0;
}
static void sig_handler()
{
if(g_rds_thread_running)
g_rds_thread_terminate = 1;
close(g_radio_fd);
printf("Terminating..\n\n");
exit(1);
}
int main()
{
char choice[100];
char exit_flag;
int ret;
struct sigaction sa;
printf("** TI Kernel Space FM Driver Test Application **\n");
printf("Opening device '%s'\n",DEFAULT_RADIO_DEVICE);
g_radio_fd = open(DEFAULT_RADIO_DEVICE, O_RDWR);
if(g_radio_fd < 0)
{
printf("Unable to open %s \nTerminating..\n",DEFAULT_RADIO_DEVICE);
return 0;
}
ret = fmapp_read_anddisplay_capabilities();
if(ret< 0)
{
close(g_radio_fd);
return ret;
}
/* to handle ctrl + c and kill signals */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
exit_flag = 1;
while(exit_flag)
{
printf("1 FM RX\n");
printf("2 FM TX\n");
printf("3 Exit\n");
fgets(choice, sizeof(choice), stdin);
switch(atoi(choice))
{
case 1: /* FM RX */
fmapp_execute_rx_command();
break;
case 2: /* FM TX */
fmapp_execute_tx_command();
break;
case 3:
printf("Terminating..\n\n");
exit_flag = 0;
break;
default:
printf("Invalid choice , try again\n");
continue;
}
}
if(g_rds_thread_running)
g_rds_thread_terminate = 1; // Terminate RDS thread
close(g_radio_fd);
return 0;
}