Change got lost during Makefile refactoring Signed-off-by: lnussel@xxxxxxx 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/ _______________________________________________ linux-dvb mailing list linux-dvb@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb