[linux-dvb] [patch 3/6] rename scan into dvbscan again
Ludwig Nussel
ludwig.nussel at suse.de
Thu Sep 7 15:08:20 CEST 2006
Change got lost during Makefile refactoring
Signed-off-by: lnussel at suse.de
diff -r c51253d44be0 util/scan/Makefile
--- a/util/scan/Makefile Thu Sep 7 12:03:08 2006
+++ b/util/scan/Makefile Thu Sep 7 14:03:14 2006
@@ -5,10 +5,10 @@
dump-vdr.o \
dump-zap.o \
lnb.o \
- scan.o \
+ dvbscan.o \
section.o
-binaries = scan
+binaries = dvbscan
inst_bin = $(binaries)
diff -r c51253d44be0 util/scan/diseqc.c
--- a/util/scan/diseqc.c Thu Sep 7 12:03:08 2006
+++ b/util/scan/diseqc.c Thu Sep 7 14:03:14 2006
@@ -2,7 +2,7 @@
#include <sys/ioctl.h>
#include <time.h>
-#include "scan.h"
+#include "dvbscan.h"
#include "diseqc.h"
diff -r c51253d44be0 util/scan/dvbscan.c
--- /dev/null Thu Sep 7 12:03:08 2006
+++ b/util/scan/dvbscan.c Thu Sep 7 14:03:14 2006
@@ -0,0 +1,2296 @@
+/*
+ * Simple MPEG parser to achieve network/service information.
+ *
+ * refered standards:
+ *
+ * ETSI EN 300 468
+ * ETSI TR 101 211
+ * ETSI ETR 211
+ * ITU-T H.222.0
+ *
+ * 2005-05-10 - Basic ATSC PSIP parsing support added
+ * ATSC Standard Revision B (A65/B)
+ *
+ * Thanks to Sean Device from Triveni for providing access to ATSC signals
+ * and to Kevin Fowlks for his independent ATSC scanning tool.
+ *
+ * Please contribute: It is possible that some descriptors for ATSC are
+ * not parsed yet and thus the result won't be complete.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <assert.h>
+#include <glob.h>
+#include <ctype.h>
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+
+#include "list.h"
+#include "diseqc.h"
+#include "dump-zap.h"
+#include "dump-vdr.h"
+#include "dvbscan.h"
+#include "lnb.h"
+
+#include "atsc_psip_section.h"
+
+static char demux_devname[80];
+
+static struct dvb_frontend_info fe_info = {
+ .type = -1
+};
+
+int verbosity = 2;
+
+static int long_timeout;
+static int current_tp_only;
+static int get_other_nits;
+static int vdr_dump_provider;
+static int vdr_dump_channum;
+static int no_ATSC_PSIP;
+static int ATSC_type=1;
+static int ca_select = 1;
+static int serv_select = 7;
+static int vdr_version = 2;
+static struct lnb_types_st lnb_type;
+static int unique_anon_services;
+
+static enum fe_spectral_inversion spectral_inversion = INVERSION_AUTO;
+
+enum table_type {
+ PAT,
+ PMT,
+ SDT,
+ NIT
+};
+
+enum format {
+ OUTPUT_ZAP,
+ OUTPUT_VDR,
+ OUTPUT_PIDS
+};
+static enum format output_format = OUTPUT_ZAP;
+static int output_format_set = 0;
+
+
+enum polarisation {
+ POLARISATION_HORIZONTAL = 0x00,
+ POLARISATION_VERTICAL = 0x01,
+ POLARISATION_CIRCULAR_LEFT = 0x02,
+ POLARISATION_CIRCULAR_RIGHT = 0x03
+};
+
+enum running_mode {
+ RM_NOT_RUNNING = 0x01,
+ RM_STARTS_SOON = 0x02,
+ RM_PAUSING = 0x03,
+ RM_RUNNING = 0x04
+};
+
+#define AUDIO_CHAN_MAX (32)
+#define CA_SYSTEM_ID_MAX (16)
+
+struct service {
+ struct list_head list;
+ int transport_stream_id;
+ int service_id;
+ char *provider_name;
+ char *service_name;
+ uint16_t pmt_pid;
+ uint16_t pcr_pid;
+ uint16_t video_pid;
+ uint16_t audio_pid[AUDIO_CHAN_MAX];
+ char audio_lang[AUDIO_CHAN_MAX][4];
+ int audio_num;
+ uint16_t ca_id[CA_SYSTEM_ID_MAX];
+ int ca_num;
+ uint16_t teletext_pid;
+ uint16_t subtitling_pid;
+ uint16_t ac3_pid;
+ unsigned int type : 8;
+ unsigned int scrambled : 1;
+ enum running_mode running;
+ void *priv;
+ int channel_num;
+};
+
+struct transponder {
+ struct list_head list;
+ struct list_head services;
+ int network_id;
+ int original_network_id;
+ int transport_stream_id;
+ enum fe_type type;
+ struct dvb_frontend_parameters param;
+ enum polarisation polarisation; /* only for DVB-S */
+ int orbital_pos; /* only for DVB-S */
+ unsigned int we_flag : 1; /* West/East Flag - only for DVB-S */
+ unsigned int scan_done : 1;
+ unsigned int last_tuning_failed : 1;
+ unsigned int other_frequency_flag : 1; /* DVB-T */
+ unsigned int wrong_frequency : 1; /* DVB-T with other_frequency_flag */
+ int n_other_f;
+ uint32_t *other_f; /* DVB-T freqeuency-list descriptor */
+};
+
+
+struct section_buf {
+ struct list_head list;
+ const char *dmx_devname;
+ unsigned int run_once : 1;
+ unsigned int segmented : 1; /* segmented by table_id_ext */
+ int fd;
+ int pid;
+ int table_id;
+ int table_id_ext;
+ int section_version_number;
+ uint8_t section_done[32];
+ int sectionfilter_done;
+ unsigned char buf[1024];
+ time_t timeout;
+ time_t start_time;
+ time_t running_time;
+ struct section_buf *next_seg; /* this is used to handle
+ * segmented tables (like NIT-other)
+ */
+};
+
+static LIST_HEAD(scanned_transponders);
+static LIST_HEAD(new_transponders);
+static struct transponder *current_tp;
+
+
+static void dump_dvb_parameters (FILE *f, struct transponder *p);
+
+static void setup_filter (struct section_buf* s, const char *dmx_devname,
+ int pid, int tid, int tid_ext,
+ int run_once, int segmented, int timeout);
+static void add_filter (struct section_buf *s);
+
+static const char * fe_type2str(fe_type_t t);
+
+/* According to the DVB standards, the combination of network_id and
+ * transport_stream_id should be unique, but in real life the satellite
+ * operators and broadcasters don't care enough to coordinate
+ * the numbering. Thus we identify TPs by frequency (dvbscan handles only
+ * one satellite at a time). Further complication: Different NITs on
+ * one satellite sometimes list the same TP with slightly different
+ * frequencies, so we have to search within some bandwidth.
+ */
+static struct transponder *alloc_transponder(uint32_t frequency)
+{
+ struct transponder *tp = calloc(1, sizeof(*tp));
+
+ tp->param.frequency = frequency;
+ INIT_LIST_HEAD(&tp->list);
+ INIT_LIST_HEAD(&tp->services);
+ list_add_tail(&tp->list, &new_transponders);
+ return tp;
+}
+
+static int is_same_transponder(uint32_t f1, uint32_t f2)
+{
+ uint32_t diff;
+ if (f1 == f2)
+ return 1;
+ diff = (f1 > f2) ? (f1 - f2) : (f2 - f1);
+ //FIXME: use symbolrate etc. to estimate bandwidth
+ if (diff < 2000) {
+ debug("f1 = %u is same TP as f2 = %u\n", f1, f2);
+ return 1;
+ }
+ return 0;
+}
+
+static struct transponder *find_transponder(uint32_t frequency)
+{
+ struct list_head *pos;
+ struct transponder *tp;
+
+ list_for_each(pos, &scanned_transponders) {
+ tp = list_entry(pos, struct transponder, list);
+ if (current_tp_only)
+ return tp;
+ if (is_same_transponder(tp->param.frequency, frequency))
+ return tp;
+ }
+ list_for_each(pos, &new_transponders) {
+ tp = list_entry(pos, struct transponder, list);
+ if (is_same_transponder(tp->param.frequency, frequency))
+ return tp;
+ }
+ return NULL;
+}
+
+static void copy_transponder(struct transponder *d, struct transponder *s)
+{
+ d->network_id = s->network_id;
+ d->original_network_id = s->original_network_id;
+ d->transport_stream_id = s->transport_stream_id;
+ d->type = s->type;
+ memcpy(&d->param, &s->param, sizeof(d->param));
+ d->polarisation = s->polarisation;
+ d->orbital_pos = s->orbital_pos;
+ d->we_flag = s->we_flag;
+ d->scan_done = s->scan_done;
+ d->last_tuning_failed = s->last_tuning_failed;
+ d->other_frequency_flag = s->other_frequency_flag;
+ d->n_other_f = s->n_other_f;
+ if (d->n_other_f) {
+ d->other_f = calloc(d->n_other_f, sizeof(uint32_t));
+ memcpy(d->other_f, s->other_f, d->n_other_f * sizeof(uint32_t));
+ }
+ else
+ d->other_f = NULL;
+}
+
+/* service_ids are guaranteed to be unique within one TP
+ * (the DVB standards say theay should be unique within one
+ * network, but in real life...)
+ */
+static struct service *alloc_service(struct transponder *tp, int service_id)
+{
+ struct service *s = calloc(1, sizeof(*s));
+ INIT_LIST_HEAD(&s->list);
+ s->service_id = service_id;
+ s->transport_stream_id = tp->transport_stream_id;
+ list_add_tail(&s->list, &tp->services);
+ return s;
+}
+
+static struct service *find_service(struct transponder *tp, int service_id)
+{
+ struct list_head *pos;
+ struct service *s;
+
+ list_for_each(pos, &tp->services) {
+ s = list_entry(pos, struct service, list);
+ if (s->service_id == service_id)
+ return s;
+ }
+ return NULL;
+}
+
+
+static void parse_ca_identifier_descriptor (const unsigned char *buf,
+ struct service *s)
+{
+ unsigned char len = buf [1];
+ unsigned int i;
+
+ buf += 2;
+
+ if (len > sizeof(s->ca_id)) {
+ len = sizeof(s->ca_id);
+ warning("too many CA system ids\n");
+ }
+ memcpy(s->ca_id, buf, len);
+ for (i = 0; i < len / sizeof(s->ca_id[0]); i++)
+ moreverbose(" CA ID 0x%04x\n", s->ca_id[i]);
+}
+
+
+static void parse_iso639_language_descriptor (const unsigned char *buf, struct service *s)
+{
+ unsigned char len = buf [1];
+
+ buf += 2;
+
+ if (len >= 4) {
+ debug(" LANG=%.3s %d\n", buf, buf[3]);
+ memcpy(s->audio_lang[s->audio_num], buf, 3);
+#if 0
+ /* seems like the audio_type is wrong all over the place */
+ //if (buf[3] == 0) -> normal
+ if (buf[3] == 1)
+ s->audio_lang[s->audio_num][3] = '!'; /* clean effects (no language) */
+ else if (buf[3] == 2)
+ s->audio_lang[s->audio_num][3] = '?'; /* for the hearing impaired */
+ else if (buf[3] == 3)
+ s->audio_lang[s->audio_num][3] = '+'; /* visually impaired commentary */
+#endif
+ }
+}
+
+static void parse_network_name_descriptor (const unsigned char *buf, void *dummy)
+{
+ (void)dummy;
+
+ unsigned char len = buf [1];
+
+ info("Network Name '%.*s'\n", len, buf + 2);
+}
+
+static void parse_terrestrial_uk_channel_number (const unsigned char *buf, void *dummy)
+{
+ (void)dummy;
+
+ int i, n, channel_num, service_id;
+ struct list_head *p1, *p2;
+ struct transponder *t;
+ struct service *s;
+
+ // 32 bits per record
+ n = buf[1] / 4;
+ if (n < 1)
+ return;
+
+ // desc id, desc len, (service id, service number)
+ buf += 2;
+ for (i = 0; i < n; i++) {
+ service_id = (buf[0]<<8)|(buf[1]&0xff);
+ channel_num = ((buf[2]&0x03)<<8)|(buf[3]&0xff);
+ debug("Service ID 0x%x has channel number %d ", service_id, channel_num);
+ list_for_each(p1, &scanned_transponders) {
+ t = list_entry(p1, struct transponder, list);
+ list_for_each(p2, &t->services) {
+ s = list_entry(p2, struct service, list);
+ if (s->service_id == service_id)
+ s->channel_num = channel_num;
+ }
+ }
+ buf += 4;
+ }
+}
+
+
+static long bcd32_to_cpu (const int b0, const int b1, const int b2, const int b3)
+{
+ return ((b0 >> 4) & 0x0f) * 10000000 + (b0 & 0x0f) * 1000000 +
+ ((b1 >> 4) & 0x0f) * 100000 + (b1 & 0x0f) * 10000 +
+ ((b2 >> 4) & 0x0f) * 1000 + (b2 & 0x0f) * 100 +
+ ((b3 >> 4) & 0x0f) * 10 + (b3 & 0x0f);
+}
+
+
+static const fe_code_rate_t fec_tab [8] = {
+ FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4,
+ FEC_5_6, FEC_7_8, FEC_NONE, FEC_NONE
+};
+
+
+static const fe_modulation_t qam_tab [6] = {
+ QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256
+};
+
+
+static void parse_cable_delivery_system_descriptor (const unsigned char *buf,
+ struct transponder *t)
+{
+ if (!t) {
+ warning("cable_delivery_system_descriptor outside transport stream definition (ignored)\n");
+ return;
+ }
+ t->type = FE_QAM;
+
+ t->param.frequency = bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]);
+ t->param.frequency *= 100;
+ t->param.u.qam.fec_inner = fec_tab[buf[12] & 0x07];
+ t->param.u.qam.symbol_rate = 10 * bcd32_to_cpu (buf[9],
+ buf[10],
+ buf[11],
+ buf[12] & 0xf0);
+ if ((buf[8] & 0x0f) > 5)
+ t->param.u.qam.modulation = QAM_AUTO;
+ else
+ t->param.u.qam.modulation = qam_tab[buf[8] & 0x0f];
+ t->param.inversion = spectral_inversion;
+
+ if (verbosity >= 5) {
+ debug("%#04x/%#04x ", t->network_id, t->transport_stream_id);
+ dump_dvb_parameters (stderr, t);
+ if (t->scan_done)
+ dprintf(5, " (done)");
+ if (t->last_tuning_failed)
+ dprintf(5, " (tuning failed)");
+ dprintf(5, "\n");
+ }
+}
+
+static void parse_satellite_delivery_system_descriptor (const unsigned char *buf,
+ struct transponder *t)
+{
+ if (!t) {
+ warning("satellite_delivery_system_descriptor outside transport stream definition (ignored)\n");
+ return;
+ }
+ t->type = FE_QPSK;
+ t->param.frequency = 10 * bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]);
+ t->param.u.qpsk.fec_inner = fec_tab[buf[12] & 0x07];
+ t->param.u.qpsk.symbol_rate = 10 * bcd32_to_cpu (buf[9],
+ buf[10],
+ buf[11],
+ buf[12] & 0xf0);
+
+ t->polarisation = (buf[8] >> 5) & 0x03;
+ t->param.inversion = spectral_inversion;
+
+ t->orbital_pos = bcd32_to_cpu (0x00, 0x00, buf[6], buf[7]);
+ t->we_flag = buf[8] >> 7;
+
+ if (verbosity >= 5) {
+ debug("%#04x/%#04x ", t->network_id, t->transport_stream_id);
+ dump_dvb_parameters (stderr, t);
+ if (t->scan_done)
+ dprintf(5, " (done)");
+ if (t->last_tuning_failed)
+ dprintf(5, " (tuning failed)");
+ dprintf(5, "\n");
+ }
+}
+
+
+static void parse_terrestrial_delivery_system_descriptor (const unsigned char *buf,
+ struct transponder *t)
+{
+ static const fe_modulation_t m_tab [] = { QPSK, QAM_16, QAM_64, QAM_AUTO };
+ static const fe_code_rate_t ofec_tab [8] = { FEC_1_2, FEC_2_3, FEC_3_4,
+ FEC_5_6, FEC_7_8 };
+ struct dvb_ofdm_parameters *o;
+
+ if (!t) {
+ warning("terrestrial_delivery_system_descriptor outside transport stream definition (ignored)\n");
+ return;
+ }
+ o = &t->param.u.ofdm;
+ t->type = FE_OFDM;
+
+ t->param.frequency = (buf[2] << 24) | (buf[3] << 16);
+ t->param.frequency |= (buf[4] << 8) | buf[5];
+ t->param.frequency *= 10;
+ t->param.inversion = spectral_inversion;
+
+ o->bandwidth = BANDWIDTH_8_MHZ + ((buf[6] >> 5) & 0x3);
+ o->constellation = m_tab[(buf[7] >> 6) & 0x3];
+ o->hierarchy_information = HIERARCHY_NONE + ((buf[7] >> 3) & 0x3);
+
+ if ((buf[7] & 0x7) > 4)
+ o->code_rate_HP = FEC_AUTO;
+ else
+ o->code_rate_HP = ofec_tab [buf[7] & 0x7];
+
+ if (((buf[8] >> 5) & 0x7) > 4)
+ o->code_rate_LP = FEC_AUTO;
+ else
+ o->code_rate_LP = ofec_tab [(buf[8] >> 5) & 0x7];
+
+ o->guard_interval = GUARD_INTERVAL_1_32 + ((buf[8] >> 3) & 0x3);
+
+ o->transmission_mode = (buf[8] & 0x2) ?
+ TRANSMISSION_MODE_8K :
+ TRANSMISSION_MODE_2K;
+
+ t->other_frequency_flag = (buf[8] & 0x01);
+
+ if (verbosity >= 5) {
+ debug("%#04x/%#04x ", t->network_id, t->transport_stream_id);
+ dump_dvb_parameters (stderr, t);
+ if (t->scan_done)
+ dprintf(5, " (done)");
+ if (t->last_tuning_failed)
+ dprintf(5, " (tuning failed)");
+ dprintf(5, "\n");
+ }
+}
+
+static void parse_frequency_list_descriptor (const unsigned char *buf,
+ struct transponder *t)
+{
+ int n, i;
+ typeof(*t->other_f) f;
+
+ if (!t) {
+ warning("frequency_list_descriptor outside transport stream definition (ignored)\n");
+ return;
+ }
+ if (t->other_f)
+ return;
+
+ n = (buf[1] - 1) / 4;
+ if (n < 1 || (buf[2] & 0x03) != 3)
+ return;
+
+ t->other_f = calloc(n, sizeof(*t->other_f));
+ t->n_other_f = n;
+ buf += 3;
+ for (i = 0; i < n; i++) {
+ f = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ t->other_f[i] = f * 10;
+ buf += 4;
+ }
+}
+
+static void parse_service_descriptor (const unsigned char *buf, struct service *s)
+{
+ unsigned char len;
+ unsigned char *src, *dest;
+
+ s->type = buf[2];
+
+ buf += 3;
+ len = *buf;
+ buf++;
+
+ if (s->provider_name)
+ free (s->provider_name);
+
+ s->provider_name = malloc (len + 1);
+ memcpy (s->provider_name, buf, len);
+ s->provider_name[len] = '\0';
+
+ /* remove control characters (FIXME: handle short/long name) */
+ /* FIXME: handle character set correctly (e.g. via iconv)
+ * c.f. EN 300 468 annex A */
+ for (src = dest = (unsigned char *) s->provider_name; *src; src++)
+ if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f))
+ *dest++ = *src;
+ *dest = '\0';
+ if (!s->provider_name[0]) {
+ /* zap zero length names */
+ free (s->provider_name);
+ s->provider_name = 0;
+ }
+
+ if (s->service_name)
+ free (s->service_name);
+
+ buf += len;
+ len = *buf;
+ buf++;
+
+ s->service_name = malloc (len + 1);
+ memcpy (s->service_name, buf, len);
+ s->service_name[len] = '\0';
+
+ /* remove control characters (FIXME: handle short/long name) */
+ /* FIXME: handle character set correctly (e.g. via iconv)
+ * c.f. EN 300 468 annex A */
+ for (src = dest = (unsigned char *) s->service_name; *src; src++)
+ if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f))
+ *dest++ = *src;
+ *dest = '\0';
+ if (!s->service_name[0]) {
+ /* zap zero length names */
+ free (s->service_name);
+ s->service_name = 0;
+ }
+
+ info("0x%04x 0x%04x: pmt_pid 0x%04x %s -- %s (%s%s)\n",
+ s->transport_stream_id,
+ s->service_id,
+ s->pmt_pid,
+ s->provider_name, s->service_name,
+ s->running == RM_NOT_RUNNING ? "not running" :
+ s->running == RM_STARTS_SOON ? "starts soon" :
+ s->running == RM_PAUSING ? "pausing" :
+ s->running == RM_RUNNING ? "running" : "???",
+ s->scrambled ? ", scrambled" : "");
+}
+
+static int find_descriptor(uint8_t tag, const unsigned char *buf,
+ int descriptors_loop_len,
+ const unsigned char **desc, int *desc_len)
+{
+ while (descriptors_loop_len > 0) {
+ unsigned char descriptor_tag = buf[0];
+ unsigned char descriptor_len = buf[1] + 2;
+
+ if (!descriptor_len) {
+ warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag);
+ break;
+ }
+
+ if (tag == descriptor_tag) {
+ if (desc)
+ *desc = buf;
+ if (desc_len)
+ *desc_len = descriptor_len;
+ return 1;
+ }
+
+ buf += descriptor_len;
+ descriptors_loop_len -= descriptor_len;
+ }
+ return 0;
+}
+
+static void parse_descriptors(enum table_type t, const unsigned char *buf,
+ int descriptors_loop_len, void *data)
+{
+ while (descriptors_loop_len > 0) {
+ unsigned char descriptor_tag = buf[0];
+ unsigned char descriptor_len = buf[1] + 2;
+
+ if (!descriptor_len) {
+ warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag);
+ break;
+ }
+
+ switch (descriptor_tag) {
+ case 0x0a:
+ if (t == PMT)
+ parse_iso639_language_descriptor (buf, data);
+ break;
+
+ case 0x40:
+ if (t == NIT)
+ parse_network_name_descriptor (buf, data);
+ break;
+
+ case 0x43:
+ if (t == NIT)
+ parse_satellite_delivery_system_descriptor (buf, data);
+ break;
+
+ case 0x44:
+ if (t == NIT)
+ parse_cable_delivery_system_descriptor (buf, data);
+ break;
+
+ case 0x48:
+ if (t == SDT)
+ parse_service_descriptor (buf, data);
+ break;
+
+ case 0x53:
+ if (t == SDT)
+ parse_ca_identifier_descriptor (buf, data);
+ break;
+
+ case 0x5a:
+ if (t == NIT)
+ parse_terrestrial_delivery_system_descriptor (buf, data);
+ break;
+
+ case 0x62:
+ if (t == NIT)
+ parse_frequency_list_descriptor (buf, data);
+ break;
+
+ case 0x83:
+ /* 0x83 is in the privately defined range of descriptor tags,
+ * so we parse this only if the user says so to avoid
+ * problems when 0x83 is something entirely different... */
+ if (t == NIT && vdr_dump_channum)
+ parse_terrestrial_uk_channel_number (buf, data);
+ break;
+
+ default:
+ verbosedebug("skip descriptor 0x%02x\n", descriptor_tag);
+ };
+
+ buf += descriptor_len;
+ descriptors_loop_len -= descriptor_len;
+ }
+}
+
+
+static void parse_pat(const unsigned char *buf, int section_length,
+ int transport_stream_id)
+{
+ (void)transport_stream_id;
+
+ while (section_length > 0) {
+ struct service *s;
+ int service_id = (buf[0] << 8) | buf[1];
+
+ if (service_id == 0)
+ goto skip; /* nit pid entry */
+
+ /* SDT might have been parsed first... */
+ s = find_service(current_tp, service_id);
+ if (!s)
+ s = alloc_service(current_tp, service_id);
+ s->pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3];
+ if (!s->priv && s->pmt_pid) {
+ s->priv = malloc(sizeof(struct section_buf));
+ setup_filter(s->priv, demux_devname,
+ s->pmt_pid, 0x02, s->service_id, 1, 0, 5);
+
+ add_filter (s->priv);
+ }
+
+skip:
+ buf += 4;
+ section_length -= 4;
+ };
+}
+
+
+static void parse_pmt (const unsigned char *buf, int section_length, int service_id)
+{
+ int program_info_len;
+ struct service *s;
+ char msg_buf[14 * AUDIO_CHAN_MAX + 1];
+ char *tmp;
+ int i;
+
+ s = find_service (current_tp, service_id);
+ if (!s) {
+ error("PMT for serivce_id 0x%04x was not in PAT\n", service_id);
+ return;
+ }
+
+ s->pcr_pid = ((buf[0] & 0x1f) << 8) | buf[1];
+
+ program_info_len = ((buf[2] & 0x0f) << 8) | buf[3];
+
+ buf += program_info_len + 4;
+ section_length -= program_info_len + 4;
+
+ while (section_length >= 5) {
+ int ES_info_len = ((buf[3] & 0x0f) << 8) | buf[4];
+ int elementary_pid = ((buf[1] & 0x1f) << 8) | buf[2];
+
+ switch (buf[0]) {
+ case 0x01:
+ case 0x02:
+ moreverbose(" VIDEO : PID 0x%04x\n", elementary_pid);
+ if (s->video_pid == 0)
+ s->video_pid = elementary_pid;
+ break;
+ case 0x03:
+ case 0x81: /* Audio per ATSC A/53B [2] Annex B */
+ case 0x04:
+ moreverbose(" AUDIO : PID 0x%04x\n", elementary_pid);
+ if (s->audio_num < AUDIO_CHAN_MAX) {
+ s->audio_pid[s->audio_num] = elementary_pid;
+ parse_descriptors (PMT, buf + 5, ES_info_len, s);
+ s->audio_num++;
+ }
+ else
+ warning("more than %i audio channels, truncating\n",
+ AUDIO_CHAN_MAX);
+ break;
+ case 0x06:
+ if (find_descriptor(0x56, buf + 5, ES_info_len, NULL, NULL)) {
+ moreverbose(" TELETEXT : PID 0x%04x\n", elementary_pid);
+ s->teletext_pid = elementary_pid;
+ break;
+ }
+ else if (find_descriptor(0x59, buf + 5, ES_info_len, NULL, NULL)) {
+ /* Note: The subtitling descriptor can also signal
+ * teletext subtitling, but then the teletext descriptor
+ * will also be present; so we can be quite confident
+ * that we catch DVB subtitling streams only here, w/o
+ * parsing the descriptor. */
+ moreverbose(" SUBTITLING: PID 0x%04x\n", elementary_pid);
+ s->subtitling_pid = elementary_pid;
+ break;
+ }
+ else if (find_descriptor(0x6a, buf + 5, ES_info_len, NULL, NULL)) {
+ moreverbose(" AC3 : PID 0x%04x\n", elementary_pid);
+ s->ac3_pid = elementary_pid;
+ break;
+ }
+ /* fall through */
+ default:
+ moreverbose(" OTHER : PID 0x%04x TYPE 0x%02x\n", elementary_pid, buf[0]);
+ };
+
+ buf += ES_info_len + 5;
+ section_length -= ES_info_len + 5;
+ };
+
+
+ tmp = msg_buf;
+ tmp += sprintf(tmp, "0x%04x (%.4s)", s->audio_pid[0], s->audio_lang[0]);
+
+ if (s->audio_num > AUDIO_CHAN_MAX) {
+ warning("more than %i audio channels: %i, truncating to %i\n",
+ AUDIO_CHAN_MAX, s->audio_num, AUDIO_CHAN_MAX);
+ s->audio_num = AUDIO_CHAN_MAX;
+ }
+
+ for (i=1; i<s->audio_num; i++)
+ tmp += sprintf(tmp, ", 0x%04x (%.4s)", s->audio_pid[i], s->audio_lang[i]);
+
+ debug("0x%04x 0x%04x: %s -- %s, pmt_pid 0x%04x, vpid 0x%04x, apid %s\n",
+ s->transport_stream_id,
+ s->service_id,
+ s->provider_name, s->service_name,
+ s->pmt_pid, s->video_pid, msg_buf);
+}
+
+
+static void parse_nit (const unsigned char *buf, int section_length, int network_id)
+{
+ int descriptors_loop_len = ((buf[0] & 0x0f) << 8) | buf[1];
+
+ if (section_length < descriptors_loop_len + 4)
+ {
+ warning("section too short: network_id == 0x%04x, section_length == %i, "
+ "descriptors_loop_len == %i\n",
+ network_id, section_length, descriptors_loop_len);
+ return;
+ }
+
+ parse_descriptors (NIT, buf + 2, descriptors_loop_len, NULL);
+
+ section_length -= descriptors_loop_len + 4;
+ buf += descriptors_loop_len + 4;
+
+ while (section_length > 6) {
+ int transport_stream_id = (buf[0] << 8) | buf[1];
+ struct transponder *t, tn;
+
+ descriptors_loop_len = ((buf[4] & 0x0f) << 8) | buf[5];
+
+ if (section_length < descriptors_loop_len + 4)
+ {
+ warning("section too short: transport_stream_id == 0x%04x, "
+ "section_length == %i, descriptors_loop_len == %i\n",
+ transport_stream_id, section_length,
+ descriptors_loop_len);
+ break;
+ }
+
+ debug("transport_stream_id 0x%04x\n", transport_stream_id);
+
+ memset(&tn, 0, sizeof(tn));
+ tn.type = -1;
+ tn.network_id = network_id;
+ tn.original_network_id = (buf[2] << 8) | buf[3];
+ tn.transport_stream_id = transport_stream_id;
+
+ parse_descriptors (NIT, buf + 6, descriptors_loop_len, &tn);
+
+ if (tn.type == fe_info.type) {
+ /* only add if develivery_descriptor matches FE type */
+ t = find_transponder(tn.param.frequency);
+ if (!t)
+ t = alloc_transponder(tn.param.frequency);
+ copy_transponder(t, &tn);
+ }
+
+ section_length -= descriptors_loop_len + 6;
+ buf += descriptors_loop_len + 6;
+ }
+}
+
+
+static void parse_sdt (const unsigned char *buf, int section_length,
+ int transport_stream_id)
+{
+ (void)transport_stream_id;
+
+ buf += 3; /* skip original network id + reserved field */
+
+ while (section_length >= 5) {
+ int service_id = (buf[0] << 8) | buf[1];
+ int descriptors_loop_len = ((buf[3] & 0x0f) << 8) | buf[4];
+ struct service *s;
+
+ if (section_length < descriptors_loop_len || !descriptors_loop_len)
+ {
+ warning("section too short: service_id == 0x%02x, section_length == %i, "
+ "descriptors_loop_len == %i\n",
+ service_id, section_length,
+ descriptors_loop_len);
+ break;
+ }
+
+ s = find_service(current_tp, service_id);
+ if (!s)
+ /* maybe PAT has not yet been parsed... */
+ s = alloc_service(current_tp, service_id);
+
+ s->running = (buf[3] >> 5) & 0x7;
+ s->scrambled = (buf[3] >> 4) & 1;
+
+ parse_descriptors (SDT, buf + 5, descriptors_loop_len, s);
+
+ section_length -= descriptors_loop_len + 5;
+ buf += descriptors_loop_len + 5;
+ };
+}
+
+/* ATSC PSIP VCT */
+static void parse_atsc_service_loc_desc(struct service *s,const unsigned char *buf)
+{
+ struct ATSC_service_location_descriptor d = read_ATSC_service_location_descriptor(buf);
+ int i;
+ unsigned char *b = (unsigned char *) buf+5;
+
+ s->pcr_pid = d.PCR_PID;
+ for (i=0; i < d.number_elements; i++) {
+ struct ATSC_service_location_element e = read_ATSC_service_location_element(b);
+ switch (e.stream_type) {
+ case 0x02: /* video */
+ s->video_pid = e.elementary_PID;
+ moreverbose(" VIDEO : PID 0x%04x\n", e.elementary_PID);
+ break;
+ case 0x81: /* ATSC audio */
+ if (s->audio_num < AUDIO_CHAN_MAX) {
+ s->audio_pid[s->audio_num] = e.elementary_PID;
+ s->audio_lang[s->audio_num][0] = (e.ISO_639_language_code >> 16) & 0xff;
+ s->audio_lang[s->audio_num][1] = (e.ISO_639_language_code >> 8) & 0xff;
+ s->audio_lang[s->audio_num][2] = e.ISO_639_language_code & 0xff;
+ s->audio_num++;
+ }
+ moreverbose(" AUDIO : PID 0x%04x lang: %s\n",e.elementary_PID,s->audio_lang[s->audio_num-1]);
+
+ break;
+ default:
+ warning("unhandled stream_type: %x\n",e.stream_type);
+ break;
+ };
+ b += 6;
+ }
+}
+
+static void parse_atsc_ext_chan_name_desc(struct service *s,const unsigned char *buf)
+{
+ unsigned char *b = (unsigned char *) buf+2;
+ int i,j;
+ int num_str = b[0];
+
+ b++;
+ for (i = 0; i < num_str; i++) {
+ int num_seg = b[3];
+ b += 4; /* skip lang code */
+ for (j = 0; j < num_seg; j++) {
+ int comp_type = b[0],/* mode = b[1],*/ num_bytes = b[2];
+
+ switch (comp_type) {
+ case 0x00:
+ if (s->service_name)
+ free(s->service_name);
+ s->service_name = malloc(num_bytes * sizeof(char) + 1);
+ memcpy(s->service_name,&b[3],num_bytes);
+ s->service_name[num_bytes] = '\0';
+ break;
+ default:
+ warning("compressed strings are not supported yet\n");
+ break;
+ }
+ b += 3 + num_bytes;
+ }
+ }
+}
+
+static void parse_psip_descriptors(struct service *s,const unsigned char *buf,int len)
+{
+ unsigned char *b = (unsigned char *) buf;
+ int desc_len;
+ while (len > 0) {
+ desc_len = b[1];
+ switch (b[0]) {
+ case ATSC_SERVICE_LOCATION_DESCRIPTOR_ID:
+ parse_atsc_service_loc_desc(s,b);
+ break;
+ case ATSC_EXTENDED_CHANNEL_NAME_DESCRIPTOR_ID:
+ parse_atsc_ext_chan_name_desc(s,b);
+ break;
+ default:
+ warning("unhandled psip descriptor: %02x\n",b[0]);
+ break;
+ }
+ b += 2 + desc_len;
+ len -= 2 + desc_len;
+ }
+}
+
+static void parse_psip_vct (const unsigned char *buf, int section_length,
+ int table_id, int transport_stream_id)
+{
+ (void)section_length;
+ (void)table_id;
+ (void)transport_stream_id;
+
+/* int protocol_version = buf[0];*/
+ int num_channels_in_section = buf[1];
+ int i;
+ int pseudo_id = 0xffff;
+ unsigned char *b = (unsigned char *) buf + 2;
+
+ for (i = 0; i < num_channels_in_section; i++) {
+ struct service *s;
+ struct tvct_channel ch = read_tvct_channel(b);
+
+ switch (ch.service_type) {
+ case 0x01:
+ info("analog channels won't be put info channels.conf\n");
+ break;
+ case 0x02: /* ATSC TV */
+ case 0x03: /* ATSC Radio */
+ break;
+ case 0x04: /* ATSC Data */
+ default:
+ continue;
+ }
+
+ if (ch.program_number == 0)
+ ch.program_number = --pseudo_id;
+
+ s = find_service(current_tp, ch.program_number);
+ if (!s)
+ s = alloc_service(current_tp, ch.program_number);
+
+ if (s->service_name)
+ free(s->service_name);
+
+ s->service_name = malloc(7*sizeof(unsigned char));
+ /* TODO find a better solution to convert UTF-16 */
+ s->service_name[0] = ch.short_name0;
+ s->service_name[1] = ch.short_name1;
+ s->service_name[2] = ch.short_name2;
+ s->service_name[3] = ch.short_name3;
+ s->service_name[4] = ch.short_name4;
+ s->service_name[5] = ch.short_name5;
+ s->service_name[6] = ch.short_name6;
+
+ parse_psip_descriptors(s,&b[32],ch.descriptors_length);
+
+ s->channel_num = ch.major_channel_number << 10 | ch.minor_channel_number;
+
+ if (ch.hidden) {
+ s->running = RM_NOT_RUNNING;
+ info("service is not running, pseudo program_number.");
+ } else {
+ s->running = RM_RUNNING;
+ info("service is running.");
+ }
+
+ info(" Channel number: %d:%d. Name: '%s'\n",
+ ch.major_channel_number, ch.minor_channel_number,s->service_name);
+
+ b += 32 + ch.descriptors_length;
+ }
+}
+
+static int get_bit (uint8_t *bitfield, int bit)
+{
+ return (bitfield[bit/8] >> (bit % 8)) & 1;
+}
+
+static void set_bit (uint8_t *bitfield, int bit)
+{
+ bitfield[bit/8] |= 1 << (bit % 8);
+}
+
+
+/**
+ * returns 0 when more sections are expected
+ * 1 when all sections are read on this pid
+ * -1 on invalid table id
+ */
+static int parse_section (struct section_buf *s)
+{
+ const unsigned char *buf = s->buf;
+ int table_id;
+ int section_length;
+ int table_id_ext;
+ int section_version_number;
+ int section_number;
+ int last_section_number;
+ int i;
+
+ table_id = buf[0];
+
+ if (s->table_id != table_id)
+ return -1;
+
+ section_length = ((buf[1] & 0x0f) << 8) | buf[2];
+
+ table_id_ext = (buf[3] << 8) | buf[4];
+ section_version_number = (buf[5] >> 1) & 0x1f;
+ section_number = buf[6];
+ last_section_number = buf[7];
+
+ if (s->segmented && s->table_id_ext != -1 && s->table_id_ext != table_id_ext) {
+ /* find or allocate actual section_buf matching table_id_ext */
+ while (s->next_seg) {
+ s = s->next_seg;
+ if (s->table_id_ext == table_id_ext)
+ break;
+ }
+ if (s->table_id_ext != table_id_ext) {
+ assert(s->next_seg == NULL);
+ s->next_seg = calloc(1, sizeof(struct section_buf));
+ s->next_seg->segmented = s->segmented;
+ s->next_seg->run_once = s->run_once;
+ s->next_seg->timeout = s->timeout;
+ s = s->next_seg;
+ s->table_id = table_id;
+ s->table_id_ext = table_id_ext;
+ s->section_version_number = section_version_number;
+ }
+ }
+
+ if (s->section_version_number != section_version_number ||
+ s->table_id_ext != table_id_ext) {
+ struct section_buf *next_seg = s->next_seg;
+
+ if (s->section_version_number != -1 && s->table_id_ext != -1)
+ debug("section version_number or table_id_ext changed "
+ "%d -> %d / %04x -> %04x\n",
+ s->section_version_number, section_version_number,
+ s->table_id_ext, table_id_ext);
+ s->table_id_ext = table_id_ext;
+ s->section_version_number = section_version_number;
+ s->sectionfilter_done = 0;
+ memset (s->section_done, 0, sizeof(s->section_done));
+ s->next_seg = next_seg;
+ }
+
+ buf += 8; /* past generic table header */
+ section_length -= 5 + 4; /* header + crc */
+ if (section_length < 0) {
+ warning("truncated section (PID 0x%04x, lenght %d)",
+ s->pid, section_length + 9);
+ return 0;
+ }
+
+ if (!get_bit(s->section_done, section_number)) {
+ set_bit (s->section_done, section_number);
+
+ debug("pid 0x%02x tid 0x%02x table_id_ext 0x%04x, "
+ "%i/%i (version %i)\n",
+ s->pid, table_id, table_id_ext, section_number,
+ last_section_number, section_version_number);
+
+ switch (table_id) {
+ case 0x00:
+ verbose("PAT\n");
+ parse_pat (buf, section_length, table_id_ext);
+ break;
+
+ case 0x02:
+ verbose("PMT 0x%04x for service 0x%04x\n", s->pid, table_id_ext);
+ parse_pmt (buf, section_length, table_id_ext);
+ break;
+
+ case 0x41:
+ verbose("////////////////////////////////////////////// NIT other\n");
+ case 0x40:
+ verbose("NIT (%s TS)\n", table_id == 0x40 ? "actual":"other");
+ parse_nit (buf, section_length, table_id_ext);
+ break;
+
+ case 0x42:
+ case 0x46:
+ verbose("SDT (%s TS)\n", table_id == 0x42 ? "actual":"other");
+ parse_sdt (buf, section_length, table_id_ext);
+ break;
+
+ case 0xc8:
+ case 0xc9:
+ verbose("ATSC VCT\n");
+ parse_psip_vct(buf, section_length, table_id, table_id_ext);
+ break;
+ default:
+ ;
+ };
+
+ for (i = 0; i <= last_section_number; i++)
+ if (get_bit (s->section_done, i) == 0)
+ break;
+
+ if (i > last_section_number)
+ s->sectionfilter_done = 1;
+ }
+
+ if (s->segmented) {
+ /* always wait for timeout; this is because we don't now how
+ * many segments there are
+ */
+ return 0;
+ }
+ else if (s->sectionfilter_done)
+ return 1;
+
+ return 0;
+}
+
+
+static int read_sections (struct section_buf *s)
+{
+ int section_length, count;
+
+ if (s->sectionfilter_done && !s->segmented)
+ return 1;
+
+ /* the section filter API guarantess that we get one full section
+ * per read(), provided that the buffer is large enough (it is)
+ */
+ if (((count = read (s->fd, s->buf, sizeof(s->buf))) < 0) && errno == EOVERFLOW)
+ count = read (s->fd, s->buf, sizeof(s->buf));
+ if (count < 0) {
+ errorn("read_sections: read error");
+ return -1;
+ }
+
+ if (count < 4)
+ return -1;
+
+ section_length = ((s->buf[1] & 0x0f) << 8) | s->buf[2];
+
+ if (count != section_length + 3)
+ return -1;
+
+ if (parse_section(s) == 1)
+ return 1;
+
+ return 0;
+}
+
+
+static LIST_HEAD(running_filters);
+static LIST_HEAD(waiting_filters);
+static int n_running;
+#define MAX_RUNNING 27
+static struct pollfd poll_fds[MAX_RUNNING];
+static struct section_buf* poll_section_bufs[MAX_RUNNING];
+
+
+static void setup_filter (struct section_buf* s, const char *dmx_devname,
+ int pid, int tid, int tid_ext,
+ int run_once, int segmented, int timeout)
+{
+ memset (s, 0, sizeof(struct section_buf));
+
+ s->fd = -1;
+ s->dmx_devname = dmx_devname;
+ s->pid = pid;
+ s->table_id = tid;
+
+ s->run_once = run_once;
+ s->segmented = segmented;
+
+ if (long_timeout)
+ s->timeout = 5 * timeout;
+ else
+ s->timeout = timeout;
+
+ s->table_id_ext = tid_ext;
+ s->section_version_number = -1;
+
+ INIT_LIST_HEAD (&s->list);
+}
+
+static void update_poll_fds(void)
+{
+ struct list_head *p;
+ struct section_buf* s;
+ int i;
+
+ memset(poll_section_bufs, 0, sizeof(poll_section_bufs));
+ for (i = 0; i < MAX_RUNNING; i++)
+ poll_fds[i].fd = -1;
+ i = 0;
+ list_for_each (p, &running_filters) {
+ if (i >= MAX_RUNNING)
+ fatal("too many poll_fds\n");
+ s = list_entry (p, struct section_buf, list);
+ if (s->fd == -1)
+ fatal("s->fd == -1 on running_filters\n");
+ verbosedebug("poll fd %d\n", s->fd);
+ poll_fds[i].fd = s->fd;
+ poll_fds[i].events = POLLIN;
+ poll_fds[i].revents = 0;
+ poll_section_bufs[i] = s;
+ i++;
+ }
+ if (i != n_running)
+ fatal("n_running is hosed\n");
+}
+
+static int start_filter (struct section_buf* s)
+{
+ struct dmx_sct_filter_params f;
+
+ if (n_running >= MAX_RUNNING)
+ goto err0;
+ if ((s->fd = open (s->dmx_devname, O_RDWR | O_NONBLOCK)) < 0)
+ goto err0;
+
+ verbosedebug("start filter pid 0x%04x table_id 0x%02x\n", s->pid, s->table_id);
+
+ memset(&f, 0, sizeof(f));
+
+ f.pid = (uint16_t) s->pid;
+
+ if (s->table_id < 0x100 && s->table_id > 0) {
+ f.filter.filter[0] = (uint8_t) s->table_id;
+ f.filter.mask[0] = 0xff;
+ }
+ if (s->table_id_ext < 0x10000 && s->table_id_ext > 0) {
+ f.filter.filter[1] = (uint8_t) ((s->table_id_ext >> 8) & 0xff);
+ f.filter.filter[2] = (uint8_t) (s->table_id_ext & 0xff);
+ f.filter.mask[1] = 0xff;
+ f.filter.mask[2] = 0xff;
+ }
+
+ f.timeout = 0;
+ f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
+
+ if (ioctl(s->fd, DMX_SET_FILTER, &f) == -1) {
+ errorn ("ioctl DMX_SET_FILTER failed");
+ goto err1;
+ }
+
+ s->sectionfilter_done = 0;
+ time(&s->start_time);
+
+ list_del_init (&s->list); /* might be in waiting filter list */
+ list_add (&s->list, &running_filters);
+
+ n_running++;
+ update_poll_fds();
+
+ return 0;
+
+err1:
+ ioctl (s->fd, DMX_STOP);
+ close (s->fd);
+err0:
+ return -1;
+}
+
+
+static void stop_filter (struct section_buf *s)
+{
+ verbosedebug("stop filter pid 0x%04x\n", s->pid);
+ ioctl (s->fd, DMX_STOP);
+ close (s->fd);
+ s->fd = -1;
+ list_del (&s->list);
+ s->running_time += time(NULL) - s->start_time;
+
+ n_running--;
+ update_poll_fds();
+}
+
+
+static void add_filter (struct section_buf *s)
+{
+ verbosedebug("add filter pid 0x%04x\n", s->pid);
+ if (start_filter (s))
+ list_add_tail (&s->list, &waiting_filters);
+}
+
+
+static void remove_filter (struct section_buf *s)
+{
+ verbosedebug("remove filter pid 0x%04x\n", s->pid);
+ stop_filter (s);
+
+ while (!list_empty(&waiting_filters)) {
+ struct list_head *next = waiting_filters.next;
+ s = list_entry (next, struct section_buf, list);
+ if (start_filter (s))
+ break;
+ };
+}
+
+
+static void read_filters (void)
+{
+ struct section_buf *s;
+ int i, n, done;
+
+ n = poll(poll_fds, n_running, 1000);
+ if (n == -1)
+ errorn("poll");
+
+ for (i = 0; i < n_running; i++) {
+ s = poll_section_bufs[i];
+ if (!s)
+ fatal("poll_section_bufs[%d] is NULL\n", i);
+ if (poll_fds[i].revents)
+ done = read_sections (s) == 1;
+ else
+ done = 0; /* timeout */
+ if (done || time(NULL) > s->start_time + s->timeout) {
+ if (s->run_once) {
+ if (done)
+ verbosedebug("filter done pid 0x%04x\n", s->pid);
+ else
+ warning("filter timeout pid 0x%04x\n", s->pid);
+ remove_filter (s);
+ }
+ }
+ }
+}
+
+
+static int mem_is_zero (const void *mem, int size)
+{
+ const char *p = mem;
+ int i;
+
+ for (i=0; i<size; i++) {
+ if (p[i] != 0x00)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int switch_pos = 0;
+
+static int __tune_to_transponder (int frontend_fd, struct transponder *t)
+{
+ struct dvb_frontend_parameters p;
+ fe_status_t s;
+ int i;
+
+ current_tp = t;
+
+ if (mem_is_zero (&t->param, sizeof(struct dvb_frontend_parameters)))
+ return -1;
+
+ memcpy (&p, &t->param, sizeof(struct dvb_frontend_parameters));
+
+ if (verbosity >= 1) {
+ dprintf(1, ">>> tune to: ");
+ dump_dvb_parameters (stderr, t);
+ if (t->last_tuning_failed)
+ dprintf(1, " (tuning failed)");
+ dprintf(1, "\n");
+ }
+
+ if (t->type == FE_QPSK) {
+ int hiband = 0;
+
+ if (lnb_type.switch_val && lnb_type.high_val &&
+ p.frequency >= lnb_type.switch_val)
+ hiband = 1;
+
+ setup_switch (frontend_fd,
+ switch_pos,
+ t->polarisation == POLARISATION_VERTICAL ? 0 : 1,
+ hiband);
+ usleep(50000);
+ if (hiband)
+ p.frequency = abs(p.frequency - lnb_type.high_val);
+ else
+ p.frequency = abs(p.frequency - lnb_type.low_val);
+ }
+
+ if (ioctl(frontend_fd, FE_SET_FRONTEND, &p) == -1) {
+ errorn("Setting frontend parameters failed");
+ return -1;
+ }
+
+ for (i = 0; i < 10; i++) {
+ usleep (200000);
+
+ if (ioctl(frontend_fd, FE_READ_STATUS, &s) == -1) {
+ errorn("FE_READ_STATUS failed");
+ return -1;
+ }
+
+ verbose(">>> tuning status == 0x%02x\n", s);
+
+ if (s & FE_HAS_LOCK) {
+ t->last_tuning_failed = 0;
+ return 0;
+ }
+ }
+
+ warning(">>> tuning failed!!!\n");
+
+ t->last_tuning_failed = 1;
+
+ return -1;
+}
+
+static int tune_to_transponder (int frontend_fd, struct transponder *t)
+{
+ /* move TP from "new" to "scanned" list */
+ list_del_init(&t->list);
+ list_add_tail(&t->list, &scanned_transponders);
+ t->scan_done = 1;
+
+ if (t->type != fe_info.type) {
+ warning("frontend type (%s) is not compatible with requested tuning type (%s)\n",
+ fe_type2str(fe_info.type),fe_type2str(t->type));
+ /* ignore cable descriptors in sat NIT and vice versa */
+ t->last_tuning_failed = 1;
+ return -1;
+ }
+
+ if (__tune_to_transponder (frontend_fd, t) == 0)
+ return 0;
+
+ return __tune_to_transponder (frontend_fd, t);
+}
+
+
+static int tune_to_next_transponder (int frontend_fd)
+{
+ struct list_head *pos, *tmp;
+ struct transponder *t, *to;
+ uint32_t freq;
+
+ list_for_each_safe(pos, tmp, &new_transponders) {
+ t = list_entry (pos, struct transponder, list);
+retry:
+ if (tune_to_transponder (frontend_fd, t) == 0)
+ return 0;
+next:
+ if (t->other_frequency_flag && t->other_f && t->n_other_f) {
+ /* check if the alternate freqeuncy is really new to us */
+ freq = t->other_f[t->n_other_f - 1];
+ t->n_other_f--;
+ if (find_transponder(freq))
+ goto next;
+
+ /* remember tuning to the old frequency failed */
+ to = calloc(1, sizeof(*to));
+ to->param.frequency = t->param.frequency;
+ to->wrong_frequency = 1;
+ INIT_LIST_HEAD(&to->list);
+ INIT_LIST_HEAD(&to->services);
+ list_add_tail(&to->list, &scanned_transponders);
+ copy_transponder(to, t);
+
+ t->param.frequency = freq;
+ info("retrying with f=%d\n", t->param.frequency);
+ goto retry;
+ }
+ }
+ return -1;
+}
+
+struct strtab {
+ const char *str;
+ int val;
+};
+static int str2enum(const char *str, const struct strtab *tab, int deflt)
+{
+ while (tab->str) {
+ if (!strcmp(tab->str, str))
+ return tab->val;
+ tab++;
+ }
+ error("invalid enum value '%s'\n", str);
+ return deflt;
+}
+
+static const char * enum2str(int v, const struct strtab *tab, const char *deflt)
+{
+ while (tab->str) {
+ if (v == tab->val)
+ return tab->str;
+ tab++;
+ }
+ error("invalid enum value '%d'\n", v);
+ return deflt;
+}
+
+static enum fe_code_rate str2fec(const char *fec)
+{
+ struct strtab fectab[] = {
+ { "NONE", FEC_NONE },
+ { "1/2", FEC_1_2 },
+ { "2/3", FEC_2_3 },
+ { "3/4", FEC_3_4 },
+ { "4/5", FEC_4_5 },
+ { "5/6", FEC_5_6 },
+ { "6/7", FEC_6_7 },
+ { "7/8", FEC_7_8 },
+ { "8/9", FEC_8_9 },
+ { "AUTO", FEC_AUTO },
+ { NULL, 0 }
+ };
+ return str2enum(fec, fectab, FEC_AUTO);
+}
+
+static enum fe_modulation str2qam(const char *qam)
+{
+ struct strtab qamtab[] = {
+ { "QPSK", QPSK },
+ { "QAM16", QAM_16 },
+ { "QAM32", QAM_32 },
+ { "QAM64", QAM_64 },
+ { "QAM128", QAM_128 },
+ { "QAM256", QAM_256 },
+ { "AUTO", QAM_AUTO },
+ { "8VSB", VSB_8 },
+ { "16VSB", VSB_16 },
+ { NULL, 0 }
+ };
+ return str2enum(qam, qamtab, QAM_AUTO);
+}
+
+static enum fe_bandwidth str2bandwidth(const char *bw)
+{
+ struct strtab bwtab[] = {
+ { "8MHz", BANDWIDTH_8_MHZ },
+ { "7MHz", BANDWIDTH_7_MHZ },
+ { "6MHz", BANDWIDTH_6_MHZ },
+ { "AUTO", BANDWIDTH_AUTO },
+ { NULL, 0 }
+ };
+ return str2enum(bw, bwtab, BANDWIDTH_AUTO);
+}
+
+static enum fe_transmit_mode str2mode(const char *mode)
+{
+ struct strtab modetab[] = {
+ { "2k", TRANSMISSION_MODE_2K },
+ { "8k", TRANSMISSION_MODE_8K },
+ { "AUTO", TRANSMISSION_MODE_AUTO },
+ { NULL, 0 }
+ };
+ return str2enum(mode, modetab, TRANSMISSION_MODE_AUTO);
+}
+
+static enum fe_guard_interval str2guard(const char *guard)
+{
+ struct strtab guardtab[] = {
+ { "1/32", GUARD_INTERVAL_1_32 },
+ { "1/16", GUARD_INTERVAL_1_16 },
+ { "1/8", GUARD_INTERVAL_1_8 },
+ { "1/4", GUARD_INTERVAL_1_4 },
+ { "AUTO", GUARD_INTERVAL_AUTO },
+ { NULL, 0 }
+ };
+ return str2enum(guard, guardtab, GUARD_INTERVAL_AUTO);
+}
+
+static enum fe_hierarchy str2hier(const char *hier)
+{
+ struct strtab hiertab[] = {
+ { "NONE", HIERARCHY_NONE },
+ { "1", HIERARCHY_1 },
+ { "2", HIERARCHY_2 },
+ { "4", HIERARCHY_4 },
+ { "AUTO", HIERARCHY_AUTO },
+ { NULL, 0 }
+ };
+ return str2enum(hier, hiertab, HIERARCHY_AUTO);
+}
+
+static const char * fe_type2str(fe_type_t t)
+{
+ struct strtab typetab[] = {
+ { "QPSK", FE_QPSK,},
+ { "QAM", FE_QAM, },
+ { "OFDM", FE_OFDM,},
+ { "ATSC", FE_ATSC,},
+ { NULL, 0 }
+ };
+
+ return enum2str(t, typetab, "UNK");
+}
+
+static int tune_initial (int frontend_fd, const char *initial)
+{
+ FILE *inif;
+ unsigned int f, sr;
+ char buf[200];
+ char pol[20], fec[20], qam[20], bw[20], fec2[20], mode[20], guard[20], hier[20];
+ struct transponder *t;
+
+ inif = fopen(initial, "r");
+ if (!inif) {
+ error("cannot open '%s': %d %m\n", initial, errno);
+ return -1;
+ }
+ while (fgets(buf, sizeof(buf), inif)) {
+ if (buf[0] == '#' || buf[0] == '\n')
+ ;
+ else if (sscanf(buf, "S %u %1[HVLR] %u %4s\n", &f, pol, &sr, fec) == 4) {
+ t = alloc_transponder(f);
+ t->type = FE_QPSK;
+ switch(pol[0]) {
+ case 'H':
+ case 'L':
+ t->polarisation = POLARISATION_HORIZONTAL;
+ break;
+ default:
+ t->polarisation = POLARISATION_VERTICAL;;
+ break;
+ }
+ t->param.inversion = spectral_inversion;
+ t->param.u.qpsk.symbol_rate = sr;
+ t->param.u.qpsk.fec_inner = str2fec(fec);
+ info("initial transponder %u %c %u %d\n",
+ t->param.frequency,
+ pol[0], sr,
+ t->param.u.qpsk.fec_inner);
+ }
+ else if (sscanf(buf, "C %u %u %4s %6s\n", &f, &sr, fec, qam) == 4) {
+ t = alloc_transponder(f);
+ t->type = FE_QAM;
+ t->param.inversion = spectral_inversion;
+ t->param.u.qam.symbol_rate = sr;
+ t->param.u.qam.fec_inner = str2fec(fec);
+ t->param.u.qam.modulation = str2qam(qam);
+ info("initial transponder %u %u %d %d\n",
+ t->param.frequency,
+ sr,
+ t->param.u.qam.fec_inner,
+ t->param.u.qam.modulation);
+ }
+ else if (sscanf(buf, "T %u %4s %4s %4s %7s %4s %4s %4s\n",
+ &f, bw, fec, fec2, qam, mode, guard, hier) == 8) {
+ t = alloc_transponder(f);
+ t->type = FE_OFDM;
+ t->param.inversion = spectral_inversion;
+ t->param.u.ofdm.bandwidth = str2bandwidth(bw);
+ t->param.u.ofdm.code_rate_HP = str2fec(fec);
+ if (t->param.u.ofdm.code_rate_HP == FEC_NONE)
+ t->param.u.ofdm.code_rate_HP = FEC_AUTO;
+ t->param.u.ofdm.code_rate_LP = str2fec(fec2);
+ if (t->param.u.ofdm.code_rate_LP == FEC_NONE)
+ t->param.u.ofdm.code_rate_LP = FEC_AUTO;
+ t->param.u.ofdm.constellation = str2qam(qam);
+ t->param.u.ofdm.transmission_mode = str2mode(mode);
+ t->param.u.ofdm.guard_interval = str2guard(guard);
+ t->param.u.ofdm.hierarchy_information = str2hier(hier);
+ info("initial transponder %u %d %d %d %d %d %d %d\n",
+ t->param.frequency,
+ t->param.u.ofdm.bandwidth,
+ t->param.u.ofdm.code_rate_HP,
+ t->param.u.ofdm.code_rate_LP,
+ t->param.u.ofdm.constellation,
+ t->param.u.ofdm.transmission_mode,
+ t->param.u.ofdm.guard_interval,
+ t->param.u.ofdm.hierarchy_information);
+ }
+ else if (sscanf(buf, "A %u %7s\n",
+ &f,qam) == 2) {
+ t = alloc_transponder(f);
+ t->type = FE_ATSC;
+ t->param.u.vsb.modulation = str2qam(qam);
+ } else
+ error("cannot parse'%s'\n", buf);
+ }
+
+ fclose(inif);
+
+ return tune_to_next_transponder(frontend_fd);
+}
+
+
+static void scan_tp_atsc(void)
+{
+ struct section_buf s0,s1,s2;
+
+ if (no_ATSC_PSIP) {
+ setup_filter(&s0, demux_devname, 0x00, 0x00, -1, 1, 0, 5); /* PAT */
+ add_filter(&s0);
+ } else {
+ if (ATSC_type & 0x1) {
+ setup_filter(&s0, demux_devname, 0x1ffb, 0xc8, -1, 1, 0, 5); /* terrestrial VCT */
+ add_filter(&s0);
+ }
+ if (ATSC_type & 0x2) {
+ setup_filter(&s1, demux_devname, 0x1ffb, 0xc9, -1, 1, 0, 5); /* cable VCT */
+ add_filter(&s1);
+ }
+ setup_filter(&s2, demux_devname, 0x00, 0x00, -1, 1, 0, 5); /* PAT */
+ add_filter(&s2);
+ }
+
+ do {
+ read_filters ();
+ } while (!(list_empty(&running_filters) &&
+ list_empty(&waiting_filters)));
+}
+
+static void scan_tp_dvb (void)
+{
+ struct section_buf s0;
+ struct section_buf s1;
+ struct section_buf s2;
+ struct section_buf s3;
+
+ /**
+ * filter timeouts > min repetition rates specified in ETR211
+ */
+ setup_filter (&s0, demux_devname, 0x00, 0x00, -1, 1, 0, 5); /* PAT */
+ setup_filter (&s1, demux_devname, 0x11, 0x42, -1, 1, 0, 5); /* SDT */
+
+ add_filter (&s0);
+ add_filter (&s1);
+
+ if (!current_tp_only || output_format != OUTPUT_PIDS) {
+ setup_filter (&s2, demux_devname, 0x10, 0x40, -1, 1, 0, 15); /* NIT */
+ add_filter (&s2);
+ if (get_other_nits) {
+ /* get NIT-others
+ * Note: There is more than one NIT-other: one per
+ * network, separated by the network_id.
+ */
+ setup_filter (&s3, demux_devname, 0x10, 0x41, -1, 1, 1, 15);
+ add_filter (&s3);
+ }
+ }
+
+ do {
+ read_filters ();
+ } while (!(list_empty(&running_filters) &&
+ list_empty(&waiting_filters)));
+}
+
+static void scan_tp(void)
+{
+ switch(fe_info.type) {
+ case FE_QPSK:
+ case FE_QAM:
+ case FE_OFDM:
+ scan_tp_dvb();
+ break;
+ case FE_ATSC:
+ scan_tp_atsc();
+ break;
+ default:
+ break;
+ }
+}
+
+static void scan_network (int frontend_fd, const char *initial)
+{
+ if (tune_initial (frontend_fd, initial) < 0) {
+ error("initial tuning failed\n");
+ return;
+ }
+
+ do {
+ scan_tp();
+ } while (tune_to_next_transponder(frontend_fd) == 0);
+}
+
+
+static void pids_dump_service_parameter_set(FILE *f, struct service *s)
+{
+ int i;
+
+ fprintf(f, "%-24.24s (0x%04x) %02x: ", s->service_name, s->service_id, s->type);
+ if (!s->pcr_pid || (s->type > 2))
+ fprintf(f, " ");
+ else if (s->pcr_pid == s->video_pid)
+ fprintf(f, "PCR == V ");
+ else if ((s->audio_num == 1) && (s->pcr_pid == s->audio_pid[0]))
+ fprintf(f, "PCR == A ");
+ else
+ fprintf(f, "PCR 0x%04x ", s->pcr_pid);
+ if (s->video_pid)
+ fprintf(f, "V 0x%04x", s->video_pid);
+ else
+ fprintf(f, " ");
+ if (s->audio_num)
+ fprintf(f, " A");
+ for (i = 0; i < s->audio_num; i++) {
+ fprintf(f, " 0x%04x", s->audio_pid[i]);
+ if (s->audio_lang[i][0])
+ fprintf(f, " (%.3s)", s->audio_lang[i]);
+ else if (s->audio_num == 1)
+ fprintf(f, " ");
+ }
+ if (s->teletext_pid)
+ fprintf(f, " TT 0x%04x", s->teletext_pid);
+ if (s->ac3_pid)
+ fprintf(f, " AC3 0x%04x", s->ac3_pid);
+ if (s->subtitling_pid)
+ fprintf(f, " SUB 0x%04x", s->subtitling_pid);
+ fprintf(f, "\n");
+}
+
+static char sat_polarisation (struct transponder *t)
+{
+ return t->polarisation == POLARISATION_VERTICAL ? 'v' : 'h';
+}
+
+static int sat_number (struct transponder *t)
+{
+ (void) t;
+
+ return switch_pos;
+}
+
+static void dump_lists (void)
+{
+ struct list_head *p1, *p2;
+ struct transponder *t;
+ struct service *s;
+ int n = 0, i;
+ char sn[20];
+ int anon_services = 0;
+
+ list_for_each(p1, &scanned_transponders) {
+ t = list_entry(p1, struct transponder, list);
+ if (t->wrong_frequency)
+ continue;
+ list_for_each(p2, &t->services) {
+ n++;
+ }
+ }
+ info("dumping lists (%d services)\n", n);
+
+ list_for_each(p1, &scanned_transponders) {
+ t = list_entry(p1, struct transponder, list);
+ if (t->wrong_frequency)
+ continue;
+ list_for_each(p2, &t->services) {
+ s = list_entry(p2, struct service, list);
+
+ if (!s->service_name) {
+ /* not in SDT */
+ if (unique_anon_services)
+ snprintf(sn, sizeof(sn), "[%03x-%04x]",
+ anon_services, s->service_id);
+ else
+ snprintf(sn, sizeof(sn), "[%04x]",
+ s->service_id);
+ s->service_name = strdup(sn);
+ anon_services++;
+ }
+ /* ':' is field separator in szap and vdr service lists */
+ for (i = 0; s->service_name[i]; i++) {
+ if (s->service_name[i] == ':')
+ s->service_name[i] = ' ';
+ }
+ for (i = 0; s->provider_name && s->provider_name[i]; i++) {
+ if (s->provider_name[i] == ':')
+ s->provider_name[i] = ' ';
+ }
+ if (s->video_pid && !(serv_select & 1))
+ continue; /* no TV services */
+ if (!s->video_pid && s->audio_num && !(serv_select & 2))
+ continue; /* no radio services */
+ if (!s->video_pid && !s->audio_num && !(serv_select & 4))
+ continue; /* no data/other services */
+ if (s->scrambled && !ca_select)
+ continue; /* FTA only */
+ switch (output_format)
+ {
+ case OUTPUT_PIDS:
+ pids_dump_service_parameter_set (stdout, s);
+ break;
+ case OUTPUT_VDR:
+ vdr_dump_service_parameter_set (stdout,
+ s->service_name,
+ s->provider_name,
+ t->type,
+ &t->param,
+ sat_polarisation(t),
+ s->video_pid,
+ s->pcr_pid,
+ s->audio_pid,
+ s->audio_lang,
+ s->audio_num,
+ s->teletext_pid,
+ s->scrambled,
+ //FIXME: s->subtitling_pid
+ s->ac3_pid,
+ s->service_id,
+ t->original_network_id,
+ s->transport_stream_id,
+ t->orbital_pos,
+ t->we_flag,
+ vdr_dump_provider,
+ ca_select,
+ vdr_version,
+ vdr_dump_channum,
+ s->channel_num);
+ break;
+ case OUTPUT_ZAP:
+ zap_dump_service_parameter_set (stdout,
+ s->service_name,
+ t->type,
+ &t->param,
+ sat_polarisation(t),
+ sat_number(t),
+ s->video_pid,
+ s->audio_pid,
+ s->service_id);
+ default:
+ break;
+ }
+ }
+ }
+ info("Done.\n");
+}
+
+static void show_existing_tuning_data_files(void)
+{
+#ifndef DATADIR
+#define DATADIR "/usr/local/share"
+#endif
+ static const char* prefixlist[] = { DATADIR "/dvb/scan", "/etc/dvb/scan",
+ DATADIR "/doc/packages/dvb/scan", 0 };
+ unsigned int i;
+ const char **prefix;
+ fprintf(stderr, "initial tuning data files:\n");
+ for (prefix = prefixlist; *prefix; prefix++) {
+ glob_t globbuf;
+ char* globspec = malloc (strlen(*prefix)+9);
+ strcpy (globspec, *prefix); strcat (globspec, "/dvb-?/*");
+ if (! glob (globspec, 0, 0, &globbuf)) {
+ for (i=0; i < globbuf.gl_pathc; i++)
+ fprintf(stderr, " file: %s\n", globbuf.gl_pathv[i]);
+ }
+ free (globspec);
+ globfree (&globbuf);
+ }
+}
+
+static void handle_sigint(int sig)
+{
+ (void)sig;
+ error("interrupted by SIGINT, dumping partial result...\n");
+ dump_lists();
+ exit(2);
+}
+
+static const char *usage = "\n"
+ "usage: %s [options...] [-c | initial-tuning-data-file]\n"
+ " atsc/dvbscan doesn't do frequency scans, hence it needs initial\n"
+ " tuning data for at least one transponder/channel.\n"
+ " -c scan on currently tuned transponder only\n"
+ " -v verbose (repeat for more)\n"
+ " -q quiet (repeat for less)\n"
+ " -a N use DVB /dev/dvb/adapterN/\n"
+ " -f N use DVB /dev/dvb/adapter?/frontendN\n"
+ " -d N use DVB /dev/dvb/adapter?/demuxN\n"
+ " -s N use DiSEqC switch position N (DVB-S only)\n"
+ " -i N spectral inversion setting (0: off, 1: on, 2: auto [default])\n"
+ " -n evaluate NIT-other for full network scan (slow!)\n"
+ " -5 multiply all filter timeouts by factor 5\n"
+ " for non-DVB-compliant section repitition rates\n"
+ " -o fmt output format: 'zap' (default), 'vdr' or 'pids' (default with -c)\n"
+ " -x N Conditional Axcess, (default 1)\n"
+ " N=0 gets only FTA channels\n"
+ " N=xxx sets ca field in vdr output to :xxx:\n"
+ " -t N Service select, Combined bitfield parameter.\n"
+ " 1 = TV, 2 = Radio, 4 = Other, (default 7)\n"
+ " -p for vdr output format: dump provider name\n"
+ " -e N VDR version, default 2 for VDR-1.2.x\n"
+ " ANYTHING ELSE GIVES NONZERO NIT and TID\n"
+ " Vdr version 1.3.x and up implies -p.\n"
+ " -l lnb-type (DVB-S Only) (use -l help to print types) or \n"
+ " -l low[,high[,switch]] in Mhz\n"
+ " -u UK DVB-T Freeview channel numbering for VDR\n\n"
+ " -P do not use ATSC PSIP tables for scanning\n"
+ " (but only PAT and PMT) (applies for ATSC only)\n"
+ " -A N check for ATSC 1=Terrestrial [default], 2=Cable or 3=both\n"
+ " -U Uniquely name unknown services\n";
+
+void
+bad_usage(char *pname, int problem)
+{
+ int i;
+ struct lnb_types_st *lnbp;
+ char **cp;
+
+ switch (problem) {
+ default:
+ case 0:
+ fprintf (stderr, usage, pname);
+ break;
+ case 1:
+ i = 0;
+ fprintf(stderr, "-l <lnb-type> or -l low[,high[,switch]] in Mhz\n"
+ "where <lnb-type> 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++;
+ }
+ break;
+ case 2:
+ show_existing_tuning_data_files();
+ fprintf (stderr, usage, pname);
+ }
+}
+
+int main (int argc, char **argv)
+{
+ char frontend_devname [80];
+ int adapter = 0, frontend = 0, demux = 0;
+ int opt, i;
+ int frontend_fd;
+ int fe_open_mode;
+ const char *initial = NULL;
+
+ if (argc <= 1) {
+ bad_usage(argv[0], 2);
+ return -1;
+ }
+
+ /* start with default lnb type */
+ lnb_type = *lnb_enum(0);
+ while ((opt = getopt(argc, argv, "5cnpa:f:d:s:o:x:e:t:i:l:vquPA:U")) != -1) {
+ switch (opt) {
+ case 'a':
+ adapter = strtoul(optarg, NULL, 0);
+ break;
+ case 'c':
+ current_tp_only = 1;
+ if (!output_format_set)
+ output_format = OUTPUT_PIDS;
+ break;
+ case 'n':
+ get_other_nits = 1;
+ break;
+ case 'd':
+ demux = strtoul(optarg, NULL, 0);
+ break;
+ case 'f':
+ frontend = strtoul(optarg, NULL, 0);
+ break;
+ case 'p':
+ vdr_dump_provider = 1;
+ break;
+ case 's':
+ switch_pos = strtoul(optarg, NULL, 0);
+ break;
+ case 'o':
+ if (strcmp(optarg, "zap") == 0) output_format = OUTPUT_ZAP;
+ else if (strcmp(optarg, "vdr") == 0) output_format = OUTPUT_VDR;
+ else if (strcmp(optarg, "pids") == 0) output_format = OUTPUT_PIDS;
+ else {
+ bad_usage(argv[0], 0);
+ return -1;
+ }
+ output_format_set = 1;
+ break;
+ case '5':
+ long_timeout = 1;
+ break;
+ case 'x':
+ ca_select = strtoul(optarg, NULL, 0);
+ break;
+ case 'e':
+ vdr_version = strtoul(optarg, NULL, 0);
+ break;
+ case 't':
+ serv_select = strtoul(optarg, NULL, 0);
+ break;
+ case 'i':
+ spectral_inversion = strtoul(optarg, NULL, 0);
+ break;
+ case 'l':
+ if (lnb_decode(optarg, &lnb_type) < 0) {
+ bad_usage(argv[0], 1);
+ return -1;
+ }
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'q':
+ if (--verbosity < 0)
+ verbosity = 0;
+ break;
+ case 'u':
+ vdr_dump_channum = 1;
+ break;
+ case 'P':
+ no_ATSC_PSIP = 1;
+ break;
+ case 'A':
+ ATSC_type = strtoul(optarg,NULL,0);
+ if (ATSC_type == 0 || ATSC_type > 3) {
+ bad_usage(argv[0], 1);
+ return -1;
+ }
+
+ break;
+ case 'U':
+ unique_anon_services = 1;
+ break;
+ default:
+ bad_usage(argv[0], 0);
+ return -1;
+ };
+ }
+
+ if (optind < argc)
+ initial = argv[optind];
+ if ((!initial && !current_tp_only) || (initial && current_tp_only) ||
+ (spectral_inversion > 2)) {
+ bad_usage(argv[0], 0);
+ return -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 (switch_pos >= 4) {
+ fprintf (stderr, "switch position needs to be < 4!\n");
+ return -1;
+ }
+ if (initial)
+ info("scanning %s\n", initial);
+
+ snprintf (frontend_devname, sizeof(frontend_devname),
+ "/dev/dvb/adapter%i/frontend%i", adapter, frontend);
+
+ snprintf (demux_devname, sizeof(demux_devname),
+ "/dev/dvb/adapter%i/demux%i", adapter, demux);
+ info("using '%s' and '%s'\n", frontend_devname, demux_devname);
+
+ for (i = 0; i < MAX_RUNNING; i++)
+ poll_fds[i].fd = -1;
+
+ fe_open_mode = current_tp_only ? O_RDONLY : O_RDWR;
+ if ((frontend_fd = open (frontend_devname, fe_open_mode)) < 0)
+ fatal("failed to open '%s': %d %m\n", frontend_devname, errno);
+ /* determine FE type and caps */
+ if (ioctl(frontend_fd, FE_GET_INFO, &fe_info) == -1)
+ fatal("FE_GET_INFO failed: %d %m\n", errno);
+
+ if ((spectral_inversion == INVERSION_AUTO ) &&
+ !(fe_info.caps & FE_CAN_INVERSION_AUTO)) {
+ info("Frontend can not do INVERSION_AUTO, trying INVERSION_OFF instead\n");
+ spectral_inversion = INVERSION_OFF;
+ }
+
+ signal(SIGINT, handle_sigint);
+
+ if (current_tp_only) {
+ current_tp = alloc_transponder(0); /* dummy */
+ /* move TP from "new" to "scanned" list */
+ list_del_init(¤t_tp->list);
+ list_add_tail(¤t_tp->list, &scanned_transponders);
+ current_tp->scan_done = 1;
+ scan_tp ();
+ }
+ else
+ scan_network (frontend_fd, initial);
+
+ close (frontend_fd);
+
+ dump_lists ();
+
+ return 0;
+}
+
+static void dump_dvb_parameters (FILE *f, struct transponder *t)
+{
+ switch (output_format) {
+ case OUTPUT_PIDS:
+ case OUTPUT_VDR:
+ vdr_dump_dvb_parameters(f, t->type, &t->param,
+ sat_polarisation (t), t->orbital_pos, t->we_flag);
+ break;
+ case OUTPUT_ZAP:
+ zap_dump_dvb_parameters (f, t->type, &t->param,
+ sat_polarisation (t), sat_number (t));
+ break;
+ default:
+ break;
+ }
+}
diff -r c51253d44be0 util/scan/dvbscan.h
--- /dev/null Thu Sep 7 12:03:08 2006
+++ b/util/scan/dvbscan.h Thu Sep 7 14:03:14 2006
@@ -0,0 +1,29 @@
+#ifndef __SCAN_H__
+#define __SCAN_H__
+
+#include <stdio.h>
+#include <errno.h>
+
+extern int verbosity;
+
+#define dprintf(level, fmt...) \
+ do { \
+ if (level <= verbosity) \
+ fprintf(stderr, fmt); \
+ } while (0)
+
+#define dpprintf(level, fmt, args...) \
+ dprintf(level, "%s:%d: " fmt, __FUNCTION__, __LINE__ , ##args)
+
+#define fatal(fmt, args...) do { dpprintf(-1, "FATAL: " fmt , ##args); exit(1); } while(0)
+#define error(msg...) dprintf(0, "ERROR: " msg)
+#define errorn(msg) dprintf(0, "%s:%d: ERROR: " msg ": %d %m\n", __FUNCTION__, __LINE__, errno)
+#define warning(msg...) dprintf(1, "WARNING: " msg)
+#define info(msg...) dprintf(2, msg)
+#define verbose(msg...) dprintf(3, msg)
+#define moreverbose(msg...) dprintf(4, msg)
+#define debug(msg...) dpprintf(5, msg)
+#define verbosedebug(msg...) dpprintf(6, msg)
+
+#endif
+
diff -r c51253d44be0 util/scan/scan.c
--- a/util/scan/scan.c Thu Sep 7 12:03:08 2006
+++ /dev/null Thu Sep 7 14:03:14 2006
@@ -1,2296 +0,0 @@
-/*
- * Simple MPEG parser to achieve network/service information.
- *
- * refered standards:
- *
- * ETSI EN 300 468
- * ETSI TR 101 211
- * ETSI ETR 211
- * ITU-T H.222.0
- *
- * 2005-05-10 - Basic ATSC PSIP parsing support added
- * ATSC Standard Revision B (A65/B)
- *
- * Thanks to Sean Device from Triveni for providing access to ATSC signals
- * and to Kevin Fowlks for his independent ATSC scanning tool.
- *
- * Please contribute: It is possible that some descriptors for ATSC are
- * not parsed yet and thus the result won't be complete.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/poll.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-#include <errno.h>
-#include <signal.h>
-#include <assert.h>
-#include <glob.h>
-#include <ctype.h>
-
-#include <linux/dvb/frontend.h>
-#include <linux/dvb/dmx.h>
-
-#include "list.h"
-#include "diseqc.h"
-#include "dump-zap.h"
-#include "dump-vdr.h"
-#include "scan.h"
-#include "lnb.h"
-
-#include "atsc_psip_section.h"
-
-static char demux_devname[80];
-
-static struct dvb_frontend_info fe_info = {
- .type = -1
-};
-
-int verbosity = 2;
-
-static int long_timeout;
-static int current_tp_only;
-static int get_other_nits;
-static int vdr_dump_provider;
-static int vdr_dump_channum;
-static int no_ATSC_PSIP;
-static int ATSC_type=1;
-static int ca_select = 1;
-static int serv_select = 7;
-static int vdr_version = 2;
-static struct lnb_types_st lnb_type;
-static int unique_anon_services;
-
-static enum fe_spectral_inversion spectral_inversion = INVERSION_AUTO;
-
-enum table_type {
- PAT,
- PMT,
- SDT,
- NIT
-};
-
-enum format {
- OUTPUT_ZAP,
- OUTPUT_VDR,
- OUTPUT_PIDS
-};
-static enum format output_format = OUTPUT_ZAP;
-static int output_format_set = 0;
-
-
-enum polarisation {
- POLARISATION_HORIZONTAL = 0x00,
- POLARISATION_VERTICAL = 0x01,
- POLARISATION_CIRCULAR_LEFT = 0x02,
- POLARISATION_CIRCULAR_RIGHT = 0x03
-};
-
-enum running_mode {
- RM_NOT_RUNNING = 0x01,
- RM_STARTS_SOON = 0x02,
- RM_PAUSING = 0x03,
- RM_RUNNING = 0x04
-};
-
-#define AUDIO_CHAN_MAX (32)
-#define CA_SYSTEM_ID_MAX (16)
-
-struct service {
- struct list_head list;
- int transport_stream_id;
- int service_id;
- char *provider_name;
- char *service_name;
- uint16_t pmt_pid;
- uint16_t pcr_pid;
- uint16_t video_pid;
- uint16_t audio_pid[AUDIO_CHAN_MAX];
- char audio_lang[AUDIO_CHAN_MAX][4];
- int audio_num;
- uint16_t ca_id[CA_SYSTEM_ID_MAX];
- int ca_num;
- uint16_t teletext_pid;
- uint16_t subtitling_pid;
- uint16_t ac3_pid;
- unsigned int type : 8;
- unsigned int scrambled : 1;
- enum running_mode running;
- void *priv;
- int channel_num;
-};
-
-struct transponder {
- struct list_head list;
- struct list_head services;
- int network_id;
- int original_network_id;
- int transport_stream_id;
- enum fe_type type;
- struct dvb_frontend_parameters param;
- enum polarisation polarisation; /* only for DVB-S */
- int orbital_pos; /* only for DVB-S */
- unsigned int we_flag : 1; /* West/East Flag - only for DVB-S */
- unsigned int scan_done : 1;
- unsigned int last_tuning_failed : 1;
- unsigned int other_frequency_flag : 1; /* DVB-T */
- unsigned int wrong_frequency : 1; /* DVB-T with other_frequency_flag */
- int n_other_f;
- uint32_t *other_f; /* DVB-T freqeuency-list descriptor */
-};
-
-
-struct section_buf {
- struct list_head list;
- const char *dmx_devname;
- unsigned int run_once : 1;
- unsigned int segmented : 1; /* segmented by table_id_ext */
- int fd;
- int pid;
- int table_id;
- int table_id_ext;
- int section_version_number;
- uint8_t section_done[32];
- int sectionfilter_done;
- unsigned char buf[1024];
- time_t timeout;
- time_t start_time;
- time_t running_time;
- struct section_buf *next_seg; /* this is used to handle
- * segmented tables (like NIT-other)
- */
-};
-
-static LIST_HEAD(scanned_transponders);
-static LIST_HEAD(new_transponders);
-static struct transponder *current_tp;
-
-
-static void dump_dvb_parameters (FILE *f, struct transponder *p);
-
-static void setup_filter (struct section_buf* s, const char *dmx_devname,
- int pid, int tid, int tid_ext,
- int run_once, int segmented, int timeout);
-static void add_filter (struct section_buf *s);
-
-static const char * fe_type2str(fe_type_t t);
-
-/* According to the DVB standards, the combination of network_id and
- * transport_stream_id should be unique, but in real life the satellite
- * operators and broadcasters don't care enough to coordinate
- * the numbering. Thus we identify TPs by frequency (dvbscan handles only
- * one satellite at a time). Further complication: Different NITs on
- * one satellite sometimes list the same TP with slightly different
- * frequencies, so we have to search within some bandwidth.
- */
-static struct transponder *alloc_transponder(uint32_t frequency)
-{
- struct transponder *tp = calloc(1, sizeof(*tp));
-
- tp->param.frequency = frequency;
- INIT_LIST_HEAD(&tp->list);
- INIT_LIST_HEAD(&tp->services);
- list_add_tail(&tp->list, &new_transponders);
- return tp;
-}
-
-static int is_same_transponder(uint32_t f1, uint32_t f2)
-{
- uint32_t diff;
- if (f1 == f2)
- return 1;
- diff = (f1 > f2) ? (f1 - f2) : (f2 - f1);
- //FIXME: use symbolrate etc. to estimate bandwidth
- if (diff < 2000) {
- debug("f1 = %u is same TP as f2 = %u\n", f1, f2);
- return 1;
- }
- return 0;
-}
-
-static struct transponder *find_transponder(uint32_t frequency)
-{
- struct list_head *pos;
- struct transponder *tp;
-
- list_for_each(pos, &scanned_transponders) {
- tp = list_entry(pos, struct transponder, list);
- if (current_tp_only)
- return tp;
- if (is_same_transponder(tp->param.frequency, frequency))
- return tp;
- }
- list_for_each(pos, &new_transponders) {
- tp = list_entry(pos, struct transponder, list);
- if (is_same_transponder(tp->param.frequency, frequency))
- return tp;
- }
- return NULL;
-}
-
-static void copy_transponder(struct transponder *d, struct transponder *s)
-{
- d->network_id = s->network_id;
- d->original_network_id = s->original_network_id;
- d->transport_stream_id = s->transport_stream_id;
- d->type = s->type;
- memcpy(&d->param, &s->param, sizeof(d->param));
- d->polarisation = s->polarisation;
- d->orbital_pos = s->orbital_pos;
- d->we_flag = s->we_flag;
- d->scan_done = s->scan_done;
- d->last_tuning_failed = s->last_tuning_failed;
- d->other_frequency_flag = s->other_frequency_flag;
- d->n_other_f = s->n_other_f;
- if (d->n_other_f) {
- d->other_f = calloc(d->n_other_f, sizeof(uint32_t));
- memcpy(d->other_f, s->other_f, d->n_other_f * sizeof(uint32_t));
- }
- else
- d->other_f = NULL;
-}
-
-/* service_ids are guaranteed to be unique within one TP
- * (the DVB standards say theay should be unique within one
- * network, but in real life...)
- */
-static struct service *alloc_service(struct transponder *tp, int service_id)
-{
- struct service *s = calloc(1, sizeof(*s));
- INIT_LIST_HEAD(&s->list);
- s->service_id = service_id;
- s->transport_stream_id = tp->transport_stream_id;
- list_add_tail(&s->list, &tp->services);
- return s;
-}
-
-static struct service *find_service(struct transponder *tp, int service_id)
-{
- struct list_head *pos;
- struct service *s;
-
- list_for_each(pos, &tp->services) {
- s = list_entry(pos, struct service, list);
- if (s->service_id == service_id)
- return s;
- }
- return NULL;
-}
-
-
-static void parse_ca_identifier_descriptor (const unsigned char *buf,
- struct service *s)
-{
- unsigned char len = buf [1];
- unsigned int i;
-
- buf += 2;
-
- if (len > sizeof(s->ca_id)) {
- len = sizeof(s->ca_id);
- warning("too many CA system ids\n");
- }
- memcpy(s->ca_id, buf, len);
- for (i = 0; i < len / sizeof(s->ca_id[0]); i++)
- moreverbose(" CA ID 0x%04x\n", s->ca_id[i]);
-}
-
-
-static void parse_iso639_language_descriptor (const unsigned char *buf, struct service *s)
-{
- unsigned char len = buf [1];
-
- buf += 2;
-
- if (len >= 4) {
- debug(" LANG=%.3s %d\n", buf, buf[3]);
- memcpy(s->audio_lang[s->audio_num], buf, 3);
-#if 0
- /* seems like the audio_type is wrong all over the place */
- //if (buf[3] == 0) -> normal
- if (buf[3] == 1)
- s->audio_lang[s->audio_num][3] = '!'; /* clean effects (no language) */
- else if (buf[3] == 2)
- s->audio_lang[s->audio_num][3] = '?'; /* for the hearing impaired */
- else if (buf[3] == 3)
- s->audio_lang[s->audio_num][3] = '+'; /* visually impaired commentary */
-#endif
- }
-}
-
-static void parse_network_name_descriptor (const unsigned char *buf, void *dummy)
-{
- (void)dummy;
-
- unsigned char len = buf [1];
-
- info("Network Name '%.*s'\n", len, buf + 2);
-}
-
-static void parse_terrestrial_uk_channel_number (const unsigned char *buf, void *dummy)
-{
- (void)dummy;
-
- int i, n, channel_num, service_id;
- struct list_head *p1, *p2;
- struct transponder *t;
- struct service *s;
-
- // 32 bits per record
- n = buf[1] / 4;
- if (n < 1)
- return;
-
- // desc id, desc len, (service id, service number)
- buf += 2;
- for (i = 0; i < n; i++) {
- service_id = (buf[0]<<8)|(buf[1]&0xff);
- channel_num = ((buf[2]&0x03)<<8)|(buf[3]&0xff);
- debug("Service ID 0x%x has channel number %d ", service_id, channel_num);
- list_for_each(p1, &scanned_transponders) {
- t = list_entry(p1, struct transponder, list);
- list_for_each(p2, &t->services) {
- s = list_entry(p2, struct service, list);
- if (s->service_id == service_id)
- s->channel_num = channel_num;
- }
- }
- buf += 4;
- }
-}
-
-
-static long bcd32_to_cpu (const int b0, const int b1, const int b2, const int b3)
-{
- return ((b0 >> 4) & 0x0f) * 10000000 + (b0 & 0x0f) * 1000000 +
- ((b1 >> 4) & 0x0f) * 100000 + (b1 & 0x0f) * 10000 +
- ((b2 >> 4) & 0x0f) * 1000 + (b2 & 0x0f) * 100 +
- ((b3 >> 4) & 0x0f) * 10 + (b3 & 0x0f);
-}
-
-
-static const fe_code_rate_t fec_tab [8] = {
- FEC_AUTO, FEC_1_2, FEC_2_3, FEC_3_4,
- FEC_5_6, FEC_7_8, FEC_NONE, FEC_NONE
-};
-
-
-static const fe_modulation_t qam_tab [6] = {
- QAM_AUTO, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256
-};
-
-
-static void parse_cable_delivery_system_descriptor (const unsigned char *buf,
- struct transponder *t)
-{
- if (!t) {
- warning("cable_delivery_system_descriptor outside transport stream definition (ignored)\n");
- return;
- }
- t->type = FE_QAM;
-
- t->param.frequency = bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]);
- t->param.frequency *= 100;
- t->param.u.qam.fec_inner = fec_tab[buf[12] & 0x07];
- t->param.u.qam.symbol_rate = 10 * bcd32_to_cpu (buf[9],
- buf[10],
- buf[11],
- buf[12] & 0xf0);
- if ((buf[8] & 0x0f) > 5)
- t->param.u.qam.modulation = QAM_AUTO;
- else
- t->param.u.qam.modulation = qam_tab[buf[8] & 0x0f];
- t->param.inversion = spectral_inversion;
-
- if (verbosity >= 5) {
- debug("%#04x/%#04x ", t->network_id, t->transport_stream_id);
- dump_dvb_parameters (stderr, t);
- if (t->scan_done)
- dprintf(5, " (done)");
- if (t->last_tuning_failed)
- dprintf(5, " (tuning failed)");
- dprintf(5, "\n");
- }
-}
-
-static void parse_satellite_delivery_system_descriptor (const unsigned char *buf,
- struct transponder *t)
-{
- if (!t) {
- warning("satellite_delivery_system_descriptor outside transport stream definition (ignored)\n");
- return;
- }
- t->type = FE_QPSK;
- t->param.frequency = 10 * bcd32_to_cpu (buf[2], buf[3], buf[4], buf[5]);
- t->param.u.qpsk.fec_inner = fec_tab[buf[12] & 0x07];
- t->param.u.qpsk.symbol_rate = 10 * bcd32_to_cpu (buf[9],
- buf[10],
- buf[11],
- buf[12] & 0xf0);
-
- t->polarisation = (buf[8] >> 5) & 0x03;
- t->param.inversion = spectral_inversion;
-
- t->orbital_pos = bcd32_to_cpu (0x00, 0x00, buf[6], buf[7]);
- t->we_flag = buf[8] >> 7;
-
- if (verbosity >= 5) {
- debug("%#04x/%#04x ", t->network_id, t->transport_stream_id);
- dump_dvb_parameters (stderr, t);
- if (t->scan_done)
- dprintf(5, " (done)");
- if (t->last_tuning_failed)
- dprintf(5, " (tuning failed)");
- dprintf(5, "\n");
- }
-}
-
-
-static void parse_terrestrial_delivery_system_descriptor (const unsigned char *buf,
- struct transponder *t)
-{
- static const fe_modulation_t m_tab [] = { QPSK, QAM_16, QAM_64, QAM_AUTO };
- static const fe_code_rate_t ofec_tab [8] = { FEC_1_2, FEC_2_3, FEC_3_4,
- FEC_5_6, FEC_7_8 };
- struct dvb_ofdm_parameters *o;
-
- if (!t) {
- warning("terrestrial_delivery_system_descriptor outside transport stream definition (ignored)\n");
- return;
- }
- o = &t->param.u.ofdm;
- t->type = FE_OFDM;
-
- t->param.frequency = (buf[2] << 24) | (buf[3] << 16);
- t->param.frequency |= (buf[4] << 8) | buf[5];
- t->param.frequency *= 10;
- t->param.inversion = spectral_inversion;
-
- o->bandwidth = BANDWIDTH_8_MHZ + ((buf[6] >> 5) & 0x3);
- o->constellation = m_tab[(buf[7] >> 6) & 0x3];
- o->hierarchy_information = HIERARCHY_NONE + ((buf[7] >> 3) & 0x3);
-
- if ((buf[7] & 0x7) > 4)
- o->code_rate_HP = FEC_AUTO;
- else
- o->code_rate_HP = ofec_tab [buf[7] & 0x7];
-
- if (((buf[8] >> 5) & 0x7) > 4)
- o->code_rate_LP = FEC_AUTO;
- else
- o->code_rate_LP = ofec_tab [(buf[8] >> 5) & 0x7];
-
- o->guard_interval = GUARD_INTERVAL_1_32 + ((buf[8] >> 3) & 0x3);
-
- o->transmission_mode = (buf[8] & 0x2) ?
- TRANSMISSION_MODE_8K :
- TRANSMISSION_MODE_2K;
-
- t->other_frequency_flag = (buf[8] & 0x01);
-
- if (verbosity >= 5) {
- debug("%#04x/%#04x ", t->network_id, t->transport_stream_id);
- dump_dvb_parameters (stderr, t);
- if (t->scan_done)
- dprintf(5, " (done)");
- if (t->last_tuning_failed)
- dprintf(5, " (tuning failed)");
- dprintf(5, "\n");
- }
-}
-
-static void parse_frequency_list_descriptor (const unsigned char *buf,
- struct transponder *t)
-{
- int n, i;
- typeof(*t->other_f) f;
-
- if (!t) {
- warning("frequency_list_descriptor outside transport stream definition (ignored)\n");
- return;
- }
- if (t->other_f)
- return;
-
- n = (buf[1] - 1) / 4;
- if (n < 1 || (buf[2] & 0x03) != 3)
- return;
-
- t->other_f = calloc(n, sizeof(*t->other_f));
- t->n_other_f = n;
- buf += 3;
- for (i = 0; i < n; i++) {
- f = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
- t->other_f[i] = f * 10;
- buf += 4;
- }
-}
-
-static void parse_service_descriptor (const unsigned char *buf, struct service *s)
-{
- unsigned char len;
- unsigned char *src, *dest;
-
- s->type = buf[2];
-
- buf += 3;
- len = *buf;
- buf++;
-
- if (s->provider_name)
- free (s->provider_name);
-
- s->provider_name = malloc (len + 1);
- memcpy (s->provider_name, buf, len);
- s->provider_name[len] = '\0';
-
- /* remove control characters (FIXME: handle short/long name) */
- /* FIXME: handle character set correctly (e.g. via iconv)
- * c.f. EN 300 468 annex A */
- for (src = dest = (unsigned char *) s->provider_name; *src; src++)
- if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f))
- *dest++ = *src;
- *dest = '\0';
- if (!s->provider_name[0]) {
- /* zap zero length names */
- free (s->provider_name);
- s->provider_name = 0;
- }
-
- if (s->service_name)
- free (s->service_name);
-
- buf += len;
- len = *buf;
- buf++;
-
- s->service_name = malloc (len + 1);
- memcpy (s->service_name, buf, len);
- s->service_name[len] = '\0';
-
- /* remove control characters (FIXME: handle short/long name) */
- /* FIXME: handle character set correctly (e.g. via iconv)
- * c.f. EN 300 468 annex A */
- for (src = dest = (unsigned char *) s->service_name; *src; src++)
- if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f))
- *dest++ = *src;
- *dest = '\0';
- if (!s->service_name[0]) {
- /* zap zero length names */
- free (s->service_name);
- s->service_name = 0;
- }
-
- info("0x%04x 0x%04x: pmt_pid 0x%04x %s -- %s (%s%s)\n",
- s->transport_stream_id,
- s->service_id,
- s->pmt_pid,
- s->provider_name, s->service_name,
- s->running == RM_NOT_RUNNING ? "not running" :
- s->running == RM_STARTS_SOON ? "starts soon" :
- s->running == RM_PAUSING ? "pausing" :
- s->running == RM_RUNNING ? "running" : "???",
- s->scrambled ? ", scrambled" : "");
-}
-
-static int find_descriptor(uint8_t tag, const unsigned char *buf,
- int descriptors_loop_len,
- const unsigned char **desc, int *desc_len)
-{
- while (descriptors_loop_len > 0) {
- unsigned char descriptor_tag = buf[0];
- unsigned char descriptor_len = buf[1] + 2;
-
- if (!descriptor_len) {
- warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag);
- break;
- }
-
- if (tag == descriptor_tag) {
- if (desc)
- *desc = buf;
- if (desc_len)
- *desc_len = descriptor_len;
- return 1;
- }
-
- buf += descriptor_len;
- descriptors_loop_len -= descriptor_len;
- }
- return 0;
-}
-
-static void parse_descriptors(enum table_type t, const unsigned char *buf,
- int descriptors_loop_len, void *data)
-{
- while (descriptors_loop_len > 0) {
- unsigned char descriptor_tag = buf[0];
- unsigned char descriptor_len = buf[1] + 2;
-
- if (!descriptor_len) {
- warning("descriptor_tag == 0x%02x, len is 0\n", descriptor_tag);
- break;
- }
-
- switch (descriptor_tag) {
- case 0x0a:
- if (t == PMT)
- parse_iso639_language_descriptor (buf, data);
- break;
-
- case 0x40:
- if (t == NIT)
- parse_network_name_descriptor (buf, data);
- break;
-
- case 0x43:
- if (t == NIT)
- parse_satellite_delivery_system_descriptor (buf, data);
- break;
-
- case 0x44:
- if (t == NIT)
- parse_cable_delivery_system_descriptor (buf, data);
- break;
-
- case 0x48:
- if (t == SDT)
- parse_service_descriptor (buf, data);
- break;
-
- case 0x53:
- if (t == SDT)
- parse_ca_identifier_descriptor (buf, data);
- break;
-
- case 0x5a:
- if (t == NIT)
- parse_terrestrial_delivery_system_descriptor (buf, data);
- break;
-
- case 0x62:
- if (t == NIT)
- parse_frequency_list_descriptor (buf, data);
- break;
-
- case 0x83:
- /* 0x83 is in the privately defined range of descriptor tags,
- * so we parse this only if the user says so to avoid
- * problems when 0x83 is something entirely different... */
- if (t == NIT && vdr_dump_channum)
- parse_terrestrial_uk_channel_number (buf, data);
- break;
-
- default:
- verbosedebug("skip descriptor 0x%02x\n", descriptor_tag);
- };
-
- buf += descriptor_len;
- descriptors_loop_len -= descriptor_len;
- }
-}
-
-
-static void parse_pat(const unsigned char *buf, int section_length,
- int transport_stream_id)
-{
- (void)transport_stream_id;
-
- while (section_length > 0) {
- struct service *s;
- int service_id = (buf[0] << 8) | buf[1];
-
- if (service_id == 0)
- goto skip; /* nit pid entry */
-
- /* SDT might have been parsed first... */
- s = find_service(current_tp, service_id);
- if (!s)
- s = alloc_service(current_tp, service_id);
- s->pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3];
- if (!s->priv && s->pmt_pid) {
- s->priv = malloc(sizeof(struct section_buf));
- setup_filter(s->priv, demux_devname,
- s->pmt_pid, 0x02, s->service_id, 1, 0, 5);
-
- add_filter (s->priv);
- }
-
-skip:
- buf += 4;
- section_length -= 4;
- };
-}
-
-
-static void parse_pmt (const unsigned char *buf, int section_length, int service_id)
-{
- int program_info_len;
- struct service *s;
- char msg_buf[14 * AUDIO_CHAN_MAX + 1];
- char *tmp;
- int i;
-
- s = find_service (current_tp, service_id);
- if (!s) {
- error("PMT for serivce_id 0x%04x was not in PAT\n", service_id);
- return;
- }
-
- s->pcr_pid = ((buf[0] & 0x1f) << 8) | buf[1];
-
- program_info_len = ((buf[2] & 0x0f) << 8) | buf[3];
-
- buf += program_info_len + 4;
- section_length -= program_info_len + 4;
-
- while (section_length >= 5) {
- int ES_info_len = ((buf[3] & 0x0f) << 8) | buf[4];
- int elementary_pid = ((buf[1] & 0x1f) << 8) | buf[2];
-
- switch (buf[0]) {
- case 0x01:
- case 0x02:
- moreverbose(" VIDEO : PID 0x%04x\n", elementary_pid);
- if (s->video_pid == 0)
- s->video_pid = elementary_pid;
- break;
- case 0x03:
- case 0x81: /* Audio per ATSC A/53B [2] Annex B */
- case 0x04:
- moreverbose(" AUDIO : PID 0x%04x\n", elementary_pid);
- if (s->audio_num < AUDIO_CHAN_MAX) {
- s->audio_pid[s->audio_num] = elementary_pid;
- parse_descriptors (PMT, buf + 5, ES_info_len, s);
- s->audio_num++;
- }
- else
- warning("more than %i audio channels, truncating\n",
- AUDIO_CHAN_MAX);
- break;
- case 0x06:
- if (find_descriptor(0x56, buf + 5, ES_info_len, NULL, NULL)) {
- moreverbose(" TELETEXT : PID 0x%04x\n", elementary_pid);
- s->teletext_pid = elementary_pid;
- break;
- }
- else if (find_descriptor(0x59, buf + 5, ES_info_len, NULL, NULL)) {
- /* Note: The subtitling descriptor can also signal
- * teletext subtitling, but then the teletext descriptor
- * will also be present; so we can be quite confident
- * that we catch DVB subtitling streams only here, w/o
- * parsing the descriptor. */
- moreverbose(" SUBTITLING: PID 0x%04x\n", elementary_pid);
- s->subtitling_pid = elementary_pid;
- break;
- }
- else if (find_descriptor(0x6a, buf + 5, ES_info_len, NULL, NULL)) {
- moreverbose(" AC3 : PID 0x%04x\n", elementary_pid);
- s->ac3_pid = elementary_pid;
- break;
- }
- /* fall through */
- default:
- moreverbose(" OTHER : PID 0x%04x TYPE 0x%02x\n", elementary_pid, buf[0]);
- };
-
- buf += ES_info_len + 5;
- section_length -= ES_info_len + 5;
- };
-
-
- tmp = msg_buf;
- tmp += sprintf(tmp, "0x%04x (%.4s)", s->audio_pid[0], s->audio_lang[0]);
-
- if (s->audio_num > AUDIO_CHAN_MAX) {
- warning("more than %i audio channels: %i, truncating to %i\n",
- AUDIO_CHAN_MAX, s->audio_num, AUDIO_CHAN_MAX);
- s->audio_num = AUDIO_CHAN_MAX;
- }
-
- for (i=1; i<s->audio_num; i++)
- tmp += sprintf(tmp, ", 0x%04x (%.4s)", s->audio_pid[i], s->audio_lang[i]);
-
- debug("0x%04x 0x%04x: %s -- %s, pmt_pid 0x%04x, vpid 0x%04x, apid %s\n",
- s->transport_stream_id,
- s->service_id,
- s->provider_name, s->service_name,
- s->pmt_pid, s->video_pid, msg_buf);
-}
-
-
-static void parse_nit (const unsigned char *buf, int section_length, int network_id)
-{
- int descriptors_loop_len = ((buf[0] & 0x0f) << 8) | buf[1];
-
- if (section_length < descriptors_loop_len + 4)
- {
- warning("section too short: network_id == 0x%04x, section_length == %i, "
- "descriptors_loop_len == %i\n",
- network_id, section_length, descriptors_loop_len);
- return;
- }
-
- parse_descriptors (NIT, buf + 2, descriptors_loop_len, NULL);
-
- section_length -= descriptors_loop_len + 4;
- buf += descriptors_loop_len + 4;
-
- while (section_length > 6) {
- int transport_stream_id = (buf[0] << 8) | buf[1];
- struct transponder *t, tn;
-
- descriptors_loop_len = ((buf[4] & 0x0f) << 8) | buf[5];
-
- if (section_length < descriptors_loop_len + 4)
- {
- warning("section too short: transport_stream_id == 0x%04x, "
- "section_length == %i, descriptors_loop_len == %i\n",
- transport_stream_id, section_length,
- descriptors_loop_len);
- break;
- }
-
- debug("transport_stream_id 0x%04x\n", transport_stream_id);
-
- memset(&tn, 0, sizeof(tn));
- tn.type = -1;
- tn.network_id = network_id;
- tn.original_network_id = (buf[2] << 8) | buf[3];
- tn.transport_stream_id = transport_stream_id;
-
- parse_descriptors (NIT, buf + 6, descriptors_loop_len, &tn);
-
- if (tn.type == fe_info.type) {
- /* only add if develivery_descriptor matches FE type */
- t = find_transponder(tn.param.frequency);
- if (!t)
- t = alloc_transponder(tn.param.frequency);
- copy_transponder(t, &tn);
- }
-
- section_length -= descriptors_loop_len + 6;
- buf += descriptors_loop_len + 6;
- }
-}
-
-
-static void parse_sdt (const unsigned char *buf, int section_length,
- int transport_stream_id)
-{
- (void)transport_stream_id;
-
- buf += 3; /* skip original network id + reserved field */
-
- while (section_length >= 5) {
- int service_id = (buf[0] << 8) | buf[1];
- int descriptors_loop_len = ((buf[3] & 0x0f) << 8) | buf[4];
- struct service *s;
-
- if (section_length < descriptors_loop_len || !descriptors_loop_len)
- {
- warning("section too short: service_id == 0x%02x, section_length == %i, "
- "descriptors_loop_len == %i\n",
- service_id, section_length,
- descriptors_loop_len);
- break;
- }
-
- s = find_service(current_tp, service_id);
- if (!s)
- /* maybe PAT has not yet been parsed... */
- s = alloc_service(current_tp, service_id);
-
- s->running = (buf[3] >> 5) & 0x7;
- s->scrambled = (buf[3] >> 4) & 1;
-
- parse_descriptors (SDT, buf + 5, descriptors_loop_len, s);
-
- section_length -= descriptors_loop_len + 5;
- buf += descriptors_loop_len + 5;
- };
-}
-
-/* ATSC PSIP VCT */
-static void parse_atsc_service_loc_desc(struct service *s,const unsigned char *buf)
-{
- struct ATSC_service_location_descriptor d = read_ATSC_service_location_descriptor(buf);
- int i;
- unsigned char *b = (unsigned char *) buf+5;
-
- s->pcr_pid = d.PCR_PID;
- for (i=0; i < d.number_elements; i++) {
- struct ATSC_service_location_element e = read_ATSC_service_location_element(b);
- switch (e.stream_type) {
- case 0x02: /* video */
- s->video_pid = e.elementary_PID;
- moreverbose(" VIDEO : PID 0x%04x\n", e.elementary_PID);
- break;
- case 0x81: /* ATSC audio */
- if (s->audio_num < AUDIO_CHAN_MAX) {
- s->audio_pid[s->audio_num] = e.elementary_PID;
- s->audio_lang[s->audio_num][0] = (e.ISO_639_language_code >> 16) & 0xff;
- s->audio_lang[s->audio_num][1] = (e.ISO_639_language_code >> 8) & 0xff;
- s->audio_lang[s->audio_num][2] = e.ISO_639_language_code & 0xff;
- s->audio_num++;
- }
- moreverbose(" AUDIO : PID 0x%04x lang: %s\n",e.elementary_PID,s->audio_lang[s->audio_num-1]);
-
- break;
- default:
- warning("unhandled stream_type: %x\n",e.stream_type);
- break;
- };
- b += 6;
- }
-}
-
-static void parse_atsc_ext_chan_name_desc(struct service *s,const unsigned char *buf)
-{
- unsigned char *b = (unsigned char *) buf+2;
- int i,j;
- int num_str = b[0];
-
- b++;
- for (i = 0; i < num_str; i++) {
- int num_seg = b[3];
- b += 4; /* skip lang code */
- for (j = 0; j < num_seg; j++) {
- int comp_type = b[0],/* mode = b[1],*/ num_bytes = b[2];
-
- switch (comp_type) {
- case 0x00:
- if (s->service_name)
- free(s->service_name);
- s->service_name = malloc(num_bytes * sizeof(char) + 1);
- memcpy(s->service_name,&b[3],num_bytes);
- s->service_name[num_bytes] = '\0';
- break;
- default:
- warning("compressed strings are not supported yet\n");
- break;
- }
- b += 3 + num_bytes;
- }
- }
-}
-
-static void parse_psip_descriptors(struct service *s,const unsigned char *buf,int len)
-{
- unsigned char *b = (unsigned char *) buf;
- int desc_len;
- while (len > 0) {
- desc_len = b[1];
- switch (b[0]) {
- case ATSC_SERVICE_LOCATION_DESCRIPTOR_ID:
- parse_atsc_service_loc_desc(s,b);
- break;
- case ATSC_EXTENDED_CHANNEL_NAME_DESCRIPTOR_ID:
- parse_atsc_ext_chan_name_desc(s,b);
- break;
- default:
- warning("unhandled psip descriptor: %02x\n",b[0]);
- break;
- }
- b += 2 + desc_len;
- len -= 2 + desc_len;
- }
-}
-
-static void parse_psip_vct (const unsigned char *buf, int section_length,
- int table_id, int transport_stream_id)
-{
- (void)section_length;
- (void)table_id;
- (void)transport_stream_id;
-
-/* int protocol_version = buf[0];*/
- int num_channels_in_section = buf[1];
- int i;
- int pseudo_id = 0xffff;
- unsigned char *b = (unsigned char *) buf + 2;
-
- for (i = 0; i < num_channels_in_section; i++) {
- struct service *s;
- struct tvct_channel ch = read_tvct_channel(b);
-
- switch (ch.service_type) {
- case 0x01:
- info("analog channels won't be put info channels.conf\n");
- break;
- case 0x02: /* ATSC TV */
- case 0x03: /* ATSC Radio */
- break;
- case 0x04: /* ATSC Data */
- default:
- continue;
- }
-
- if (ch.program_number == 0)
- ch.program_number = --pseudo_id;
-
- s = find_service(current_tp, ch.program_number);
- if (!s)
- s = alloc_service(current_tp, ch.program_number);
-
- if (s->service_name)
- free(s->service_name);
-
- s->service_name = malloc(7*sizeof(unsigned char));
- /* TODO find a better solution to convert UTF-16 */
- s->service_name[0] = ch.short_name0;
- s->service_name[1] = ch.short_name1;
- s->service_name[2] = ch.short_name2;
- s->service_name[3] = ch.short_name3;
- s->service_name[4] = ch.short_name4;
- s->service_name[5] = ch.short_name5;
- s->service_name[6] = ch.short_name6;
-
- parse_psip_descriptors(s,&b[32],ch.descriptors_length);
-
- s->channel_num = ch.major_channel_number << 10 | ch.minor_channel_number;
-
- if (ch.hidden) {
- s->running = RM_NOT_RUNNING;
- info("service is not running, pseudo program_number.");
- } else {
- s->running = RM_RUNNING;
- info("service is running.");
- }
-
- info(" Channel number: %d:%d. Name: '%s'\n",
- ch.major_channel_number, ch.minor_channel_number,s->service_name);
-
- b += 32 + ch.descriptors_length;
- }
-}
-
-static int get_bit (uint8_t *bitfield, int bit)
-{
- return (bitfield[bit/8] >> (bit % 8)) & 1;
-}
-
-static void set_bit (uint8_t *bitfield, int bit)
-{
- bitfield[bit/8] |= 1 << (bit % 8);
-}
-
-
-/**
- * returns 0 when more sections are expected
- * 1 when all sections are read on this pid
- * -1 on invalid table id
- */
-static int parse_section (struct section_buf *s)
-{
- const unsigned char *buf = s->buf;
- int table_id;
- int section_length;
- int table_id_ext;
- int section_version_number;
- int section_number;
- int last_section_number;
- int i;
-
- table_id = buf[0];
-
- if (s->table_id != table_id)
- return -1;
-
- section_length = ((buf[1] & 0x0f) << 8) | buf[2];
-
- table_id_ext = (buf[3] << 8) | buf[4];
- section_version_number = (buf[5] >> 1) & 0x1f;
- section_number = buf[6];
- last_section_number = buf[7];
-
- if (s->segmented && s->table_id_ext != -1 && s->table_id_ext != table_id_ext) {
- /* find or allocate actual section_buf matching table_id_ext */
- while (s->next_seg) {
- s = s->next_seg;
- if (s->table_id_ext == table_id_ext)
- break;
- }
- if (s->table_id_ext != table_id_ext) {
- assert(s->next_seg == NULL);
- s->next_seg = calloc(1, sizeof(struct section_buf));
- s->next_seg->segmented = s->segmented;
- s->next_seg->run_once = s->run_once;
- s->next_seg->timeout = s->timeout;
- s = s->next_seg;
- s->table_id = table_id;
- s->table_id_ext = table_id_ext;
- s->section_version_number = section_version_number;
- }
- }
-
- if (s->section_version_number != section_version_number ||
- s->table_id_ext != table_id_ext) {
- struct section_buf *next_seg = s->next_seg;
-
- if (s->section_version_number != -1 && s->table_id_ext != -1)
- debug("section version_number or table_id_ext changed "
- "%d -> %d / %04x -> %04x\n",
- s->section_version_number, section_version_number,
- s->table_id_ext, table_id_ext);
- s->table_id_ext = table_id_ext;
- s->section_version_number = section_version_number;
- s->sectionfilter_done = 0;
- memset (s->section_done, 0, sizeof(s->section_done));
- s->next_seg = next_seg;
- }
-
- buf += 8; /* past generic table header */
- section_length -= 5 + 4; /* header + crc */
- if (section_length < 0) {
- warning("truncated section (PID 0x%04x, lenght %d)",
- s->pid, section_length + 9);
- return 0;
- }
-
- if (!get_bit(s->section_done, section_number)) {
- set_bit (s->section_done, section_number);
-
- debug("pid 0x%02x tid 0x%02x table_id_ext 0x%04x, "
- "%i/%i (version %i)\n",
- s->pid, table_id, table_id_ext, section_number,
- last_section_number, section_version_number);
-
- switch (table_id) {
- case 0x00:
- verbose("PAT\n");
- parse_pat (buf, section_length, table_id_ext);
- break;
-
- case 0x02:
- verbose("PMT 0x%04x for service 0x%04x\n", s->pid, table_id_ext);
- parse_pmt (buf, section_length, table_id_ext);
- break;
-
- case 0x41:
- verbose("////////////////////////////////////////////// NIT other\n");
- case 0x40:
- verbose("NIT (%s TS)\n", table_id == 0x40 ? "actual":"other");
- parse_nit (buf, section_length, table_id_ext);
- break;
-
- case 0x42:
- case 0x46:
- verbose("SDT (%s TS)\n", table_id == 0x42 ? "actual":"other");
- parse_sdt (buf, section_length, table_id_ext);
- break;
-
- case 0xc8:
- case 0xc9:
- verbose("ATSC VCT\n");
- parse_psip_vct(buf, section_length, table_id, table_id_ext);
- break;
- default:
- ;
- };
-
- for (i = 0; i <= last_section_number; i++)
- if (get_bit (s->section_done, i) == 0)
- break;
-
- if (i > last_section_number)
- s->sectionfilter_done = 1;
- }
-
- if (s->segmented) {
- /* always wait for timeout; this is because we don't now how
- * many segments there are
- */
- return 0;
- }
- else if (s->sectionfilter_done)
- return 1;
-
- return 0;
-}
-
-
-static int read_sections (struct section_buf *s)
-{
- int section_length, count;
-
- if (s->sectionfilter_done && !s->segmented)
- return 1;
-
- /* the section filter API guarantess that we get one full section
- * per read(), provided that the buffer is large enough (it is)
- */
- if (((count = read (s->fd, s->buf, sizeof(s->buf))) < 0) && errno == EOVERFLOW)
- count = read (s->fd, s->buf, sizeof(s->buf));
- if (count < 0) {
- errorn("read_sections: read error");
- return -1;
- }
-
- if (count < 4)
- return -1;
-
- section_length = ((s->buf[1] & 0x0f) << 8) | s->buf[2];
-
- if (count != section_length + 3)
- return -1;
-
- if (parse_section(s) == 1)
- return 1;
-
- return 0;
-}
-
-
-static LIST_HEAD(running_filters);
-static LIST_HEAD(waiting_filters);
-static int n_running;
-#define MAX_RUNNING 27
-static struct pollfd poll_fds[MAX_RUNNING];
-static struct section_buf* poll_section_bufs[MAX_RUNNING];
-
-
-static void setup_filter (struct section_buf* s, const char *dmx_devname,
- int pid, int tid, int tid_ext,
- int run_once, int segmented, int timeout)
-{
- memset (s, 0, sizeof(struct section_buf));
-
- s->fd = -1;
- s->dmx_devname = dmx_devname;
- s->pid = pid;
- s->table_id = tid;
-
- s->run_once = run_once;
- s->segmented = segmented;
-
- if (long_timeout)
- s->timeout = 5 * timeout;
- else
- s->timeout = timeout;
-
- s->table_id_ext = tid_ext;
- s->section_version_number = -1;
-
- INIT_LIST_HEAD (&s->list);
-}
-
-static void update_poll_fds(void)
-{
- struct list_head *p;
- struct section_buf* s;
- int i;
-
- memset(poll_section_bufs, 0, sizeof(poll_section_bufs));
- for (i = 0; i < MAX_RUNNING; i++)
- poll_fds[i].fd = -1;
- i = 0;
- list_for_each (p, &running_filters) {
- if (i >= MAX_RUNNING)
- fatal("too many poll_fds\n");
- s = list_entry (p, struct section_buf, list);
- if (s->fd == -1)
- fatal("s->fd == -1 on running_filters\n");
- verbosedebug("poll fd %d\n", s->fd);
- poll_fds[i].fd = s->fd;
- poll_fds[i].events = POLLIN;
- poll_fds[i].revents = 0;
- poll_section_bufs[i] = s;
- i++;
- }
- if (i != n_running)
- fatal("n_running is hosed\n");
-}
-
-static int start_filter (struct section_buf* s)
-{
- struct dmx_sct_filter_params f;
-
- if (n_running >= MAX_RUNNING)
- goto err0;
- if ((s->fd = open (s->dmx_devname, O_RDWR | O_NONBLOCK)) < 0)
- goto err0;
-
- verbosedebug("start filter pid 0x%04x table_id 0x%02x\n", s->pid, s->table_id);
-
- memset(&f, 0, sizeof(f));
-
- f.pid = (uint16_t) s->pid;
-
- if (s->table_id < 0x100 && s->table_id > 0) {
- f.filter.filter[0] = (uint8_t) s->table_id;
- f.filter.mask[0] = 0xff;
- }
- if (s->table_id_ext < 0x10000 && s->table_id_ext > 0) {
- f.filter.filter[1] = (uint8_t) ((s->table_id_ext >> 8) & 0xff);
- f.filter.filter[2] = (uint8_t) (s->table_id_ext & 0xff);
- f.filter.mask[1] = 0xff;
- f.filter.mask[2] = 0xff;
- }
-
- f.timeout = 0;
- f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
-
- if (ioctl(s->fd, DMX_SET_FILTER, &f) == -1) {
- errorn ("ioctl DMX_SET_FILTER failed");
- goto err1;
- }
-
- s->sectionfilter_done = 0;
- time(&s->start_time);
-
- list_del_init (&s->list); /* might be in waiting filter list */
- list_add (&s->list, &running_filters);
-
- n_running++;
- update_poll_fds();
-
- return 0;
-
-err1:
- ioctl (s->fd, DMX_STOP);
- close (s->fd);
-err0:
- return -1;
-}
-
-
-static void stop_filter (struct section_buf *s)
-{
- verbosedebug("stop filter pid 0x%04x\n", s->pid);
- ioctl (s->fd, DMX_STOP);
- close (s->fd);
- s->fd = -1;
- list_del (&s->list);
- s->running_time += time(NULL) - s->start_time;
-
- n_running--;
- update_poll_fds();
-}
-
-
-static void add_filter (struct section_buf *s)
-{
- verbosedebug("add filter pid 0x%04x\n", s->pid);
- if (start_filter (s))
- list_add_tail (&s->list, &waiting_filters);
-}
-
-
-static void remove_filter (struct section_buf *s)
-{
- verbosedebug("remove filter pid 0x%04x\n", s->pid);
- stop_filter (s);
-
- while (!list_empty(&waiting_filters)) {
- struct list_head *next = waiting_filters.next;
- s = list_entry (next, struct section_buf, list);
- if (start_filter (s))
- break;
- };
-}
-
-
-static void read_filters (void)
-{
- struct section_buf *s;
- int i, n, done;
-
- n = poll(poll_fds, n_running, 1000);
- if (n == -1)
- errorn("poll");
-
- for (i = 0; i < n_running; i++) {
- s = poll_section_bufs[i];
- if (!s)
- fatal("poll_section_bufs[%d] is NULL\n", i);
- if (poll_fds[i].revents)
- done = read_sections (s) == 1;
- else
- done = 0; /* timeout */
- if (done || time(NULL) > s->start_time + s->timeout) {
- if (s->run_once) {
- if (done)
- verbosedebug("filter done pid 0x%04x\n", s->pid);
- else
- warning("filter timeout pid 0x%04x\n", s->pid);
- remove_filter (s);
- }
- }
- }
-}
-
-
-static int mem_is_zero (const void *mem, int size)
-{
- const char *p = mem;
- int i;
-
- for (i=0; i<size; i++) {
- if (p[i] != 0x00)
- return 0;
- }
-
- return 1;
-}
-
-
-static int switch_pos = 0;
-
-static int __tune_to_transponder (int frontend_fd, struct transponder *t)
-{
- struct dvb_frontend_parameters p;
- fe_status_t s;
- int i;
-
- current_tp = t;
-
- if (mem_is_zero (&t->param, sizeof(struct dvb_frontend_parameters)))
- return -1;
-
- memcpy (&p, &t->param, sizeof(struct dvb_frontend_parameters));
-
- if (verbosity >= 1) {
- dprintf(1, ">>> tune to: ");
- dump_dvb_parameters (stderr, t);
- if (t->last_tuning_failed)
- dprintf(1, " (tuning failed)");
- dprintf(1, "\n");
- }
-
- if (t->type == FE_QPSK) {
- int hiband = 0;
-
- if (lnb_type.switch_val && lnb_type.high_val &&
- p.frequency >= lnb_type.switch_val)
- hiband = 1;
-
- setup_switch (frontend_fd,
- switch_pos,
- t->polarisation == POLARISATION_VERTICAL ? 0 : 1,
- hiband);
- usleep(50000);
- if (hiband)
- p.frequency = abs(p.frequency - lnb_type.high_val);
- else
- p.frequency = abs(p.frequency - lnb_type.low_val);
- }
-
- if (ioctl(frontend_fd, FE_SET_FRONTEND, &p) == -1) {
- errorn("Setting frontend parameters failed");
- return -1;
- }
-
- for (i = 0; i < 10; i++) {
- usleep (200000);
-
- if (ioctl(frontend_fd, FE_READ_STATUS, &s) == -1) {
- errorn("FE_READ_STATUS failed");
- return -1;
- }
-
- verbose(">>> tuning status == 0x%02x\n", s);
-
- if (s & FE_HAS_LOCK) {
- t->last_tuning_failed = 0;
- return 0;
- }
- }
-
- warning(">>> tuning failed!!!\n");
-
- t->last_tuning_failed = 1;
-
- return -1;
-}
-
-static int tune_to_transponder (int frontend_fd, struct transponder *t)
-{
- /* move TP from "new" to "scanned" list */
- list_del_init(&t->list);
- list_add_tail(&t->list, &scanned_transponders);
- t->scan_done = 1;
-
- if (t->type != fe_info.type) {
- warning("frontend type (%s) is not compatible with requested tuning type (%s)\n",
- fe_type2str(fe_info.type),fe_type2str(t->type));
- /* ignore cable descriptors in sat NIT and vice versa */
- t->last_tuning_failed = 1;
- return -1;
- }
-
- if (__tune_to_transponder (frontend_fd, t) == 0)
- return 0;
-
- return __tune_to_transponder (frontend_fd, t);
-}
-
-
-static int tune_to_next_transponder (int frontend_fd)
-{
- struct list_head *pos, *tmp;
- struct transponder *t, *to;
- uint32_t freq;
-
- list_for_each_safe(pos, tmp, &new_transponders) {
- t = list_entry (pos, struct transponder, list);
-retry:
- if (tune_to_transponder (frontend_fd, t) == 0)
- return 0;
-next:
- if (t->other_frequency_flag && t->other_f && t->n_other_f) {
- /* check if the alternate freqeuncy is really new to us */
- freq = t->other_f[t->n_other_f - 1];
- t->n_other_f--;
- if (find_transponder(freq))
- goto next;
-
- /* remember tuning to the old frequency failed */
- to = calloc(1, sizeof(*to));
- to->param.frequency = t->param.frequency;
- to->wrong_frequency = 1;
- INIT_LIST_HEAD(&to->list);
- INIT_LIST_HEAD(&to->services);
- list_add_tail(&to->list, &scanned_transponders);
- copy_transponder(to, t);
-
- t->param.frequency = freq;
- info("retrying with f=%d\n", t->param.frequency);
- goto retry;
- }
- }
- return -1;
-}
-
-struct strtab {
- const char *str;
- int val;
-};
-static int str2enum(const char *str, const struct strtab *tab, int deflt)
-{
- while (tab->str) {
- if (!strcmp(tab->str, str))
- return tab->val;
- tab++;
- }
- error("invalid enum value '%s'\n", str);
- return deflt;
-}
-
-static const char * enum2str(int v, const struct strtab *tab, const char *deflt)
-{
- while (tab->str) {
- if (v == tab->val)
- return tab->str;
- tab++;
- }
- error("invalid enum value '%d'\n", v);
- return deflt;
-}
-
-static enum fe_code_rate str2fec(const char *fec)
-{
- struct strtab fectab[] = {
- { "NONE", FEC_NONE },
- { "1/2", FEC_1_2 },
- { "2/3", FEC_2_3 },
- { "3/4", FEC_3_4 },
- { "4/5", FEC_4_5 },
- { "5/6", FEC_5_6 },
- { "6/7", FEC_6_7 },
- { "7/8", FEC_7_8 },
- { "8/9", FEC_8_9 },
- { "AUTO", FEC_AUTO },
- { NULL, 0 }
- };
- return str2enum(fec, fectab, FEC_AUTO);
-}
-
-static enum fe_modulation str2qam(const char *qam)
-{
- struct strtab qamtab[] = {
- { "QPSK", QPSK },
- { "QAM16", QAM_16 },
- { "QAM32", QAM_32 },
- { "QAM64", QAM_64 },
- { "QAM128", QAM_128 },
- { "QAM256", QAM_256 },
- { "AUTO", QAM_AUTO },
- { "8VSB", VSB_8 },
- { "16VSB", VSB_16 },
- { NULL, 0 }
- };
- return str2enum(qam, qamtab, QAM_AUTO);
-}
-
-static enum fe_bandwidth str2bandwidth(const char *bw)
-{
- struct strtab bwtab[] = {
- { "8MHz", BANDWIDTH_8_MHZ },
- { "7MHz", BANDWIDTH_7_MHZ },
- { "6MHz", BANDWIDTH_6_MHZ },
- { "AUTO", BANDWIDTH_AUTO },
- { NULL, 0 }
- };
- return str2enum(bw, bwtab, BANDWIDTH_AUTO);
-}
-
-static enum fe_transmit_mode str2mode(const char *mode)
-{
- struct strtab modetab[] = {
- { "2k", TRANSMISSION_MODE_2K },
- { "8k", TRANSMISSION_MODE_8K },
- { "AUTO", TRANSMISSION_MODE_AUTO },
- { NULL, 0 }
- };
- return str2enum(mode, modetab, TRANSMISSION_MODE_AUTO);
-}
-
-static enum fe_guard_interval str2guard(const char *guard)
-{
- struct strtab guardtab[] = {
- { "1/32", GUARD_INTERVAL_1_32 },
- { "1/16", GUARD_INTERVAL_1_16 },
- { "1/8", GUARD_INTERVAL_1_8 },
- { "1/4", GUARD_INTERVAL_1_4 },
- { "AUTO", GUARD_INTERVAL_AUTO },
- { NULL, 0 }
- };
- return str2enum(guard, guardtab, GUARD_INTERVAL_AUTO);
-}
-
-static enum fe_hierarchy str2hier(const char *hier)
-{
- struct strtab hiertab[] = {
- { "NONE", HIERARCHY_NONE },
- { "1", HIERARCHY_1 },
- { "2", HIERARCHY_2 },
- { "4", HIERARCHY_4 },
- { "AUTO", HIERARCHY_AUTO },
- { NULL, 0 }
- };
- return str2enum(hier, hiertab, HIERARCHY_AUTO);
-}
-
-static const char * fe_type2str(fe_type_t t)
-{
- struct strtab typetab[] = {
- { "QPSK", FE_QPSK,},
- { "QAM", FE_QAM, },
- { "OFDM", FE_OFDM,},
- { "ATSC", FE_ATSC,},
- { NULL, 0 }
- };
-
- return enum2str(t, typetab, "UNK");
-}
-
-static int tune_initial (int frontend_fd, const char *initial)
-{
- FILE *inif;
- unsigned int f, sr;
- char buf[200];
- char pol[20], fec[20], qam[20], bw[20], fec2[20], mode[20], guard[20], hier[20];
- struct transponder *t;
-
- inif = fopen(initial, "r");
- if (!inif) {
- error("cannot open '%s': %d %m\n", initial, errno);
- return -1;
- }
- while (fgets(buf, sizeof(buf), inif)) {
- if (buf[0] == '#' || buf[0] == '\n')
- ;
- else if (sscanf(buf, "S %u %1[HVLR] %u %4s\n", &f, pol, &sr, fec) == 4) {
- t = alloc_transponder(f);
- t->type = FE_QPSK;
- switch(pol[0]) {
- case 'H':
- case 'L':
- t->polarisation = POLARISATION_HORIZONTAL;
- break;
- default:
- t->polarisation = POLARISATION_VERTICAL;;
- break;
- }
- t->param.inversion = spectral_inversion;
- t->param.u.qpsk.symbol_rate = sr;
- t->param.u.qpsk.fec_inner = str2fec(fec);
- info("initial transponder %u %c %u %d\n",
- t->param.frequency,
- pol[0], sr,
- t->param.u.qpsk.fec_inner);
- }
- else if (sscanf(buf, "C %u %u %4s %6s\n", &f, &sr, fec, qam) == 4) {
- t = alloc_transponder(f);
- t->type = FE_QAM;
- t->param.inversion = spectral_inversion;
- t->param.u.qam.symbol_rate = sr;
- t->param.u.qam.fec_inner = str2fec(fec);
- t->param.u.qam.modulation = str2qam(qam);
- info("initial transponder %u %u %d %d\n",
- t->param.frequency,
- sr,
- t->param.u.qam.fec_inner,
- t->param.u.qam.modulation);
- }
- else if (sscanf(buf, "T %u %4s %4s %4s %7s %4s %4s %4s\n",
- &f, bw, fec, fec2, qam, mode, guard, hier) == 8) {
- t = alloc_transponder(f);
- t->type = FE_OFDM;
- t->param.inversion = spectral_inversion;
- t->param.u.ofdm.bandwidth = str2bandwidth(bw);
- t->param.u.ofdm.code_rate_HP = str2fec(fec);
- if (t->param.u.ofdm.code_rate_HP == FEC_NONE)
- t->param.u.ofdm.code_rate_HP = FEC_AUTO;
- t->param.u.ofdm.code_rate_LP = str2fec(fec2);
- if (t->param.u.ofdm.code_rate_LP == FEC_NONE)
- t->param.u.ofdm.code_rate_LP = FEC_AUTO;
- t->param.u.ofdm.constellation = str2qam(qam);
- t->param.u.ofdm.transmission_mode = str2mode(mode);
- t->param.u.ofdm.guard_interval = str2guard(guard);
- t->param.u.ofdm.hierarchy_information = str2hier(hier);
- info("initial transponder %u %d %d %d %d %d %d %d\n",
- t->param.frequency,
- t->param.u.ofdm.bandwidth,
- t->param.u.ofdm.code_rate_HP,
- t->param.u.ofdm.code_rate_LP,
- t->param.u.ofdm.constellation,
- t->param.u.ofdm.transmission_mode,
- t->param.u.ofdm.guard_interval,
- t->param.u.ofdm.hierarchy_information);
- }
- else if (sscanf(buf, "A %u %7s\n",
- &f,qam) == 2) {
- t = alloc_transponder(f);
- t->type = FE_ATSC;
- t->param.u.vsb.modulation = str2qam(qam);
- } else
- error("cannot parse'%s'\n", buf);
- }
-
- fclose(inif);
-
- return tune_to_next_transponder(frontend_fd);
-}
-
-
-static void scan_tp_atsc(void)
-{
- struct section_buf s0,s1,s2;
-
- if (no_ATSC_PSIP) {
- setup_filter(&s0, demux_devname, 0x00, 0x00, -1, 1, 0, 5); /* PAT */
- add_filter(&s0);
- } else {
- if (ATSC_type & 0x1) {
- setup_filter(&s0, demux_devname, 0x1ffb, 0xc8, -1, 1, 0, 5); /* terrestrial VCT */
- add_filter(&s0);
- }
- if (ATSC_type & 0x2) {
- setup_filter(&s1, demux_devname, 0x1ffb, 0xc9, -1, 1, 0, 5); /* cable VCT */
- add_filter(&s1);
- }
- setup_filter(&s2, demux_devname, 0x00, 0x00, -1, 1, 0, 5); /* PAT */
- add_filter(&s2);
- }
-
- do {
- read_filters ();
- } while (!(list_empty(&running_filters) &&
- list_empty(&waiting_filters)));
-}
-
-static void scan_tp_dvb (void)
-{
- struct section_buf s0;
- struct section_buf s1;
- struct section_buf s2;
- struct section_buf s3;
-
- /**
- * filter timeouts > min repetition rates specified in ETR211
- */
- setup_filter (&s0, demux_devname, 0x00, 0x00, -1, 1, 0, 5); /* PAT */
- setup_filter (&s1, demux_devname, 0x11, 0x42, -1, 1, 0, 5); /* SDT */
-
- add_filter (&s0);
- add_filter (&s1);
-
- if (!current_tp_only || output_format != OUTPUT_PIDS) {
- setup_filter (&s2, demux_devname, 0x10, 0x40, -1, 1, 0, 15); /* NIT */
- add_filter (&s2);
- if (get_other_nits) {
- /* get NIT-others
- * Note: There is more than one NIT-other: one per
- * network, separated by the network_id.
- */
- setup_filter (&s3, demux_devname, 0x10, 0x41, -1, 1, 1, 15);
- add_filter (&s3);
- }
- }
-
- do {
- read_filters ();
- } while (!(list_empty(&running_filters) &&
- list_empty(&waiting_filters)));
-}
-
-static void scan_tp(void)
-{
- switch(fe_info.type) {
- case FE_QPSK:
- case FE_QAM:
- case FE_OFDM:
- scan_tp_dvb();
- break;
- case FE_ATSC:
- scan_tp_atsc();
- break;
- default:
- break;
- }
-}
-
-static void scan_network (int frontend_fd, const char *initial)
-{
- if (tune_initial (frontend_fd, initial) < 0) {
- error("initial tuning failed\n");
- return;
- }
-
- do {
- scan_tp();
- } while (tune_to_next_transponder(frontend_fd) == 0);
-}
-
-
-static void pids_dump_service_parameter_set(FILE *f, struct service *s)
-{
- int i;
-
- fprintf(f, "%-24.24s (0x%04x) %02x: ", s->service_name, s->service_id, s->type);
- if (!s->pcr_pid || (s->type > 2))
- fprintf(f, " ");
- else if (s->pcr_pid == s->video_pid)
- fprintf(f, "PCR == V ");
- else if ((s->audio_num == 1) && (s->pcr_pid == s->audio_pid[0]))
- fprintf(f, "PCR == A ");
- else
- fprintf(f, "PCR 0x%04x ", s->pcr_pid);
- if (s->video_pid)
- fprintf(f, "V 0x%04x", s->video_pid);
- else
- fprintf(f, " ");
- if (s->audio_num)
- fprintf(f, " A");
- for (i = 0; i < s->audio_num; i++) {
- fprintf(f, " 0x%04x", s->audio_pid[i]);
- if (s->audio_lang[i][0])
- fprintf(f, " (%.3s)", s->audio_lang[i]);
- else if (s->audio_num == 1)
- fprintf(f, " ");
- }
- if (s->teletext_pid)
- fprintf(f, " TT 0x%04x", s->teletext_pid);
- if (s->ac3_pid)
- fprintf(f, " AC3 0x%04x", s->ac3_pid);
- if (s->subtitling_pid)
- fprintf(f, " SUB 0x%04x", s->subtitling_pid);
- fprintf(f, "\n");
-}
-
-static char sat_polarisation (struct transponder *t)
-{
- return t->polarisation == POLARISATION_VERTICAL ? 'v' : 'h';
-}
-
-static int sat_number (struct transponder *t)
-{
- (void) t;
-
- return switch_pos;
-}
-
-static void dump_lists (void)
-{
- struct list_head *p1, *p2;
- struct transponder *t;
- struct service *s;
- int n = 0, i;
- char sn[20];
- int anon_services = 0;
-
- list_for_each(p1, &scanned_transponders) {
- t = list_entry(p1, struct transponder, list);
- if (t->wrong_frequency)
- continue;
- list_for_each(p2, &t->services) {
- n++;
- }
- }
- info("dumping lists (%d services)\n", n);
-
- list_for_each(p1, &scanned_transponders) {
- t = list_entry(p1, struct transponder, list);
- if (t->wrong_frequency)
- continue;
- list_for_each(p2, &t->services) {
- s = list_entry(p2, struct service, list);
-
- if (!s->service_name) {
- /* not in SDT */
- if (unique_anon_services)
- snprintf(sn, sizeof(sn), "[%03x-%04x]",
- anon_services, s->service_id);
- else
- snprintf(sn, sizeof(sn), "[%04x]",
- s->service_id);
- s->service_name = strdup(sn);
- anon_services++;
- }
- /* ':' is field separator in szap and vdr service lists */
- for (i = 0; s->service_name[i]; i++) {
- if (s->service_name[i] == ':')
- s->service_name[i] = ' ';
- }
- for (i = 0; s->provider_name && s->provider_name[i]; i++) {
- if (s->provider_name[i] == ':')
- s->provider_name[i] = ' ';
- }
- if (s->video_pid && !(serv_select & 1))
- continue; /* no TV services */
- if (!s->video_pid && s->audio_num && !(serv_select & 2))
- continue; /* no radio services */
- if (!s->video_pid && !s->audio_num && !(serv_select & 4))
- continue; /* no data/other services */
- if (s->scrambled && !ca_select)
- continue; /* FTA only */
- switch (output_format)
- {
- case OUTPUT_PIDS:
- pids_dump_service_parameter_set (stdout, s);
- break;
- case OUTPUT_VDR:
- vdr_dump_service_parameter_set (stdout,
- s->service_name,
- s->provider_name,
- t->type,
- &t->param,
- sat_polarisation(t),
- s->video_pid,
- s->pcr_pid,
- s->audio_pid,
- s->audio_lang,
- s->audio_num,
- s->teletext_pid,
- s->scrambled,
- //FIXME: s->subtitling_pid
- s->ac3_pid,
- s->service_id,
- t->original_network_id,
- s->transport_stream_id,
- t->orbital_pos,
- t->we_flag,
- vdr_dump_provider,
- ca_select,
- vdr_version,
- vdr_dump_channum,
- s->channel_num);
- break;
- case OUTPUT_ZAP:
- zap_dump_service_parameter_set (stdout,
- s->service_name,
- t->type,
- &t->param,
- sat_polarisation(t),
- sat_number(t),
- s->video_pid,
- s->audio_pid,
- s->service_id);
- default:
- break;
- }
- }
- }
- info("Done.\n");
-}
-
-static void show_existing_tuning_data_files(void)
-{
-#ifndef DATADIR
-#define DATADIR "/usr/local/share"
-#endif
- static const char* prefixlist[] = { DATADIR "/dvb", "/etc/dvb",
- DATADIR "/doc/packages/dvb", 0 };
- unsigned int i;
- const char **prefix;
- fprintf(stderr, "initial tuning data files:\n");
- for (prefix = prefixlist; *prefix; prefix++) {
- glob_t globbuf;
- char* globspec = malloc (strlen(*prefix)+9);
- strcpy (globspec, *prefix); strcat (globspec, "/dvb-?/*");
- if (! glob (globspec, 0, 0, &globbuf)) {
- for (i=0; i < globbuf.gl_pathc; i++)
- fprintf(stderr, " file: %s\n", globbuf.gl_pathv[i]);
- }
- free (globspec);
- globfree (&globbuf);
- }
-}
-
-static void handle_sigint(int sig)
-{
- (void)sig;
- error("interrupted by SIGINT, dumping partial result...\n");
- dump_lists();
- exit(2);
-}
-
-static const char *usage = "\n"
- "usage: %s [options...] [-c | initial-tuning-data-file]\n"
- " atsc/dvbscan doesn't do frequency scans, hence it needs initial\n"
- " tuning data for at least one transponder/channel.\n"
- " -c scan on currently tuned transponder only\n"
- " -v verbose (repeat for more)\n"
- " -q quiet (repeat for less)\n"
- " -a N use DVB /dev/dvb/adapterN/\n"
- " -f N use DVB /dev/dvb/adapter?/frontendN\n"
- " -d N use DVB /dev/dvb/adapter?/demuxN\n"
- " -s N use DiSEqC switch position N (DVB-S only)\n"
- " -i N spectral inversion setting (0: off, 1: on, 2: auto [default])\n"
- " -n evaluate NIT-other for full network scan (slow!)\n"
- " -5 multiply all filter timeouts by factor 5\n"
- " for non-DVB-compliant section repitition rates\n"
- " -o fmt output format: 'zap' (default), 'vdr' or 'pids' (default with -c)\n"
- " -x N Conditional Axcess, (default 1)\n"
- " N=0 gets only FTA channels\n"
- " N=xxx sets ca field in vdr output to :xxx:\n"
- " -t N Service select, Combined bitfield parameter.\n"
- " 1 = TV, 2 = Radio, 4 = Other, (default 7)\n"
- " -p for vdr output format: dump provider name\n"
- " -e N VDR version, default 2 for VDR-1.2.x\n"
- " ANYTHING ELSE GIVES NONZERO NIT and TID\n"
- " Vdr version 1.3.x and up implies -p.\n"
- " -l lnb-type (DVB-S Only) (use -l help to print types) or \n"
- " -l low[,high[,switch]] in Mhz\n"
- " -u UK DVB-T Freeview channel numbering for VDR\n\n"
- " -P do not use ATSC PSIP tables for scanning\n"
- " (but only PAT and PMT) (applies for ATSC only)\n"
- " -A N check for ATSC 1=Terrestrial [default], 2=Cable or 3=both\n"
- " -U Uniquely name unknown services\n";
-
-void
-bad_usage(char *pname, int problem)
-{
- int i;
- struct lnb_types_st *lnbp;
- char **cp;
-
- switch (problem) {
- default:
- case 0:
- fprintf (stderr, usage, pname);
- break;
- case 1:
- i = 0;
- fprintf(stderr, "-l <lnb-type> or -l low[,high[,switch]] in Mhz\n"
- "where <lnb-type> 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++;
- }
- break;
- case 2:
- show_existing_tuning_data_files();
- fprintf (stderr, usage, pname);
- }
-}
-
-int main (int argc, char **argv)
-{
- char frontend_devname [80];
- int adapter = 0, frontend = 0, demux = 0;
- int opt, i;
- int frontend_fd;
- int fe_open_mode;
- const char *initial = NULL;
-
- if (argc <= 1) {
- bad_usage(argv[0], 2);
- return -1;
- }
-
- /* start with default lnb type */
- lnb_type = *lnb_enum(0);
- while ((opt = getopt(argc, argv, "5cnpa:f:d:s:o:x:e:t:i:l:vquPA:U")) != -1) {
- switch (opt) {
- case 'a':
- adapter = strtoul(optarg, NULL, 0);
- break;
- case 'c':
- current_tp_only = 1;
- if (!output_format_set)
- output_format = OUTPUT_PIDS;
- break;
- case 'n':
- get_other_nits = 1;
- break;
- case 'd':
- demux = strtoul(optarg, NULL, 0);
- break;
- case 'f':
- frontend = strtoul(optarg, NULL, 0);
- break;
- case 'p':
- vdr_dump_provider = 1;
- break;
- case 's':
- switch_pos = strtoul(optarg, NULL, 0);
- break;
- case 'o':
- if (strcmp(optarg, "zap") == 0) output_format = OUTPUT_ZAP;
- else if (strcmp(optarg, "vdr") == 0) output_format = OUTPUT_VDR;
- else if (strcmp(optarg, "pids") == 0) output_format = OUTPUT_PIDS;
- else {
- bad_usage(argv[0], 0);
- return -1;
- }
- output_format_set = 1;
- break;
- case '5':
- long_timeout = 1;
- break;
- case 'x':
- ca_select = strtoul(optarg, NULL, 0);
- break;
- case 'e':
- vdr_version = strtoul(optarg, NULL, 0);
- break;
- case 't':
- serv_select = strtoul(optarg, NULL, 0);
- break;
- case 'i':
- spectral_inversion = strtoul(optarg, NULL, 0);
- break;
- case 'l':
- if (lnb_decode(optarg, &lnb_type) < 0) {
- bad_usage(argv[0], 1);
- return -1;
- }
- break;
- case 'v':
- verbosity++;
- break;
- case 'q':
- if (--verbosity < 0)
- verbosity = 0;
- break;
- case 'u':
- vdr_dump_channum = 1;
- break;
- case 'P':
- no_ATSC_PSIP = 1;
- break;
- case 'A':
- ATSC_type = strtoul(optarg,NULL,0);
- if (ATSC_type == 0 || ATSC_type > 3) {
- bad_usage(argv[0], 1);
- return -1;
- }
-
- break;
- case 'U':
- unique_anon_services = 1;
- break;
- default:
- bad_usage(argv[0], 0);
- return -1;
- };
- }
-
- if (optind < argc)
- initial = argv[optind];
- if ((!initial && !current_tp_only) || (initial && current_tp_only) ||
- (spectral_inversion > 2)) {
- bad_usage(argv[0], 0);
- return -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 (switch_pos >= 4) {
- fprintf (stderr, "switch position needs to be < 4!\n");
- return -1;
- }
- if (initial)
- info("scanning %s\n", initial);
-
- snprintf (frontend_devname, sizeof(frontend_devname),
- "/dev/dvb/adapter%i/frontend%i", adapter, frontend);
-
- snprintf (demux_devname, sizeof(demux_devname),
- "/dev/dvb/adapter%i/demux%i", adapter, demux);
- info("using '%s' and '%s'\n", frontend_devname, demux_devname);
-
- for (i = 0; i < MAX_RUNNING; i++)
- poll_fds[i].fd = -1;
-
- fe_open_mode = current_tp_only ? O_RDONLY : O_RDWR;
- if ((frontend_fd = open (frontend_devname, fe_open_mode)) < 0)
- fatal("failed to open '%s': %d %m\n", frontend_devname, errno);
- /* determine FE type and caps */
- if (ioctl(frontend_fd, FE_GET_INFO, &fe_info) == -1)
- fatal("FE_GET_INFO failed: %d %m\n", errno);
-
- if ((spectral_inversion == INVERSION_AUTO ) &&
- !(fe_info.caps & FE_CAN_INVERSION_AUTO)) {
- info("Frontend can not do INVERSION_AUTO, trying INVERSION_OFF instead\n");
- spectral_inversion = INVERSION_OFF;
- }
-
- signal(SIGINT, handle_sigint);
-
- if (current_tp_only) {
- current_tp = alloc_transponder(0); /* dummy */
- /* move TP from "new" to "scanned" list */
- list_del_init(¤t_tp->list);
- list_add_tail(¤t_tp->list, &scanned_transponders);
- current_tp->scan_done = 1;
- scan_tp ();
- }
- else
- scan_network (frontend_fd, initial);
-
- close (frontend_fd);
-
- dump_lists ();
-
- return 0;
-}
-
-static void dump_dvb_parameters (FILE *f, struct transponder *t)
-{
- switch (output_format) {
- case OUTPUT_PIDS:
- case OUTPUT_VDR:
- vdr_dump_dvb_parameters(f, t->type, &t->param,
- sat_polarisation (t), t->orbital_pos, t->we_flag);
- break;
- case OUTPUT_ZAP:
- zap_dump_dvb_parameters (f, t->type, &t->param,
- sat_polarisation (t), sat_number (t));
- break;
- default:
- break;
- }
-}
diff -r c51253d44be0 util/scan/scan.h
--- a/util/scan/scan.h Thu Sep 7 12:03:08 2006
+++ /dev/null Thu Sep 7 14:03:14 2006
@@ -1,29 +0,0 @@
-#ifndef __SCAN_H__
-#define __SCAN_H__
-
-#include <stdio.h>
-#include <errno.h>
-
-extern int verbosity;
-
-#define dprintf(level, fmt...) \
- do { \
- if (level <= verbosity) \
- fprintf(stderr, fmt); \
- } while (0)
-
-#define dpprintf(level, fmt, args...) \
- dprintf(level, "%s:%d: " fmt, __FUNCTION__, __LINE__ , ##args)
-
-#define fatal(fmt, args...) do { dpprintf(-1, "FATAL: " fmt , ##args); exit(1); } while(0)
-#define error(msg...) dprintf(0, "ERROR: " msg)
-#define errorn(msg) dprintf(0, "%s:%d: ERROR: " msg ": %d %m\n", __FUNCTION__, __LINE__, errno)
-#define warning(msg...) dprintf(1, "WARNING: " msg)
-#define info(msg...) dprintf(2, msg)
-#define verbose(msg...) dprintf(3, msg)
-#define moreverbose(msg...) dprintf(4, msg)
-#define debug(msg...) dpprintf(5, msg)
-#define verbosedebug(msg...) dpprintf(6, msg)
-
-#endif
-
--
(o_ Ludwig Nussel
//\ SUSE LINUX Products GmbH, Development
V_/_ http://www.suse.de/
More information about the linux-dvb
mailing list