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
* 10 + 1000.5;
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_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
);
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
)
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
))
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
))
147 tuner_set_freq(const struct tuner
*tuner
, long long int freq
,
150 long long int adj_freq
;
151 struct v4l2_frequency vf
;
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
)
160 fatal(0, "Frequency %.1f MHz out of range (%.1f - %.1f MHz)",
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
;
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
);
186 tuner_usleep(const struct tuner
*tuner
, int usecs
)
193 tuner_sleep(const struct tuner
*tuner
, int secs
)
200 query_control(const struct tuner
*tuner
, uint32_t id
,
201 struct v4l2_queryctrl
*qc
)
203 memset(qc
, 0, sizeof *qc
);
206 assert(id
== V4L2_CID_AUDIO_VOLUME
);
209 } else if (ioctl(tuner
->fd
, VIDIOC_QUERYCTRL
, qc
) == -1)
210 fatal(errno
, "VIDIOC_QUERYCTRL");
214 get_control(const struct tuner
*tuner
, uint32_t id
)
216 struct v4l2_control control
;
218 memset(&control
, 0, sizeof control
);
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
;
229 set_control(const struct tuner
*tuner
, uint32_t id
, int32_t value
)
231 struct v4l2_control control
;
233 memset(&control
, 0, sizeof control
);
235 control
.value
= value
;
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
;
245 } else if (ioctl(tuner
->fd
, VIDIOC_S_CTRL
, &control
) == -1)
246 fatal(errno
, "VIDIOC_S_CTRL");
250 query_tuner(const struct tuner
*tuner
, struct v4l2_tuner
*vt
)
252 memset(vt
, 0, sizeof *vt
);
253 vt
->index
= tuner
->index
;
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
262 } else if (ioctl(tuner
->fd
, VIDIOC_G_TUNER
, vt
) == -1)
263 fatal(errno
, "VIDIOC_G_TUNER");