/* * Tvheadend - Linux DVB satconf * * Copyright (C) 2013 Adam Sutton * * 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 3 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, see . */ #include "tvheadend.h" #include "linuxdvb_private.h" #include "settings.h" #include #include #include #include #include #include #include #include static struct linuxdvb_satconf_type * linuxdvb_satconf_type_find ( const char *type ); struct linuxdvb_satconf_type { int ports; const char *type; const char *name; const idclass_t *idc; }; /* ************************************************************************** * Types * *************************************************************************/ static linuxdvb_satconf_ele_t * linuxdvb_satconf_class_find_ele( linuxdvb_satconf_t *ls, int idx ) { int i = 0; linuxdvb_satconf_ele_t *lse; TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) { if (i == idx) break; i++; } return lse; } static const void * linuxdvb_satconf_class_network_get( linuxdvb_satconf_t *ls, int idx ) { linuxdvb_satconf_ele_t *lse = linuxdvb_satconf_class_find_ele(ls, idx); if (lse) return idnode_set_as_htsmsg(lse->lse_networks); return NULL; } static int linuxdvb_satconf_ele_class_network_set( void *o, const void *p ); static int linuxdvb_satconf_class_network_set ( linuxdvb_satconf_t *ls, int idx, const void *networks ) { linuxdvb_satconf_ele_t *lse = linuxdvb_satconf_class_find_ele(ls, idx); if (lse) return linuxdvb_satconf_ele_class_network_set(lse, networks); return 0; } static htsmsg_t * linuxdvb_satconf_class_network_enum( void *o ) { htsmsg_t *m = htsmsg_create_map(); htsmsg_t *p = htsmsg_create_map(); htsmsg_add_str(m, "type", "api"); htsmsg_add_str(m, "uri", "idnode/load"); htsmsg_add_str(m, "event", "mpegts_network"); htsmsg_add_u32(p, "enum", 1); htsmsg_add_str(p, "class", dvb_network_dvbs_class.ic_class); htsmsg_add_msg(m, "params", p); return m; } static char * linuxdvb_satconf_ele_class_network_rend( void *o ); static char * linuxdvb_satconf_class_network_rend( linuxdvb_satconf_t *ls, int idx ) { linuxdvb_satconf_ele_t *lse = linuxdvb_satconf_class_find_ele(ls, idx); if (lse) return linuxdvb_satconf_ele_class_network_rend(lse); return NULL; } #define linuxdvb_satconf_class_network_getset(x)\ static int \ linuxdvb_satconf_class_network_set##x ( void *o, const void *v )\ {\ return linuxdvb_satconf_class_network_set(o, x, (void*)v);\ }\ static const void * \ linuxdvb_satconf_class_network_get##x ( void *o )\ {\ return linuxdvb_satconf_class_network_get(o, x);\ }\ static char * \ linuxdvb_satconf_class_network_rend##x ( void *o )\ {\ return linuxdvb_satconf_class_network_rend(o, x);\ } linuxdvb_satconf_class_network_getset(0); linuxdvb_satconf_class_network_getset(1); linuxdvb_satconf_class_network_getset(2); linuxdvb_satconf_class_network_getset(3); static const char * linuxdvb_satconf_class_get_title ( idnode_t *p ) { linuxdvb_satconf_t *ls = (linuxdvb_satconf_t*)p; struct linuxdvb_satconf_type *lst = linuxdvb_satconf_type_find(ls->ls_type); return lst ? lst->name : ls->ls_type; } static void linuxdvb_satconf_class_save ( idnode_t *s ) { linuxdvb_satconf_t *ls = (linuxdvb_satconf_t*)s; linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)ls->ls_frontend; linuxdvb_adapter_t *la = lfe->lfe_adapter; linuxdvb_adapter_save(la); } static const void * linuxdvb_satconf_class_orbitalpos_get ( void *p ) { static int n; linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse; n = 0; TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) n++; return &n; } static int linuxdvb_satconf_class_orbitalpos_set ( void *p, const void *v ) { linuxdvb_satconf_ele_t *lse; linuxdvb_satconf_t *ls = p; int c = *(int*)linuxdvb_satconf_class_orbitalpos_get(p); int n = *(int*)v; char buf[20]; if (n == c) return 0; /* Add */ if (n > c) { while (c < n) { lse = linuxdvb_satconf_ele_create0(NULL, NULL, ls); if (lse->lse_name == NULL) { snprintf(buf, sizeof(buf), "Position #%i", c + 1); lse->lse_name = strdup(buf); } c++; } /* Remove */ } else { while (c > n) { lse = TAILQ_LAST(&ls->ls_elements, linuxdvb_satconf_ele_list); linuxdvb_satconf_ele_destroy(lse); c--; } } return 1; } static idnode_set_t * linuxdvb_satconf_class_get_childs ( idnode_t *o ) { linuxdvb_satconf_t *ls = (linuxdvb_satconf_t*)o; linuxdvb_satconf_ele_t *lse; idnode_set_t *is = idnode_set_create(); TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) idnode_set_add(is, &lse->lse_id, NULL); return is; } /* * Generic satconf */ const idclass_t linuxdvb_satconf_class = { .ic_class = "linuxdvb_satconf", .ic_caption = "DVB-S Satconf", .ic_event = "linuxdvb_satconf", .ic_get_title = linuxdvb_satconf_class_get_title, .ic_save = linuxdvb_satconf_class_save, .ic_properties = (const property_t[]) { { .type = PT_INT, .id = "diseqc_repeats", .name = "DiseqC repeats", .off = offsetof(linuxdvb_satconf_t, ls_diseqc_repeats), .opts = PO_ADVANCED, .def.i = 0 }, { .type = PT_BOOL, .id = "lnb_poweroff", .name = "Turn off LNB when idle", .off = offsetof(linuxdvb_satconf_t, ls_lnb_poweroff), .opts = PO_ADVANCED, .def.i = 1 }, {} } }; /* * Simple LNB only */ const idclass_t linuxdvb_satconf_lnbonly_class = { .ic_super = &linuxdvb_satconf_class, .ic_class = "linuxdvb_satconf_lnbonly", .ic_caption = "DVB-S Simple", .ic_properties = (const property_t[]) { { .type = PT_STR, .id = "networks", .name = "Networks", .islist = 1, .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend0, .opts = PO_NOSAVE, }, {} } }; /* * 2 port switch */ const idclass_t linuxdvb_satconf_2port_class = { .ic_super = &linuxdvb_satconf_class, .ic_class = "linuxdvb_satconf_2port", .ic_caption = "DVB-S Toneburst", .ic_properties = (const property_t[]) { { .type = PT_STR, .id = "network_a", .name = "A", .islist = 1, .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend0, .opts = PO_NOSAVE, }, { .type = PT_STR, .id = "network_b", .name = "B", .islist = 1, .get = linuxdvb_satconf_class_network_get1, .set = linuxdvb_satconf_class_network_set1, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend1, .opts = PO_NOSAVE, }, {} } }; /* * 4 port switch */ const idclass_t linuxdvb_satconf_4port_class = { .ic_super = &linuxdvb_satconf_class, .ic_class = "linuxdvb_satconf_4port", .ic_caption = "DVB-S 4-port", .ic_properties = (const property_t[]) { { .type = PT_STR, .id = "network_aa", .name = "AA", .islist = 1, .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend0, .opts = PO_NOSAVE, }, { .type = PT_STR, .id = "network_ab", .name = "AB", .islist = 1, .get = linuxdvb_satconf_class_network_get1, .set = linuxdvb_satconf_class_network_set1, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend1, .opts = PO_NOSAVE, }, { .type = PT_STR, .id = "network_ba", .name = "BA", .islist = 1, .get = linuxdvb_satconf_class_network_get2, .set = linuxdvb_satconf_class_network_set2, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend2, .opts = PO_NOSAVE, }, { .type = PT_STR, .id = "network_bb", .name = "BB", .islist = 1, .get = linuxdvb_satconf_class_network_get3, .set = linuxdvb_satconf_class_network_set3, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend3, .opts = PO_NOSAVE, }, {} } }; /* * Unicable (EN50494) */ static const void * linuxdvb_satconf_class_en50494_id_get ( void *p ) { linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse = TAILQ_FIRST(&ls->ls_elements); return &(((linuxdvb_en50494_t*)lse->lse_en50494)->le_id); } static int linuxdvb_satconf_class_en50494_id_set ( void *p, const void *v ) { linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse; TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) (((linuxdvb_en50494_t*)lse->lse_en50494)->le_id) = *(uint16_t*)v; return 1; } static const void * linuxdvb_satconf_class_en50494_pin_get ( void *p ) { linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse = TAILQ_FIRST(&ls->ls_elements); return &(((linuxdvb_en50494_t*)lse->lse_en50494)->le_pin); } static int linuxdvb_satconf_class_en50494_pin_set ( void *p, const void *v ) { linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse; TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) (((linuxdvb_en50494_t*)lse->lse_en50494)->le_pin) = *(uint16_t*)v; return 1; } static const void * linuxdvb_satconf_class_en50494_freq_get ( void *p ) { linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse = TAILQ_FIRST(&ls->ls_elements); return &(((linuxdvb_en50494_t*)lse->lse_en50494)->le_frequency); } static int linuxdvb_satconf_class_en50494_freq_set ( void *p, const void *v ) { linuxdvb_satconf_t *ls = p; linuxdvb_satconf_ele_t *lse; TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) (((linuxdvb_en50494_t*)lse->lse_en50494)->le_frequency) = *(uint16_t*)v; return 1; } const idclass_t linuxdvb_satconf_en50494_class = { .ic_super = &linuxdvb_satconf_class, .ic_class = "linuxdvb_satconf_en50494", .ic_caption = "DVB-S EN50494 (UniCable)", .ic_properties = (const property_t[]) { { .type = PT_U16, .id = "id", .name = "SCR (ID)", .get = linuxdvb_satconf_class_en50494_id_get, .set = linuxdvb_satconf_class_en50494_id_set, .list = linuxdvb_en50494_id_list, .opts = PO_NOSAVE, }, { .type = PT_U16, .id = "pin", .name = "Pin", .get = linuxdvb_satconf_class_en50494_pin_get, .set = linuxdvb_satconf_class_en50494_pin_set, .list = linuxdvb_en50494_pin_list, .opts = PO_NOSAVE, }, { .type = PT_U16, .id = "frequency", .name = "Frequency (MHz)", .get = linuxdvb_satconf_class_en50494_freq_get, .set = linuxdvb_satconf_class_en50494_freq_set, .opts = PO_NOSAVE, }, { .type = PT_STR, .id = "network_a", .name = "Network A", .islist = 1, .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend0, .opts = PO_NOSAVE, }, { .type = PT_STR, .id = "network_b", .name = "Network B", .islist = 1, .get = linuxdvb_satconf_class_network_get1, .set = linuxdvb_satconf_class_network_set1, .list = linuxdvb_satconf_class_network_enum, .rend = linuxdvb_satconf_class_network_rend1, .opts = PO_NOSAVE, }, {} } }; /* * Advanced */ const idclass_t linuxdvb_satconf_advanced_class = { .ic_super = &linuxdvb_satconf_class, .ic_class = "linuxdvb_satconf_advanced", .ic_caption = "DVB-S Advanced", .ic_get_childs = linuxdvb_satconf_class_get_childs, .ic_properties = (const property_t[]) { { .type = PT_INT, .id = "orbital_pos", .name = "Orbital Positions", .get = linuxdvb_satconf_class_orbitalpos_get, .set = linuxdvb_satconf_class_orbitalpos_set, }, {} } }; /* ************************************************************************** * Types * *************************************************************************/ /* Types/classes */ static struct linuxdvb_satconf_type linuxdvb_satconf_types[] = { { .type = "simple", .name = "Universal LNB only", .idc = &linuxdvb_satconf_lnbonly_class, .ports = 1, }, { .type = "2port", .name = "2-port Switch (Universal LNB)", .idc = &linuxdvb_satconf_2port_class, .ports = 2, }, { .type = "4port", .name = "4-port Switch (Universal LNB)", .idc = &linuxdvb_satconf_4port_class, .ports = 4, }, { .type = "en50494", .name = "Unicable Switch (Universal LNB)", .idc = &linuxdvb_satconf_en50494_class, .ports = 2, }, { .type = "advanced", .name = "Advanced (Non-Universal LNBs, Rotors, etc.)", .idc = &linuxdvb_satconf_advanced_class, .ports = 0, }, }; /* Find type (with default) */ static struct linuxdvb_satconf_type * linuxdvb_satconf_type_find ( const char *type ) { int i; for (i = 0; i < ARRAY_SIZE(linuxdvb_satconf_types); i++) if (!strcmp(type ?: "", linuxdvb_satconf_types[i].type)) return linuxdvb_satconf_types+i; return linuxdvb_satconf_types; } /* List of types */ htsmsg_t * linuxdvb_satconf_type_list ( void *p ) { int i; htsmsg_t *e, *m = htsmsg_create_list(); for (i = 0; i < ARRAY_SIZE(linuxdvb_satconf_types); i++) { e = htsmsg_create_map(); htsmsg_add_str(e, "key", linuxdvb_satconf_types[i].type); htsmsg_add_str(e, "val", linuxdvb_satconf_types[i].name); htsmsg_add_msg(m, NULL, e); } return m; } /* * Frontend callbacks */ static linuxdvb_satconf_ele_t * linuxdvb_satconf_find_ele( linuxdvb_satconf_t *ls, mpegts_mux_t *mux ) { linuxdvb_satconf_ele_t *lse; TAILQ_FOREACH(lse, &ls->ls_elements, lse_link) { if (idnode_set_exists(lse->lse_networks, &mux->mm_network->mn_id)) return lse; } return NULL; } int linuxdvb_satconf_get_priority ( linuxdvb_satconf_t *ls, mpegts_mux_t *mm ) { linuxdvb_satconf_ele_t *lse = linuxdvb_satconf_find_ele(ls, mm); return lse->lse_priority; } void linuxdvb_satconf_post_stop_mux ( linuxdvb_satconf_t *ls ) { gtimer_disarm(&ls->ls_diseqc_timer); if (ls->ls_frontend && ls->ls_lnb_poweroff) linuxdvb_diseqc_set_volt( ((linuxdvb_frontend_t *)ls->ls_frontend)->lfe_fe_fd, -1); } int linuxdvb_satconf_get_grace ( linuxdvb_satconf_t *ls, mpegts_mux_t *mm ) { linuxdvb_satconf_ele_t *lse = linuxdvb_satconf_find_ele(ls, mm); int i, r = 10; linuxdvb_diseqc_t *lds[] = { (linuxdvb_diseqc_t*)lse->lse_en50494, (linuxdvb_diseqc_t*)lse->lse_switch, (linuxdvb_diseqc_t*)lse->lse_rotor, (linuxdvb_diseqc_t*)lse->lse_lnb }; /* Add diseqc delay */ for (i = 0; i < 3; i++) { if (lds[i] && lds[i]->ld_grace) r += lds[i]->ld_grace(lds[i], (dvb_mux_t*)mm); } return r; } static void linuxdvb_satconf_ele_tune_cb ( void *o ); static int linuxdvb_satconf_ele_tune ( linuxdvb_satconf_ele_t *lse ) { int r, i, b; uint32_t f; linuxdvb_satconf_t *ls = lse->lse_parent; /* Get beans in a row */ mpegts_mux_instance_t *mmi = ls->ls_mmi; linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)ls->ls_frontend; dvb_mux_t *lm = (dvb_mux_t*)mmi->mmi_mux; linuxdvb_diseqc_t *lds[] = { lse->lse_rotor ? (linuxdvb_diseqc_t*)lse->lse_switch : NULL, (linuxdvb_diseqc_t*)lse->lse_rotor, (linuxdvb_diseqc_t*)lse->lse_switch, (linuxdvb_diseqc_t*)lse->lse_en50494, (linuxdvb_diseqc_t*)lse->lse_lnb }; // TODO: really need to understand whether or not we need to pre configure // and/or re-affirm the switch /* Disable tone */ if (ioctl(lfe->lfe_fe_fd, FE_SET_TONE, SEC_TONE_OFF)) { tvherror("diseqc", "failed to disable tone"); return -1; } /* Diseqc */ for (i = ls->ls_diseqc_idx; i < ARRAY_SIZE(lds); i++) { if (!lds[i]) continue; r = lds[i]->ld_tune(lds[i], lm, lse, lfe->lfe_fe_fd); /* Error */ if (r < 0) return r; /* Pending */ if (r != 0) { gtimer_arm(&ls->ls_diseqc_timer, linuxdvb_satconf_ele_tune_cb, lse, r); ls->ls_diseqc_idx = i + 1; return 0; } } /* Remember the last network position for rotor */ dvb_network_get_orbital_pos(lm->mm_network, &lse->lse_parent->ls_orbital_pos, &lse->lse_parent->ls_orbital_dir); /* Set the tone */ b = lse->lse_lnb->lnb_band(lse->lse_lnb, lm); tvhtrace("disqec", "set diseqc tone %s", b ? "on" : "off"); if (ioctl(lfe->lfe_fe_fd, FE_SET_TONE, b ? SEC_TONE_ON : SEC_TONE_OFF)) { tvherror("diseqc", "failed to set diseqc tone (e=%s)", strerror(errno)); return -1; } usleep(20000); // Allow LNB to settle before tuning /* Frontend */ /* use en50494 tuning frequency, if needed (not channel frequency) */ f = lse->lse_en50494 ? ((linuxdvb_en50494_t*)lse->lse_en50494)->le_tune_freq : lse->lse_lnb->lnb_freq(lse->lse_lnb, lm); return linuxdvb_frontend_tune1(lfe, mmi, f); } static void linuxdvb_satconf_ele_tune_cb ( void *o ) { (void)linuxdvb_satconf_ele_tune(o); // TODO: how to signal error } int linuxdvb_satconf_start_mux ( linuxdvb_satconf_t *ls, mpegts_mux_instance_t *mmi ) { int r; uint32_t f; linuxdvb_satconf_ele_t *lse = linuxdvb_satconf_find_ele(ls, mmi->mmi_mux); linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)ls->ls_frontend; dvb_mux_t *lm = (dvb_mux_t*)mmi->mmi_mux; /* Not fully configured */ if (!lse) return SM_CODE_TUNING_FAILED; /* Test run */ // Note: basically this ensures the tuning params are acceptable // for the FE, so that if they're not we don't have to wait // for things like rotors and switches if (!lse->lse_lnb) return SM_CODE_TUNING_FAILED; f = lse->lse_lnb->lnb_freq(lse->lse_lnb, lm); if (f == (uint32_t)-1) return SM_CODE_TUNING_FAILED; r = linuxdvb_frontend_tune0(lfe, mmi, f); if (r) return r; /* Diseqc */ ls->ls_mmi = mmi; ls->ls_diseqc_idx = 0; return linuxdvb_satconf_ele_tune(lse); } /* ************************************************************************** * Create/Delete satconf * *************************************************************************/ linuxdvb_satconf_t * linuxdvb_satconf_create ( linuxdvb_frontend_t *lfe, const char *type, const char *uuid, htsmsg_t *conf ) { int i; htsmsg_t *l, *e; htsmsg_field_t *f; linuxdvb_satconf_ele_t *lse; const char *str; struct linuxdvb_satconf_type *lst = linuxdvb_satconf_type_find(type); assert(lst); linuxdvb_satconf_t *ls = calloc(1, sizeof(linuxdvb_satconf_t)); ls->ls_frontend = (mpegts_input_t*)lfe; ls->ls_type = lst->type; TAILQ_INIT(&ls->ls_elements); /* Create node */ if (idnode_insert(&ls->ls_id, uuid, lst->idc, 0)) { free(ls); return NULL; } /* Load config */ if (conf) { /* Load elements */ // Note: we do things this way else hte orbital_pos field in advanced // will result in extra elements if ((l = htsmsg_get_list(conf, "elements"))) { HTSMSG_FOREACH(f, l) { if (!(e = htsmsg_field_get_map(f))) continue; /* Fix config */ if ((str = htsmsg_get_str(e, "network")) && !htsmsg_get_list(e, "networks")) { htsmsg_t *l = htsmsg_create_list(); htsmsg_add_str(l, NULL, str); htsmsg_add_msg(e, "networks", l); } (void)linuxdvb_satconf_ele_create0(htsmsg_get_str(e, "uuid"), e, ls); } } /* Load node */ idnode_load(&ls->ls_id, conf); } /* Create elements */ i = 0; lse = TAILQ_FIRST(&ls->ls_elements); while (i < lst->ports) { if (!lse) lse = linuxdvb_satconf_ele_create0(NULL, NULL, ls); if (!lse->lse_lnb) lse->lse_lnb = linuxdvb_lnb_create0(NULL, NULL, lse); if (lst->ports > 1) { if (!strcmp(lst->type, "en50494")) { if (!lse->lse_en50494) lse->lse_en50494 = linuxdvb_en50494_create0("Generic", NULL, lse, i); } else { if (!lse->lse_switch) lse->lse_switch = linuxdvb_switch_create0("Generic", NULL, lse, i, -1); } } lse = TAILQ_NEXT(lse, lse_link); i++; } return ls; } void linuxdvb_satconf_save ( linuxdvb_satconf_t *ls, htsmsg_t *m ) { linuxdvb_satconf_ele_t *lse; htsmsg_t *l, *e, *c; htsmsg_add_str(m, "type", ls->ls_type); idnode_save(&ls->ls_id, m); l = htsmsg_create_list(); TAILQ_FOREACH(lse, &ls->ls_elements, lse_link){ e = htsmsg_create_map(); idnode_save(&lse->lse_id, e); htsmsg_add_str(e, "uuid", idnode_uuid_as_str(&lse->lse_id)); if (lse->lse_lnb) { c = htsmsg_create_map(); idnode_save(&lse->lse_lnb->ld_id, c); htsmsg_add_msg(e, "lnb_conf", c); } if (lse->lse_switch) { c = htsmsg_create_map(); idnode_save(&lse->lse_switch->ld_id, c); htsmsg_add_msg(e, "switch_conf", c); } if (lse->lse_rotor) { c = htsmsg_create_map(); idnode_save(&lse->lse_rotor->ld_id, c); htsmsg_add_msg(e, "rotor_conf", c); } if (lse->lse_en50494) { c = htsmsg_create_map(); idnode_save(&lse->lse_en50494->ld_id, c); htsmsg_add_msg(e, "en50494_conf", c); } htsmsg_add_msg(l, NULL, e); } htsmsg_add_msg(m, "elements", l); } /* ************************************************************************** * Class definition * *************************************************************************/ extern const idclass_t mpegts_input_class; static const void * linuxdvb_satconf_ele_class_network_get( void *o ) { linuxdvb_satconf_ele_t *ls = o; return idnode_set_as_htsmsg(ls->lse_networks); } static int linuxdvb_satconf_ele_class_network_set( void *o, const void *p ) { linuxdvb_satconf_ele_t *ls = o; const htsmsg_t *msg = p; mpegts_network_t *mn; idnode_set_t *n = idnode_set_create(); htsmsg_field_t *f; const char *str; int i, save; HTSMSG_FOREACH(f, msg) { if (!(str = htsmsg_field_get_str(f))) continue; if (!(mn = mpegts_network_find(str))) continue; idnode_set_add(n, &mn->mn_id, NULL); } save = n->is_count != ls->lse_networks->is_count; if (!save) { for (i = 0; i < n->is_count; i++) if (!idnode_set_exists(ls->lse_networks, n->is_array[i])) { save = 1; break; } } if (save) { /* update the local (antenna satconf) network list */ idnode_set_free(ls->lse_networks); ls->lse_networks = n; /* update the input (frontend) network list */ htsmsg_t *l = htsmsg_create_list(); linuxdvb_satconf_t *sc = ls->lse_parent; linuxdvb_satconf_ele_t *lse; TAILQ_FOREACH(lse, &sc->ls_elements, lse_link) { for (i = 0; i < lse->lse_networks->is_count; i++) htsmsg_add_str(l, NULL, idnode_uuid_as_str(lse->lse_networks->is_array[i])); } mpegts_input_class_network_set(ls->lse_parent->ls_frontend, l); htsmsg_destroy(l); } else { idnode_set_free(n); } return save; } static htsmsg_t * linuxdvb_satconf_ele_class_network_enum( void *o ) { linuxdvb_satconf_ele_t *ls = o; return mpegts_input_class_network_enum(ls->lse_parent->ls_frontend); } static char * linuxdvb_satconf_ele_class_network_rend( void *o ) { linuxdvb_satconf_ele_t *ls = o; htsmsg_t *l = idnode_set_as_htsmsg(ls->lse_networks); char *str = htsmsg_list_2_csv(l); htsmsg_destroy(l); return str; } static int linuxdvb_satconf_ele_class_lnbtype_set ( void *o, const void *p ) { linuxdvb_satconf_ele_t *ls = o; const char *str = p; if (ls->lse_lnb && !strcmp(str ?: "", ls->lse_lnb->ld_type)) return 0; if (ls->lse_lnb) linuxdvb_lnb_destroy(ls->lse_lnb); ls->lse_lnb = linuxdvb_lnb_create0(str, NULL, ls); return 1; } static const void * linuxdvb_satconf_ele_class_lnbtype_get ( void *o ) { static const char *s; linuxdvb_satconf_ele_t *ls = o; s = ls->lse_lnb ? ls->lse_lnb->ld_type : NULL; return &s; } static int linuxdvb_satconf_ele_class_en50494type_set ( void *o, const void *p ) { linuxdvb_satconf_ele_t *ls = o; const char *str = p; if (ls->lse_en50494) linuxdvb_en50494_destroy(ls->lse_en50494); ls->lse_en50494 = linuxdvb_en50494_create0(str, NULL, ls, 0); return 1; } static const void * linuxdvb_satconf_ele_class_en50494type_get ( void *o ) { static const char *s; linuxdvb_satconf_ele_t *ls = o; s = ls->lse_en50494 ? ls->lse_en50494->ld_type : NULL; return &s; } static int linuxdvb_satconf_ele_class_switchtype_set ( void *o, const void *p ) { linuxdvb_satconf_ele_t *ls = o; const char *str = p; if (ls->lse_switch && !strcmp(str ?: "", ls->lse_switch->ld_type)) return 0; if (ls->lse_switch) linuxdvb_switch_destroy(ls->lse_switch); ls->lse_switch = linuxdvb_switch_create0(str, NULL, ls, -1, -1); return 1; } static const void * linuxdvb_satconf_ele_class_switchtype_get ( void *o ) { static const char *s; linuxdvb_satconf_ele_t *ls = o; s = ls->lse_switch ? ls->lse_switch->ld_type : NULL; return &s; } static int linuxdvb_satconf_ele_class_rotortype_set ( void *o, const void *p ) { linuxdvb_satconf_ele_t *ls = o; const char *str = p; if (ls->lse_rotor && !strcmp(str ?: "", ls->lse_rotor->ld_type)) return 0; if (ls->lse_rotor) linuxdvb_rotor_destroy(ls->lse_rotor); ls->lse_rotor = linuxdvb_rotor_create0(str, NULL, ls); return 1; } static const void * linuxdvb_satconf_ele_class_rotortype_get ( void *o ) { static const char *s; linuxdvb_satconf_ele_t *ls = o; s = ls->lse_rotor ? ls->lse_rotor->ld_type : NULL; return &s; } static const char * linuxdvb_satconf_ele_class_get_title ( idnode_t *o ) { return ((linuxdvb_satconf_ele_t *)o)->lse_name; } static idnode_set_t * linuxdvb_satconf_ele_class_get_childs ( idnode_t *o ) { linuxdvb_satconf_ele_t *ls = (linuxdvb_satconf_ele_t*)o; idnode_set_t *is = idnode_set_create(); if (ls->lse_lnb) idnode_set_add(is, &ls->lse_lnb->ld_id, NULL); if (ls->lse_switch) idnode_set_add(is, &ls->lse_switch->ld_id, NULL); if (ls->lse_rotor) idnode_set_add(is, &ls->lse_rotor->ld_id, NULL); if (ls->lse_en50494) idnode_set_add(is, &ls->lse_en50494->ld_id, NULL); return is; } static void linuxdvb_satconf_ele_class_save ( idnode_t *in ) { linuxdvb_satconf_ele_t *lse = (linuxdvb_satconf_ele_t*)in; linuxdvb_satconf_class_save(&lse->lse_parent->ls_id); } const idclass_t linuxdvb_satconf_ele_class = { .ic_class = "linuxdvb_satconf_ele", .ic_caption = "Satconf", .ic_event = "linuxdvb_satconf_ele", .ic_get_title = linuxdvb_satconf_ele_class_get_title, .ic_get_childs = linuxdvb_satconf_ele_class_get_childs, .ic_save = linuxdvb_satconf_ele_class_save, .ic_properties = (const property_t[]) { { .type = PT_BOOL, .id = "enabled", .name = "Enabled", .off = offsetof(linuxdvb_satconf_ele_t, lse_enabled), }, { .type = PT_STR, .id = "displayname", .name = "Name", .off = offsetof(linuxdvb_satconf_ele_t, lse_name), .notify = idnode_notify_title_changed, }, { .type = PT_INT, .id = "priority", .name = "Priority", .off = offsetof(linuxdvb_satconf_ele_t, lse_priority), .def.i = 1, .opts = PO_ADVANCED, }, { .type = PT_STR, .id = "networks", .name = "Networks", .islist = 1, .set = linuxdvb_satconf_ele_class_network_set, .get = linuxdvb_satconf_ele_class_network_get, .list = linuxdvb_satconf_ele_class_network_enum, .rend = linuxdvb_satconf_ele_class_network_rend, }, { .type = PT_STR, .id = "lnb_type", .name = "LNB Type", .set = linuxdvb_satconf_ele_class_lnbtype_set, .get = linuxdvb_satconf_ele_class_lnbtype_get, .list = linuxdvb_lnb_list, .def.s = "Universal", }, { .type = PT_STR, .id = "switch_type", .name = "Switch Type", .set = linuxdvb_satconf_ele_class_switchtype_set, .get = linuxdvb_satconf_ele_class_switchtype_get, .list = linuxdvb_switch_list, .def.s = "None", }, { .type = PT_STR, .id = "rotor_type", .name = "Rotor Type", .set = linuxdvb_satconf_ele_class_rotortype_set, .get = linuxdvb_satconf_ele_class_rotortype_get, .list = linuxdvb_rotor_list, .def.s = "None", }, { .type = PT_STR, .id = "en50494_type", .name = "Unicable Type", .set = linuxdvb_satconf_ele_class_en50494type_set, .get = linuxdvb_satconf_ele_class_en50494type_get, .list = linuxdvb_en50494_list, .def.s = "None", }, {} } }; /* ************************************************************************** * Creation/Config * *************************************************************************/ void linuxdvb_satconf_ele_destroy ( linuxdvb_satconf_ele_t *ls ) { TAILQ_REMOVE(&ls->lse_parent->ls_elements, ls, lse_link); idnode_unlink(&ls->lse_id); if (ls->lse_lnb) linuxdvb_lnb_destroy(ls->lse_lnb); if (ls->lse_switch) linuxdvb_switch_destroy(ls->lse_switch); if (ls->lse_rotor) linuxdvb_rotor_destroy(ls->lse_rotor); if (ls->lse_en50494) linuxdvb_en50494_destroy(ls->lse_en50494); free(ls->lse_name); free(ls); } linuxdvb_satconf_ele_t * linuxdvb_satconf_ele_create0 ( const char *uuid, htsmsg_t *conf, linuxdvb_satconf_t *ls ) { htsmsg_t *e; linuxdvb_satconf_ele_t *lse = calloc(1, sizeof(*lse)); if (idnode_insert(&lse->lse_id, uuid, &linuxdvb_satconf_ele_class, 0)) { free(lse); return NULL; } lse->lse_networks = idnode_set_create(); lse->lse_parent = ls; TAILQ_INSERT_TAIL(&ls->ls_elements, lse, lse_link); if (conf) idnode_load(&lse->lse_id, conf); /* Config */ if (conf) { /* LNB */ if (lse->lse_lnb && (e = htsmsg_get_map(conf, "lnb_conf"))) idnode_load(&lse->lse_lnb->ld_id, e); /* Switch */ if (lse->lse_switch && (e = htsmsg_get_map(conf, "switch_conf"))) idnode_load(&lse->lse_switch->ld_id, e); /* Rotor */ if (lse->lse_rotor && (e = htsmsg_get_map(conf, "rotor_conf"))) idnode_load(&lse->lse_rotor->ld_id, e); /* EN50494 */ if (lse->lse_en50494 && (e = htsmsg_get_map(conf, "en50494_conf"))) idnode_load(&lse->lse_en50494->ld_id, e); } /* Create default LNB */ if (!lse->lse_lnb) lse->lse_lnb = linuxdvb_lnb_create0(NULL, NULL, lse); return lse; } void linuxdvb_satconf_delete ( linuxdvb_satconf_t *ls, int delconf ) { linuxdvb_satconf_ele_t *lse, *nxt; const char *uuid = idnode_uuid_as_str(&ls->ls_id); if (delconf) hts_settings_remove("input/linuxdvb/satconfs/%s", uuid); gtimer_disarm(&ls->ls_diseqc_timer); for (lse = TAILQ_FIRST(&ls->ls_elements); lse != NULL; lse = nxt) { nxt = TAILQ_NEXT(lse, lse_link); TAILQ_REMOVE(&ls->ls_elements, lse, lse_link); if (lse->lse_lnb) linuxdvb_lnb_destroy(lse->lse_lnb); if (lse->lse_switch) linuxdvb_switch_destroy(lse->lse_switch); if (lse->lse_rotor) linuxdvb_rotor_destroy(lse->lse_rotor); if (lse->lse_en50494) linuxdvb_en50494_destroy(lse->lse_en50494); idnode_unlink(&lse->lse_id); idnode_set_free(lse->lse_networks); free(lse); } idnode_unlink(&ls->ls_id); free(ls); } /****************************************************************************** * DiseqC *****************************************************************************/ static const char * linuxdvb_diseqc_class_get_title ( idnode_t *o ) { linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o; return ld->ld_type; } static void linuxdvb_diseqc_class_save ( idnode_t *o ) { linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o; if (ld->ld_satconf) linuxdvb_satconf_ele_class_save(&ld->ld_satconf->lse_id); } const idclass_t linuxdvb_diseqc_class = { .ic_class = "linuxdvb_diseqc", .ic_caption = "DiseqC", .ic_event = "linuxdvb_diseqc", .ic_get_title = linuxdvb_diseqc_class_get_title, .ic_save = linuxdvb_diseqc_class_save, }; linuxdvb_diseqc_t * linuxdvb_diseqc_create0 ( linuxdvb_diseqc_t *ld, const char *uuid, const idclass_t *idc, htsmsg_t *conf, const char *type, linuxdvb_satconf_ele_t *parent ) { /* Insert */ if (idnode_insert(&ld->ld_id, uuid, idc, 0)) { free(ld); return NULL; } assert(type != NULL); ld->ld_type = strdup(type); ld->ld_satconf = parent; /* Load config */ if (conf) idnode_load(&ld->ld_id, conf); return ld; } void linuxdvb_diseqc_destroy ( linuxdvb_diseqc_t *ld ) { idnode_unlink(&ld->ld_id); free((void *)ld->ld_type); } int linuxdvb_diseqc_send (int fd, uint8_t framing, uint8_t addr, uint8_t cmd, uint8_t len, ...) { int i; va_list ap; struct dvb_diseqc_master_cmd message; #if ENABLE_TRACE char buf[256]; size_t c = 0; #endif /* Build message */ message.msg_len = len + 3; message.msg[0] = framing; message.msg[1] = addr; message.msg[2] = cmd; va_start(ap, len); for (i = 0; i < len; i++) { message.msg[3 + i] = (uint8_t)va_arg(ap, int); #if ENABLE_TRACE c += snprintf(buf + c, sizeof(buf) - c, "%02X ", message.msg[3 + i]); #endif } va_end(ap); tvhtrace("diseqc", "sending diseqc (len %d) %02X %02X %02X %s", len + 3, framing, addr, cmd, buf); /* Send */ if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &message)) { tvherror("diseqc", "failed to send diseqc cmd (e=%s)", strerror(errno)); return -1; } return 0; } int linuxdvb_diseqc_set_volt ( int fd, int vol ) { /* Set voltage */ tvhtrace("diseqc", "set voltage %dV", vol ? (vol < 0 ? 0 : 18) : 13); if (ioctl(fd, FE_SET_VOLTAGE, vol ? (vol < 0 ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_18) : SEC_VOLTAGE_13)) { tvherror("diseqc", "failed to set voltage (e=%s)", strerror(errno)); return -1; } if (vol >= 0) usleep(15000); return 0; } /****************************************************************************** * Editor Configuration * * vim:sts=2:ts=2:sw=2:et *****************************************************************************/