1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/kernel.h>
21#include <linux/ktime.h>
22#include <linux/string.h>
23#include <linux/videodev2.h>
24
25#include "vivid-rds-gen.h"
26
27static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp)
28{
29 switch (grp) {
30 case 0:
31 return (rds->dyn_pty << 2) | (grp & 3);
32 case 1:
33 return (rds->compressed << 2) | (grp & 3);
34 case 2:
35 return (rds->art_head << 2) | (grp & 3);
36 case 3:
37 return (rds->mono_stereo << 2) | (grp & 3);
38 }
39 return 0;
40}
41
42
43
44
45
46
47
48
49
50
51
52
53
54void vivid_rds_generate(struct vivid_rds_gen *rds)
55{
56 struct v4l2_rds_data *data = rds->data;
57 unsigned grp;
58 unsigned idx;
59 struct tm tm;
60 unsigned date;
61 unsigned time;
62 int l;
63
64 for (grp = 0; grp < VIVID_RDS_GEN_GROUPS; grp++, data += VIVID_RDS_GEN_BLKS_PER_GRP) {
65 data[0].lsb = rds->picode & 0xff;
66 data[0].msb = rds->picode >> 8;
67 data[0].block = V4L2_RDS_BLOCK_A | (V4L2_RDS_BLOCK_A << 3);
68 data[1].lsb = rds->pty << 5;
69 data[1].msb = (rds->pty >> 3) | (rds->tp << 2);
70 data[1].block = V4L2_RDS_BLOCK_B | (V4L2_RDS_BLOCK_B << 3);
71 data[3].block = V4L2_RDS_BLOCK_D | (V4L2_RDS_BLOCK_D << 3);
72
73 switch (grp) {
74 case 0 ... 3:
75 case 22 ... 25:
76 case 44 ... 47:
77 idx = (grp % 22) % 4;
78 data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
79 data[1].lsb |= vivid_get_di(rds, idx);
80 data[1].msb |= 1 << 3;
81 data[2].lsb = rds->picode & 0xff;
82 data[2].msb = rds->picode >> 8;
83 data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
84 data[3].lsb = rds->psname[2 * idx + 1];
85 data[3].msb = rds->psname[2 * idx];
86 break;
87 case 4 ... 19:
88 case 26 ... 41:
89 idx = ((grp - 4) % 22) % 16;
90 data[1].lsb |= idx;
91 data[1].msb |= 4 << 3;
92 data[2].msb = rds->radiotext[4 * idx];
93 data[2].lsb = rds->radiotext[4 * idx + 1];
94 data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
95 data[3].msb = rds->radiotext[4 * idx + 2];
96 data[3].lsb = rds->radiotext[4 * idx + 3];
97 break;
98 case 56:
99
100
101
102
103
104
105
106 time_to_tm(get_seconds(), 0, &tm);
107 l = tm.tm_mon <= 1;
108 date = 14956 + tm.tm_mday + ((tm.tm_year - l) * 1461) / 4 +
109 ((tm.tm_mon + 2 + l * 12) * 306001) / 10000;
110 time = (tm.tm_hour << 12) |
111 (tm.tm_min << 6) |
112 (sys_tz.tz_minuteswest >= 0 ? 0x20 : 0) |
113 (abs(sys_tz.tz_minuteswest) / 30);
114 data[1].lsb &= ~3;
115 data[1].lsb |= date >> 15;
116 data[1].msb |= 8 << 3;
117 data[2].lsb = (date << 1) & 0xfe;
118 data[2].lsb |= (time >> 16) & 1;
119 data[2].msb = (date >> 7) & 0xff;
120 data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
121 data[3].lsb = time & 0xff;
122 data[3].msb = (time >> 8) & 0xff;
123 break;
124 default:
125 data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
126 data[1].lsb |= vivid_get_di(rds, grp % 22);
127 data[1].msb |= 0x1f << 3;
128 data[2].lsb = rds->picode & 0xff;
129 data[2].msb = rds->picode >> 8;
130 data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
131 data[3].lsb = rds->pty << 5;
132 data[3].lsb |= (rds->ta << 4) | (rds->ms << 3);
133 data[3].lsb |= vivid_get_di(rds, grp % 22);
134 data[3].msb |= rds->pty >> 3;
135 data[3].msb |= 0x1f << 3;
136 break;
137 }
138 }
139}
140
141void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
142 bool alt)
143{
144
145 if (rds->use_rbds) {
146 rds->picode = 0x2e75;
147 rds->pty = alt ? 29 : 2;
148 } else {
149 rds->picode = 0x8088;
150 rds->pty = alt ? 16 : 3;
151 }
152 rds->mono_stereo = true;
153 rds->art_head = false;
154 rds->compressed = false;
155 rds->dyn_pty = false;
156 rds->tp = true;
157 rds->ta = alt;
158 rds->ms = true;
159 snprintf(rds->psname, sizeof(rds->psname), "%6d.%1d",
160 freq / 16, ((freq & 0xf) * 10) / 16);
161 if (alt)
162 strlcpy(rds->radiotext,
163 " The Radio Data System can switch between different Radio Texts ",
164 sizeof(rds->radiotext));
165 else
166 strlcpy(rds->radiotext,
167 "An example of Radio Text as transmitted by the Radio Data System",
168 sizeof(rds->radiotext));
169}
170