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