summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-01-30 09:52:05 -0500
committerMichael Sevakis <jethead71@rockbox.org>2017-02-11 22:19:32 -0500
commitfc9695eb47732e1c189e2f033dbd55e5c346e8c4 (patch)
tree550830e9af67f47e3eb87587770b25d22110f57a
parent6436c6e749ab04fbd5d97804a6a1c3b3122b326d (diff)
downloadrockbox-fc9695eb47732e1c189e2f033dbd55e5c346e8c4.tar.gz
rockbox-fc9695eb47732e1c189e2f033dbd55e5c346e8c4.zip
Improve radio RDS driver and framework
* Remove unused bits like the radio event and simplify basic radio interface. It can be more self-contained with rds.h only required by radio and tuner code. * Add post-processing to text a-la Silicon Labs AN243. The chip's error correction can only do so much; additional checks are highly recommended. Simply testing for two identical messages in a row is extremely effective and I've never seen corrupted text since doing that, even with mediocre reception. Groups segments must arrive in order, not randomly; logic change only accepts them in order, starting at 0. Time readout was made a bit better but really we'd need to use verbose mode and ensure that no errors were seen during receiving of time and more checks would be need to have a stable PI. The text is the important bit anyway. * Time out of stale text. * Text is no longer updated until a complete group has been received, as is specified in the standard. Perhaps go back to scrolling text lines in the radio screen? * Add proper character conversion to UTF-8. Only the default G0 table for the moment. The other two could be added in. * Add variants "RDS_CFG_PROCESS" and "RDS_CFG_PUSH" to allow the option for processed RDS data to be pushed to the driver and still do proper post-processing (only text conversion for now for the latter). Change-Id: I4d83f8b2e89a209a5096d15ec266477318c66925
-rw-r--r--apps/debug_menu.c24
-rw-r--r--apps/gui/skin_engine/skin_tokens.c6
-rw-r--r--apps/radio/radio.c4
-rw-r--r--firmware/drivers/rds.c486
-rw-r--r--firmware/drivers/tuner/ipod_remote_tuner.c33
-rw-r--r--firmware/drivers/tuner/si4700.c87
-rw-r--r--firmware/export/config.h10
-rw-r--r--firmware/export/config/gigabeats.h2
-rw-r--r--firmware/export/config/ipod4g.h1
-rw-r--r--firmware/export/config/ipod6g.h1
-rw-r--r--firmware/export/config/ipodcolor.h1
-rw-r--r--firmware/export/config/ipodmini1g.h1
-rw-r--r--firmware/export/config/ipodmini2g.h1
-rw-r--r--firmware/export/config/ipodnano1g.h1
-rw-r--r--firmware/export/config/ipodnano2g.h1
-rw-r--r--firmware/export/config/ipodvideo.h1
-rw-r--r--firmware/export/ipod_remote_tuner.h3
-rw-r--r--firmware/export/rds.h39
-rw-r--r--firmware/export/si4700.h28
-rw-r--r--firmware/export/tuner.h19
-rw-r--r--firmware/target/arm/as3525/fmradio-i2c-as3525.c8
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c8
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c57
-rw-r--r--firmware/target/hosted/samsungypr/radio-ypr.c6
-rw-r--r--firmware/tuner.c23
25 files changed, 511 insertions, 340 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 000dbdc4fc..d761b9f868 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -127,10 +127,6 @@
127#include "iap.h" 127#include "iap.h"
128#endif 128#endif
129 129
130#ifdef HAVE_RDS_CAP
131#include "rds.h"
132#endif
133
134#include "talk.h" 130#include "talk.h"
135 131
136static const char* threads_getname(int selected_item, void *data, 132static const char* threads_getname(int selected_item, void *data,
@@ -2166,17 +2162,25 @@ static int radio_callback(int btn, struct gui_synclist *lists)
2166#endif /* TEA5760 */ 2162#endif /* TEA5760 */
2167 2163
2168#ifdef HAVE_RDS_CAP 2164#ifdef HAVE_RDS_CAP
2169 simplelist_addline("PI:%04X PS:'%8s'", 2165 {
2170 rds_get_pi(), rds_get_ps()); 2166 char buf[65*4];
2171 simplelist_addline("RT:%s", 2167 uint16_t pi;
2172 rds_get_rt()); 2168 time_t seconds;
2173 time_t seconds = rds_get_ct(); 2169
2170 tuner_get_rds_info(RADIO_RDS_NAME, buf, sizeof (buf));
2171 tuner_get_rds_info(RADIO_RDS_PROGRAM_INFO, &pi, sizeof (pi));
2172 simplelist_addline("PI:%04X PS:'%8s'", pi, buf);
2173 tuner_get_rds_info(RADIO_RDS_TEXT, buf, sizeof (buf));
2174 simplelist_addline("RT:%s", buf);
2175 tuner_get_rds_info(RADIO_RDS_CURRENT_TIME, &seconds, sizeof (seconds));
2176
2174 struct tm* time = gmtime(&seconds); 2177 struct tm* time = gmtime(&seconds);
2175 simplelist_addline( 2178 simplelist_addline(
2176 "CT:%4d-%02d-%02d %02d:%02d", 2179 "CT:%4d-%02d-%02d %02d:%02d",
2177 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, 2180 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
2178 time->tm_hour, time->tm_min, time->tm_sec); 2181 time->tm_hour, time->tm_min, time->tm_sec);
2179#endif 2182 }
2183#endif /* HAVE_RDS_CAP */
2180 return ACTION_REDRAW; 2184 return ACTION_REDRAW;
2181} 2185}
2182static bool dbg_fm_radio(void) 2186static bool dbg_fm_radio(void)
diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c
index c07d4cf2df..cbf732fe10 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -540,9 +540,11 @@ const char *get_radio_token(struct wps_token *token, int preset_offset,
540#ifdef HAVE_RDS_CAP 540#ifdef HAVE_RDS_CAP
541 return "rds"; 541 return "rds";
542 case SKIN_TOKEN_RDS_NAME: 542 case SKIN_TOKEN_RDS_NAME:
543 return tuner_get_rds_info(RADIO_RDS_NAME); 543 tuner_get_rds_info(RADIO_RDS_NAME, buf, buf_size);
544 return buf;
544 case SKIN_TOKEN_RDS_TEXT: 545 case SKIN_TOKEN_RDS_TEXT:
545 return tuner_get_rds_info(RADIO_RDS_TEXT); 546 tuner_get_rds_info(RADIO_RDS_TEXT, buf, buf_size);
547 return buf;
546#else 548#else
547 return NULL; /* end of the SKIN_TOKEN_HAVE_RDS case */ 549 return NULL; /* end of the SKIN_TOKEN_HAVE_RDS case */
548#endif /* HAVE_RDS_CAP */ 550#endif /* HAVE_RDS_CAP */
diff --git a/apps/radio/radio.c b/apps/radio/radio.c
index d89c0be304..77fd324c8f 100644
--- a/apps/radio/radio.c
+++ b/apps/radio/radio.c
@@ -696,10 +696,6 @@ void radio_screen(void)
696 696
697 default: 697 default:
698 default_event_handler(button); 698 default_event_handler(button);
699#ifdef HAVE_RDS_CAP
700 if (tuner_get(RADIO_EVENT))
701 update_type = SKIN_REFRESH_ALL;
702#endif
703 if (!tuner_get(RADIO_PRESENT)) 699 if (!tuner_get(RADIO_PRESENT))
704 { 700 {
705#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) 701#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
diff --git a/firmware/drivers/rds.c b/firmware/drivers/rds.c
index 2f296cdbe8..731207de59 100644
--- a/firmware/drivers/rds.c
+++ b/firmware/drivers/rds.c
@@ -21,73 +21,195 @@
21#include <stdbool.h> 21#include <stdbool.h>
22#include <stdint.h> 22#include <stdint.h>
23#include <string.h> 23#include <string.h>
24#include <strlcpy.h>
25#include <system.h> 24#include <system.h>
26#include <kernel.h> 25#include <kernel.h>
27#include "rds.h" 26#include "rds.h"
28#include "time.h" 27#include "time.h"
28#include "string-extra.h"
29
30#define TIMED_OUT(tick) \
31 TIME_AFTER(current_tick, (tick))
32#define SET_TIMEOUT(tick, duration) \
33 ({ (tick) = current_tick + (duration); })
34
35/* Driver keeps strings in native character format, translating on demand */
36static char ps_copy[9]; /* copy of final message */
37static long ps_copy_tmo; /* timeout to discard programme service name */
38static char rt_copy[65]; /* copy of final message */
39static long rt_copy_tmo; /* time to discard radio text */
40static uint16_t pi_code; /* current programme identifier code */
41static time_t ct_data; /* date/time (not robust; not essential) */
42
43/* timeout before text times out */
44#define TEXT_TIMEOUT (30 * HZ)
45
46/* timeout before RDS is considered idle and is reset */
47#define RDS_TIMEOUT (10 * HZ)
48static long rds_timeout; /* timeout until rds is thought idle */
49static bool rds_active; /* if active, timeouts are monitored */
50
51#if (CONFIG_RDS & RDS_CFG_PROCESS)
52/* timeout before group segment obsolescence */
53#define GROUP0_TIMEOUT (2 * HZ)
54#define GROUP2_TIMEOUT (10 * HZ)
55
56/* programme identification (not robust; not really used anyway) */
57static uint16_t pi_last; /* previously read code */
58
59/* programme service name */
60static char ps_data[2][9]; /* round-robin driver work queue */
61static int ps_segment; /* next expected segment */
62static long ps_timeout; /* timeout to receive full group */
63static int ps_data_idx; /* ps_data[0 or 1] */
64#define PS_DATA_INC(x) ps_data[ps_data_idx ^= (x)]
29 65
30// timeout before segment obsolescence
31#define PS_SEGMENT_TIMEOUT (HZ / 2)
32#define RT_SEGMENT_TIMEOUT (10 * HZ)
33
34/* programme identification */
35static uint16_t pi_code;
36static uint16_t pi_last;
37/* program service name */
38static char ps_data[9];
39static char ps_copy[9];
40static long ps_segment_timeout[4];
41static int ps_segment;// bitmap of received segments
42/* radio text */ 66/* radio text */
43static char rt_data[65]; 67static char rt_data[2][65]; /* round-robin driver work queue */
44static char rt_copy[65]; 68static int rt_segment; /* next expected segment */
45static long rt_segment_timeout[16]; 69static long rt_timeout; /* timeout to receive full group */
46static int rt_segment;// bitmap of received segments 70static int rt_abflag; /* message change flag */
47static int rt_abflag; 71static int rt_data_idx; /* rt_data[0 or 1] */
48/* date/time */ 72#define RT_DATA_INC(x) rt_data[rt_data_idx ^= (x)]
49static time_t ct_data; 73#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
50 74
51#ifdef RDS_ISR_PROCESSING 75#if (CONFIG_RDS & RDS_CFG_ISR)
52/* Functions are called in ISR context */ 76/* Functions are called in ISR context */
53#define rds_disable_irq_save() disable_irq_save() 77#define rds_disable_irq_save() disable_irq_save()
54#define rds_restore_irq(old) restore_irq(old) 78#define rds_restore_irq(old) restore_irq(old)
55/* Need triple buffer so string isn't clobbered while caller is using it */ 79#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
56static inline char * get_ps(void) 80#define rds_disable_irq_save() 0
81#define rds_restore_irq(old) ((void)(old))
82#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
83
84/* RDS code table G0 to UTF-8 translation */
85static const uint16_t rds_tbl_g0[0x100-0x20] =
57{ 86{
58 static char ps_out[9]; 87 /* codes 0x00 .. 0x1F are omitted because they are identities and not
59 int oldlevel = rds_disable_irq_save(); 88 * actually spec'ed as part of the character maps anyway */
60 strcpy(ps_out, ps_copy); 89 /* 0 1 2 3 4 5 6 7 */
61 rds_restore_irq(oldlevel); 90 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, /* 20 */
62 return ps_out; 91 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, /* 28 */
92 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, /* 30 */
93 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, /* 38 */
94 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, /* 40 */
95 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, /* 48 */
96 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, /* 50 */
97 0x0058, 0x0059, 0x005A, 0x005B, 0x005B, 0x005D, 0x2015, 0x005F, /* 58 */
98 0x2016, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, /* 60 */
99 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, /* 68 */
100 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, /* 70 */
101 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x203E, 0x0020, /* 78 */
102 0x00E1, 0x00E0, 0x00E9, 0x00E8, 0x00ED, 0x00EC, 0x00F3, 0x00F2, /* 80 */
103 0x00FA, 0x00F9, 0x00D1, 0x00C7, 0x015E, 0x00DF, 0x00A1, 0x0132, /* 88 */
104 0x00E2, 0x00E4, 0x00EA, 0x00EB, 0x00EE, 0x00EF, 0x00F4, 0x00F6, /* 90 */
105 0x00FB, 0x00FC, 0x00F1, 0x00E7, 0x015F, 0x01E7, 0x0131, 0x0133, /* 98 */
106 0x00AA, 0x03B1, 0x00A9, 0x2030, 0x01E6, 0x011B, 0x0148, 0x0151, /* A0 */
107 0x03C0, 0x20A0, 0x00A3, 0x0024, 0x2190, 0x2191, 0x2192, 0x2193, /* A8 */
108 0x00BA, 0x00B9, 0x00B2, 0x00B3, 0x00B1, 0x0130, 0x0144, 0x0171, /* B0 */
109 0x00B5, 0x00BF, 0x00F7, 0x00B0, 0x00BC, 0x00BD, 0x00BE, 0x00A7, /* B8 */
110 0x00C1, 0x00C0, 0x00C9, 0x00C8, 0x00CD, 0x00CC, 0x00D3, 0x00D2, /* C0 */
111 0x00DA, 0x00D9, 0x0158, 0x010C, 0x0160, 0x017D, 0x0110, 0x013F, /* C8 */
112 0x00C2, 0x00C4, 0x00CA, 0x00CB, 0x00CE, 0x00CF, 0x00D4, 0x00D6, /* D0 */
113 0x00DB, 0x00DC, 0x0159, 0x010D, 0x0161, 0x017E, 0x0111, 0x0140, /* D8 */
114 0x00C3, 0x00C5, 0x00C6, 0x0152, 0x0177, 0x00DD, 0x00D5, 0x00D8, /* E0 */
115 0x00DE, 0x014A, 0x0158, 0x0106, 0x015A, 0x0179, 0x0166, 0x00F0, /* E8 */
116 0x00E3, 0x00E5, 0x00E6, 0x0153, 0x0175, 0x00FD, 0x00F5, 0x00F8, /* F0 */
117 0x00FE, 0x014B, 0x0159, 0x0107, 0x015B, 0x017A, 0x0167, 0x0020, /* F8 */
118};
119
120/* could add tables G1 and G2 without much trouble */
121
122/* write one UTF-8 character; returns original 'dst' if insufficient space */
123static char * convert_rds_char(char *dst, unsigned int c, size_t dstsize)
124{
125 unsigned int u = c >= 0x20 ? (rds_tbl_g0 - 0x20)[c] : c;
126
127 if (LIKELY(u <= 0x7F)) {
128 /* U+0000 .. U+007F -> 0xxx xxxx */
129 if (dstsize > 1) {
130 *dst++ = u;
131 }
132 }
133 else if (u <= 0x7FF) {
134 /* U+0080 .. U+07FF -> 110x xxxx 10 xx xxxx */
135 if (dstsize > 2) {
136 *dst++ = 0xC0 | (u >> 6);
137 *dst++ = 0x80 | (u & 0x3F);
138 }
139 }
140 else /* if (u <= 0xFFFF) */ {
141 /* U+0800 .. U+FFFF -> 1110 xxxx 10xx xxxx 10xx xxxx */
142 if (dstsize > 3) {
143 *dst++ = 0xE0 | (u >> 12);
144 *dst++ = 0x80 | ((u >> 6) & 0x3F);
145 *dst++ = 0x80 | (u & 0x3F);
146 }
147 }
148#if 0 /* No four-byte characters are used right now */
149 else {
150 /* U+10000 .. U+10FFFF -> 11110xxx 10xx xxxx 10xx xxxx 10xx xxxx */
151 if (dstsize > 4) {
152 *dst++ = 0xF0 | (c >> 18);
153 *dst++ = 0x80 | ((c >> 12) & 0x3F);
154 *dst++ = 0x80 | ((c >> 6) & 0x3F);
155 *dst++ = 0x80 | (c & 0x3F);
156 }
157 }
158#endif /* 0 */
159 return dst;
63} 160}
64static inline char * get_rt(void) 161
162/* Copy RDS character string with conversion to UTF-8
163 * Acts like strlcpy but won't split multibyte characters */
164static size_t copy_rds_string(char *dst, const char *src, size_t dstsize)
65{ 165{
66 static char rt_out[65]; 166 char *p = dst;
67 int oldlevel = rds_disable_irq_save(); 167 unsigned int c;
68 strcpy(rt_out, rt_copy); 168
69 rds_restore_irq(oldlevel); 169 while ((c = (unsigned char)*src++)) {
70 return rt_out; 170 char *q = p;
171
172 p = convert_rds_char(q, c, dstsize);
173 if (p == q) {
174 dst -= dstsize;
175 break;
176 }
177
178 dstsize -= p - q;
179 }
180
181 if (dstsize > 0) {
182 *p = '\0';
183 }
184
185 return p - dst;
186}
187
188/* indicate recent processing activity */
189static void register_activity(void)
190{
191 SET_TIMEOUT(rds_timeout, RDS_TIMEOUT);
192 rds_active = true;
71} 193}
72#else /* ndef RDS_ISR_PROCESSING */
73#define rds_disable_irq_save() 0
74#define rds_restore_irq(old) ((void)(old))
75static inline char * get_ps(void) { return ps_copy; }
76static inline char * get_rt(void) { return rt_copy; }
77#endif /* RDS_ISR_PROCESSING */
78 194
79/* resets the rds parser */ 195/* resets the rds parser */
80void rds_reset(void) 196void rds_reset(void)
81{ 197{
82 int oldlevel = rds_disable_irq_save(); 198 int oldlevel = rds_disable_irq_save();
83 199
84 pi_code = 0; 200 /* reset general info */
85 pi_last = 0; 201 pi_code = 0;
202 ct_data = 0;
86 ps_copy[0] = '\0'; 203 ps_copy[0] = '\0';
87 ps_segment = 0;
88 rt_copy[0] = '\0'; 204 rt_copy[0] = '\0';
205 rds_active = false;
206
207#if (CONFIG_RDS & RDS_CFG_PROCESS)
208 /* reset driver info */
209 pi_last = 0;
210 ps_segment = 0;
89 rt_segment = 0; 211 rt_segment = 0;
90 ct_data = 0; 212#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
91 213
92 rds_restore_irq(oldlevel); 214 rds_restore_irq(oldlevel);
93} 215}
@@ -98,105 +220,141 @@ void rds_init(void)
98 rds_reset(); 220 rds_reset();
99} 221}
100 222
223/* sync RDS state */
224void rds_sync(void)
225{
226 int oldlevel = rds_disable_irq_save();
227
228 if (rds_active) {
229 if (TIMED_OUT(rds_timeout)) {
230 rds_reset();
231 }
232 else {
233 if (TIMED_OUT(ps_copy_tmo)) {
234 ps_copy[0] = '\0';
235 }
236 if (TIMED_OUT(rt_copy_tmo)) {
237 rt_copy[0] = '\0';
238 }
239 }
240 }
241
242 rds_restore_irq(oldlevel);
243}
244
245#if (CONFIG_RDS & RDS_CFG_PROCESS)
101/* handles a group 0 packet, returns true if a new message was received */ 246/* handles a group 0 packet, returns true if a new message was received */
102static bool handle_group0(uint16_t data[4]) 247static void handle_group0(const uint16_t data[4])
103{ 248{
104 int segment, pos; 249 int segment, pos;
105 250 char *ps;
106 /* remove obsolete segments */
107 for(int i = 0; i < 4; i++)
108 if(TIME_AFTER(current_tick, ps_segment_timeout[i]))
109 ps_segment &= ~(1 << i);
110 251
111 segment = data[1] & 3; 252 segment = data[1] & 3;
112 253
254 if (segment == 0) {
255 ps_segment = 0;
256 }
257 else if (segment != ps_segment || TIMED_OUT(ps_timeout)) {
258 ps_segment = 0;
259 return;
260 }
261
113 /* store data */ 262 /* store data */
114 pos = segment * 2; 263 pos = segment * 2;
115 ps_data[pos++] = (data[3] >> 8) & 0xFF; 264 ps = PS_DATA_INC(0);
116 ps_data[pos++] = (data[3] >> 0) & 0xFF; 265 ps[pos + 0] = (data[3] >> 8) & 0xFF;
117 ps_segment |= 1 << segment; 266 ps[pos + 1] = (data[3] >> 0) & 0xFF;
118 ps_segment_timeout[segment] = current_tick + PS_SEGMENT_TIMEOUT; 267
119 if (ps_segment == 0xf) { 268 if (++ps_segment < 4) {
120 ps_data[8] = '\0'; 269 /* don't have all segments yet */
121 if (strcmp(ps_copy, ps_data) != 0) { 270 SET_TIMEOUT(ps_timeout, GROUP0_TIMEOUT);
122 /* we got an updated message */ 271 return;
123 strcpy(ps_copy, ps_data); 272 }
124 return true; 273
125 } 274 ps[8] = '\0';
275
276 /* two messages in a row must be the same */
277 if (memcmp(ps, PS_DATA_INC(1), 8) == 0) {
278 memcpy(ps_copy, ps, 9);
279 SET_TIMEOUT(ps_copy_tmo, TEXT_TIMEOUT);
126 } 280 }
127 return false;
128} 281}
129 282
130/* handles a radio text characters, returns true if end-of-line found */ 283/* handles a radio text characters, returns true if end-of-line found */
131static bool handle_rt(int pos, char c) 284static bool handle_rt(int *pos_p, char c)
132{ 285{
286 char *rt = RT_DATA_INC(0);
287
133 switch (c) { 288 switch (c) {
134 case 0x0A: 289 case 0x0D: /* end of line */
135 /* line break hint */
136 rt_data[pos] = ' ';
137 return false;
138 case 0x0D:
139 /* end of line */
140 rt_data[pos] = '\0';
141 return true; 290 return true;
142 default: 291 case 0x0A: /* optional line break */
143 rt_data[pos] = c; 292 case 0x0B: /* end of headline */
293 c = ' ';
294 default: /* regular character */
295 rt[(*pos_p)++] = c;
296 case 0x00 ... 0x09: /* unprintable */
297 case 0x0C:
298 case 0x0E ... 0x1E:
299 case 0x1F: /* soft hyphen */
144 return false; 300 return false;
145 } 301 }
146} 302}
147 303
148/* handles a group 2 packet, returns true if a new message was received */ 304/* handles a group 2 packet, returns true if a new message was received */
149static bool handle_group2(uint16_t data[4]) 305static void handle_group2(const uint16_t data[4])
150{ 306{
151 int abflag, segment, version, pos; 307 int abflag, segment, version, pos;
308 char *rt;
152 bool done = false; 309 bool done = false;
153 310
154 /* remove obsolete segments */
155 for(int i = 0; i < 16; i++)
156 if(TIME_AFTER(current_tick, rt_segment_timeout[i]))
157 rt_segment &= ~(1 << i);
158
159 /* reset parsing if the message type changed */ 311 /* reset parsing if the message type changed */
160 abflag = (data[1] >> 4) & 1; 312 abflag = (data[1] >> 4) & 1;
161 segment = data[1] & 0xF; 313 segment = data[1] & 0xF;
162 if (abflag != rt_abflag) { 314 version = (data[1] >> 11) & 1;
315
316 if (abflag != rt_abflag || segment == 0) {
163 rt_abflag = abflag; 317 rt_abflag = abflag;
164 rt_segment = 0; 318 rt_segment = 0;
165 } 319 }
166 320 else if (segment != rt_segment || TIMED_OUT(rt_timeout)) {
167 rt_segment |= 1 << segment; 321 rt_segment = 0;
168 rt_segment_timeout[segment] = current_tick + RT_SEGMENT_TIMEOUT; 322 return;
323 }
169 324
170 /* store data */ 325 /* store data */
171 version = (data[1] >> 11) & 1;
172 if (version == 0) { 326 if (version == 0) {
173 pos = segment * 4; 327 pos = segment * 4;
174 done = done || handle_rt(pos++, (data[2] >> 8) & 0xFF); 328 done = done || handle_rt(&pos, (data[2] >> 8) & 0xFF);
175 done = done || handle_rt(pos++, (data[2] >> 0) & 0xFF); 329 done = done || handle_rt(&pos, (data[2] >> 0) & 0xFF);
176 done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF); 330 done = done || handle_rt(&pos, (data[3] >> 8) & 0xFF);
177 done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF); 331 done = done || handle_rt(&pos, (data[3] >> 0) & 0xFF);
178 } else { 332 } else {
179 pos = segment * 2; 333 pos = segment * 2;
180 done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF); 334 done = done || handle_rt(&pos, (data[3] >> 8) & 0xFF);
181 done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF); 335 done = done || handle_rt(&pos, (data[3] >> 0) & 0xFF);
182 } 336 }
337
183 /* there are two cases for completion: 338 /* there are two cases for completion:
184 * - we got all 16 segments 339 * - we got all 16 segments
185 * - we found a end of line AND we got all segments before it */ 340 * - we found an end of line */
186 if (rt_segment == 0xffff || (done && rt_segment == (1 << segment) - 1)) { 341 if (++rt_segment < 16 && !done) {
187 rt_data[pos] = '\0'; 342 SET_TIMEOUT(rt_timeout, GROUP2_TIMEOUT);
188 if (strcmp(rt_copy, rt_data) != 0) { 343 return;
189 /* we got an updated message */
190 strcpy(rt_copy, rt_data);
191 return true;
192 }
193 } 344 }
194 345
195 return false; 346 rt = RT_DATA_INC(0);
347 rt[pos++] = '\0';
348
349 /* two messages in a row must be the same */
350 if (memcmp(rt, RT_DATA_INC(1), pos) == 0) {
351 memcpy(rt_copy, rt, pos);
352 SET_TIMEOUT(rt_copy_tmo, TEXT_TIMEOUT);
353 }
196} 354}
197 355
198/* handles a group 4a packet (clock-time) */ 356/* handles a group 4a packet (clock-time) */
199static bool handle_group4a(uint16_t data[4]) 357static void handle_group4a(const uint16_t data[4])
200{ 358{
201 int daycode = ((data[1] << 15) & 0x18000) | 359 int daycode = ((data[1] << 15) & 0x18000) |
202 ((data[2] >> 1) & 0x07FFF); 360 ((data[2] >> 1) & 0x07FFF);
@@ -208,29 +366,30 @@ static bool handle_group4a(uint16_t data[4])
208 366
209 if (daycode < 55927) { 367 if (daycode < 55927) {
210 /* invalid date, before 2012-01-01 */ 368 /* invalid date, before 2012-01-01 */
211 return false; 369 return;
212 } 370 }
213 if ((hour >= 24) || (minute >= 60)) { 371 if ((hour >= 24) || (minute >= 60)) {
214 /* invalid time */ 372 /* invalid time */
215 return false; 373 return;
216 } 374 }
217 if (offset_abs > 24) { 375 if (offset_abs > 24) {
218 /* invalid local time offset */ 376 /* invalid local time offset */
219 return false; 377 return;
220 } 378 }
221 379
222 /* convert modified julian day + time to UTC */ 380 /* convert modified julian day + time to UTC */
223 time_t seconds = (daycode - 40587) * 86400; 381 time_t seconds = daycode - 40587;
224 seconds += hour * 3600; 382 if (seconds < 24854) {
225 seconds += minute * 60; 383 seconds *= 86400;
226 seconds += ((offset_sig == 0) ? offset_abs : -offset_abs) * 1800; 384 seconds += hour * 3600;
227 ct_data = seconds; 385 seconds += minute * 60;
228 386 seconds += ((offset_sig == 0) ? offset_abs : -offset_abs) * 1800;
229 return true; 387 ct_data = seconds;
388 }
230} 389}
231 390
232/* processes one rds packet, returns true if a new message was received */ 391/* processes one rds packet */
233bool rds_process(uint16_t data[4]) 392void rds_process(const uint16_t data[4])
234{ 393{
235 int group; 394 int group;
236 395
@@ -240,53 +399,98 @@ bool rds_process(uint16_t data[4])
240 pi_code = pi; 399 pi_code = pi;
241 } 400 }
242 pi_last = pi; 401 pi_last = pi;
243 402
244 /* handle rds data based on group */ 403 /* handle rds data based on group */
245 group = (data[1] >> 11) & 0x1F; 404 group = (data[1] >> 11) & 0x1F;
246 switch (group) { 405 switch (group) {
247 406
248 case 0: /* group 0A: basic info */ 407 case 0: /* group 0A: basic info */
249 case 1: /* group 0B: basic info */ 408 case 1: /* group 0B: basic info */
250 return handle_group0(data); 409 handle_group0(data);
251 410 break;
411
252 case 4: /* group 2A: radio text */ 412 case 4: /* group 2A: radio text */
253 case 5: /* group 2B: radio text */ 413 case 5: /* group 2B: radio text */
254 return handle_group2(data); 414 handle_group2(data);
415 break;
255 416
256 case 8: /* group 4A: clock-time */ 417 case 8: /* group 4A: clock-time */
257 return handle_group4a(data); 418 handle_group4a(data);
258
259 default:
260 break; 419 break;
261 } 420 }
262 421
263 return false; 422 register_activity();
264} 423}
424#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
265 425
266/* TODO: The caller really should provide the buffer in order to regulate 426#if (CONFIG_RDS & RDS_CFG_PUSH)
267 access */ 427/* pushes preprocesed RDS information */
268 428void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size)
269/* returns the programme identification code */
270uint16_t rds_get_pi(void)
271{ 429{
272 return pi_code; 430 switch (info_id) {
273} 431#if 0
432 case RDS_INFO_CODETABLE:
433 /* nothing doing for now */
434 break;
435#endif
436 case RDS_INFO_PI:
437 pi_code = (uint16_t)data;
438 break;
439 case RDS_INFO_PS:
440 strmemcpy(ps_copy, (const char *)data, MIN(size, sizeof (ps_copy)-1));
441 SET_TIMEOUT(ps_copy_tmo, TEXT_TIMEOUT);
442 break;
443 case RDS_INFO_RT:
444 strmemcpy(rt_copy, (const char *)data, MIN(size, sizeof (rt_copy)-1));
445 SET_TIMEOUT(rt_copy_tmo, TEXT_TIMEOUT);
446 break;
447 case RDS_INFO_CT:
448 ct_data = (time_t)data;
449 break;
274 450
275/* returns the most recent valid programme service name */ 451 default:;
276char* rds_get_ps(void) 452 }
277{
278 return get_ps();
279}
280 453
281/* returns the most recent valid RadioText message */ 454 register_activity();
282char* rds_get_rt(void)
283{
284 return get_rt();
285} 455}
456#endif /* (CONFIG_RDS & RDS_CFG_PUSH) */
286 457
287/* returns the most recent valid clock-time value (or 0 if invalid) */ 458/* read fully-processed RDS data */
288time_t rds_get_ct(void) 459size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size)
289{ 460{
290 return ct_data; 461 int oldlevel = rds_disable_irq_save();
291}
292 462
463 rds_sync();
464
465 switch (info_id) {
466#if 0
467 case RDS_INFO_CODETABLE:
468 /* nothing doing for now */
469 break;
470#endif
471 case RDS_INFO_PI:
472 if (size >= sizeof (uint16_t)) {
473 *(uint16_t *)data = pi_code;
474 }
475 size = sizeof (uint16_t);
476 break;
477 case RDS_INFO_PS:
478 size = copy_rds_string((char *)data, ps_copy, size);
479 break;
480 case RDS_INFO_RT:
481 size = copy_rds_string((char *)data, rt_copy, size);
482 break;
483 case RDS_INFO_CT:
484 if (size >= sizeof (time_t)) {
485 *(time_t *)data = ct_data;
486 }
487 size = sizeof (time_t);
488 break;
489
490 default:
491 size = 0;
492 }
493
494 rds_restore_irq(oldlevel);
495 return size;
496}
diff --git a/firmware/drivers/tuner/ipod_remote_tuner.c b/firmware/drivers/tuner/ipod_remote_tuner.c
index 8b599cb79c..e283ddfb68 100644
--- a/firmware/drivers/tuner/ipod_remote_tuner.c
+++ b/firmware/drivers/tuner/ipod_remote_tuner.c
@@ -30,6 +30,7 @@
30#include "adc.h" 30#include "adc.h"
31#include "settings.h" 31#include "settings.h"
32#include "power.h" 32#include "power.h"
33#include "rds.h"
33 34
34static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF; 35static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF;
35/* temp var for tests to avoid looping execution in submenus settings*/ 36/* temp var for tests to avoid looping execution in submenus settings*/
@@ -40,7 +41,6 @@ int radio_present = 0;
40static int tuner_frequency = 0; 41static int tuner_frequency = 0;
41static int tuner_signal_power = 0; 42static int tuner_signal_power = 0;
42static bool radio_tuned = false; 43static bool radio_tuned = false;
43static bool rds_event = false;
44 44
45static char rds_radioname[9]; 45static char rds_radioname[9];
46static char rds_radioinfo[65]; 46static char rds_radioinfo[65];
@@ -90,6 +90,7 @@ static void rmt_tuner_sleep(int state)
90{ 90{
91 if (state == 0) 91 if (state == 0)
92 { 92 {
93 rds_init();
93 tuner_param = 0x00; 94 tuner_param = 0x00;
94 old_tuner_param = 0xFF; 95 old_tuner_param = 0xFF;
95 mono_mode = -1; 96 mono_mode = -1;
@@ -273,13 +274,12 @@ void rmt_tuner_rds_data(unsigned int len, const unsigned char *buf)
273{ 274{
274 if (buf[2] == 0x1E) 275 if (buf[2] == 0x1E)
275 { 276 {
276 strlcpy(rds_radioname,buf+4,8); 277 rds_push_info(RDS_INFO_PS, (uintptr_t)(buf+4), 8);
277 } 278 }
278 else if(buf[2] == 0x04) 279 else if(buf[2] == 0x04)
279 { 280 {
280 strlcpy(rds_radioinfo,buf+4,len-4); 281 rds_push_info(RDS_INFO_RT, (uintptr_t)(buf+4), len-4);
281 } 282 }
282 rds_event = true;
283} 283}
284 284
285/* tuner abstraction layer: set something to the tuner */ 285/* tuner abstraction layer: set something to the tuner */
@@ -421,31 +421,6 @@ int ipod_rmt_tuner_get(int setting)
421 case RADIO_STEREO: 421 case RADIO_STEREO:
422 val = true; 422 val = true;
423 break; 423 break;
424
425 case RADIO_EVENT:
426 if (rds_event)
427 {
428 val = 1;
429 rds_event = false;
430 }
431 break;
432 } 424 }
433 return val; 425 return val;
434} 426}
435
436char* ipod_get_rds_info(int setting)
437{
438 char *text = NULL;
439
440 switch(setting)
441 {
442 case RADIO_RDS_NAME:
443 text = rds_radioname;
444 break;
445
446 case RADIO_RDS_TEXT:
447 text = rds_radioinfo;
448 break;
449 }
450 return text;
451}
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index 90d8df27dc..c7d942f293 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -213,9 +213,6 @@
213static bool tuner_present = false; 213static bool tuner_present = false;
214static uint16_t cache[16]; 214static uint16_t cache[16];
215static struct mutex fmr_mutex SHAREDBSS_ATTR; 215static struct mutex fmr_mutex SHAREDBSS_ATTR;
216#ifdef HAVE_RDS_CAP
217static int rds_event = 0;
218#endif
219 216
220/* reads <len> registers from radio at offset 0x0A into cache */ 217/* reads <len> registers from radio at offset 0x0A into cache */
221static void si4700_read(int len) 218static void si4700_read(int len)
@@ -373,6 +370,7 @@ void si4700_init(void)
373 si4700_sleep(1); 370 si4700_sleep(1);
374 371
375#ifdef HAVE_RDS_CAP 372#ifdef HAVE_RDS_CAP
373 rds_init();
376 si4700_rds_init(); 374 si4700_rds_init();
377#endif 375#endif
378 } 376 }
@@ -528,21 +526,6 @@ int si4700_get(int setting)
528 case RADIO_RSSI_MAX: 526 case RADIO_RSSI_MAX:
529 val = RSSI_MAX; 527 val = RSSI_MAX;
530 break; 528 break;
531
532#ifdef HAVE_RDS_CAP
533 case RADIO_EVENT:
534 {
535 #ifdef RDS_ISR_PROCESSING
536 int oldlevel = disable_irq_save();
537 #endif
538 val = rds_event;
539 rds_event = 0;
540 #ifdef RDS_ISR_PROCESSING
541 restore_irq(oldlevel);
542 #endif
543 break;
544 }
545#endif
546 } 529 }
547 530
548 mutex_unlock(&fmr_mutex); 531 mutex_unlock(&fmr_mutex);
@@ -567,77 +550,45 @@ void si4700_dbg_info(struct si4700_dbg_info *nfo)
567 550
568#ifdef HAVE_RDS_CAP 551#ifdef HAVE_RDS_CAP
569 552
570#ifdef RDS_ISR_PROCESSING 553#if (CONFIG_RDS & RDS_CFG_ISR)
571/* Read raw RDS info for processing - in ISR */ 554static unsigned char isr_regbuf[(RDSD - STATUSRSSI + 1) * 2];
572 555
573/* Assumes regbuf is 32 bytes */ 556/* Called by RDS interrupt on target */
574void si4700_rds_read_raw_async(void) 557void si4700_rds_interrupt(void)
575{ 558{
576 si4700_read_raw_async((RDSD - STATUSRSSI + 1) * 2); 559 si4700_rds_read_raw_async(isr_regbuf, sizeof (isr_regbuf));
577} 560}
578 561
579void si4700_rds_read_raw_async_complete(unsigned char *regbuf, 562/* Handle RDS event from ISR */
580 uint16_t data[4]) 563void si4700_rds_process(void)
581{ 564{
582 const int index = (RDSA - STATUSRSSI) * 2; 565 uint16_t rds_data[4];
566 int index = (RDSA - STATUSRSSI) * 2;
583 567
584 for (int i = 0; i < 4; i++) { 568 for (int i = 0; i < 4; i++) {
585 data[i] = regbuf[index] << 8 | regbuf[index + 1]; 569 rds_data[i] = isr_regbuf[index] << 8 | isr_regbuf[index + 1];
586 regbuf += 2; 570 index += 2;
587 } 571 }
588}
589 572
590/* Set the event flag */ 573 rds_process(rds_data);
591void si4700_rds_set_event(void)
592{
593 rds_event = 1;
594} 574}
595 575
596#else /* ndef RDS_ISR_PROCESSING */ 576#else /* !(CONFIG_RDS & RDS_CFG_ISR) */
597/* Read raw RDS info for processing */
598bool si4700_rds_read_raw(uint16_t data[4])
599{
600 bool retval = false;
601 577
578/* Handle RDS event from thread */
579void si4700_rds_process(void)
580{
602 mutex_lock(&fmr_mutex); 581 mutex_lock(&fmr_mutex);
603 582
604 if (tuner_powered()) 583 if (tuner_powered())
605 { 584 {
606 si4700_read_reg(RDSD); 585 si4700_read_reg(RDSD);
607 memcpy(data, &cache[RDSA], 4 * sizeof (uint16_t)); 586 rds_process(&cache[RDSA]);
608 retval = true;
609 } 587 }
610 588
611 mutex_unlock(&fmr_mutex); 589 mutex_unlock(&fmr_mutex);
612
613 return retval;
614}
615
616/* Set the event flag */
617void si4700_rds_set_event(void)
618{
619 mutex_lock(&fmr_mutex);
620 rds_event = 1;
621 mutex_unlock(&fmr_mutex);
622} 590}
623#endif /* RDS_ISR_PROCESSING */ 591#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
624 592
625char * si4700_get_rds_info(int setting)
626{
627 char *text = NULL;
628
629 switch(setting)
630 {
631 case RADIO_RDS_NAME:
632 text = rds_get_ps();
633 break;
634
635 case RADIO_RDS_TEXT:
636 text = rds_get_rt();
637 break;
638 }
639
640 return text;
641}
642#endif /* HAVE_RDS_CAP */ 593#endif /* HAVE_RDS_CAP */
643 594
diff --git a/firmware/export/config.h b/firmware/export/config.h
index efad75f1b2..4209955c2d 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -712,6 +712,16 @@ Lyre prototype 1 */
712#define BATTERY_CAPACITY_INC 0 712#define BATTERY_CAPACITY_INC 0
713#endif 713#endif
714 714
715#ifdef HAVE_RDS_CAP
716/* combinable bitflags */
717#define RDS_CFG_ISR 0x1 /* uses ISR to process packets */
718#define RDS_CFG_PROCESS 0x2 /* uses raw packet processing */
719#define RDS_CFG_PUSH 0x4 /* pushes processed information */
720#ifndef CONFIG_RDS
721#define CONFIG_RDS RDS_CFG_PROCESS /* thread processing+raw processing */
722#endif /* CONFIG_RDS */
723#endif /* HAVE_RDS_CAP */
724
715#ifndef CONFIG_ORIENTATION 725#ifndef CONFIG_ORIENTATION
716#if LCD_HEIGHT > LCD_WIDTH 726#if LCD_HEIGHT > LCD_WIDTH
717#define CONFIG_ORIENTATION SCREEN_PORTRAIT 727#define CONFIG_ORIENTATION SCREEN_PORTRAIT
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h
index e1bbb18529..928e8c0e60 100644
--- a/firmware/export/config/gigabeats.h
+++ b/firmware/export/config/gigabeats.h
@@ -121,7 +121,7 @@
121/* Define this if you have a SI4700 fm radio tuner */ 121/* Define this if you have a SI4700 fm radio tuner */
122#define CONFIG_TUNER SI4700 122#define CONFIG_TUNER SI4700
123#define HAVE_RDS_CAP 123#define HAVE_RDS_CAP
124#define RDS_ISR_PROCESSING 124#define CONFIG_RDS (RDS_CFG_ISR | RDS_CFG_PROCESS)
125 125
126/* define this if you can flip your LCD */ 126/* define this if you can flip your LCD */
127#define HAVE_LCD_FLIP 127#define HAVE_LCD_FLIP
diff --git a/firmware/export/config/ipod4g.h b/firmware/export/config/ipod4g.h
index dc83a594fc..785491a9ab 100644
--- a/firmware/export/config/ipod4g.h
+++ b/firmware/export/config/ipod4g.h
@@ -164,6 +164,7 @@
164/* Define Apple remote tuner */ 164/* Define Apple remote tuner */
165#define CONFIG_TUNER IPOD_REMOTE_TUNER 165#define CONFIG_TUNER IPOD_REMOTE_TUNER
166#define HAVE_RDS_CAP 166#define HAVE_RDS_CAP
167#define CONFIG_RDS RDS_CFG_PUSH
167 168
168/* Define this if you have a PortalPlayer PP5020 */ 169/* Define this if you have a PortalPlayer PP5020 */
169#define CONFIG_CPU PP5020 170#define CONFIG_CPU PP5020
diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h
index 5494cf387a..7664fd4f90 100644
--- a/firmware/export/config/ipod6g.h
+++ b/firmware/export/config/ipod6g.h
@@ -167,6 +167,7 @@
167/* Define Apple remote tuner */ 167/* Define Apple remote tuner */
168//#define CONFIG_TUNER IPOD_REMOTE_TUNER 168//#define CONFIG_TUNER IPOD_REMOTE_TUNER
169//#define HAVE_RDS_CAP 169//#define HAVE_RDS_CAP
170//#define CONFIG_RDS RDS_CFG_PUSH
170 171
171/* The exact type of CPU */ 172/* The exact type of CPU */
172#define CONFIG_CPU S5L8702 173#define CONFIG_CPU S5L8702
diff --git a/firmware/export/config/ipodcolor.h b/firmware/export/config/ipodcolor.h
index 9f34a7955b..33533f925f 100644
--- a/firmware/export/config/ipodcolor.h
+++ b/firmware/export/config/ipodcolor.h
@@ -151,6 +151,7 @@
151/* Define Apple remote tuner */ 151/* Define Apple remote tuner */
152#define CONFIG_TUNER IPOD_REMOTE_TUNER 152#define CONFIG_TUNER IPOD_REMOTE_TUNER
153#define HAVE_RDS_CAP 153#define HAVE_RDS_CAP
154#define CONFIG_RDS RDS_CFG_PUSH
154 155
155/* Define this if you have a PortalPlayer PP5020 */ 156/* Define this if you have a PortalPlayer PP5020 */
156#define CONFIG_CPU PP5020 157#define CONFIG_CPU PP5020
diff --git a/firmware/export/config/ipodmini1g.h b/firmware/export/config/ipodmini1g.h
index 71dfe16c04..4f9f1b3dd9 100644
--- a/firmware/export/config/ipodmini1g.h
+++ b/firmware/export/config/ipodmini1g.h
@@ -163,6 +163,7 @@
163/* Define Apple remote tuner */ 163/* Define Apple remote tuner */
164#define CONFIG_TUNER IPOD_REMOTE_TUNER 164#define CONFIG_TUNER IPOD_REMOTE_TUNER
165#define HAVE_RDS_CAP 165#define HAVE_RDS_CAP
166#define CONFIG_RDS RDS_CFG_PUSH
166 167
167/* Define this if you have a PortalPlayer PP5020 */ 168/* Define this if you have a PortalPlayer PP5020 */
168#define CONFIG_CPU PP5020 169#define CONFIG_CPU PP5020
diff --git a/firmware/export/config/ipodmini2g.h b/firmware/export/config/ipodmini2g.h
index a2b199afee..977eb3da3f 100644
--- a/firmware/export/config/ipodmini2g.h
+++ b/firmware/export/config/ipodmini2g.h
@@ -164,6 +164,7 @@
164/* Define Apple remote tuner */ 164/* Define Apple remote tuner */
165#define CONFIG_TUNER IPOD_REMOTE_TUNER 165#define CONFIG_TUNER IPOD_REMOTE_TUNER
166#define HAVE_RDS_CAP 166#define HAVE_RDS_CAP
167#define CONFIG_RDS RDS_CFG_PUSH
167 168
168/* Define this if you have a PortalPlayer PP5022 */ 169/* Define this if you have a PortalPlayer PP5022 */
169#define CONFIG_CPU PP5022 170#define CONFIG_CPU PP5022
diff --git a/firmware/export/config/ipodnano1g.h b/firmware/export/config/ipodnano1g.h
index acdfff3c15..81d4a0ae4c 100644
--- a/firmware/export/config/ipodnano1g.h
+++ b/firmware/export/config/ipodnano1g.h
@@ -154,6 +154,7 @@
154/* Define Apple remote tuner */ 154/* Define Apple remote tuner */
155#define CONFIG_TUNER IPOD_REMOTE_TUNER 155#define CONFIG_TUNER IPOD_REMOTE_TUNER
156#define HAVE_RDS_CAP 156#define HAVE_RDS_CAP
157#define CONFIG_RDS RDS_CFG_PUSH
157 158
158/* Define this if you have a PortalPlayer PP5022 */ 159/* Define this if you have a PortalPlayer PP5022 */
159#define CONFIG_CPU PP5022 160#define CONFIG_CPU PP5022
diff --git a/firmware/export/config/ipodnano2g.h b/firmware/export/config/ipodnano2g.h
index aeff6aeda1..9d8c39ef7e 100644
--- a/firmware/export/config/ipodnano2g.h
+++ b/firmware/export/config/ipodnano2g.h
@@ -164,6 +164,7 @@
164/* Define Apple remote tuner */ 164/* Define Apple remote tuner */
165//#define CONFIG_TUNER IPOD_REMOTE_TUNER 165//#define CONFIG_TUNER IPOD_REMOTE_TUNER
166//#define HAVE_RDS_CAP 166//#define HAVE_RDS_CAP
167//#define CONFIG_RDS RDS_CFG_PUSH
167 168
168/* The exact type of CPU */ 169/* The exact type of CPU */
169#define CONFIG_CPU S5L8701 170#define CONFIG_CPU S5L8701
diff --git a/firmware/export/config/ipodvideo.h b/firmware/export/config/ipodvideo.h
index 2f28182731..7f1d83f4a5 100644
--- a/firmware/export/config/ipodvideo.h
+++ b/firmware/export/config/ipodvideo.h
@@ -170,6 +170,7 @@
170/* Define Apple remote tuner */ 170/* Define Apple remote tuner */
171#define CONFIG_TUNER IPOD_REMOTE_TUNER 171#define CONFIG_TUNER IPOD_REMOTE_TUNER
172#define HAVE_RDS_CAP 172#define HAVE_RDS_CAP
173#define CONFIG_RDS RDS_CFG_PUSH
173 174
174/* Define this if you have a PortalPlayer PP5022 */ 175/* Define this if you have a PortalPlayer PP5022 */
175#define CONFIG_CPU PP5022 176#define CONFIG_CPU PP5022
diff --git a/firmware/export/ipod_remote_tuner.h b/firmware/export/ipod_remote_tuner.h
index 30c83a4135..37bf412f53 100644
--- a/firmware/export/ipod_remote_tuner.h
+++ b/firmware/export/ipod_remote_tuner.h
@@ -33,13 +33,10 @@ extern void rmt_tuner_rds_data(unsigned int len, const unsigned char *buf);
33 33
34int ipod_rmt_tuner_set(int setting, int value); 34int ipod_rmt_tuner_set(int setting, int value);
35int ipod_rmt_tuner_get(int setting); 35int ipod_rmt_tuner_get(int setting);
36char* ipod_get_rds_info(int setting);
37
38 36
39#ifndef CONFIG_TUNER_MULTI 37#ifndef CONFIG_TUNER_MULTI
40#define tuner_set ipod_rmt_tuner_set 38#define tuner_set ipod_rmt_tuner_set
41#define tuner_get ipod_rmt_tuner_get 39#define tuner_get ipod_rmt_tuner_get
42#define tuner_get_rds_info ipod_get_rds_info
43#endif 40#endif
44 41
45#endif /* _IPOD_REMOTE_TUNER_H_ */ 42#endif /* _IPOD_REMOTE_TUNER_H_ */
diff --git a/firmware/export/rds.h b/firmware/export/rds.h
index ff1608f5c4..6c42e16f56 100644
--- a/firmware/export/rds.h
+++ b/firmware/export/rds.h
@@ -18,18 +18,45 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#ifndef RDS_H
22#define RDS_H
23
21#include <stdint.h> 24#include <stdint.h>
22#include <stdbool.h> 25#include <stdbool.h>
23#include "time.h" 26#include "time.h"
24 27
25void rds_init(void); 28void rds_init(void);
26
27void rds_reset(void); 29void rds_reset(void);
28bool rds_process(uint16_t data[4]); 30void rds_sync(void);
31
32#if (CONFIG_RDS & RDS_CFG_PROCESS)
33/* RDS raw data processing */
34void rds_process(const uint16_t data[4]);
35#endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */
36
37enum rds_info_id
38{
39 RDS_INFO_NULL = 0,
40 RDS_INFO_CODEABLE, /* code table, right now only G0 */
41 RDS_INFO_PI, /* programme identifier */
42 RDS_INFO_PS, /* programme service name */
43 RDS_INFO_RT, /* radio text */
44 RDS_INFO_CT, /* clock time */
45};
46
47enum rds_code_table
48{
49 RDS_CT_G0, /* default code table G0 */
50 RDS_CT_G1, /* alternate code table G1 */
51 RDS_CT_G2, /* alternate code table G2 */
52};
29 53
30uint16_t rds_get_pi(void); 54#if (CONFIG_RDS & RDS_CFG_PUSH)
31char* rds_get_ps(void); 55/* pushes preprocesed RDS information */
32char* rds_get_rt(void); 56void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size);
33time_t rds_get_ct(void); 57#endif /* (CONFIG_RDS & RDS_CFG_PUSH) */
34 58
59/* read fully-processed RDS data */
60size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size);
35 61
62#endif /* RDS_H */
diff --git a/firmware/export/si4700.h b/firmware/export/si4700.h
index 6b7992c025..bd75bf0817 100644
--- a/firmware/export/si4700.h
+++ b/firmware/export/si4700.h
@@ -35,36 +35,34 @@ struct si4700_dbg_info
35}; 35};
36 36
37bool si4700_detect(void); 37bool si4700_detect(void);
38void si4700_init(void); 38void si4700_init(void) INIT_ATTR;
39int si4700_set(int setting, int value); 39int si4700_set(int setting, int value);
40int si4700_get(int setting); 40int si4700_get(int setting);
41void si4700_dbg_info(struct si4700_dbg_info *nfo); 41void si4700_dbg_info(struct si4700_dbg_info *nfo);
42/* For interrupt-based mono/stereo indicator */ 42/* For interrupt-based mono/stereo indicator */
43bool si4700_st(void); 43bool si4700_st(void);
44 44
45#ifdef HAVE_RDS_CAP
45/** RDS support **/ 46/** RDS support **/
46void si4700_rds_init(void); 47
48void si4700_rds_init(void) INIT_ATTR;
47/* Radio is fully powered up or about to be powered down */ 49/* Radio is fully powered up or about to be powered down */
48void si4700_rds_powerup(bool on); 50void si4700_rds_powerup(bool on);
49#ifdef RDS_ISR_PROCESSING 51
52#if (CONFIG_RDS & RDS_CFG_ISR)
50/* Read raw RDS info for processing - asynchronously */ 53/* Read raw RDS info for processing - asynchronously */
51void si4700_read_raw_async(int count); /* implemented by target */ 54void si4700_rds_read_raw_async(unsigned char *buf, int count); /* implemented by target */
52void si4700_rds_read_raw_async(void); 55void si4700_rds_interrupt(void);
53void si4700_rds_read_raw_async_complete(unsigned char *regbuf, 56#endif /* (CONFIG_RDS & RDS_CFG_ISR) */
54 uint16_t data[4]); 57
55#else /* ndef RDS_ISR_PROCESSING */
56/* Read raw RDS info for processing */ 58/* Read raw RDS info for processing */
57bool si4700_rds_read_raw(uint16_t data[4]); 59void si4700_rds_process(void);
58#endif /* RDS_ISR_PROCESSING */ 60
59/* Obtain specified string */ 61#endif /* HAVE_RDS_CAP */
60char* si4700_get_rds_info(int setting);
61/* Set the event flag */
62void si4700_rds_set_event(void);
63 62
64#ifndef CONFIG_TUNER_MULTI 63#ifndef CONFIG_TUNER_MULTI
65#define tuner_set si4700_set 64#define tuner_set si4700_set
66#define tuner_get si4700_get 65#define tuner_get si4700_get
67#define tuner_get_rds_info si4700_get_rds_info
68#endif 66#endif
69 67
70#endif /* _SI4700_H_ */ 68#endif /* _SI4700_H_ */
diff --git a/firmware/export/tuner.h b/firmware/export/tuner.h
index 050bbd0f33..a166eacf33 100644
--- a/firmware/export/tuner.h
+++ b/firmware/export/tuner.h
@@ -25,6 +25,10 @@
25#include "config.h" 25#include "config.h"
26#include "hwcompat.h" 26#include "hwcompat.h"
27 27
28#ifdef HAVE_RDS_CAP
29#include <sys/types.h>
30#endif
31
28/** Settings to the tuner layer **/ 32/** Settings to the tuner layer **/
29enum 33enum
30{ 34{
@@ -45,8 +49,6 @@ enum
45 RADIO_PRESENT = 0, 49 RADIO_PRESENT = 0,
46 RADIO_TUNED, 50 RADIO_TUNED,
47 RADIO_STEREO, 51 RADIO_STEREO,
48 /* RADIO_EVENT is an event that requests a screen update */
49 RADIO_EVENT,
50 RADIO_RSSI, 52 RADIO_RSSI,
51 RADIO_RSSI_MIN, 53 RADIO_RSSI_MIN,
52 RADIO_RSSI_MAX, 54 RADIO_RSSI_MAX,
@@ -57,15 +59,20 @@ enum
57 59
58#ifdef HAVE_RDS_CAP 60#ifdef HAVE_RDS_CAP
59/** Readback from the tuner RDS layer **/ 61/** Readback from the tuner RDS layer **/
60enum 62/* returns needed size if buffer size is inadequate */
63size_t tuner_get_rds_info(int setting, void *dst, size_t dstsize);
64
65enum RADIO_RDS_INFO
61{ 66{
62 RADIO_RDS_NAME, 67 RADIO_RDS_NAME, /* dst: array of char, dstsize: buffer size */
63 RADIO_RDS_TEXT, 68 RADIO_RDS_TEXT, /* dst: array of char, dstsize: buffer size */
69 RADIO_RDS_PROGRAM_INFO, /* dst: uint16_t *, dstsize: >= sizeof(uint16_t) */
70 RADIO_RDS_CURRENT_TIME, /* dst: time_t *, dstsize: >= sizeof(time_t) */
64 71
65 /* Put new general-purpose readback values above this line */ 72 /* Put new general-purpose readback values above this line */
66 __RADIO_GET_RDS_INFO_STANDARD_LAST 73 __RADIO_GET_RDS_INFO_STANDARD_LAST
67}; 74};
68#endif 75#endif /* HAVE_RDS_CAP */
69 76
70/** Tuner regions **/ 77/** Tuner regions **/
71 78
diff --git a/firmware/target/arm/as3525/fmradio-i2c-as3525.c b/firmware/target/arm/as3525/fmradio-i2c-as3525.c
index ebe7ebeffd..5b629f5ad4 100644
--- a/firmware/target/arm/as3525/fmradio-i2c-as3525.c
+++ b/firmware/target/arm/as3525/fmradio-i2c-as3525.c
@@ -35,7 +35,6 @@
35#include "generic_i2c.h" 35#include "generic_i2c.h"
36#include "fmradio_i2c.h" 36#include "fmradio_i2c.h"
37#include "thread.h" 37#include "thread.h"
38#include "rds.h"
39 38
40#if defined(SANSA_CLIP) || defined(SANSA_C200V2) 39#if defined(SANSA_CLIP) || defined(SANSA_C200V2)
41#define I2C_SCL_GPIO(x) GPIOB_PIN(x) 40#define I2C_SCL_GPIO(x) GPIOB_PIN(x)
@@ -203,13 +202,9 @@ void tuner_isr(void)
203/* Captures RDS data and processes it */ 202/* Captures RDS data and processes it */
204static void NORETURN_ATTR rds_thread(void) 203static void NORETURN_ATTR rds_thread(void)
205{ 204{
206 uint16_t rds_data[4];
207
208 while (true) { 205 while (true) {
209 semaphore_wait(&rds_sema, TIMEOUT_BLOCK); 206 semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
210 if (si4700_rds_read_raw(rds_data) && rds_process(rds_data)) { 207 si4700_rds_process();
211 si4700_rds_set_event();
212 }
213 } 208 }
214} 209}
215 210
@@ -233,7 +228,6 @@ void si4700_rds_powerup(bool on)
233void si4700_rds_init(void) 228void si4700_rds_init(void)
234{ 229{
235 semaphore_init(&rds_sema, 1, 0); 230 semaphore_init(&rds_sema, 1, 0);
236 rds_init();
237 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" 231 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
238 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); 232 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
239} 233}
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c
index bc4a83665d..5d146c4846 100644
--- a/firmware/target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c
@@ -23,7 +23,6 @@
23#include "system.h" 23#include "system.h"
24#include "kernel.h" 24#include "kernel.h"
25#include "pinctrl-imx233.h" 25#include "pinctrl-imx233.h"
26#include "rds.h"
27#include "si4700.h" 26#include "si4700.h"
28 27
29/** 28/**
@@ -52,13 +51,11 @@ static void stc_rds_callback(int bank, int pin, intptr_t user)
52/* Captures RDS data and processes it */ 51/* Captures RDS data and processes it */
53static void NORETURN_ATTR rds_thread(void) 52static void NORETURN_ATTR rds_thread(void)
54{ 53{
55 uint16_t rds_data[4];
56
57 while(true) 54 while(true)
58 { 55 {
59 semaphore_wait(&rds_sema, TIMEOUT_BLOCK); 56 semaphore_wait(&rds_sema, TIMEOUT_BLOCK);
60 if(si4700_rds_read_raw(rds_data) && rds_process(rds_data)) 57 si4700_rds_process();
61 si4700_rds_set_event(); 58
62 /* renable callback */ 59 /* renable callback */
63 imx233_pinctrl_setup_irq(2, 27, true, true, false, &stc_rds_callback, 0); 60 imx233_pinctrl_setup_irq(2, 27, true, true, false, &stc_rds_callback, 0);
64 } 61 }
@@ -86,7 +83,6 @@ void si4700_rds_powerup(bool on)
86void si4700_rds_init(void) 83void si4700_rds_init(void)
87{ 84{
88 semaphore_init(&rds_sema, 1, 0); 85 semaphore_init(&rds_sema, 1, 0);
89 rds_init();
90 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" 86 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
91 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); 87 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU));
92} 88}
diff --git a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
index ee91b99c0f..5b0c71110d 100644
--- a/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
@@ -29,7 +29,6 @@
29#include "gpio-target.h" 29#include "gpio-target.h"
30#include "i2c-imx31.h" 30#include "i2c-imx31.h"
31#include "fmradio_i2c.h" 31#include "fmradio_i2c.h"
32#include "rds.h"
33#include "tuner.h" 32#include "tuner.h"
34 33
35static struct i2c_node si4700_i2c_node = 34static struct i2c_node si4700_i2c_node =
@@ -128,60 +127,44 @@ bool si4700_st(void)
128 127
129 128
130/* Low-level RDS Support */ 129/* Low-level RDS Support */
131
132/* Transfer descriptor for RDS async operations */
133static struct si4700_i2c_transfer_desc
134{
135 struct i2c_transfer_desc xfer;
136 unsigned char regbuf[32];
137} si4700_xfer =
138{
139 .xfer = { .node = &si4700_i2c_node }
140};
141
142static bool int_restore; 130static bool int_restore;
143 131
144static void si4700_rds_read_raw_callback(struct i2c_transfer_desc *xfer) 132/* Called after I2C read cycle completes */
133static void si4700_rds_read_raw_async_callback(struct i2c_transfer_desc *xfer)
145{ 134{
146 struct si4700_i2c_transfer_desc *xf =
147 (struct si4700_i2c_transfer_desc *)xfer;
148
149 if (xfer->rxcount == 0) 135 if (xfer->rxcount == 0)
150 { 136 si4700_rds_process();
151 uint16_t rds_data[4];
152 si4700_rds_read_raw_async_complete(xf->regbuf, rds_data);
153
154 if (rds_process(rds_data))
155 si4700_rds_set_event();
156 }
157 /* else read didn't finish */ 137 /* else read didn't finish */
158 138
159 if (int_restore) 139 if (int_restore)
160 gpio_int_enable(SI4700_EVENT_ID); 140 gpio_int_enable(SI4700_EVENT_ID);
161} 141}
162 142
163/* Callback from si4700_rds_read_raw to execute the read */ 143/* Called to read registers from ISR context */
164void si4700_read_raw_async(int count) 144void si4700_rds_read_raw_async(unsigned char *buf, int count)
165{ 145{
166 si4700_xfer.xfer.txdata = NULL; 146 /* transfer descriptor for RDS async operations */
167 si4700_xfer.xfer.txcount = 0; 147 static struct i2c_transfer_desc xfer = { .node = &si4700_i2c_node };
168 si4700_xfer.xfer.rxdata = si4700_xfer.regbuf; 148
169 si4700_xfer.xfer.rxcount = count; 149 xfer.txdata = NULL;
170 si4700_xfer.xfer.callback = si4700_rds_read_raw_callback; 150 xfer.txcount = 0;
171 si4700_xfer.xfer.next = NULL; 151 xfer.rxdata = buf;
172 152 xfer.rxcount = count;
173 i2c_transfer(&si4700_xfer.xfer); 153 xfer.callback = si4700_rds_read_raw_async_callback;
154 xfer.next = NULL;
155
156 i2c_transfer(&xfer);
174} 157}
175 158
176/* RDS GPIO interrupt handler - start RDS data read */ 159/* RDS GPIO interrupt handler - start RDS data read */
177void INT_SI4700_RDS(void) 160void INT_SI4700_RDS(void)
178{ 161{
179 /* mask and clear the interrupt */ 162 /* mask and clear the interrupt until we're done */
180 gpio_int_disable(SI4700_EVENT_ID); 163 gpio_int_disable(SI4700_EVENT_ID);
181 gpio_int_clear(SI4700_EVENT_ID); 164 gpio_int_clear(SI4700_EVENT_ID);
182 165
183 /* read the RDS data */ 166 /* tell radio driver about it */
184 si4700_rds_read_raw_async(); 167 si4700_rds_interrupt();
185} 168}
186 169
187/* Called with on=true after full radio power up, and with on=false before 170/* Called with on=true after full radio power up, and with on=false before
@@ -197,5 +180,5 @@ void si4700_rds_powerup(bool on)
197/* One-time RDS init at startup */ 180/* One-time RDS init at startup */
198void si4700_rds_init(void) 181void si4700_rds_init(void)
199{ 182{
200 rds_init(); 183 /* nothing to do */
201} 184}
diff --git a/firmware/target/hosted/samsungypr/radio-ypr.c b/firmware/target/hosted/samsungypr/radio-ypr.c
index 1929bb7c90..af49c4c3f2 100644
--- a/firmware/target/hosted/samsungypr/radio-ypr.c
+++ b/firmware/target/hosted/samsungypr/radio-ypr.c
@@ -29,7 +29,6 @@
29#include "kernel.h" 29#include "kernel.h"
30 30
31#include "radio-ypr.h" 31#include "radio-ypr.h"
32#include "rds.h"
33#include "si4700.h" 32#include "si4700.h"
34#include "power.h" 33#include "power.h"
35 34
@@ -79,7 +78,6 @@ int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count)
79/* Low-level RDS Support */ 78/* Low-level RDS Support */
80static struct event_queue rds_queue; 79static struct event_queue rds_queue;
81static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)]; 80static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)];
82static uint16_t rds_data[4];
83 81
84enum { 82enum {
85 Q_POWERUP, 83 Q_POWERUP,
@@ -101,8 +99,7 @@ static void NORETURN_ATTR rds_thread(void)
101 case SYS_TIMEOUT: 99 case SYS_TIMEOUT:
102 /* Captures RDS data and processes it */ 100 /* Captures RDS data and processes it */
103 if ((si4709_read_reg(STATUSRSSI) & STATUSRSSI_RDSR) >> 8) { 101 if ((si4709_read_reg(STATUSRSSI) & STATUSRSSI_RDSR) >> 8) {
104 if (si4700_rds_read_raw(rds_data) && rds_process(rds_data)) 102 si4700_rds_process();
105 si4700_rds_set_event();
106 } 103 }
107 break; 104 break;
108 } 105 }
@@ -121,6 +118,5 @@ void si4700_rds_init(void)
121 queue_init(&rds_queue, false); 118 queue_init(&rds_queue, false);
122 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" 119 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds"
123 IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); 120 IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
124 rds_init();
125} 121}
126#endif /* HAVE_RDS_CAP */ 122#endif /* HAVE_RDS_CAP */
diff --git a/firmware/tuner.c b/firmware/tuner.c
index c9c5bc0639..565850c659 100644
--- a/firmware/tuner.c
+++ b/firmware/tuner.c
@@ -24,6 +24,9 @@
24#include "kernel.h" 24#include "kernel.h"
25#include "tuner.h" 25#include "tuner.h"
26#include "fmradio.h" 26#include "fmradio.h"
27#ifdef HAVE_RDS_CAP
28#include "rds.h"
29#endif /* HAVE_RDS_CAP */
27 30
28/* General region information */ 31/* General region information */
29const struct fm_region_data fm_region_data[TUNER_NUM_REGIONS] = 32const struct fm_region_data fm_region_data[TUNER_NUM_REGIONS] =
@@ -102,4 +105,24 @@ void tuner_init(void)
102 #endif 105 #endif
103 } 106 }
104} 107}
108
109#ifdef HAVE_RDS_CAP
110size_t tuner_get_rds_info(int setting, void *dst, size_t dstsize)
111{
112 /* TODO: integrate this into tuner_get/set */
113 static const unsigned char info_id_tbl[] =
114 {
115 [RADIO_RDS_NAME] = RDS_INFO_PS,
116 [RADIO_RDS_TEXT] = RDS_INFO_RT,
117 [RADIO_RDS_PROGRAM_INFO] = RDS_INFO_PI,
118 [RADIO_RDS_CURRENT_TIME] = RDS_INFO_CT,
119 };
120
121 if ((unsigned int)setting >= ARRAYLEN(info_id_tbl))
122 return 0;
123
124 return rds_pull_info(info_id_tbl[setting], (uintptr_t)dst, dstsize);
125}
126#endif /* HAVE_RDS_CAP */
127
105#endif /* SIMULATOR */ 128#endif /* SIMULATOR */