Add precision to printed frequency values and update tests to match
[fmtools.git] / fmlib.c
blobba185a33353af9beaccc0e93b3233c9262584af1
1 /* fmlib.c - simple V4L2 compatible tuner for radio cards
3 Copyright (C) 2009, 2012 Ben Pfaff <blp@cs.stanford.edu>
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the Free
7 Software Foundation; either version 2 of the License, or (at your option)
8 any later version.
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 more details.
15 You should have received a copy of the GNU General Public License along with
16 this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "fmlib.h"
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/ioctl.h>
28 #include <unistd.h>
30 struct tuner_test {
31 int freq; /* In 1/16 MHz. */
32 int volume; /* Between 1000 and 2000. */
33 bool mute; /* Muted? */
36 char *program_name;
38 static void query_control(const struct tuner *, uint32_t id,
39 struct v4l2_queryctrl *);
40 static int32_t get_control(const struct tuner *, uint32_t id);
41 static void set_control(const struct tuner *, uint32_t id, int32_t value);
42 static void query_tuner(const struct tuner *, struct v4l2_tuner *);
44 void
45 fatal(int error, const char *msg, ...)
47 va_list args;
49 fprintf(stderr, "%s: ", program_name);
51 va_start(args, msg);
52 vfprintf(stderr, msg, args);
53 va_end(args);
55 if (error)
56 fprintf(stderr, ": %s", strerror(error));
57 putc('\n', stderr);
59 exit(EXIT_FAILURE);
62 static void *
63 xmalloc(size_t n)
65 void *p = malloc(n ? n : 1);
66 if (!p)
67 fatal(0, "out of memory");
68 return p;
71 void
72 tuner_open(struct tuner *tuner, const char *device, int index)
74 memset(tuner, 0, sizeof *tuner);
76 if (!device)
77 device = "/dev/radio0";
78 else if (!strcmp(device, "test") || !strncmp(device, "test ", 5)) {
79 double volume;
80 int mute;
81 int n;
83 n = sscanf(device, "test %lf %d", &volume, &mute);
84 if (n < 1)
85 volume = 50;
86 if (n < 2)
87 mute = 0;
89 tuner->test = xmalloc(sizeof *tuner->test);
90 tuner->test->freq = 90 * 16;
91 tuner->test->volume = volume >= 0 ? volume * 10 + 1000.5 : 0;
92 tuner->test->mute = mute != 0;
94 device = "/dev/null";
97 tuner->fd = open(device, O_RDONLY);
98 if (tuner->fd < 0)
99 fatal(errno, "Unable to open %s", device);
100 tuner->index = index;
102 query_control(tuner, V4L2_CID_AUDIO_VOLUME, &tuner->volume_ctrl);
103 query_tuner(tuner, &tuner->tuner);
106 void
107 tuner_close(struct tuner *tuner)
109 close(tuner->fd);
112 bool
113 tuner_is_muted(const struct tuner *tuner)
115 return get_control(tuner, V4L2_CID_AUDIO_MUTE);
118 void
119 tuner_set_mute(struct tuner *tuner, bool mute)
121 set_control(tuner, V4L2_CID_AUDIO_MUTE, mute);
124 bool
125 tuner_has_volume_control(const struct tuner *tuner)
127 const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
128 return vqc->maximum > vqc->minimum;
131 double
132 tuner_get_volume(const struct tuner *tuner)
134 if (tuner_has_volume_control(tuner)) {
135 const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
136 int volume = get_control(tuner, V4L2_CID_AUDIO_VOLUME);
137 return (100.0
138 * (volume - vqc->minimum)
139 / (vqc->maximum - vqc->minimum));
140 } else {
141 return 100.0;
145 void
146 tuner_set_volume(struct tuner *tuner, double volume)
148 if (tuner_has_volume_control(tuner)) {
149 struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
150 set_control(tuner, V4L2_CID_AUDIO_VOLUME,
151 (volume / 100.0 * (vqc->maximum - vqc->minimum)
152 + vqc->minimum));
156 long long int
157 tuner_get_min_freq(const struct tuner *tuner)
159 long long int rangelow = tuner->tuner.rangelow;
160 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
161 rangelow *= 1000;
162 return rangelow;
165 long long int
166 tuner_get_max_freq(const struct tuner *tuner)
168 long long int rangehigh = tuner->tuner.rangehigh;
169 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
170 rangehigh *= 1000;
171 return rangehigh;
174 void
175 tuner_set_freq(const struct tuner *tuner, long long int freq,
176 bool override_range)
178 long long int adj_freq;
179 struct v4l2_frequency vf;
181 adj_freq = freq;
182 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
183 adj_freq = (adj_freq + 500) / 1000;
185 if ((adj_freq < tuner->tuner.rangelow
186 || adj_freq > tuner->tuner.rangehigh)
187 && !override_range)
188 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
189 freq / 16000.0,
190 tuner_get_min_freq(tuner) / 16000.0,
191 tuner_get_max_freq(tuner) / 16000.0);
193 memset(&vf, 0, sizeof vf);
194 vf.tuner = tuner->index;
195 vf.type = tuner->tuner.type;
196 vf.frequency = adj_freq;
197 if (tuner->test) {
198 tuner->test->freq = adj_freq;
200 else if (ioctl(tuner->fd, VIDIOC_S_FREQUENCY, &vf) == -1)
201 fatal(errno, "VIDIOC_S_FREQUENCY");
205 tuner_get_signal(const struct tuner *tuner)
207 struct v4l2_tuner vt;
209 query_tuner(tuner, &vt);
210 return vt.signal;
213 void
214 tuner_usleep(const struct tuner *tuner, int usecs)
216 if (!tuner->test)
217 usleep(usecs);
220 void
221 tuner_sleep(const struct tuner *tuner, int secs)
223 if (!tuner->test)
224 sleep(secs);
227 /* Queries 'tuner' for a property with the given 'id' and fills in 'qc' with
228 * the result. If 'tuner' doesn't have such a property, clears 'qc' to
229 * all-zeros. */
230 static void
231 query_control(const struct tuner *tuner, uint32_t id,
232 struct v4l2_queryctrl *qc)
234 memset(qc, 0, sizeof *qc);
235 qc->id = id;
236 if (tuner->test) {
237 assert(id == V4L2_CID_AUDIO_VOLUME);
238 if (tuner->test->volume) {
239 qc->minimum = 1000;
240 qc->maximum = 2000;
242 } else if (ioctl(tuner->fd, VIDIOC_QUERYCTRL, qc) != -1) {
243 /* Success. */
244 } else if (errno == ENOTTY) {
245 /* This tuner doesn't support 'id'. */
246 memset(qc, 0, sizeof *qc);
247 } else {
248 fatal(errno, "VIDIOC_QUERYCTRL");
252 static int32_t
253 get_control(const struct tuner *tuner, uint32_t id)
255 struct v4l2_control control;
257 memset(&control, 0, sizeof control);
258 control.id = id;
259 if (tuner->test) {
260 if (id == V4L2_CID_AUDIO_VOLUME)
261 control.value = tuner->test->volume;
262 else if (id == V4L2_CID_AUDIO_MUTE)
263 control.value = tuner->test->mute;
264 else
265 abort();
266 } else if (ioctl(tuner->fd, VIDIOC_G_CTRL, &control) == -1)
267 fatal(errno, "VIDIOC_G_CTRL");
268 return control.value;
271 static void
272 set_control(const struct tuner *tuner, uint32_t id, int32_t value)
274 struct v4l2_control control;
276 memset(&control, 0, sizeof control);
277 control.id = id;
278 control.value = value;
279 if (tuner->test) {
280 if (id == V4L2_CID_AUDIO_MUTE) {
281 assert(value == 0 || value == 1);
282 tuner->test->mute = value;
283 } else if (id == V4L2_CID_AUDIO_VOLUME) {
284 assert(value >= 1000 && value <= 2000);
285 tuner->test->volume = value;
286 } else {
287 abort();
289 } else if (ioctl(tuner->fd, VIDIOC_S_CTRL, &control) == -1)
290 fatal(errno, "VIDIOC_S_CTRL");
293 static void
294 query_tuner(const struct tuner *tuner, struct v4l2_tuner *vt)
296 memset(vt, 0, sizeof *vt);
297 vt->index = tuner->index;
298 if (tuner->test) {
299 int freq = tuner->test->freq;
300 vt->rangelow = 16 * 89;
301 vt->rangehigh = 16 * 91;
302 vt->signal = (freq == (int) (16 * 89.6 + .5) ? 64000
303 : freq == (int) (16 * 90.4 + .5) ? 50000
304 : freq == (int) (16 * 90.5 + .5) ? 40000
305 : 1000);
306 } else if (ioctl(tuner->fd, VIDIOC_G_TUNER, vt) == -1)
307 fatal(errno, "VIDIOC_G_TUNER");