I don't call myself a programmer (I've never seen any C guide), but
somehow I figured out how to add an extra switch to tzap to make it
print the status in (human-readable) decimal instead of hex. It is
attached. It would be really nice if this would make it into the
dvb-apps on linuxtv..
Talking about that, could anybody tell me the minimal and maximal and/or
possible values for status, signal, snr, ber and uncorrected? If I would
know them I could try to make the numbers more human-readable (eg signal
ranging from 0 to 99 or so).
/* tzap -- DVB-T zapping utility
*/
/*
* Added recording to a file
* arguments:
*
* -t timeout (seconds)
* -o filename output filename (use -o - for stdout)
* -s only print summary
* -S run silently (no output)
*
* Bernard Hatt 24/2/04
*/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE 1
#define _LARGEFILE64_SOURCE 1
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
static char FRONTEND_DEV [80];
static char DEMUX_DEV [80];
static char DVR_DEV [80];
static int timeout_flag=0;
static int silent=0,timeout=0;
static int decread=0;
static int exit_after_tuning;
#define CHANNEL_FILE "channels.conf"
#define ERROR(x...) \
do { \
fprintf(stderr, "ERROR: "); \
fprintf(stderr, x); \
fprintf (stderr, "\n"); \
} while (0)
#define PERROR(x...) \
do { \
fprintf(stderr, "ERROR: "); \
fprintf(stderr, x); \
fprintf (stderr, " (%s)\n", strerror(errno)); \
} while (0)
typedef struct {
char *name;
int value;
} Param;
static const Param inversion_list [] = {
{ "INVERSION_OFF", INVERSION_OFF },
{ "INVERSION_ON", INVERSION_ON },
{ "INVERSION_AUTO", INVERSION_AUTO }
};
static const Param bw_list [] = {
{ "BANDWIDTH_6_MHZ", BANDWIDTH_6_MHZ },
{ "BANDWIDTH_7_MHZ", BANDWIDTH_7_MHZ },
{ "BANDWIDTH_8_MHZ", BANDWIDTH_8_MHZ }
};
static const Param fec_list [] = {
{ "FEC_1_2", FEC_1_2 },
{ "FEC_2_3", FEC_2_3 },
{ "FEC_3_4", FEC_3_4 },
{ "FEC_4_5", FEC_4_5 },
{ "FEC_5_6", FEC_5_6 },
{ "FEC_6_7", FEC_6_7 },
{ "FEC_7_8", FEC_7_8 },
{ "FEC_8_9", FEC_8_9 },
{ "FEC_AUTO", FEC_AUTO },
{ "FEC_NONE", FEC_NONE }
};
static const Param guard_list [] = {
{"GUARD_INTERVAL_1_16", GUARD_INTERVAL_1_16},
{"GUARD_INTERVAL_1_32", GUARD_INTERVAL_1_32},
{"GUARD_INTERVAL_1_4", GUARD_INTERVAL_1_4},
{"GUARD_INTERVAL_1_8", GUARD_INTERVAL_1_8},
{"GUARD_INTERVAL_AUTO", GUARD_INTERVAL_AUTO}
};
static const Param hierarchy_list [] = {
{ "HIERARCHY_1", HIERARCHY_1 },
{ "HIERARCHY_2", HIERARCHY_2 },
{ "HIERARCHY_4", HIERARCHY_4 },
{ "HIERARCHY_NONE", HIERARCHY_NONE },
{ "HIERARCHY_AUTO", HIERARCHY_AUTO }
};
static const Param constellation_list [] = {
{ "QPSK", QPSK },
{ "QAM_128", QAM_128 },
{ "QAM_16", QAM_16 },
{ "QAM_256", QAM_256 },
{ "QAM_32", QAM_32 },
{ "QAM_64", QAM_64 },
{ "QAM_AUTO", QAM_AUTO }
};
static const Param transmissionmode_list [] = {
{ "TRANSMISSION_MODE_2K", TRANSMISSION_MODE_2K },
{ "TRANSMISSION_MODE_8K", TRANSMISSION_MODE_8K },
{ "TRANSMISSION_MODE_AUTO", TRANSMISSION_MODE_AUTO }
};
#define LIST_SIZE(x) sizeof(x)/sizeof(Param)
static
int parse_param (int fd, const Param * plist, int list_size, int *param)
{
char c;
int character = 0;
int _index = 0;
while (1) {
if (read(fd, &c, 1) < 1)
return -1; /* EOF? */
if ((c == ':' || c == '\n')
&& plist->name[character] == '\0')
break;
while (toupper(c) != plist->name[character]) {
_index++;
plist++;
if (_index >= list_size) /* parse error, no valid */
return -2; /* parameter name found */
}
character++;
}
*param = plist->value;
return 0;
}
static
int parse_int(int fd, int *val)
{
char number[11]; /* 2^32 needs 10 digits... */
int character = 0;
while (1) {
if (read(fd, &number[character], 1) < 1)
return -1; /* EOF? */
if (number[character] == ':' || number[character] == '\n') {
number[character] = '\0';
break;
}
if (!isdigit(number[character]))
return -2; /* parse error, not a digit... */
character++;
if (character > 10) /* overflow, number too big */
return -3; /* to fit in 32 bit */
};
*val = strtol(number, NULL, 10);
return 0;
}
static
int find_channel(int fd, const char *channel)
{
int character = 0;
while (1) {
char c;
if (read(fd, &c, 1) < 1)
return -1; /* EOF! */
if ( '\n' == c ) /* start of line */
character = 0;
else if ( character >= 0 ) { /* we are in the namefield */
if (c == ':' && channel[character] == '\0')
break;
if (toupper(c) == toupper(channel[character]))
character++;
else
character = -1;
}
};
return 0;
}
static
int try_parse_int(int fd, int *val, const char *pname)
{
int err;
err = parse_int(fd, val);
if (err)
ERROR("error while parsing %s (%s)", pname,
err == -1 ? "end of file" :
err == -2 ? "not a number" : "number too big");
return err;
}
static
int try_parse_param(int fd, const Param * plist, int list_size, int *param,
const char *pname)
{
int err;
err = parse_param(fd, plist, list_size, param);
if (err)
ERROR("error while parsing %s (%s)", pname,
err == -1 ? "end of file" : "syntax error");
return err;
}
static int check_fec(fe_code_rate_t *fec)
{
switch (*fec)
{
case FEC_NONE:
*fec = FEC_AUTO;
case FEC_AUTO:
case FEC_1_2:
case FEC_2_3:
case FEC_3_4:
case FEC_5_6:
case FEC_7_8:
return 0;
default:
;
}
return 1;
}
int parse(const char *fname, const char *channel,
struct dvb_frontend_parameters *frontend, int *vpid, int *apid)
{
int fd;
int err;
int tmp;
if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
PERROR ("could not open file '%s'", fname);
perror ("");
return -1;
}
if (find_channel(fd, channel) < 0) {
ERROR("could not find channel '%s' in channel list", channel);
return -2;
}
if ((err = try_parse_int(fd, &tmp, "frequency")))
return -3;
frontend->frequency = tmp;
if ((err = try_parse_param(fd,
inversion_list, LIST_SIZE(inversion_list),
&tmp, "inversion")))
return -4;
frontend->inversion = tmp;
if ((err = try_parse_param(fd, bw_list, LIST_SIZE(bw_list),
&tmp, "bandwidth")))
return -5;
frontend->u.ofdm.bandwidth = tmp;
if ((err = try_parse_param(fd, fec_list, LIST_SIZE(fec_list),
&tmp, "code_rate_HP")))
return -6;
frontend->u.ofdm.code_rate_HP = tmp;
if (check_fec(&frontend->u.ofdm.code_rate_HP))
return -6;
if ((err = try_parse_param(fd, fec_list, LIST_SIZE(fec_list),
&tmp, "code_rate_LP")))
return -7;
frontend->u.ofdm.code_rate_LP = tmp;
if (check_fec(&frontend->u.ofdm.code_rate_LP))
return -7;
if ((err = try_parse_param(fd, constellation_list,
LIST_SIZE(constellation_list),
&tmp, "constellation")))
return -8;
frontend->u.ofdm.constellation = tmp;
if ((err = try_parse_param(fd, transmissionmode_list,
LIST_SIZE(transmissionmode_list),
&tmp, "transmission_mode")))
return -9;
frontend->u.ofdm.transmission_mode = tmp;
if ((err = try_parse_param(fd, guard_list, LIST_SIZE(guard_list),
&tmp, "guard_interval")))
return -10;
frontend->u.ofdm.guard_interval = tmp;
if ((err = try_parse_param(fd, hierarchy_list,
LIST_SIZE(hierarchy_list),
&tmp, "hierarchy_information")))
return -11;
frontend->u.ofdm.hierarchy_information = tmp;
if ((err = try_parse_int(fd, vpid, "Video PID")))
return -12;
if ((err = try_parse_int(fd, apid, "Audio PID")))
return -13;
close(fd);
return 0;
}
static
int set_pesfilter (int fd, int pid, dmx_pes_type_t type, int dvr)
{
struct dmx_pes_filter_params pesfilter;
if (pid <= 0 || pid >= 0x1fff)
return 0;
pesfilter.pid = pid;
pesfilter.input = DMX_IN_FRONTEND;
pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER;
pesfilter.pes_type = type;
pesfilter.flags = DMX_IMMEDIATE_START;
if (ioctl(fd, DMX_SET_PES_FILTER, &pesfilter) < 0) {
PERROR ("ioctl(DMX_SET_PES_FILTER) for %s PID failed",
type == DMX_PES_AUDIO ? "Audio" :
type == DMX_PES_VIDEO ? "Video" : "??");
return -1;
}
return 0;
}
static
int setup_frontend (int fe_fd, struct dvb_frontend_parameters *frontend)
{
struct dvb_frontend_info fe_info;
if (ioctl(fe_fd, FE_GET_INFO, &fe_info) < 0) {
PERROR("ioctl FE_GET_INFO failed");
return -1;
}
if (fe_info.type != FE_OFDM) {
ERROR ("frontend device is not a OFDM (DVB-T) device");
return -1;
}
if (silent<2)
fprintf (stderr,"tuning to %i Hz\n", frontend->frequency);
if (ioctl(fe_fd, FE_SET_FRONTEND, frontend) < 0) {
PERROR("ioctl FE_SET_FRONTEND failed");
return -1;
}
return 0;
}
static void
do_timeout(int x)
{
(void)x;
if (timeout_flag==0)
{
timeout_flag=1;
alarm(2);
signal(SIGALRM, do_timeout);
}
else
{
/* something has gone wrong ... exit */
exit(1);
}
}
static void
print_frontend_stats (int fe_fd)
{
fe_status_t status;
uint16_t snr, _signal;
uint32_t ber, uncorrected_blocks;
ioctl(fe_fd, FE_READ_STATUS, &status);
ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &_signal);
ioctl(fe_fd, FE_READ_SNR, &snr);
ioctl(fe_fd, FE_READ_BER, &ber);
ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks);
if(decread==1)
{
fprintf (stderr,"status %d | signal %d | snr %d | "
"ber %d | unc %d | ",
status, _signal, snr, ber, uncorrected_blocks);
}
else
{
fprintf (stderr,"status %02x | signal %04x | snr %04x | "
"ber %08x | unc %08x | ",
status, _signal, snr, ber, uncorrected_blocks);
}
if (status & FE_HAS_LOCK)
fprintf(stderr,"FE_HAS_LOCK");
fprintf(stderr,"\n");
}
static
int check_frontend (int fe_fd)
{
fe_status_t status;
do {
ioctl(fe_fd, FE_READ_STATUS, &status);
if (!silent)
print_frontend_stats(fe_fd);
if (exit_after_tuning && (status & FE_HAS_LOCK))
break;
usleep(1000000);
} while (!timeout_flag);
if (silent < 2)
print_frontend_stats (fe_fd);
return 0;
}
#define BUFLEN (188*256)
static
void copy_to_file(int in_fd, int out_fd)
{
char buf[BUFLEN];
int r;
long long int rc = 0LL;
while(timeout_flag==0)
{
r=read(in_fd,buf,BUFLEN);
if (r < 0) {
if (errno == EOVERFLOW) {
printf("buffer overrun\n");
continue;
}
PERROR("Read failed");
break;
}
if (write(out_fd,buf,r) < 0) {
PERROR("Write failed");
break;
}
rc+=r;
}
if (silent<2)
{
fprintf(stderr, "copied %lld bytes (%lld Kbytes/sec)\n",rc,rc/(1024*timeout));
}
}
static char *usage =
"usage:\n"
" tzap [options] <channel_name>\n"
" zap to channel channel_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"
" -x : exit after tuning\n"
" -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n"
" -s : only print summary\n"
" -S : run silently (no output)\n"
" -D : Show status with decimal numbers instead of hex\n"
" -F : set up frontend only, don't touch demux\n"
" -t number : timeout (seconds)\n"
" -o file : output filename (use -o - for stdout)\n"
" -h -? : display this help and exit\n";
int main(int argc, char **argv)
{
struct dvb_frontend_parameters frontend_param;
char *homedir = getenv ("HOME");
char *confname = NULL;
char *channel = NULL;
int adapter = 0, frontend = 0, demux = 0, dvr = 0;
int vpid, apid;
int frontend_fd, audio_fd = 0, video_fd = 0, dvr_fd, file_fd;
int opt;
int record = 0;
int frontend_only = 0;
char *filename = NULL;
while ((opt = getopt(argc, argv, "?DhrxRsFSn:a:f:d:c:t:o:")) != -1) {
switch (opt) {
case 'a':
adapter = strtoul(optarg, NULL, 0);
break;
case 'f':
frontend = strtoul(optarg, NULL, 0);
break;
case 'd':
demux = strtoul(optarg, NULL, 0);
break;
case 't':
timeout = strtoul(optarg, NULL, 0);
break;
case 'o':
filename = strdup(optarg);
record=1;
/* fall through */
case 'r':
dvr = 1;
break;
case 'x':
exit_after_tuning = 1;
break;
case 'c':
confname = optarg;
break;
case 's':
silent = 1;
break;
case 'S':
silent = 2;
break;
case 'F':
frontend_only = 1;
break;
case 'D':
decread = 1; /* alternative status readings (decimal instead of hex) */
break;
case '?':
case 'h':
default:
fprintf (stderr, usage, argv[0]);
return -1;
};
}
if (optind < argc)
channel = argv[optind];
if (!channel) {
fprintf (stderr, usage, argv[0]);
return -1;
}
snprintf (FRONTEND_DEV, sizeof(FRONTEND_DEV),
"/dev/dvb/adapter%i/frontend%i", adapter, frontend);
snprintf (DEMUX_DEV, sizeof(DEMUX_DEV),
"/dev/dvb/adapter%i/demux%i", adapter, demux);
snprintf (DVR_DEV, sizeof(DVR_DEV),
"/dev/dvb/adapter%i/dvr%i", adapter, demux);
if (silent<2)
fprintf (stderr,"using '%s' and '%s'\n", FRONTEND_DEV, DEMUX_DEV);
if (!confname)
{
int len = strlen(homedir) + strlen(CHANNEL_FILE) + 18;
if (!homedir)
ERROR ("$HOME not set");
confname = malloc (len);
snprintf (confname, len, "%s/.tzap/%i/%s",
homedir, adapter, CHANNEL_FILE);
if (access (confname, R_OK))
snprintf (confname, len, "%s/.tzap/%s",
homedir, CHANNEL_FILE);
}
printf("reading channels from file '%s'\n", confname);
memset(&frontend_param, 0, sizeof(struct dvb_frontend_parameters));
if (parse (confname, channel, &frontend_param, &vpid, &apid))
return -1;
if ((frontend_fd = open(FRONTEND_DEV, O_RDWR)) < 0) {
PERROR ("failed opening '%s'", FRONTEND_DEV);
return -1;
}
if (setup_frontend (frontend_fd, &frontend_param) < 0)
return -1;
if (frontend_only)
goto just_the_frontend_dude;
if ((video_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
PERROR("failed opening '%s'", DEMUX_DEV);
return -1;
}
if (silent<2)
fprintf (stderr,"video pid 0x%04x, audio pid 0x%04x\n", vpid, apid);
if (set_pesfilter (video_fd, vpid, DMX_PES_VIDEO, dvr) < 0)
return -1;
if ((audio_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
PERROR("failed opening '%s'", DEMUX_DEV);
return -1;
}
if (set_pesfilter (audio_fd, apid, DMX_PES_AUDIO, dvr) < 0)
return -1;
signal(SIGALRM,do_timeout);
if (timeout>0)
alarm(timeout);
if (record)
{
if (filename!=NULL)
{
if (strcmp(filename,"-")!=0)
{
file_fd = open (filename,O_WRONLY|O_LARGEFILE|O_CREAT,0644);
if (file_fd<0)
{
PERROR("open of '%s' failed",filename);
return -1;
}
}
else
{
file_fd=1;
}
}
else
{
PERROR("Record mode but no filename!");
return -1;
}
if ((dvr_fd = open(DVR_DEV, O_RDONLY)) < 0) {
PERROR("failed opening '%s'", DVR_DEV);
return -1;
}
if (ioctl(dvr_fd, DMX_SET_BUFFER_SIZE, 1024 * 1024)<0)
{
PERROR("DMX_SET_BUFFER_SIZE failed");
return -1;
}
if (silent<2)
print_frontend_stats (frontend_fd);
copy_to_file(dvr_fd,file_fd);
if (silent<2)
print_frontend_stats (frontend_fd);
}
else {
just_the_frontend_dude:
check_frontend (frontend_fd);
}
close (audio_fd);
close (video_fd);
close (frontend_fd);
return 0;
}
_______________________________________________
linux-dvb mailing list
linux-dvb@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb