summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHemant Gupta <hemant.gupta@stericsson.com>2011-05-30 20:13:57 +0530
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:06:01 +0200
commit1111f67691c150cae3e88f12fc9c50ab76b3aaff (patch)
treee8a72334da1be440363321eac7afe9949037f6f5
parentea3500f7b5e6f419ddc5552e2ff7c7d6909ab725 (diff)
CG2900 FM Radio: Handling Mono-Stereo Interrupt.
The linux FM driver for CG2900 now supports mono-stereo transition interrupt from the CG2900. It indicates stereo to mono transition or vice versa. Support for RDS Interrupt, Scan Completion Interrupt, CG2900 Reset Interrupt, etc is also added in this patch. ST-Ericsson ID: 324264 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I280a2a00a6f58e105d11f3a9f14ea662719c485e Signed-off-by: Hemant Gupta <hemant.gupta@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30511 Reviewed-by: Johan PALMAEUS <johan.xj.palmaeus@stericsson.com> Reviewed-by: Virupax SADASHIVPETIMATH <virupax.sadashivpetimath@stericsson.com> Tested-by: Virupax SADASHIVPETIMATH <virupax.sadashivpetimath@stericsson.com>
-rw-r--r--Documentation/DocBook/cg2900_fm_radio.tmpl370
-rw-r--r--drivers/media/radio/CG2900/cg2900_fm_api.c324
-rw-r--r--drivers/media/radio/CG2900/cg2900_fm_api.h19
-rw-r--r--drivers/media/radio/CG2900/cg2900_fm_driver.c18
-rw-r--r--[-rwxr-xr-x]drivers/media/radio/CG2900/cg2900_fm_driver.h5
-rw-r--r--drivers/media/radio/CG2900/radio-cg2900.c756
-rw-r--r--include/linux/videodev2.h11
7 files changed, 984 insertions, 519 deletions
diff --git a/Documentation/DocBook/cg2900_fm_radio.tmpl b/Documentation/DocBook/cg2900_fm_radio.tmpl
index 5832c389cfd..2cdbf146ff4 100644
--- a/Documentation/DocBook/cg2900_fm_radio.tmpl
+++ b/Documentation/DocBook/cg2900_fm_radio.tmpl
@@ -358,7 +358,19 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
<term>Band Scan</term>
<listitem>
<para>
- 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 thread should be created to start polling to FM driver, to wait till scan is complete. 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.
+ 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-&gt;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.
+ <itemizedlist>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED</para></listitem>
+ </itemizedlist>
+
+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.
<programlisting>
void Band_Scan()
{
@@ -372,62 +384,129 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
}
pthread_create(&amp;fmScanThread, NULL, FmScanThread, NULL);
}
- static void *FmScanThread(void *param)
+
+ static void *FmInterruptMonitoringThread(void *param)
{
- struct v4l2_ext_controls scanResult;
+ 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;
- ret = poll(&amp;pollFd, 1, MAX_SCAN_SEEK_TIME);
- if(ret)
- {
+ /* wait infinitely for interrupt */
+ timeout = -1;
+ ret = poll(&amp;pollFd, 1, timeout);
+ if(!closeApp)
+ break;
+ if(ret) {
if(pollFd.revents &amp; POLLRDNORM)
{
- /* Get the Number OF Channels */
+ /* 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-&gt;id = V4L2_CID_CG2900_RADIO_GET_INTERRUPT;
+ FmInterrupt.controls-&gt;size = 2;
+ FmInterrupt.controls-&gt;string = (int *)malloc(sizeof(int) * FmInterrupt.controls-&gt;size);
+ interrupt_buffer_pointer = FmInterrupt.controls-&gt;string;
+ if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;FmInterrupt) &lt; 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-&gt;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));
- scanResult.controls->id = V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS;
- scanResult.controls->size = 0;
- scanResult.controls->string = NULL;
+ if(!scanResult.controls)
+ goto done;
+ scanResult.controls-&gt;id = V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS;
+ scanResult.controls-&gt;size = 0;
+ scanResult.controls-&gt;string = NULL;
err = ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp; scanResult);
+
if (err &lt; 0 &amp; &amp; errno != ENOSPC) {
printf("VIDIOC_G_EXT_CTRLS:error!!\n");
- goto err;
+ goto error_free_ext_control_control;
}
- /* Allocate memory to receive the data from driver */
- if(scanResult.controls->size &gt; 0 )
+
+ if(scanResult.controls-&gt;size &gt; 0 )
{
- scanResult.controls->string = (long *)malloc(sizeof(long) * 2 * scanResult.controls->size );
- p = scanResult.controls->string;
- printf("\nNumber of Channels Found = %d \n", scanResult.controls->size);
- /* Retrieve the Data now */
- ret = ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;scanResult);
- if (ret &lt; 0) {
+ scanResult.controls-&gt;string = (long *)malloc(sizeof(long) * 2 * scanResult.controls-&gt;size );
+ band_scan_pointer = scanResult.controls-&gt;string;
+ printf("\n\n\n==================================\n");
+ printf("\nNumber of Channels Found = %d \n", scanResult.controls-&gt;size);
+ printf("\n==================================\n");
+ if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;scanResult) &lt; 0) {
printf("VIDIOC_G_EXT_CTRLS:error!!\n");
- goto err;
+ goto error_free_ext_control_string;
}
- for (index = 0, count = 0; index &lt; scanResult.controls->size; index ++, count +=2) {
- printf("%d %d.%d %d\n", index + 1, MEGAHRTZ((*(p +count + 0) * 125) / 2), *(p + count + 1));
+ printf("\n================================\n");
+ printf("\nSNo. Frequency(MHz) RSSI\n");
+ printf("\n================================\n");
+ for (index = 0, count = 0; index &lt; scanResult.controls-&gt;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));
}
- err:
- free(p);
- free(scanResult.controls);
+ printf("\n================================\n");
+ error_free_ext_control_string:
+ free(band_scan_pointer);
}
- else if( pollFd.revents &amp; POLLHUP)
+ else if(scanResult.controls-&gt;size == 0)
{
- printf("\nScan Cancelled By User!!\n");
+ printf("\nNo channels found during scanning!!\n");
}
+ error_free_ext_control_control:
free(scanResult.controls);
+ done:
+ otherOperationInProgress = 0;
}
- else if( ret == 0){
- printf("\nError in Scanning, Timeout!!!\n");
- }
- return 0;
- }
+
</programlisting>
Note: Currently the retrieved signal strength is in decimals and not in "dBuV", proper external conversion required.
</para>
@@ -445,7 +524,19 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
<term>Block Scan</term>
<listitem>
<para>
- 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 thread should be created to start polling to FM driver, to wait till scan is complete. 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.
+ 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-&gt;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.
+ <itemizedlist>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET</para></listitem>
+ <listitem><para>V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED</para></listitem>
+ </itemizedlist>
+
+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.
<programlisting>
void Block_Scan()
{
@@ -458,39 +549,103 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
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;
+ ext_ctrl.controls-&gt;id = V4L2_CID_CG2900_RADIO_BLOCKSCAN_START;
+ ext_ctrl.controls-&gt;size = 2;
+ ext_ctrl.controls-&gt;string = (long *)malloc(sizeof(long) * ext_ctrl.controls-&gt;size);
+ p = ext_ctrl.controls-&gt;string;
*p = (StartFreq * 2)/ 125;
*(p + 1) = (EndFreq * 2)/ 125;;
if (ioctl(fd, VIDIOC_S_EXT_CTRLS, &amp;ext_ctrl) &lt; 0)
printf("APP_BlockScanStart:VIDIOC_S_EXT_CTRLS:error!!\n");
- free(ext_ctrl.controls->string);
+ free(ext_ctrl.controls-&gt;string);
free(ext_ctrl.controls);
pthread_create(&amp;fmBlockScanThread, NULL, FmBlockScanThread, NULL);
}
- static void *FmBlockScanThread(void *param)
+
+ static void *FmInterruptMonitoringThread(void *param)
{
- struct v4l2_ext_controls blockscanResult;
+ 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(&amp;pollFd, 1, timeout);
+ if(!closeApp)
+ break;
+ if(ret) {
+ if(pollFd.revents &amp; 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-&gt;id = V4L2_CID_CG2900_RADIO_GET_INTERRUPT;
+ FmInterrupt.controls-&gt;size = 2;
+ FmInterrupt.controls-&gt;string = (int *)malloc(sizeof(int) * FmInterrupt.controls-&gt;size);
+ interrupt_buffer_pointer = FmInterrupt.controls-&gt;string;
+ if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;FmInterrupt) &lt; 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-&gt;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
- */
+ /* Retrieve the currently set grid to determine the next channel is 50 Khz, 100 Khz or 200 Khz apart */
fscanf(fp, "%d", &amp;current_grid);
fclose(fp);
}
+
if(current_grid == 0) {
next_freq_offset = 50000;
} else if (current_grid == 1) {
@@ -498,58 +653,54 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
} else if (current_grid == 2) {
next_freq_offset = 200000;
}
- pollFd.fd = fd;
- pollFd.events = POLLRDNORM;
- ret = poll(&amp;pollFd, 1, MAX_SCAN_SEEK_TIME);
- if(ret) {
- if(pollFd.revents &amp; POLLRDNORM)
- {
+
/* 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));
- blockscanResult.controls->id = V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS;
- blockscanResult.controls->size = 0;
- blockscanResult.controls->string = NULL;
+ if(!blockscanResult.controls)
+ goto done;
+ blockscanResult.controls-&gt;id = V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS;
+ blockscanResult.controls-&gt;size = 0;
+
+ blockscanResult.controls-&gt;string = NULL;
err = ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;blockscanResult);
+
if (err &lt; 0 &amp;&amp; errno != ENOSPC) {
printf("VIDIOC_G_EXT_CTRLS:error!!\n");
- goto err;
+ goto error_free_ext_control_control;
}
- if(blockscanResult.controls->size > 0 )
+
+ if(blockscanResult.controls-&gt;size &gt; 0)
{
- blockscanResult.controls->string = (long *)malloc(sizeof(long) * blockscanResult.controls->size );
- p = blockscanResult.controls->string;
+ blockscanResult.controls-&gt;string = (long *)malloc(sizeof(long) * blockscanResult.controls-&gt;size );
+ block_scan_pointer = blockscanResult.controls-&gt;string;
if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;blockscanResult) &lt; 0) {
printf("VIDIOC_G_EXT_CTRLS:error!!\n");
- goto err;
+ goto error_free_ext_control_string;
}
- printf("\nSNo. RSSI\n");
- for (index = 0; index &lt; blockscanResult.controls->size; index ++) {
- printf("%d.%d %d\n", MEGAHRTZ(start_freq), *(p + index));
+ printf("\n================================\n");
+ printf("\nMHz. RSSI\n");
+ printf("\n================================\n");
+ for (index = 0; index &lt; blockscanResult.controls-&gt;size; index ++) {
+ printf("%d.%d %d\n", MEGAHRTZ(start_freq), *(block_scan_pointer + index));
start_freq += next_freq_offset;
}
- err:
- free(p);
+ printf("\n================================\n");
+ error_free_ext_control_string:
+ free(block_scan_pointer);
}
- else if(blockscanResult.controls->size == 0)
+ else if(blockscanResult.controls-&gt;size == 0)
{
printf("\nNo channels found during Block Scan!!\n");
}
+ error_free_ext_control_control:
free(blockscanResult.controls);
- }
- else if( pollFd.revents &amp; POLLHUP)
- {
- printf("\nBlock Scan Cancelled By User!!\n");
- }
- }
- else if(ret == 0) {
- printf("\nError in Block Scan, Timeout!!!\n");
- }
+ done:
otherOperationInProgress = 0;
- return 0;
}
+
</programlisting>
Note: Currently the retrieved signal strength is in decimals and not in "dBuV", proper external conversion required.
</para>
@@ -567,7 +718,7 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
<term>Cancel Scan/Seek</term>
<listitem>
<para>
- 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.
+ 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.
<programlisting>
struct v4l2_control sctrl;
int ret;
@@ -577,6 +728,72 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
if (ret &lt; 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(&amp;pollFd, 1, timeout);
+ if(!closeApp)
+ break;
+ if(ret) {
+ if(pollFd.revents &amp; 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-&gt;id = V4L2_CID_CG2900_RADIO_GET_INTERRUPT;
+ FmInterrupt.controls-&gt;size = 2;
+ FmInterrupt.controls-&gt;string = (int *)malloc(sizeof(int) * FmInterrupt.controls-&gt;size);
+ interrupt_buffer_pointer = FmInterrupt.controls-&gt;string;
+ if (ioctl(fd, VIDIOC_G_EXT_CTRLS, &amp;FmInterrupt) &lt; 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-&gt;string);
+error_free_ext_control_control:
+ free(FmInterrupt.controls);
+error:
+ otherOperationInProgress = 0;
+ } else {
+ printf ("FmInterruptMonitoringThread : poll returned = %d\n", ret);
+ }
+ }
+ }
+ return 0;
+ }
+
</programlisting>
</para>
</listitem>
@@ -593,7 +810,7 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
<term>RDS Receive</term>
<listitem>
<para>
- 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. A thread should be created and read() should be called to receive RDS data from 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.
+ 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.
<programlisting>
void rds_rx_set(bool enable_rds)
{
@@ -1004,7 +1221,6 @@ Note: Currently the retrieved signal strength is in decimals and not in "dBuV",
</variablelist>
</section>
-
</chapter>
<chapter id="driver-configuration">
<title>Driver Configuration and Interaction</title>
diff --git a/drivers/media/radio/CG2900/cg2900_fm_api.c b/drivers/media/radio/CG2900/cg2900_fm_api.c
index b6be4e462e3..b7f5f9867d6 100644
--- a/drivers/media/radio/CG2900/cg2900_fm_api.c
+++ b/drivers/media/radio/CG2900/cg2900_fm_api.c
@@ -597,91 +597,108 @@ static void cg2900_fm_driver_callback(
bool event_successful
)
{
+ struct sk_buff *skb;
+
FM_INFO_REPORT("cg2900_fm_driver_callback: "
"event = %02x, event_successful = %x",
event, event_successful);
- if (event_successful) {
- switch (event) {
- case FMD_EVENT_GEN_POWERUP:
- FM_DEBUG_REPORT("FMD_EVENT_GEN_POWERUP");
- break;
- case FMD_EVENT_ANTENNA_STATUS_CHANGED:
- FM_DEBUG_REPORT("FMD_EVENT_ANTENNA_STATUS_CHANGED");
- break;
- case FMD_EVENT_FREQUENCY_CHANGED:
- FM_DEBUG_REPORT("FMD_EVENT_FREQUENCY_CHANGED ");
- break;
-
- case FMD_EVENT_SEEK_STOPPED:
- FM_DEBUG_REPORT("FMD_EVENT_SEEK_STOPPED");
- fm_event = CG2900_EVENT_SCAN_CANCELLED;
- wake_up_poll_queue();
- break;
-
- case FMD_EVENT_SEEK_COMPLETED:
- FM_DEBUG_REPORT("FMD_EVENT_SEEK_COMPLETED");
- fm_event = CG2900_EVENT_SEARCH_CHANNEL_FOUND;
- wake_up_poll_queue();
- break;
-
- case FMD_EVENT_SCAN_BAND_COMPLETED:
- FM_DEBUG_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED");
- fm_event = CG2900_EVENT_SCAN_CHANNELS_FOUND;
- wake_up_poll_queue();
- break;
-
- case FMD_EVENT_BLOCK_SCAN_COMPLETED:
- FM_DEBUG_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED");
- fm_event = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND;
- wake_up_poll_queue();
- break;
-
- case FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE:
- FM_DEBUG_REPORT("FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE");
- break;
-
- case FMD_EVENT_RDSGROUP_RCVD:
- FM_DEBUG_REPORT("FMD_EVENT_RDSGROUP_RCVD");
- fmd_set_rds_sem();
- break;
-
- default:
- FM_INFO_REPORT("cg2900_fm_driver_callback: "
- "Unknown event = %x", event);
- break;
+ switch (event) {
+ case FMD_EVENT_GEN_POWERUP:
+ FM_DEBUG_REPORT("FMD_EVENT_GEN_POWERUP");
+ break;
+ case FMD_EVENT_ANTENNA_STATUS_CHANGED:
+ FM_DEBUG_REPORT("FMD_EVENT_ANTENNA_STATUS_CHANGED");
+ break;
+ case FMD_EVENT_FREQUENCY_CHANGED:
+ FM_DEBUG_REPORT("FMD_EVENT_FREQUENCY_CHANGED ");
+ break;
+ case FMD_EVENT_SEEK_STOPPED:
+ FM_DEBUG_REPORT("FMD_EVENT_SEEK_STOPPED");
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA,
+ GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("cg2900_fm_driver_callback: "
+ "Unable to Allocate Memory");
+ return;
}
- } else {
- switch (event) {
- /*
- * Seek stop, band scan, seek, block scan could
- * fail for some reason so wake up poll queue
- */
- case FMD_EVENT_SEEK_STOPPED:
- FM_ERR_REPORT("FMD_EVENT_SEEK_STOPPED");
- fm_event = CG2900_EVENT_SCAN_CANCELLED;
- wake_up_poll_queue();
- break;
- case FMD_EVENT_SEEK_COMPLETED:
- FM_ERR_REPORT("FMD_EVENT_SEEK_COMPLETED");
- fm_event = CG2900_EVENT_SEARCH_CHANNEL_FOUND;
- wake_up_poll_queue();
- break;
- case FMD_EVENT_SCAN_BAND_COMPLETED:
- FM_ERR_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED");
- fm_event = CG2900_EVENT_SCAN_CHANNELS_FOUND;
- wake_up_poll_queue();
- break;
- case FMD_EVENT_BLOCK_SCAN_COMPLETED:
- FM_ERR_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED");
- fm_event = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND;
- wake_up_poll_queue();
- break;
- default:
+ skb->data[0] = CG2900_EVENT_SCAN_CANCELLED;
+ skb->data[1] = event_successful;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_SEEK_COMPLETED:
+ FM_DEBUG_REPORT("FMD_EVENT_SEEK_COMPLETED");
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA,
+ GFP_KERNEL);
+ if (!skb) {
FM_ERR_REPORT("cg2900_fm_driver_callback: "
- "event = %x failed!!!!", event);
- break;
+ "Unable to Allocate Memory");
+ return;
+ }
+ skb->data[0] = CG2900_EVENT_SEARCH_CHANNEL_FOUND;
+ skb->data[1] = event_successful;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_SCAN_BAND_COMPLETED:
+ FM_DEBUG_REPORT("FMD_EVENT_SCAN_BAND_COMPLETED");
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA,
+ GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("cg2900_fm_driver_callback: "
+ "Unable to Allocate Memory");
+ return;
}
+ skb->data[0] = CG2900_EVENT_SCAN_CHANNELS_FOUND;
+ skb->data[1] = event_successful;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_BLOCK_SCAN_COMPLETED:
+ FM_DEBUG_REPORT("FMD_EVENT_BLOCK_SCAN_COMPLETED");
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA,
+ GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("cg2900_fm_driver_callback: "
+ "Unable to Allocate Memory");
+ return;
+ }
+ skb->data[0] = CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND;
+ skb->data[1] = event_successful;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+ wake_up_poll_queue();
+ break;
+ case FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE:
+ FM_DEBUG_REPORT("FMD_EVENT_AF_UPDATE_SWITCH_COMPLETE");
+ break;
+ case FMD_EVENT_RDSGROUP_RCVD:
+ FM_DEBUG_REPORT("FMD_EVENT_RDSGROUP_RCVD");
+ /*
+ * Release the rds semaphore, poll queue
+ * will be woken-up in rds callback
+ */
+ fmd_set_rds_sem();
+ break;
+ case FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE:
+ FM_ERR_REPORT(
+ "FMD_EVENT_MONO_STEREO_TRANSITION_COMPLETE");
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA,
+ GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("cg2900_fm_driver_callback: "
+ "Unable to Allocate Memory");
+ return;
+ }
+ skb->data[0] = CG2900_EVENT_MONO_STEREO_TRANSITION;
+ skb->data[1] = event_successful;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+ wake_up_poll_queue();
+ break;
+ default:
+ FM_INFO_REPORT("cg2900_fm_driver_callback: "
+ "Unknown event = %x", event);
+ break;
}
}
@@ -696,6 +713,8 @@ static void cg2900_fm_rds_callback(void)
u8 index = 0;
u16 rds_local_buf_count;
int result;
+ struct sk_buff *skb;
+
FM_INFO_REPORT("cg2900_fm_rds_callback");
/*
@@ -751,7 +770,21 @@ static void cg2900_fm_rds_callback(void)
fm_rds_info.rds_head++;
if (fm_rds_info.rds_head == MAX_RDS_BUFFER)
fm_rds_info.rds_head = 0;
- wake_up_read_queue();
+
+ /* Queue the RDS event */
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA,
+ GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("cg2900_fm_rds_callback: "
+ "Unable to Allocate Memory");
+ goto error;
+ }
+ skb->data[0] = CG2900_EVENT_RDS_EVENT;
+ skb->data[1] = true;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+
+ /* Wake up the poll queue */
+ wake_up_poll_queue();
error:
mutex_unlock(&rds_mutex);
}
@@ -879,10 +912,11 @@ int cg2900_fm_switch_on(
fm_state = CG2900_FM_STATE_SWITCHED_ON;
fm_mode = CG2900_FM_IDLE_MODE;
- memset(&fm_rds_info, 0, sizeof(struct cg2900_fm_rds_info));
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
error:
FM_DEBUG_REPORT("cg2900_fm_switch_on: returning %d",
@@ -931,10 +965,12 @@ int cg2900_fm_switch_off(void)
fm_state = CG2900_FM_STATE_INITIALIZED;
fm_mode = CG2900_FM_IDLE_MODE;
memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
+ sizeof(struct cg2900_fm_rds_info));
memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
}
error:
@@ -955,6 +991,13 @@ int cg2900_fm_standby(void)
result = -EINVAL;
goto error;
}
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
result = fmd_goto_standby();
if (0 != result) {
FM_ERR_REPORT("cg2900_fm_standby: "
@@ -1118,6 +1161,9 @@ int cg2900_fm_set_rx_default_settings(
goto error;
}
+ /* Remove all Interrupt from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
+
FM_DEBUG_REPORT("cg2900_fm_set_rx_default_settings: "
"Sending Set rds");
@@ -1194,13 +1240,15 @@ int cg2900_fm_set_tx_default_settings(
fm_rds_status = false;
fmd_stop_rds_thread();
memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
+ sizeof(struct cg2900_fm_rds_info));
memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
/* Give 50 ms delay to exit the RDS thread */
schedule_timeout_interruptible(msecs_to_jiffies(50));
}
+ /* Remove all Interrupt from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
/* Switch To Tx mode */
FM_DEBUG_REPORT("cg2900_fm_set_tx_default_settings: "
@@ -1396,6 +1444,14 @@ int cg2900_fm_search_up_freq(void)
/* Stop RDS if it is active */
result = cg2900_fm_rds_off();
fm_prev_rds_status = true;
+ } else {
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
}
result = fmd_rx_seek(CG2900_DIR_UP);
if (0 != result) {
@@ -1404,11 +1460,6 @@ int cg2900_fm_search_up_freq(void)
result = -EINVAL;
goto error;
}
- memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
- memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
result = 0;
error:
@@ -1433,6 +1484,14 @@ int cg2900_fm_search_down_freq(void)
/* Stop RDS if it is active */
result = cg2900_fm_rds_off();
fm_prev_rds_status = true;
+ } else {
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
}
result = fmd_rx_seek(CG2900_DIR_DOWN);
if (0 != result) {
@@ -1441,11 +1500,6 @@ int cg2900_fm_search_down_freq(void)
result = -EINVAL;
goto error;
}
- memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
- memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
result = 0;
error:
@@ -1470,6 +1524,14 @@ int cg2900_fm_start_band_scan(void)
/* Stop RDS if it is active */
result = cg2900_fm_rds_off();
fm_prev_rds_status = true;
+ } else {
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
}
result = fmd_rx_scan_band(DEFAULT_CHANNELS_TO_SCAN);
if (0 != result) {
@@ -1478,11 +1540,6 @@ int cg2900_fm_start_band_scan(void)
result = -EINVAL;
goto error;
}
- memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
- memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
result = 0;
error:
@@ -1648,6 +1705,14 @@ int cg2900_fm_start_block_scan(
/* Stop RDS if it is active */
result = cg2900_fm_rds_off();
fm_prev_rds_status = true;
+ } else {
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
}
result = fmd_get_antenna(
&antenna);
@@ -1661,11 +1726,6 @@ int cg2900_fm_start_block_scan(
result = -EINVAL;
goto error;
}
- memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
- memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
result = 0;
error:
@@ -1990,8 +2050,8 @@ int cg2900_fm_tx_get_pilot_tone_status(
result = fmd_tx_get_stereo_mode(enable);
if (0 != result) {
FM_ERR_REPORT("cg2900_fm_tx_get_pilot_tone_status: "
- "fmd_tx_get_stereo_mode failed %d",
- (unsigned int)result);
+ "fmd_tx_get_stereo_mode failed %d",
+ result);
result = -EINVAL;
goto error;
}
@@ -2334,6 +2394,13 @@ int cg2900_fm_rds_off(void)
result = -EINVAL;
goto error;
}
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
result = fmd_rx_set_rds(FMD_SWITCH_OFF_RDS);
if (0 != result) {
FM_ERR_REPORT("cg2900_fm_rds_off: fmd_rx_set_rds failed, "
@@ -2343,11 +2410,6 @@ int cg2900_fm_rds_off(void)
}
/* Stop the RDS Thread */
fm_rds_status = false;
- memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
- memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
FM_DEBUG_REPORT("cg2900_fm_rds_off: "
"Stopping RDS Thread");
fmd_stop_rds_thread();
@@ -2399,10 +2461,10 @@ int cg2900_fm_rds_on(void)
/* Start the RDS Thread to read the RDS Buffers */
fm_rds_status = true;
memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
+ sizeof(struct cg2900_fm_rds_info));
memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
fmd_start_rds_thread(cg2900_fm_rds_callback);
error:
@@ -2584,6 +2646,13 @@ int cg2900_fm_set_frequency(
result = -EINVAL;
goto error;
}
+ memset(&fm_rds_info, 0,
+ sizeof(struct cg2900_fm_rds_info));
+ memset(fm_rds_buf, 0,
+ sizeof(struct cg2900_fm_rds_buf) *
+ MAX_RDS_BUFFER * MAX_RDS_GROUPS);
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
if (CG2900_FM_RX_MODE == fm_mode) {
FM_DEBUG_REPORT("cg2900_fm_set_frequency: "
"fmd_rx_set_frequency");
@@ -2616,11 +2685,6 @@ int cg2900_fm_set_frequency(
result = -EINVAL;
goto error;
}
- memset(&fm_rds_info, 0,
- sizeof(struct cg2900_fm_rds_info));
- memset(fm_rds_buf, 0,
- sizeof(struct cg2900_fm_rds_buf) *
- MAX_RDS_BUFFER * MAX_RDS_GROUPS);
result = 0;
}
@@ -2820,16 +2884,7 @@ int cg2900_fm_get_mode(
FM_DEBUG_REPORT("cg2900_fm_get_mode: "
"fmd_rx_get_stereo_mode");
result = fmd_rx_get_stereo_mode(cur_mode);
- switch (*cur_mode) {
- case FMD_STEREOMODE_OFF:
- case FMD_STEREOMODE_BLENDING:
- *cur_mode = CG2900_MODE_STEREO;
- break;
- case FMD_STEREOMODE_MONO:
- default:
- *cur_mode = CG2900_MODE_MONO;
- break;
- }
+ FM_DEBUG_REPORT("cg2900_fm_get_mode: cur_mode = %x", *cur_mode);
} else if (CG2900_FM_TX_MODE == fm_mode) {
FM_DEBUG_REPORT("cg2900_fm_get_mode: "
"fmd_tx_get_stereo_mode");
@@ -3024,8 +3079,7 @@ int cg2900_fm_set_test_tone_generator(
if (0 != result) {
FM_ERR_REPORT("cg2900_fm_set_test_tone_generator: "
"fmd_set_test_tone_generator_status failed"
- ", Error Code %d",
- result);
+ ", Error Code %d", result);
result = -EINVAL;
goto error;
}
diff --git a/drivers/media/radio/CG2900/cg2900_fm_api.h b/drivers/media/radio/CG2900/cg2900_fm_api.h
index 747cdbc1e0d..ec9e6e86f77 100644
--- a/drivers/media/radio/CG2900/cg2900_fm_api.h
+++ b/drivers/media/radio/CG2900/cg2900_fm_api.h
@@ -12,10 +12,13 @@
#define CG2900_FM_API_H
#include <linux/device.h>
+#include <linux/skbuff.h>
/* Callback function to receive RDS Data. */
typedef void (*cg2900_fm_rds_cb)(void);
+extern struct sk_buff_head fm_interrupt_queue;
+
/**
* struct cg2900_fm_rds_buf - RDS Group Receiving Structure
*
@@ -145,6 +148,9 @@ enum cg2900_fm_grid {
* @CG2900_EVENT_SCAN_CHANNELS_FOUND: Band Scan is completed.
* @CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND: Block Scan is completed.
* @CG2900_EVENT_SCAN_CANCELLED: Scan/Seek is cancelled.
+ * @CG2900_EVENT_MONO_STEREO_TRANSITION: Mono/Stereo Transition has taken place.
+ * @CG2900_EVENT_DEVICE_RESET: CG2900 has been reset by some other IP.
+ * @CG2900_EVENT_RDS_EVENT: RDS data interrupt has been received from chip.
*
* Various Events reported by FM API layer.
*/
@@ -153,7 +159,10 @@ enum cg2900_fm_event {
CG2900_EVENT_SEARCH_CHANNEL_FOUND,
CG2900_EVENT_SCAN_CHANNELS_FOUND,
CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND,
- CG2900_EVENT_SCAN_CANCELLED
+ CG2900_EVENT_SCAN_CANCELLED,
+ CG2900_EVENT_MONO_STEREO_TRANSITION,
+ CG2900_EVENT_DEVICE_RESET,
+ CG2900_EVENT_RDS_EVENT
};
/**
@@ -197,6 +206,7 @@ enum cg2900_fm_stereo_mode {
#define DEFAULT_CHANNELS_TO_SCAN 32
#define MAX_CHANNELS_TO_SCAN 99
#define MAX_CHANNELS_FOR_BLOCK_SCAN 198
+#define SKB_FM_INTERRUPT_DATA 2
extern u8 fm_event;
extern struct cg2900_fm_rds_buf fm_rds_buf[MAX_RDS_BUFFER][MAX_RDS_GROUPS];
@@ -978,13 +988,6 @@ void cg2900_handle_device_reset(void);
void wake_up_poll_queue(void);
/**
- * void wake_up_read_queue()- Wakes up the Task waiting on Read Queue.
- * This function is called when RDS data is available for reading by
- * application.
- */
-void wake_up_read_queue(void);
-
-/**
* void cg2900_fm_set_chip_version()- Sets the Version of the Controller.
*
* This function is used to update the Chip Version information at time
diff --git a/drivers/media/radio/CG2900/cg2900_fm_driver.c b/drivers/media/radio/CG2900/cg2900_fm_driver.c
index 6ac380409c1..9959790cbe8 100644
--- a/drivers/media/radio/CG2900/cg2900_fm_driver.c
+++ b/drivers/media/radio/CG2900/cg2900_fm_driver.c
@@ -2309,6 +2309,9 @@ int fmd_rx_get_stereo_mode(
)
{
int err;
+ int io_result;
+ u16 response_count;
+ u16 response_data[CMD_RP_GET_STATE_RSP_PARAM_LEN];
if (fmd_go_cmd_busy()) {
err = -EBUSY;
@@ -2325,7 +2328,20 @@ int fmd_rx_get_stereo_mode(
goto error;
}
- *mode = fmd_state_info.rx_stereo_mode;
+ io_result = fmd_send_cmd_and_read_resp(
+ CMD_FMR_RP_GET_STATE,
+ CMD_RP_GET_STATE_PARAM_LEN,
+ NULL,
+ &response_count,
+ response_data);
+
+ if (io_result != 0) {
+ err = io_result;
+ goto error;
+ }
+
+ /* 2nd element of response is stereo signal */
+ *mode = response_data[1];
err = 0;
error:
diff --git a/drivers/media/radio/CG2900/cg2900_fm_driver.h b/drivers/media/radio/CG2900/cg2900_fm_driver.h
index 313824a1e00..fc20476c64a 100755..100644
--- a/drivers/media/radio/CG2900/cg2900_fm_driver.h
+++ b/drivers/media/radio/CG2900/cg2900_fm_driver.h
@@ -173,6 +173,7 @@ enum fmd_debug_levels {
#define CMD_FMR_DP_BUFFER_SET_THRESHOLD 0x06C3
#define CMD_FMR_DP_SET_CONTROL 0x02A3
#define CMD_FMR_RP_GET_RSSI 0x0083
+#define CMD_FMR_RP_GET_STATE 0x0063
#define CMD_FMR_RP_STEREO_SET_MODE 0x0123
#define CMD_FMR_SET_ANTENNA 0x0663
#define CMD_FMR_SP_AF_SWITCH_GET_RESULT 0x0603
@@ -218,7 +219,7 @@ enum fmd_debug_levels {
#define CMD_GEN_SET_REGISTER_VALUE 0x0101
#define CMD_TST_TONE_ENABLE 0x0027
#define CMD_TST_TONE_CONNECT 0x0047
-#define CMD_TST_TONE_SET_PARAMS 0x0067
+#define CMD_TST_TONE_SET_PARAMS 0x0067
/* FM Command Id Parameter Length */
#define CMD_GET_VERSION_PARAM_LEN 0
@@ -233,6 +234,8 @@ enum fmd_debug_levels {
#define CMD_RP_STEREO_SET_MODE_PARAM_LEN 1
#define CMD_RP_GET_RSSI_PARAM_LEN 0
#define CMD_RP_GET_RSSI_RSP_PARAM_LEN 1
+#define CMD_RP_GET_STATE_PARAM_LEN 0
+#define CMD_RP_GET_STATE_RSP_PARAM_LEN 2
#define CMD_SP_SEARCH_START_PARAM_LEN 4
#define CMD_SP_SCAN_START_PARAM_LEN 4
#define CMD_SP_SCAN_GET_RESULT_PARAM_LEN 1
diff --git a/drivers/media/radio/CG2900/radio-cg2900.c b/drivers/media/radio/CG2900/radio-cg2900.c
index 4fe3e6ffd5c..6a74c0ac257 100644
--- a/drivers/media/radio/CG2900/radio-cg2900.c
+++ b/drivers/media/radio/CG2900/radio-cg2900.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include<linux/string.h>
#include<linux/wait.h>
+#include<linux/mfd/cg2900.h>
#include"cg2900_fm_driver.h"
#define RADIO_CG2900_VERSION KERNEL_VERSION(1, 1, 0)
@@ -35,6 +36,7 @@
#define FMR_USA_GRID_IN_HZ 200000
#define FMR_AF_SWITCH_DATA_SIZE 2
#define FMR_BLOCK_SCAN_DATA_SIZE 2
+#define FMR_GET_INTERRUPT_DATA_SIZE 2
#define FMR_TEST_TONE_CONNECT_DATA_SIZE 2
#define FMR_TEST_TONE_SET_PARAMS_DATA_SIZE 6
@@ -148,6 +150,9 @@ static void cg2900_convert_err_to_v4l2(
char status_byte,
char *out_byte
);
+static int cg2900_map_event_to_v4l2(
+ u8 fm_event
+ );
static u32 freq_low;
static u32 freq_high;
@@ -160,8 +165,7 @@ static int band;
/* cg2900_poll_queue - Main Wait Queue for polling (Scan/Seek) */
static wait_queue_head_t cg2900_poll_queue;
-/* cg2900_read_queue - Main Wait Queue for receiving RDS Data */
-static DECLARE_WAIT_QUEUE_HEAD(cg2900_read_queue);
+struct sk_buff_head fm_interrupt_queue;
/**
* enum fm_seek_status - Seek status of FM Radio.
@@ -378,7 +382,7 @@ static int vidioc_get_tuner(
status = cg2900_fm_get_mode(&mode);
- FM_DEBUG_REPORT("vidioc_get_tuner: mode = %d, ", mode);
+ FM_DEBUG_REPORT("vidioc_get_tuner: mode = %x, ", mode);
if (0 != status) {
/* Get mode API failed, set mode to mono */
@@ -612,7 +616,7 @@ static int vidioc_get_modulator(
if (cg2900_device.fm_mode == CG2900_FM_TX_MODE) {
status = cg2900_fm_get_mode(&mode);
- FM_DEBUG_REPORT("vidioc_get_modulator: mode = %d", mode);
+ FM_DEBUG_REPORT("vidioc_get_modulator: mode = %x", mode);
if (0 != status) {
/* Get mode API failed, set mode to mono */
modulator->txsubchans = V4L2_TUNER_SUB_MONO;
@@ -795,6 +799,7 @@ static int vidioc_get_frequency(
int status;
u32 frequency;
int ret_val = -EINVAL;
+ struct sk_buff *skb;
FM_INFO_REPORT("vidioc_get_frequency: Status = %d",
cg2900_device.seekstatus);
@@ -807,6 +812,26 @@ static int vidioc_get_frequency(
}
if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS) {
+ if (skb_queue_empty(&fm_interrupt_queue)) {
+ /* No Interrupt, bad case */
+ FM_ERR_REPORT("vidioc_get_frequency: "
+ "No Interrupt to read");
+ fm_event = CG2900_EVENT_NO_EVENT;
+ goto error;
+ }
+ spin_lock(&fm_spinlock);
+ skb = skb_dequeue(&fm_interrupt_queue);
+ spin_unlock(&fm_spinlock);
+ if (!skb) {
+ /* No Interrupt, bad case */
+ FM_ERR_REPORT("vidioc_get_frequency: "
+ "No Interrupt to read");
+ fm_event = CG2900_EVENT_NO_EVENT;
+ goto error;
+ }
+ fm_event = (u8)skb->data[0];
+ FM_DEBUG_REPORT("vidioc_get_frequency: Interrupt = %x",
+ fm_event);
/* Check if seek is finished or not */
if (CG2900_EVENT_SEARCH_CHANNEL_FOUND == fm_event) {
/* seek is finished */
@@ -815,6 +840,12 @@ static int vidioc_get_frequency(
freq->frequency = cg2900_device.frequency;
cg2900_device.seekstatus = FMR_SEEK_NONE;
fm_event = CG2900_EVENT_NO_EVENT;
+ kfree_skb(skb);
+ spin_unlock(&fm_spinlock);
+ } else {
+ /* Some other interrupt, queue it back */
+ spin_lock(&fm_spinlock);
+ skb_queue_head(&fm_interrupt_queue, skb);
spin_unlock(&fm_spinlock);
}
} else {
@@ -991,7 +1022,7 @@ static int vidioc_query_ctrl(
default:
FM_DEBUG_REPORT("vidioc_query_ctrl: "
- "--> unsupported id = %d", (int)query_ctrl->id);
+ "--> unsupported id = %x", query_ctrl->id);
break;
}
@@ -1053,7 +1084,7 @@ static int vidioc_get_ctrl(
break;
case V4L2_CID_CG2900_RADIO_SELECT_ANTENNA:
status = cg2900_fm_get_antenna(&antenna);
- FM_DEBUG_REPORT("vidioc_get_ctrl: Antenna = %d", antenna);
+ FM_DEBUG_REPORT("vidioc_get_ctrl: Antenna = %x", antenna);
if (0 == status) {
ctrl->value = antenna;
ret_val = 0;
@@ -1061,7 +1092,7 @@ static int vidioc_get_ctrl(
break;
case V4L2_CID_CG2900_RADIO_RDS_AF_UPDATE_GET_RESULT:
status = cg2900_fm_af_update_get_result(&rssi);
- FM_DEBUG_REPORT("vidioc_get_ctrl: AF RSSI Level = %d", rssi);
+ FM_DEBUG_REPORT("vidioc_get_ctrl: AF RSSI Level = %x", rssi);
if (0 == status) {
ctrl->value = rssi;
ret_val = 0;
@@ -1069,7 +1100,7 @@ static int vidioc_get_ctrl(
break;
case V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_GET_RESULT:
status = cg2900_fm_af_switch_get_result(&conclusion);
- FM_DEBUG_REPORT("vidioc_get_ctrl: AF Switch conclusion = %d",
+ FM_DEBUG_REPORT("vidioc_get_ctrl: AF Switch conclusion = %x",
conclusion);
if (0 != status)
break;
@@ -1086,13 +1117,13 @@ static int vidioc_get_ctrl(
*/
ctrl->value = -conclusion;
FM_ERR_REPORT("vidioc_get_ctrl: "
- "AF-Switch failed with value %d", (__s32)ctrl->value);
+ "AF-Switch failed with value %d", ctrl->value);
ret_val = 0;
}
break;
default:
FM_DEBUG_REPORT("vidioc_get_ctrl: "
- "unsupported (id = %d)", (int)ctrl->id);
+ "unsupported (id = %x)", (int)ctrl->id);
ret_val = -EINVAL;
}
FM_DEBUG_REPORT("vidioc_get_ctrl: returning = %d",
@@ -1242,7 +1273,8 @@ static int vidioc_set_ctrl(
"V4L2_CID_CG2900_RADIO_TEST_TONE_GENERATOR_SET_STATUS "
"state = %d ", ctrl->value);
if (ctrl->value < V4L2_CG2900_RADIO_TEST_TONE_GEN_OFF ||
- ctrl->value > V4L2_CG2900_RADIO_TEST_TONE_GENERATOR_ON_WO_SRC) {
+ ctrl->value >
+ V4L2_CG2900_RADIO_TEST_TONE_GENERATOR_ON_WO_SRC) {
FM_ERR_REPORT("Invalid parameter = %d", ctrl->value);
break;
}
@@ -1253,8 +1285,7 @@ static int vidioc_set_ctrl(
case V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS:
FM_DEBUG_REPORT("vidioc_set_ctrl: "
"V4L2_CID_CG2900_RADIO_TUNE_DEEMPHASIS, "
- "Value = %d",
- ctrl->value);
+ "Value = %d", ctrl->value);
if ((V4L2_CG2900_RADIO_DEEMPHASIS_DISABLED >
ctrl->value) ||
@@ -1285,7 +1316,7 @@ static int vidioc_set_ctrl(
break;
default:
FM_DEBUG_REPORT("vidioc_set_ctrl: "
- "unsupported (id = %d)", (int)ctrl->id);
+ "unsupported (id = %x)", ctrl->id);
}
FM_DEBUG_REPORT("vidioc_set_ctrl: returning = %d",
ret_val);
@@ -1321,6 +1352,10 @@ static int vidioc_get_ext_ctrls(
int count = 0;
int ret_val = -EINVAL;
int status;
+ struct sk_buff *skb;
+ u8 mode;
+ s8 interrupt_success;
+ int *fm_interrupt_buffer;
FM_INFO_REPORT("vidioc_get_ext_ctrls: Id = %04x,"
"ext_ctrl->ctrl_class = %04x",
@@ -1336,217 +1371,340 @@ static int vidioc_get_ext_ctrls(
switch (ext_ctrl->controls->id) {
case V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS:
- {
- if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
- FM_ERR_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS "
- "Unsupported ctrl_class = %04x",
- ext_ctrl->ctrl_class);
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_BANDSCAN_GET_RESULTS "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS) {
+ spin_lock(&fm_spinlock);
+ skb = skb_dequeue(&fm_interrupt_queue);
+ spin_unlock(&fm_spinlock);
+ if (!skb) {
+ /* No Interrupt, bad case */
+ FM_ERR_REPORT("No Interrupt to read");
+ fm_event = CG2900_EVENT_NO_EVENT;
break;
}
- if (cg2900_device.seekstatus ==
- FMR_SEEK_IN_PROGRESS) {
- if (fm_event ==
- CG2900_EVENT_SCAN_CHANNELS_FOUND) {
- /* Check to get Scan Result */
- status =
- cg2900_fm_get_scan_result
- (&no_of_scan_freq, scanfreq,
- scanfreq_rssi_level);
- if (0 != status) {
- FM_ERR_REPORT
- ("vidioc_get_ext_ctrls: "
- "cg2900_fm_get_scan_"
- "result: returned %d",
- status);
- break;
- }
+ fm_event = (u8)skb->data[0];
+ FM_DEBUG_REPORT(
+ "V4L2_CID_CG2900_RADIO"
+ "_BANDSCAN_GET_RESULTS: "
+ "fm_event = %x", fm_event);
+ if (fm_event ==
+ CG2900_EVENT_SCAN_CHANNELS_FOUND) {
+ /* Check to get Scan Result */
+ status =
+ cg2900_fm_get_scan_result
+ (&no_of_scan_freq, scanfreq,
+ scanfreq_rssi_level);
+ if (0 != status) {
+ FM_ERR_REPORT
+ ("vidioc_get_ext_ctrls: "
+ "cg2900_fm_get_scan_"
+ "result: returned %d",
+ status);
+ kfree_skb(skb);
+ break;
}
+ kfree_skb(skb);
+ } else {
+ /* Some other interrupt, Queue it back */
+ spin_lock(&fm_spinlock);
+ skb_queue_head(&fm_interrupt_queue, skb);
+ spin_unlock(&fm_spinlock);
}
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "SeekStatus = %d, GlobalEvent = %d, "
- "numchannels = %d",
- cg2900_device.seekstatus,
- fm_event, no_of_scan_freq);
-
- if (ext_ctrl->controls->size == 0 &&
- ext_ctrl->controls->string == NULL) {
- if (cg2900_device.seekstatus ==
- FMR_SEEK_IN_PROGRESS &&
- CG2900_EVENT_SCAN_CHANNELS_FOUND
- == fm_event) {
- spin_lock(&fm_spinlock);
- ext_ctrl->controls->size =
- no_of_scan_freq;
- cg2900_device.seekstatus
- = FMR_SEEK_NONE;
- fm_event =
- CG2900_EVENT_NO_EVENT;
- spin_unlock(&fm_spinlock);
- return -ENOSPC;
- }
- } else if (ext_ctrl->controls->string != NULL) {
- dest_buffer =
- (u32 *) ext_ctrl->controls->string;
- while (index < no_of_scan_freq) {
- *(dest_buffer + count + 0) =
- HZ_TO_V4L2(scanfreq[index]);
- *(dest_buffer + count + 1) =
- scanfreq_rssi_level[index];
- count += 2;
- index++;
- }
- ret_val = 0;
+ }
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "SeekStatus = %x, GlobalEvent = %x, "
+ "numchannels = %x",
+ cg2900_device.seekstatus,
+ fm_event, no_of_scan_freq);
+
+ if (ext_ctrl->controls->size == 0 &&
+ ext_ctrl->controls->string == NULL) {
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS &&
+ CG2900_EVENT_SCAN_CHANNELS_FOUND
+ == fm_event) {
+ spin_lock(&fm_spinlock);
+ ext_ctrl->controls->size =
+ no_of_scan_freq;
+ cg2900_device.seekstatus
+ = FMR_SEEK_NONE;
+ fm_event =
+ CG2900_EVENT_NO_EVENT;
+ spin_unlock(&fm_spinlock);
+ return -ENOSPC;
}
- break;
+ } else if (ext_ctrl->controls->string != NULL) {
+ dest_buffer =
+ (u32 *) ext_ctrl->controls->string;
+ while (index < no_of_scan_freq) {
+ *(dest_buffer + count + 0) =
+ HZ_TO_V4L2(scanfreq[index]);
+ *(dest_buffer + count + 1) =
+ scanfreq_rssi_level[index];
+ count += 2;
+ index++;
+ }
+ ret_val = 0;
}
+ break;
case V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS:
- {
- if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
- FM_ERR_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_CG2900_RADIO_BLOCKSCAN_GET_RESULTS "
- "Unsupported ctrl_class = %04x",
- ext_ctrl->ctrl_class);
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_BLOCKSCAN"
+ "_GET_RESULTS "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS) {
+ spin_lock(&fm_spinlock);
+ skb = skb_dequeue(&fm_interrupt_queue);
+ spin_unlock(&fm_spinlock);
+ if (!skb) {
+ /* No Interrupt, bad case */
+ FM_ERR_REPORT("No Interrupt to read");
+ fm_event = CG2900_EVENT_NO_EVENT;
break;
}
- if (cg2900_device.seekstatus ==
- FMR_SEEK_IN_PROGRESS) {
- if (fm_event ==
- CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) {
- /* Check to get BlockScan Result */
- status =
- cg2900_fm_get_block_scan_result
- (&no_of_block_scan_freq,
- block_scan_rssi_level);
- if (0 != status) {
- FM_ERR_REPORT
- ("vidioc_get_ext_ctrls: "
- "cg2900_fm_get_block_scan_"
- "result: " "returned %d",
- status);
- return ret_val;
- }
+ fm_event = (u8)skb->data[0];
+ FM_DEBUG_REPORT(
+ "V4L2_CID_CG2900_RADIO_BLOCKSCAN"
+ "GET_RESULTS: "
+ "fm_event = %x", fm_event);
+ if (fm_event ==
+ CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) {
+ /* Check for BlockScan Result */
+ status =
+ cg2900_fm_get_block_scan_result
+ (&no_of_block_scan_freq,
+ block_scan_rssi_level);
+ if (0 != status) {
+ FM_ERR_REPORT
+ ("vidioc_get_ext_ctrls: "
+ "cg2900_fm_get_block_scan_"
+ "result: returned %d",
+ status);
+ kfree_skb(skb);
+ break;
}
+ kfree_skb(skb);
+ } else {
+ /* Some other interrupt,
+ Queue it back */
+ spin_lock(&fm_spinlock);
+ skb_queue_head(&fm_interrupt_queue, skb);
+ spin_unlock(&fm_spinlock);
}
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "SeekStatus = %d, GlobalEvent = %d, "
- "numchannels = %d",
- cg2900_device.seekstatus,
- fm_event, no_of_block_scan_freq);
- if (ext_ctrl->controls->size == 0 &&
- ext_ctrl->controls->string == NULL) {
- if (cg2900_device.seekstatus ==
- FMR_SEEK_IN_PROGRESS &&
- CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND
- == fm_event) {
- spin_lock(&fm_spinlock);
- ext_ctrl->controls->size =
- no_of_block_scan_freq;
- cg2900_device.seekstatus
- = FMR_SEEK_NONE;
- fm_event =
- CG2900_EVENT_NO_EVENT;
- spin_unlock(&fm_spinlock);
- return -ENOSPC;
- }
- } else if (ext_ctrl->controls->size >=
- no_of_block_scan_freq &&
- ext_ctrl->controls->string != NULL) {
- dest_buffer =
- (u32 *) ext_ctrl->controls->string;
- while (index < no_of_block_scan_freq) {
- *(dest_buffer + index) =
- block_scan_rssi_level
- [index];
- index++;
- }
- ret_val = 0;
- return ret_val;
+ }
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "SeekStatus = %x, GlobalEvent = %x, "
+ "numchannels = %x",
+ cg2900_device.seekstatus,
+ fm_event, no_of_block_scan_freq);
+ if (ext_ctrl->controls->size == 0 &&
+ ext_ctrl->controls->string == NULL) {
+ if (cg2900_device.seekstatus ==
+ FMR_SEEK_IN_PROGRESS &&
+ CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND
+ == fm_event) {
+ spin_lock(&fm_spinlock);
+ ext_ctrl->controls->size =
+ no_of_block_scan_freq;
+ cg2900_device.seekstatus
+ = FMR_SEEK_NONE;
+ fm_event =
+ CG2900_EVENT_NO_EVENT;
+ spin_unlock(&fm_spinlock);
+ return -ENOSPC;
}
- break;
+ } else if (ext_ctrl->controls->size >=
+ no_of_block_scan_freq &&
+ ext_ctrl->controls->string != NULL) {
+ dest_buffer =
+ (u32 *) ext_ctrl->controls->string;
+ while (index < no_of_block_scan_freq) {
+ *(dest_buffer + index) =
+ block_scan_rssi_level
+ [index];
+ index++;
+ }
+ ret_val = 0;
+ return ret_val;
}
+ break;
case V4L2_CID_RDS_TX_DEVIATION:
- {
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_RDS_TX_DEVIATION");
- if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
- FM_ERR_REPORT("Invalid Ctrl Class = %d",
- ext_ctrl->ctrl_class);
- break;
- }
- status = cg2900_fm_tx_get_rds_deviation((u16 *) &
- ext_ctrl->
- controls->value);
- if (status == 0)
- ret_val = 0;
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_RDS_TX_DEVIATION");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %x",
+ ext_ctrl->ctrl_class);
break;
}
+ status = cg2900_fm_tx_get_rds_deviation((u16 *) &
+ ext_ctrl->
+ controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
case V4L2_CID_PILOT_TONE_ENABLED:
- {
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_PILOT_TONE_ENABLED");
- if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
- FM_ERR_REPORT("Invalid Ctrl Class = %d",
- ext_ctrl->ctrl_class);
- break;
- }
- status = cg2900_fm_tx_get_pilot_tone_status(
- (bool *)&ext_ctrl->controls->value);
- if (status == 0)
- ret_val = 0;
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_PILOT_TONE_ENABLED");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %x",
+ ext_ctrl->ctrl_class);
break;
}
+ status = cg2900_fm_tx_get_pilot_tone_status(
+ (bool *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
case V4L2_CID_PILOT_TONE_DEVIATION:
- {
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_PILOT_TONE_DEVIATION");
- if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
- FM_ERR_REPORT("Invalid Ctrl Class = %d",
- ext_ctrl->ctrl_class);
- break;
- }
- status = cg2900_fm_tx_get_pilot_deviation(
- (u16 *)&ext_ctrl->controls->value);
- if (status == 0)
- ret_val = 0;
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_PILOT_TONE_DEVIATION");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %x",
+ ext_ctrl->ctrl_class);
break;
}
+ status = cg2900_fm_tx_get_pilot_deviation(
+ (u16 *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
case V4L2_CID_TUNE_PREEMPHASIS:
- {
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_TUNE_PREEMPHASIS");
- if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
- FM_ERR_REPORT("Invalid Ctrl Class = %d",
- ext_ctrl->ctrl_class);
- break;
- }
- status = cg2900_fm_tx_get_preemphasis(
- (u8 *)&ext_ctrl->controls->value);
- if (status == 0)
- ret_val = 0;
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_TUNE_PREEMPHASIS");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %x",
+ ext_ctrl->ctrl_class);
break;
}
+ status = cg2900_fm_tx_get_preemphasis(
+ (u8 *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
case V4L2_CID_TUNE_POWER_LEVEL:
- {
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "V4L2_CID_TUNE_POWER_LEVEL");
- if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
- FM_ERR_REPORT("Invalid Ctrl Class = %d",
- ext_ctrl->ctrl_class);
- break;
- }
- status = cg2900_fm_tx_get_power_level(
- (u16 *)&ext_ctrl->controls->value);
- if (status == 0)
- ret_val = 0;
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_TUNE_POWER_LEVEL");
+ if (V4L2_CTRL_CLASS_FM_TX != ext_ctrl->ctrl_class) {
+ FM_ERR_REPORT("Invalid Ctrl Class = %x",
+ ext_ctrl->ctrl_class);
break;
}
- default:
- {
- FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
- "unsupported (id = %d)",
- (int)ext_ctrl->controls->id);
+ status = cg2900_fm_tx_get_power_level(
+ (u16 *)&ext_ctrl->controls->value);
+ if (status == 0)
+ ret_val = 0;
+ break;
+ case V4L2_CID_CG2900_RADIO_GET_INTERRUPT:
+ if (ext_ctrl->ctrl_class != V4L2_CTRL_CLASS_USER) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_GET_INTERRUPT "
+ "Unsupported ctrl_class = %04x",
+ ext_ctrl->ctrl_class);
+ break;
+ }
+ if (ext_ctrl->controls->size != FMR_GET_INTERRUPT_DATA_SIZE ||
+ ext_ctrl->controls->string == NULL) {
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: "
+ "V4L2_CID_CG2900_RADIO_GET_INTERRUPT "
+ "Invalid parameters, ext_ctrl->controls->size = %x "
+ "ext_ctrl->controls->string = %08x",
+ ext_ctrl->controls->size,
+ (unsigned int)ext_ctrl->controls->string);
+ ret_val = -ENOSPC;
+ break;
+ }
+ spin_lock(&fm_spinlock);
+ skb = skb_dequeue(&fm_interrupt_queue);
+ spin_unlock(&fm_spinlock);
+ if (!skb) {
+ /* No Interrupt, bad case */
+ FM_ERR_REPORT("V4L2_CID_CG2900_RADIO_GET_INTERRUPT: "
+ "No Interrupt to read");
+ fm_event = CG2900_EVENT_NO_EVENT;
+ break;
+ }
+ fm_event = (u8)skb->data[0];
+ interrupt_success = (s8)skb->data[1];
+ FM_DEBUG_REPORT("vidioc_get_ctrl: Interrupt = %x "
+ "interrupt_success = %x",
+ fm_event, interrupt_success);
+ fm_interrupt_buffer =
+ (int *) ext_ctrl->controls->string;
+ /* Interrupt that has occurred */
+ *fm_interrupt_buffer = cg2900_map_event_to_v4l2(fm_event);
+
+ /* Interrupt success or failed */
+ if (interrupt_success) {
+ /* Interrupt Success, return 0 */
+ *(fm_interrupt_buffer + 1) = 0;
+ } else {
+ spin_lock(&fm_spinlock);
+ no_of_scan_freq = 0;
+ no_of_block_scan_freq = 0;
+ spin_unlock(&fm_spinlock);
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ /* Clear the Interrupt flag */
+ fm_event = CG2900_EVENT_NO_EVENT;
+ kfree_skb(skb);
+ /* Interrupt Success, return negative error */
+ *(fm_interrupt_buffer + 1) = -1;
+ FM_ERR_REPORT("vidioc_get_ext_ctrls: Interrupt = %d "
+ "failed with reason = %d",
+ (*fm_interrupt_buffer),
+ (*(fm_interrupt_buffer + 1)));
+ break;
+ }
+
+ if (CG2900_EVENT_MONO_STEREO_TRANSITION
+ == fm_event) {
+ /*
+ * In case of Mono/Stereo Interrupt,
+ * get the current value from chip
+ */
+ status = cg2900_fm_get_mode(&mode);
+ cg2900_device.rx_stereo_status = (bool)mode;
+ /* Clear the Interrupt flag */
+ fm_event = CG2900_EVENT_NO_EVENT;
+ kfree_skb(skb);
+ } else if (CG2900_EVENT_SCAN_CANCELLED ==
+ fm_event) {
+ /* Scan/Search cancelled by User */
+ spin_lock(&fm_spinlock);
+ no_of_scan_freq = 0;
+ no_of_block_scan_freq = 0;
+ spin_unlock(&fm_spinlock);
+ cg2900_device.seekstatus = FMR_SEEK_NONE;
+ /* Clear the Interrupt flag */
+ fm_event = CG2900_EVENT_NO_EVENT;
+ kfree_skb(skb);
+ } else {
+ /* Queue the interrupt back
+ for later dequeuing */
+ FM_DEBUG_REPORT("V4L2_CID_CG2900"
+ "_RADIO_GET_INTERRUPT: "
+ "Queuing the interrupt"
+ "again to head of list");
+ spin_lock(&fm_spinlock);
+ skb_queue_head(&fm_interrupt_queue, skb);
+ spin_unlock(&fm_spinlock);
}
+ ret_val = 0;
+ break;
+ default:
+ FM_DEBUG_REPORT("vidioc_get_ext_ctrls: "
+ "unsupported (id = %x)",
+ ext_ctrl->controls->id);
}
error:
@@ -1622,8 +1780,7 @@ static int vidioc_set_ext_ctrls(
FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
"V4L2_CID_CG2900_RADIO_RDS_AF_SWITCH_START: "
"AF Switch Freq =%d Hz AF Switch PI = %04x",
- af_switch_freq,
- af_switch_pi);
+ (int)af_switch_freq, af_switch_pi);
if (af_switch_freq < (FMR_CHINA_LOW_FREQ_IN_MHZ
* FMR_HZ_TO_MHZ_CONVERTER) ||
@@ -1713,7 +1870,7 @@ static int vidioc_set_ext_ctrls(
FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
"V4L2_CID_RDS_TX_PS_NAME, "
- "PSN = %s, Len = %d",
+ "PSN = %s, Len = %x",
ext_ctrl->controls->string,
ext_ctrl->controls->size);
@@ -1735,7 +1892,7 @@ static int vidioc_set_ext_ctrls(
FM_DEBUG_REPORT("vidioc_set_ext_ctrls: "
"V4L2_CID_RDS_TX_RADIO_TEXT, "
- "RT = %s, Len = %d",
+ "RT = %s, Len = %x",
ext_ctrl->controls->string,
ext_ctrl->controls->size);
@@ -1911,8 +2068,8 @@ static int vidioc_set_ext_ctrls(
"BLOCKSCAN_START: "
"Start Freq = %d Hz "
"End Freq = %d Hz",
- start_freq,
- end_freq);
+ (int)start_freq,
+ (int)end_freq);
result_freq = end_freq - start_freq;
no_of_block_scan_channels =
@@ -1922,8 +2079,8 @@ static int vidioc_set_ext_ctrls(
if (end_freq < start_freq) {
FM_ERR_REPORT("Start Freq (%d Hz) "
" > End Freq (%d Hz)",
- start_freq,
- end_freq);
+ (int)start_freq,
+ (int)end_freq);
break;
}
@@ -1931,7 +2088,7 @@ static int vidioc_set_ext_ctrls(
(start_freq > high_freq)) {
FM_ERR_REPORT("Out of Band Freq: "
"Start Freq = %d Hz",
- start_freq);
+ (int)start_freq);
break;
}
@@ -1939,15 +2096,15 @@ static int vidioc_set_ext_ctrls(
(end_freq > high_freq)) {
FM_ERR_REPORT("Out of Band Freq: "
"End Freq = %d Hz",
- end_freq);
+ (int)end_freq);
break;
}
/* Maximum allowed block scan range */
if (FMR_MAX_BLOCK_SCAN_CHANNELS <
no_of_block_scan_channels) {
- FM_ERR_REPORT("No of channels (%d)"
- "exceeds Max Block Scan (%d)",
+ FM_ERR_REPORT("No of channels (%x)"
+ "exceeds Max Block Scan (%x)",
no_of_block_scan_channels,
FMR_MAX_BLOCK_SCAN_CHANNELS);
break;
@@ -2153,8 +2310,8 @@ static int vidioc_set_hw_freq_seek(
FM_INFO_REPORT("vidioc_set_hw_freq_seek");
- FM_DEBUG_REPORT("vidioc_set_hw_freq_seek: Status = %d, "
- "Upwards = %d, Wrap Around = %d",
+ FM_DEBUG_REPORT("vidioc_set_hw_freq_seek: Status = %x, "
+ "Upwards = %x, Wrap Around = %x",
cg2900_device.seekstatus,
freq_seek->seek_upward, freq_seek->wrap_around);
@@ -2322,6 +2479,39 @@ static void cg2900_convert_err_to_v4l2(
}
/**
+ * cg2900_map_event_to_v4l2()- Maps cg2900 event to v4l2 events .
+ *
+ * This function maps cg2900 events to corresponding v4l2 events.
+ *
+ * @event: This contains the cg2900 event to be converted.
+ *
+ * Returns: Corresponding V4L2 events.
+ */
+static int cg2900_map_event_to_v4l2(
+ u8 event
+ )
+{
+ switch (event) {
+ case CG2900_EVENT_MONO_STEREO_TRANSITION:
+ return V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION;
+ case CG2900_EVENT_SEARCH_CHANNEL_FOUND:
+ return V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED;
+ case CG2900_EVENT_SCAN_CHANNELS_FOUND:
+ return V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED;
+ case CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND:
+ return V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED;
+ case CG2900_EVENT_SCAN_CANCELLED:
+ return V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED;
+ case CG2900_EVENT_DEVICE_RESET:
+ return V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET;
+ case CG2900_EVENT_RDS_EVENT:
+ return V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED;
+ default:
+ return V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN;
+ }
+}
+
+/**
* cg2900_open()- This function nitializes and switches on FM.
*
* This is called when the application opens the character device.
@@ -2471,21 +2661,23 @@ static ssize_t cg2900_read(
int current_rds_grp;
int index = 0;
int blocks_to_read;
- int ret;
struct v4l2_rds_data rdsbuf[MAX_RDS_GROUPS * NUM_OF_RDS_BLOCKS];
struct v4l2_rds_data *rdslocalbuf = rdsbuf;
+ struct sk_buff *skb;
FM_INFO_REPORT("cg2900_read");
blocks_to_read = (count / sizeof(struct v4l2_rds_data));
if (!cg2900_device.rx_rds_enabled) {
+ /* Remove all Interrupts from the queue */
+ skb_queue_purge(&fm_interrupt_queue);
FM_INFO_REPORT("cg2900_read: returning 0");
return 0;
}
if (count % sizeof(struct v4l2_rds_data) != 0) {
- FM_ERR_REPORT("cg2900_read: Invalid Number of bytes %d "
+ FM_ERR_REPORT("cg2900_read: Invalid Number of bytes %x "
"requested to read", count);
return -EIO;
}
@@ -2496,44 +2688,6 @@ static ssize_t cg2900_read(
return -EAGAIN;
}
- if (file->f_flags & O_NONBLOCK) {
- /* Non blocking mode selected by application */
- if (fm_rds_info.rds_head == fm_rds_info.rds_tail) {
- FM_DEBUG_REPORT("cg2900_read: Non Blocking mode "
- "selected by application, returning as "
- "no RDS data is available");
- return -EAGAIN;
- }
- }
-
- if (fm_rds_info.rds_head == fm_rds_info.rds_tail) {
- /*
- * Blocking mode selected by application
- * if data is not available block on read queue
- */
- FM_DEBUG_REPORT("cg2900_read: Blocking mode "
- "selected by application, waiting till "
- "rds data is available");
- cg2900_device.wait_on_read_queue = true;
- ret = wait_event_interruptible(cg2900_read_queue,
- (fm_rds_info.rds_head !=
- fm_rds_info.rds_tail));
- cg2900_device.wait_on_read_queue = false;
- FM_DEBUG_REPORT("cg2900_read: "
- "wait_event_interruptible returned = %d", ret);
- if (ret == -ERESTARTSYS)
- return -EINTR;
- }
-
- /*
- * Check again that in the meantime RDS is not disabled
- * by the user, If yes, return.
- */
- if (!cg2900_device.rx_rds_enabled) {
- FM_INFO_REPORT("cg2900_read: returning 0");
- return 0;
- }
-
current_rds_grp = fm_rds_info.rds_group_sent;
if ((fm_rds_info.rds_head == fm_rds_info.rds_tail) ||
@@ -2614,6 +2768,29 @@ static ssize_t cg2900_read(
if (current_rds_grp == MAX_RDS_GROUPS) {
fm_rds_info.rds_tail++;
current_rds_grp = 0;
+ /* Dequeue Rds Interrupt here */
+ skb = skb_dequeue(&fm_interrupt_queue);
+ if (!skb) {
+ /* No Interrupt, bad case */
+ FM_ERR_REPORT("cg2900_read: "
+ "skb is NULL. Major error");
+ spin_unlock(&fm_spinlock);
+ return 0;
+ }
+ fm_event = (u8)skb->data[0];
+ if (fm_event != CG2900_EVENT_RDS_EVENT) {
+ /* RDS interrupt not found */
+ FM_ERR_REPORT("cg2900_read:"
+ "RDS interrupt not found"
+ "for de-queuing."
+ "fm_event = %x", fm_event);
+ /* Queue the event back */
+ skb_queue_head(&fm_interrupt_queue,
+ skb);
+ spin_unlock(&fm_spinlock);
+ return 0;
+ }
+ kfree_skb(skb);
}
break;
default:
@@ -2637,13 +2814,12 @@ static ssize_t cg2900_read(
if (fm_rds_info.rds_tail == MAX_RDS_BUFFER)
fm_rds_info.rds_tail = 0;
+ spin_unlock(&fm_spinlock);
if (copy_to_user(data, rdslocalbuf, count)) {
- spin_unlock(&fm_spinlock);
FM_ERR_REPORT("cg2900_read: Error "
"in copying, returning");
return -EFAULT;
}
- spin_unlock(&fm_spinlock);
return count;
}
@@ -2651,17 +2827,16 @@ static ssize_t cg2900_read(
* cg2900_poll()- Check if the operation is complete or not.
*
* This function is invoked by application on calling poll() and is used to
- * wait till the desired operation seek/Band Scan are complete.
- * Driver blocks till the seek/BandScan Operation is complete. The application
- * decides to read the results of seek/Band Scan based on the returned value of
- * this function.
+ * wait till the any FM interrupt is received from the chip.
+ * The application decides to read the corresponding data depending on FM
+ * interrupt.
*
* @file: File structure.
* @wait: poll table
*
* Returns:
- * POLLRDNORM when Scan Band/Block Scan/Seek station is complete.
- * POLLHUP when seek station/Band Scan is stopped by user using Stop Scan.
+ * POLLRDNORM|POLLIN whenever FM interrupt has occurred.
+ * 0 whenever the call times out.
*/
static unsigned int cg2900_poll(
struct file *file,
@@ -2672,32 +2847,17 @@ static unsigned int cg2900_poll(
FM_INFO_REPORT("cg2900_poll");
- poll_wait(file, &cg2900_poll_queue, wait);
-
- if (cg2900_device.seekstatus == FMR_SEEK_IN_PROGRESS &&
- ((fm_event == CG2900_EVENT_SCAN_CHANNELS_FOUND) ||
- (fm_event == CG2900_EVENT_BLOCK_SCAN_CHANNELS_FOUND) ||
- (fm_event == CG2900_EVENT_SEARCH_CHANNEL_FOUND))) {
- /* Scan Completed, send event to application */
- FM_DEBUG_REPORT("poll_wait returning POLLRDNORM");
- ret_val = POLLRDNORM;
- goto done;
+ /* Check if we have some data in queue already */
+ if (skb_queue_empty(&fm_interrupt_queue)) {
+ FM_DEBUG_REPORT("cg2900_poll: Interrupt Queue Empty, waiting");
+ /* No Interrupt, wait for it to occur */
+ poll_wait(file, &cg2900_poll_queue, wait);
}
-
- if (fm_event == CG2900_EVENT_SCAN_CANCELLED) {
- /* Scan/Search cancelled by User */
- spin_lock(&fm_spinlock);
- no_of_scan_freq = 0;
- no_of_block_scan_freq = 0;
- spin_unlock(&fm_spinlock);
- cg2900_device.seekstatus = FMR_SEEK_NONE;
- fm_event = CG2900_EVENT_NO_EVENT;
- FM_DEBUG_REPORT("cg2900_poll: Cancel operation "
- "returning POLLHUP");
- FM_DEBUG_REPORT("poll_wait returning POLLHUP");
- ret_val = POLLHUP;
+ /* Check if we now have interrupt to read in queue */
+ if (skb_queue_empty(&fm_interrupt_queue))
goto done;
- }
+
+ ret_val = POLLIN | POLLRDNORM;
done:
FM_DEBUG_REPORT("poll_wait returning %d", ret_val);
@@ -2747,6 +2907,7 @@ static int __devinit radio_cg2900_probe(
mutex_init(&fm_mutex);
spin_lock_init(&fm_spinlock);
init_waitqueue_head(&cg2900_poll_queue);
+ skb_queue_head_init(&fm_interrupt_queue);
users = 0;
return 0;
}
@@ -2767,10 +2928,15 @@ static int __devexit radio_cg2900_remove(
)
{
FM_INFO_REPORT("radio_cg2900_remove");
+ /* Wake up the poll queue since we are now exiting */
+ wake_up_poll_queue();
+ /* Give some time for application to exit the poll thread */
+ schedule_timeout_interruptible(msecs_to_jiffies(500));
/* Try to Switch Off FM in case it is still switched on */
cg2900_fm_switch_off();
cg2900_fm_deinit();
+ skb_queue_purge(&fm_interrupt_queue);
mutex_destroy(&fm_mutex);
video_unregister_device(&cg2900_video_device);
fmd_set_dev(NULL);
@@ -2814,24 +2980,20 @@ void wake_up_poll_queue(void)
wake_up_interruptible(&cg2900_poll_queue);
}
-void wake_up_read_queue(void)
-{
- FM_INFO_REPORT("wake_up_read_queue");
- if (cg2900_device.wait_on_read_queue)
- wake_up_interruptible(&cg2900_read_queue);
-}
-
void cg2900_handle_device_reset(void)
{
+ struct sk_buff *skb;
FM_INFO_REPORT("cg2900_handle_device_reset");
- users = 0;
- cg2900_device.state = FMR_SWITCH_OFF;
- cg2900_device.frequency = 0;
- cg2900_device.rx_rds_enabled = false;
- cg2900_device.muted = false;
- cg2900_device.seekstatus = FMR_SEEK_NONE;
- fm_event = CG2900_EVENT_NO_EVENT;
- no_of_scan_freq = 0;
+ skb = alloc_skb(SKB_FM_INTERRUPT_DATA, GFP_KERNEL);
+ if (!skb) {
+ FM_ERR_REPORT("cg2900_handle_device_reset: "
+ "Unable to Allocate Memory");
+ return;
+ }
+ skb->data[0] = CG2900_EVENT_DEVICE_RESET;
+ skb->data[1] = true;
+ skb_queue_tail(&fm_interrupt_queue, skb);
+ wake_up_poll_queue();
}
module_init(radio_cg2900_init);
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 7df8cce67fb..42332ae92fd 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1700,6 +1700,17 @@ enum v4l2_cg2900_radio_deemphasis {
V4L2_CG2900_RADIO_DEEMPHASIS_50_uS = 1,
V4L2_CG2900_RADIO_DEEMPHASIS_75_uS = 2,
};
+#define V4L2_CID_CG2900_RADIO_GET_INTERRUPT (V4L2_CID_CG2900_RADIO_PRIVATE_BASE+16)
+enum v4l2_cg2900_radio_interrupt {
+ V4L2_CG2900_RADIO_INTERRUPT_UNKNOWN = 0,
+ V4L2_CG2900_RADIO_INTERRUPT_SEARCH_COMPLETED = 1,
+ V4L2_CG2900_RADIO_INTERRUPT_BAND_SCAN_COMPLETED = 2,
+ V4L2_CG2900_RADIO_INTERRUPT_BLOCK_SCAN_COMPLETED = 3,
+ V4L2_CG2900_RADIO_INTERRUPT_SCAN_CANCELLED = 4,
+ V4L2_CG2900_RADIO_INTERRUPT_MONO_STEREO_TRANSITION = 5,
+ V4L2_CG2900_RADIO_INTERRUPT_DEVICE_RESET = 6,
+ V4L2_CG2900_RADIO_INTERRUPT_RDS_RECEIVED = 7
+};
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)