Release fmtools 2.0.1.
[fmtools.git] / fmlib.c
blob60bb9d1c6f29246d0b629f3cde9d7203cfca9cfb
1 /* fmlib.c - simple V4L2 compatible tuner for radio cards
3 Copyright (C) 2009 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 static void
228 query_control(const struct tuner *tuner, uint32_t id,
229 struct v4l2_queryctrl *qc)
231 memset(qc, 0, sizeof *qc);
232 qc->id = id;
233 if (tuner->test) {
234 assert(id == V4L2_CID_AUDIO_VOLUME);
235 if (tuner->test->volume) {
236 qc->minimum = 1000;
237 qc->maximum = 2000;
239 } else if (ioctl(tuner->fd, VIDIOC_QUERYCTRL, qc) == -1)
240 fatal(errno, "VIDIOC_QUERYCTRL");
243 static int32_t
244 get_control(const struct tuner *tuner, uint32_t id)
246 struct v4l2_control control;
248 memset(&control, 0, sizeof control);
249 control.id = id;
250 if (tuner->test) {
251 if (id == V4L2_CID_AUDIO_VOLUME)
252 control.value = tuner->test->volume;
253 else if (id == V4L2_CID_AUDIO_MUTE)
254 control.value = tuner->test->mute;
255 else
256 abort();
257 } else if (ioctl(tuner->fd, VIDIOC_G_CTRL, &control) == -1)
258 fatal(errno, "VIDIOC_G_CTRL");
259 return control.value;
262 static void
263 set_control(const struct tuner *tuner, uint32_t id, int32_t value)
265 struct v4l2_control control;
267 memset(&control, 0, sizeof control);
268 control.id = id;
269 control.value = value;
270 if (tuner->test) {
271 if (id == V4L2_CID_AUDIO_MUTE) {
272 assert(value == 0 || value == 1);
273 tuner->test->mute = value;
274 } else if (id == V4L2_CID_AUDIO_VOLUME) {
275 assert(value >= 1000 && value <= 2000);
276 tuner->test->volume = value;
277 } else {
278 abort();
280 } else if (ioctl(tuner->fd, VIDIOC_S_CTRL, &control) == -1)
281 fatal(errno, "VIDIOC_S_CTRL");
284 static void
285 query_tuner(const struct tuner *tuner, struct v4l2_tuner *vt)
287 memset(vt, 0, sizeof *vt);
288 vt->index = tuner->index;
289 if (tuner->test) {
290 int freq = tuner->test->freq;
291 vt->rangelow = 16 * 89;
292 vt->rangehigh = 16 * 91;
293 vt->signal = (freq == (int) (16 * 89.6 + .5) ? 64000
294 : freq == (int) (16 * 90.4 + .5) ? 50000
295 : freq == (int) (16 * 90.5 + .5) ? 40000
296 : 1000);
297 } else if (ioctl(tuner->fd, VIDIOC_G_TUNER, vt) == -1)
298 fatal(errno, "VIDIOC_G_TUNER");