Rewrite to use video4linux2 API.
[fmtools.git] / fmlib.c
blob7cc56bd030bc2175a36753a8bd6f8e3633ccaab6
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 if (!device)
74 device = "/dev/radio0";
75 else if (!strcmp(device, "test") || !strncmp(device, "test ", 5)) {
76 double volume;
78 if (sscanf(device, "test %lf", &volume) != 1)
79 volume = 50;
81 tuner->test = xmalloc(sizeof *tuner->test);
82 tuner->test->freq = 90 * 16;
83 tuner->test->volume = volume * 10 + 1000.5;
85 device = "/dev/null";
88 tuner->fd = open(device, O_RDONLY);
89 if (tuner->fd < 0)
90 fatal(errno, "Unable to open %s", device);
91 tuner->index = index;
93 query_control(tuner, V4L2_CID_AUDIO_VOLUME, &tuner->volume_ctrl);
94 query_tuner(tuner, &tuner->tuner);
97 void
98 tuner_close(struct tuner *tuner)
100 close(tuner->fd);
103 void
104 tuner_set_mute(struct tuner *tuner, bool mute)
106 set_control(tuner, V4L2_CID_AUDIO_MUTE, mute);
109 double
110 tuner_get_volume(const struct tuner *tuner)
112 const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
113 int volume = get_control(tuner, V4L2_CID_AUDIO_VOLUME);
114 return 100.0 * (volume - vqc->minimum) / (vqc->maximum - vqc->minimum);
117 void
118 tuner_set_volume(struct tuner *tuner, double volume)
120 struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
121 set_control(tuner, V4L2_CID_AUDIO_VOLUME,
122 (volume / 100.0 * (vqc->maximum - vqc->minimum)
123 + vqc->minimum));
126 long long int
127 tuner_get_min_freq(const struct tuner *tuner)
129 long long int rangelow = tuner->tuner.rangelow;
130 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
131 rangelow *= 1000;
132 return rangelow;
135 long long int
136 tuner_get_max_freq(const struct tuner *tuner)
138 long long int rangehigh = tuner->tuner.rangehigh;
139 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
140 rangehigh *= 1000;
141 return rangehigh;
144 void
145 tuner_set_freq(const struct tuner *tuner, long long int freq,
146 bool override_range)
148 long long int adj_freq;
149 struct v4l2_frequency vf;
151 adj_freq = freq;
152 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
153 adj_freq = (adj_freq + 500) / 1000;
155 if ((adj_freq < tuner->tuner.rangelow
156 || adj_freq > tuner->tuner.rangehigh)
157 && !override_range)
158 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
159 freq / 16000.0,
160 tuner_get_min_freq(tuner) / 16000.0,
161 tuner_get_max_freq(tuner) / 16000.0);
163 memset(&vf, 0, sizeof vf);
164 vf.tuner = tuner->index;
165 vf.type = tuner->tuner.type;
166 vf.frequency = adj_freq;
167 if (tuner->test) {
168 tuner->test->freq = adj_freq;
170 else if (ioctl(tuner->fd, VIDIOC_S_FREQUENCY, &vf) == -1)
171 fatal(errno, "VIDIOC_S_FREQUENCY");
175 tuner_get_signal(const struct tuner *tuner)
177 struct v4l2_tuner vt;
179 query_tuner(tuner, &vt);
180 return vt.signal;
183 void
184 tuner_usleep(const struct tuner *tuner, int usecs)
186 if (!tuner->test)
187 usleep(usecs);
190 void
191 tuner_sleep(const struct tuner *tuner, int secs)
193 if (!tuner->test)
194 sleep(secs);
197 static void
198 query_control(const struct tuner *tuner, uint32_t id,
199 struct v4l2_queryctrl *qc)
201 memset(qc, 0, sizeof *qc);
202 qc->id = id;
203 if (tuner->test) {
204 assert(id == V4L2_CID_AUDIO_VOLUME);
205 qc->minimum = 1000;
206 qc->maximum = 2000;
207 } else if (ioctl(tuner->fd, VIDIOC_QUERYCTRL, qc) == -1)
208 fatal(errno, "VIDIOC_QUERYCTRL");
211 static int32_t
212 get_control(const struct tuner *tuner, uint32_t id)
214 struct v4l2_control control;
216 memset(&control, 0, sizeof control);
217 control.id = id;
218 if (tuner->test) {
219 assert(id == V4L2_CID_AUDIO_VOLUME);
220 control.value = tuner->test->volume;
221 } else if (ioctl(tuner->fd, VIDIOC_G_CTRL, &control) == -1)
222 fatal(errno, "VIDIOC_G_CTRL");
223 return control.value;
226 static void
227 set_control(const struct tuner *tuner, uint32_t id, int32_t value)
229 struct v4l2_control control;
231 memset(&control, 0, sizeof control);
232 control.id = id;
233 control.value = value;
234 if (tuner->test) {
235 if (id == V4L2_CID_AUDIO_MUTE)
236 assert(value == 0 || value == 1);
237 else if (id == V4L2_CID_AUDIO_VOLUME) {
238 assert(value >= 1000 && value <= 2000);
239 tuner->test->volume = value;
240 } else {
241 abort();
243 } else if (ioctl(tuner->fd, VIDIOC_S_CTRL, &control) == -1)
244 fatal(errno, "VIDIOC_S_CTRL");
247 static void
248 query_tuner(const struct tuner *tuner, struct v4l2_tuner *vt)
250 memset(vt, 0, sizeof *vt);
251 vt->index = tuner->index;
252 if (tuner->test) {
253 int freq = tuner->test->freq;
254 vt->rangelow = 16 * 89;
255 vt->rangehigh = 16 * 91;
256 vt->signal = (freq == (int) (16 * 89.6 + .5) ? 64000
257 : freq == (int) (16 * 90.4 + .5) ? 50000
258 : freq == (int) (16 * 90.5 + .5) ? 40000
259 : 1000);
260 } else if (ioctl(tuner->fd, VIDIOC_G_TUNER, vt) == -1)
261 fatal(errno, "VIDIOC_G_TUNER");