diff --git a/.gitignore b/.gitignore index 217c9961..949dbf66 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ debian/tvheadend debian/tvheadend-dbg debian/tvheadend*substvars debian/tvheadend*.debhelper* +/etc diff --git a/Makefile b/Makefile index 5d7bfb19..a2e5c65c 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ PROG := $(BUILDDIR)/tvheadend CFLAGS += -Wall -Werror -Wwrite-strings -Wno-deprecated-declarations CFLAGS += -Wmissing-prototypes -fms-extensions -CFLAGS += -g -funsigned-char -O2 +CFLAGS += -g -funsigned-char CFLAGS += -D_FILE_OFFSET_BITS=64 CFLAGS += -I${BUILDDIR} -I${ROOTDIR}/src -I${ROOTDIR} LDFLAGS += -lrt -ldl -lpthread -lm @@ -195,6 +195,7 @@ SRCS-${CONFIG_LINUXDVB} += \ src/input/mpegts/linuxdvb/linuxdvb_lnb.c \ src/input/mpegts/linuxdvb/linuxdvb_switch.c \ src/input/mpegts/linuxdvb/linuxdvb_rotor.c \ + src/input/mpegts/linuxdvb/linuxdvb_en50494.c \ src/input/mpegts/linuxdvb/scanfile.c \ # IPTV diff --git a/src/input/mpegts/linuxdvb/linuxdvb_en50494.c b/src/input/mpegts/linuxdvb/linuxdvb_en50494.c new file mode 100644 index 00000000..fe073707 --- /dev/null +++ b/src/input/mpegts/linuxdvb/linuxdvb_en50494.c @@ -0,0 +1,274 @@ +/* + * Tvheadend - Linux DVB EN50494 + * (known under trademark "UniCable") + * + * Copyright (C) 2013 Sascha "InuSasha" Kuehndel + * + * 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 . + * + * Open things: + * - make linuxdvb_en50494_tune thread safe + * avoiding self-raised collisions + * - collision dectection + * when a diseqc-command wasn't executed succesful, retry. + * delay time is easly random, but in standard is special (complicated) way described (cap 8). + */ + +#include "tvheadend.h" +#include "linuxdvb_private.h" +#include "settings.h" + +#include +#include + +/* ************************************************************************** + * Static definition + * *************************************************************************/ +#define LINUXDVB_EN50494_NAME "en50494" + +#define LINUXDVB_EN50494_NOPIN 256 + +#define LINUXDVB_EN50494_FRAME 0xE0 +/* adresses 0x00, 0x10 and 0x11 are possible */ +#define LINUXDVB_EN50494_ADDRESS 0x10 + +#define LINUXDVB_EN50494_CMD_NORMAL 0x5A +#define LINUXDVB_EN50494_CMD_NORMAL_MULTIHOME 0x5C +/* special modes not implemented yet */ +#define LINUXDVB_EN50494_CMD_SPECIAL 0x5B +#define LINUXDVB_EN50494_CMD_SPECIAL_MULTIHOME 0x5D + +#define LINUXDVB_EN50494_SAT_A 0x00 +#define LINUXDVB_EN50494_SAT_B 0x01 + + +/* ************************************************************************** + * Class definition + * *************************************************************************/ + +typedef struct linuxdvb_en50494 +{ + linuxdvb_diseqc_t; + + /* en50494 configuration*/ + uint8_t le_position; /* satelitte A(0) or B(1) */ + uint16_t le_frequency; /* user band frequency in MHz */ + uint8_t le_id; /* user band id 0-7 */ + uint16_t le_pin; /* 0-255 or LINUXDVB_EN50494_NOPIN */ + + /* runtime */ + uint32_t (*lnb_freq)(linuxdvb_lnb_t*, linuxdvb_mux_t*); + +} linuxdvb_en50494_t; + +static const char * +linuxdvb_en50494_class_get_title ( idnode_t *o ) +{ + static char buf[256]; + linuxdvb_diseqc_t *ld = (linuxdvb_diseqc_t*)o; + snprintf(buf, sizeof(buf), "%s: %s", LINUXDVB_EN50494_NAME, ld->ld_type); + return buf; +} + +static htsmsg_t * +linuxdvb_en50494_class_position_list ( void *o ) +{ + htsmsg_t *m = htsmsg_create_list(); + htsmsg_add_u32(m, NULL, LINUXDVB_EN50494_SAT_A); + htsmsg_add_u32(m, NULL, LINUXDVB_EN50494_SAT_B); + return m; +} + +static htsmsg_t * +linuxdvb_en50494_class_id_list ( void *o ) +{ + uint32_t i; + + htsmsg_t *m = htsmsg_create_list(); + for (i = 0; i < 8; i++) { + htsmsg_add_u32(m, NULL, i); + } + return m; +} + +static htsmsg_t * +linuxdvb_en50494_class_pin_list ( void *o ) +{ + int32_t i; + + htsmsg_t *m = htsmsg_create_list(); + for (i = -1; i < 256; i++) { + htsmsg_add_s32(m, NULL, i); + } + return m; +} + +extern const idclass_t linuxdvb_diseqc_class; +const idclass_t linuxdvb_en50494_class = +{ + .ic_super = &linuxdvb_diseqc_class, + .ic_class = "linuxdvb_en50494", + .ic_caption = LINUXDVB_EN50494_NAME, + .ic_get_title = linuxdvb_en50494_class_get_title, + .ic_properties = (const property_t[]) { + { + .type = PT_INT, + .id = "position", + .name = "Position", + .off = offsetof(linuxdvb_en50494_t, le_position), + .list = linuxdvb_en50494_class_position_list + }, + { + .type = PT_INT, + .id = "frequency", + .name = "Frequency", + .off = offsetof(linuxdvb_en50494_t, le_frequency), + }, + { + .type = PT_INT, + .id = "id", + .name = "ID", + .off = offsetof(linuxdvb_en50494_t, le_id), + .list = linuxdvb_en50494_class_id_list + }, + { + .type = PT_INT, + .id = "pin", + .name = "Pin", + .off = offsetof(linuxdvb_en50494_t, le_pin), + .list = linuxdvb_en50494_class_pin_list + }, + {} + } +}; + + +/* ************************************************************************** + * Class methods + * *************************************************************************/ + +static int +linuxdvb_en50494_tune + ( linuxdvb_diseqc_t *ld, linuxdvb_mux_t *lm, linuxdvb_satconf_ele_t *sc, int fd ) +{ + int ret = 0; + linuxdvb_en50494_t *le = (linuxdvb_en50494_t*) ld; + linuxdvb_lnb_t *lnb = sc->ls_lnb; + + /* band & polarisation */ + uint8_t pol = lnb->lnb_pol(lnb, lm); + uint8_t band = lnb->lnb_band(lnb, lm); + uint32_t freq = lnb->lnb_freq(lnb, lm); + + /* transponder value - t*/ + uint16_t t = round((( (freq / 1000) + 2 + le->le_frequency) / 4) - 350); + if ( t > 1024) { + tvhlog(LOG_ERR, LINUXDVB_EN50494_NAME, "transponder value bigger then 1024"); + return -1; + } +// uint32_t tunefreq = (t + 350) * 4000 - freq; /* real used en50494 frequency */ + + /* 2 data fields (16bit) */ + uint8_t data1, data2; + data1 = le->le_id << 5; /* 3bit user-band */ + data1 |= le->le_position << 4; /* 1bit position (satelitte A(0)/B(0)) */ + data1 |= pol << 3; /* 1bit polarisation v(0)/h(1) */ + data1 |= band << 2; /* 1bit band lower(0)/upper(1) */ + data1 |= t >> 8; /* 2bit transponder value bit 1-2 */ + data2 = t & 0xFF; /* 8bit transponder value bit 3-10 */ + tvhlog(LOG_INFO, LINUXDVB_EN50494_NAME, + "lnb=%i, id=%i, freq=%i, pin=%i, v/h=%i, l/u=%i, f=%i, data=0x%02X%02X", + le->le_position, le->le_id, le->le_frequency, le->le_pin, pol, band, freq, data1, data2); + + /* use 18V */ + if (linuxdvb_diseqc_set_volt(fd, SEC_VOLTAGE_18)) { + tvhlog(LOG_ERR, LINUXDVB_EN50494_NAME, "error setting lnb voltage to 18V"); + return -1; + } + usleep(15000); /* standard: 4ms < x < 22ms */ + + /* send tune command (with/with pin) */ + if (le->le_pin != LINUXDVB_EN50494_NOPIN) { + ret = linuxdvb_diseqc_send(fd, + LINUXDVB_EN50494_FRAME, + LINUXDVB_EN50494_ADDRESS, + LINUXDVB_EN50494_CMD_NORMAL_MULTIHOME, + 3, + data1, data2, (uint8_t)le->le_pin); + } else { + ret = linuxdvb_diseqc_send(fd, + LINUXDVB_EN50494_FRAME, + LINUXDVB_EN50494_ADDRESS, + LINUXDVB_EN50494_CMD_NORMAL, + 2, + data1, data2); + } + if (ret != 0) { + tvhlog(LOG_ERR, LINUXDVB_EN50494_NAME, "error send tune command"); + return -1; + } + usleep(50000); /* standard: 2ms < x < 60ms */ + + /* return to 13V */ + if (linuxdvb_diseqc_set_volt(fd, SEC_VOLTAGE_13)) { + tvhlog(LOG_ERR, LINUXDVB_EN50494_NAME, "error setting lnb voltage back to 13V"); + return -1; + } + + return 0; +} + + +/* ************************************************************************** + * Create / Config + * *************************************************************************/ + +htsmsg_t * +linuxdvb_en50494_list ( void *o ) +{ + htsmsg_t *m = htsmsg_create_list(); + htsmsg_add_str(m, NULL, "None"); + htsmsg_add_str(m, NULL, "EN50494/UniCable"); + return m; +} + +linuxdvb_diseqc_t * +linuxdvb_en50494_create0 + ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls) +{ + linuxdvb_diseqc_t *ld; +// linuxdvb_en50494_t *le; + + ld = linuxdvb_diseqc_create0( + calloc(1, sizeof(linuxdvb_en50494_t)), + NULL, + &linuxdvb_en50494_class, + conf, + LINUXDVB_EN50494_NAME, + ls); +// le = (linuxdvb_en50494_t*)ld; + if (ld) { + ld->ld_tune = linuxdvb_en50494_tune; + /* May not needed: ld->ld_grace = linuxdvb_en50494_grace; */ + } + + return (linuxdvb_diseqc_t*)ld; +} + +void +linuxdvb_en50494_destroy ( linuxdvb_diseqc_t *le ) +{ + linuxdvb_diseqc_destroy(le); + free(le); +} diff --git a/src/input/mpegts/linuxdvb/linuxdvb_private.h b/src/input/mpegts/linuxdvb/linuxdvb_private.h index 06a44fff..35683d78 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_private.h +++ b/src/input/mpegts/linuxdvb/linuxdvb_private.h @@ -199,6 +199,7 @@ struct linuxdvb_satconf_ele linuxdvb_lnb_t *ls_lnb; linuxdvb_diseqc_t *ls_switch; linuxdvb_diseqc_t *ls_rotor; + linuxdvb_diseqc_t *ls_en50494; }; struct linuxdvb_diseqc @@ -329,14 +330,18 @@ linuxdvb_diseqc_t *linuxdvb_switch_create0 ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls, int u, int c ); linuxdvb_diseqc_t *linuxdvb_rotor_create0 ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls ); +linuxdvb_diseqc_t *linuxdvb_en50494_create0 + ( const char *name, htsmsg_t *conf, linuxdvb_satconf_ele_t *ls ); -void linuxdvb_lnb_destroy ( linuxdvb_lnb_t *lnb ); -void linuxdvb_switch_destroy ( linuxdvb_diseqc_t *ld ); -void linuxdvb_rotor_destroy ( linuxdvb_diseqc_t *ld ); +void linuxdvb_lnb_destroy ( linuxdvb_lnb_t *lnb ); +void linuxdvb_switch_destroy ( linuxdvb_diseqc_t *ld ); +void linuxdvb_rotor_destroy ( linuxdvb_diseqc_t *ld ); +void linuxdvb_en50494_destroy ( linuxdvb_diseqc_t *ld ); -htsmsg_t *linuxdvb_lnb_list ( void *o ); -htsmsg_t *linuxdvb_switch_list ( void *o ); -htsmsg_t *linuxdvb_rotor_list ( void *o ); +htsmsg_t *linuxdvb_lnb_list ( void *o ); +htsmsg_t *linuxdvb_switch_list ( void *o ); +htsmsg_t *linuxdvb_rotor_list ( void *o ); +htsmsg_t *linuxdvb_en50494_list ( void *o ); int linuxdvb_diseqc_send diff --git a/src/input/mpegts/linuxdvb/linuxdvb_satconf.c b/src/input/mpegts/linuxdvb/linuxdvb_satconf.c index 99fbaeed..de1b1bbd 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_satconf.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_satconf.c @@ -209,7 +209,7 @@ const idclass_t linuxdvb_satconf_lnbonly_class = .type = PT_STR, .id = "network", .name = "Network", - .get = linuxdvb_satconf_class_network_get0, + .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -231,7 +231,7 @@ const idclass_t linuxdvb_satconf_2port_class = .type = PT_STR, .id = "network_a", .name = "A", - .get = linuxdvb_satconf_class_network_get0, + .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -240,7 +240,7 @@ const idclass_t linuxdvb_satconf_2port_class = .type = PT_STR, .id = "network_b", .name = "B", - .get = linuxdvb_satconf_class_network_get1, + .get = linuxdvb_satconf_class_network_get1, .set = linuxdvb_satconf_class_network_set1, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -262,7 +262,7 @@ const idclass_t linuxdvb_satconf_4port_class = .type = PT_STR, .id = "network_aa", .name = "AA", - .get = linuxdvb_satconf_class_network_get0, + .get = linuxdvb_satconf_class_network_get0, .set = linuxdvb_satconf_class_network_set0, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -271,7 +271,7 @@ const idclass_t linuxdvb_satconf_4port_class = .type = PT_STR, .id = "network_ab", .name = "AB", - .get = linuxdvb_satconf_class_network_get1, + .get = linuxdvb_satconf_class_network_get1, .set = linuxdvb_satconf_class_network_set1, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -280,7 +280,7 @@ const idclass_t linuxdvb_satconf_4port_class = .type = PT_STR, .id = "network_ba", .name = "BA", - .get = linuxdvb_satconf_class_network_get2, + .get = linuxdvb_satconf_class_network_get2, .set = linuxdvb_satconf_class_network_set2, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -289,7 +289,7 @@ const idclass_t linuxdvb_satconf_4port_class = .type = PT_STR, .id = "network_bb", .name = "BB", - .get = linuxdvb_satconf_class_network_get3, + .get = linuxdvb_satconf_class_network_get3, .set = linuxdvb_satconf_class_network_set3, .list = linuxdvb_satconf_ele_class_network_enum, .opts = PO_NOSAVE, @@ -298,6 +298,37 @@ const idclass_t linuxdvb_satconf_4port_class = } }; +/* + * en50494 + */ +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_STR, + .id = "network_a", + .name = "Network A", + .get = linuxdvb_satconf_class_network_get0, + .set = linuxdvb_satconf_class_network_set0, + .list = linuxdvb_satconf_ele_class_network_enum, + .opts = PO_NOSAVE, + }, + { + .type = PT_STR, + .id = "network_b", + .name = "Netwotk B", + .get = linuxdvb_satconf_class_network_get1, + .set = linuxdvb_satconf_class_network_set1, + .list = linuxdvb_satconf_ele_class_network_enum, + .opts = PO_NOSAVE, + }, + {} + } +}; + /* * Advanced */ @@ -344,6 +375,12 @@ static struct linuxdvb_satconf_type linuxdvb_satconf_types[] = { .idc = &linuxdvb_satconf_4port_class, .ports = 4, }, + { + .type = "en50494", + .name = "EN50494/UniCable Switch (Universal LNB)", + .idc = &linuxdvb_satconf_en50494_class, + .ports = 2, + }, { .type = "advanced", .name = "Advanced", @@ -566,6 +603,26 @@ linuxdvb_satconf_ele_class_lnbtype_get ( void *o ) 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->ls_en50494) + linuxdvb_en50494_destroy(ls->ls_en50494); + ls->ls_en50494 = linuxdvb_en50494_create0(str, NULL, ls); + 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->ls_en50494 ? ls->ls_en50494->ld_type : NULL; + return &s; +} + static int linuxdvb_satconf_ele_class_switchtype_set ( void *o, const void *p ) { @@ -631,6 +688,8 @@ linuxdvb_satconf_ele_class_get_childs ( idnode_t *o ) idnode_set_add(is, &ls->ls_switch->ld_id, NULL); if (ls->ls_rotor) idnode_set_add(is, &ls->ls_rotor->ld_id, NULL); + if (ls->ls_en50494) + idnode_set_add(is, &ls->ls_en50494->ld_id, NULL); return is; } @@ -661,8 +720,8 @@ const idclass_t linuxdvb_satconf_ele_class = .type = PT_STR, .id = "network", .name = "Network", - .get = linuxdvb_satconf_ele_class_network_get, - .set = linuxdvb_satconf_ele_class_network_set, + .get = linuxdvb_satconf_ele_class_network_get, + .set = linuxdvb_satconf_ele_class_network_set, .list = linuxdvb_satconf_ele_class_network_enum, .rend = linuxdvb_satconf_ele_class_network_rend, }, @@ -693,6 +752,15 @@ const idclass_t linuxdvb_satconf_ele_class = .list = linuxdvb_rotor_list, .def.s = "None", }, + { + .type = PT_STR, + .id = "en50494_type", + .name = "EN50494 Type", + .set = linuxdvb_satconf_ele_class_en50494type_set, + .get = linuxdvb_satconf_ele_class_en50494type_get, + .list = linuxdvb_en50494_list, + .def.s = "None", + }, {} } }; @@ -760,6 +828,7 @@ linuxdvb_satconf_ele_tune ( linuxdvb_satconf_ele_t *lse ) lse->ls_rotor ? (linuxdvb_diseqc_t*)lse->ls_switch : NULL, (linuxdvb_diseqc_t*)lse->ls_rotor, (linuxdvb_diseqc_t*)lse->ls_switch, + (linuxdvb_diseqc_t*)lse->ls_en50494, (linuxdvb_diseqc_t*)lse->ls_lnb }; // TODO: really need to understand whether or not we need to pre configure @@ -798,7 +867,8 @@ linuxdvb_satconf_ele_tune ( linuxdvb_satconf_ele_t *lse ) usleep(20000); // Allow LNB to settle before tuning /* Frontend */ - f = lse->ls_lnb->lnb_freq(lse->ls_lnb, lm); + // TODO: get en50494 tuning frequency, not channel frequency + f = 2040500;// lse->ls_lnb->lnb_freq(lse->ls_lnb, lm); return linuxdvb_frontend_tune1(lfe, mmi, f); } @@ -891,6 +961,7 @@ linuxdvb_satconf_ele_get_grace linuxdvb_satconf_ele_t *lse = (linuxdvb_satconf_ele_t*)mi; linuxdvb_satconf_t *ls = lse->ls_parent; linuxdvb_diseqc_t *lds[] = { + (linuxdvb_diseqc_t*)lse->ls_en50494, (linuxdvb_diseqc_t*)lse->ls_switch, (linuxdvb_diseqc_t*)lse->ls_rotor, (linuxdvb_diseqc_t*)lse->ls_lnb @@ -928,9 +999,10 @@ void linuxdvb_satconf_ele_destroy ( linuxdvb_satconf_ele_t *ls ) { TAILQ_REMOVE(&ls->ls_parent->ls_elements, ls, ls_link); - if (ls->ls_lnb) linuxdvb_lnb_destroy(ls->ls_lnb); - if (ls->ls_switch) linuxdvb_switch_destroy(ls->ls_switch); - if (ls->ls_rotor) linuxdvb_rotor_destroy(ls->ls_rotor); + if (ls->ls_lnb) linuxdvb_lnb_destroy(ls->ls_lnb); + if (ls->ls_switch) linuxdvb_switch_destroy(ls->ls_switch); + if (ls->ls_rotor) linuxdvb_rotor_destroy(ls->ls_rotor); + if (ls->ls_en50494) linuxdvb_switch_destroy(ls->ls_en50494); mpegts_input_delete((mpegts_input_t*)ls); } @@ -974,7 +1046,11 @@ linuxdvb_satconf_ele_create0 /* Rotor */ if (lse->ls_rotor && (e = htsmsg_get_map(conf, "rotor_conf"))) idnode_load(&lse->ls_rotor->ld_id, e); - } + + /* EN50494 */ + if (lse->ls_en50494 && (e = htsmsg_get_map(conf, "en50494_conf"))) + idnode_load(&lse->ls_en50494->ld_id, e); +} /* Create default LNB */ if (!lse->ls_lnb) @@ -995,6 +1071,8 @@ linuxdvb_satconf_delete ( linuxdvb_satconf_t *ls ) linuxdvb_switch_destroy(ls->ls_switch); if (ls->ls_rotor) linuxdvb_rotor_destroy(ls->ls_rotor); + if (ls->ls_en50494) + linuxdvb_en50494_destroy(ls->ls_en50494); mpegts_input_delete((mpegts_input_t*)ls); #endif }