No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2007 James Henstridge <james@jamesh.id.au>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 #include <config.h>
30
31 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <string.h>
37
38 #include <linux/videodev2.h>
39
40 #include <glib.h>
41
42 #include "rb-debug.h"
43 #include "rb-radio-tuner.h"
44
45 struct _RBRadioTunerPrivate {
46 int fd;
47 guint32 range_low;
48 guint32 range_high;
49 guint32 current_frequency;
50 guint32 freq_mul;
51 };
52
53 G_DEFINE_DYNAMIC_TYPE (RBRadioTuner, rb_radio_tuner, G_TYPE_OBJECT);
54
55 static void rb_radio_tuner_finalize (GObject *object);
56
57 static void
58 rb_radio_tuner_class_init (RBRadioTunerClass *class)
59 {
60 GObjectClass *object_class = G_OBJECT_CLASS (class);
61
62 object_class->finalize = rb_radio_tuner_finalize;
63
64 g_type_class_add_private (class, sizeof (RBRadioTunerPrivate));
65 }
66
67 static void
68 rb_radio_tuner_class_finalize (RBRadioTunerClass *class)
69 {
70 }
71
72 static void
73 rb_radio_tuner_init (RBRadioTuner *self)
74 {
75 self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RB_TYPE_RADIO_TUNER,
76 RBRadioTunerPrivate);
77 self->priv->fd = -1;
78 }
79
80 static void
81 rb_radio_tuner_finalize (GObject *object)
82 {
83 RBRadioTuner *self = (RBRadioTuner *)object;
84
85 g_free (self->card_name);
86 self->card_name = NULL;
87 if (self->priv->fd >= 0)
88 close (self->priv->fd);
89 self->priv->fd = -1;
90
91 G_OBJECT_CLASS (rb_radio_tuner_parent_class)->finalize (object);
92 }
93
94 RBRadioTuner *
95 rb_radio_tuner_new (const gchar *devname, GError **err)
96 {
97 int fd = -1;
98 struct v4l2_capability caps;
99 struct v4l2_tuner tuner;
100 RBRadioTuner *self;
101
102 if (devname == NULL)
103 devname = "/dev/radio0";
104
105 fd = open(devname, O_RDONLY);
106 if (fd < 0) {
107 g_warning("Could not open device %s", devname);
108 goto err;
109 }
110 memset (&caps, 0, sizeof (caps));
111 if (ioctl (fd, VIDIOC_QUERYCAP, &caps) < 0) {
112 g_warning("Could not query device capabilities: %s",
113 g_strerror(errno));
114 goto err;
115 }
116 if ((caps.capabilities & V4L2_CAP_TUNER) == 0) {
117 g_warning("Device is not a tuner");
118 goto err;
119 }
120
121 /* check the tuner */
122 memset(&tuner, 0, sizeof(tuner));
123 tuner.index = 0;
124 if (ioctl(fd, VIDIOC_G_TUNER, &tuner) < 0) {
125 g_warning("Could not query tuner info: %s", g_strerror(errno));
126 goto err;
127 }
128 if (tuner.type != V4L2_TUNER_RADIO) {
129 g_warning("Device is not a radio tuner");
130 goto err;
131 }
132
133
134 self = RB_RADIO_TUNER (g_object_new (RB_TYPE_RADIO_TUNER, NULL));
135
136 /* fill in information */
137 self->priv->fd = fd;
138 self->card_name = g_strndup((const char *)caps.card,
139 sizeof(caps.card));
140 self->priv->range_low = tuner.rangelow;
141 self->priv->range_high = tuner.rangehigh;
142 if ((tuner.capability & V4L2_TUNER_CAP_LOW) != 0)
143 self->priv->freq_mul = 16000;
144 else
145 self->priv->freq_mul = 16;
146
147 self->min_freq = (double)self->priv->range_low / self->priv->freq_mul;
148 self->max_freq = (double)self->priv->range_high / self->priv->freq_mul;
149
150 rb_radio_tuner_update (self);
151 return self;
152
153 err:
154 if (fd >= 0)
155 close (fd);
156 return NULL;
157 }
158
159 void
160 rb_radio_tuner_update (RBRadioTuner *self)
161 {
162 struct v4l2_tuner tuner;
163 struct v4l2_control control;
164 struct v4l2_frequency frequency;
165 gboolean has_changed = FALSE;
166
167 memset (&tuner, 0, sizeof (tuner));
168 tuner.index = 0;
169 if (ioctl (self->priv->fd, VIDIOC_G_TUNER, &tuner) >= 0) {
170 if (self->is_stereo != (tuner.audmode==V4L2_TUNER_MODE_STEREO))
171 has_changed = TRUE;
172 self->is_stereo = (tuner.audmode==V4L2_TUNER_MODE_STEREO);
173
174 if (self->signal != tuner.signal)
175 has_changed = TRUE;
176 self->signal = tuner.signal;
177 }
178
179 memset (&control, 0, sizeof (control));
180 control.id = V4L2_CID_AUDIO_MUTE;
181 if (ioctl (self->priv->fd, VIDIOC_G_CTRL, &control) >= 0) {
182 control.value = !!control.value;
183 if (self->is_muted != control.value)
184 has_changed = TRUE;
185 self->is_muted = control.value;
186 }
187
188 memset (&frequency, 0, sizeof (frequency));
189 frequency.tuner = 0;
190 if (ioctl (self->priv->fd, VIDIOC_G_FREQUENCY, &frequency) >= 0) {
191 if (self->priv->current_frequency != frequency.frequency)
192 has_changed = TRUE;
193 self->priv->current_frequency = frequency.frequency;
194 self->frequency = (double)frequency.frequency
195 / self->priv->freq_mul;
196 }
197
198 rb_debug ("Tuner %s", has_changed ? "has changed" : "has not changed");
199
200 #if 0
201 if (has_changed) {
202 g_signal_emit (self, CHANGED);
203 }
204 #endif
205 }
206
207 gboolean
208 rb_radio_tuner_set_frequency (RBRadioTuner *self, double frequency)
209 {
210 struct v4l2_frequency freq;
211 guint new_freq;
212
213 new_freq = frequency * self->priv->freq_mul;
214 new_freq = CLAMP (new_freq,
215 self->priv->range_low, self->priv->range_high);
216
217 memset (&freq, 0, sizeof (freq));
218 freq.tuner = 0;
219 freq.type = V4L2_TUNER_RADIO;
220 freq.frequency = new_freq;
221 return ioctl (self->priv->fd, VIDIOC_S_FREQUENCY, &freq) >= 0;
222 }
223
224 gboolean
225 rb_radio_tuner_set_mute (RBRadioTuner *self, gboolean mute)
226 {
227 struct v4l2_control control;
228
229 memset(&control, 0, sizeof(control));
230 control.id = V4L2_CID_AUDIO_MUTE;
231 control.value = !!mute;
232 return ioctl (self->priv->fd, VIDIOC_S_CTRL, &control) >= 0;
233 }
234
235 void
236 _rb_radio_tuner_register_type (GTypeModule *module)
237 {
238 rb_radio_tuner_register_type (module);
239 }