1 /* fmlib.c - simple V4L2 compatible tuner for radio cards
3 Copyright (C) 2009, 2012 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. */
33 bool mute
; /* Muted? */
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
*);
45 fatal(int error
, const char *msg
, ...)
49 fprintf(stderr
, "%s: ", program_name
);
52 vfprintf(stderr
, msg
, args
);
56 fprintf(stderr
, ": %s", strerror(error
));
65 void *p
= malloc(n
? n
: 1);
67 fatal(0, "out of memory");
72 tuner_open(struct tuner
*tuner
, const char *device
, int index
)
74 memset(tuner
, 0, sizeof *tuner
);
77 device
= "/dev/radio0";
78 else if (!strcmp(device
, "test") || !strncmp(device
, "test ", 5)) {
83 n
= sscanf(device
, "test %lf %d", &volume
, &mute
);
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;
97 tuner
->fd
= open(device
, O_RDONLY
);
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
);
107 tuner_close(struct tuner
*tuner
)
113 tuner_is_muted(const struct tuner
*tuner
)
115 return get_control(tuner
, V4L2_CID_AUDIO_MUTE
);
119 tuner_set_mute(struct tuner
*tuner
, bool mute
)
121 set_control(tuner
, V4L2_CID_AUDIO_MUTE
, mute
);
125 tuner_has_volume_control(const struct tuner
*tuner
)
127 const struct v4l2_queryctrl
*vqc
= &tuner
->volume_ctrl
;
128 return vqc
->maximum
> vqc
->minimum
;
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
);
138 * (volume
- vqc
->minimum
)
139 / (vqc
->maximum
- vqc
->minimum
));
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
)
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
))
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
))
175 tuner_set_freq(const struct tuner
*tuner
, long long int freq
,
178 long long int adj_freq
;
179 struct v4l2_frequency vf
;
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
)
188 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
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
;
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
);
214 tuner_usleep(const struct tuner
*tuner
, int usecs
)
221 tuner_sleep(const struct tuner
*tuner
, int secs
)
227 /* Queries 'tuner' for a property with the given 'id' and fills in 'qc' with
228 * the result. If 'tuner' doesn't have such a property, clears 'qc' to
231 query_control(const struct tuner
*tuner
, uint32_t id
,
232 struct v4l2_queryctrl
*qc
)
234 memset(qc
, 0, sizeof *qc
);
237 assert(id
== V4L2_CID_AUDIO_VOLUME
);
238 if (tuner
->test
->volume
) {
242 } else if (ioctl(tuner
->fd
, VIDIOC_QUERYCTRL
, qc
) != -1) {
244 } else if (errno
== ENOTTY
|| errno
== EINVAL
) {
245 /* This tuner doesn't support either query operation
246 * or the specific control ('id') respectively */
247 memset(qc
, 0, sizeof *qc
);
249 fatal(errno
, "VIDIOC_QUERYCTRL");
254 get_control(const struct tuner
*tuner
, uint32_t id
)
256 struct v4l2_control control
;
258 memset(&control
, 0, sizeof control
);
261 if (id
== V4L2_CID_AUDIO_VOLUME
)
262 control
.value
= tuner
->test
->volume
;
263 else if (id
== V4L2_CID_AUDIO_MUTE
)
264 control
.value
= tuner
->test
->mute
;
267 } else if (ioctl(tuner
->fd
, VIDIOC_G_CTRL
, &control
) == -1)
268 fatal(errno
, "VIDIOC_G_CTRL");
269 return control
.value
;
273 set_control(const struct tuner
*tuner
, uint32_t id
, int32_t value
)
275 struct v4l2_control control
;
277 memset(&control
, 0, sizeof control
);
279 control
.value
= value
;
281 if (id
== V4L2_CID_AUDIO_MUTE
) {
282 assert(value
== 0 || value
== 1);
283 tuner
->test
->mute
= value
;
284 } else if (id
== V4L2_CID_AUDIO_VOLUME
) {
285 assert(value
>= 1000 && value
<= 2000);
286 tuner
->test
->volume
= value
;
290 } else if (ioctl(tuner
->fd
, VIDIOC_S_CTRL
, &control
) == -1)
291 fatal(errno
, "VIDIOC_S_CTRL");
295 query_tuner(const struct tuner
*tuner
, struct v4l2_tuner
*vt
)
297 memset(vt
, 0, sizeof *vt
);
298 vt
->index
= tuner
->index
;
300 int freq
= tuner
->test
->freq
;
301 vt
->rangelow
= 16 * 89;
302 vt
->rangehigh
= 16 * 91;
303 vt
->signal
= (freq
== (int) (16 * 89.6 + .5) ? 64000
304 : freq
== (int) (16 * 90.4 + .5) ? 50000
305 : freq
== (int) (16 * 90.5 + .5) ? 40000
307 } else if (ioctl(tuner
->fd
, VIDIOC_G_TUNER
, vt
) == -1)
308 fatal(errno
, "VIDIOC_G_TUNER");