A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

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

+508 -337
+14 -10
apps/debug_menu.c
··· 127 127 #include "iap.h" 128 128 #endif 129 129 130 - #ifdef HAVE_RDS_CAP 131 - #include "rds.h" 132 - #endif 133 - 134 130 #include "talk.h" 135 131 136 132 static const char* threads_getname(int selected_item, void *data, ··· 2166 2162 #endif /* TEA5760 */ 2167 2163 2168 2164 #ifdef HAVE_RDS_CAP 2169 - simplelist_addline("PI:%04X PS:'%8s'", 2170 - rds_get_pi(), rds_get_ps()); 2171 - simplelist_addline("RT:%s", 2172 - rds_get_rt()); 2173 - time_t seconds = rds_get_ct(); 2165 + { 2166 + char buf[65*4]; 2167 + uint16_t pi; 2168 + time_t seconds; 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 2177 struct tm* time = gmtime(&seconds); 2175 2178 simplelist_addline( 2176 2179 "CT:%4d-%02d-%02d %02d:%02d", 2177 2180 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, 2178 2181 time->tm_hour, time->tm_min, time->tm_sec); 2179 - #endif 2182 + } 2183 + #endif /* HAVE_RDS_CAP */ 2180 2184 return ACTION_REDRAW; 2181 2185 } 2182 2186 static bool dbg_fm_radio(void)
+4 -2
apps/gui/skin_engine/skin_tokens.c
··· 540 540 #ifdef HAVE_RDS_CAP 541 541 return "rds"; 542 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 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 548 #else 547 549 return NULL; /* end of the SKIN_TOKEN_HAVE_RDS case */ 548 550 #endif /* HAVE_RDS_CAP */
-4
apps/radio/radio.c
··· 696 696 697 697 default: 698 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 699 if (!tuner_get(RADIO_PRESENT)) 704 700 { 705 701 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
+343 -139
firmware/drivers/rds.c
··· 21 21 #include <stdbool.h> 22 22 #include <stdint.h> 23 23 #include <string.h> 24 - #include <strlcpy.h> 25 24 #include <system.h> 26 25 #include <kernel.h> 27 26 #include "rds.h" 28 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 */ 36 + static char ps_copy[9]; /* copy of final message */ 37 + static long ps_copy_tmo; /* timeout to discard programme service name */ 38 + static char rt_copy[65]; /* copy of final message */ 39 + static long rt_copy_tmo; /* time to discard radio text */ 40 + static uint16_t pi_code; /* current programme identifier code */ 41 + static 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) 48 + static long rds_timeout; /* timeout until rds is thought idle */ 49 + static 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) */ 57 + static uint16_t pi_last; /* previously read code */ 29 58 30 - // timeout before segment obsolescence 31 - #define PS_SEGMENT_TIMEOUT (HZ / 2) 32 - #define RT_SEGMENT_TIMEOUT (10 * HZ) 59 + /* programme service name */ 60 + static char ps_data[2][9]; /* round-robin driver work queue */ 61 + static int ps_segment; /* next expected segment */ 62 + static long ps_timeout; /* timeout to receive full group */ 63 + static int ps_data_idx; /* ps_data[0 or 1] */ 64 + #define PS_DATA_INC(x) ps_data[ps_data_idx ^= (x)] 33 65 34 - /* programme identification */ 35 - static uint16_t pi_code; 36 - static uint16_t pi_last; 37 - /* program service name */ 38 - static char ps_data[9]; 39 - static char ps_copy[9]; 40 - static long ps_segment_timeout[4]; 41 - static int ps_segment;// bitmap of received segments 42 66 /* radio text */ 43 - static char rt_data[65]; 44 - static char rt_copy[65]; 45 - static long rt_segment_timeout[16]; 46 - static int rt_segment;// bitmap of received segments 47 - static int rt_abflag; 48 - /* date/time */ 49 - static time_t ct_data; 67 + static char rt_data[2][65]; /* round-robin driver work queue */ 68 + static int rt_segment; /* next expected segment */ 69 + static long rt_timeout; /* timeout to receive full group */ 70 + static int rt_abflag; /* message change flag */ 71 + static int rt_data_idx; /* rt_data[0 or 1] */ 72 + #define RT_DATA_INC(x) rt_data[rt_data_idx ^= (x)] 73 + #endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */ 50 74 51 - #ifdef RDS_ISR_PROCESSING 75 + #if (CONFIG_RDS & RDS_CFG_ISR) 52 76 /* Functions are called in ISR context */ 53 77 #define rds_disable_irq_save() disable_irq_save() 54 78 #define rds_restore_irq(old) restore_irq(old) 55 - /* Need triple buffer so string isn't clobbered while caller is using it */ 56 - static inline char * get_ps(void) 79 + #else /* !(CONFIG_RDS & RDS_CFG_ISR) */ 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 */ 85 + static const uint16_t rds_tbl_g0[0x100-0x20] = 86 + { 87 + /* codes 0x00 .. 0x1F are omitted because they are identities and not 88 + * actually spec'ed as part of the character maps anyway */ 89 + /* 0 1 2 3 4 5 6 7 */ 90 + 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, /* 20 */ 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 */ 123 + static 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; 160 + } 161 + 162 + /* Copy RDS character string with conversion to UTF-8 163 + * Acts like strlcpy but won't split multibyte characters */ 164 + static size_t copy_rds_string(char *dst, const char *src, size_t dstsize) 57 165 { 58 - static char ps_out[9]; 59 - int oldlevel = rds_disable_irq_save(); 60 - strcpy(ps_out, ps_copy); 61 - rds_restore_irq(oldlevel); 62 - return ps_out; 166 + char *p = dst; 167 + unsigned int c; 168 + 169 + while ((c = (unsigned char)*src++)) { 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; 63 186 } 64 - static inline char * get_rt(void) 187 + 188 + /* indicate recent processing activity */ 189 + static void register_activity(void) 65 190 { 66 - static char rt_out[65]; 67 - int oldlevel = rds_disable_irq_save(); 68 - strcpy(rt_out, rt_copy); 69 - rds_restore_irq(oldlevel); 70 - return rt_out; 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)) 75 - static inline char * get_ps(void) { return ps_copy; } 76 - static inline char * get_rt(void) { return rt_copy; } 77 - #endif /* RDS_ISR_PROCESSING */ 78 194 79 195 /* resets the rds parser */ 80 196 void rds_reset(void) 81 197 { 82 198 int oldlevel = rds_disable_irq_save(); 83 199 84 - pi_code = 0; 85 - pi_last = 0; 200 + /* reset general info */ 201 + pi_code = 0; 202 + ct_data = 0; 86 203 ps_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; 87 210 ps_segment = 0; 88 - rt_copy[0] = '\0'; 89 211 rt_segment = 0; 90 - ct_data = 0; 212 + #endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */ 91 213 92 214 rds_restore_irq(oldlevel); 93 215 } ··· 98 220 rds_reset(); 99 221 } 100 222 223 + /* sync RDS state */ 224 + void 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 246 /* handles a group 0 packet, returns true if a new message was received */ 102 - static bool handle_group0(uint16_t data[4]) 247 + static void handle_group0(const uint16_t data[4]) 103 248 { 104 249 int segment, pos; 105 - 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); 250 + char *ps; 110 251 111 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 262 /* store data */ 114 263 pos = segment * 2; 115 - ps_data[pos++] = (data[3] >> 8) & 0xFF; 116 - ps_data[pos++] = (data[3] >> 0) & 0xFF; 117 - ps_segment |= 1 << segment; 118 - ps_segment_timeout[segment] = current_tick + PS_SEGMENT_TIMEOUT; 119 - if (ps_segment == 0xf) { 120 - ps_data[8] = '\0'; 121 - if (strcmp(ps_copy, ps_data) != 0) { 122 - /* we got an updated message */ 123 - strcpy(ps_copy, ps_data); 124 - return true; 125 - } 264 + ps = PS_DATA_INC(0); 265 + ps[pos + 0] = (data[3] >> 8) & 0xFF; 266 + ps[pos + 1] = (data[3] >> 0) & 0xFF; 267 + 268 + if (++ps_segment < 4) { 269 + /* don't have all segments yet */ 270 + SET_TIMEOUT(ps_timeout, GROUP0_TIMEOUT); 271 + return; 272 + } 273 + 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 283 /* handles a radio text characters, returns true if end-of-line found */ 131 - static bool handle_rt(int pos, char c) 284 + static bool handle_rt(int *pos_p, char c) 132 285 { 286 + char *rt = RT_DATA_INC(0); 287 + 133 288 switch (c) { 134 - case 0x0A: 135 - /* line break hint */ 136 - rt_data[pos] = ' '; 137 - return false; 138 - case 0x0D: 139 - /* end of line */ 140 - rt_data[pos] = '\0'; 289 + case 0x0D: /* end of line */ 141 290 return true; 142 - default: 143 - rt_data[pos] = c; 291 + case 0x0A: /* optional line break */ 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 300 return false; 145 301 } 146 302 } 147 303 148 304 /* handles a group 2 packet, returns true if a new message was received */ 149 - static bool handle_group2(uint16_t data[4]) 305 + static void handle_group2(const uint16_t data[4]) 150 306 { 151 307 int abflag, segment, version, pos; 308 + char *rt; 152 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 311 /* reset parsing if the message type changed */ 160 312 abflag = (data[1] >> 4) & 1; 161 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 317 rt_abflag = abflag; 164 318 rt_segment = 0; 165 319 } 166 - 167 - rt_segment |= 1 << segment; 168 - rt_segment_timeout[segment] = current_tick + RT_SEGMENT_TIMEOUT; 320 + else if (segment != rt_segment || TIMED_OUT(rt_timeout)) { 321 + rt_segment = 0; 322 + return; 323 + } 169 324 170 325 /* store data */ 171 - version = (data[1] >> 11) & 1; 172 326 if (version == 0) { 173 327 pos = segment * 4; 174 - done = done || handle_rt(pos++, (data[2] >> 8) & 0xFF); 175 - done = done || handle_rt(pos++, (data[2] >> 0) & 0xFF); 176 - done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF); 177 - done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF); 328 + done = done || handle_rt(&pos, (data[2] >> 8) & 0xFF); 329 + done = done || handle_rt(&pos, (data[2] >> 0) & 0xFF); 330 + done = done || handle_rt(&pos, (data[3] >> 8) & 0xFF); 331 + done = done || handle_rt(&pos, (data[3] >> 0) & 0xFF); 178 332 } else { 179 333 pos = segment * 2; 180 - done = done || handle_rt(pos++, (data[3] >> 8) & 0xFF); 181 - done = done || handle_rt(pos++, (data[3] >> 0) & 0xFF); 334 + done = done || handle_rt(&pos, (data[3] >> 8) & 0xFF); 335 + done = done || handle_rt(&pos, (data[3] >> 0) & 0xFF); 182 336 } 337 + 183 338 /* there are two cases for completion: 184 339 * - we got all 16 segments 185 - * - we found a end of line AND we got all segments before it */ 186 - if (rt_segment == 0xffff || (done && rt_segment == (1 << segment) - 1)) { 187 - rt_data[pos] = '\0'; 188 - if (strcmp(rt_copy, rt_data) != 0) { 189 - /* we got an updated message */ 190 - strcpy(rt_copy, rt_data); 191 - return true; 192 - } 340 + * - we found an end of line */ 341 + if (++rt_segment < 16 && !done) { 342 + SET_TIMEOUT(rt_timeout, GROUP2_TIMEOUT); 343 + return; 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 356 /* handles a group 4a packet (clock-time) */ 199 - static bool handle_group4a(uint16_t data[4]) 357 + static void handle_group4a(const uint16_t data[4]) 200 358 { 201 359 int daycode = ((data[1] << 15) & 0x18000) | 202 360 ((data[2] >> 1) & 0x07FFF); ··· 208 366 209 367 if (daycode < 55927) { 210 368 /* invalid date, before 2012-01-01 */ 211 - return false; 369 + return; 212 370 } 213 371 if ((hour >= 24) || (minute >= 60)) { 214 372 /* invalid time */ 215 - return false; 373 + return; 216 374 } 217 375 if (offset_abs > 24) { 218 376 /* invalid local time offset */ 219 - return false; 377 + return; 220 378 } 221 379 222 380 /* convert modified julian day + time to UTC */ 223 - time_t seconds = (daycode - 40587) * 86400; 224 - seconds += hour * 3600; 225 - seconds += minute * 60; 226 - seconds += ((offset_sig == 0) ? offset_abs : -offset_abs) * 1800; 227 - ct_data = seconds; 228 - 229 - return true; 381 + time_t seconds = daycode - 40587; 382 + if (seconds < 24854) { 383 + seconds *= 86400; 384 + seconds += hour * 3600; 385 + seconds += minute * 60; 386 + seconds += ((offset_sig == 0) ? offset_abs : -offset_abs) * 1800; 387 + ct_data = seconds; 388 + } 230 389 } 231 390 232 - /* processes one rds packet, returns true if a new message was received */ 233 - bool rds_process(uint16_t data[4]) 391 + /* processes one rds packet */ 392 + void rds_process(const uint16_t data[4]) 234 393 { 235 394 int group; 236 395 ··· 240 399 pi_code = pi; 241 400 } 242 401 pi_last = pi; 243 - 402 + 244 403 /* handle rds data based on group */ 245 404 group = (data[1] >> 11) & 0x1F; 246 405 switch (group) { 247 - 406 + 248 407 case 0: /* group 0A: basic info */ 249 408 case 1: /* group 0B: basic info */ 250 - return handle_group0(data); 251 - 409 + handle_group0(data); 410 + break; 411 + 252 412 case 4: /* group 2A: radio text */ 253 413 case 5: /* group 2B: radio text */ 254 - return handle_group2(data); 414 + handle_group2(data); 415 + break; 255 416 256 417 case 8: /* group 4A: clock-time */ 257 - return handle_group4a(data); 258 - 259 - default: 418 + handle_group4a(data); 260 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 267 - access */ 268 - 269 - /* returns the programme identification code */ 270 - uint16_t rds_get_pi(void) 426 + #if (CONFIG_RDS & RDS_CFG_PUSH) 427 + /* pushes preprocesed RDS information */ 428 + void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size) 271 429 { 272 - return pi_code; 273 - } 430 + switch (info_id) { 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 */ 276 - char* rds_get_ps(void) 277 - { 278 - return get_ps(); 279 - } 451 + default:; 452 + } 280 453 281 - /* returns the most recent valid RadioText message */ 282 - char* rds_get_rt(void) 283 - { 284 - return get_rt(); 454 + register_activity(); 285 455 } 456 + #endif /* (CONFIG_RDS & RDS_CFG_PUSH) */ 286 457 287 - /* returns the most recent valid clock-time value (or 0 if invalid) */ 288 - time_t rds_get_ct(void) 458 + /* read fully-processed RDS data */ 459 + size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size) 289 460 { 290 - return ct_data; 291 - } 461 + int oldlevel = rds_disable_irq_save(); 462 + 463 + rds_sync(); 292 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 + }
+4 -29
firmware/drivers/tuner/ipod_remote_tuner.c
··· 30 30 #include "adc.h" 31 31 #include "settings.h" 32 32 #include "power.h" 33 + #include "rds.h" 33 34 34 35 static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF; 35 36 /* temp var for tests to avoid looping execution in submenus settings*/ ··· 40 41 static int tuner_frequency = 0; 41 42 static int tuner_signal_power = 0; 42 43 static bool radio_tuned = false; 43 - static bool rds_event = false; 44 44 45 45 static char rds_radioname[9]; 46 46 static char rds_radioinfo[65]; ··· 90 90 { 91 91 if (state == 0) 92 92 { 93 + rds_init(); 93 94 tuner_param = 0x00; 94 95 old_tuner_param = 0xFF; 95 96 mono_mode = -1; ··· 273 274 { 274 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 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 285 /* tuner abstraction layer: set something to the tuner */ ··· 421 421 case RADIO_STEREO: 422 422 val = true; 423 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 425 return val; 434 426 } 435 - 436 - char* 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 - }
+19 -68
firmware/drivers/tuner/si4700.c
··· 213 213 static bool tuner_present = false; 214 214 static uint16_t cache[16]; 215 215 static struct mutex fmr_mutex SHAREDBSS_ATTR; 216 - #ifdef HAVE_RDS_CAP 217 - static int rds_event = 0; 218 - #endif 219 216 220 217 /* reads <len> registers from radio at offset 0x0A into cache */ 221 218 static void si4700_read(int len) ··· 373 370 si4700_sleep(1); 374 371 375 372 #ifdef HAVE_RDS_CAP 373 + rds_init(); 376 374 si4700_rds_init(); 377 375 #endif 378 376 } ··· 528 526 case RADIO_RSSI_MAX: 529 527 val = RSSI_MAX; 530 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 531 mutex_unlock(&fmr_mutex); ··· 567 550 568 551 #ifdef HAVE_RDS_CAP 569 552 570 - #ifdef RDS_ISR_PROCESSING 571 - /* Read raw RDS info for processing - in ISR */ 553 + #if (CONFIG_RDS & RDS_CFG_ISR) 554 + static unsigned char isr_regbuf[(RDSD - STATUSRSSI + 1) * 2]; 572 555 573 - /* Assumes regbuf is 32 bytes */ 574 - void si4700_rds_read_raw_async(void) 556 + /* Called by RDS interrupt on target */ 557 + void 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 579 - void si4700_rds_read_raw_async_complete(unsigned char *regbuf, 580 - uint16_t data[4]) 562 + /* Handle RDS event from ISR */ 563 + void 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 568 for (int i = 0; i < 4; i++) { 585 - data[i] = regbuf[index] << 8 | regbuf[index + 1]; 586 - regbuf += 2; 569 + rds_data[i] = isr_regbuf[index] << 8 | isr_regbuf[index + 1]; 570 + index += 2; 587 571 } 572 + 573 + rds_process(rds_data); 588 574 } 589 575 590 - /* Set the event flag */ 591 - void si4700_rds_set_event(void) 592 - { 593 - rds_event = 1; 594 - } 576 + #else /* !(CONFIG_RDS & RDS_CFG_ISR) */ 595 577 596 - #else /* ndef RDS_ISR_PROCESSING */ 597 - /* Read raw RDS info for processing */ 598 - bool si4700_rds_read_raw(uint16_t data[4]) 578 + /* Handle RDS event from thread */ 579 + void si4700_rds_process(void) 599 580 { 600 - bool retval = false; 601 - 602 581 mutex_lock(&fmr_mutex); 603 582 604 583 if (tuner_powered()) 605 584 { 606 585 si4700_read_reg(RDSD); 607 - memcpy(data, &cache[RDSA], 4 * sizeof (uint16_t)); 608 - retval = true; 586 + rds_process(&cache[RDSA]); 609 587 } 610 588 611 589 mutex_unlock(&fmr_mutex); 612 - 613 - return retval; 614 590 } 591 + #endif /* (CONFIG_RDS & RDS_CFG_ISR) */ 615 592 616 - /* Set the event flag */ 617 - void si4700_rds_set_event(void) 618 - { 619 - mutex_lock(&fmr_mutex); 620 - rds_event = 1; 621 - mutex_unlock(&fmr_mutex); 622 - } 623 - #endif /* RDS_ISR_PROCESSING */ 624 - 625 - char * 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 593 #endif /* HAVE_RDS_CAP */ 643 594
+10
firmware/export/config.h
··· 712 712 #define BATTERY_CAPACITY_INC 0 713 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 725 #ifndef CONFIG_ORIENTATION 716 726 #if LCD_HEIGHT > LCD_WIDTH 717 727 #define CONFIG_ORIENTATION SCREEN_PORTRAIT
+1 -1
firmware/export/config/gigabeats.h
··· 121 121 /* Define this if you have a SI4700 fm radio tuner */ 122 122 #define CONFIG_TUNER SI4700 123 123 #define HAVE_RDS_CAP 124 - #define RDS_ISR_PROCESSING 124 + #define CONFIG_RDS (RDS_CFG_ISR | RDS_CFG_PROCESS) 125 125 126 126 /* define this if you can flip your LCD */ 127 127 #define HAVE_LCD_FLIP
+1
firmware/export/config/ipod4g.h
··· 164 164 /* Define Apple remote tuner */ 165 165 #define CONFIG_TUNER IPOD_REMOTE_TUNER 166 166 #define HAVE_RDS_CAP 167 + #define CONFIG_RDS RDS_CFG_PUSH 167 168 168 169 /* Define this if you have a PortalPlayer PP5020 */ 169 170 #define CONFIG_CPU PP5020
+1
firmware/export/config/ipod6g.h
··· 167 167 /* Define Apple remote tuner */ 168 168 //#define CONFIG_TUNER IPOD_REMOTE_TUNER 169 169 //#define HAVE_RDS_CAP 170 + //#define CONFIG_RDS RDS_CFG_PUSH 170 171 171 172 /* The exact type of CPU */ 172 173 #define CONFIG_CPU S5L8702
+1
firmware/export/config/ipodcolor.h
··· 151 151 /* Define Apple remote tuner */ 152 152 #define CONFIG_TUNER IPOD_REMOTE_TUNER 153 153 #define HAVE_RDS_CAP 154 + #define CONFIG_RDS RDS_CFG_PUSH 154 155 155 156 /* Define this if you have a PortalPlayer PP5020 */ 156 157 #define CONFIG_CPU PP5020
+1
firmware/export/config/ipodmini1g.h
··· 163 163 /* Define Apple remote tuner */ 164 164 #define CONFIG_TUNER IPOD_REMOTE_TUNER 165 165 #define HAVE_RDS_CAP 166 + #define CONFIG_RDS RDS_CFG_PUSH 166 167 167 168 /* Define this if you have a PortalPlayer PP5020 */ 168 169 #define CONFIG_CPU PP5020
+1
firmware/export/config/ipodmini2g.h
··· 164 164 /* Define Apple remote tuner */ 165 165 #define CONFIG_TUNER IPOD_REMOTE_TUNER 166 166 #define HAVE_RDS_CAP 167 + #define CONFIG_RDS RDS_CFG_PUSH 167 168 168 169 /* Define this if you have a PortalPlayer PP5022 */ 169 170 #define CONFIG_CPU PP5022
+1
firmware/export/config/ipodnano1g.h
··· 154 154 /* Define Apple remote tuner */ 155 155 #define CONFIG_TUNER IPOD_REMOTE_TUNER 156 156 #define HAVE_RDS_CAP 157 + #define CONFIG_RDS RDS_CFG_PUSH 157 158 158 159 /* Define this if you have a PortalPlayer PP5022 */ 159 160 #define CONFIG_CPU PP5022
+1
firmware/export/config/ipodnano2g.h
··· 164 164 /* Define Apple remote tuner */ 165 165 //#define CONFIG_TUNER IPOD_REMOTE_TUNER 166 166 //#define HAVE_RDS_CAP 167 + //#define CONFIG_RDS RDS_CFG_PUSH 167 168 168 169 /* The exact type of CPU */ 169 170 #define CONFIG_CPU S5L8701
+1
firmware/export/config/ipodvideo.h
··· 170 170 /* Define Apple remote tuner */ 171 171 #define CONFIG_TUNER IPOD_REMOTE_TUNER 172 172 #define HAVE_RDS_CAP 173 + #define CONFIG_RDS RDS_CFG_PUSH 173 174 174 175 /* Define this if you have a PortalPlayer PP5022 */ 175 176 #define CONFIG_CPU PP5022
-3
firmware/export/ipod_remote_tuner.h
··· 33 33 34 34 int ipod_rmt_tuner_set(int setting, int value); 35 35 int ipod_rmt_tuner_get(int setting); 36 - char* ipod_get_rds_info(int setting); 37 - 38 36 39 37 #ifndef CONFIG_TUNER_MULTI 40 38 #define tuner_set ipod_rmt_tuner_set 41 39 #define tuner_get ipod_rmt_tuner_get 42 - #define tuner_get_rds_info ipod_get_rds_info 43 40 #endif 44 41 45 42 #endif /* _IPOD_REMOTE_TUNER_H_ */
+33 -6
firmware/export/rds.h
··· 18 18 * KIND, either express or implied. 19 19 * 20 20 ****************************************************************************/ 21 + #ifndef RDS_H 22 + #define RDS_H 23 + 21 24 #include <stdint.h> 22 25 #include <stdbool.h> 23 26 #include "time.h" 24 27 25 28 void rds_init(void); 29 + void rds_reset(void); 30 + void rds_sync(void); 31 + 32 + #if (CONFIG_RDS & RDS_CFG_PROCESS) 33 + /* RDS raw data processing */ 34 + void rds_process(const uint16_t data[4]); 35 + #endif /* (CONFIG_RDS & RDS_CFG_PROCESS) */ 36 + 37 + enum 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 + }; 26 46 27 - void rds_reset(void); 28 - bool rds_process(uint16_t data[4]); 47 + enum 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 30 - uint16_t rds_get_pi(void); 31 - char* rds_get_ps(void); 32 - char* rds_get_rt(void); 33 - time_t rds_get_ct(void); 54 + #if (CONFIG_RDS & RDS_CFG_PUSH) 55 + /* pushes preprocesed RDS information */ 56 + void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size); 57 + #endif /* (CONFIG_RDS & RDS_CFG_PUSH) */ 34 58 59 + /* read fully-processed RDS data */ 60 + size_t rds_pull_info(enum rds_info_id info_id, uintptr_t data, size_t size); 35 61 62 + #endif /* RDS_H */
+13 -15
firmware/export/si4700.h
··· 35 35 }; 36 36 37 37 bool si4700_detect(void); 38 - void si4700_init(void); 38 + void si4700_init(void) INIT_ATTR; 39 39 int si4700_set(int setting, int value); 40 40 int si4700_get(int setting); 41 41 void si4700_dbg_info(struct si4700_dbg_info *nfo); 42 42 /* For interrupt-based mono/stereo indicator */ 43 43 bool si4700_st(void); 44 44 45 + #ifdef HAVE_RDS_CAP 45 46 /** RDS support **/ 46 - void si4700_rds_init(void); 47 + 48 + void si4700_rds_init(void) INIT_ATTR; 47 49 /* Radio is fully powered up or about to be powered down */ 48 50 void si4700_rds_powerup(bool on); 49 - #ifdef RDS_ISR_PROCESSING 51 + 52 + #if (CONFIG_RDS & RDS_CFG_ISR) 50 53 /* Read raw RDS info for processing - asynchronously */ 51 - void si4700_read_raw_async(int count); /* implemented by target */ 52 - void si4700_rds_read_raw_async(void); 53 - void si4700_rds_read_raw_async_complete(unsigned char *regbuf, 54 - uint16_t data[4]); 55 - #else /* ndef RDS_ISR_PROCESSING */ 54 + void si4700_rds_read_raw_async(unsigned char *buf, int count); /* implemented by target */ 55 + void si4700_rds_interrupt(void); 56 + #endif /* (CONFIG_RDS & RDS_CFG_ISR) */ 57 + 56 58 /* Read raw RDS info for processing */ 57 - bool si4700_rds_read_raw(uint16_t data[4]); 58 - #endif /* RDS_ISR_PROCESSING */ 59 - /* Obtain specified string */ 60 - char* si4700_get_rds_info(int setting); 61 - /* Set the event flag */ 62 - void si4700_rds_set_event(void); 59 + void si4700_rds_process(void); 60 + 61 + #endif /* HAVE_RDS_CAP */ 63 62 64 63 #ifndef CONFIG_TUNER_MULTI 65 64 #define tuner_set si4700_set 66 65 #define tuner_get si4700_get 67 - #define tuner_get_rds_info si4700_get_rds_info 68 66 #endif 69 67 70 68 #endif /* _SI4700_H_ */
+13 -6
firmware/export/tuner.h
··· 25 25 #include "config.h" 26 26 #include "hwcompat.h" 27 27 28 + #ifdef HAVE_RDS_CAP 29 + #include <sys/types.h> 30 + #endif 31 + 28 32 /** Settings to the tuner layer **/ 29 33 enum 30 34 { ··· 45 49 RADIO_PRESENT = 0, 46 50 RADIO_TUNED, 47 51 RADIO_STEREO, 48 - /* RADIO_EVENT is an event that requests a screen update */ 49 - RADIO_EVENT, 50 52 RADIO_RSSI, 51 53 RADIO_RSSI_MIN, 52 54 RADIO_RSSI_MAX, ··· 57 59 58 60 #ifdef HAVE_RDS_CAP 59 61 /** Readback from the tuner RDS layer **/ 60 - enum 62 + /* returns needed size if buffer size is inadequate */ 63 + size_t tuner_get_rds_info(int setting, void *dst, size_t dstsize); 64 + 65 + enum RADIO_RDS_INFO 61 66 { 62 - RADIO_RDS_NAME, 63 - RADIO_RDS_TEXT, 67 + RADIO_RDS_NAME, /* dst: array of char, dstsize: buffer size */ 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 72 /* Put new general-purpose readback values above this line */ 66 73 __RADIO_GET_RDS_INFO_STANDARD_LAST 67 74 }; 68 - #endif 75 + #endif /* HAVE_RDS_CAP */ 69 76 70 77 /** Tuner regions **/ 71 78
+1 -7
firmware/target/arm/as3525/fmradio-i2c-as3525.c
··· 35 35 #include "generic_i2c.h" 36 36 #include "fmradio_i2c.h" 37 37 #include "thread.h" 38 - #include "rds.h" 39 38 40 39 #if defined(SANSA_CLIP) || defined(SANSA_C200V2) 41 40 #define I2C_SCL_GPIO(x) GPIOB_PIN(x) ··· 203 202 /* Captures RDS data and processes it */ 204 203 static void NORETURN_ATTR rds_thread(void) 205 204 { 206 - uint16_t rds_data[4]; 207 - 208 205 while (true) { 209 206 semaphore_wait(&rds_sema, TIMEOUT_BLOCK); 210 - if (si4700_rds_read_raw(rds_data) && rds_process(rds_data)) { 211 - si4700_rds_set_event(); 212 - } 207 + si4700_rds_process(); 213 208 } 214 209 } 215 210 ··· 233 228 void si4700_rds_init(void) 234 229 { 235 230 semaphore_init(&rds_sema, 1, 0); 236 - rds_init(); 237 231 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" 238 232 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); 239 233 }
+2 -6
firmware/target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c
··· 23 23 #include "system.h" 24 24 #include "kernel.h" 25 25 #include "pinctrl-imx233.h" 26 - #include "rds.h" 27 26 #include "si4700.h" 28 27 29 28 /** ··· 52 51 /* Captures RDS data and processes it */ 53 52 static void NORETURN_ATTR rds_thread(void) 54 53 { 55 - uint16_t rds_data[4]; 56 - 57 54 while(true) 58 55 { 59 56 semaphore_wait(&rds_sema, TIMEOUT_BLOCK); 60 - if(si4700_rds_read_raw(rds_data) && rds_process(rds_data)) 61 - si4700_rds_set_event(); 57 + si4700_rds_process(); 58 + 62 59 /* renable callback */ 63 60 imx233_pinctrl_setup_irq(2, 27, true, true, false, &stc_rds_callback, 0); 64 61 } ··· 86 83 void si4700_rds_init(void) 87 84 { 88 85 semaphore_init(&rds_sema, 1, 0); 89 - rds_init(); 90 86 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" 91 87 IF_PRIO(, PRIORITY_REALTIME) IF_COP(, CPU)); 92 88 }
+19 -36
firmware/target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c
··· 29 29 #include "gpio-target.h" 30 30 #include "i2c-imx31.h" 31 31 #include "fmradio_i2c.h" 32 - #include "rds.h" 33 32 #include "tuner.h" 34 33 35 34 static struct i2c_node si4700_i2c_node = ··· 128 127 129 128 130 129 /* Low-level RDS Support */ 131 - 132 - /* Transfer descriptor for RDS async operations */ 133 - static 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 - 142 130 static bool int_restore; 143 131 144 - static void si4700_rds_read_raw_callback(struct i2c_transfer_desc *xfer) 132 + /* Called after I2C read cycle completes */ 133 + static 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 135 if (xfer->rxcount == 0) 150 - { 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 - } 136 + si4700_rds_process(); 157 137 /* else read didn't finish */ 158 138 159 139 if (int_restore) 160 140 gpio_int_enable(SI4700_EVENT_ID); 161 141 } 162 142 163 - /* Callback from si4700_rds_read_raw to execute the read */ 164 - void si4700_read_raw_async(int count) 143 + /* Called to read registers from ISR context */ 144 + void si4700_rds_read_raw_async(unsigned char *buf, int count) 165 145 { 166 - si4700_xfer.xfer.txdata = NULL; 167 - si4700_xfer.xfer.txcount = 0; 168 - si4700_xfer.xfer.rxdata = si4700_xfer.regbuf; 169 - si4700_xfer.xfer.rxcount = count; 170 - si4700_xfer.xfer.callback = si4700_rds_read_raw_callback; 171 - si4700_xfer.xfer.next = NULL; 146 + /* transfer descriptor for RDS async operations */ 147 + static struct i2c_transfer_desc xfer = { .node = &si4700_i2c_node }; 148 + 149 + xfer.txdata = NULL; 150 + xfer.txcount = 0; 151 + xfer.rxdata = buf; 152 + xfer.rxcount = count; 153 + xfer.callback = si4700_rds_read_raw_async_callback; 154 + xfer.next = NULL; 172 155 173 - i2c_transfer(&si4700_xfer.xfer); 156 + i2c_transfer(&xfer); 174 157 } 175 158 176 159 /* RDS GPIO interrupt handler - start RDS data read */ 177 160 void INT_SI4700_RDS(void) 178 161 { 179 - /* mask and clear the interrupt */ 162 + /* mask and clear the interrupt until we're done */ 180 163 gpio_int_disable(SI4700_EVENT_ID); 181 164 gpio_int_clear(SI4700_EVENT_ID); 182 165 183 - /* read the RDS data */ 184 - si4700_rds_read_raw_async(); 166 + /* tell radio driver about it */ 167 + si4700_rds_interrupt(); 185 168 } 186 169 187 170 /* Called with on=true after full radio power up, and with on=false before ··· 197 180 /* One-time RDS init at startup */ 198 181 void si4700_rds_init(void) 199 182 { 200 - rds_init(); 183 + /* nothing to do */ 201 184 }
+1 -5
firmware/target/hosted/samsungypr/radio-ypr.c
··· 29 29 #include "kernel.h" 30 30 31 31 #include "radio-ypr.h" 32 - #include "rds.h" 33 32 #include "si4700.h" 34 33 #include "power.h" 35 34 ··· 79 78 /* Low-level RDS Support */ 80 79 static struct event_queue rds_queue; 81 80 static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)]; 82 - static uint16_t rds_data[4]; 83 81 84 82 enum { 85 83 Q_POWERUP, ··· 101 99 case SYS_TIMEOUT: 102 100 /* Captures RDS data and processes it */ 103 101 if ((si4709_read_reg(STATUSRSSI) & STATUSRSSI_RDSR) >> 8) { 104 - if (si4700_rds_read_raw(rds_data) && rds_process(rds_data)) 105 - si4700_rds_set_event(); 102 + si4700_rds_process(); 106 103 } 107 104 break; 108 105 } ··· 121 118 queue_init(&rds_queue, false); 122 119 create_thread(rds_thread, rds_stack, sizeof(rds_stack), 0, "rds" 123 120 IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); 124 - rds_init(); 125 121 } 126 122 #endif /* HAVE_RDS_CAP */
+23
firmware/tuner.c
··· 24 24 #include "kernel.h" 25 25 #include "tuner.h" 26 26 #include "fmradio.h" 27 + #ifdef HAVE_RDS_CAP 28 + #include "rds.h" 29 + #endif /* HAVE_RDS_CAP */ 27 30 28 31 /* General region information */ 29 32 const struct fm_region_data fm_region_data[TUNER_NUM_REGIONS] = ··· 102 105 #endif 103 106 } 104 107 } 108 + 109 + #ifdef HAVE_RDS_CAP 110 + size_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 128 #endif /* SIMULATOR */