669cab3837abc39759e75699d7e848e04c04f2f3
[fmtools.git] / fmlib.c
blob669cab3837abc39759e75699d7e848e04c04f2f3
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 * 10 + 1000.5;
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 double
112 tuner_get_volume(const struct tuner *tuner)
114 const struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
115 int volume = get_control(tuner, V4L2_CID_AUDIO_VOLUME);
116 return 100.0 * (volume - vqc->minimum) / (vqc->maximum - vqc->minimum);
119 void
120 tuner_set_volume(struct tuner *tuner, double volume)
122 struct v4l2_queryctrl *vqc = &tuner->volume_ctrl;
123 set_control(tuner, V4L2_CID_AUDIO_VOLUME,
124 (volume / 100.0 * (vqc->maximum - vqc->minimum)
125 + vqc->minimum));
128 long long int
129 tuner_get_min_freq(const struct tuner *tuner)
131 long long int rangelow = tuner->tuner.rangelow;
132 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
133 rangelow *= 1000;
134 return rangelow;
137 long long int
138 tuner_get_max_freq(const struct tuner *tuner)
140 long long int rangehigh = tuner->tuner.rangehigh;
141 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
142 rangehigh *= 1000;
143 return rangehigh;
146 void
147 tuner_set_freq(const struct tuner *tuner, long long int freq,
148 bool override_range)
150 long long int adj_freq;
151 struct v4l2_frequency vf;
153 adj_freq = freq;
154 if (!(tuner->tuner.capability & V4L2_TUNER_CAP_LOW))
155 adj_freq = (adj_freq + 500) / 1000;
157 if ((adj_freq < tuner->tuner.rangelow
158 || adj_freq > tuner->tuner.rangehigh)
159 && !override_range)
160 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
161 freq / 16000.0,
162 tuner_get_min_freq(tuner) / 16000.0,
163 tuner_get_max_freq(tuner) / 16000.0);
165 memset(&vf, 0, sizeof vf);
166 vf.tuner = tuner->index;
167 vf.type = tuner->tuner.type;
168 vf.frequency = adj_freq;
169 if (tuner->test) {
170 tuner->test->freq = adj_freq;
172 else if (ioctl(tuner->fd, VIDIOC_S_FREQUENCY, &vf) == -1)
173 fatal(errno, "VIDIOC_S_FREQUENCY");
177 tuner_get_signal(const struct tuner *tuner)
179 struct v4l2_tuner vt;
181 query_tuner(tuner, &vt);
182 return vt.signal;
185 void
186 tuner_usleep(const struct tuner *tuner, int usecs)
188 if (!tuner->test)
189 usleep(usecs);
192 void
193 tuner_sleep(const struct tuner *tuner, int secs)
195 if (!tuner->test)
196 sleep(secs);
199 static void
200 query_control(const struct tuner *tuner, uint32_t id,
201 struct v4l2_queryctrl *qc)
203 memset(qc, 0, sizeof *qc);
204 qc->id = id;
205 if (tuner->test) {
206 assert(id == V4L2_CID_AUDIO_VOLUME);
207 qc->minimum = 1000;
208 qc->maximum = 2000;
209 } else if (ioctl(tuner->fd, VIDIOC_QUERYCTRL, qc) == -1)
210 fatal(errno, "VIDIOC_QUERYCTRL");
213 static int32_t
214 get_control(const struct tuner *tuner, uint32_t id)
216 struct v4l2_control control;
218 memset(&control, 0, sizeof control);
219 control.id = id;
220 if (tuner->test) {
221 assert(id == V4L2_CID_AUDIO_VOLUME);
222 control.value = tuner->test->volume;
223 } else if (ioctl(tuner->fd, VIDIOC_G_CTRL, &control) == -1)
224 fatal(errno, "VIDIOC_G_CTRL");
225 return control.value;
228 static void
229 set_control(const struct tuner *tuner, uint32_t id, int32_t value)
231 struct v4l2_control control;
233 memset(&control, 0, sizeof control);
234 control.id = id;
235 control.value = value;
236 if (tuner->test) {
237 if (id == V4L2_CID_AUDIO_MUTE)
238 assert(value == 0 || value == 1);
239 else if (id == V4L2_CID_AUDIO_VOLUME) {
240 assert(value >= 1000 && value <= 2000);
241 tuner->test->volume = value;
242 } else {
243 abort();
245 } else if (ioctl(tuner->fd, VIDIOC_S_CTRL, &control) == -1)
246 fatal(errno, "VIDIOC_S_CTRL");
249 static void
250 query_tuner(const struct tuner *tuner, struct v4l2_tuner *vt)
252 memset(vt, 0, sizeof *vt);
253 vt->index = tuner->index;
254 if (tuner->test) {
255 int freq = tuner->test->freq;
256 vt->rangelow = 16 * 89;
257 vt->rangehigh = 16 * 91;
258 vt->signal = (freq == (int) (16 * 89.6 + .5) ? 64000
259 : freq == (int) (16 * 90.4 + .5) ? 50000
260 : freq == (int) (16 * 90.5 + .5) ? 40000
261 : 1000);
262 } else if (ioctl(tuner->fd, VIDIOC_G_TUNER, vt) == -1)
263 fatal(errno, "VIDIOC_G_TUNER");