|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "v4lradiocontrol_maemo5.h" |
|
43 #include "v4lradioservice.h" |
|
44 |
|
45 #include "linux/videodev2.h" |
|
46 #include <linux/videodev.h> |
|
47 #include <sys/soundcard.h> |
|
48 #include <stdio.h> |
|
49 #include <sys/types.h> |
|
50 #include <sys/ioctl.h> |
|
51 #include <fcntl.h> |
|
52 #include <unistd.h> |
|
53 |
|
54 #define HEADPHONE_STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state" |
|
55 #define HEADPHONE_CONNECTED_STATE "connected" |
|
56 #define HEADPHONE_DISCONNECTED_STATE "disconnected" |
|
57 |
|
58 #define FMRXENABLER_DBUS_SERVICE "de.pycage.FMRXEnabler" |
|
59 #define FMRXENABLER_DBUS_OBJ_PATH "/de/pycage/FMRXEnabler" |
|
60 #define FMRXENABLER_DBUS_IFACE_NAME "de.pycage.FMRXEnabler" |
|
61 |
|
62 gboolean |
|
63 state_file_changed(GIOChannel* source, GIOCondition /*condition*/, gpointer data) |
|
64 { |
|
65 V4LRadioControl* radioControl = (V4LRadioControl*)data; |
|
66 gchar* result; |
|
67 |
|
68 g_io_channel_seek_position(source, 0, G_SEEK_SET, NULL); |
|
69 g_io_channel_read_line(source, &result, NULL, NULL, NULL); |
|
70 g_strstrip(result); |
|
71 |
|
72 if (g_ascii_strcasecmp(result, HEADPHONE_DISCONNECTED_STATE) == 0) { |
|
73 radioControl->enablePipeline(false); |
|
74 } else if (g_ascii_strcasecmp(result, HEADPHONE_CONNECTED_STATE) == 0) { |
|
75 // Wait 400ms until audio is routed again to headphone to prevent sound coming from speakers |
|
76 QTimer::singleShot(400,radioControl,SLOT(enablePipeline())); |
|
77 } |
|
78 |
|
79 #ifdef MULTIMEDIA_MAEMO_DEBUG |
|
80 qDebug() << "Headphone is now" << result; |
|
81 #endif |
|
82 |
|
83 g_free(result); |
|
84 return true; |
|
85 } |
|
86 |
|
87 V4LRadioControl::V4LRadioControl(QObject *parent) |
|
88 : QRadioTunerControl(parent) |
|
89 , fd(1) |
|
90 , m_error(false) |
|
91 , muted(false) |
|
92 , stereo(false) |
|
93 , step(100000) |
|
94 , sig(0) |
|
95 , scanning(false) |
|
96 , currentBand(QRadioTuner::FM) |
|
97 , pipeline(0) |
|
98 { |
|
99 if (QDBusConnection::systemBus().isConnected()) { |
|
100 FMRXEnablerIFace = new QDBusInterface(FMRXENABLER_DBUS_SERVICE, |
|
101 FMRXENABLER_DBUS_OBJ_PATH, |
|
102 FMRXENABLER_DBUS_IFACE_NAME, |
|
103 QDBusConnection::systemBus()); |
|
104 } |
|
105 |
|
106 createGstPipeline(); |
|
107 |
|
108 GIOChannel* headphoneStateFile = NULL; |
|
109 headphoneStateFile = g_io_channel_new_file(HEADPHONE_STATE_FILE, "r", NULL); |
|
110 if (headphoneStateFile != NULL) { |
|
111 g_io_add_watch(headphoneStateFile, G_IO_PRI, state_file_changed, this); |
|
112 } else { |
|
113 #ifdef MULTIMEDIA_MAEMO_DEBUG |
|
114 qWarning() << QString("File %1 can't be read!").arg(HEADPHONE_STATE_FILE) ; |
|
115 qWarning() << "Monitoring headphone state isn't possible!"; |
|
116 #endif |
|
117 } |
|
118 |
|
119 enableFMRX(); |
|
120 initRadio(); |
|
121 setupHeadPhone(); |
|
122 |
|
123 setMuted(false); |
|
124 |
|
125 timer = new QTimer(this); |
|
126 timer->setInterval(200); |
|
127 connect(timer,SIGNAL(timeout()),this,SLOT(search())); |
|
128 |
|
129 tickTimer = new QTimer(this); |
|
130 tickTimer->setInterval(10000); |
|
131 connect(tickTimer,SIGNAL(timeout()),this,SLOT(enableFMRX())); |
|
132 tickTimer->start(); |
|
133 } |
|
134 |
|
135 V4LRadioControl::~V4LRadioControl() |
|
136 { |
|
137 if (pipeline) |
|
138 { |
|
139 gst_element_set_state (pipeline, GST_STATE_NULL); |
|
140 gst_object_unref (GST_OBJECT (pipeline)); |
|
141 } |
|
142 |
|
143 if(fd > 0) |
|
144 ::close(fd); |
|
145 } |
|
146 |
|
147 void V4LRadioControl::enablePipeline(bool enable) |
|
148 { |
|
149 if (enable == true) |
|
150 gst_element_set_state (pipeline, GST_STATE_PLAYING); |
|
151 else |
|
152 gst_element_set_state (pipeline, GST_STATE_NULL); |
|
153 } |
|
154 |
|
155 void V4LRadioControl::enableFMRX() |
|
156 { |
|
157 if (FMRXEnablerIFace && FMRXEnablerIFace->isValid()) { |
|
158 FMRXEnablerIFace->call("request"); // Return value ignored |
|
159 } |
|
160 } |
|
161 |
|
162 // Workaround to capture sound from the PGA line and play it back using gstreamer |
|
163 // because N900 doesn't output sound directly to speakers |
|
164 bool V4LRadioControl::createGstPipeline() |
|
165 { |
|
166 GstElement *source, *sink; |
|
167 |
|
168 gst_init (NULL, NULL); |
|
169 pipeline = gst_pipeline_new("my-pipeline"); |
|
170 |
|
171 source = gst_element_factory_make ("pulsesrc", "source"); |
|
172 sink = gst_element_factory_make ("pulsesink", "sink"); |
|
173 |
|
174 gst_bin_add_many (GST_BIN (pipeline), source, sink, (char *)NULL); |
|
175 |
|
176 if (!gst_element_link_many (source, sink, (char *)NULL)) { |
|
177 return false; |
|
178 } |
|
179 |
|
180 gst_element_set_state (pipeline, GST_STATE_PLAYING); |
|
181 |
|
182 return true; |
|
183 } |
|
184 |
|
185 bool V4LRadioControl::isAvailable() const |
|
186 { |
|
187 return available; |
|
188 } |
|
189 |
|
190 QtMultimediaKit::AvailabilityError V4LRadioControl::availabilityError() const |
|
191 { |
|
192 if (fd > 0) |
|
193 return QtMultimediaKit::NoError; |
|
194 else |
|
195 return QtMultimediaKit::ResourceError; |
|
196 } |
|
197 |
|
198 QRadioTuner::State V4LRadioControl::state() const |
|
199 { |
|
200 return fd > 0 ? QRadioTuner::ActiveState : QRadioTuner::StoppedState; |
|
201 } |
|
202 |
|
203 QRadioTuner::Band V4LRadioControl::band() const |
|
204 { |
|
205 return currentBand; |
|
206 } |
|
207 |
|
208 bool V4LRadioControl::isBandSupported(QRadioTuner::Band b) const |
|
209 { |
|
210 QRadioTuner::Band bnd = (QRadioTuner::Band)b; |
|
211 switch(bnd) { |
|
212 case QRadioTuner::FM: |
|
213 if(freqMin <= 87500000 && freqMax >= 108000000) |
|
214 return true; |
|
215 break; |
|
216 case QRadioTuner::LW: |
|
217 if(freqMin <= 148500 && freqMax >= 283500) |
|
218 return true; |
|
219 case QRadioTuner::AM: |
|
220 if(freqMin <= 520000 && freqMax >= 1610000) |
|
221 return true; |
|
222 default: |
|
223 if(freqMin <= 1711000 && freqMax >= 30000000) |
|
224 return true; |
|
225 } |
|
226 |
|
227 return false; |
|
228 } |
|
229 |
|
230 void V4LRadioControl::setBand(QRadioTuner::Band b) |
|
231 { |
|
232 if(freqMin <= 87500000 && freqMax >= 108000000 && b == QRadioTuner::FM) { |
|
233 // FM 87.5 to 108.0 MHz, except Japan 76-90 MHz |
|
234 currentBand = (QRadioTuner::Band)b; |
|
235 step = 100000; // 100kHz steps |
|
236 emit bandChanged(currentBand); |
|
237 |
|
238 } else if(freqMin <= 148500 && freqMax >= 283500 && b == QRadioTuner::LW) { |
|
239 // LW 148.5 to 283.5 kHz, 9kHz channel spacing (Europe, Africa, Asia) |
|
240 currentBand = (QRadioTuner::Band)b; |
|
241 step = 1000; // 1kHz steps |
|
242 emit bandChanged(currentBand); |
|
243 |
|
244 } else if(freqMin <= 520000 && freqMax >= 1610000 && b == QRadioTuner::AM) { |
|
245 // AM 520 to 1610 kHz, 9 or 10kHz channel spacing, extended 1610 to 1710 kHz |
|
246 currentBand = (QRadioTuner::Band)b; |
|
247 step = 1000; // 1kHz steps |
|
248 emit bandChanged(currentBand); |
|
249 |
|
250 } else if(freqMin <= 1711000 && freqMax >= 30000000 && b == QRadioTuner::SW) { |
|
251 // SW 1.711 to 30.0 MHz, divided into 15 bands. 5kHz channel spacing |
|
252 currentBand = (QRadioTuner::Band)b; |
|
253 step = 500; // 500Hz steps |
|
254 emit bandChanged(currentBand); |
|
255 } |
|
256 } |
|
257 |
|
258 int V4LRadioControl::frequency() const |
|
259 { |
|
260 return currentFreq; |
|
261 } |
|
262 |
|
263 int V4LRadioControl::frequencyStep(QRadioTuner::Band b) const |
|
264 { |
|
265 int step = 0; |
|
266 |
|
267 if(b == QRadioTuner::FM) |
|
268 step = 100000; // 100kHz steps |
|
269 else if(b == QRadioTuner::LW) |
|
270 step = 1000; // 1kHz steps |
|
271 else if(b == QRadioTuner::AM) |
|
272 step = 1000; // 1kHz steps |
|
273 else if(b == QRadioTuner::SW) |
|
274 step = 500; // 500Hz steps |
|
275 |
|
276 return step; |
|
277 } |
|
278 |
|
279 QPair<int,int> V4LRadioControl::frequencyRange(QRadioTuner::Band b) const |
|
280 { |
|
281 if(b == QRadioTuner::AM) |
|
282 return qMakePair<int,int>(520000,1710000); |
|
283 else if(b == QRadioTuner::FM) |
|
284 return qMakePair<int,int>(87500000,108000000); |
|
285 else if(b == QRadioTuner::SW) |
|
286 return qMakePair<int,int>(1711111,30000000); |
|
287 else if(b == QRadioTuner::LW) |
|
288 return qMakePair<int,int>(148500,283500); |
|
289 |
|
290 return qMakePair<int,int>(0,0); |
|
291 } |
|
292 |
|
293 void V4LRadioControl::setFrequency(int frequency) |
|
294 { |
|
295 qint64 f = frequency; |
|
296 |
|
297 v4l2_frequency freq; |
|
298 |
|
299 if(frequency < freqMin) |
|
300 f = freqMax; |
|
301 if(frequency > freqMax) |
|
302 f = freqMin; |
|
303 |
|
304 if(fd > 0) { |
|
305 memset(&freq, 0, sizeof(freq)); |
|
306 // Use the first tuner |
|
307 freq.tuner = 0; |
|
308 if (::ioctl(fd, VIDIOC_G_FREQUENCY, &freq) >= 0) { |
|
309 if(low) { |
|
310 // For low, freq in units of 62.5Hz, so convert from Hz to units. |
|
311 freq.frequency = (int)(f/62.5); |
|
312 } else { |
|
313 // For high, freq in units of 62.5kHz, so convert from Hz to units. |
|
314 freq.frequency = (int)(f/62500); |
|
315 } |
|
316 ::ioctl(fd, VIDIOC_S_FREQUENCY, &freq); |
|
317 currentFreq = f; |
|
318 emit frequencyChanged(currentFreq); |
|
319 |
|
320 int signal = signalStrength(); |
|
321 if(sig != signal) { |
|
322 sig = signal; |
|
323 emit signalStrengthChanged(sig); |
|
324 } |
|
325 } |
|
326 } |
|
327 } |
|
328 |
|
329 bool V4LRadioControl::isStereo() const |
|
330 { |
|
331 return stereo; |
|
332 } |
|
333 |
|
334 QRadioTuner::StereoMode V4LRadioControl::stereoMode() const |
|
335 { |
|
336 return QRadioTuner::Auto; |
|
337 } |
|
338 |
|
339 void V4LRadioControl::setStereoMode(QRadioTuner::StereoMode mode) |
|
340 { |
|
341 bool stereo = true; |
|
342 |
|
343 if(mode == QRadioTuner::ForceMono) |
|
344 stereo = false; |
|
345 |
|
346 v4l2_tuner tuner; |
|
347 |
|
348 memset(&tuner, 0, sizeof(tuner)); |
|
349 |
|
350 if (ioctl(fd, VIDIOC_G_TUNER, &tuner) >= 0) { |
|
351 if(stereo) |
|
352 tuner.audmode = V4L2_TUNER_MODE_STEREO; |
|
353 else |
|
354 tuner.audmode = V4L2_TUNER_MODE_MONO; |
|
355 |
|
356 if (ioctl(fd, VIDIOC_S_TUNER, &tuner) >= 0) { |
|
357 emit stereoStatusChanged(stereo); |
|
358 } |
|
359 } |
|
360 } |
|
361 |
|
362 int V4LRadioControl::signalStrength() const |
|
363 { |
|
364 v4l2_tuner tuner; |
|
365 |
|
366 tuner.index = 0; |
|
367 if (ioctl(fd, VIDIOC_G_TUNER, &tuner) < 0 || tuner.type != V4L2_TUNER_RADIO) |
|
368 return 0; |
|
369 return tuner.signal*100/65535; |
|
370 } |
|
371 |
|
372 int V4LRadioControl::vol(snd_hctl_elem_t *elem) const |
|
373 { |
|
374 const QString card("hw:0"); |
|
375 int err; |
|
376 snd_ctl_elem_id_t *id; |
|
377 snd_ctl_elem_info_t *info; |
|
378 snd_ctl_elem_value_t *control; |
|
379 snd_ctl_elem_id_alloca(&id); |
|
380 snd_ctl_elem_info_alloca(&info); |
|
381 snd_ctl_elem_value_alloca(&control); |
|
382 if ((err = snd_hctl_elem_info(elem, info)) < 0) { |
|
383 return 0; |
|
384 } |
|
385 |
|
386 snd_hctl_elem_get_id(elem, id); |
|
387 snd_hctl_elem_read(elem, control); |
|
388 |
|
389 return snd_ctl_elem_value_get_integer(control, 0); |
|
390 } |
|
391 |
|
392 int V4LRadioControl::volume() const |
|
393 { |
|
394 const QString ctlName("Line DAC Playback Volume"); |
|
395 const QString card("hw:0"); |
|
396 |
|
397 int volume = 0; |
|
398 int err; |
|
399 static snd_ctl_t *handle = NULL; |
|
400 snd_ctl_elem_info_t *info; |
|
401 snd_ctl_elem_id_t *id; |
|
402 snd_ctl_elem_value_t *control; |
|
403 |
|
404 snd_ctl_elem_info_alloca(&info); |
|
405 snd_ctl_elem_id_alloca(&id); |
|
406 snd_ctl_elem_value_alloca(&control); |
|
407 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* MIXER */ |
|
408 |
|
409 snd_ctl_elem_id_set_name(id, ctlName.toAscii()); |
|
410 |
|
411 if ((err = snd_ctl_open(&handle, card.toAscii(), 0)) < 0) { |
|
412 return 0; |
|
413 } |
|
414 |
|
415 snd_ctl_elem_info_set_id(info, id); |
|
416 if ((err = snd_ctl_elem_info(handle, info)) < 0) { |
|
417 snd_ctl_close(handle); |
|
418 handle = NULL; |
|
419 return 0; |
|
420 } |
|
421 |
|
422 snd_ctl_elem_info_get_id(info, id); /* FIXME: Remove it when hctl find works ok !!! */ |
|
423 snd_ctl_elem_value_set_id(control, id); |
|
424 |
|
425 snd_ctl_close(handle); |
|
426 handle = NULL; |
|
427 |
|
428 snd_hctl_t *hctl; |
|
429 snd_hctl_elem_t *elem; |
|
430 if ((err = snd_hctl_open(&hctl, card.toAscii(), 0)) < 0) { |
|
431 return 0; |
|
432 } |
|
433 if ((err = snd_hctl_load(hctl)) < 0) { |
|
434 return 0; |
|
435 } |
|
436 elem = snd_hctl_find_elem(hctl, id); |
|
437 if (elem) |
|
438 volume = vol(elem); |
|
439 |
|
440 snd_hctl_close(hctl); |
|
441 |
|
442 return (volume/118.0) * 100; |
|
443 } |
|
444 |
|
445 void V4LRadioControl::setVolume(int volume) |
|
446 { |
|
447 int vol = (volume / 100.0) * 118; // 118 is a headphone max setting |
|
448 callAmixer("Line DAC Playback Volume", QString().setNum(vol)+QString(",")+QString().setNum(vol)); |
|
449 } |
|
450 |
|
451 bool V4LRadioControl::isMuted() const |
|
452 { |
|
453 return muted; |
|
454 } |
|
455 |
|
456 void V4LRadioControl::setMuted(bool muted) |
|
457 { |
|
458 struct v4l2_control vctrl; |
|
459 vctrl.id = V4L2_CID_AUDIO_MUTE; |
|
460 vctrl.value = muted ? 1 : 0; |
|
461 ioctl(fd, VIDIOC_S_CTRL, &vctrl); |
|
462 } |
|
463 |
|
464 void V4LRadioControl::setupHeadPhone() |
|
465 { |
|
466 QMap<QString, QString> settings; |
|
467 |
|
468 settings["Jack Function"] = "Headset"; |
|
469 settings["Left DAC_L1 Mixer HP Switch"] = "off"; |
|
470 settings["Right DAC_R1 Mixer HP Switch"] = "off"; |
|
471 settings["Line DAC Playback Switch"] = "on"; |
|
472 settings["Line DAC Playback Volume"] = "118,118"; // Volume is set to 100% |
|
473 settings["HPCOM DAC Playback Switch"] = "off"; |
|
474 settings["Left DAC_L1 Mixer HP Switch"] = "off"; |
|
475 settings["Left DAC_L1 Mixer Line Switch"] = "on"; |
|
476 settings["Right DAC_R1 Mixer HP Switch"] = "off"; |
|
477 settings["Right DAC_R1 Mixer Line Switch"] = "on"; |
|
478 settings["Speaker Function"] = "Off"; |
|
479 |
|
480 settings["Input Select"] = "ADC"; |
|
481 settings["Left PGA Mixer Line1L Switch"] = "off"; |
|
482 settings["Right PGA Mixer Line1L Switch"] = "off"; |
|
483 settings["Right PGA Mixer Line1R Switch"] = "off"; |
|
484 settings["PGA Capture Volume"] = "0,0"; |
|
485 |
|
486 settings["PGA Capture Switch"] = "on"; |
|
487 |
|
488 settings["Left PGA Mixer Line2L Switch"] = "on"; |
|
489 settings["Right PGA Mixer Line2R Switch"] = "on"; |
|
490 |
|
491 QMapIterator<QString, QString> i(settings); |
|
492 while (i.hasNext()) { |
|
493 i.next(); |
|
494 callAmixer(i.key(), i.value()); |
|
495 } |
|
496 } |
|
497 |
|
498 void V4LRadioControl::callAmixer(const QString& target, const QString& value) |
|
499 { |
|
500 int err; |
|
501 long tmp; |
|
502 unsigned int count; |
|
503 static snd_ctl_t *handle = NULL; |
|
504 QString card("hw:0"); |
|
505 snd_ctl_elem_info_t *info; |
|
506 snd_ctl_elem_id_t *id; |
|
507 snd_ctl_elem_value_t *control; |
|
508 snd_ctl_elem_type_t type; |
|
509 |
|
510 snd_ctl_elem_info_alloca(&info); |
|
511 snd_ctl_elem_id_alloca(&id); |
|
512 snd_ctl_elem_value_alloca(&control); |
|
513 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* MIXER */ |
|
514 |
|
515 // in amixer parse func |
|
516 // char target[64]; |
|
517 // e.g. PGA CAPTure Switch |
|
518 snd_ctl_elem_id_set_name(id, target.toAscii()); |
|
519 |
|
520 if (handle == NULL && (err = snd_ctl_open(&handle, card.toAscii(), 0)) < 0) |
|
521 { |
|
522 return; |
|
523 } |
|
524 |
|
525 snd_ctl_elem_info_set_id(info, id); |
|
526 if ((err = snd_ctl_elem_info(handle, info)) < 0) |
|
527 { |
|
528 snd_ctl_close(handle); |
|
529 handle = NULL; |
|
530 return; |
|
531 } |
|
532 |
|
533 snd_ctl_elem_info_get_id(info, id); /* FIXME: Remove it when hctl find works ok !!! */ |
|
534 type = snd_ctl_elem_info_get_type(info); |
|
535 count = snd_ctl_elem_info_get_count(info); |
|
536 |
|
537 snd_ctl_elem_value_set_id(control, id); |
|
538 |
|
539 tmp = 0; |
|
540 for (uint idx = 0; idx < count && idx < 128; idx++) |
|
541 { |
|
542 switch (type) |
|
543 { |
|
544 case SND_CTL_ELEM_TYPE_BOOLEAN: |
|
545 #ifdef MULTIMEDIA_MAEMO_DEBUG |
|
546 qDebug() << "SND_CTL_ELEM_TYPE_BOOLEAN" << SND_CTL_ELEM_TYPE_BOOLEAN; |
|
547 #endif |
|
548 if ((value == "on") ||(value == "1")) |
|
549 { |
|
550 tmp = 1; |
|
551 } |
|
552 snd_ctl_elem_value_set_boolean(control, idx, tmp); |
|
553 break; |
|
554 case SND_CTL_ELEM_TYPE_ENUMERATED: |
|
555 tmp = getEnumItemIndex(handle, info, value); |
|
556 snd_ctl_elem_value_set_enumerated(control, idx, tmp); |
|
557 break; |
|
558 case SND_CTL_ELEM_TYPE_INTEGER: |
|
559 #ifdef MULTIMEDIA_MAEMO_DEBUG |
|
560 qDebug() << "SND_CTL_ELEM_TYPE_INTEGER" << SND_CTL_ELEM_TYPE_INTEGER; |
|
561 #endif |
|
562 tmp = atoi(value.toAscii()); |
|
563 if (tmp < snd_ctl_elem_info_get_min(info)) |
|
564 tmp = snd_ctl_elem_info_get_min(info); |
|
565 else if (tmp > snd_ctl_elem_info_get_max(info)) |
|
566 tmp = snd_ctl_elem_info_get_max(info); |
|
567 snd_ctl_elem_value_set_integer(control, idx, tmp); |
|
568 break; |
|
569 default: |
|
570 break; |
|
571 |
|
572 } |
|
573 } |
|
574 |
|
575 if ((err = snd_ctl_elem_write(handle, control)) < 0) { |
|
576 snd_ctl_close(handle); |
|
577 handle = NULL; |
|
578 return; |
|
579 } |
|
580 |
|
581 snd_ctl_close(handle); |
|
582 handle = NULL; |
|
583 } |
|
584 |
|
585 |
|
586 int V4LRadioControl::getEnumItemIndex(snd_ctl_t *handle, snd_ctl_elem_info_t *info, |
|
587 const QString &value) |
|
588 { |
|
589 int items, i; |
|
590 |
|
591 items = snd_ctl_elem_info_get_items(info); |
|
592 if (items <= 0) |
|
593 return -1; |
|
594 |
|
595 for (i = 0; i < items; i++) |
|
596 { |
|
597 snd_ctl_elem_info_set_item(info, i); |
|
598 if (snd_ctl_elem_info(handle, info) < 0) |
|
599 return -1; |
|
600 QString name = snd_ctl_elem_info_get_item_name(info); |
|
601 if(name == value) |
|
602 { |
|
603 return i; |
|
604 } |
|
605 } |
|
606 return -1; |
|
607 } |
|
608 |
|
609 bool V4LRadioControl::isSearching() const |
|
610 { |
|
611 return scanning; |
|
612 } |
|
613 |
|
614 void V4LRadioControl::cancelSearch() |
|
615 { |
|
616 scanning = false; |
|
617 timer->stop(); |
|
618 } |
|
619 |
|
620 void V4LRadioControl::searchForward() |
|
621 { |
|
622 // Scan up |
|
623 if(scanning) { |
|
624 cancelSearch(); |
|
625 return; |
|
626 } |
|
627 scanning = true; |
|
628 forward = true; |
|
629 timer->start(); |
|
630 } |
|
631 |
|
632 void V4LRadioControl::searchBackward() |
|
633 { |
|
634 // Scan down |
|
635 if(scanning) { |
|
636 cancelSearch(); |
|
637 return; |
|
638 } |
|
639 scanning = true; |
|
640 forward = false; |
|
641 timer->start(); |
|
642 } |
|
643 |
|
644 void V4LRadioControl::start() |
|
645 { |
|
646 } |
|
647 |
|
648 void V4LRadioControl::stop() |
|
649 { |
|
650 } |
|
651 |
|
652 QRadioTuner::Error V4LRadioControl::error() const |
|
653 { |
|
654 if(m_error) |
|
655 return QRadioTuner::OpenError; |
|
656 |
|
657 return QRadioTuner::NoError; |
|
658 } |
|
659 |
|
660 QString V4LRadioControl::errorString() const |
|
661 { |
|
662 return QString(); |
|
663 } |
|
664 |
|
665 void V4LRadioControl::search() |
|
666 { |
|
667 if(!scanning) return; |
|
668 |
|
669 if(forward) { |
|
670 setFrequency(currentFreq+step); |
|
671 } else { |
|
672 setFrequency(currentFreq-step); |
|
673 } |
|
674 |
|
675 int signal = signalStrength(); |
|
676 if(sig != signal) { |
|
677 sig = signal; |
|
678 emit signalStrengthChanged(sig); |
|
679 } |
|
680 |
|
681 if (signal > 25) { |
|
682 cancelSearch(); |
|
683 return; |
|
684 } |
|
685 } |
|
686 |
|
687 bool V4LRadioControl::initRadio() |
|
688 { |
|
689 v4l2_tuner tuner; |
|
690 v4l2_frequency freq; |
|
691 v4l2_capability cap; |
|
692 |
|
693 low = false; |
|
694 available = false; |
|
695 freqMin = freqMax = currentFreq = 0; |
|
696 |
|
697 fd = ::open("/dev/radio1", O_RDWR); |
|
698 |
|
699 if(fd != -1) { |
|
700 // Capabilites |
|
701 memset(&cap, 0, sizeof(cap)); |
|
702 if(::ioctl(fd, VIDIOC_QUERYCAP, &cap ) >= 0) { |
|
703 available = true; |
|
704 } |
|
705 |
|
706 tuner.index = 0; |
|
707 |
|
708 if (ioctl(fd, VIDIOC_G_TUNER, &tuner) < 0) { |
|
709 return false; |
|
710 } |
|
711 |
|
712 if (tuner.type != V4L2_TUNER_RADIO) |
|
713 return false; |
|
714 |
|
715 if ((tuner.capability & V4L2_TUNER_CAP_LOW) != 0) { |
|
716 // Units are 1/16th of a kHz. |
|
717 low = true; |
|
718 } |
|
719 |
|
720 if(low) { |
|
721 freqMin = tuner.rangelow * 62.5; |
|
722 freqMax = tuner.rangehigh * 62.5; |
|
723 } else { |
|
724 freqMin = tuner.rangelow * 62500; |
|
725 freqMax = tuner.rangehigh * 62500; |
|
726 } |
|
727 |
|
728 // frequency |
|
729 memset(&freq, 0, sizeof(freq)); |
|
730 |
|
731 if(::ioctl(fd, VIDIOC_G_FREQUENCY, &freq) >= 0) { |
|
732 if (((int)freq.frequency) != -1) { // -1 means not set. |
|
733 if(low) |
|
734 currentFreq = freq.frequency * 62.5; |
|
735 else |
|
736 currentFreq = freq.frequency * 62500; |
|
737 } |
|
738 } |
|
739 |
|
740 // stereo |
|
741 bool stereo = false; |
|
742 memset(&tuner, 0, sizeof(tuner)); |
|
743 if (ioctl(fd, VIDIOC_G_TUNER, &tuner) >= 0) { |
|
744 if((tuner.rxsubchans & V4L2_TUNER_SUB_STEREO) != 0) |
|
745 stereo = true; |
|
746 } |
|
747 |
|
748 return true; |
|
749 } |
|
750 |
|
751 m_error = true; |
|
752 emit error(); |
|
753 |
|
754 return false; |
|
755 } |