diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2017-01-30 09:52:05 -0500 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2017-02-11 22:19:32 -0500 |
commit | fc9695eb47732e1c189e2f033dbd55e5c346e8c4 (patch) | |
tree | 550830e9af67f47e3eb87587770b25d22110f57a /firmware | |
parent | 6436c6e749ab04fbd5d97804a6a1c3b3122b326d (diff) | |
download | rockbox-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
Diffstat (limited to 'firmware')
22 files changed, 493 insertions, 324 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 */ | ||
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 */ | ||
58 | |||
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)] | ||
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 */ | ||
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 | /* radio text */ | 66 | /* radio text */ |
43 | static char rt_data[65]; | 67 | static char rt_data[2][65]; /* round-robin driver work queue */ |
44 | static char rt_copy[65]; | 68 | static int rt_segment; /* next expected segment */ |
45 | static long rt_segment_timeout[16]; | 69 | static long rt_timeout; /* timeout to receive full group */ |
46 | static int rt_segment;// bitmap of received segments | 70 | static int rt_abflag; /* message change flag */ |
47 | static int rt_abflag; | 71 | static int rt_data_idx; /* rt_data[0 or 1] */ |
48 | /* date/time */ | 72 | #define RT_DATA_INC(x) rt_data[rt_data_idx ^= (x)] |
49 | static 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) */ |
56 | static 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 */ | ||
85 | static 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 */ | ||
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; | ||
63 | } | 160 | } |
64 | static 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 */ | ||
164 | static 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 */ | ||
189 | static 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)) | ||
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 | /* resets the rds parser */ | 195 | /* resets the rds parser */ |
80 | void rds_reset(void) | 196 | void 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 */ | ||
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 | /* 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 */ |
102 | static bool handle_group0(uint16_t data[4]) | 247 | static 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 */ |
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 | 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 */ |
149 | static bool handle_group2(uint16_t data[4]) | 305 | static 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) */ |
199 | static bool handle_group4a(uint16_t data[4]) | 357 | static 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 */ |
233 | bool rds_process(uint16_t data[4]) | 392 | void 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 | 428 | void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size) | |
269 | /* returns the programme identification code */ | ||
270 | uint16_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:; |
276 | char* rds_get_ps(void) | 452 | } |
277 | { | ||
278 | return get_ps(); | ||
279 | } | ||
280 | 453 | ||
281 | /* returns the most recent valid RadioText message */ | 454 | register_activity(); |
282 | char* 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 */ |
288 | time_t rds_get_ct(void) | 459 | size_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 | ||
34 | static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF; | 35 | static 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; | |||
40 | static int tuner_frequency = 0; | 41 | static int tuner_frequency = 0; |
41 | static int tuner_signal_power = 0; | 42 | static int tuner_signal_power = 0; |
42 | static bool radio_tuned = false; | 43 | static bool radio_tuned = false; |
43 | static bool rds_event = false; | ||
44 | 44 | ||
45 | static char rds_radioname[9]; | 45 | static char rds_radioname[9]; |
46 | static char rds_radioinfo[65]; | 46 | static 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 | |||
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 | } | ||
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 @@ | |||
213 | static bool tuner_present = false; | 213 | static bool tuner_present = false; |
214 | static uint16_t cache[16]; | 214 | static uint16_t cache[16]; |
215 | static struct mutex fmr_mutex SHAREDBSS_ATTR; | 215 | static struct mutex fmr_mutex SHAREDBSS_ATTR; |
216 | #ifdef HAVE_RDS_CAP | ||
217 | static 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 */ |
221 | static void si4700_read(int len) | 218 | static 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 */ | 554 | static unsigned char isr_regbuf[(RDSD - STATUSRSSI + 1) * 2]; |
572 | 555 | ||
573 | /* Assumes regbuf is 32 bytes */ | 556 | /* Called by RDS interrupt on target */ |
574 | void si4700_rds_read_raw_async(void) | 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, | 562 | /* Handle RDS event from ISR */ |
580 | uint16_t data[4]) | 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 | 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); |
591 | void 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 */ | ||
598 | bool si4700_rds_read_raw(uint16_t data[4]) | ||
599 | { | ||
600 | bool retval = false; | ||
601 | 577 | ||
578 | /* Handle RDS event from thread */ | ||
579 | void 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 */ | ||
617 | void 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 | ||
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 | #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 | ||
34 | int ipod_rmt_tuner_set(int setting, int value); | 34 | int ipod_rmt_tuner_set(int setting, int value); |
35 | int ipod_rmt_tuner_get(int setting); | 35 | int ipod_rmt_tuner_get(int setting); |
36 | char* 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 | ||
25 | void rds_init(void); | 28 | void rds_init(void); |
26 | |||
27 | void rds_reset(void); | 29 | void rds_reset(void); |
28 | bool rds_process(uint16_t data[4]); | 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 | }; | ||
46 | |||
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); | 54 | #if (CONFIG_RDS & RDS_CFG_PUSH) |
31 | char* rds_get_ps(void); | 55 | /* pushes preprocesed RDS information */ |
32 | char* rds_get_rt(void); | 56 | void rds_push_info(enum rds_info_id info_id, uintptr_t data, size_t size); |
33 | time_t rds_get_ct(void); | 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 */ | ||
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 | ||
37 | bool si4700_detect(void); | 37 | bool si4700_detect(void); |
38 | void si4700_init(void); | 38 | void si4700_init(void) INIT_ATTR; |
39 | int si4700_set(int setting, int value); | 39 | int si4700_set(int setting, int value); |
40 | int si4700_get(int setting); | 40 | int si4700_get(int setting); |
41 | void si4700_dbg_info(struct si4700_dbg_info *nfo); | 41 | void si4700_dbg_info(struct si4700_dbg_info *nfo); |
42 | /* For interrupt-based mono/stereo indicator */ | 42 | /* For interrupt-based mono/stereo indicator */ |
43 | bool si4700_st(void); | 43 | bool si4700_st(void); |
44 | 44 | ||
45 | #ifdef HAVE_RDS_CAP | ||
45 | /** RDS support **/ | 46 | /** RDS support **/ |
46 | void si4700_rds_init(void); | 47 | |
48 | void 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 */ |
48 | void si4700_rds_powerup(bool on); | 50 | void 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 */ |
51 | void si4700_read_raw_async(int count); /* implemented by target */ | 54 | void si4700_rds_read_raw_async(unsigned char *buf, int count); /* implemented by target */ |
52 | void si4700_rds_read_raw_async(void); | 55 | void si4700_rds_interrupt(void); |
53 | void 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 */ |
57 | bool si4700_rds_read_raw(uint16_t data[4]); | 59 | void si4700_rds_process(void); |
58 | #endif /* RDS_ISR_PROCESSING */ | 60 | |
59 | /* Obtain specified string */ | 61 | #endif /* HAVE_RDS_CAP */ |
60 | char* si4700_get_rds_info(int setting); | ||
61 | /* Set the event flag */ | ||
62 | void 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 **/ |
29 | enum | 33 | enum |
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 **/ |
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, | 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 */ |
204 | static void NORETURN_ATTR rds_thread(void) | 203 | static 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) | |||
233 | void si4700_rds_init(void) | 228 | void 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 */ |
53 | static void NORETURN_ATTR rds_thread(void) | 52 | static 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) | |||
86 | void si4700_rds_init(void) | 83 | void 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 | ||
35 | static struct i2c_node si4700_i2c_node = | 34 | static 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 */ | ||
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 | static bool int_restore; | 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 | 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 */ |
164 | void si4700_read_raw_async(int count) | 144 | void 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 */ |
177 | void INT_SI4700_RDS(void) | 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 | 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 */ |
198 | void si4700_rds_init(void) | 181 | void 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 */ |
80 | static struct event_queue rds_queue; | 79 | static struct event_queue rds_queue; |
81 | static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)]; | 80 | static uint32_t rds_stack[DEFAULT_STACK_SIZE / sizeof(uint32_t)]; |
82 | static uint16_t rds_data[4]; | ||
83 | 81 | ||
84 | enum { | 82 | enum { |
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 */ |
29 | const struct fm_region_data fm_region_data[TUNER_NUM_REGIONS] = | 32 | const 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 | ||
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 | #endif /* SIMULATOR */ | 128 | #endif /* SIMULATOR */ |