summaryrefslogtreecommitdiff
path: root/firmware/drivers/rds.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/rds.c')
-rw-r--r--firmware/drivers/rds.c486
1 files changed, 345 insertions, 141 deletions
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}