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
)
74 device
= "/dev/radio0";
75 else if (!strcmp(device
, "test") || !strncmp(device
, "test ", 5)) {
78 if (sscanf(device
, "test %lf", &volume
) != 1)
81 tuner
->test
= xmalloc(sizeof *tuner
->test
);
82 tuner
->test
->freq
= 90 * 16;
83 tuner
->test
->volume
= volume
* 10 + 1000.5;
88 tuner
->fd
= open(device
, O_RDONLY
);
90 fatal(errno
, "Unable to open %s", device
);
93 query_control(tuner
, V4L2_CID_AUDIO_VOLUME
, &tuner
->volume_ctrl
);
94 query_tuner(tuner
, &tuner
->tuner
);
98 tuner_close(struct tuner
*tuner
)
104 tuner_set_mute(struct tuner
*tuner
, bool mute
)
106 set_control(tuner
, V4L2_CID_AUDIO_MUTE
, mute
);
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
);
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
)
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
))
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
))
145 tuner_set_freq(const struct tuner
*tuner
, long long int freq
,
148 long long int adj_freq
;
149 struct v4l2_frequency vf
;
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
)
158 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
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
;
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
);
184 tuner_usleep(const struct tuner
*tuner
, int usecs
)
191 tuner_sleep(const struct tuner
*tuner
, int secs
)
198 query_control(const struct tuner
*tuner
, uint32_t id
,
199 struct v4l2_queryctrl
*qc
)
201 memset(qc
, 0, sizeof *qc
);
204 assert(id
== V4L2_CID_AUDIO_VOLUME
);
207 } else if (ioctl(tuner
->fd
, VIDIOC_QUERYCTRL
, qc
) == -1)
208 fatal(errno
, "VIDIOC_QUERYCTRL");
212 get_control(const struct tuner
*tuner
, uint32_t id
)
214 struct v4l2_control control
;
216 memset(&control
, 0, sizeof control
);
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
;
227 set_control(const struct tuner
*tuner
, uint32_t id
, int32_t value
)
229 struct v4l2_control control
;
231 memset(&control
, 0, sizeof control
);
233 control
.value
= value
;
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
;
243 } else if (ioctl(tuner
->fd
, VIDIOC_S_CTRL
, &control
) == -1)
244 fatal(errno
, "VIDIOC_S_CTRL");
248 query_tuner(const struct tuner
*tuner
, struct v4l2_tuner
*vt
)
250 memset(vt
, 0, sizeof *vt
);
251 vt
->index
= tuner
->index
;
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
260 } else if (ioctl(tuner
->fd
, VIDIOC_G_TUNER
, vt
) == -1)
261 fatal(errno
, "VIDIOC_G_TUNER");