V4L FM Radio Driver for CG2900 Hemant Gupta
hemant.gupta@stericsson.com
2010 ST-Ericsson Connectivity This documentation is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 For more details see the file COPYING in the source distribution of Linux.
Introduction This documentation describes the functions provided by the CG2900 FM Driver. Getting Started There are no special compilation flags needed to build the CG2900 FM Driver. There must be coeffecient and firmware files that match the used chip version inside the firmware folder. The files: cg2900_fm_bt_src_coeff_info.fw.org cg2900_fm_ext_src_coeff_info.fw.org cg2900_fm_fm_coeff_info.fw.org cg2900_fm_fm_prog_info.fw.org handle the mapping between chip version and correct firmware files (firmware and coeffecient files). The necessary firmware and coeffecient files should be placed with the extension .fw.org. Note that there is a limitation in the Kernel firmware system regarding name length of a file.
Basic Tutorial To enable the CG2900 FM Driver using KConfig go to Device Drivers -> Multimedia devices and enable the following: Video For Linux Enable Video For Linux API 1 compatible Layer Radio Adapters Radio Adapter -> ST-Ericsson CG2900 FM Radio support Select the driver as built in kernel object.
Concepts The CG2900 FM driver acts as an interface between Video4Linux and CG2900-Protocol Driver. It configures the FM chip in FM Rx or FM Tx mode. It also sends the unformatted RDS data to the application for decoding while in FM rx mode and sends the formatted RDS data to FM Chip while in Tx mode. FM Driver Working In order to send and receive data on an H:4 channel, the FM Driver opens the channel by registering with the CG2900 Protocol driver. After this the FM driver encapsulates the user operation into specific HCI comamnds and sends that data to the CG2900 Connectivity Controller and waits till the response for the previous command is received. FM Driver in this way maintains the flow control. Tasks
Switch On FM FM specific tasks Switching On FM For switching on FM the character device /dev/radio0 should be opened from user space. This sets the FM Radio in Idle mode. For configuring the FM Radio in Rx or Tx mode the IOCTL's VIDIOC_S_TUNER and VIDIOC_S_MODULATOR respectively. int fd; fd = open("/dev/radio0", O_RDONLY); if(fd < 0) { printf("open:error!!!\n"); goto err; }
Switch Off FM Switching Off FM For switching OFF FM the character device /dev/radio0 should be closed from user space. if(fd >= 0) close(fd);
Switching To FM Rx Mode Switching To FM Rx Mode For switching on FM Rx mode the IOCTL VIDIOC_S_TUNER should be called with appropriate parameters. memset(&tuner, 0, sizeof(tuner)); tuner.index = 0; tuner.rxsubchans |= V4L2_TUNER_SUB_STEREO; if (ioctl(fd, VIDIOC_S_TUNER, &tuner) < 0) { printf("VIDIOC_S_TUNER:error!!\n"); return;
Switching To FM Tx Mode Switching To FM Tx Mode For switching on FM Tx mode the IOCTL VIDIOC_S_MODULATOR should be called with appropriate parameters. memset(&modulator, 0, sizeof(modulator)); modulator.index = 0; modulator.txsubchans |= V4L2_TUNER_SUB_STEREO; if (ioctl(fd, VIDIOC_S_MODULATOR, &modulator) < 0) { printf("VIDIOC_S_MODULATOR:error!!\n"); return;
Standby Making the FM Radio go in Standby Mode For making the FM Radio go in Standby mode, the IOCTL VIDIOC_S_CTRL should be used. The id of the v4l2_control structure should be set to V4L2_CID_CG2900_RADIO_CHIP_STATE and the value of v4l2_control structure should be set as V4L2_CG2900_RADIO_STANDBY. struct v4l2_control sctrl; int ret; sctrl.id = V4L2_CID_CG2900_RADIO_CHIP_STATE; sctrl.value = V4L2_CG2900_RADIO_STANDBY; ret = ioctl(fd, VIDIOC_S_CTRL, &sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); }
Powering Up FM From Standby Mode Powering Up FM Radio from Standby Mode For powering up FM radio again from standby mode, the IOCTL VIDIOC_S_CTRL should be used. The id of the v4l2_control structure should be set to V4L2_CID_CG2900_RADIO_CHIP_STATE and the value of v4l2_control structure should be set as V4L2_CG2900_RADIO_POWERUP. struct v4l2_control sctrl; int ret; sctrl.id = V4L2_CID_CG2900_RADIO_CHIP_STATE; sctrl.value = V4L2_CG2900_RADIO_POWERUP; ret = ioctl(fd, VIDIOC_S_CTRL, &sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); }
Tune Channel Tune to a particular station for tuning to a particular station, the IOCTL VIDIOC_S_FREQUENCY should be used. The frequency of the v4l2_frequency structure should be converted to V4L2 format. struct v4l2_frequency freq; int ret; /* Convert frequency in Hz to V4L2 Format */ freq.frequency = (frequency * 2)/ 125; ret = ioctl(fd, VIDIOC_S_FREQUENCY, &freq); if (ret < 0) { printf("VIDIOC_S_FREQUENCY:error!!\n"); }
Get Tuned Channel Get the Currently tuned Station Frequncy for tuning to a particular station, the IOCTL VIDIOC_G_FREQUENCY should be used. The frequency returned in the v4l2_frequency structure would be in V4L2 format. struct v4l2_frequency freq; int ret; ret = ioctl(fd, VIDIOC_G_FREQUENCY, &freq); if (ret < 0) { printf("VIDIOC_G_FREQUENCY:error!!\n"); *frequency = 0; return; } /* Convert frequency to Hz from V4L2 Format */ *frequency = (freq.frequency * 125)/2;
Retreive Signal Strength Retreive Signal Strength For retreiving the Signal strength of the currently tuned channel in FM Rx mode, IOCTL VIDIOC_G_TUNER should be called. The current signal strength would be represented by the parameter signal of the v4l2_tuner strucure. void get_signal_strength(int *rssi) { struct v4l2_tuner tuner; int ret; memset(&tuner, 0, sizeof(tuner)); tuner.index = 0; ret = ioctl(fd, VIDIOC_G_TUNER, &tuner); if (ret < 0) { printf("VIDIOC_G_TUNER:error!!\n"); *rssi = 0; return; } *rssi = tuner.signal; } Note: Currently the retrieved signal strength is in decimals and not in "dBuV", proper external conversion required.
Band Scan Band Scan For doing a band scan, ie search for all available stations in the entire FM band, IOCTL VIDIOC_S_CTRL should be used with parameter id of the v4l2_control structure should be set to V4L2_CID_CG2900_RADIO_BANDSCAN and the value of v4l2_control structure should be set as V4L2_CG2900_RADIO_BANDSCAN_START. If the IOCTL returns successfully, a common thread (which should have been created at the start of the application and is already polling to FM driver for multiple other interrupts including events related to Block Scan and Search Frequency operation) which polls with an infinite timeout iteratively until the end of the user-space application, when poll in one iteration is complete. When poll is complete, thread will make an IOCTL call with VIDIOC_G_EXT_CTRLS with parameter id set to V4L2_CID_CG2900_RADIO_GET_INTERRUPT and the data structure FmInterrupt.controls->string associated with V4L2_CID_CG2900_RADIO_GET_INTERRUPT shall contain the interrupt retrieved from FMD. Interrupts received in this manner from FMD can be any of the following. V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED For Band Scan it shall be V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETE, an appropriate handler should be then called from thethread to retrieve the found stations along with RSSI using the IOCTL VIDIOC_G_EXT_CTRLS, this IOCTL should be used with parameters as described in example code. Note that the common thread for capturing synchronous as well asynchronous events used here is FmInterruptMonitoringThread. It shall be used in other sections i.e. Block Scan, Cancel Scan, Search Frequency and Mono Stereo Transition. void Band_Scan() { struct v4l2_control sctrl int ret; sctrl.id = V4L2_CID_CG2900_RADIO_BANDSCAN; sctrl.value = V4L2_CG2900_RADIO_BANDSCAN_START; ret = ioctl(fd, VIDIOC_S_CTRL, &sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); } pthread_create(&fmScanThread, NULL, FmScanThread, NULL); } static void *FmInterruptMonitoringThread(void *param) { struct v4l2_ext_controls FmInterrupt; struct pollfd pollFd; long * p = NULL; int index, ret, count = 0; int interrupt, interrupt_reason; int err; while(closeApp) { pollFd.fd = fd; pollFd.events = POLLRDNORM; /* wait infinitely for interrupt */ timeout = -1; ret = poll(&pollFd, 1, timeout); if(!closeApp) break; if(ret) { if(pollFd.revents & POLLRDNORM) { /* Get the interrupt */ FmInterrupt.count = 0; FmInterrupt.ctrl_class = V4L2_CTRL_CLASS_USER; FmInterrupt.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); if(!FmInterrupt.controls) goto error; FmInterrupt.controls->id = V4L2_CID_CG2900_RADIO_GET_INTERRUPT; FmInterrupt.controls->size = 2; FmInterrupt.controls->string = (int *)malloc(sizeof(int) * FmInterrupt.controls->size); interrupt_buffer_pointer = FmInterrupt.controls->string; if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &FmInterrupt) < 0) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); ret_val = -1; goto error_free_ext_control_string; } if(!ret_val) { interrupt = *interrupt_buffer_pointer; interrupt_reason = *(interrupt_buffer_pointer + 1); printf("Interrupt = %d, , Result = %d\n", interrupt, interrupt_reason); if(interrupt_reason == 0) { switch(interrupt) { case V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED: /* Band Scan Completed */ HandleBandScanCompletion(); otherOperationInProgress = 0; break; } } } error_free_ext_control_string: free(FmInterrupt.controls->string); error_free_ext_control_control: free(FmInterrupt.controls); error: otherOperationInProgress = 0; } else { printf ("FmInterruptMonitoringThread : poll returned = %d\n", ret); } } } return 0; } static void HandleBandScanCompletion() { struct v4l2_ext_controls scanResult; long * band_scan_pointer = NULL; int err; int index, ret, count = 0; /* Get the Number Of Channels */ scanResult.count = 0; scanResult.ctrl_class = V4L2_CTRL_CLASS_USER; scanResult.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); if(!scanResult.controls) goto done; scanResult.controls->id = V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS; scanResult.controls->size = 0; scanResult.controls->string = NULL; err = ioctl(fd, VIDIOC_G_EXT_CTRLS, & scanResult); if (err < 0 & & errno != ENOSPC) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); goto error_free_ext_control_control; } if(scanResult.controls->size > 0 ) { scanResult.controls->string = (long *)malloc(sizeof(long) * 2 * scanResult.controls->size ); band_scan_pointer = scanResult.controls->string; printf("\n\n\n==================================\n"); printf("\nNumber of Channels Found = %d \n", scanResult.controls->size); printf("\n==================================\n"); if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &scanResult) < 0) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); goto error_free_ext_control_string; } printf("\n================================\n"); printf("\nSNo. Frequency(MHz) RSSI\n"); printf("\n================================\n"); for (index = 0, count = 0; index < scanResult.controls->size; index ++, count +=2) { printf("%d %d.%d %d\n", index + 1, MEGAHRTZ((*(band_scan_pointer +count + 0) * 125) / 2), *(band_scan_pointer + count + 1)); } printf("\n================================\n"); error_free_ext_control_string: free(band_scan_pointer); } else if(scanResult.controls->size == 0) { printf("\nNo channels found during scanning!!\n"); } error_free_ext_control_control: free(scanResult.controls); done: otherOperationInProgress = 0; } Note: Currently the retrieved signal strength is in decimals and not in "dBuV", proper external conversion required.
Block Scan Block Scan The Block Scan functionality will take two inputs, start and stop frequency (V4L2 compliance) and enables the host to scan all channels with in that range for RSSI values. The measured channels will be stored in a list in order of channel number and the block scan feature to identify "empty" channels for transmission. And for doing a block scan, IOCTL VIDIOC_S_EXT_CTRL with parameter id of the v4l2_control structure should be set to V4L2_CID_FM_RADIO_BLOCKSCAN_START. If the IOCTL returns successfully, a common thread (which should have been created at the start of the application and is already polling to FM driver for multiple other interrupts including events related to Band Scan and Search Frequency operation) which polls with an infinite timeout iteratively until the end of the user-space application, when poll in one iteration is complete. When poll is complete, thread will make an IOCTL call with VIDIOC_G_EXT_CTRLS with parameter id set to V4L2_CID_CG2900_RADIO_GET_INTERRUPT and the data structure FmInterrupt.controls->string associated with V4L2_CID_CG2900_RADIO_GET_INTERRUPT shall contain the interrupt retrieved from FMD. Interrupts received in this manner from FMD can be any of the following. V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED For Block Scan it shall be V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED, an appropriate handler should be then called from thethread to retrieve the found stations along with RSSI using the IOCTL VIDIOC_G_EXT_CTRLS, this IOCTL should be used with parameters as described in example code. Note that the common thread for capturing synchronous as well asynchronous events used here is FmInterruptMonitoringThread. It shall be used in other sections i.e. Band Scan, Cancel Scan, Search Frequency and Mono Stereo Transition. When poll is complete, the found stations along with RSSI should be retrieved using the IOCTL VIDIOC_G_EXT_CTRLS should be used with parameters as described in example code. void Block_Scan() { struct v4l2_ext_controls ext_ctrl; long *p = NULL; int index; int ret_val; if(1 == mode) { otherOperationInProgress = 1; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_USER; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_CG2900_RADIO_BLOCKSCAN_START; ext_ctrl.controls->size = 2; ext_ctrl.controls->string = (long *)malloc(sizeof(long) * ext_ctrl.controls->size); p = ext_ctrl.controls->string; *p = (StartFreq * 2)/ 125; *(p + 1) = (EndFreq * 2)/ 125;; if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &ext_ctrl) < 0) printf("APP_BlockScanStart:VIDIOC_S_EXT_CTRLS:error!!\n"); free(ext_ctrl.controls->string); free(ext_ctrl.controls); pthread_create(&fmBlockScanThread, NULL, FmBlockScanThread, NULL); } static void *FmInterruptMonitoringThread(void *param) { struct v4l2_ext_controls FmInterrupt; struct pollfd pollFd; long * p = NULL; int index, ret, count = 0; int interrupt, interrupt_reason; int err; while(closeApp) { pollFd.fd = fd; pollFd.events = POLLRDNORM; /* wait infinitely for interrupt */ timeout = -1; ret = poll(&pollFd, 1, timeout); if(!closeApp) break; if(ret) { if(pollFd.revents & POLLRDNORM) { /* Get the interrupt */ FmInterrupt.count = 0; FmInterrupt.ctrl_class = V4L2_CTRL_CLASS_USER; FmInterrupt.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); if(!FmInterrupt.controls) goto error; FmInterrupt.controls->id = V4L2_CID_CG2900_RADIO_GET_INTERRUPT; FmInterrupt.controls->size = 2; FmInterrupt.controls->string = (int *)malloc(sizeof(int) * FmInterrupt.controls->size); interrupt_buffer_pointer = FmInterrupt.controls->string; if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &FmInterrupt) < 0) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); ret_val = -1; goto error_free_ext_control_string; } if(!ret_val) { interrupt = *interrupt_buffer_pointer; interrupt_reason = *(interrupt_buffer_pointer + 1); printf("Interrupt = %d, , Result = %d\n", interrupt, interrupt_reason); if(interrupt_reason == 0) { switch(interrupt) { case V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED: /* Block Scan Completed */ HandleBlockScanCompletion(); otherOperationInProgress = 0; break; } } } error_free_ext_control_string: free(FmInterrupt.controls->string); error_free_ext_control_control: free(FmInterrupt.controls); error: otherOperationInProgress = 0; } else { printf ("FmInterruptMonitoringThread : poll returned = %d\n", ret); } } } return 0; } static void HandleBlockScanCompletion() { struct v4l2_ext_controls blockscanResult; long * block_scan_pointer = NULL; int index, ret; int err; int current_grid = -1; FILE *fp; long start_freq = StartFreq; long next_freq_offset = 0; fp = fopen("/sys/module/radio_cg2900/parameters/grid", "r"); if(fp != NULL) { /* Retrieve the currently set grid to determine the next channel is 50 Khz, 100 Khz or 200 Khz apart */ fscanf(fp, "%d", &current_grid); fclose(fp); } if(current_grid == 0) { next_freq_offset = 50000; } else if (current_grid == 1) { next_freq_offset = 100000; } else if (current_grid == 2) { next_freq_offset = 200000; } /* Get the Number Of Channels */ blockscanResult.count = 0; blockscanResult.ctrl_class = V4L2_CTRL_CLASS_USER; blockscanResult.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); if(!blockscanResult.controls) goto done; blockscanResult.controls->id = V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS; blockscanResult.controls->size = 0; blockscanResult.controls->string = NULL; err = ioctl(fd, VIDIOC_G_EXT_CTRLS, &blockscanResult); if (err < 0 && errno != ENOSPC) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); goto error_free_ext_control_control; } if(blockscanResult.controls->size > 0) { blockscanResult.controls->string = (long *)malloc(sizeof(long) * blockscanResult.controls->size ); block_scan_pointer = blockscanResult.controls->string; if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &blockscanResult) < 0) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); goto error_free_ext_control_string; } printf("\n================================\n"); printf("\nMHz. RSSI\n"); printf("\n================================\n"); for (index = 0; index < blockscanResult.controls->size; index ++) { printf("%d.%d %d\n", MEGAHRTZ(start_freq), *(block_scan_pointer + index)); start_freq += next_freq_offset; } printf("\n================================\n"); error_free_ext_control_string: free(block_scan_pointer); } else if(blockscanResult.controls->size == 0) { printf("\nNo channels found during Block Scan!!\n"); } error_free_ext_control_control: free(blockscanResult.controls); done: otherOperationInProgress = 0; } Note: Currently the retrieved signal strength is in decimals and not in "dBuV", proper external conversion required.
Cancel Scan Cancel Scan/Seek This is used for stopping an active Band Scan, Seek operation and Block Scan. IOCTL VIDIOC_S_CTRL should be used with parameter id of the v4l2_control structure. For Eg incase of Band scan the parameter id should be set to V4L2_CID_CG2900_RADIO_BANDSCAN and the value of v4l2_control structure should be set as V4L2_CG2900_RADIO_BANDSCAN_STOP. The example thread shown in following code snippet FmInterruptMonitoringThread shall have already been started as mentioned in Band Scan and Block sections, it shall receive an asynchronous event V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED. struct v4l2_control sctrl; int ret; sctrl.id = V4L2_CID_CG2900_RADIO_BANDSCAN; sctrl.value = V4L2_CG2900_RADIO_BANDSCAN_STOP; ret = ioctl(fd, VIDIOC_S_CTRL, &sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); } static void *FmInterruptMonitoringThread(void *param) { struct v4l2_ext_controls FmInterrupt; struct pollfd pollFd; long * p = NULL; int index, ret, count = 0; int interrupt, interrupt_reason; int err; while(closeApp) { pollFd.fd = fd; pollFd.events = POLLRDNORM; /* wait infinitely for interrupt */ timeout = -1; ret = poll(&pollFd, 1, timeout); if(!closeApp) break; if(ret) { if(pollFd.revents & POLLRDNORM) { /* Get the interrupt */ FmInterrupt.count = 0; FmInterrupt.ctrl_class = V4L2_CTRL_CLASS_USER; FmInterrupt.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); if(!FmInterrupt.controls) goto error; FmInterrupt.controls->id = V4L2_CID_CG2900_RADIO_GET_INTERRUPT; FmInterrupt.controls->size = 2; FmInterrupt.controls->string = (int *)malloc(sizeof(int) * FmInterrupt.controls->size); interrupt_buffer_pointer = FmInterrupt.controls->string; if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &FmInterrupt) < 0) { printf("VIDIOC_G_EXT_CTRLS:error!!\n"); ret_val = -1; goto error_free_ext_control_string; } if(!ret_val) { interrupt = *interrupt_buffer_pointer; interrupt_reason = *(interrupt_buffer_pointer + 1); printf("Interrupt = %d, , Result = %d\n", interrupt, interrupt_reason); if(interrupt_reason == 0) { switch(interrupt) { case V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED: /* Scan/Search/Block Scan Cancelled */ printf(" Scan cancelled by user\n"); otherOperationInProgress = 0; break; } } } error_free_ext_control_string: free(FmInterrupt.controls->string); error_free_ext_control_control: free(FmInterrupt.controls); error: otherOperationInProgress = 0; } else { printf ("FmInterruptMonitoringThread : poll returned = %d\n", ret); } } } return 0; }
RDS Receive RDS Receive For enabling/disabling RDS for FM Rx, IOCTL VIDIOC_S_TUNER should be used with parameter rxsubchans of the v4l2_tuner structure set to V4L2_TUNER_SUB_RDS if rds needs to be enabled and the same value must not be set in case rds is to be disabled. Once RDS data is available, the appplication would be signalled waiting on poll. If the Interrupt retrieved using V4L2_CID_CG2900_RADIO_GET_INTERRUPT is V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED, RDS data can be retrieved using the read() functionality from the CG2900 FM driver. The RDS data received from FM Driver should be parsed in user space to retrive RDS information i.e Radio Text, Program Service Name, Program Identification, Program Type, Alternate Frequency, etc. void rds_rx_set(bool enable_rds) { struct v4l2_tuner tuner; int ret; memset(&tuner, 0, sizeof(tuner)); tuner.index = 0; if(enable_rds) tuner.rxsubchans |= V4L2_TUNER_SUB_RDS; else tuner.rxsubchans & = ~V4L2_TUNER_SUB_RDS; ret = ioctl(fd, VIDIOC_S_TUNER, &tuner); if (ret < 0) { printf("VIDIOC_S_TUNER:error!!\n"); } }
AF Update and Switching AF Update & Switching Alternate Frequency (AF) Handling needs to be done in user space. The application should use the AF RDS group data to compose a list of AFs when tuned to a new channel. When the reception of the currently tuned frequency falls below a set threshold, it can decide to switch to one of the alternative frequencies for this channel. The application can perform an AF Update, which returns the RSSI value for all or some of the channel's AFs. Thus allowing the hardware to switch to the AF with the highest RSSI. The AF Update could be designed to stop as soon as it finds an AF with an acceptable RSSI level. In the event that all the AF RSSI values are lower than the base channel, the AF Switch would not be necessary. To know the RSSI of the alternative frequencies (V4L2 compliance), the application can use the IOCTL VIDIOC_S_CTRL with parameter id set to V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_START, and the parameter value be set as the frequency in Hz for a channel from the AF List. If this call returns successfully, the RSSI of the frequency can then be retrieved. Using IOCTL VIDIOC_G_CTRL, with the parameter id set to V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_GET_RESULT, and the output parameter value will contain the RSSI of the AF frequency. If it is still deemed necessary to switch channels, the next step is then to switch to an alternative frequency in the AF list. This can be done using the IOCTL VIDIOC_S_EXT_CTRLS, with: Parameter id set to V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START Parameter size set to 2 Parameters filled as below (string field of the parameter) Control class parameter set to V4L2_CTRL_CLASS_USER The AF switch frequency in Hz Expected PI code The application can check if the AF switch succeeded or not using the IOCTL VIDIOC_G_CTRL, with parameter id set to V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT, and the output parameter value will contain the AF switch conclusion. The example code below illustrates both the aforementioned functionalities. void PerformAFUpdate(long AF_Frequency, int *AF_Rssi) { struct v4l2_control sctrl, gctrl; int ret; sctrl.id = V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_START; sctrl.value = AF_Frequency; ret = ioctl(fd, VIDIOC_S_CTRL, & sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); } gctrl.id = V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_GET_RESULT; ret = ioctl(fd, VIDIOC_G_CTRL, & gctrl); if (ret < 0) { printf("VIDIOC_G_CTRL:error!!\n"); } *AF_Rssi = gctrl.value; } void PerformAFSwitch(long AF_BestFrequency, int AF_ExpectedPI, int *AF_SwitchConclusion) { struct v4l2_control gctrl; struct v4l2_ext_controls ext_ctrl; int ret; int conclusion; long freq; long *p = NULL; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_USER; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START; ext_ctrl.controls->size = 2; ext_ctrl.controls->string = (long *)malloc(sizeof(long) * ext_ctrl.controls->size); p = ext_ctrl.controls->string; *p = (AF_BestFrequency * 2)/ 125; *(p+1) = (long)AF_ExpectedPI; ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRLS:error!!\n"); } free(ext_ctrl.controls->string); free(ext_ctrl.controls); gctrl.id = V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT; ret = ioctl(fd, VIDIOC_G_CTRL, & gctrl); if (ret < 0) { printf("VIDIOC_G_CTRL:error!!\n"); } *AF_SwitchConclusion = gctrl.value; } Note: For V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT returned values are: -1 AF Switch failed, the AF-RSSI was too low. -2 AF Switch failed, the AF-PI Doesn't correlate. -3 AF Switch failed, the AF-RDS SYNC Lost.
RDS Transmit RDS Transmit For enabling/disabling RDS for FM Tx, IOCTL VIDIOC_S_MODULATOR should be used with parameter txsubchans of the v4l2_modulator structure set to V4L2_TUNER_SUB_RDS if rds needs to be enabled and the same value must not be set in case rds is to be disabled. For Trasmitting RDS Data like PI, PTY, PSN, RT, VIDIOC_S_EXT_CTRLS IOCTL should be used with the id set to V4L2_CID_RDS_TX_PI, V4L2_CID_RDS_TX_PTY, V4L2_CID_RDS_TX_PS_NAME and V4L2_CID_RDS_TX_RADIO_TEXT respectively. Below example shows how to transmit various RDS functionalities. void rds_tx_set(bool enable_rds) { struct v4l2_modulator modulator; int ret; memset(&modulator, 0, sizeof(modulator)); modulator.index = 0; if(enable_rds) modulator.txsubchans |= V4L2_TUNER_SUB_RDS; else modulator.txsubchans & = ~V4L2_TUNER_SUB_RDS; ret = ioctl(fd, VIDIOC_S_MODULATOR, & modulator); if (ret < 0) { printf("VIDIOC_S_MODULATOR:error!!\n"); } } void rds_tx_PI(void *value) { struct v4l2_ext_controls ext_ctrl; int ret; unsigned short *pi_code = (unsigned short *)value; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_FM_TX; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_RDS_TX_PI; ext_ctrl.controls->size = 0; ext_ctrl.controls->string = NULL; ext_ctrl.controls->value = *pi_code; ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRLS:error!!\n"); } free(ext_ctrl.controls); } void rds_tx_PTY(void *value) { struct v4l2_ext_controls ext_ctrl; int ret; unsigned short *pty_code = (unsigned short *)value; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_FM_TX; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_RDS_TX_PTY; ext_ctrl.controls->size = 0; ext_ctrl.controls->string = NULL; ext_ctrl.controls->value = *pty_code; ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRLS:error!!\n"); } free(ext_ctrl.controls); } void rds_tx_PSN(void *value) { struct v4l2_ext_controls ext_ctrl; int ret; char *psn = (char *)value; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_FM_TX; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_RDS_TX_PS_NAME; ext_ctrl.controls->size = strlen(psn); ext_ctrl.controls->value = 0; ext_ctrl.controls->string = (char *)malloc(ext_ctrl.controls->size); memcpy(ext_ctrl.controls->string, psn, ext_ctrl.controls->size); ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRLS:error!!\n"); } free(ext_ctrl.controls->string); free(ext_ctrl.controls); } void rds_tx_RT(void *value) { struct v4l2_ext_controls ext_ctrl; int ret; char *radio_text = (char *)value; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_FM_TX; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_RDS_TX_RADIO_TEXT; ext_ctrl.controls->size = strlen(radio_text); ext_ctrl.controls->value = 0; ext_ctrl.controls->string = (char *)malloc(ext_ctrl.controls->size); memcpy(ext_ctrl.controls->string, radio_text, ext_ctrl.controls->size); ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRLS:error!!\n"); } free(ext_ctrl.controls->string); free(ext_ctrl.controls); } Note: RDS default parameters Programme Identification code[PI]: Default value -> 0x1234 Programme Type[PTY]: Default value -> OTHER_MUSIC Music/Speech switch[M/S]: Default value -> Music Programme Service name[PS]: Default value -> FM Xmit Radio text[RT]: Default value -> Default Radio Text
Test Tone Generation 1. Enabling-Disabling test tone 1. For setting the test tone status, application should use IOCTL VIDIOC_S_CTRL with its parameter id set to V4L2_CID_CG2900_RADIO_TEST_TONE_GENERATOR_SET_STATUS and parameter value set to any of the following : Turn off test tone - use V4L2_CG2900_RADIO_TEST_TONE_GEN_OFF Turn on test tone - use V4L2_CG2900_RADIO_TEST_TONE_GEN_ON_WO_SRC Turn on with sample rate correction - use V4L2_CG2900_RADIO_TEST_TONE_GEN_ON_W_SRC void SetTestToneStatus(int state) { struct v4l2_control sctrl; int ret; sctrl.id = V4L2_CID_CG2900_RADIO_TEST_TONE_GENERATOR_SET_STATUS; sctrl.value = state; ret = ioctl(fd, VIDIOC_S_CTRL, & sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); } } 2. Test Tone Connect 2. The internal test tone can be used following modes: FMR (receiver mode) and FMT (transmitter mode). Each of the waves, or the sum of them, can be used as audio source for the receiver audio outputs, or for the transmitter audio input. This can be done by means of command "Test Tone Connect". For the receiver, all available audio outputs will be connected to the tone generator. After switching FM to another mode, the command "Test Tone Connect" must be executed again to set up a connection in the new mode. The IOCTL VIDIOC_S_EXT_CTRLS is used to perform Test Tone Connect operation, with following parameters: Parameter id set to V4L2_CID_CG2900_RADIO_TEST_TONE_CONNECT Parameter size set to 2 Control class parameter set to V4L2_CTRL_CLASS_USER Parameters value filled as below in code snippet(string field of the parameter) First byte of Parameter value shall contain connect parameter for left audio output Second byte of Parameter value shall contain connect parameter forright audio output Value of either of Parameter values (for left or right audio outputs)can assume values: V4L2_CG2900_RADIO_TEST_TONE_NORMAL_AUDIO - Normal Audio V4L2_CG2900_RADIO_TEST_TONE_ZERO - Zero V4L2_CG2900_RADIO_TEST_TONE_TONE_1 - Tone_1 V4L2_CG2900_RADIO_TEST_TONE_TONE_2 - Tone_2 V4L2_CG2900_RADIO_TEST_TONE_TONE_SUM - Tone_Sum void TestToneConnect(u8 left_audio_mode, u8 right_audio_mode) { u8 *test_tone_connect_ptr = NULL; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_USER; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_CG2900_RADIO_TEST_TONE_CONNECT; ext_ctrl.controls->size = 2; ext_ctrl.controls->string = (u8 *)malloc(sizeof(u8) * ext_ctrl.controls->size); test_tone_connect_ptr = ext_ctrl.controls->string; *(test_tone_connect_ptr) = left_audio_mode; *(test_tone_connect_ptr + 1) = right_audio_mode; ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRL:error!!\n"); } } 3. Test Tone Set Parameters 3. The tone generator is capable of generating two different sine waves with adjustable offset, amplitude, phase offset and frequency. These properties can be changed with command "Test Tone Set Params" The IOCTL VIDIOC_S_EXT_CTRLS is used to perform Test Tone Set Parameters operation, with following parameters: Parameter id set to V4L2_CID_CG2900_RADIO_TEST_TONE_SET_PARAMS Parameter size set to 6 Control class parameter set to V4L2_CTRL_CLASS_USER Parameters value filled as below in code snippet(string field of the parameter) First word of Parameter value shall contain tone_gen (0: tone_1, 1:tone_2) Second word of Parameter value shall contain frequency ([0x0000..0x7FFF], (default = 0x064D)) Third word of Parameter value shall contain volume ([0x0000..0x7FFF], (default = 0x0CCD)) Fourth word of Parameter value shall contain phase offset([0x8000..0x7FFF], (default = 0x0000)) Fifth word of Parameter value shall contain DC to add to tone([0x8000..0x7FFF], (default = 0x0000)) Sixth word of Parameter value shall contain waveform type(0=sine shaped, 1=Pulse shaped) void Sample_TestToneSetParams() { u8 *test_tone_connect_ptr = NULL; u8 tone_gen =0, waveform = 1; /* Tone_Gen = Tone_1, waveform type = pulse shaped */ u16 frequency = 0x064D, volume = 0x0CCD, phase_offset = 0x0000, dc=0x0000; ext_ctrl.ctrl_class = V4L2_CTRL_CLASS_USER; ext_ctrl.controls = (struct v4l2_ext_control *) malloc(sizeof(struct v4l2_ext_control)); ext_ctrl.count = 0; ext_ctrl.controls->id = V4L2_CID_CG2900_RADIO_TEST_TONE_CONNECT; ext_ctrl.controls->size = 6; ext_ctrl.controls->string = (u16 *)malloc(sizeof(u16) * ext_ctrl.controls->size); test_tone_connect_ptr = ext_ctrl.controls->string; *(test_tone_connect_ptr) = tone_gen; *(test_tone_connect_ptr + 1) = frequency; *(test_tone_connect_ptr + 2) = volume; *(test_tone_connect_ptr + 3) = phase_offset; *(test_tone_connect_ptr + 4) = dc; *(test_tone_connect_ptr + 5) = waveform; ret = ioctl(fd, VIDIOC_S_EXT_CTRLS, & ext_ctrl); if (ret < 0) { printf("VIDIOC_S_EXT_CTRL:error!!\n"); } }
Test Tone Generation Set De-Emphasis Filter To apply de-emphasis filter to the FM received signal to compansate for preemphasis that has been applied to the signal by the FM transmitter. IOCTL VIDIOC_S_CTRL is used in with parameter id set to V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS and parameter value can take following values: Disable de-emphasis - use V4L2_CG2900_RADIO_DEEMPHASIS_DISABLED De-emphasis with 50 micro seconds - use V4L2_CG2900_RADIO_DEEMPHASIS_50_uS De-emphasis filter with 75 micro seconds - use V4L2_CG2900_RADIO_DEEMPHASIS_75_uS void SetDeemphasisLevel(int deemphasis_level) { struct v4l2_control sctrl; int ret; sctrl.id = V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS ; sctrl.value = deemphasis_level; ret = ioctl(fd, VIDIOC_S_CTRL, & sctrl); if (ret < 0) { printf("VIDIOC_S_CTRL:error!!\n"); } }
Driver Configuration and Interaction For debug purposes the variable cg2900_fm_debug_level in the file cg2900_fm_driver.c can be changed to set how much debug printouts that shall be generated. 1 = Error logs 2 = Info logs, e.g. function entries 3 = Debug logs 4 = HCI logs, i.e. contents of the transferred data
Implemented operations in driver Supported device driver operations when using character device open Opening a character device will Initialize the FM Chip and download the firmware files. close Closes a character device will deinitialize the FM Chip. poll Polling a character device will check if there is requested data is available or not. read Reading from a character device reads RDS data from the Chip
Driver loading parameters radio_nr Parameter type int Default value 0 Runtime readable/modifiable Readable Description The parameter radio_nr in radio-cg2900.c can be set to register a particular minor number with Video4Linux. Currently this parameter is set to 0 by default, signifying that the "\dev\radio0" is the character device assigned to CG2900 FM Driver in Video4Linux. If the Platform has more than 1 radio drivers, the radio_nr parameter should be changed in file radio-cg2900.c. Checking the Radio Number cat sys/module/radio_cg2900/parameters/radio_nr The above command gets the radio number registered with Video4Linux. This is used for opening the FM Radio character device from user space. grid Parameter type int Default value 1 Runtime readable/modifiable Readable Description The parameter grid in radio-cg2900.c defines the spacing to be used in Khz while switching on FM Radio. 0: 50 kHz (China) 1: 100 kHz (Europe, Japan) 2: 200 kHz (USA) Changing the Grid echo 1 > /sys/module/radio_cg2900/parameters/grid. The above command sets the radio band spacing between two adjacent radio channels, in this case sets to 100KHz suitable for Europe. The change is applicable before switching on FM Radio, otherwise the change takes effect from next FM switch on. Note: The Grid parameter cannot be changed during FM radio is operational. The user must change the grid value and restart the FM radio when moving into a different radio region. Checking the current Grid Value cat sys/module/radio_cg2900/parameters/grid. The above command gets the radio band spacing between two adjacent radio channels currently set. band Parameter type int Default value 0 Runtime readable/modifiable Readable Description The parameter band in radio-cg2900.c defines the band to be used while switching on FM Radio. 0: 87.5 - 108 MHz (USA, Europe) 1: 76 - 90 MHz (Japan) 2: 70 - 108 MHz (China wide band) Changing the Band echo 0 > /sys/module/radio_cg2900/parameters/band. The above command sets the FM band to be used. In this case, it sets the FM band 87.5 - 100 MHz. The change is applicable before switching on FM Radio, otherwise the change takes effect from next FM switch on. Note: The band parameter cannot be changed during FM radio is operational. The user must change the band value and restart the FM radio when moving into a different radio region. Checking the current Band Value cat sys/module/radio_cg2900/parameters/band. The above command gets the current radio band set. cg2900_fm_debug_level Parameter type int Default value 1 Runtime readable/modifiable Readable Description The parameter CG2900_fm_debug_level in platformosapi.c defines the debug level that is currently used. The higher the debug level the more print-outs are received in the terminal window. The following values are supported: 1 = Error logs 2 = Info logs, e.g. function entries 3 = Debug logs 4 = HCI logs, i.e. contents of the transferred data Changing the Log Level echo 3 > /sys/module/radio_cg2900/parameters/cg2900_fm_debug_level. The above command sets the Logging level of FM Driver. In this case, it set will print all the debug messages except the HCI commands exchanged with FM Chip. Checking the current Log Level cat sys/module/radio_cg2900/parameters/cg2900_fm_debug_level. The above command gets the current debug log level set.
Driver IO Control Describes the FM driver IO control parameters VIDIOC_QUERYCAP Direction Get Parameter v4l2_capability Description The VIDIOC_QUERYCAP IOCTL is used to query the capabilities supported by FM Driver. IF the FM Driver supports FM Rx it should set the capabilities field bit should be bitwise OR'd with V4L2_CAP_TUNER, otherwise if it supports FM Tx, the capabilities field bit should be bitwise OR'd with V4L2_CAP_MODULATOR. Returned values are: If IOCTL is able to retrive the Capabilities successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_G_TUNER Direction Get Parameter v4l2_tuner Description The VIDIOC_G_TUNER IOCTL gets the FM Radio Tuner properties supported by FM Radio. It is also used to retrieve RDS status, mono/stereo status and Signal strength of the tuned channel. These values are valid when FM is configured using IOCTL VIDIOC_S_TUNER, i.e in FM Rx mode. Returned values are: If IOCTL is able to retrive the tuner properties successfully without errors the IOCTL function will return 0. A negative value will indicate error. Note: Currently the retrieved signal strength is in decimals and not in "dBuV", proper external conversion required. VIDIOC_S_TUNER Direction Set Parameter v4l2_tuner Description The VIDIOC_S_TUNER IOCTL configures the FM radio in Rx mode. Only 1 FM Tuner is supported by FM Driver. Returned values are: If IOCTL is able to set the tuner properties successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_G_MODULATOR Direction Get Parameter v4l2_tuner Description The VIDIOC_G_MODULATOR IOCTL gets the FM Radio Modulator properties supported by FM Radio. It is also used to retrieve RDS status and mono/stereo status. These values are valid when FM is configured using IOCTL VIDIOC_S_MODULATOR, i.e in FM Tx mode. Returned values are: If IOCTL is able to retrive the tuner properties successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_S_MODULATOR Direction Set Parameter v4l2_modulator Description The VIDIOC_S_MODULATOR IOCTL configures the FM radio in Tx mode. Only 1 FM Modulator is supported by FM Driver. Returned values are: If IOCTL is able to set the modulator properties successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_S_FREQUENCY Direction Set Parameter v4l2_frequency Description The VIDIOC_S_FREQUENCY IOCTL sets the frequency on FM radio in Rx or Tx mode. The frequency parameter passed is in V4L2 format. Returned values are: If IOCTL is able to set the frequency successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_G_FREQUENCY Direction Set Parameter v4l2_modulator Description The VIDIOC_G_FREQUENCY IOCTL retrives the currently set frequency on FM Radio in Rx or Tx mode. Returned values are: If IOCTL is able to get the frequency successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_S_HW_FREQ_SEEK Direction Set Parameter v4l2_hw_freq_seek Description The VIDIOC_S_HW_FREQ_SEEK IOCTL starts the seek operation when FM Radio is configured in Rx mode. The direction parameter indicates the direction of seeking from the current station. At present the FM Driver ignores the wrap_Around parameter and unconditional wrap around is supported. If the operation is started successfully, the application should use poll() to identify when the seek is over. Returned values are: If IOCTL is able to start the seek successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_G_CTRL Direction Get Parameter v4l2_control Description The VIDIOC_G_CTRL IOCTL to retrive value of a paticular control. The following controls are supported by FM Driver: V4L2_CID_AUDIO_VOLUME V4L2_CID_AUDIO_MUTE V4L2_CID_AUDIO_BALANCE V4L2_CID_CG2900_RADIO_RSSI_THRESHOLD V4L2_CID_CG2900_RADIO_SELECT_ANTENNA V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_GET_RESULT V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT Generic returned values are: If IOCTL is able to retrive the value of the control successfully without errors the IOCTL function will return 0. A negative value will indicate error. Note: For V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT returned values are: -1 AF Switch failed, the AF-RSSI was too low. -2 AF Switch failed, the AF-PI Doesn't correlate. -3 AF Switch failed, the AF-RDS SYNC Lost. VIDIOC_S_CTRL Direction Set Parameter v4l2_control Description The VIDIOC_S_CTRL IOCTL to set value of a paticular control. The following controls are supported by FM Driver: V4L2_CID_CG2900_RADIO_CHIP_STATE V4L2_CID_CG2900_RADIO_BANDSCAN V4L2_CID_CG2900_RADIO_BLOCKSCAN_START V4L2_CID_CG2900_RADIO_SELECT_ANTENNA V4L2_CID_CG2900_RADIO_RSSI_THRESHOLD V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_START Returned values are: If IOCTL is able to set the value of the control successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_G_EXT_CTRLS Direction Get Parameter v4l2_ext_controls Description The VIDIOC_G_EXT_CTRLS IOCTL to retrive value of a paticular control. This is used when a control class is defined or when the value to be retrieved is more than 1 parameter(s). Only V4L2_CTRL_CLASS_FM_TX class is supported for this IOCTL in FM Driver. The following controls are supported by FM Driver: V4L2_CID_RDS_TX_DEVIATION V4L2_CID_PILOT_TONE_ENABLED V4L2_CID_PILOT_TONE_DEVIATION V4L2_CID_TUNE_PREEMPHASIS V4L2_CID_TUNE_POWER_LEVEL Returned values are: If IOCTL is able to retrive the value(s) of the control successfully without errors the IOCTL function will return 0. A negative value will indicate error. VIDIOC_S_EXT_CTRLS Direction Set Parameter v4l2_ext_controls Description The VIDIOC_S_CTRL IOCTL to set value of a paticular control when the parameters to be set are more than 1 parameter or when a control class is defined. At present only the V4L2_CTRL_CLASS_FM_TX and V4L2_CTRL_CLASS_USER control classes are supported by FM Driver. The following controls are supported by FM Driver: V4L2_CID_RDS_TX_DEVIATION V4L2_CID_RDS_TX_PI V4L2_CID_RDS_TX_PTY V4L2_CID_RDS_TX_PS_NAME V4L2_CID_RDS_TX_RADIO_TEXT V4L2_CID_PILOT_TONE_ENABLED V4L2_CID_PILOT_TONE_DEVIATION V4L2_CID_TUNE_PREEMPHASIS V4L2_CID_TUNE_POWER_LEVEL V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START Returned values are: If IOCTL is able to set the value of the control successfully without errors the IOCTL function will return 0. A negative value will indicate error.
Driver Interaction with Sysfs Not Applicable
Driver Interaction using /proc filesystem Not Applicable
Other means for Driver Interaction Not Applicable
Driver Node File FM Radio Device File /dev/radio0 Description The radio device for FM Radio.
Known Bugs And Limitations No known issues. Internal Functions Provided List of internal functions used in FM Driver.
radio-cg2900.c !Idrivers/media/radio/CG2900/radio-cg2900.c
cg2900_fm_api.h !Idrivers/media/radio/CG2900/cg2900_fm_api.h
cg2900_fm_api.c !Idrivers/media/radio/CG2900/cg2900_fm_api.c
cg2900_fm_driver.h !Idrivers/media/radio/CG2900/cg2900_fm_driver.h
cg2900_fm_driver.c !Idrivers/media/radio/CG2900/cg2900_fm_driver.c