1 #include <cmath>
2
3 #include "v4l2-ctl.h"
4
5 static int tuner_index;
6 static struct v4l2_tuner tuner; /* set_freq/get_freq */
7 static struct v4l2_modulator modulator; /* set_freq/get_freq */
8 static int txsubchans; /* set_modulator */
9 static double freq; /* get/set frequency */
10 static struct v4l2_frequency vf; /* get_freq/set_freq */
11 static struct v4l2_hw_freq_seek freq_seek; /* freq-seek */
12 static double low, high; /* freq-seek frequency range */
13 static int mode = V4L2_TUNER_MODE_STEREO; /* set audio mode */
14
tuner_usage()15 void tuner_usage()
16 {
17 printf("\nTuner/Modulator options:\n"
18 " -F, --get-freq query the frequency [VIDIOC_G_FREQUENCY]\n"
19 " -f, --set-freq <freq>\n"
20 " set the frequency to <freq> MHz [VIDIOC_S_FREQUENCY]\n"
21 " -T, --get-tuner query the tuner settings [VIDIOC_G_TUNER]\n"
22 " -t, --set-tuner <mode>\n"
23 " set the audio mode of the tuner [VIDIOC_S_TUNER]\n"
24 " Possible values: mono, stereo, lang2, lang1, bilingual\n"
25 " --tuner-index <idx> Use idx as tuner idx for tuner/modulator commands\n"
26 " --list-freq-bands display all frequency bands for the tuner/modulator\n"
27 " [VIDIOC_ENUM_FREQ_BANDS]\n"
28 " --get-modulator query the modulator settings [VIDIOC_G_MODULATOR]\n"
29 " --set-modulator <txsubchans>\n"
30 " set the sub-carrier modulation [VIDIOC_S_MODULATOR]\n"
31 " <txsubchans> is one of:\n"
32 " mono: Modulate as mono\n"
33 " mono-rds: Modulate as mono with RDS (radio only)\n"
34 " stereo: Modulate as stereo\n"
35 " stereo-rds: Modulate as stereo with RDS (radio only)\n"
36 " bilingual: Modulate as bilingual\n"
37 " mono-sap: Modulate as mono with Second Audio Program\n"
38 " stereo-sap: Modulate as stereo with Second Audio Program\n"
39 " --freq-seek dir=<0/1>,wrap=<0/1>,spacing=<hz>,low=<freq>,high=<freq>\n"
40 " perform a hardware frequency seek [VIDIOC_S_HW_FREQ_SEEK]\n"
41 " dir is 0 (seek downward) or 1 (seek upward)\n"
42 " wrap is 0 (do not wrap around) or 1 (wrap around)\n"
43 " spacing sets the seek resolution (use 0 for default)\n"
44 " low and high set the low and high seek frequency range in MHz\n"
45 );
46 }
47
parse_freq_seek(char * optarg,struct v4l2_hw_freq_seek & seek)48 static void parse_freq_seek(char *optarg, struct v4l2_hw_freq_seek &seek)
49 {
50 char *value;
51 char *subs = optarg;
52
53 while (*subs != '\0') {
54 static constexpr const char *subopts[] = {
55 "dir",
56 "wrap",
57 "spacing",
58 "low",
59 "high",
60 nullptr
61 };
62
63 switch (parse_subopt(&subs, subopts, &value)) {
64 case 0:
65 seek.seek_upward = strtol(value, nullptr, 0);
66 break;
67 case 1:
68 seek.wrap_around = strtol(value, nullptr, 0);
69 break;
70 case 2:
71 seek.spacing = strtol(value, nullptr, 0);
72 break;
73 case 3:
74 low = strtod(value, nullptr);
75 break;
76 case 4:
77 high = strtod(value, nullptr);
78 break;
79 default:
80 tuner_usage();
81 std::exit(EXIT_FAILURE);
82 }
83 }
84 }
85
tuner_cmd(int ch,char * optarg)86 void tuner_cmd(int ch, char *optarg)
87 {
88 switch (ch) {
89 case OptSetFreq:
90 freq = strtod(optarg, nullptr);
91 break;
92 case OptSetTuner:
93 if (!strcmp(optarg, "stereo"))
94 mode = V4L2_TUNER_MODE_STEREO;
95 else if (!strcmp(optarg, "lang1"))
96 mode = V4L2_TUNER_MODE_LANG1;
97 else if (!strcmp(optarg, "lang2"))
98 mode = V4L2_TUNER_MODE_LANG2;
99 else if (!strcmp(optarg, "bilingual"))
100 mode = V4L2_TUNER_MODE_LANG1_LANG2;
101 else if (!strcmp(optarg, "mono"))
102 mode = V4L2_TUNER_MODE_MONO;
103 else {
104 fprintf(stderr, "Unknown audio mode\n");
105 tuner_usage();
106 std::exit(EXIT_FAILURE);
107 }
108 break;
109 case OptSetModulator:
110 txsubchans = strtol(optarg, nullptr, 0);
111 if (!strcmp(optarg, "stereo"))
112 txsubchans = V4L2_TUNER_SUB_STEREO;
113 else if (!strcmp(optarg, "stereo-sap"))
114 txsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_SAP;
115 else if (!strcmp(optarg, "bilingual"))
116 txsubchans = V4L2_TUNER_SUB_LANG1;
117 else if (!strcmp(optarg, "mono"))
118 txsubchans = V4L2_TUNER_SUB_MONO;
119 else if (!strcmp(optarg, "mono-sap"))
120 txsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_SAP;
121 else if (!strcmp(optarg, "stereo-rds"))
122 txsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS;
123 else if (!strcmp(optarg, "mono-rds"))
124 txsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_RDS;
125 else {
126 fprintf(stderr, "Unknown txsubchans value\n");
127 tuner_usage();
128 std::exit(EXIT_FAILURE);
129 }
130 break;
131 case OptFreqSeek:
132 parse_freq_seek(optarg, freq_seek);
133 break;
134 case OptTunerIndex:
135 tuner_index = strtoul(optarg, nullptr, 0);
136 break;
137 }
138 }
139
tuner_set(cv4l_fd & _fd)140 void tuner_set(cv4l_fd &_fd)
141 {
142 int fd = _fd.g_fd();
143 __u32 type = (capabilities & V4L2_CAP_RADIO) ?
144 V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
145 double fac = 16;
146
147 if (!options[OptSetFreq] && !options[OptSetTuner] && !options[OptListFreqBands]
148 && !options[OptSetModulator] && !options[OptFreqSeek]) {
149 /* Don't actually call G_[MODULATOR/TUNER] if we don't intend to
150 actually perform any tuner related function */
151 return;
152 }
153
154 if (capabilities & V4L2_CAP_MODULATOR) {
155 type = V4L2_TUNER_RADIO;
156 modulator.index = tuner_index;
157 if (doioctl(fd, VIDIOC_G_MODULATOR, &modulator) == 0) {
158 if (modulator.capability & V4L2_TUNER_CAP_LOW)
159 fac = 16000;
160 else if (modulator.capability & V4L2_TUNER_CAP_1HZ)
161 fac = 1000000;
162 else
163 fac = 16;
164 }
165 } else if (capabilities & V4L2_CAP_TUNER) {
166 tuner.index = tuner_index;
167 if (doioctl(fd, VIDIOC_G_TUNER, &tuner) == 0) {
168 if (tuner.capability & V4L2_TUNER_CAP_LOW)
169 fac = 16000;
170 else if (tuner.capability & V4L2_TUNER_CAP_1HZ)
171 fac = 1000000;
172 else
173 fac = 16;
174
175 type = tuner.type;
176 }
177 }
178 if (options[OptSetFreq]) {
179 vf.type = type;
180 vf.tuner = tuner_index;
181 vf.frequency = __u32(freq * fac);
182 if (doioctl(fd, VIDIOC_S_FREQUENCY, &vf) == 0)
183 printf("Frequency for tuner %d set to %d (%f MHz)\n",
184 vf.tuner, vf.frequency, vf.frequency / fac);
185 }
186
187 if (options[OptSetTuner]) {
188 struct v4l2_tuner vt;
189
190 memset(&vt, 0, sizeof(struct v4l2_tuner));
191 vt.index = tuner_index;
192 if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
193 vt.audmode = mode;
194 doioctl(fd, VIDIOC_S_TUNER, &vt);
195 }
196 }
197
198 if (options[OptListFreqBands]) {
199 struct v4l2_frequency_band band;
200
201 memset(&band, 0, sizeof(band));
202 band.tuner = tuner_index;
203 band.type = type;
204 band.index = 0;
205 printf("ioctl: VIDIOC_ENUM_FREQ_BANDS\n");
206 while (test_ioctl(fd, VIDIOC_ENUM_FREQ_BANDS, &band) >= 0) {
207 if (band.index)
208 printf("\n");
209 printf("\tIndex : %d\n", band.index);
210 printf("\tModulation : %s\n", modulation2s(band.modulation).c_str());
211 printf("\tCapability : %s\n", tcap2s(band.capability).c_str());
212 if (band.capability & V4L2_TUNER_CAP_LOW)
213 printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
214 band.rangelow / 16000.0, band.rangehigh / 16000.0);
215 else if (band.capability & V4L2_TUNER_CAP_1HZ)
216 printf("\tFrequency Range: %.6f MHz - %.6f MHz\n",
217 band.rangelow / 1000000.0, band.rangehigh / 1000000.0);
218 else
219 printf("\tFrequency Range: %.3f MHz - %.3f MHz\n",
220 band.rangelow / 16.0, band.rangehigh / 16.0);
221 band.index++;
222 }
223 }
224
225 if (options[OptSetModulator]) {
226 struct v4l2_modulator mt;
227
228 memset(&mt, 0, sizeof(struct v4l2_modulator));
229 mt.index = tuner_index;
230 if (doioctl(fd, VIDIOC_G_MODULATOR, &mt) == 0) {
231 mt.txsubchans = txsubchans;
232 doioctl(fd, VIDIOC_S_MODULATOR, &mt);
233 }
234 }
235
236 if (options[OptFreqSeek]) {
237 freq_seek.tuner = tuner_index;
238 freq_seek.type = type;
239 freq_seek.rangelow = __u32(low * fac);
240 freq_seek.rangehigh = __u32(high * fac);
241 doioctl(fd, VIDIOC_S_HW_FREQ_SEEK, &freq_seek);
242 }
243 }
244
tuner_get(cv4l_fd & _fd)245 void tuner_get(cv4l_fd &_fd)
246 {
247 int fd = _fd.g_fd();
248
249 if (options[OptGetFreq]) {
250 double fac = 16;
251
252 if (capabilities & V4L2_CAP_MODULATOR) {
253 vf.type = V4L2_TUNER_RADIO;
254 modulator.index = tuner_index;
255 if (doioctl(fd, VIDIOC_G_MODULATOR, &modulator) == 0) {
256 if (modulator.capability & V4L2_TUNER_CAP_LOW)
257 fac = 16000;
258 else if (modulator.capability & V4L2_TUNER_CAP_1HZ)
259 fac = 1000000;
260 else
261 fac = 16;
262 }
263 } else {
264 vf.type = V4L2_TUNER_ANALOG_TV;
265 tuner.index = tuner_index;
266 if (doioctl(fd, VIDIOC_G_TUNER, &tuner) == 0) {
267 if (tuner.capability & V4L2_TUNER_CAP_LOW)
268 fac = 16000;
269 else if (tuner.capability & V4L2_TUNER_CAP_1HZ)
270 fac = 1000000;
271 else
272 fac = 16;
273 vf.type = tuner.type;
274 }
275 }
276 vf.tuner = tuner_index;
277 if (doioctl(fd, VIDIOC_G_FREQUENCY, &vf) == 0)
278 printf("Frequency for %s %d: %d (%f MHz)\n",
279 (capabilities & V4L2_CAP_MODULATOR) ?
280 "modulator" : "tuner",
281 vf.tuner, vf.frequency, vf.frequency / fac);
282 }
283
284 if (options[OptGetTuner]) {
285 struct v4l2_tuner vt;
286
287 memset(&vt, 0, sizeof(struct v4l2_tuner));
288 vt.index = tuner_index;
289 if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
290 printf("Tuner %d:\n", vt.index);
291 printf("\tName : %s\n", vt.name);
292 printf("\tType : %s\n", ttype2s(vt.type).c_str());
293 printf("\tCapabilities : %s\n", tcap2s(vt.capability).c_str());
294 if (vt.capability & V4L2_TUNER_CAP_LOW)
295 printf("\tFrequency range : %.3f MHz - %.3f MHz\n",
296 vt.rangelow / 16000.0, vt.rangehigh / 16000.0);
297 else if (vt.capability & V4L2_TUNER_CAP_1HZ)
298 printf("\tFrequency range : %.6f MHz - %.6f MHz\n",
299 vt.rangelow / 1000000.0, vt.rangehigh / 1000000.0);
300 else
301 printf("\tFrequency range : %.3f MHz - %.3f MHz\n",
302 vt.rangelow / 16.0, vt.rangehigh / 16.0);
303
304 if (vt.type != V4L2_TUNER_SDR && vt.type != V4L2_TUNER_RF) {
305 printf("\tSignal strength/AFC : %ld%%/%d\n",
306 lround(vt.signal / 655.35), vt.afc);
307 printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode).c_str());
308 printf("\tAvailable subchannels: %s\n", rxsubchans2s(vt.rxsubchans).c_str());
309 }
310 }
311 }
312
313 if (options[OptGetModulator]) {
314 struct v4l2_modulator mt;
315
316 memset(&mt, 0, sizeof(struct v4l2_modulator));
317 modulator.index = tuner_index;
318 if (doioctl(fd, VIDIOC_G_MODULATOR, &mt) == 0) {
319 printf("Modulator %d:\n", modulator.index);
320 printf("\tName : %s\n", mt.name);
321 printf("\tType : %s\n", ttype2s(mt.type).c_str());
322 printf("\tCapabilities : %s\n", tcap2s(mt.capability).c_str());
323 if (mt.capability & V4L2_TUNER_CAP_LOW)
324 printf("\tFrequency range : %.1f MHz - %.1f MHz\n",
325 mt.rangelow / 16000.0, mt.rangehigh / 16000.0);
326 else if (mt.capability & V4L2_TUNER_CAP_1HZ)
327 printf("\tFrequency range : %.6f MHz - %.6f MHz\n",
328 mt.rangelow / 1000000.0, mt.rangehigh / 1000000.0);
329 else
330 printf("\tFrequency range : %.1f MHz - %.1f MHz\n",
331 mt.rangelow / 16.0, mt.rangehigh / 16.0);
332 printf("\tSubchannel modulation: %s\n",
333 txsubchans2s(mt.txsubchans).c_str());
334 }
335 }
336 }
337