/* * Copyright (c) 2011-2012 - Mauro Carvalho Chehab * * 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 version 2 * of the License. * * 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 * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * */ #include #include #include #include /* strcasecmp */ #include "dvb-fe-priv.h" #include #include #ifdef ENABLE_NLS # include "gettext.h" # include # define _(string) dgettext(LIBDVBV5_DOMAIN, string) #else # define _(string) string #endif # define N_(string) string static const struct dvb_sat_lnb lnb[] = { { .name = "Europe", .alias = "UNIVERSAL", .lowfreq = 9750, .highfreq = 10600, .rangeswitch = 11700, .freqrange = { { 10800, 11800 }, { 11600, 12700 }, } }, { .name = "Expressvu, North America", .alias = "DBS", .lowfreq = 11250, .freqrange = { { 12200, 12700 } } }, { .name = "Astra 1E, European Universal Ku (extended)", .alias = "EXTENDED", .lowfreq = 9750, .highfreq = 10600, .rangeswitch = 11700, .freqrange = { { 10700, 11700 }, { 11700, 12750 }, } }, { .name = "Standard", .alias = "STANDARD", .lowfreq = 10000, .freqrange = { { 10945, 11450 } }, }, { .name = "Astra", .alias = "ENHANCED", .lowfreq = 9750, .freqrange = { { 10700, 11700 } }, }, { .name = "Big Dish - Monopoint LNBf", .alias = "C-BAND", .lowfreq = 5150, .freqrange = { { 3700, 4200 } }, }, { .name = "Big Dish - Multipoint LNBf", .alias = "C-MULT", .lowfreq = 5150, .highfreq = 5750, .freqrange = { { 3700, 4200 } }, }, { .name = "DishPro LNBf", .alias = "DISHPRO", .lowfreq = 11250, .highfreq = 14350, .freqrange = { { 12200, 12700 } } }, { .name = "Japan 110BS/CS LNBf", .alias = "110BS", .lowfreq = 10678, .freqrange = { { 11710, 12751 } } }, }; int dvb_sat_search_lnb(const char *name) { int i = 0; for (i = 0; i < ARRAY_SIZE(lnb); i++) { if (!strcasecmp(name, lnb[i].alias)) return i; } return -1; } int dvb_print_lnb(int i) { if (i < 0 || i >= ARRAY_SIZE(lnb)) return -1; printf("%s\n\t%s\n", lnb[i].alias, lnb[i].name); printf(_("\t%d to %d MHz"), lnb[i].freqrange[0].low, lnb[i].freqrange[0].high); if (lnb[i].freqrange[1].low) printf(_(" and %d to %d MHz"), lnb[i].freqrange[1].low, lnb[i].freqrange[1].high); printf("\n\t%s LO, ", lnb[i].highfreq ? _("Dual") : _("Single")); if (!lnb[i].highfreq) { printf("IF = %d MHz\n", lnb[i].lowfreq); return 0; } if (!lnb[i].rangeswitch) { printf(_("Bandstacking, LO POL_R %d MHZ, LO POL_L %d MHz\n"), lnb[i].lowfreq, lnb[i].highfreq); return 0; } printf(_("IF = lowband %d MHz, highband %d MHz\n"), lnb[i].lowfreq, lnb[i].highfreq); return 0; } void dvb_print_all_lnb(void) { int i; for (i = 0; i < ARRAY_SIZE(lnb); i++) { dvb_print_lnb(i); printf("\n"); } } const struct dvb_sat_lnb *dvb_sat_get_lnb(int i) { if (i < 0 || i >= ARRAY_SIZE(lnb)) return NULL; return &lnb[i]; } /* * DVB satellite Diseqc specifics * According with: * http://www.eutelsat.com/satellites/pdf/Diseqc/Reference%20docs/bus_spec.pdf * http://www.eutelsat.com/satellites/pdf/Diseqc/associated%20docs/applic_info_turner-receiver.pdf */ struct diseqc_cmd { int len; union { unsigned char msg[6]; struct { unsigned char framing; unsigned char address; unsigned char command; unsigned char data0; unsigned char data1; unsigned char data2; }; }; }; enum diseqc_type { DISEQC_BROADCAST, DISEQC_BROADCAST_LNB_SWITCHER_SMATV, DISEQC_LNB, DISEQC_LNB_WITH_LOOP_SWITCH, DISEQC_SWITCHER, DISEQC_SWITHCER_WITH_LOOP, DISEQC_SMATV, DISEQC_ANY_POLARISER, DISEQC_LINEAR_POLARISER, DISEQC_ANY_POSITIONER, DISEQC_AZIMUTH_POSITIONER, DISEQC_ELEVATON_POSITIONER, DISEQC_ANY_INSTALLER_AID, DISEQC_SIGNAL_STRENGH_ANALOGUE_VAL, DISEQC_ANY_INTELLIGENT_SLAVE, DISEQC_SUBSCRIBER_HEADENDS, }; static int diseqc_addr[] = { [DISEQC_BROADCAST] = 0x00, [DISEQC_BROADCAST_LNB_SWITCHER_SMATV] = 0x10, [DISEQC_LNB] = 0x11, [DISEQC_LNB_WITH_LOOP_SWITCH] = 0x12, [DISEQC_SWITCHER] = 0x14, [DISEQC_SWITHCER_WITH_LOOP] = 0x15, [DISEQC_SMATV] = 0x18, [DISEQC_ANY_POLARISER] = 0x20, [DISEQC_LINEAR_POLARISER] = 0x21, [DISEQC_ANY_POSITIONER] = 0x30, [DISEQC_AZIMUTH_POSITIONER] = 0x31, [DISEQC_ELEVATON_POSITIONER] = 0x32, [DISEQC_ANY_INSTALLER_AID] = 0x40, [DISEQC_SIGNAL_STRENGH_ANALOGUE_VAL] = 0x41, [DISEQC_ANY_INTELLIGENT_SLAVE] = 0x70, [DISEQC_SUBSCRIBER_HEADENDS] = 0x71, }; static void dvbsat_diseqc_prep_frame_addr(struct diseqc_cmd *cmd, enum diseqc_type type, int reply, int repeat) { cmd->framing = 0xe0; /* First four bits are always 1110 */ if (reply) cmd->framing |=0x02; if (repeat) cmd->framing |=1; cmd->address = diseqc_addr[type]; } //struct dvb_v5_fe_parms *parms; // legacy code, used for parms->fd, FIXME anyway /* Inputs are numbered from 1 to 16, according with the spec */ static int dvbsat_diseqc_write_to_port_group(struct dvb_v5_fe_parms_priv *parms, struct diseqc_cmd *cmd, int high_band, int pol_v, int sat_number) { dvbsat_diseqc_prep_frame_addr(cmd, DISEQC_BROADCAST_LNB_SWITCHER_SMATV, 0, 0); cmd->command = 0x38; /* Write to Port group 0 (Committed switches) */ cmd->len = 4; /* Fill the 4 bits for the "input" select */ cmd->data0 = 0xf0; cmd->data0 |= high_band; cmd->data0 |= pol_v ? 0 : 2; /* Instead of using position/option, use a number from 0 to 3 */ cmd->data0 |= (sat_number % 0x3) << 2; return dvb_fe_diseqc_cmd(&parms->p, cmd->len, cmd->msg); } static int dvbsat_scr_odu_channel_change(struct dvb_v5_fe_parms_priv *parms, struct diseqc_cmd *cmd, int high_band, int pol_v, int sat_number, uint16_t t) { int pos_b; dvbsat_diseqc_prep_frame_addr(cmd, DISEQC_BROADCAST_LNB_SWITCHER_SMATV, 0, 0); cmd->command = 0x5a; /* ODU Channel Change */ cmd->len = 5; /* Fill the tuning parameter */ cmd->data0 = (t >> 8) & 0x03; cmd->data1 = t & 0xff; /* Fill the satelite number - highest bit is for pos A/pos B */ cmd->data0 |= (sat_number % 0x7) << 5; pos_b = (sat_number & 0x8) ? 1 : 0; /* Fill the LNB number */ cmd->data0 |= high_band ? 0 : 4; cmd->data0 |= pol_v ? 8 : 0; cmd->data0 |= pos_b ? 16 : 0; return dvb_fe_diseqc_cmd(&parms->p, cmd->len, cmd->msg); } static int dvbsat_diseqc_set_input(struct dvb_v5_fe_parms_priv *parms, uint16_t t) { int rc; enum dvb_sat_polarization pol; dvb_fe_retrieve_parm(&parms->p, DTV_POLARIZATION, &pol); int pol_v = (pol == POLARIZATION_V) || (pol == POLARIZATION_R); int high_band = parms->high_band; int sat_number = parms->p.sat_number; int vol_high = 0; int tone_on = 0; int mini_b = 0; struct diseqc_cmd cmd; if (!lnb->rangeswitch) { /* * Bandstacking switches don't use 2 bands nor use * DISEqC for setting the polarization. It also doesn't * use any tone/tone burst */ pol_v = 0; high_band = 1; if (parms->p.current_sys == SYS_ISDBS) vol_high = 1; } else { /* Adjust voltage/tone accordingly */ if (parms->p.sat_number < 2) { vol_high = pol_v ? 0 : 1; tone_on = high_band; mini_b = parms->p.sat_number & 1; } } rc = dvb_fe_sec_voltage(&parms->p, 1, vol_high); if (rc) return rc; if (parms->p.sat_number > 0) { rc = dvb_fe_sec_tone(&parms->p, SEC_TONE_OFF); if (rc) return rc; usleep(15 * 1000); if (!t) rc = dvbsat_diseqc_write_to_port_group(parms, &cmd, high_band, pol_v, sat_number); else rc = dvbsat_scr_odu_channel_change(parms, &cmd, high_band, pol_v, sat_number, t); if (rc) { dvb_logerr(_("sending diseq failed")); return rc; } usleep((15 + parms->p.diseqc_wait) * 1000); rc = dvb_fe_diseqc_burst(&parms->p, mini_b); if (rc) return rc; usleep(15 * 1000); } rc = dvb_fe_sec_tone(&parms->p, tone_on ? SEC_TONE_ON : SEC_TONE_OFF); return rc; } /* * DVB satellite get/set params hooks */ int dvb_sat_set_parms(struct dvb_v5_fe_parms *p) { struct dvb_v5_fe_parms_priv *parms = (void *)p; const struct dvb_sat_lnb *lnb = p->lnb; enum dvb_sat_polarization pol; dvb_fe_retrieve_parm(&parms->p, DTV_POLARIZATION, &pol); uint32_t freq; uint16_t t = 0; int rc; dvb_fe_retrieve_parm(&parms->p, DTV_FREQUENCY, &freq); if (!lnb) { dvb_logerr(_("Need a LNBf to work")); return -EINVAL; } /* Simple case: LNBf with just Single LO */ if (!lnb->highfreq) { parms->freq_offset = lnb->lowfreq * 1000; goto ret; } /* polarization-controlled multi LNBf */ if (!lnb->rangeswitch) { if ((pol == POLARIZATION_V) || (pol == POLARIZATION_R)) parms->freq_offset = lnb->lowfreq * 1000; else parms->freq_offset = lnb->highfreq * 1000; goto ret; } /* Voltage-controlled multiband switch */ parms->high_band = (freq > lnb->rangeswitch * 1000) ? 1 : 0; /* Adjust frequency */ if (parms->high_band) parms->freq_offset = lnb->highfreq * 1000; else parms->freq_offset = lnb->lowfreq * 1000; /* For SCR/Unicable setups */ if (parms->p.freq_bpf) { t = (((freq / 1000) + parms->p.freq_bpf + 2) / 4) - 350; parms->freq_offset += ((t + 350) * 4) * 1000; } ret: rc = dvbsat_diseqc_set_input(parms, t); freq = abs(freq - parms->freq_offset); dvb_fe_store_parm(&parms->p, DTV_FREQUENCY, freq); return rc; }