/* * fmrxd - a daemon to enable and multiplex access to the N950/N9 radio tuner * Copyright (C) 2011 Javier S. Pedro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include "fmrxd.h" int tuner_fd = -1; static bool tuner_precise; static unsigned long tuner_min, tuner_max; static unsigned long tuner_freq = 0; static inline bool tuner_is_open() { return tuner_fd != -1; } static inline unsigned long v4l_to_hz(unsigned f) { if (tuner_precise) { /* f * 62.5 */ return (f * 125UL) / 2; } else { return f * 62500UL; } } static inline unsigned hz_to_v4l(unsigned long f) { if (tuner_precise) { /* f / 62.5 */ return (f * 2UL) / 125; } else { return f / 62500; } } static inline unsigned long mhz_to_hz(double f) { return f * 1000000.0; } static inline double hz_to_mhz(unsigned long f) { return f / 1000000.0; } static bool tuner_tune(unsigned long hz) { struct v4l2_frequency t_freq = { .tuner = TUNER_DEVICE_ID, .type = V4L2_TUNER_RADIO, .frequency = hz_to_v4l(hz) }; g_return_val_if_fail(tuner_is_open(), false); if (ioctl(tuner_fd, VIDIOC_S_FREQUENCY, &t_freq) < 0) { g_warning("Failed to tune: %s", strerror(errno)); return false; } return true; } static unsigned long tuner_get_tuned_freq() { struct v4l2_frequency t_freq = { .tuner = TUNER_DEVICE_ID }; g_return_val_if_fail(tuner_is_open(), 0); if (ioctl(tuner_fd, VIDIOC_G_FREQUENCY, &t_freq) < 0) { g_warning("Failed to get freq: %s", strerror(errno)); return 0; } return v4l_to_hz(t_freq.frequency); } bool configure_tuner(bool on) { if (on) { struct v4l2_tuner tuner = { 0 }; int res; tuner_fd = open(TUNER_DEVICE, O_RDONLY); if (tuner_fd == -1) { g_critical("Couldn't open V4L2 tuner"); return false; } tuner.index = 0; res = ioctl(tuner_fd, VIDIOC_G_TUNER, &tuner); if (res < 0) { g_critical("Couldn't get V4L2 tuner information"); return false; } if (tuner.type != V4L2_TUNER_RADIO) { g_critical("Not a radio tuner\n"); return false; } tuner_precise = (tuner.capability & V4L2_TUNER_CAP_LOW) ? TRUE : FALSE; tuner_min = v4l_to_hz(tuner.rangelow); tuner_max = v4l_to_hz(tuner.rangelow); if (tuner_freq >= tuner_min && tuner_freq <= tuner_max && tuner_tune(tuner_freq)) { // All is well, we are on air! } else { // Use whatever frequency the tuner is currently using tuner_freq = tuner_get_tuned_freq(); } server_notify_tuned(hz_to_mhz(tuner_freq)); return true; } else { if (tuner_fd != -1) { close(tuner_fd); tuner_fd = -1; } return true; } } bool tuner_set_frequency(double mhz) { unsigned long hz = mhz_to_hz(mhz); if (!tuner_is_open()) { // This is not an error; we just store the frequency // and will set it later, when the tuner is opened. tuner_freq = hz; return true; } else if (tuner_tune(hz)) { g_message("Tuned to %.1f Mhz", mhz); server_notify_tuned(mhz); tuner_freq = hz; signal_reset(); rds_reset(); return true; } return false; } int32_t tuner_get_signal() { struct v4l2_tuner t_tuner = { .index = TUNER_DEVICE_ID }; g_return_val_if_fail(tuner_is_open(), 0); if (ioctl(tuner_fd, VIDIOC_G_TUNER, &t_tuner) < 0) { g_warning("Failed to get signal level: %s", strerror(errno)); return 0; } return t_tuner.signal; } bool tuner_search(bool forward) { struct v4l2_hw_freq_seek t_freq_seek = { .tuner = TUNER_DEVICE_ID, .type = V4L2_TUNER_RADIO, .seek_upward = forward }; g_return_val_if_fail(tuner_is_open(), false); if (ioctl(tuner_fd, VIDIOC_S_HW_FREQ_SEEK, &t_freq_seek) < 0) { g_warning("Failed to start seek: %s", strerror(errno)); return false; } // Search complete, get new frequency unsigned long hz = tuner_get_tuned_freq(); if (!hz) { return false; } // Got new frequency, fire signals. tuner_freq = hz; server_notify_tuned(hz_to_mhz(hz)); signal_reset(); rds_reset(); return true; }