/*
 *  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;
}