mirror of
https://github.com/tbsdtv/media_build.git
synced 2025-07-23 04:13:02 +02:00
1090 lines
26 KiB
C
1090 lines
26 KiB
C
/* tzap-t2 -- simple zapping tool for the Linux DVB S2 API
|
|
*
|
|
* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
|
|
* Copyright (C) 2013 CrazyCat (crazycat69@narod.ru)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/param.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <stdint.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <linux/dvb/frontend.h>
|
|
#include <linux/dvb/dmx.h>
|
|
#include <linux/dvb/audio.h>
|
|
#include <linux/dvb/version.h>
|
|
#include "lnb.h"
|
|
|
|
#if DVB_API_VERSION < 5 || DVB_API_VERSION_MINOR < 2
|
|
#error szap-s2 requires Linux DVB driver API version 5.2 and newer!
|
|
#endif
|
|
|
|
#ifndef DTV_STREAM_ID
|
|
#define DTV_STREAM_ID 42
|
|
#endif
|
|
|
|
#ifndef NO_STREAM_ID_FILTER
|
|
#define NO_STREAM_ID_FILTER (~0U)
|
|
#endif
|
|
|
|
#ifndef TRUE
|
|
#define TRUE (1==1)
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE (1==0)
|
|
#endif
|
|
|
|
/* location of channel list file */
|
|
#define CHANNEL_FILE "channels.conf"
|
|
|
|
/* one line of the szap channel file has the following format:
|
|
* ^name:frequency_KHz:inversion:bandwidth:coderate_hp:coderate_lp:modulation:transmission_mode:guard_interval:hierarchy:vpid:apid:service_id$
|
|
* one line of the VDR channel file has the following format:
|
|
* ^name:frequency_KHz:[bandwidth][delivery][modulation][coderate_hp][coderate_lp][transmission_mode][guard_interval][hierarchy]]plp_id]:vpid:apid:?:?:service_id:?:?:?$
|
|
*/
|
|
|
|
|
|
#define FRONTENDDEVICE "/dev/dvb/adapter%d/frontend%d"
|
|
#define DEMUXDEVICE "/dev/dvb/adapter%d/demux%d"
|
|
#define AUDIODEVICE "/dev/dvb/adapter%d/audio%d"
|
|
|
|
struct t_channel_parameter_map {
|
|
int user_value;
|
|
int driver_value;
|
|
const char *user_string;
|
|
};
|
|
/* --- Channel Parameter Maps From VDR---*/
|
|
|
|
static struct t_channel_parameter_map inversion_values[] = {
|
|
{ 0, INVERSION_OFF, "off" },
|
|
{ 1, INVERSION_ON, "on" },
|
|
{ 999, INVERSION_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map bw_values [] = {
|
|
{ 6, BANDWIDTH_6_MHZ, "6MHz" },
|
|
{ 7, BANDWIDTH_7_MHZ, "7MHz" },
|
|
{ 8, BANDWIDTH_8_MHZ, "8MHz" },
|
|
{ 5, BANDWIDTH_6_MHZ, "5MHz" },
|
|
{ 10, BANDWIDTH_10_MHZ, "10MHz" },
|
|
{ 999, BANDWIDTH_AUTO, "auto" },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map coderate_values[] = {
|
|
{ 0, FEC_NONE, "none" },
|
|
{ 12, FEC_1_2, "1/2" },
|
|
{ 23, FEC_2_3, "2/3" },
|
|
{ 34, FEC_3_4, "3/4" },
|
|
{ 35, FEC_3_5, "3/5" },
|
|
{ 45, FEC_4_5, "4/5" },
|
|
{ 56, FEC_5_6, "5/6" },
|
|
{ 67, FEC_6_7, "6/7" },
|
|
{ 78, FEC_7_8, "7/8" },
|
|
{ 89, FEC_8_9, "8/9" },
|
|
{ 910, FEC_9_10, "9/10" },
|
|
{ 999, FEC_AUTO, "auto" },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map modulation_values[] = {
|
|
// { 0, NONE, "none" },
|
|
{ 2, QPSK, "QPSK" },
|
|
{ 10, VSB_8, "VSB8" },
|
|
{ 11, VSB_16, "VSB16" },
|
|
{ 16, QAM_16, "QAM16" },
|
|
{ 64, QAM_64, "QAM64" },
|
|
{ 128, QAM_128, "QAM128" },
|
|
{ 256, QAM_256, "QAM256" },
|
|
{ 999, QAM_16 },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map mode_values [] = {
|
|
{ 2, TRANSMISSION_MODE_2K, "2k" },
|
|
{ 8, TRANSMISSION_MODE_2K, "8k" },
|
|
{ 999, TRANSMISSION_MODE_AUTO, "auto" },
|
|
{ 4, TRANSMISSION_MODE_4K, "4k" },
|
|
{ 1, TRANSMISSION_MODE_1K, "1k" },
|
|
{ 16, TRANSMISSION_MODE_16K, "16k" },
|
|
{ 32, TRANSMISSION_MODE_32K, "32k" },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map guard_values [] = {
|
|
{ 32, GUARD_INTERVAL_1_32, "1/32" },
|
|
{ 16, GUARD_INTERVAL_1_16, "1/16" },
|
|
{ 8, GUARD_INTERVAL_1_8, "1/8" },
|
|
{ 4, GUARD_INTERVAL_1_4, "1/4" },
|
|
{ 999, GUARD_INTERVAL_AUTO, "auto" },
|
|
{ 1128, GUARD_INTERVAL_1_128, "1/128" },
|
|
{ 19128, GUARD_INTERVAL_19_128, "19/128" },
|
|
{ 19256, GUARD_INTERVAL_19_256, "19/256" },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map hierarchy_values [] = {
|
|
{ 0, HIERARCHY_NONE, "none" },
|
|
{ 1, HIERARCHY_NONE, "1" },
|
|
{ 2, HIERARCHY_NONE, "2" },
|
|
{ 4, HIERARCHY_NONE, "4" },
|
|
{ 999, HIERARCHY_NONE, "auto" },
|
|
{ -1 }
|
|
};
|
|
|
|
static struct t_channel_parameter_map system_values[] = {
|
|
{ 0, SYS_DVBT, "DVB-T" },
|
|
{ 1, SYS_DVBT2, "DVB-T2" },
|
|
{ -1 }
|
|
};
|
|
|
|
static int user_index(int value, const struct t_channel_parameter_map * map)
|
|
{
|
|
const struct t_channel_parameter_map *umap = map;
|
|
while (umap && umap->user_value != -1) {
|
|
if (umap->user_value == value)
|
|
return umap - map;
|
|
umap++;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
static int driver_index(int value, const struct t_channel_parameter_map *map)
|
|
{
|
|
const struct t_channel_parameter_map *umap = map;
|
|
while (umap && umap->user_value != -1) {
|
|
if (umap->driver_value == value)
|
|
return umap - map;
|
|
umap++;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
static int map_to_user(int value, const struct t_channel_parameter_map *map, char **string)
|
|
{
|
|
int n = driver_index(value, map);
|
|
if (n >= 0) {
|
|
if (string)
|
|
*string = (char *)map[n].user_string;
|
|
return map[n].user_value;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int map_to_driver(int value, const struct t_channel_parameter_map *map)
|
|
{
|
|
int n = user_index(value, map);
|
|
if (n >= 0)
|
|
return map[n].driver_value;
|
|
return -1;
|
|
}
|
|
|
|
static struct lnb_types_st lnb_type;
|
|
|
|
static int exit_after_tuning;
|
|
static int interactive;
|
|
|
|
static char *usage_str =
|
|
"\nusage: tzap-t2 -q\n"
|
|
" list known channels\n"
|
|
" tzap-t2 [options] {-n channel-number|channel_name}\n"
|
|
" zap to channel via number or full name (case insensitive)\n"
|
|
" -a number : use given adapter (default 0)\n"
|
|
" -f number : use given frontend (default 0)\n"
|
|
" -d number : use given demux (default 0)\n"
|
|
" -c file : read channels list from 'file'\n"
|
|
" -V : use VDR channels list file format (default zap)\n"
|
|
" -b : enable Audio Bypass (default no)\n"
|
|
" -x : exit after tuning\n"
|
|
" -H : human readable output\n"
|
|
" -D : params debug\n"
|
|
" -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n"
|
|
" -i : run interactively, allowing you to type in channel names\n"
|
|
" -p : add pat and pmt to TS recording (implies -r)\n"
|
|
" or -n numbers for zapping\n"
|
|
" -t : add teletext to TS recording (needs -V)\n"
|
|
" -S : delivery system type DVB-T=0, DVB-T2=1\n"
|
|
" -M : modulation 2=QPSK 16=16QAM 64=64QAM 256=256QAM\n"
|
|
" -C : fec 0=NONE 12=1/2 23=2/3 34=3/4 35=3/5 45=4/5 56=5/6 67=6/7 89=8/9 910=9/10 999=AUTO\n"
|
|
" -m : physical layer pipe 0-255\n";
|
|
|
|
static int set_demux(int dmxfd, int pid, int pes_type, int dvr)
|
|
{
|
|
struct dmx_pes_filter_params pesfilter;
|
|
|
|
if (pid < 0 || pid >= 0x1fff) /* ignore this pid to allow radio services */
|
|
return TRUE;
|
|
|
|
if (dvr) {
|
|
int buffersize = 64 * 1024;
|
|
if (ioctl(dmxfd, DMX_SET_BUFFER_SIZE, buffersize) == -1)
|
|
perror("DMX_SET_BUFFER_SIZE failed");
|
|
}
|
|
|
|
pesfilter.pid = pid;
|
|
pesfilter.input = DMX_IN_FRONTEND;
|
|
pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER;
|
|
pesfilter.pes_type = pes_type;
|
|
pesfilter.flags = DMX_IMMEDIATE_START;
|
|
|
|
if (ioctl(dmxfd, DMX_SET_PES_FILTER, &pesfilter) == -1) {
|
|
fprintf(stderr, "DMX_SET_PES_FILTER failed "
|
|
"(PID = 0x%04x): %d %m\n", pid, errno);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int get_pmt_pid(char *dmxdev, int sid)
|
|
{
|
|
int patfd, count;
|
|
int pmt_pid = 0;
|
|
int patread = 0;
|
|
int section_length;
|
|
unsigned char buft[4096];
|
|
unsigned char *buf = buft;
|
|
struct dmx_sct_filter_params f;
|
|
|
|
memset(&f, 0, sizeof(f));
|
|
f.pid = 0;
|
|
f.filter.filter[0] = 0x00;
|
|
f.filter.mask[0] = 0xff;
|
|
f.timeout = 0;
|
|
f.flags = DMX_IMMEDIATE_START | DMX_CHECK_CRC;
|
|
|
|
if ((patfd = open(dmxdev, O_RDWR)) < 0) {
|
|
perror("openening pat demux failed");
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(patfd, DMX_SET_FILTER, &f) == -1) {
|
|
perror("ioctl DMX_SET_FILTER failed");
|
|
close(patfd);
|
|
return -1;
|
|
}
|
|
|
|
while (!patread) {
|
|
if (((count = read(patfd, buf, sizeof(buft))) < 0) && errno == EOVERFLOW)
|
|
count = read(patfd, buf, sizeof(buft));
|
|
if (count < 0) {
|
|
perror("read_sections: read error");
|
|
close(patfd);
|
|
return -1;
|
|
}
|
|
|
|
section_length = ((buf[1] & 0x0f) << 8) | buf[2];
|
|
if (count != section_length + 3)
|
|
continue;
|
|
|
|
buf += 8;
|
|
section_length -= 8;
|
|
|
|
patread = 1; /* assumes one section contains the whole pat */
|
|
while (section_length > 0) {
|
|
int service_id = (buf[0] << 8) | buf[1];
|
|
if (service_id == sid) {
|
|
pmt_pid = ((buf[2] & 0x1f) << 8) | buf[3];
|
|
section_length = 0;
|
|
}
|
|
buf += 4;
|
|
section_length -= 4;
|
|
}
|
|
}
|
|
close(patfd);
|
|
return pmt_pid;
|
|
}
|
|
|
|
static int do_tune(int fefd, unsigned int freq, unsigned int bw, enum fe_delivery_system delsys,
|
|
int modulation, int fec_hp, int fec_lp, int mode, int guard, int hierarchy, int stream_id)
|
|
{
|
|
struct dvb_frontend_event ev;
|
|
struct dtv_property p[] = {
|
|
{ .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
|
|
{ .cmd = DTV_FREQUENCY, .u.data = freq },
|
|
{ .cmd = DTV_BANDWIDTH_HZ, .u.data = bw },
|
|
{ .cmd = DTV_MODULATION, .u.data = modulation },
|
|
{ .cmd = DTV_CODE_RATE_HP, .u.data = fec_hp },
|
|
{ .cmd = DTV_CODE_RATE_LP, .u.data = fec_lp },
|
|
{ .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO },
|
|
{ .cmd = DTV_TRANSMISSION_MODE, .u.data = mode },
|
|
{ .cmd = DTV_GUARD_INTERVAL, .u.data = guard },
|
|
{ .cmd = DTV_HIERARCHY, .u.data = hierarchy },
|
|
{ .cmd = DTV_STREAM_ID, .u.data = stream_id },
|
|
{ .cmd = DTV_TUNE },
|
|
};
|
|
struct dtv_properties cmdseq = {
|
|
.num = sizeof(p)/sizeof(p[0]),
|
|
.props = p
|
|
};
|
|
|
|
/* discard stale QPSK events */
|
|
while (1) {
|
|
if (ioctl(fefd, FE_GET_EVENT, &ev) == -1)
|
|
break;
|
|
}
|
|
|
|
if ((delsys != SYS_DVBT) && (delsys != SYS_DVBT2))
|
|
return -EINVAL;
|
|
|
|
if ((ioctl(fefd, FE_SET_PROPERTY, &cmdseq)) == -1) {
|
|
perror("FE_SET_PROPERTY failed");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static
|
|
int check_frontend (int fe_fd, int dvr, int human_readable, int params_debug)
|
|
{
|
|
(void)dvr;
|
|
fe_status_t status;
|
|
uint16_t snr, signal;
|
|
uint32_t ber, uncorrected_blocks;
|
|
int timeout = 0;
|
|
char *field;
|
|
struct dtv_property p[] = {
|
|
{ .cmd = DTV_FREQUENCY },
|
|
{ .cmd = DTV_BANDWIDTH_HZ },
|
|
{ .cmd = DTV_DELIVERY_SYSTEM },
|
|
{ .cmd = DTV_MODULATION },
|
|
{ .cmd = DTV_CODE_RATE_HP },
|
|
{ .cmd = DTV_CODE_RATE_LP },
|
|
{ .cmd = DTV_TRANSMISSION_MODE },
|
|
{ .cmd = DTV_GUARD_INTERVAL },
|
|
{ .cmd = DTV_HIERARCHY },
|
|
};
|
|
struct dtv_properties cmdseq = {
|
|
.num = sizeof(p)/sizeof(p[0]),
|
|
.props = p
|
|
};
|
|
|
|
do {
|
|
if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1)
|
|
perror("FE_READ_STATUS failed");
|
|
/* some frontends might not support all these ioctls, thus we
|
|
* avoid printing errors
|
|
*/
|
|
if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1)
|
|
signal = -2;
|
|
if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1)
|
|
snr = -2;
|
|
if (ioctl(fe_fd, FE_READ_BER, &ber) == -1)
|
|
ber = -2;
|
|
if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1)
|
|
uncorrected_blocks = -2;
|
|
|
|
if (human_readable) {
|
|
printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ",
|
|
status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks);
|
|
} else {
|
|
printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ",
|
|
status, signal, snr, ber, uncorrected_blocks);
|
|
}
|
|
if (status & FE_HAS_LOCK)
|
|
printf("FE_HAS_LOCK");
|
|
printf("\n");
|
|
|
|
if (exit_after_tuning && ((status & FE_HAS_LOCK) || (++timeout >= 10)))
|
|
break;
|
|
|
|
usleep(1000000);
|
|
} while (1);
|
|
|
|
if ((status & FE_HAS_LOCK) == 0)
|
|
return 0;
|
|
|
|
if ((ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq)) == -1) {
|
|
perror("FE_GET_PROPERTY failed");
|
|
return 0;
|
|
}
|
|
/* printout found parameters here */
|
|
if (params_debug){
|
|
printf("frequency %d, ", p[0].u.data);
|
|
printf("bandwidth %d, ", p[1].u.data);
|
|
printf("delivery %d, ", p[2].u.data);
|
|
printf("modulation %d, ", p[3].u.data);
|
|
printf("coderate hp %d, ", p[4].u.data);
|
|
printf("coderate lp %d, ", p[5].u.data);
|
|
printf("mode %d, ", p[6].u.data);
|
|
printf("guard %d, ", p[7].u.data);
|
|
printf("hierarchy %d", p[8].u.data);
|
|
} else {
|
|
printf("frequency %d, ",p[0].u.data);
|
|
field = NULL;
|
|
map_to_user(p[1].u.data, bw_values, &field);
|
|
printf("bandwidth %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[2].u.data, system_values, &field);
|
|
printf("delivery %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[3].u.data, modulation_values, &field);
|
|
printf("modulation %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[4].u.data, coderate_values, &field);
|
|
printf("coderate-hp %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[5].u.data, coderate_values, &field);
|
|
printf("coderate-lp %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[6].u.data, mode_values, &field);
|
|
printf("mode %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[7].u.data, guard_values, &field);
|
|
printf("guard %s, ", field);
|
|
field = NULL;
|
|
map_to_user(p[8].u.data, hierarchy_values, &field);
|
|
printf("hierarchy %s", field);
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int zap_to(unsigned int adapter, unsigned int frontend, unsigned int demux,
|
|
unsigned int freq, unsigned int bw,
|
|
unsigned int vpid, unsigned int apid,
|
|
unsigned int tpid, int sid,
|
|
int dvr, int rec_psi, int bypass, unsigned int delivery,
|
|
int modulation, int fec_hp, int fec_lp, int mode, int guard, int hierarchy, int stream_id, int human_readable,
|
|
int params_debug)
|
|
{
|
|
struct dtv_property p[] = {
|
|
{ .cmd = DTV_CLEAR },
|
|
};
|
|
|
|
struct dtv_properties cmdseq = {
|
|
.num = sizeof(p)/sizeof(p[0]),
|
|
.props = p
|
|
};
|
|
|
|
char fedev[128], dmxdev[128], auddev[128];
|
|
static int fefd, dmxfda, dmxfdv, dmxfdt = -1, audiofd = -1, patfd, pmtfd;
|
|
int pmtpid;
|
|
int result;
|
|
|
|
if (!fefd) {
|
|
snprintf(fedev, sizeof(fedev), FRONTENDDEVICE, adapter, frontend);
|
|
snprintf(dmxdev, sizeof(dmxdev), DEMUXDEVICE, adapter, demux);
|
|
snprintf(auddev, sizeof(auddev), AUDIODEVICE, adapter, demux);
|
|
printf("using '%s' and '%s'\n", fedev, dmxdev);
|
|
|
|
if ((fefd = open(fedev, O_RDWR | O_NONBLOCK)) < 0) {
|
|
perror("opening frontend failed");
|
|
return FALSE;
|
|
}
|
|
|
|
if ((dmxfdv = open(dmxdev, O_RDWR)) < 0) {
|
|
perror("opening video demux failed");
|
|
close(fefd);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((dmxfda = open(dmxdev, O_RDWR)) < 0) {
|
|
perror("opening audio demux failed");
|
|
close(fefd);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((dmxfdt = open(dmxdev, O_RDWR)) < 0) {
|
|
perror("opening teletext demux failed");
|
|
close(fefd);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dvr == 0) /* DMX_OUT_DECODER */
|
|
audiofd = open(auddev, O_RDWR);
|
|
|
|
if (rec_psi){
|
|
if ((patfd = open(dmxdev, O_RDWR)) < 0) {
|
|
perror("opening pat demux failed");
|
|
close(audiofd);
|
|
close(dmxfda);
|
|
close(dmxfdv);
|
|
close(dmxfdt);
|
|
close(fefd);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((pmtfd = open(dmxdev, O_RDWR)) < 0) {
|
|
perror("opening pmt demux failed");
|
|
close(patfd);
|
|
close(audiofd);
|
|
close(dmxfda);
|
|
close(dmxfdv);
|
|
close(dmxfdt);
|
|
close(fefd);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
result = FALSE;
|
|
|
|
if ((ioctl(fefd, FE_SET_PROPERTY, &cmdseq)) == -1) {
|
|
perror("FE_SET_PROPERTY DTV_CLEAR failed");
|
|
return FALSE;
|
|
}
|
|
|
|
switch(bw)
|
|
{
|
|
case BANDWIDTH_5_MHZ: bw = 5000000; break;
|
|
case BANDWIDTH_6_MHZ: bw = 6000000; break;
|
|
case BANDWIDTH_7_MHZ: bw = 7000000; break;
|
|
case BANDWIDTH_8_MHZ: bw = 8000000; break;
|
|
case BANDWIDTH_10_MHZ: bw = 10000000; break;
|
|
case BANDWIDTH_AUTO:
|
|
default: bw = 0;
|
|
}
|
|
|
|
if (do_tune(fefd, freq, bw, delivery, modulation, fec_hp, fec_lp, mode, guard, hierarchy, stream_id))
|
|
if (set_demux(dmxfdv, vpid, DMX_PES_VIDEO, dvr))
|
|
if (audiofd >= 0)
|
|
(void)ioctl(audiofd, AUDIO_SET_BYPASS_MODE, bypass);
|
|
if (set_demux(dmxfda, apid, DMX_PES_AUDIO, dvr)) {
|
|
if (rec_psi) {
|
|
pmtpid = get_pmt_pid(dmxdev, sid);
|
|
if (pmtpid < 0) {
|
|
result = FALSE;
|
|
}
|
|
if (pmtpid == 0) {
|
|
fprintf(stderr,"couldn't find pmt-pid for sid %04x\n",sid);
|
|
result = FALSE;
|
|
}
|
|
if (set_demux(patfd, 0, DMX_PES_OTHER, dvr))
|
|
if (set_demux(pmtfd, pmtpid, DMX_PES_OTHER, dvr))
|
|
result = TRUE;
|
|
} else {
|
|
result = TRUE;
|
|
}
|
|
}
|
|
|
|
if (tpid != -1 && !set_demux(dmxfdt, tpid, DMX_PES_TELETEXT, dvr)) {
|
|
fprintf(stderr, "set_demux DMX_PES_TELETEXT failed\n");
|
|
}
|
|
|
|
check_frontend (fefd, dvr, human_readable, params_debug);
|
|
|
|
if (!interactive) {
|
|
close(patfd);
|
|
close(pmtfd);
|
|
if (audiofd >= 0)
|
|
close(audiofd);
|
|
close(dmxfda);
|
|
close(dmxfdv);
|
|
close(dmxfdt);
|
|
close(fefd);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
static char *parse_parameter(const char *s, int *value, const struct t_channel_parameter_map *map)
|
|
{
|
|
if (*++s) {
|
|
char *p = NULL;
|
|
errno = 0;
|
|
int n = strtol(s, &p, 10);
|
|
if (!errno && p != s) {
|
|
value[0] = map_to_driver(n, map);
|
|
if (value[0] >= 0)
|
|
return p;
|
|
}
|
|
}
|
|
fprintf(stderr, "ERROR: invalid value for parameter '%C'\n", *(s - 1));
|
|
return NULL;
|
|
}
|
|
|
|
static int read_channels(const char *filename, int list_channels,
|
|
uint32_t chan_no, const char *chan_name,
|
|
unsigned int adapter, unsigned int frontend,
|
|
unsigned int demux, int dvr, int rec_psi,
|
|
int bypass, unsigned int delsys,
|
|
int modulation, int fec_hp, int fec_lp, int mode, int guard, int hierarchy, int stream_id, int human_readable,
|
|
int params_debug, int use_vdr_format, int use_tpid)
|
|
{
|
|
FILE *cfp;
|
|
char buf[4096];
|
|
char inp[256];
|
|
char *field, *tmp, *p;
|
|
unsigned int line;
|
|
unsigned int freq = 0, bw = 0, vpid, apid, tpid, sid;
|
|
int ret;
|
|
int trash;
|
|
again:
|
|
line = 0;
|
|
if (!(cfp = fopen(filename, "r"))) {
|
|
fprintf(stderr, "error opening channel list '%s': %d %m\n",
|
|
filename, errno);
|
|
return FALSE;
|
|
}
|
|
|
|
if (interactive) {
|
|
fprintf(stderr, "\n>>> ");
|
|
if (!fgets(inp, sizeof(inp), stdin)) {
|
|
printf("\n");
|
|
return -1;
|
|
}
|
|
if (inp[0] == '-' && inp[1] == 'n') {
|
|
chan_no = strtoul(inp+2, NULL, 0);
|
|
chan_name = NULL;
|
|
if (!chan_no) {
|
|
fprintf(stderr, "bad channel number\n");
|
|
goto again;
|
|
}
|
|
} else {
|
|
p = strchr(inp, '\n');
|
|
if (p)
|
|
*p = '\0';
|
|
chan_name = inp;
|
|
chan_no = 0;
|
|
}
|
|
}
|
|
|
|
while (!feof(cfp)) {
|
|
if (fgets(buf, sizeof(buf), cfp)) {
|
|
line++;
|
|
|
|
if (chan_no && chan_no != line)
|
|
continue;
|
|
|
|
tmp = buf;
|
|
field = strsep(&tmp, ":");
|
|
|
|
if (!field)
|
|
goto syntax_err;
|
|
|
|
if (list_channels) {
|
|
printf("%03u %s\n", line, field);
|
|
continue;
|
|
}
|
|
|
|
if (chan_name && strcasecmp(chan_name, field) != 0)
|
|
continue;
|
|
|
|
printf("zapping to %d '%s':\n", line, field);
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
freq = strtoul(field, NULL, 0);
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
while (field && *field) {
|
|
switch (toupper(*field)) {
|
|
case 'B':
|
|
field = parse_parameter(field, &bw, bw_values);
|
|
break;
|
|
case 'M':
|
|
if (modulation == -1)
|
|
field = parse_parameter(field, &modulation, modulation_values);
|
|
else
|
|
field = parse_parameter(field, &trash, modulation_values);
|
|
break;
|
|
case 'I':/* ignore */
|
|
field = parse_parameter(field, &ret, inversion_values);
|
|
break;
|
|
case 'C':
|
|
if (fec_hp == -1)
|
|
field = parse_parameter(field, &fec_hp, coderate_values);
|
|
else
|
|
field = parse_parameter(field, &trash, coderate_values);
|
|
break;
|
|
case 'D':
|
|
if (fec_lp == -1)
|
|
field = parse_parameter(field, &fec_lp, coderate_values);
|
|
else
|
|
field = parse_parameter(field, &trash, coderate_values);
|
|
break;
|
|
case 'T':
|
|
if (mode == -1)
|
|
field = parse_parameter(field, &mode, mode_values);
|
|
else
|
|
field = parse_parameter(field, &trash, mode_values);
|
|
break;
|
|
case 'G':
|
|
if (guard == -1)
|
|
field = parse_parameter(field, &guard, guard_values);
|
|
else
|
|
field = parse_parameter(field, &trash, guard_values);
|
|
break;
|
|
case 'Y':
|
|
if (hierarchy == -1)
|
|
field = parse_parameter(field, &hierarchy, hierarchy_values);
|
|
else
|
|
field = parse_parameter(field, &trash, hierarchy_values);
|
|
break;
|
|
case 'P':
|
|
stream_id = strtol(++field, &field, 10);
|
|
break;
|
|
case 'S':
|
|
if (delsys == -1)
|
|
field = parse_parameter(field, &delsys, system_values);
|
|
else
|
|
field = parse_parameter(field, &trash, system_values);
|
|
break;
|
|
default:
|
|
goto syntax_err;
|
|
}
|
|
}
|
|
/* default values for empty parameters */
|
|
if (modulation == -1)
|
|
modulation = QAM_16;
|
|
|
|
if (delsys == -1)
|
|
delsys = SYS_DVBT;
|
|
|
|
if (fec_hp == -1)
|
|
fec_hp = FEC_AUTO;
|
|
|
|
if (fec_lp == -1)
|
|
fec_lp = FEC_AUTO;
|
|
|
|
if (mode == -1)
|
|
mode = TRANSMISSION_MODE_AUTO;
|
|
|
|
if (guard == -1)
|
|
guard = GUARD_INTERVAL_AUTO;
|
|
|
|
if (hierarchy == -1)
|
|
hierarchy = HIERARCHY_NONE;
|
|
|
|
if (stream_id<0 || stream_id>255)
|
|
stream_id = NO_STREAM_ID_FILTER;
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
vpid = strtoul(field, NULL, 0);
|
|
if (!vpid)
|
|
vpid = 0x1fff;
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
p = strchr(field, ';');
|
|
|
|
if (p) {
|
|
*p = '\0';
|
|
p++;
|
|
if (bypass) {
|
|
if (!p || !*p)
|
|
goto syntax_err;
|
|
field = p;
|
|
}
|
|
}
|
|
|
|
apid = strtoul(field, NULL, 0);
|
|
if (!apid)
|
|
apid = 0x1fff;
|
|
|
|
tpid = -1;
|
|
if (use_vdr_format) {
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
if (use_tpid)
|
|
tpid = strtoul(field, NULL, 0);
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
strtoul(field, NULL, 0);
|
|
}
|
|
|
|
if (!(field = strsep(&tmp, ":")))
|
|
goto syntax_err;
|
|
|
|
sid = strtoul(field, NULL, 0);
|
|
|
|
fclose(cfp);
|
|
|
|
field = NULL;
|
|
map_to_user(bw, bw_values, &field);
|
|
printf("frequency %u KHz, bandwidth %s, ",
|
|
freq, field);
|
|
|
|
if (params_debug){
|
|
printf("delivery %d, ", delsys);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(delsys, system_values, &field);
|
|
printf("delivery %s, ", field);
|
|
}
|
|
|
|
if (params_debug){
|
|
printf("modulation %d\n", modulation);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(modulation, modulation_values, &field);
|
|
printf("modulation %s\n", field);
|
|
}
|
|
|
|
if (params_debug){
|
|
printf("coderate-hp %d, ", fec_hp);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(fec_hp, coderate_values, &field);
|
|
printf("coderate-hp %s, ", field);
|
|
}
|
|
|
|
if (params_debug){
|
|
printf("coderate-lp %d, ", fec_hp);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(fec_lp, coderate_values, &field);
|
|
printf("coderate-lp %s, ", field);
|
|
}
|
|
|
|
if (params_debug){
|
|
printf("mode %d, ", mode);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(mode, mode_values, &field);
|
|
printf("mode %s, ", field);
|
|
}
|
|
|
|
if (params_debug){
|
|
printf("guard %d, ", mode);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(guard, guard_values, &field);
|
|
printf("guard %s, ", field);
|
|
}
|
|
|
|
if (params_debug){
|
|
printf("hierarchy %d, ", mode);
|
|
} else {
|
|
field = NULL;
|
|
map_to_user(hierarchy, hierarchy_values, &field);
|
|
printf("hierarchy %s, ", field);
|
|
}
|
|
|
|
printf("plp_id %d\n", stream_id);
|
|
|
|
printf("vpid 0x%04x, apid 0x%04x, sid 0x%04x\n", vpid, apid, sid);
|
|
|
|
ret = zap_to(adapter, frontend, demux, freq * 1000, bw,
|
|
vpid, apid, tpid, sid, dvr, rec_psi, bypass,
|
|
delsys, modulation, fec_hp, fec_lp, mode, guard, hierarchy, stream_id, human_readable,
|
|
params_debug);
|
|
|
|
if (interactive)
|
|
goto again;
|
|
|
|
if (ret)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
syntax_err:
|
|
fprintf(stderr, "syntax error in line %u: '%s'\n", line, buf);
|
|
} else if (ferror(cfp)) {
|
|
fprintf(stderr, "error reading channel list '%s': %d %m\n",
|
|
filename, errno);
|
|
fclose(cfp);
|
|
return FALSE;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
fclose(cfp);
|
|
|
|
if (!list_channels) {
|
|
fprintf(stderr, "channel not found\n");
|
|
|
|
if (!interactive)
|
|
return FALSE;
|
|
}
|
|
if (interactive)
|
|
goto again;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void handle_sigint(int sig)
|
|
{
|
|
fprintf(stderr, "Interrupted by SIGINT!\n");
|
|
exit(2);
|
|
}
|
|
|
|
void
|
|
bad_usage(char *pname)
|
|
{
|
|
fprintf (stderr, usage_str, pname);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const char *home;
|
|
char chanfile[2 * PATH_MAX];
|
|
int list_channels = 0;
|
|
unsigned int chan_no = 0;
|
|
const char *chan_name = NULL;
|
|
unsigned int adapter = 0, frontend = 0, demux = 0, dvr = 0, rec_psi = 0;
|
|
int bypass = 0;
|
|
int opt, copt = 0;
|
|
int human_readable = 0;
|
|
int params_debug = 0;
|
|
int use_vdr_format = 0;
|
|
int use_tpid = 0;
|
|
|
|
|
|
int delsys = -1;
|
|
int modulation = -1;
|
|
int fec = -1;
|
|
int stream_id = NO_STREAM_ID_FILTER;
|
|
|
|
while ((opt = getopt(argc, argv, "M:m:C:O:HDVhqrpn:a:f:d:S:c:l:xib")) != -1) {
|
|
switch (opt) {
|
|
case '?':
|
|
case 'h':
|
|
default:
|
|
bad_usage(argv[0]);
|
|
break;
|
|
case 'C':
|
|
parse_parameter(--optarg, &fec, coderate_values);
|
|
break;
|
|
case 'M':
|
|
parse_parameter(--optarg, &modulation, modulation_values);
|
|
break;
|
|
case 'm':
|
|
stream_id = strtol(optarg, NULL, 0);
|
|
break;
|
|
case 'S':
|
|
parse_parameter(--optarg, &delsys, system_values);
|
|
break;
|
|
case 'b':
|
|
bypass = 1;
|
|
break;
|
|
case 'q':
|
|
list_channels = 1;
|
|
break;
|
|
case 'r':
|
|
dvr = 1;
|
|
break;
|
|
case 'n':
|
|
chan_no = strtoul(optarg, NULL, 0);
|
|
break;
|
|
case 'a':
|
|
adapter = strtoul(optarg, NULL, 0);
|
|
break;
|
|
case 'f':
|
|
frontend = strtoul(optarg, NULL, 0);
|
|
break;
|
|
case 'p':
|
|
rec_psi = 1;
|
|
break;
|
|
case 'd':
|
|
demux = strtoul(optarg, NULL, 0);
|
|
break;
|
|
case 'c':
|
|
copt = 1;
|
|
strncpy(chanfile, optarg, sizeof(chanfile));
|
|
break;
|
|
case 'x':
|
|
exit_after_tuning = 1;
|
|
break;
|
|
case 'H':
|
|
human_readable = 1;
|
|
break;
|
|
case 'D':
|
|
params_debug = 1;
|
|
break;
|
|
case 'V':
|
|
use_vdr_format = 1;
|
|
break;
|
|
case 't':
|
|
use_tpid = 1;
|
|
break;
|
|
case 'i':
|
|
interactive = 1;
|
|
exit_after_tuning = 1;
|
|
}
|
|
}
|
|
if (optind < argc)
|
|
chan_name = argv[optind];
|
|
if (chan_name && chan_no) {
|
|
bad_usage(argv[0]);
|
|
return -1;
|
|
}
|
|
if (list_channels && (chan_name || chan_no)) {
|
|
bad_usage(argv[0]);
|
|
return -1;
|
|
}
|
|
if (!list_channels && !chan_name && !chan_no && !interactive) {
|
|
bad_usage(argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
if (!copt) {
|
|
if (!(home = getenv("HOME"))) {
|
|
fprintf(stderr, "error: $HOME not set\n");
|
|
return TRUE;
|
|
}
|
|
snprintf(chanfile, sizeof(chanfile),
|
|
"%s/.tzap/%i/%s", home, adapter, CHANNEL_FILE);
|
|
if (access(chanfile, R_OK))
|
|
snprintf(chanfile, sizeof(chanfile),
|
|
"%s/.tzap/%s", home, CHANNEL_FILE);
|
|
}
|
|
|
|
printf("reading channels from file '%s'\n", chanfile);
|
|
|
|
if (rec_psi)
|
|
dvr=1;
|
|
|
|
signal(SIGINT, handle_sigint);
|
|
|
|
if (!read_channels(chanfile, list_channels, chan_no, chan_name,
|
|
adapter, frontend, demux, dvr, rec_psi, bypass, delsys,
|
|
modulation, fec, -1, -1, -1, -1, stream_id, human_readable, params_debug,
|
|
use_vdr_format, use_tpid))
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|