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)
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
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/>.
27 #include <sys/ioctl.h>
31 int freq
; /* In 1/16 MHz. */
32 int volume
; /* Between 1000 and 2000. */
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
*);
44 fatal(int error
, const char *msg
, ...)
48 fprintf(stderr
, "%s: ", program_name
);
51 vfprintf(stderr
, msg
, args
);
55 fprintf(stderr
, ": %s", strerror(error
));
64 void *p
= malloc(n
? n
: 1);
66 fatal(0, "out of memory");
71 tuner_open(struct tuner
*tuner
, const char *device
, int index
)
73 memset(tuner
, 0, sizeof *tuner
);
76 device
= "/dev/radio0";
77 else if (!strcmp(device
, "test") || !strncmp(device
, "test ", 5)) {
80 if (sscanf(device
, "test %lf", &volume
) != 1)
83 tuner
->test
= xmalloc(sizeof *tuner
->test
);
84 tuner
->test
->freq
= 90 * 16;
85 tuner
->test
->volume
= volume
>= 0 ? volume
* 10 + 1000.5 : 0;
90 tuner
->fd
= open(device
, O_RDONLY
);
92 fatal(errno
, "Unable to open %s", device
);
95 query_control(tuner
, V4L2_CID_AUDIO_VOLUME
, &tuner
->volume_ctrl
);
96 query_tuner(tuner
, &tuner
->tuner
);
100 tuner_close(struct tuner
*tuner
)
106 tuner_set_mute(struct tuner
*tuner
, bool mute
)
108 set_control(tuner
, V4L2_CID_AUDIO_MUTE
, mute
);
112 tuner_has_volume_control(const struct tuner
*tuner
)
114 const struct v4l2_queryctrl
*vqc
= &tuner
->volume_ctrl
;
115 return vqc
->maximum
> vqc
->minimum
;
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
);
125 * (volume
- vqc
->minimum
)
126 / (vqc
->maximum
- vqc
->minimum
));
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
)
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
))
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
))
162 tuner_set_freq(const struct tuner
*tuner
, long long int freq
,
165 long long int adj_freq
;
166 struct v4l2_frequency vf
;
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
)
175 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
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
;
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
);
201 tuner_usleep(const struct tuner
*tuner
, int usecs
)
208 tuner_sleep(const struct tuner
*tuner
, int secs
)
215 query_control(const struct tuner
*tuner
, uint32_t id
,
216 struct v4l2_queryctrl
*qc
)
218 memset(qc
, 0, sizeof *qc
);
221 assert(id
== V4L2_CID_AUDIO_VOLUME
);
222 if (tuner
->test
->volume
) {
226 } else if (ioctl(tuner
->fd
, VIDIOC_QUERYCTRL
, qc
) == -1)
227 fatal(errno
, "VIDIOC_QUERYCTRL");
231 get_control(const struct tuner
*tuner
, uint32_t id
)
233 struct v4l2_control control
;
235 memset(&control
, 0, sizeof control
);
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
;
246 set_control(const struct tuner
*tuner
, uint32_t id
, int32_t value
)
248 struct v4l2_control control
;
250 memset(&control
, 0, sizeof control
);
252 control
.value
= value
;
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
;
262 } else if (ioctl(tuner
->fd
, VIDIOC_S_CTRL
, &control
) == -1)
263 fatal(errno
, "VIDIOC_S_CTRL");
267 query_tuner(const struct tuner
*tuner
, struct v4l2_tuner
*vt
)
269 memset(vt
, 0, sizeof *vt
);
270 vt
->index
= tuner
->index
;
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
279 } else if (ioctl(tuner
->fd
, VIDIOC_G_TUNER
, vt
) == -1)
280 fatal(errno
, "VIDIOC_G_TUNER");