From 9dbd0bb545d82e6328e148d75c59ffb440f7fbbe Mon Sep 17 00:00:00 2001 From: CrazyCat Date: Mon, 12 Oct 2020 23:02:17 +0300 Subject: [PATCH] Internal szap-s2, femon-s2 utils. --- femon-s2/Makefile | 26 + femon-s2/README | 34 ++ femon-s2/femon-s2.c | 164 +++++++ szap-s2/Makefile | 37 ++ szap-s2/README | 62 +++ szap-s2/lnb.c | 101 ++++ szap-s2/lnb.h | 24 + szap-s2/szap-s2.c | 1127 +++++++++++++++++++++++++++++++++++++++++++ szap-s2/tzap-t2.c | 1089 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 2664 insertions(+) create mode 100644 femon-s2/Makefile create mode 100644 femon-s2/README create mode 100644 femon-s2/femon-s2.c create mode 100644 szap-s2/Makefile create mode 100644 szap-s2/README create mode 100644 szap-s2/lnb.c create mode 100644 szap-s2/lnb.h create mode 100644 szap-s2/szap-s2.c create mode 100644 szap-s2/tzap-t2.c diff --git a/femon-s2/Makefile b/femon-s2/Makefile new file mode 100644 index 0000000..001f6da --- /dev/null +++ b/femon-s2/Makefile @@ -0,0 +1,26 @@ +CC=gcc + +SRC=femon-s2.c +OBJ=femon-s2.o + +BIND=/usr/local/bin/ +INCLUDE=-I ../linux/include/uapi + +TARGET=femon-s2 + +all: $(TARGET) $(TARGET) + +$(TARGET): $(OBJ) + $(CC) $(CFLG) $(OBJ) -o $(TARGET) $(CLIB) + +install: all + cp $(TARGET) $(BIND) + +uninstall: + rm $(BIND)$(TARGET) + +clean: + rm -f $(OBJ) $(TARGET) *~ + +%.o: %.c + $(CC) $(INCLUDE) -c $< -o $@ diff --git a/femon-s2/README b/femon-s2/README new file mode 100644 index 0000000..9c34067 --- /dev/null +++ b/femon-s2/README @@ -0,0 +1,34 @@ +femon -- simple frontend monitoring tool for the Linux DVB. + +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., 675 Mass Ave, Cambridge, MA 02139, USA. + +femon requires Linux DVB driver API version >= 5.0. + +If compiler not found DVB headers in ../s2/linux/include/linux/dvb, +then it looks at usual location /usr/include/linux/dvb. + +Install as follows: + +(Option) make + make install +Uninstall + make uninstall + +usage: femon [options] + -H : human readable output + -a number : use given adapter (default 0) + -f number : use given frontend (default 0) + -c number : samples to take (default 0 = infinite) + +Igor M. Liplianin (liplianin@me.by) diff --git a/femon-s2/femon-s2.c b/femon-s2/femon-s2.c new file mode 100644 index 0000000..b082784 --- /dev/null +++ b/femon-s2/femon-s2.c @@ -0,0 +1,164 @@ +/* femon-s2 -- monitor frontend status + * + * Copyright (C) 2003 convergence GmbH + * Johannes Stezenbach + * + * Copyright (C) 2009 Igor M. Liplianin + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * femon 2011/06/16 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#if DVB_API_VERSION < 5 +#error femon-s2 requires Linux DVB driver API version >= 5.0! +#endif + +#define FRONTENDDEVICE "/dev/dvb/adapter%d/frontend%d" + +static char *usage_str = + "\nusage: femon-s2 [options]\n" + " -H : human readable output\n" + " -a number : use given adapter (default 0)\n" + " -f number : use given frontend (default 0)\n" + " -c number : samples to take (default 0 = infinite)\n\n"; + + +static void usage(void) +{ + fprintf(stderr, usage_str); + exit(1); +} + +static +int check_frontend (int fe_fd, int human_readable, unsigned int count) +{ + fe_status_t status; + uint16_t snr = 10, signal = 10; + uint32_t ber = 1, uncorrected_blocks =1; + int16_t temp = 0; + int timeout = 0; + unsigned int samples = 0; + + do { + if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1) + perror("FE_READ_STATUS failed"); + /* some frontends might not support all these ioctls, thus we + * avoid printing errors + */ + if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1) + signal = -2; + if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1) + snr = -2; + if (ioctl(fe_fd, FE_READ_BER, &ber) == -1) + ber = -2; + if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1) + uncorrected_blocks = -2; + if (ioctl(fe_fd, FE_READ_TEMP, &temp) == -1) + ber = -2; + + if (human_readable) { + printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | temp %d | ", + status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks, temp); + } else { + printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | temp %d | ", + status, signal, snr, ber, uncorrected_blocks, temp); + } + if (status & FE_HAS_LOCK) + printf("FE_HAS_LOCK"); + printf("\n"); + + usleep(1000000); + samples++; + } while ((!count) || (count-samples)); + + return 0; +} + +static +int do_mon(unsigned int adapter, unsigned int frontend, int human_readable, unsigned int count) +{ + int result; + static int fefd; + char fedev[128]; + + if (!fefd) { + snprintf(fedev, sizeof(fedev), FRONTENDDEVICE, adapter, frontend); + + if ((fefd = open(fedev, O_RDONLY | O_NONBLOCK)) < 0) { + perror("opening frontend failed"); + return 0; + } + } + + /* TODO here we may put frontend description */ + + result = check_frontend (fefd, human_readable, count); + + close(fefd); + + return result; +} + +int main(int argc, char *argv[]) +{ + unsigned int adapter = 0, frontend = 0, count = 0; + int human_readable = 0; + int opt; + + while ((opt = getopt(argc, argv, "Ha:f:c:")) != -1) { + switch (opt) + { + default: + usage(); + break; + case 'a': + adapter = strtoul(optarg, NULL, 0); + break; + case 'c': + count = strtoul(optarg, NULL, 0); + break; + case 'f': + frontend = strtoul(optarg, NULL, 0); + break; + case 'H': + human_readable = 1; + break; + } + } + + do_mon(adapter, frontend, human_readable, count); + + return 0; +} diff --git a/szap-s2/Makefile b/szap-s2/Makefile new file mode 100644 index 0000000..0a86ea1 --- /dev/null +++ b/szap-s2/Makefile @@ -0,0 +1,37 @@ +CC=gcc + +SRC=lnb.c szap-s2.c tzap-t2.c +HED=lnb.h +OBJ1=lnb.o szap-s2.o +OBJ2=tzap-t2.o lnb.o + +BIND=/usr/local/bin/ +INCLUDE=-I ../linux/include/uapi + +TARGET1=szap-s2 +TARGET2=tzap-t2 + +all: $(TARGET1) $(TARGET2) + +$(TARGET1): $(OBJ1) + $(CC) $(CFLG) $(OBJ1) -o $(TARGET1) $(CLIB) + +$(TARGET2): $(OBJ2) + $(CC) $(CFLG) $(OBJ2) -o $(TARGET2) $(CLIB) + +$(OBJ): $(HED) + +install: all + cp $(TARGET1) $(BIND) + cp $(TARGET2) $(BIND) + +uninstall: + rm $(BIND)$(TARGET1) + rm $(BIND)$(TARGET2) + +clean: + rm -f $(OBJ1) $(TARGET1) *~ + rm -f $(OBJ2) $(TARGET2) *~ + +%.o: %.c + $(CC) $(INCLUDE) -c $< -o $@ diff --git a/szap-s2/README b/szap-s2/README new file mode 100644 index 0000000..ea1c952 --- /dev/null +++ b/szap-s2/README @@ -0,0 +1,62 @@ +szap-s2 -- simple zapping tool for the Linux DVB S2 API + +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., 675 Mass Ave, Cambridge, MA 02139, USA. + +szap-s2 requires Linux DVB driver API version 5.0. + +If compiler not found DVB headers in ../s2/linux/include/linux/dvb, +then it looks at usual location /usr/include/linux/dvb. + +Install as follows: + +(Option) make + make install +Uninstall + make uninstall + +location of channel list file is ~/.szap/channels.conf + +one line of the szap channel file has the following format: +name:frequency_MHz:polarization[coderate][delivery][modulation][rolloff]:sat_no:symbolrate:vpid:apid:service_id +one line of the VDR channel file has the following format: +name:frequency_MHz:polarization[coderate][delivery][modulation][rolloff]:sat_no:symbolrate:vpid:apid:?:?:service_id:?:?:? + +usage: +szap -q + list known channels +szap [options] {-n channel-number|channel_name} + zap to channel via number or full name (case insensitive) + -a number : use given adapter (default 0) + -f number : use given frontend (default 0) + -d number : use given demux (default 0) + -c file : read channels list from 'file' + -V : use vdr channels list file format (default zap) + -b : enable Audio Bypass (default no) + -x : exit after tuning + -H : human readable output + -D : params debug + -r : set up /dev/dvb/adapterX/dvr0 for TS recording + -l lnb-type (DVB-S Only) (use -l help to print types) or + -l low[,high[,switch]] in Mhz + -i : run interactively, allowing you to type in channel names + -p : add pat and pmt to TS recording (implies -r) + or -n numbers for zapping + -S : delivery system type DVB-S=0, DVB-S2=1 + -M : modulation 1=BPSK 2=QPSK 5=8PSK + -C : fec 0=NONE 12=1/2 23=2/3 34=3/4 35=3/5 45=4/5 56=5/6 67=6/7 89=8/9 910=9/10 999=AUTO + -O : rolloff 35=0.35 25=0.25 20=0.20 0=UNKNOWN + -m : input stream 0-255 + + +Igor M. Liplianin (liplianin@me.by) diff --git a/szap-s2/lnb.c b/szap-s2/lnb.c new file mode 100644 index 0000000..9052d1c --- /dev/null +++ b/szap-s2/lnb.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include "lnb.h" + +static char *univ_desc[] = { + "Europe", + "10800 to 11800 MHz and 11600 to 12700 Mhz", + "Dual LO, loband 9750, hiband 10600 MHz", + (char *)NULL }; + +static char *dbs_desc[] = { + "Expressvu, North America", + "12200 to 12700 MHz", + "Single LO, 11250 MHz", + (char *)NULL }; + +static char *standard_desc[] = { + "10945 to 11450 Mhz", + "Single LO, 10000 Mhz", + (char *)NULL }; + +static char *enhan_desc[] = { + "Astra", + "10700 to 11700 MHz", + "Single LO, 9750 MHz", + (char *)NULL }; + +static char *cband_desc[] = { + "Big Dish", + "3700 to 4200 MHz", + "Single LO, 5150 Mhz", + (char *)NULL }; + +static struct lnb_types_st lnbs[] = { + {"UNIVERSAL", univ_desc, 9750, 10600, 11700 }, + {"DBS", dbs_desc, 11250, 0, 0 }, + {"STANDARD", standard_desc, 10000, 0, 0 }, + {"ENHANCED", enhan_desc, 9750, 0, 0 }, + {"C-BAND", cband_desc, 5150, 0, 0 } +}; + +/* Enumerate through standard types of LNB's until NULL returned. + * Increment curno each time + */ + +struct lnb_types_st * +lnb_enum(int curno) +{ + if (curno >= (int) (sizeof(lnbs) / sizeof(lnbs[0]))) + return (struct lnb_types_st *)NULL; + return &lnbs[curno]; +} + +/* Decode an lnb type, for example given on a command line + * If alpha and standard type, e.g. "Universal" then match that + * otherwise low[,high[,switch]] + */ + +int +lnb_decode(char *str, struct lnb_types_st *lnbp) +{ +int i; +char *cp, *np; + + memset(lnbp, 0, sizeof(*lnbp)); + cp = str; + while(*cp && isspace(*cp)) + cp++; + if (isalpha(*cp)) { + for (i = 0; i < (int)(sizeof(lnbs) / sizeof(lnbs[0])); i++) { + if (!strcasecmp(lnbs[i].name, cp)) { + *lnbp = lnbs[i]; + return 1; + } + } + return -1; + } + if (*cp == '\0' || !isdigit(*cp)) + return -1; + lnbp->low_val = strtoul(cp, &np, 0); + if (lnbp->low_val == 0) + return -1; + cp = np; + while(*cp && (isspace(*cp) || *cp == ',')) + cp++; + if (*cp == '\0') + return 1; + if (!isdigit(*cp)) + return -1; + lnbp->high_val = strtoul(cp, &np, 0); + cp = np; + while(*cp && (isspace(*cp) || *cp == ',')) + cp++; + if (*cp == '\0') + return 1; + if (!isdigit(*cp)) + return -1; + lnbp->switch_val = strtoul(cp, NULL, 0); + return 1; +} diff --git a/szap-s2/lnb.h b/szap-s2/lnb.h new file mode 100644 index 0000000..f78b7a6 --- /dev/null +++ b/szap-s2/lnb.h @@ -0,0 +1,24 @@ + +struct lnb_types_st { + char *name; + char **desc; + unsigned long low_val; + unsigned long high_val; /* zero indicates no hiband */ + unsigned long switch_val; /* zero indicates no hiband */ +}; + +/* Enumerate through standard types of LNB's until NULL returned. + * Increment curno each time + */ + +struct lnb_types_st * +lnb_enum(int curno); + +/* Decode an lnb type, for example given on a command line + * If alpha and standard type, e.g. "Universal" then match that + * otherwise low[,high[,switch]] + */ + +int +lnb_decode(char *str, struct lnb_types_st *lnbp); + diff --git a/szap-s2/szap-s2.c b/szap-s2/szap-s2.c new file mode 100644 index 0000000..de11439 --- /dev/null +++ b/szap-s2/szap-s2.c @@ -0,0 +1,1127 @@ +/* szap-s2 -- simple zapping tool for the Linux DVB S2 API + * + * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) + * Copyright (C) 2013 CrazyCat (crazycat69@narod.ru) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "lnb.h" + +#if DVB_API_VERSION < 5 || DVB_API_VERSION_MINOR < 2 +#error szap-s2 requires Linux DVB driver API version 5.2 and newer! +#endif + +#if 0 +#ifndef DTV_STREAM_ID + #define DTV_STREAM_ID 42 +#endif + +#ifndef DTV_MODCODE + #define DTV_MODCODE 43 +#endif + +#ifndef NO_STREAM_ID_FILTER + #define NO_STREAM_ID_FILTER (~0U) +#endif + +#ifndef MODCODE_ALL + #define MODCODE_ALL (~0U) +#endif +#endif + +#ifndef TRUE +#define TRUE (1==1) +#endif +#ifndef FALSE +#define FALSE (1==0) +#endif + +/* location of channel list file */ +#define CHANNEL_FILE "channels.conf" + +/* one line of the szap channel file has the following format: + * ^name:frequency_MHz:polarization:sat_no:symbolrate:vpid:apid:service_id$ + * one line of the VDR channel file has the following format: + * ^name:frequency_MHz:polarization[coderate][delivery][modulation][rolloff]:sat_no:symbolrate:vpid:apid:?:?:service_id:?:?:?$ + */ + + +#define FRONTENDDEVICE "/dev/dvb/adapter%d/frontend%d" +#define DEMUXDEVICE "/dev/dvb/adapter%d/demux%d" +#define AUDIODEVICE "/dev/dvb/adapter%d/audio%d" + +struct t_channel_parameter_map { + int user_value; + int driver_value; + const char *user_string; + }; +/* --- Channel Parameter Maps From VDR---*/ + +static struct t_channel_parameter_map inversion_values[] = { + { 0, INVERSION_OFF, "off" }, + { 1, INVERSION_ON, "on" }, + { 999, INVERSION_AUTO }, + { -1 } + }; + +static struct t_channel_parameter_map coderate_values[] = { + { 0, FEC_NONE, "none" }, + { 12, FEC_1_2, "1/2" }, +// { 13, FEC_1_3, "1/3" }, +// { 14, FEC_1_4, "1/4" }, + { 23, FEC_2_3, "2/3" }, +// { 25, FEC_2_5, "2/5" }, + { 34, FEC_3_4, "3/4" }, + { 35, FEC_3_5, "3/5" }, + { 45, FEC_4_5, "4/5" }, + { 56, FEC_5_6, "5/6" }, + { 67, FEC_6_7, "6/7" }, + { 78, FEC_7_8, "7/8" }, + { 89, FEC_8_9, "8/9" }, + { 910, FEC_9_10, "9/10" }, + { 999, FEC_AUTO, "auto" }, + { -1 } + }; + +static struct t_channel_parameter_map modulation_values[] = { + // { 0, NONE, "none" }, + // { 4, QAM_4, "QAM4" }, + { 16, QAM_16, "QAM16" }, + { 32, QAM_32, "QAM32" }, + { 64, QAM_64, "QAM64" }, + { 128, QAM_128, "QAM128" }, + { 256, QAM_256, "QAM256" }, +// { 512, QAM_512, "QAM512" }, +// {1024, QAM_1024, "QAM1024" }, +// { 1, BPSK, "BPSK" }, + { 2, QPSK, "QPSK" }, +// { 3, OQPSK, "OQPSK" }, + { 5, PSK_8, "8PSK" }, + { 6, APSK_16, "16APSK" }, + { 7, APSK_32, "32APSK" }, +// { 8, OFDM, "OFDM" }, +// { 9, COFDM, "COFDM" }, + { 10, VSB_8, "VSB8" }, + { 11, VSB_16, "VSB16" }, + { 998, QAM_AUTO, "QAMAUTO" }, +// { 999, AUTO }, + { -1 } + }; + +static struct t_channel_parameter_map system_values[] = { + { 0, SYS_DVBS, "DVB-S" }, + { 1, SYS_DVBS2, "DVB-S2" }, + { -1 } + }; + + +static struct t_channel_parameter_map rolloff_values[] = { + // { 0, ROLLOFF_AUTO, "auto"}, + { 20, ROLLOFF_20, "0.20" }, + { 25, ROLLOFF_25, "0.25" }, + { 35, ROLLOFF_35, "0.35" }, + { -1 } + }; + +static int user_index(int value, const struct t_channel_parameter_map * map) +{ + const struct t_channel_parameter_map *umap = map; + while (umap && umap->user_value != -1) { + if (umap->user_value == value) + return umap - map; + umap++; + } + return -1; +}; + +static int driver_index(int value, const struct t_channel_parameter_map *map) +{ + const struct t_channel_parameter_map *umap = map; + while (umap && umap->user_value != -1) { + if (umap->driver_value == value) + return umap - map; + umap++; + } + return -1; +}; + +static int map_to_user(int value, const struct t_channel_parameter_map *map, char **string) +{ + int n = driver_index(value, map); + if (n >= 0) { + if (string) + *string = (char *)map[n].user_string; + return map[n].user_value; + } + return -1; +} + +static int map_to_driver(int value, const struct t_channel_parameter_map *map) +{ + int n = user_index(value, map); + if (n >= 0) + return map[n].driver_value; + return -1; +} + +static struct lnb_types_st lnb_type; + +static int exit_after_tuning; +static int interactive; + +static char *usage_str = + "\nusage: szap-s2 -q\n" + " list known channels\n" + " szap-s2 [options] {-n channel-number|channel_name}\n" + " zap to channel via number or full name (case insensitive)\n" + " -a number : use given adapter (default 0)\n" + " -f number : use given frontend (default 0)\n" + " -d number : use given demux (default 0)\n" + " -c file : read channels list from 'file'\n" + " -V : use VDR channels list file format (default zap)\n" + " -b : enable Audio Bypass (default no)\n" + " -x : exit after tuning\n" + " -H : human readable output\n" + " -D : params debug\n" + " -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n" + " -l lnb-type (DVB-S Only) (use -l help to print types) or \n" + " -l low[,high[,switch]] in Mhz\n" + " -i : run interactively, allowing you to type in channel names\n" + " -p : add pat and pmt to TS recording (implies -r)\n" + " or -n numbers for zapping\n" + " -t : add teletext to TS recording (needs -V)\n" + " -S : delivery system type DVB-S=0, DVB-S2=1\n" + " -M : modulation 1=BPSK 2=QPSK 5=8PSK\n" + " -C : fec 0=NONE 12=1/2 23=2/3 34=3/4 35=3/5 45=4/5 56=5/6 67=6/7 89=8/9 910=9/10 999=AUTO\n" + " -O : rolloff 35=0.35 25=0.25 20=0.20 0=UNKNOWN\n" + " -m : modcode mask\n" + " -s : input stream 0-255\n"; + +static int set_demux(int dmxfd, int pid, int pes_type, int dvr) +{ + struct dmx_pes_filter_params pesfilter; + + if (pid < 0 || pid >= 0x1fff) /* ignore this pid to allow radio services */ + return TRUE; + + if (dvr) { + int buffersize = 64 * 1024; + if (ioctl(dmxfd, DMX_SET_BUFFER_SIZE, buffersize) == -1) + perror("DMX_SET_BUFFER_SIZE failed"); + } + + pesfilter.pid = pid; + pesfilter.input = DMX_IN_FRONTEND; + pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER; + pesfilter.pes_type = pes_type; + pesfilter.flags = DMX_IMMEDIATE_START; + + if (ioctl(dmxfd, DMX_SET_PES_FILTER, &pesfilter) == -1) { + fprintf(stderr, "DMX_SET_PES_FILTER failed " + "(PID = 0x%04x): %d %m\n", pid, errno); + + return FALSE; + } + + return TRUE; +} + +int get_pmt_pid(char *dmxdev, int sid) +{ + int patfd, count; + int pmt_pid = 0; + int patread = 0; + int section_length; + unsigned char buft[4096]; + unsigned char *buf = buft; + struct dmx_sct_filter_params f; + + memset(&f, 0, sizeof(f)); + f.pid = 0; + f.filter.filter[0] = 0x00; + f.filter.mask[0] = 0xff; + f.timeout = 0; + f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; + + if ((patfd = open(dmxdev, O_RDWR)) < 0) { + perror("openening pat demux failed"); + return -1; + } + + if (ioctl(patfd, DMX_SET_FILTER, &f) == -1) { + perror("ioctl DMX_SET_FILTER failed"); + close(patfd); + return -1; + } + + while (!patread) { + if (((count = read(patfd, buf, sizeof(buft))) < 0) && errno == EOVERFLOW) + count = read(patfd, buf, sizeof(buft)); + if (count < 0) { + perror("read_sections: read error"); + close(patfd); + return -1; + } + + section_length = ((buf[1] & 0x0f) << 8) | buf[2]; + if (count != section_length + 3) + continue; + + buf += 8; + section_length -= 8; + + patread = 1; /* assumes one section contains the whole pat */ + while (section_length > 0) { + int service_id = (buf[0] << 8) | buf[1]; + if (service_id == sid) { + pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3]; + section_length = 0; + } + buf += 4; + section_length -= 4; + } + } + close(patfd); + return pmt_pid; +} + +struct diseqc_cmd { + struct dvb_diseqc_master_cmd cmd; + uint32_t wait; +}; + +void diseqc_send_msg(int fd, fe_sec_voltage_t v, struct diseqc_cmd *cmd, + fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b) +{ + if (ioctl(fd, FE_SET_TONE, SEC_TONE_OFF) == -1) + perror("FE_SET_TONE failed"); + if (ioctl(fd, FE_SET_VOLTAGE, v) == -1) + perror("FE_SET_VOLTAGE failed"); + usleep(15 * 1000); + if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) == -1) + perror("FE_DISEQC_SEND_MASTER_CMD failed"); + usleep(cmd->wait * 1000); + usleep(15 * 1000); + if (ioctl(fd, FE_DISEQC_SEND_BURST, b) == -1) + perror("FE_DISEQC_SEND_BURST failed"); + usleep(15 * 1000); + if (ioctl(fd, FE_SET_TONE, t) == -1) + perror("FE_SET_TONE failed"); + +} + + + + +/* digital satellite equipment control, + * specification is available from http://www.eutelsat.com/ + */ +static int diseqc(int secfd, int sat_no, int pol_vert, int hi_band) +{ + struct diseqc_cmd cmd = + { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 }; + + /** + * param: high nibble: reset bits, low nibble set bits, + * bits are: option, position, polarizaion, band + */ + cmd.cmd.msg[3] = + 0xf0 | (((sat_no * 4) & 0x0f) | (hi_band ? 1 : 0) | (pol_vert ? 0 : 2)); + + diseqc_send_msg(secfd, pol_vert ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18, + &cmd, hi_band ? SEC_TONE_ON : SEC_TONE_OFF, + (sat_no / 4) % 2 ? SEC_MINI_B : SEC_MINI_A); + + return TRUE; +} + +static int do_tune(int fefd, unsigned int ifreq, unsigned int sr, enum fe_delivery_system delsys, + int modulation, int fec, int rolloff, int stream_id, unsigned int modcode) +{ + struct dvb_frontend_event ev; + struct dtv_property p[] = { + { .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys }, + { .cmd = DTV_FREQUENCY, .u.data = ifreq }, + { .cmd = DTV_MODULATION, .u.data = modulation }, + { .cmd = DTV_SYMBOL_RATE, .u.data = sr }, + { .cmd = DTV_INNER_FEC, .u.data = fec }, + { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, + { .cmd = DTV_ROLLOFF, .u.data = rolloff }, + { .cmd = DTV_PILOT, .u.data = PILOT_AUTO }, + { .cmd = DTV_STREAM_ID, .u.data = stream_id }, + { .cmd = DTV_MODCODE, .u.data = modcode }, + { .cmd = DTV_TUNE }, + }; + struct dtv_properties cmdseq = { + .num = sizeof(p)/sizeof(p[0]), + .props = p + }; + + /* discard stale QPSK events */ + while (1) { + if (ioctl(fefd, FE_GET_EVENT, &ev) == -1) + break; + } + + if ((delsys != SYS_DVBS) && (delsys != SYS_DVBS2)) + return -EINVAL; + + if ((ioctl(fefd, FE_SET_PROPERTY, &cmdseq)) == -1) { + perror("FE_SET_PROPERTY failed"); + return FALSE; + } + + return TRUE; +} + + +static +int check_frontend (int fe_fd, int dvr, int human_readable, int params_debug, + int hiband) +{ + (void)dvr; + fe_status_t status; + uint16_t snr, signal; + uint32_t ber, uncorrected_blocks; + int timeout = 0; + char *field; + struct dtv_property p[] = { + { .cmd = DTV_DELIVERY_SYSTEM }, + { .cmd = DTV_MODULATION }, + { .cmd = DTV_INNER_FEC }, + { .cmd = DTV_ROLLOFF }, + { .cmd = DTV_FREQUENCY }, + { .cmd = DTV_SYMBOL_RATE }, + }; + struct dtv_properties cmdseq = { + .num = sizeof(p)/sizeof(p[0]), + .props = p + }; + + do { + if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1) + perror("FE_READ_STATUS failed"); + /* some frontends might not support all these ioctls, thus we + * avoid printing errors + */ + if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1) + signal = -2; + if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1) + snr = -2; + if (ioctl(fe_fd, FE_READ_BER, &ber) == -1) + ber = -2; + if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1) + uncorrected_blocks = -2; + + if (human_readable) { + printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ", + status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks); + } else { + printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ", + status, signal, snr, ber, uncorrected_blocks); + } + if (status & FE_HAS_LOCK) + printf("FE_HAS_LOCK"); + printf("\n"); + + if (exit_after_tuning && ((status & FE_HAS_LOCK) || (++timeout >= 10))) + break; + + usleep(1000000); + } while (1); + + if ((status & FE_HAS_LOCK) == 0) + return 0; + + if ((ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq)) == -1) { + perror("FE_GET_PROPERTY failed"); + return 0; + } + /* printout found parameters here */ + if (params_debug){ + printf("delivery 0x%x, ", p[0].u.data); + printf("modulation 0x%x\n", p[1].u.data); + printf("coderate 0x%x, ", p[2].u.data); + printf("rolloff 0x%x\n", p[3].u.data); + printf("intermediate frequency %d,", p[4].u.data); + } else { + field = NULL; + map_to_user(p[0].u.data, system_values, &field); + printf("delivery %s, ", field); + field = NULL; + map_to_user(p[1].u.data, modulation_values, &field); + printf("modulation %s\n", field); + field = NULL; + map_to_user(p[2].u.data, coderate_values, &field); + printf("coderate %s, ", field); + field = NULL; + map_to_user(p[3].u.data, rolloff_values, &field); + printf("rolloff %s\n", field); + if (hiband) + printf("frequency %lu,",lnb_type.high_val + p[4].u.data); + else + printf("frequency %lu,",lnb_type.low_val + p[4].u.data); + + } + + printf("symbol_rate %d\n", p[5].u.data); + + return 0; +} + +static +int zap_to(unsigned int adapter, unsigned int frontend, unsigned int demux, + unsigned int sat_no, unsigned int freq, unsigned int pol, + unsigned int sr, unsigned int vpid, unsigned int apid, + unsigned int tpid, int sid, + int dvr, int rec_psi, int bypass, unsigned int delivery, + int modulation, int fec, int rolloff, int stream_id, unsigned int modcode, + int human_readable, int params_debug) +{ + struct dtv_property p[] = { + { .cmd = DTV_CLEAR }, + }; + + struct dtv_properties cmdseq = { + .num = sizeof(p)/sizeof(p[0]), + .props = p + }; + + char fedev[128], dmxdev[128], auddev[128]; + static int fefd, dmxfda, dmxfdv, dmxfdt = -1, audiofd = -1, patfd, pmtfd; + int pmtpid; + uint32_t ifreq; + int hiband, result; + + if (!fefd) { + snprintf(fedev, sizeof(fedev), FRONTENDDEVICE, adapter, frontend); + snprintf(dmxdev, sizeof(dmxdev), DEMUXDEVICE, adapter, demux); + snprintf(auddev, sizeof(auddev), AUDIODEVICE, adapter, demux); + printf("using '%s' and '%s'\n", fedev, dmxdev); + + if ((fefd = open(fedev, O_RDWR | O_NONBLOCK)) < 0) { + perror("opening frontend failed"); + return FALSE; + } + + if ((dmxfdv = open(dmxdev, O_RDWR)) < 0) { + perror("opening video demux failed"); + close(fefd); + return FALSE; + } + + if ((dmxfda = open(dmxdev, O_RDWR)) < 0) { + perror("opening audio demux failed"); + close(fefd); + return FALSE; + } + + if ((dmxfdt = open(dmxdev, O_RDWR)) < 0) { + perror("opening teletext demux failed"); + close(fefd); + return FALSE; + } + + if (dvr == 0) /* DMX_OUT_DECODER */ + audiofd = open(auddev, O_RDWR); + + if (rec_psi){ + if ((patfd = open(dmxdev, O_RDWR)) < 0) { + perror("opening pat demux failed"); + close(audiofd); + close(dmxfda); + close(dmxfdv); + close(dmxfdt); + close(fefd); + return FALSE; + } + + if ((pmtfd = open(dmxdev, O_RDWR)) < 0) { + perror("opening pmt demux failed"); + close(patfd); + close(audiofd); + close(dmxfda); + close(dmxfdv); + close(dmxfdt); + close(fefd); + return FALSE; + } + } + } + + + hiband = 0; + if (lnb_type.switch_val && lnb_type.high_val && + freq >= lnb_type.switch_val) + hiband = 1; + + if (hiband) + ifreq = freq - lnb_type.high_val; + else { + if (freq < lnb_type.low_val) + ifreq = lnb_type.low_val - freq; + else + ifreq = freq - lnb_type.low_val; + } + result = FALSE; + + if ((ioctl(fefd, FE_SET_PROPERTY, &cmdseq)) == -1) { + perror("FE_SET_PROPERTY DTV_CLEAR failed"); + return FALSE; + } + + if (diseqc(fefd, sat_no, pol, hiband)) + if (do_tune(fefd, ifreq, sr, delivery, modulation, fec, rolloff, stream_id, modcode)) + if (set_demux(dmxfdv, vpid, DMX_PES_VIDEO, dvr)) + if (audiofd >= 0) + (void)ioctl(audiofd, AUDIO_SET_BYPASS_MODE, bypass); + if (set_demux(dmxfda, apid, DMX_PES_AUDIO, dvr)) { + if (rec_psi) { + pmtpid = get_pmt_pid(dmxdev, sid); + if (pmtpid < 0) { + result = FALSE; + } + if (pmtpid == 0) { + fprintf(stderr,"couldn't find pmt-pid for sid %04x\n",sid); + result = FALSE; + } + if (set_demux(patfd, 0, DMX_PES_OTHER, dvr)) + if (set_demux(pmtfd, pmtpid, DMX_PES_OTHER, dvr)) + result = TRUE; + } else { + result = TRUE; + } + } + + if (tpid != -1 && !set_demux(dmxfdt, tpid, DMX_PES_TELETEXT, dvr)) { + fprintf(stderr, "set_demux DMX_PES_TELETEXT failed\n"); + } + + check_frontend (fefd, dvr, human_readable, params_debug, hiband); + + if (!interactive) { + close(patfd); + close(pmtfd); + if (audiofd >= 0) + close(audiofd); + close(dmxfda); + close(dmxfdv); + close(dmxfdt); + close(fefd); + } + + return result; +} +static char *parse_parameter(const char *s, int *value, const struct t_channel_parameter_map *map) +{ + if (*++s) { + char *p = NULL; + errno = 0; + int n = strtol(s, &p, 10); + if (!errno && p != s) { + value[0] = map_to_driver(n, map); + if (value[0] >= 0) + return p; + } + } + fprintf(stderr, "ERROR: invalid value for parameter '%C'\n", *(s - 1)); + return NULL; +} + +static int read_channels(const char *filename, int list_channels, + uint32_t chan_no, const char *chan_name, + unsigned int adapter, unsigned int frontend, + unsigned int demux, int dvr, int rec_psi, + int bypass, unsigned int delsys, + int modulation, int fec, int rolloff, int stream_id, unsigned int modcode, + int human_readable, int params_debug, + int use_vdr_format, int use_tpid) +{ + FILE *cfp; + char buf[4096]; + char inp[256]; + char *field, *tmp, *p; + unsigned int line; + unsigned int freq, pol, sat_no, sr, vpid, apid, tpid, sid; + int ret; + int trash; +again: + line = 0; + if (!(cfp = fopen(filename, "r"))) { + fprintf(stderr, "error opening channel list '%s': %d %m\n", + filename, errno); + return FALSE; + } + + if (interactive) { + fprintf(stderr, "\n>>> "); + if (!fgets(inp, sizeof(inp), stdin)) { + printf("\n"); + return -1; + } + if (inp[0] == '-' && inp[1] == 'n') { + chan_no = strtoul(inp+2, NULL, 0); + chan_name = NULL; + if (!chan_no) { + fprintf(stderr, "bad channel number\n"); + goto again; + } + } else { + p = strchr(inp, '\n'); + if (p) + *p = '\0'; + chan_name = inp; + chan_no = 0; + } + } + + while (!feof(cfp)) { + if (fgets(buf, sizeof(buf), cfp)) { + line++; + + if (chan_no && chan_no != line) + continue; + + tmp = buf; + field = strsep(&tmp, ":"); + + if (!field) + goto syntax_err; + + if (list_channels) { + printf("%03u %s\n", line, field); + continue; + } + + if (chan_name && strcasecmp(chan_name, field) != 0) + continue; + + printf("zapping to %d '%s':\n", line, field); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + freq = strtoul(field, NULL, 0); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + while (field && *field) { + switch (toupper(*field)) { + case 'C': + if (fec == -1) + field = parse_parameter(field, &fec, coderate_values); + else + field = parse_parameter(field, &trash, coderate_values); + break; + case 'H': + pol = 0; + *field++; + break; + case 'I':/* ignore */ + field = parse_parameter(field, &ret, inversion_values); + break; + case 'L': + pol = 0; + *field++; + break; + case 'M': + if (modulation == -1) + field = parse_parameter(field, &modulation, modulation_values); + else + field = parse_parameter(field, &trash, modulation_values); + break; + case 'Z': + case 'O': + if (rolloff == -1) + field = parse_parameter(field, &rolloff, rolloff_values); + else + field = parse_parameter(field, &trash, rolloff_values); + break; + case 'P': + stream_id = strtol(++field, &field, 10); + break; + case 'W': + modcode = strtoul(++field, &field, 10); + break; + case 'R': + pol = 1; + *field++; + break; + case 'S': + if (delsys == -1) + field = parse_parameter(field, &delsys, system_values); + else + field = parse_parameter(field, &trash, system_values); + break; + case 'V': + pol = 1; + *field++; + break; + default: + goto syntax_err; + } + } + /* default values for empty parameters */ + if (fec == -1) + fec = FEC_AUTO; + + if (modulation == -1) + modulation = QPSK; + + if (delsys == -1) + delsys = SYS_DVBS; + + if (rolloff == -1) + rolloff = ROLLOFF_35; + +#if 0 + if (stream_id<0 || stream_id>255) + stream_id = NO_STREAM_ID_FILTER; +#endif + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + sat_no = strtoul(field, NULL, 0); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + sr = strtoul(field, NULL, 0) * 1000; + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + vpid = strtoul(field, NULL, 0); + if (!vpid) + vpid = 0x1fff; + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + p = strchr(field, ';'); + + if (p) { + *p = '\0'; + p++; + if (bypass) { + if (!p || !*p) + goto syntax_err; + field = p; + } + } + + apid = strtoul(field, NULL, 0); + if (!apid) + apid = 0x1fff; + + tpid = -1; + if (use_vdr_format) { + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + if (use_tpid) + tpid = strtoul(field, NULL, 0); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + strtoul(field, NULL, 0); + } + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + sid = strtoul(field, NULL, 0); + + fclose(cfp); + if (params_debug){ + printf("delivery 0x%x, ", delsys); + } else { + field = NULL; + map_to_user(delsys, system_values, &field); + printf("delivery %s, ", field); + } + + if (params_debug){ + printf("modulation 0x%x\n", modulation); + } else { + field = NULL; + map_to_user(modulation, modulation_values, &field); + printf("modulation %s\n", field); + } + + printf("sat %u, frequency %u MHz %c, symbolrate %u, ", + sat_no, freq, pol ? 'V' : 'H', sr); + + if (params_debug){ + printf("coderate 0x%x, ", fec); + } else { + field = NULL; + map_to_user(fec, coderate_values, &field); + printf("coderate %s, ", field); + } + + if (params_debug){ + printf("rolloff 0x%x stream_id %d modcode %u\n" + "vpid 0x%04x, apid 0x%04x, sid 0x%04x\n", rolloff, stream_id, modcode, vpid, apid, sid); + } else { + field = NULL; + map_to_user(rolloff, rolloff_values, &field); + printf("rolloff %s stream_id %d stream_id %d\n" + "vpid 0x%04x, apid 0x%04x, sid 0x%04x\n", field, stream_id, modcode, vpid, apid, sid); + } + + ret = zap_to(adapter, frontend, demux, sat_no, freq * 1000, + pol, sr, vpid, apid, tpid, sid, dvr, rec_psi, bypass, + delsys, modulation, fec, rolloff, stream_id, modcode, human_readable, + params_debug); + + if (interactive) + goto again; + + if (ret) + return TRUE; + + return FALSE; + +syntax_err: + fprintf(stderr, "syntax error in line %u: '%s'\n", line, buf); + } else if (ferror(cfp)) { + fprintf(stderr, "error reading channel list '%s': %d %m\n", + filename, errno); + fclose(cfp); + return FALSE; + } else + break; + } + + fclose(cfp); + + if (!list_channels) { + fprintf(stderr, "channel not found\n"); + + if (!interactive) + return FALSE; + } + if (interactive) + goto again; + + return TRUE; +} + +static void handle_sigint(int sig) +{ + fprintf(stderr, "Interrupted by SIGINT!\n"); + exit(2); +} + +void +bad_usage(char *pname, int prlnb) +{ + int i; + struct lnb_types_st *lnbp; + char **cp; + + if (!prlnb) { + fprintf (stderr, usage_str, pname); + } else { + i = 0; + fprintf(stderr, "-l or -l low[,high[,switch]] in Mhz\nwhere is:\n"); + while(NULL != (lnbp = lnb_enum(i))) { + fprintf (stderr, "%s\n", lnbp->name); + for (cp = lnbp->desc; *cp ; cp++) { + fprintf (stderr, " %s\n", *cp); + } + i++; + } + } +} + +int main(int argc, char *argv[]) +{ + const char *home; + char chanfile[2 * PATH_MAX]; + int list_channels = 0; + unsigned int chan_no = 0; + const char *chan_name = NULL; + unsigned int adapter = 0, frontend = 0, demux = 0, dvr = 0, rec_psi = 0; + int bypass = 0; + int opt, copt = 0; + int human_readable = 0; + int params_debug = 0; + int use_vdr_format = 0; + int use_tpid = 0; + + + int delsys = -1; + int modulation = -1; + int fec = -1; + int rolloff = -1; + int stream_id = NO_STREAM_ID_FILTER; + unsigned int modcode = MODCODE_ALL; + + lnb_type = *lnb_enum(0); + while ((opt = getopt(argc, argv, "M:m:s:C:O:HDVhqrpn:a:f:d:S:c:l:xib")) != -1) { + switch (opt) { + case '?': + case 'h': + default: + bad_usage(argv[0], 0); + break; + case 'C': + parse_parameter(--optarg, &fec, coderate_values); + break; + case 'M': + parse_parameter(--optarg, &modulation, modulation_values); + break; + case 'Z': + case 'O': + parse_parameter(--optarg, &rolloff, rolloff_values); + break; + case 's': + stream_id = strtol(optarg, NULL, 0); + break; + case 'm': + modcode = strtoul(optarg, NULL, 0); + break; + case 'S': + parse_parameter(--optarg, &delsys, system_values); + break; + case 'b': + bypass = 1; + break; + case 'q': + list_channels = 1; + break; + case 'r': + dvr = 1; + break; + case 'n': + chan_no = strtoul(optarg, NULL, 0); + break; + case 'a': + adapter = strtoul(optarg, NULL, 0); + break; + case 'f': + frontend = strtoul(optarg, NULL, 0); + break; + case 'p': + rec_psi = 1; + break; + case 'd': + demux = strtoul(optarg, NULL, 0); + break; + case 'c': + copt = 1; + strncpy(chanfile, optarg, sizeof(chanfile)); + break; + case 'l': + if (lnb_decode(optarg, &lnb_type) < 0) { + bad_usage(argv[0], 1); + return -1; + } + break; + case 'x': + exit_after_tuning = 1; + break; + case 'H': + human_readable = 1; + break; + case 'D': + params_debug = 1; + break; + case 'V': + use_vdr_format = 1; + break; + case 't': + use_tpid = 1; + break; + case 'i': + interactive = 1; + exit_after_tuning = 1; + } + } + lnb_type.low_val *= 1000; /* convert to kiloherz */ + lnb_type.high_val *= 1000; /* convert to kiloherz */ + lnb_type.switch_val *= 1000; /* convert to kiloherz */ + if (optind < argc) + chan_name = argv[optind]; + if (chan_name && chan_no) { + bad_usage(argv[0], 0); + return -1; + } + if (list_channels && (chan_name || chan_no)) { + bad_usage(argv[0], 0); + return -1; + } + if (!list_channels && !chan_name && !chan_no && !interactive) { + bad_usage(argv[0], 0); + return -1; + } + + if (!copt) { + if (!(home = getenv("HOME"))) { + fprintf(stderr, "error: $HOME not set\n"); + return TRUE; + } + snprintf(chanfile, sizeof(chanfile), + "%s/.szap/%i/%s", home, adapter, CHANNEL_FILE); + if (access(chanfile, R_OK)) + snprintf(chanfile, sizeof(chanfile), + "%s/.szap/%s", home, CHANNEL_FILE); + } + + printf("reading channels from file '%s'\n", chanfile); + + if (rec_psi) + dvr=1; + + signal(SIGINT, handle_sigint); + + if (!read_channels(chanfile, list_channels, chan_no, chan_name, + adapter, frontend, demux, dvr, rec_psi, bypass, delsys, + modulation, fec, rolloff, stream_id, modcode, human_readable, params_debug, + use_vdr_format, use_tpid)) + + return TRUE; + + return FALSE; +} diff --git a/szap-s2/tzap-t2.c b/szap-s2/tzap-t2.c new file mode 100644 index 0000000..6aed2e5 --- /dev/null +++ b/szap-s2/tzap-t2.c @@ -0,0 +1,1089 @@ +/* tzap-t2 -- simple zapping tool for the Linux DVB S2 API + * + * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) + * Copyright (C) 2013 CrazyCat (crazycat69@narod.ru) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "lnb.h" + +#if DVB_API_VERSION < 5 || DVB_API_VERSION_MINOR < 2 +#error szap-s2 requires Linux DVB driver API version 5.2 and newer! +#endif + +#ifndef DTV_STREAM_ID + #define DTV_STREAM_ID 42 +#endif + +#ifndef NO_STREAM_ID_FILTER + #define NO_STREAM_ID_FILTER (~0U) +#endif + +#ifndef TRUE +#define TRUE (1==1) +#endif +#ifndef FALSE +#define FALSE (1==0) +#endif + +/* location of channel list file */ +#define CHANNEL_FILE "channels.conf" + +/* one line of the szap channel file has the following format: + * ^name:frequency_KHz:inversion:bandwidth:coderate_hp:coderate_lp:modulation:transmission_mode:guard_interval:hierarchy:vpid:apid:service_id$ + * one line of the VDR channel file has the following format: + * ^name:frequency_KHz:[bandwidth][delivery][modulation][coderate_hp][coderate_lp][transmission_mode][guard_interval][hierarchy]]plp_id]:vpid:apid:?:?:service_id:?:?:?$ + */ + + +#define FRONTENDDEVICE "/dev/dvb/adapter%d/frontend%d" +#define DEMUXDEVICE "/dev/dvb/adapter%d/demux%d" +#define AUDIODEVICE "/dev/dvb/adapter%d/audio%d" + +struct t_channel_parameter_map { + int user_value; + int driver_value; + const char *user_string; + }; +/* --- Channel Parameter Maps From VDR---*/ + +static struct t_channel_parameter_map inversion_values[] = { + { 0, INVERSION_OFF, "off" }, + { 1, INVERSION_ON, "on" }, + { 999, INVERSION_AUTO }, + { -1 } + }; + +static struct t_channel_parameter_map bw_values [] = { + { 6, BANDWIDTH_6_MHZ, "6MHz" }, + { 7, BANDWIDTH_7_MHZ, "7MHz" }, + { 8, BANDWIDTH_8_MHZ, "8MHz" }, + { 5, BANDWIDTH_6_MHZ, "5MHz" }, + { 10, BANDWIDTH_10_MHZ, "10MHz" }, + { 999, BANDWIDTH_AUTO, "auto" }, + { -1 } + }; + +static struct t_channel_parameter_map coderate_values[] = { + { 0, FEC_NONE, "none" }, + { 12, FEC_1_2, "1/2" }, + { 23, FEC_2_3, "2/3" }, + { 34, FEC_3_4, "3/4" }, + { 35, FEC_3_5, "3/5" }, + { 45, FEC_4_5, "4/5" }, + { 56, FEC_5_6, "5/6" }, + { 67, FEC_6_7, "6/7" }, + { 78, FEC_7_8, "7/8" }, + { 89, FEC_8_9, "8/9" }, + { 910, FEC_9_10, "9/10" }, + { 999, FEC_AUTO, "auto" }, + { -1 } + }; + +static struct t_channel_parameter_map modulation_values[] = { + // { 0, NONE, "none" }, + { 2, QPSK, "QPSK" }, + { 10, VSB_8, "VSB8" }, + { 11, VSB_16, "VSB16" }, + { 16, QAM_16, "QAM16" }, + { 64, QAM_64, "QAM64" }, + { 128, QAM_128, "QAM128" }, + { 256, QAM_256, "QAM256" }, + { 999, QAM_16 }, + { -1 } + }; + +static struct t_channel_parameter_map mode_values [] = { + { 2, TRANSMISSION_MODE_2K, "2k" }, + { 8, TRANSMISSION_MODE_2K, "8k" }, + { 999, TRANSMISSION_MODE_AUTO, "auto" }, + { 4, TRANSMISSION_MODE_4K, "4k" }, + { 1, TRANSMISSION_MODE_1K, "1k" }, + { 16, TRANSMISSION_MODE_16K, "16k" }, + { 32, TRANSMISSION_MODE_32K, "32k" }, + { -1 } + }; + +static struct t_channel_parameter_map guard_values [] = { + { 32, GUARD_INTERVAL_1_32, "1/32" }, + { 16, GUARD_INTERVAL_1_16, "1/16" }, + { 8, GUARD_INTERVAL_1_8, "1/8" }, + { 4, GUARD_INTERVAL_1_4, "1/4" }, + { 999, GUARD_INTERVAL_AUTO, "auto" }, + { 1128, GUARD_INTERVAL_1_128, "1/128" }, + { 19128, GUARD_INTERVAL_19_128, "19/128" }, + { 19256, GUARD_INTERVAL_19_256, "19/256" }, + { -1 } + }; + +static struct t_channel_parameter_map hierarchy_values [] = { + { 0, HIERARCHY_NONE, "none" }, + { 1, HIERARCHY_NONE, "1" }, + { 2, HIERARCHY_NONE, "2" }, + { 4, HIERARCHY_NONE, "4" }, + { 999, HIERARCHY_NONE, "auto" }, + { -1 } + }; + +static struct t_channel_parameter_map system_values[] = { + { 0, SYS_DVBT, "DVB-T" }, + { 1, SYS_DVBT2, "DVB-T2" }, + { -1 } + }; + +static int user_index(int value, const struct t_channel_parameter_map * map) +{ + const struct t_channel_parameter_map *umap = map; + while (umap && umap->user_value != -1) { + if (umap->user_value == value) + return umap - map; + umap++; + } + return -1; +}; + +static int driver_index(int value, const struct t_channel_parameter_map *map) +{ + const struct t_channel_parameter_map *umap = map; + while (umap && umap->user_value != -1) { + if (umap->driver_value == value) + return umap - map; + umap++; + } + return -1; +}; + +static int map_to_user(int value, const struct t_channel_parameter_map *map, char **string) +{ + int n = driver_index(value, map); + if (n >= 0) { + if (string) + *string = (char *)map[n].user_string; + return map[n].user_value; + } + return -1; +} + +static int map_to_driver(int value, const struct t_channel_parameter_map *map) +{ + int n = user_index(value, map); + if (n >= 0) + return map[n].driver_value; + return -1; +} + +static struct lnb_types_st lnb_type; + +static int exit_after_tuning; +static int interactive; + +static char *usage_str = + "\nusage: tzap-t2 -q\n" + " list known channels\n" + " tzap-t2 [options] {-n channel-number|channel_name}\n" + " zap to channel via number or full name (case insensitive)\n" + " -a number : use given adapter (default 0)\n" + " -f number : use given frontend (default 0)\n" + " -d number : use given demux (default 0)\n" + " -c file : read channels list from 'file'\n" + " -V : use VDR channels list file format (default zap)\n" + " -b : enable Audio Bypass (default no)\n" + " -x : exit after tuning\n" + " -H : human readable output\n" + " -D : params debug\n" + " -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n" + " -i : run interactively, allowing you to type in channel names\n" + " -p : add pat and pmt to TS recording (implies -r)\n" + " or -n numbers for zapping\n" + " -t : add teletext to TS recording (needs -V)\n" + " -S : delivery system type DVB-T=0, DVB-T2=1\n" + " -M : modulation 2=QPSK 16=16QAM 64=64QAM 256=256QAM\n" + " -C : fec 0=NONE 12=1/2 23=2/3 34=3/4 35=3/5 45=4/5 56=5/6 67=6/7 89=8/9 910=9/10 999=AUTO\n" + " -m : physical layer pipe 0-255\n"; + +static int set_demux(int dmxfd, int pid, int pes_type, int dvr) +{ + struct dmx_pes_filter_params pesfilter; + + if (pid < 0 || pid >= 0x1fff) /* ignore this pid to allow radio services */ + return TRUE; + + if (dvr) { + int buffersize = 64 * 1024; + if (ioctl(dmxfd, DMX_SET_BUFFER_SIZE, buffersize) == -1) + perror("DMX_SET_BUFFER_SIZE failed"); + } + + pesfilter.pid = pid; + pesfilter.input = DMX_IN_FRONTEND; + pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER; + pesfilter.pes_type = pes_type; + pesfilter.flags = DMX_IMMEDIATE_START; + + if (ioctl(dmxfd, DMX_SET_PES_FILTER, &pesfilter) == -1) { + fprintf(stderr, "DMX_SET_PES_FILTER failed " + "(PID = 0x%04x): %d %m\n", pid, errno); + + return FALSE; + } + + return TRUE; +} + +int get_pmt_pid(char *dmxdev, int sid) +{ + int patfd, count; + int pmt_pid = 0; + int patread = 0; + int section_length; + unsigned char buft[4096]; + unsigned char *buf = buft; + struct dmx_sct_filter_params f; + + memset(&f, 0, sizeof(f)); + f.pid = 0; + f.filter.filter[0] = 0x00; + f.filter.mask[0] = 0xff; + f.timeout = 0; + f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC; + + if ((patfd = open(dmxdev, O_RDWR)) < 0) { + perror("openening pat demux failed"); + return -1; + } + + if (ioctl(patfd, DMX_SET_FILTER, &f) == -1) { + perror("ioctl DMX_SET_FILTER failed"); + close(patfd); + return -1; + } + + while (!patread) { + if (((count = read(patfd, buf, sizeof(buft))) < 0) && errno == EOVERFLOW) + count = read(patfd, buf, sizeof(buft)); + if (count < 0) { + perror("read_sections: read error"); + close(patfd); + return -1; + } + + section_length = ((buf[1] & 0x0f) << 8) | buf[2]; + if (count != section_length + 3) + continue; + + buf += 8; + section_length -= 8; + + patread = 1; /* assumes one section contains the whole pat */ + while (section_length > 0) { + int service_id = (buf[0] << 8) | buf[1]; + if (service_id == sid) { + pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3]; + section_length = 0; + } + buf += 4; + section_length -= 4; + } + } + close(patfd); + return pmt_pid; +} + +static int do_tune(int fefd, unsigned int freq, unsigned int bw, enum fe_delivery_system delsys, + int modulation, int fec_hp, int fec_lp, int mode, int guard, int hierarchy, int stream_id) +{ + struct dvb_frontend_event ev; + struct dtv_property p[] = { + { .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys }, + { .cmd = DTV_FREQUENCY, .u.data = freq }, + { .cmd = DTV_BANDWIDTH_HZ, .u.data = bw }, + { .cmd = DTV_MODULATION, .u.data = modulation }, + { .cmd = DTV_CODE_RATE_HP, .u.data = fec_hp }, + { .cmd = DTV_CODE_RATE_LP, .u.data = fec_lp }, + { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, + { .cmd = DTV_TRANSMISSION_MODE, .u.data = mode }, + { .cmd = DTV_GUARD_INTERVAL, .u.data = guard }, + { .cmd = DTV_HIERARCHY, .u.data = hierarchy }, + { .cmd = DTV_STREAM_ID, .u.data = stream_id }, + { .cmd = DTV_TUNE }, + }; + struct dtv_properties cmdseq = { + .num = sizeof(p)/sizeof(p[0]), + .props = p + }; + + /* discard stale QPSK events */ + while (1) { + if (ioctl(fefd, FE_GET_EVENT, &ev) == -1) + break; + } + + if ((delsys != SYS_DVBT) && (delsys != SYS_DVBT2)) + return -EINVAL; + + if ((ioctl(fefd, FE_SET_PROPERTY, &cmdseq)) == -1) { + perror("FE_SET_PROPERTY failed"); + return FALSE; + } + + return TRUE; +} + + +static +int check_frontend (int fe_fd, int dvr, int human_readable, int params_debug) +{ + (void)dvr; + fe_status_t status; + uint16_t snr, signal; + uint32_t ber, uncorrected_blocks; + int timeout = 0; + char *field; + struct dtv_property p[] = { + { .cmd = DTV_FREQUENCY }, + { .cmd = DTV_BANDWIDTH_HZ }, + { .cmd = DTV_DELIVERY_SYSTEM }, + { .cmd = DTV_MODULATION }, + { .cmd = DTV_CODE_RATE_HP }, + { .cmd = DTV_CODE_RATE_LP }, + { .cmd = DTV_TRANSMISSION_MODE }, + { .cmd = DTV_GUARD_INTERVAL }, + { .cmd = DTV_HIERARCHY }, + }; + struct dtv_properties cmdseq = { + .num = sizeof(p)/sizeof(p[0]), + .props = p + }; + + do { + if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1) + perror("FE_READ_STATUS failed"); + /* some frontends might not support all these ioctls, thus we + * avoid printing errors + */ + if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1) + signal = -2; + if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1) + snr = -2; + if (ioctl(fe_fd, FE_READ_BER, &ber) == -1) + ber = -2; + if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1) + uncorrected_blocks = -2; + + if (human_readable) { + printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ", + status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks); + } else { + printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ", + status, signal, snr, ber, uncorrected_blocks); + } + if (status & FE_HAS_LOCK) + printf("FE_HAS_LOCK"); + printf("\n"); + + if (exit_after_tuning && ((status & FE_HAS_LOCK) || (++timeout >= 10))) + break; + + usleep(1000000); + } while (1); + + if ((status & FE_HAS_LOCK) == 0) + return 0; + + if ((ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq)) == -1) { + perror("FE_GET_PROPERTY failed"); + return 0; + } + /* printout found parameters here */ + if (params_debug){ + printf("frequency %d, ", p[0].u.data); + printf("bandwidth %d, ", p[1].u.data); + printf("delivery %d, ", p[2].u.data); + printf("modulation %d, ", p[3].u.data); + printf("coderate hp %d, ", p[4].u.data); + printf("coderate lp %d, ", p[5].u.data); + printf("mode %d, ", p[6].u.data); + printf("guard %d, ", p[7].u.data); + printf("hierarchy %d", p[8].u.data); + } else { + printf("frequency %d, ",p[0].u.data); + field = NULL; + map_to_user(p[1].u.data, bw_values, &field); + printf("bandwidth %s, ", field); + field = NULL; + map_to_user(p[2].u.data, system_values, &field); + printf("delivery %s, ", field); + field = NULL; + map_to_user(p[3].u.data, modulation_values, &field); + printf("modulation %s, ", field); + field = NULL; + map_to_user(p[4].u.data, coderate_values, &field); + printf("coderate-hp %s, ", field); + field = NULL; + map_to_user(p[5].u.data, coderate_values, &field); + printf("coderate-lp %s, ", field); + field = NULL; + map_to_user(p[6].u.data, mode_values, &field); + printf("mode %s, ", field); + field = NULL; + map_to_user(p[7].u.data, guard_values, &field); + printf("guard %s, ", field); + field = NULL; + map_to_user(p[8].u.data, hierarchy_values, &field); + printf("hierarchy %s", field); + } + + printf("\n"); + + return 0; +} + +static +int zap_to(unsigned int adapter, unsigned int frontend, unsigned int demux, + unsigned int freq, unsigned int bw, + unsigned int vpid, unsigned int apid, + unsigned int tpid, int sid, + int dvr, int rec_psi, int bypass, unsigned int delivery, + int modulation, int fec_hp, int fec_lp, int mode, int guard, int hierarchy, int stream_id, int human_readable, + int params_debug) +{ + struct dtv_property p[] = { + { .cmd = DTV_CLEAR }, + }; + + struct dtv_properties cmdseq = { + .num = sizeof(p)/sizeof(p[0]), + .props = p + }; + + char fedev[128], dmxdev[128], auddev[128]; + static int fefd, dmxfda, dmxfdv, dmxfdt = -1, audiofd = -1, patfd, pmtfd; + int pmtpid; + int result; + + if (!fefd) { + snprintf(fedev, sizeof(fedev), FRONTENDDEVICE, adapter, frontend); + snprintf(dmxdev, sizeof(dmxdev), DEMUXDEVICE, adapter, demux); + snprintf(auddev, sizeof(auddev), AUDIODEVICE, adapter, demux); + printf("using '%s' and '%s'\n", fedev, dmxdev); + + if ((fefd = open(fedev, O_RDWR | O_NONBLOCK)) < 0) { + perror("opening frontend failed"); + return FALSE; + } + + if ((dmxfdv = open(dmxdev, O_RDWR)) < 0) { + perror("opening video demux failed"); + close(fefd); + return FALSE; + } + + if ((dmxfda = open(dmxdev, O_RDWR)) < 0) { + perror("opening audio demux failed"); + close(fefd); + return FALSE; + } + + if ((dmxfdt = open(dmxdev, O_RDWR)) < 0) { + perror("opening teletext demux failed"); + close(fefd); + return FALSE; + } + + if (dvr == 0) /* DMX_OUT_DECODER */ + audiofd = open(auddev, O_RDWR); + + if (rec_psi){ + if ((patfd = open(dmxdev, O_RDWR)) < 0) { + perror("opening pat demux failed"); + close(audiofd); + close(dmxfda); + close(dmxfdv); + close(dmxfdt); + close(fefd); + return FALSE; + } + + if ((pmtfd = open(dmxdev, O_RDWR)) < 0) { + perror("opening pmt demux failed"); + close(patfd); + close(audiofd); + close(dmxfda); + close(dmxfdv); + close(dmxfdt); + close(fefd); + return FALSE; + } + } + } + + + result = FALSE; + + if ((ioctl(fefd, FE_SET_PROPERTY, &cmdseq)) == -1) { + perror("FE_SET_PROPERTY DTV_CLEAR failed"); + return FALSE; + } + + switch(bw) + { + case BANDWIDTH_5_MHZ: bw = 5000000; break; + case BANDWIDTH_6_MHZ: bw = 6000000; break; + case BANDWIDTH_7_MHZ: bw = 7000000; break; + case BANDWIDTH_8_MHZ: bw = 8000000; break; + case BANDWIDTH_10_MHZ: bw = 10000000; break; + case BANDWIDTH_AUTO: + default: bw = 0; + } + + if (do_tune(fefd, freq, bw, delivery, modulation, fec_hp, fec_lp, mode, guard, hierarchy, stream_id)) + if (set_demux(dmxfdv, vpid, DMX_PES_VIDEO, dvr)) + if (audiofd >= 0) + (void)ioctl(audiofd, AUDIO_SET_BYPASS_MODE, bypass); + if (set_demux(dmxfda, apid, DMX_PES_AUDIO, dvr)) { + if (rec_psi) { + pmtpid = get_pmt_pid(dmxdev, sid); + if (pmtpid < 0) { + result = FALSE; + } + if (pmtpid == 0) { + fprintf(stderr,"couldn't find pmt-pid for sid %04x\n",sid); + result = FALSE; + } + if (set_demux(patfd, 0, DMX_PES_OTHER, dvr)) + if (set_demux(pmtfd, pmtpid, DMX_PES_OTHER, dvr)) + result = TRUE; + } else { + result = TRUE; + } + } + + if (tpid != -1 && !set_demux(dmxfdt, tpid, DMX_PES_TELETEXT, dvr)) { + fprintf(stderr, "set_demux DMX_PES_TELETEXT failed\n"); + } + + check_frontend (fefd, dvr, human_readable, params_debug); + + if (!interactive) { + close(patfd); + close(pmtfd); + if (audiofd >= 0) + close(audiofd); + close(dmxfda); + close(dmxfdv); + close(dmxfdt); + close(fefd); + } + + return result; +} +static char *parse_parameter(const char *s, int *value, const struct t_channel_parameter_map *map) +{ + if (*++s) { + char *p = NULL; + errno = 0; + int n = strtol(s, &p, 10); + if (!errno && p != s) { + value[0] = map_to_driver(n, map); + if (value[0] >= 0) + return p; + } + } + fprintf(stderr, "ERROR: invalid value for parameter '%C'\n", *(s - 1)); + return NULL; +} + +static int read_channels(const char *filename, int list_channels, + uint32_t chan_no, const char *chan_name, + unsigned int adapter, unsigned int frontend, + unsigned int demux, int dvr, int rec_psi, + int bypass, unsigned int delsys, + int modulation, int fec_hp, int fec_lp, int mode, int guard, int hierarchy, int stream_id, int human_readable, + int params_debug, int use_vdr_format, int use_tpid) +{ + FILE *cfp; + char buf[4096]; + char inp[256]; + char *field, *tmp, *p; + unsigned int line; + unsigned int freq = 0, bw = 0, vpid, apid, tpid, sid; + int ret; + int trash; +again: + line = 0; + if (!(cfp = fopen(filename, "r"))) { + fprintf(stderr, "error opening channel list '%s': %d %m\n", + filename, errno); + return FALSE; + } + + if (interactive) { + fprintf(stderr, "\n>>> "); + if (!fgets(inp, sizeof(inp), stdin)) { + printf("\n"); + return -1; + } + if (inp[0] == '-' && inp[1] == 'n') { + chan_no = strtoul(inp+2, NULL, 0); + chan_name = NULL; + if (!chan_no) { + fprintf(stderr, "bad channel number\n"); + goto again; + } + } else { + p = strchr(inp, '\n'); + if (p) + *p = '\0'; + chan_name = inp; + chan_no = 0; + } + } + + while (!feof(cfp)) { + if (fgets(buf, sizeof(buf), cfp)) { + line++; + + if (chan_no && chan_no != line) + continue; + + tmp = buf; + field = strsep(&tmp, ":"); + + if (!field) + goto syntax_err; + + if (list_channels) { + printf("%03u %s\n", line, field); + continue; + } + + if (chan_name && strcasecmp(chan_name, field) != 0) + continue; + + printf("zapping to %d '%s':\n", line, field); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + freq = strtoul(field, NULL, 0); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + while (field && *field) { + switch (toupper(*field)) { + case 'B': + field = parse_parameter(field, &bw, bw_values); + break; + case 'M': + if (modulation == -1) + field = parse_parameter(field, &modulation, modulation_values); + else + field = parse_parameter(field, &trash, modulation_values); + break; + case 'I':/* ignore */ + field = parse_parameter(field, &ret, inversion_values); + break; + case 'C': + if (fec_hp == -1) + field = parse_parameter(field, &fec_hp, coderate_values); + else + field = parse_parameter(field, &trash, coderate_values); + break; + case 'D': + if (fec_lp == -1) + field = parse_parameter(field, &fec_lp, coderate_values); + else + field = parse_parameter(field, &trash, coderate_values); + break; + case 'T': + if (mode == -1) + field = parse_parameter(field, &mode, mode_values); + else + field = parse_parameter(field, &trash, mode_values); + break; + case 'G': + if (guard == -1) + field = parse_parameter(field, &guard, guard_values); + else + field = parse_parameter(field, &trash, guard_values); + break; + case 'Y': + if (hierarchy == -1) + field = parse_parameter(field, &hierarchy, hierarchy_values); + else + field = parse_parameter(field, &trash, hierarchy_values); + break; + case 'P': + stream_id = strtol(++field, &field, 10); + break; + case 'S': + if (delsys == -1) + field = parse_parameter(field, &delsys, system_values); + else + field = parse_parameter(field, &trash, system_values); + break; + default: + goto syntax_err; + } + } + /* default values for empty parameters */ + if (modulation == -1) + modulation = QAM_16; + + if (delsys == -1) + delsys = SYS_DVBT; + + if (fec_hp == -1) + fec_hp = FEC_AUTO; + + if (fec_lp == -1) + fec_lp = FEC_AUTO; + + if (mode == -1) + mode = TRANSMISSION_MODE_AUTO; + + if (guard == -1) + guard = GUARD_INTERVAL_AUTO; + + if (hierarchy == -1) + hierarchy = HIERARCHY_NONE; + + if (stream_id<0 || stream_id>255) + stream_id = NO_STREAM_ID_FILTER; + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + vpid = strtoul(field, NULL, 0); + if (!vpid) + vpid = 0x1fff; + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + p = strchr(field, ';'); + + if (p) { + *p = '\0'; + p++; + if (bypass) { + if (!p || !*p) + goto syntax_err; + field = p; + } + } + + apid = strtoul(field, NULL, 0); + if (!apid) + apid = 0x1fff; + + tpid = -1; + if (use_vdr_format) { + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + if (use_tpid) + tpid = strtoul(field, NULL, 0); + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + strtoul(field, NULL, 0); + } + + if (!(field = strsep(&tmp, ":"))) + goto syntax_err; + + sid = strtoul(field, NULL, 0); + + fclose(cfp); + + field = NULL; + map_to_user(bw, bw_values, &field); + printf("frequency %u KHz, bandwidth %s, ", + freq, field); + + if (params_debug){ + printf("delivery %d, ", delsys); + } else { + field = NULL; + map_to_user(delsys, system_values, &field); + printf("delivery %s, ", field); + } + + if (params_debug){ + printf("modulation %d\n", modulation); + } else { + field = NULL; + map_to_user(modulation, modulation_values, &field); + printf("modulation %s\n", field); + } + + if (params_debug){ + printf("coderate-hp %d, ", fec_hp); + } else { + field = NULL; + map_to_user(fec_hp, coderate_values, &field); + printf("coderate-hp %s, ", field); + } + + if (params_debug){ + printf("coderate-lp %d, ", fec_hp); + } else { + field = NULL; + map_to_user(fec_lp, coderate_values, &field); + printf("coderate-lp %s, ", field); + } + + if (params_debug){ + printf("mode %d, ", mode); + } else { + field = NULL; + map_to_user(mode, mode_values, &field); + printf("mode %s, ", field); + } + + if (params_debug){ + printf("guard %d, ", mode); + } else { + field = NULL; + map_to_user(guard, guard_values, &field); + printf("guard %s, ", field); + } + + if (params_debug){ + printf("hierarchy %d, ", mode); + } else { + field = NULL; + map_to_user(hierarchy, hierarchy_values, &field); + printf("hierarchy %s, ", field); + } + + printf("plp_id %d\n", stream_id); + + printf("vpid 0x%04x, apid 0x%04x, sid 0x%04x\n", vpid, apid, sid); + + ret = zap_to(adapter, frontend, demux, freq * 1000, bw, + vpid, apid, tpid, sid, dvr, rec_psi, bypass, + delsys, modulation, fec_hp, fec_lp, mode, guard, hierarchy, stream_id, human_readable, + params_debug); + + if (interactive) + goto again; + + if (ret) + return TRUE; + + return FALSE; + +syntax_err: + fprintf(stderr, "syntax error in line %u: '%s'\n", line, buf); + } else if (ferror(cfp)) { + fprintf(stderr, "error reading channel list '%s': %d %m\n", + filename, errno); + fclose(cfp); + return FALSE; + } else + break; + } + + fclose(cfp); + + if (!list_channels) { + fprintf(stderr, "channel not found\n"); + + if (!interactive) + return FALSE; + } + if (interactive) + goto again; + + return TRUE; +} + +static void handle_sigint(int sig) +{ + fprintf(stderr, "Interrupted by SIGINT!\n"); + exit(2); +} + +void +bad_usage(char *pname) +{ + fprintf (stderr, usage_str, pname); +} + +int main(int argc, char *argv[]) +{ + const char *home; + char chanfile[2 * PATH_MAX]; + int list_channels = 0; + unsigned int chan_no = 0; + const char *chan_name = NULL; + unsigned int adapter = 0, frontend = 0, demux = 0, dvr = 0, rec_psi = 0; + int bypass = 0; + int opt, copt = 0; + int human_readable = 0; + int params_debug = 0; + int use_vdr_format = 0; + int use_tpid = 0; + + + int delsys = -1; + int modulation = -1; + int fec = -1; + int stream_id = NO_STREAM_ID_FILTER; + + while ((opt = getopt(argc, argv, "M:m:C:O:HDVhqrpn:a:f:d:S:c:l:xib")) != -1) { + switch (opt) { + case '?': + case 'h': + default: + bad_usage(argv[0]); + break; + case 'C': + parse_parameter(--optarg, &fec, coderate_values); + break; + case 'M': + parse_parameter(--optarg, &modulation, modulation_values); + break; + case 'm': + stream_id = strtol(optarg, NULL, 0); + break; + case 'S': + parse_parameter(--optarg, &delsys, system_values); + break; + case 'b': + bypass = 1; + break; + case 'q': + list_channels = 1; + break; + case 'r': + dvr = 1; + break; + case 'n': + chan_no = strtoul(optarg, NULL, 0); + break; + case 'a': + adapter = strtoul(optarg, NULL, 0); + break; + case 'f': + frontend = strtoul(optarg, NULL, 0); + break; + case 'p': + rec_psi = 1; + break; + case 'd': + demux = strtoul(optarg, NULL, 0); + break; + case 'c': + copt = 1; + strncpy(chanfile, optarg, sizeof(chanfile)); + break; + case 'x': + exit_after_tuning = 1; + break; + case 'H': + human_readable = 1; + break; + case 'D': + params_debug = 1; + break; + case 'V': + use_vdr_format = 1; + break; + case 't': + use_tpid = 1; + break; + case 'i': + interactive = 1; + exit_after_tuning = 1; + } + } + if (optind < argc) + chan_name = argv[optind]; + if (chan_name && chan_no) { + bad_usage(argv[0]); + return -1; + } + if (list_channels && (chan_name || chan_no)) { + bad_usage(argv[0]); + return -1; + } + if (!list_channels && !chan_name && !chan_no && !interactive) { + bad_usage(argv[0]); + return -1; + } + + if (!copt) { + if (!(home = getenv("HOME"))) { + fprintf(stderr, "error: $HOME not set\n"); + return TRUE; + } + snprintf(chanfile, sizeof(chanfile), + "%s/.tzap/%i/%s", home, adapter, CHANNEL_FILE); + if (access(chanfile, R_OK)) + snprintf(chanfile, sizeof(chanfile), + "%s/.tzap/%s", home, CHANNEL_FILE); + } + + printf("reading channels from file '%s'\n", chanfile); + + if (rec_psi) + dvr=1; + + signal(SIGINT, handle_sigint); + + if (!read_channels(chanfile, list_channels, chan_no, chan_name, + adapter, frontend, demux, dvr, rec_psi, bypass, delsys, + modulation, fec, -1, -1, -1, -1, stream_id, human_readable, params_debug, + use_vdr_format, use_tpid)) + + return TRUE; + + return FALSE; +}