• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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