1d0922a80986ffa9eaa715403280fc6155d438ca
[fmtools.git] / fmlib.c
blob1d0922a80986ffa9eaa715403280fc6155d438ca
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. */
35 char *program_name;
37 static void query_control(const struct tuner *, uint32_t id,
38 struct v4l2_queryctrl *);
39 static int32_t get_control(const struct tuner *, uint32_t id);
40 static void set_control(const struct tuner *, uint32_t id, int32_t value);
41 static void query_tuner(const struct tuner *, struct v4l2_tuner *);
43 void
44 fatal(int error, const char *msg, ...)
46 va_list args;
48 fprintf(stderr, "%s: ", program_name);
50 va_start(args, msg);
51 vfprintf(stderr, msg, args);
52 va_end(args);
54 if (error)
55 fprintf(stderr, ": %s", strerror(error));
56 putc('\n', stderr);
58 exit(EXIT_FAILURE);
61 static void *
62 xmalloc(size_t n)
64 void *p = malloc(n ? n : 1);
65 if (!p)
66 fatal(0, "out of memory");
67 return p;
70 void
71 tuner_open(struct tuner *tuner, const char *device, int index)
73 memset(tuner, 0, sizeof *tuner);
75 if (!device)
76 device = "/dev/radio0";
77 else if (!strcmp(device, "test") || !strncmp(device, "test ", 5)) {
78 double volume;
80 if (sscanf(device, "test %lf", &volume) != 1)
81 volume = 50;
83 tuner->test = xmalloc(sizeof *tuner->test);
84 tuner->test->freq = 90 * 16;
85 tuner->test->volume = volume >= 0 ? volume * 10 + 1000.5 : 0;
87 device = "/dev/null";
90 tuner->fd = open(device, O_RDONLY);
91 if (tuner->fd < 0)
92 fatal(errno, "Unable to open %s", device);
93 tuner->index = index;
95 query_control(tuner, V4L2_CID_AUDIO_VOLUME, &tuner->volume_ctrl);
96 query_tuner(tuner, &tuner->tuner);
99 void
100 tuner_close(struct tuner *tuner)
102 close(tuner->fd);
105 void
106 tuner_set_mute(struct tuner *tuner, bool mute)
108 set_control(tuner, V4L2_CID_AUDIO_MUTE, mute);
111 bool
112 tuner_has_volume_control(const struct tuner *tuner)
114 const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
115 return vqc->maximum > vqc->minimum;
118 double
119 tuner_get_volume(const struct tuner *tuner)
121 if (tuner_has_volume_control(tuner)) {
122 const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
123 int volume = get_control(tuner, V4L2_CID_AUDIO_VOLUME);
124 return (100.0
125 * (volume - vqc->minimum)
126 / (vqc->maximum - vqc->minimum));
127 } else {
128 return 100.0;
132 void
133 tuner_set_volume(struct tuner *tuner, double volume)
135 if (tuner_has_volume_control(tuner)) {
136 struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
137 set_control(tuner, V4L2_CID_AUDIO_VOLUME,
138 (volume / 100.0 * (vqc->maximum - vqc->minimum)
139 + vqc->minimum));
143 long long int
144 tuner_get_min_freq(const struct tuner *tuner)
146 long long int rangelow = tuner->tuner.rangelow;
147 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
148 rangelow *= 1000;
149 return rangelow;
152 long long int
153 tuner_get_max_freq(const struct tuner *tuner)
155 long long int rangehigh = tuner->tuner.rangehigh;
156 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
157 rangehigh *= 1000;
158 return rangehigh;
161 void
162 tuner_set_freq(const struct tuner *tuner, long long int freq,
163 bool override_range)
165 long long int adj_freq;
166 struct v4l2_frequency vf;
168 adj_freq = freq;
169 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
170 adj_freq = (adj_freq + 500) / 1000;
172 if ((adj_freq < tuner->tuner.rangelow
173 || adj_freq > tuner->tuner.rangehigh)
174 && !override_range)
175 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
176 freq / 16000.0,
177 tuner_get_min_freq(tuner) / 16000.0,
178 tuner_get_max_freq(tuner) / 16000.0);
180 memset(&vf, 0, sizeof vf);
181 vf.tuner = tuner->index;
182 vf.type = tuner->tuner.type;
183 vf.frequency = adj_freq;
184 if (tuner->test) {
185 tuner->test->freq = adj_freq;
187 else if (ioctl(tuner->fd, VIDIOC_S_FREQUENCY, &vf) == -1)
188 fatal(errno, "VIDIOC_S_FREQUENCY");
192 tuner_get_signal(const struct tuner *tuner)
194 struct v4l2_tuner vt;
196 query_tuner(tuner, &vt);
197 return vt.signal;
200 void
201 tuner_usleep(const struct tuner *tuner, int usecs)
203 if (!tuner->test)
204 usleep(usecs);
207 void
208 tuner_sleep(const struct tuner *tuner, int secs)
210 if (!tuner->test)
211 sleep(secs);
214 static void
215 query_control(const struct tuner *tuner, uint32_t id,
216 struct v4l2_queryctrl *qc)
218 memset(qc, 0, sizeof *qc);
219 qc->id = id;
220 if (tuner->test) {
221 assert(id == V4L2_CID_AUDIO_VOLUME);
222 if (tuner->test->volume) {
223 qc->minimum = 1000;
224 qc->maximum = 2000;
226 } else if (ioctl(tuner->fd, VIDIOC_QUERYCTRL, qc) == -1)
227 fatal(errno, "VIDIOC_QUERYCTRL");
230 static int32_t
231 get_control(const struct tuner *tuner, uint32_t id)
233 struct v4l2_control control;
235 memset(&control, 0, sizeof control);
236 control.id = id;
237 if (tuner->test) {
238 assert(id == V4L2_CID_AUDIO_VOLUME);
239 control.value = tuner->test->volume;
240 } else if (ioctl(tuner->fd, VIDIOC_G_CTRL, &control) == -1)
241 fatal(errno, "VIDIOC_G_CTRL");
242 return control.value;
245 static void
246 set_control(const struct tuner *tuner, uint32_t id, int32_t value)
248 struct v4l2_control control;
250 memset(&control, 0, sizeof control);
251 control.id = id;
252 control.value = value;
253 if (tuner->test) {
254 if (id == V4L2_CID_AUDIO_MUTE)
255 assert(value == 0 || value == 1);
256 else if (id == V4L2_CID_AUDIO_VOLUME) {
257 assert(value >= 1000 && value <= 2000);
258 tuner->test->volume = value;
259 } else {
260 abort();
262 } else if (ioctl(tuner->fd, VIDIOC_S_CTRL, &control) == -1)
263 fatal(errno, "VIDIOC_S_CTRL");
266 static void
267 query_tuner(const struct tuner *tuner, struct v4l2_tuner *vt)
269 memset(vt, 0, sizeof *vt);
270 vt->index = tuner->index;
271 if (tuner->test) {
272 int freq = tuner->test->freq;
273 vt->rangelow = 16 * 89;
274 vt->rangehigh = 16 * 91;
275 vt->signal = (freq == (int) (16 * 89.6 + .5) ? 64000
276 : freq == (int) (16 * 90.4 + .5) ? 50000
277 : freq == (int) (16 * 90.5 + .5) ? 40000
278 : 1000);
279 } else if (ioctl(tuner->fd, VIDIOC_G_TUNER, vt) == -1)
280 fatal(errno, "VIDIOC_G_TUNER");