1 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 #include "nsThreadUtils.h"
19 #include "mozilla/FileUtils.h"
21 #include <cutils/properties.h>
24 #include <linux/videodev2.h>
28 #include <sys/types.h>
31 /* Bionic might not have the newer version of the v4l2 headers that
32 * define these controls, so we define them here if they're not found.
34 #ifndef V4L2_CTRL_CLASS_FM_RX
35 #define V4L2_CTRL_CLASS_FM_RX 0x00a10000
36 #define V4L2_CID_FM_RX_CLASS_BASE (V4L2_CTRL_CLASS_FM_RX | 0x900)
37 #define V4L2_CID_TUNE_DEEMPHASIS (V4L2_CID_FM_RX_CLASS_BASE + 1)
38 #define V4L2_DEEMPHASIS_DISABLED 0
39 #define V4L2_DEEMPHASIS_50_uS 1
40 #define V4L2_DEEMPHASIS_75_uS 2
41 #define V4L2_CID_RDS_RECEPTION (V4L2_CID_FM_RX_CLASS_BASE + 2)
47 uint32_t GetFMRadioFrequency();
50 static bool sRadioEnabled
;
51 static pthread_t sRadioThread
;
52 static hal::FMRadioSettings sRadioSettings
;
53 static int sMsmFMVersion
;
54 static bool sMsmFMMode
;
57 setControl(uint32_t id
, int32_t value
)
59 struct v4l2_control control
;
61 control
.value
= value
;
62 return ioctl(sRadioFD
, VIDIOC_S_CTRL
, &control
);
65 class RadioUpdate
: public nsRunnable
{
66 hal::FMRadioOperation mOp
;
67 hal::FMRadioOperationStatus mStatus
;
69 RadioUpdate(hal::FMRadioOperation op
, hal::FMRadioOperationStatus status
)
75 hal::FMRadioOperationInformation info
;
76 info
.operation() = mOp
;
77 info
.status() = mStatus
;
78 info
.frequency() = GetFMRadioFrequency();
79 hal::NotifyFMRadioStatus(info
);
84 /* Runs on the radio thread */
86 initMsmFMRadio(hal::FMRadioSettings
&aInfo
)
88 mozilla::ScopedClose
fd(sRadioFD
);
91 snprintf(version
, sizeof(version
), "%d", sMsmFMVersion
);
92 property_set("hw.fm.version", version
);
94 /* Set the mode for soc downloader */
95 property_set("hw.fm.mode", "normal");
96 /* start fm_dl service */
97 property_set("ctl.start", "fm_dl");
100 * Fix bug 800263. Wait until the FM radio chips initialization is done
101 * then set other properties, or the system will hang and reboot. This
102 * work around is from codeaurora
103 * (git://codeaurora.org/platform/frameworks/base.git).
105 for (int i
= 0; i
< 4; ++i
) {
107 char value
[PROPERTY_VALUE_MAX
];
108 property_get("hw.fm.init", value
, "0");
109 if (!strcmp(value
, "1")) {
114 rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_STATE
, FM_RECV
);
116 HAL_LOG(("Unable to turn on radio |%s|", strerror(errno
)));
120 int preEmphasis
= aInfo
.preEmphasis() <= 50;
121 rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_EMPHASIS
, preEmphasis
);
123 HAL_LOG(("Unable to configure preemphasis"));
127 rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_RDS_STD
, 0);
129 HAL_LOG(("Unable to configure RDS"));
134 switch (aInfo
.spaceType()) {
136 spacing
= FM_CH_SPACE_50KHZ
;
139 spacing
= FM_CH_SPACE_100KHZ
;
142 spacing
= FM_CH_SPACE_200KHZ
;
145 HAL_LOG(("Unsupported space value - %d", aInfo
.spaceType()));
149 rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_SPACING
, spacing
);
151 HAL_LOG(("Unable to configure spacing"));
156 * Frequency conversions
158 * HAL uses units of 1k for frequencies
159 * V4L2 uses units of 62.5kHz
160 * Multiplying by (10000 / 625) converts from HAL units to V4L2.
163 struct v4l2_tuner tuner
= {0};
164 tuner
.rangelow
= (aInfo
.lowerLimit() * 10000) / 625;
165 tuner
.rangehigh
= (aInfo
.upperLimit() * 10000) / 625;
166 tuner
.audmode
= V4L2_TUNER_MODE_STEREO
;
167 rc
= ioctl(fd
, VIDIOC_S_TUNER
, &tuner
);
169 HAL_LOG(("Unable to adjust band limits"));
173 rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_REGION
, TAVARUA_REGION_OTHER
);
175 HAL_LOG(("Unable to configure region"));
179 // Some devices do not support analog audio routing. This should be
180 // indicated by the 'ro.moz.fm.noAnalog' property at build time.
181 char propval
[PROPERTY_VALUE_MAX
];
182 property_get("ro.moz.fm.noAnalog", propval
, "");
183 bool noAnalog
= !strcmp(propval
, "true");
185 rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH
,
186 noAnalog
? FM_DIGITAL_PATH
: FM_ANALOG_PATH
);
188 HAL_LOG(("Unable to set audio path"));
193 /* Set the mode for soc downloader */
194 property_set("hw.fm.mode", "config_dac");
195 /* Use analog mode FM */
196 property_set("hw.fm.isAnalog", "true");
197 /* start fm_dl service */
198 property_set("ctl.start", "fm_dl");
200 for (int i
= 0; i
< 4; ++i
) {
202 char value
[PROPERTY_VALUE_MAX
];
203 property_get("hw.fm.init", value
, "0");
204 if (!strcmp(value
, "1")) {
211 sRadioEnabled
= true;
214 /* Runs on the radio thread */
216 runMsmFMRadio(void *)
218 initMsmFMRadio(sRadioSettings
);
219 if (!sRadioEnabled
) {
220 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE
,
221 hal::FM_RADIO_OPERATION_STATUS_FAIL
));
226 struct v4l2_buffer buffer
= {0};
228 buffer
.type
= V4L2_BUF_TYPE_PRIVATE
;
229 buffer
.length
= sizeof(buf
);
230 buffer
.m
.userptr
= (long unsigned int)buf
;
232 while (sRadioEnabled
) {
233 if (ioctl(sRadioFD
, VIDIOC_DQBUF
, &buffer
) < 0) {
239 /* The tavarua driver reports a number of things asynchronously.
240 * In those cases, the status update comes from this thread. */
241 for (unsigned int i
= 0; i
< buffer
.bytesused
; i
++) {
243 case TAVARUA_EVT_RADIO_READY
:
244 // The driver sends RADIO_READY both when we turn the radio on and when we turn
247 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE
,
248 hal::FM_RADIO_OPERATION_STATUS_SUCCESS
));
252 case TAVARUA_EVT_SEEK_COMPLETE
:
253 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK
,
254 hal::FM_RADIO_OPERATION_STATUS_SUCCESS
));
256 case TAVARUA_EVT_TUNE_SUCC
:
257 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_TUNE
,
258 hal::FM_RADIO_OPERATION_STATUS_SUCCESS
));
269 /* This runs on the main thread but most of the
270 * initialization is pushed to the radio thread. */
272 EnableFMRadio(const hal::FMRadioSettings
& aInfo
)
275 HAL_LOG(("Radio already enabled!"));
279 hal::FMRadioOperationInformation info
;
280 info
.operation() = hal::FM_RADIO_OPERATION_ENABLE
;
281 info
.status() = hal::FM_RADIO_OPERATION_STATUS_FAIL
;
283 mozilla::ScopedClose
fd(open("/dev/radio0", O_RDWR
));
285 HAL_LOG(("Unable to open radio device"));
286 hal::NotifyFMRadioStatus(info
);
290 struct v4l2_capability cap
;
291 int rc
= ioctl(fd
, VIDIOC_QUERYCAP
, &cap
);
293 HAL_LOG(("Unable to query radio device"));
294 hal::NotifyFMRadioStatus(info
);
298 sMsmFMMode
= !strcmp((char *)cap
.driver
, "radio-tavarua") ||
299 !strcmp((char *)cap
.driver
, "radio-iris");
300 HAL_LOG(("Radio: %s (%s)\n", cap
.driver
, cap
.card
));
302 if (!(cap
.capabilities
& V4L2_CAP_RADIO
)) {
303 HAL_LOG(("/dev/radio0 isn't a radio"));
304 hal::NotifyFMRadioStatus(info
);
308 if (!(cap
.capabilities
& V4L2_CAP_TUNER
)) {
309 HAL_LOG(("/dev/radio0 doesn't support the tuner interface"));
310 hal::NotifyFMRadioStatus(info
);
313 sRadioSettings
= aInfo
;
316 sRadioFD
= fd
.forget();
317 sMsmFMVersion
= cap
.version
;
318 if (pthread_create(&sRadioThread
, nullptr, runMsmFMRadio
, nullptr)) {
319 HAL_LOG(("Couldn't create radio thread"));
320 hal::NotifyFMRadioStatus(info
);
325 struct v4l2_tuner tuner
= {0};
326 tuner
.type
= V4L2_TUNER_RADIO
;
327 tuner
.rangelow
= (aInfo
.lowerLimit() * 10000) / 625;
328 tuner
.rangehigh
= (aInfo
.upperLimit() * 10000) / 625;
329 tuner
.audmode
= V4L2_TUNER_MODE_STEREO
;
330 rc
= ioctl(fd
, VIDIOC_S_TUNER
, &tuner
);
332 HAL_LOG(("Unable to adjust band limits"));
336 switch (aInfo
.preEmphasis()) {
338 emphasis
= V4L2_DEEMPHASIS_DISABLED
;
341 emphasis
= V4L2_DEEMPHASIS_50_uS
;
344 emphasis
= V4L2_DEEMPHASIS_75_uS
;
347 MOZ_CRASH("Invalid preemphasis setting");
350 rc
= setControl(V4L2_CID_TUNE_DEEMPHASIS
, emphasis
);
352 HAL_LOG(("Unable to configure deemphasis"));
355 sRadioFD
= fd
.forget();
356 sRadioEnabled
= true;
358 info
.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS
;
359 hal::NotifyFMRadioStatus(info
);
368 sRadioEnabled
= false;
371 int rc
= setControl(V4L2_CID_PRIVATE_TAVARUA_STATE
, FM_OFF
);
373 HAL_LOG(("Unable to turn off radio"));
376 pthread_join(sRadioThread
, nullptr);
381 hal::FMRadioOperationInformation info
;
382 info
.operation() = hal::FM_RADIO_OPERATION_DISABLE
;
383 info
.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS
;
384 hal::NotifyFMRadioStatus(info
);
388 FMRadioSeek(const hal::FMRadioSeekDirection
& aDirection
)
390 struct v4l2_hw_freq_seek seek
= {0};
391 seek
.type
= V4L2_TUNER_RADIO
;
392 seek
.seek_upward
= aDirection
== hal::FMRadioSeekDirection::FM_RADIO_SEEK_DIRECTION_UP
;
394 /* ICS and older don't have the spacing field */
395 #if ANDROID_VERSION == 15
396 seek
.reserved
[0] = sRadioSettings
.spaceType() * 1000;
398 seek
.spacing
= sRadioSettings
.spaceType() * 1000;
401 int rc
= ioctl(sRadioFD
, VIDIOC_S_HW_FREQ_SEEK
, &seek
);
402 if (sMsmFMMode
&& rc
>= 0)
405 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK
,
407 hal::FM_RADIO_OPERATION_STATUS_FAIL
:
408 hal::FM_RADIO_OPERATION_STATUS_SUCCESS
));
411 HAL_LOG(("Could not initiate hardware seek"));
415 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_TUNE
,
416 hal::FM_RADIO_OPERATION_STATUS_SUCCESS
));
420 GetFMRadioSettings(hal::FMRadioSettings
* aInfo
)
422 if (!sRadioEnabled
) {
426 struct v4l2_tuner tuner
= {0};
427 int rc
= ioctl(sRadioFD
, VIDIOC_G_TUNER
, &tuner
);
429 HAL_LOG(("Could not query fm radio for settings"));
433 aInfo
->upperLimit() = (tuner
.rangehigh
* 625) / 10000;
434 aInfo
->lowerLimit() = (tuner
.rangelow
* 625) / 10000;
438 SetFMRadioFrequency(const uint32_t frequency
)
440 struct v4l2_frequency freq
= {0};
441 freq
.type
= V4L2_TUNER_RADIO
;
442 freq
.frequency
= (frequency
* 10000) / 625;
444 int rc
= ioctl(sRadioFD
, VIDIOC_S_FREQUENCY
, &freq
);
446 HAL_LOG(("Could not set radio frequency"));
448 if (sMsmFMMode
&& rc
>= 0)
451 NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_TUNE
,
453 hal::FM_RADIO_OPERATION_STATUS_FAIL
:
454 hal::FM_RADIO_OPERATION_STATUS_SUCCESS
));
458 GetFMRadioFrequency()
463 struct v4l2_frequency freq
;
464 int rc
= ioctl(sRadioFD
, VIDIOC_G_FREQUENCY
, &freq
);
466 HAL_LOG(("Could not get radio frequency"));
470 return (freq
.frequency
* 625) / 10000;
476 return sRadioEnabled
;
480 GetFMRadioSignalStrength()
482 struct v4l2_tuner tuner
= {0};
483 int rc
= ioctl(sRadioFD
, VIDIOC_G_TUNER
, &tuner
);
485 HAL_LOG(("Could not query fm radio for signal strength"));
497 } // namespace mozilla