| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- /* szap -- simple zapping tool for the Linux DVB API
- *
- * szap operates on VDR (http://www.cadsoft.de/people/kls/vdr/index.htm)
- * satellite channel lists (e.g. from http://www.dxandy.de/cgi-bin/dvbchan.pl).
- * szap assumes you have a "Universal LNB" (i.e. with LOFs 9750/10600 MHz).
- *
- * Compilation: `gcc -Wall -I../../ost/include -O2 szap.c -o szap`
- * or, if your DVB driver is in the kernel source tree:
- * `gcc -Wall -DDVB_IN_KERNEL -O2 szap.c -o szap`
- *
- * Copyright (C) 2001 Johannes Stezenbach (js@convergence.de)
- * for convergence integrated media
- *
- * 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 <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 "lnb.h"
- #include "util.h"
- #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 VDR channel file has the following format:
- * ^name:frequency_MHz:polarization:sat_no:symbolrate:vpid:apid:?:service_id$
- */
- #define FRONTENDDEVICE "/dev/dvb/adapter%d/frontend%d"
- #define DEMUXDEVICE "/dev/dvb/adapter%d/demux%d"
- #define AUDIODEVICE "/dev/dvb/adapter%d/audio%d"
- static struct lnb_types_st lnb_type;
- static int exit_after_tuning;
- static int interactive;
- static char *usage_str =
- "\nusage: szap -q\n"
- " list known channels\n"
- " szap [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"
- " -b : enable Audio Bypass (default no)\n"
- " -x : exit after tuning\n"
- " -H : human readable output\n"
- " -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n"
- " -l lnb-type (DVB-S Only) (use -l help to print types) or \n"
- " -l low[,high[,switch]] in Mhz\n"
- " -i : run interactively, allowing you to type in channel names\n"
- " -p : add pat and pmt to TS recording (implies -r)\n"
- " or -n numbers for zapping\n";
- struct diseqc_cmd {
- struct dvb_diseqc_master_cmd cmd;
- uint32_t wait;
- };
- void diseqc_send_msg(int fd, fe_sec_voltage_t v, struct diseqc_cmd *cmd,
- fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b)
- {
- if (ioctl(fd, FE_SET_TONE, SEC_TONE_OFF) == -1)
- perror("FE_SET_TONE failed");
- if (ioctl(fd, FE_SET_VOLTAGE, v) == -1)
- perror("FE_SET_VOLTAGE failed");
- usleep(15 * 1000);
- if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) == -1)
- perror("FE_DISEQC_SEND_MASTER_CMD failed");
- usleep(cmd->wait * 1000);
- usleep(15 * 1000);
- if (ioctl(fd, FE_DISEQC_SEND_BURST, b) == -1)
- perror("FE_DISEQC_SEND_BURST failed");
- usleep(15 * 1000);
- if (ioctl(fd, FE_SET_TONE, t) == -1)
- perror("FE_SET_TONE failed");
- }
- /* digital satellite equipment control,
- * specification is available from http://www.eutelsat.com/
- */
- static int diseqc(int secfd, int sat_no, int pol_vert, int hi_band)
- {
- struct diseqc_cmd cmd =
- { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 };
- /* param: high nibble: reset bits, low nibble set bits,
- * bits are: option, position, polarization, band
- */
- cmd.cmd.msg[3] =
- 0xf0 | (((sat_no * 4) & 0x0f) | (hi_band ? 1 : 0) | (pol_vert ? 0 : 2));
- diseqc_send_msg(secfd, pol_vert ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18,
- &cmd, hi_band ? SEC_TONE_ON : SEC_TONE_OFF,
- sat_no % 2 ? SEC_MINI_B : SEC_MINI_A);
- return TRUE;
- }
- static int do_tune(int fefd, unsigned int ifreq, unsigned int sr)
- {
- struct dvb_frontend_parameters tuneto;
- struct dvb_frontend_event ev;
- /* discard stale QPSK events */
- while (1) {
- if (ioctl(fefd, FE_GET_EVENT, &ev) == -1)
- break;
- }
- tuneto.frequency = ifreq;
- tuneto.inversion = INVERSION_AUTO;
- tuneto.u.qpsk.symbol_rate = sr;
- tuneto.u.qpsk.fec_inner = FEC_AUTO;
- if (ioctl(fefd, FE_SET_FRONTEND, &tuneto) == -1) {
- perror("FE_SET_FRONTEND failed");
- return FALSE;
- }
- return TRUE;
- }
- static int monitor_frontend (int fe_fd, int dvr, int human_readable)
- {
- (void)dvr;
- fe_status_t status;
- uint16_t snr, signal;
- uint32_t ber, uncorrected_blocks;
- int timeout = 0;
- do {
- if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1)
- perror("FE_READ_STATUS failed");
- /* some frontends might not support all these ioctls, thus we
- * avoid printing errors
- */
- if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1)
- signal = -2;
- if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1)
- snr = -2;
- if (ioctl(fe_fd, FE_READ_BER, &ber) == -1)
- ber = -2;
- if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1)
- uncorrected_blocks = -2;
- if (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);
- return 0;
- }
- static
- int zap_to(unsigned int adapter, unsigned int frontend, unsigned int demux,
- unsigned int sat_no, unsigned int freq, unsigned int pol,
- unsigned int sr, unsigned int vpid, unsigned int apid, int sid,
- int dvr, int rec_psi, int bypass, int human_readable)
- {
- char fedev[128], dmxdev[128], auddev[128];
- static int fefd, dmxfda, dmxfdv, audiofd = -1, patfd, pmtfd;
- int pmtpid;
- uint32_t ifreq, mstd;
- int hiband, result;
- if (!fefd) {
- snprintf(fedev, sizeof(fedev), FRONTENDDEVICE, adapter, frontend);
- snprintf(dmxdev, sizeof(dmxdev), DEMUXDEVICE, adapter, demux);
- snprintf(auddev, sizeof(auddev), AUDIODEVICE, adapter, demux);
- printf("using '%s' and '%s'\n", fedev, dmxdev);
- if ((fefd = open(fedev, O_RDWR | O_NONBLOCK)) < 0) {
- perror("opening frontend failed");
- return FALSE;
- }
- if (check_frontend(fefd, FE_QPSK, &mstd) < 0) {
- close(fefd);
- return FALSE;
- }
- /* TODO! Some frontends need to be explicit delivery system */
- 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 (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(fefd);
- return FALSE;
- }
- if ((pmtfd = open(dmxdev, O_RDWR)) < 0) {
- perror("opening pmt demux failed");
- close(patfd);
- close(audiofd);
- close(dmxfda);
- close(dmxfdv);
- close(fefd);
- return FALSE;
- }
- }
- }
- hiband = 0;
- if (lnb_type.switch_val && lnb_type.high_val && freq >= lnb_type.switch_val)
- hiband = 1;
- if (hiband)
- ifreq = freq - lnb_type.high_val;
- else {
- if (freq < lnb_type.low_val)
- ifreq = lnb_type.low_val - freq;
- else
- ifreq = freq - lnb_type.low_val;
- }
- result = FALSE;
- if (diseqc(fefd, sat_no, pol, hiband))
- if (do_tune(fefd, ifreq, sr))
- if (set_pesfilter(dmxfdv, vpid, DMX_PES_VIDEO, dvr))
- if (audiofd >= 0)
- (void)ioctl(audiofd, AUDIO_SET_BYPASS_MODE, bypass);
- if (set_pesfilter(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_pesfilter(patfd, 0, DMX_PES_OTHER, dvr))
- if (set_pesfilter(pmtfd, pmtpid, DMX_PES_OTHER, dvr))
- result = TRUE;
- } else {
- result = TRUE;
- }
- }
- monitor_frontend (fefd, dvr, human_readable);
- if (!interactive) {
- close(patfd);
- close(pmtfd);
- if (audiofd >= 0)
- close(audiofd);
- close(dmxfda);
- close(dmxfdv);
- close(fefd);
- }
- return result;
- }
- 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, int human_readable)
- {
- FILE *cfp;
- char buf[4096];
- char inp[256];
- char *field, *tmp, *p;
- unsigned int line;
- unsigned int freq, pol, sat_no, sr, vpid, apid, sid;
- int ret;
- 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;
- pol = (field[0] == 'h' ? 0 : 1);
- if (!(field = strsep(&tmp, ":")))
- goto syntax_err;
- sat_no = strtoul(field, NULL, 0);
- if (!(field = strsep(&tmp, ":")))
- goto syntax_err;
- sr = strtoul(field, NULL, 0) * 1000;
- if (!(field = strsep(&tmp, ":")))
- goto syntax_err;
- vpid = strtoul(field, NULL, 0);
- if (!vpid)
- vpid = 0x1fff;
- if (!(field = strsep(&tmp, ":")))
- goto syntax_err;
- p = strchr(field, ';');
- if (p) {
- *p = '\0';
- p++;
- if (bypass) {
- if (!p || !*p)
- goto syntax_err;
- field = p;
- }
- }
- apid = strtoul(field, NULL, 0);
- if (!apid)
- apid = 0x1fff;
- if (!(field = strsep(&tmp, ":")))
- goto syntax_err;
- sid = strtoul(field, NULL, 0);
- printf("sat %u, frequency = %u MHz %c, symbolrate %u, "
- "vpid = 0x%04x, apid = 0x%04x sid = 0x%04x\n",
- sat_no, freq, pol ? 'V' : 'H', sr, vpid, apid, sid);
- fclose(cfp);
- ret = zap_to(adapter,
- frontend,
- demux,
- sat_no,
- freq * 1000,
- pol,
- sr,
- vpid,
- apid,
- sid,
- dvr,
- rec_psi,
- bypass,
- human_readable);
- 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;
- }
- void bad_usage(char *pname, int prlnb)
- {
- int i;
- struct lnb_types_st *lnbp;
- char **cp;
- if (!prlnb) {
- fprintf (stderr, usage_str, pname);
- } else {
- i = 0;
- fprintf(stderr, "-l <lnb-type> or -l low[,high[,switch]] in Mhz\nwhere <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++;
- }
- }
- }
- 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;
- lnb_type = *lnb_enum(0);
- while ((opt = getopt(argc, argv, "Hhqrpn:a:f:d:c:l:xib")) != -1) {
- switch (opt) {
- case '?':
- case 'h':
- default:
- bad_usage(argv[0], 0);
- case 'b':
- bypass = 1;
- break;
- case 'q':
- list_channels = 1;
- break;
- case 'r':
- dvr = 1;
- break;
- case 'n':
- chan_no = strtoul(optarg, NULL, 0);
- break;
- case 'a':
- adapter = strtoul(optarg, NULL, 0);
- break;
- case 'f':
- frontend = strtoul(optarg, NULL, 0);
- break;
- case 'p':
- rec_psi = 1;
- break;
- case 'd':
- demux = strtoul(optarg, NULL, 0);
- break;
- case 'c':
- copt = 1;
- strncpy(chanfile, optarg, sizeof(chanfile));
- break;
- case 'l':
- if (lnb_decode(optarg, &lnb_type) < 0) {
- bad_usage(argv[0], 1);
- return -1;
- }
- break;
- case 'x':
- exit_after_tuning = 1;
- break;
- case 'H':
- human_readable = 1;
- break;
- case 'i':
- interactive = 1;
- exit_after_tuning = 1;
- }
- }
- lnb_type.low_val *= 1000; /* convert to kiloherz */
- lnb_type.high_val *= 1000; /* convert to kiloherz */
- lnb_type.switch_val *= 1000; /* convert to kiloherz */
- if (optind < argc)
- chan_name = argv[optind];
- if (chan_name && chan_no) {
- bad_usage(argv[0], 0);
- return -1;
- }
- if (list_channels && (chan_name || chan_no)) {
- bad_usage(argv[0], 0);
- return -1;
- }
- if (!list_channels && !chan_name && !chan_no && !interactive) {
- bad_usage(argv[0], 0);
- return -1;
- }
- if (!copt) {
- if (!(home = getenv("HOME"))) {
- fprintf(stderr, "error: $HOME not set\n");
- return TRUE;
- }
- snprintf(chanfile,
- sizeof(chanfile),
- "%s/.szap/%i/%s",
- home,
- adapter,
- CHANNEL_FILE);
- if (access(chanfile, R_OK))
- snprintf(chanfile,
- sizeof(chanfile),
- "%s/.szap/%s",
- home,
- CHANNEL_FILE);
- }
- printf("reading channels from file '%s'\n", chanfile);
- if (rec_psi)
- dvr=1;
- if (!read_channels(chanfile,
- list_channels,
- chan_no,
- chan_name,
- adapter,
- frontend,
- demux,
- dvr,
- rec_psi,
- bypass,
- human_readable))
- return TRUE;
- return FALSE;
- }
|