szap.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. /* szap -- simple zapping tool for the Linux DVB API
  2. *
  3. * szap operates on VDR (http://www.cadsoft.de/people/kls/vdr/index.htm)
  4. * satellite channel lists (e.g. from http://www.dxandy.de/cgi-bin/dvbchan.pl).
  5. * szap assumes you have a "Universal LNB" (i.e. with LOFs 9750/10600 MHz).
  6. *
  7. * Compilation: `gcc -Wall -I../../ost/include -O2 szap.c -o szap`
  8. * or, if your DVB driver is in the kernel source tree:
  9. * `gcc -Wall -DDVB_IN_KERNEL -O2 szap.c -o szap`
  10. *
  11. * Copyright (C) 2001 Johannes Stezenbach (js@convergence.de)
  12. * for convergence integrated media
  13. *
  14. * This program is free software; you can redistribute it and/or modify
  15. * it under the terms of the GNU General Public License as published by
  16. * the Free Software Foundation; either version 2 of the License, or
  17. * (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program; if not, write to the Free Software
  26. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  27. */
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <limits.h>
  31. #include <string.h>
  32. #include <errno.h>
  33. #include <sys/ioctl.h>
  34. #include <sys/types.h>
  35. #include <sys/stat.h>
  36. #include <sys/poll.h>
  37. #include <sys/param.h>
  38. #include <fcntl.h>
  39. #include <time.h>
  40. #include <unistd.h>
  41. #include <stdint.h>
  42. #include <sys/time.h>
  43. #include <linux/dvb/frontend.h>
  44. #include <linux/dvb/dmx.h>
  45. #include <linux/dvb/audio.h>
  46. #include "lnb.h"
  47. #include "util.h"
  48. #ifndef TRUE
  49. #define TRUE (1==1)
  50. #endif
  51. #ifndef FALSE
  52. #define FALSE (1==0)
  53. #endif
  54. /* location of channel list file */
  55. #define CHANNEL_FILE "channels.conf"
  56. /* one line of the VDR channel file has the following format:
  57. * ^name:frequency_MHz:polarization:sat_no:symbolrate:vpid:apid:?:service_id$
  58. */
  59. #define FRONTENDDEVICE "/dev/dvb/adapter%d/frontend%d"
  60. #define DEMUXDEVICE "/dev/dvb/adapter%d/demux%d"
  61. #define AUDIODEVICE "/dev/dvb/adapter%d/audio%d"
  62. static struct lnb_types_st lnb_type;
  63. static int exit_after_tuning;
  64. static int interactive;
  65. static char *usage_str =
  66. "\nusage: szap -q\n"
  67. " list known channels\n"
  68. " szap [options] {-n channel-number|channel_name}\n"
  69. " zap to channel via number or full name (case insensitive)\n"
  70. " -a number : use given adapter (default 0)\n"
  71. " -f number : use given frontend (default 0)\n"
  72. " -d number : use given demux (default 0)\n"
  73. " -c file : read channels list from 'file'\n"
  74. " -b : enable Audio Bypass (default no)\n"
  75. " -x : exit after tuning\n"
  76. " -H : human readable output\n"
  77. " -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n"
  78. " -l lnb-type (DVB-S Only) (use -l help to print types) or \n"
  79. " -l low[,high[,switch]] in Mhz\n"
  80. " -i : run interactively, allowing you to type in channel names\n"
  81. " -p : add pat and pmt to TS recording (implies -r)\n"
  82. " or -n numbers for zapping\n";
  83. struct diseqc_cmd {
  84. struct dvb_diseqc_master_cmd cmd;
  85. uint32_t wait;
  86. };
  87. void diseqc_send_msg(int fd, fe_sec_voltage_t v, struct diseqc_cmd *cmd,
  88. fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b)
  89. {
  90. if (ioctl(fd, FE_SET_TONE, SEC_TONE_OFF) == -1)
  91. perror("FE_SET_TONE failed");
  92. if (ioctl(fd, FE_SET_VOLTAGE, v) == -1)
  93. perror("FE_SET_VOLTAGE failed");
  94. usleep(15 * 1000);
  95. if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) == -1)
  96. perror("FE_DISEQC_SEND_MASTER_CMD failed");
  97. usleep(cmd->wait * 1000);
  98. usleep(15 * 1000);
  99. if (ioctl(fd, FE_DISEQC_SEND_BURST, b) == -1)
  100. perror("FE_DISEQC_SEND_BURST failed");
  101. usleep(15 * 1000);
  102. if (ioctl(fd, FE_SET_TONE, t) == -1)
  103. perror("FE_SET_TONE failed");
  104. }
  105. /* digital satellite equipment control,
  106. * specification is available from http://www.eutelsat.com/
  107. */
  108. static int diseqc(int secfd, int sat_no, int pol_vert, int hi_band)
  109. {
  110. struct diseqc_cmd cmd =
  111. { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 };
  112. /* param: high nibble: reset bits, low nibble set bits,
  113. * bits are: option, position, polarization, band
  114. */
  115. cmd.cmd.msg[3] =
  116. 0xf0 | (((sat_no * 4) & 0x0f) | (hi_band ? 1 : 0) | (pol_vert ? 0 : 2));
  117. diseqc_send_msg(secfd, pol_vert ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18,
  118. &cmd, hi_band ? SEC_TONE_ON : SEC_TONE_OFF,
  119. sat_no % 2 ? SEC_MINI_B : SEC_MINI_A);
  120. return TRUE;
  121. }
  122. static int do_tune(int fefd, unsigned int ifreq, unsigned int sr)
  123. {
  124. struct dvb_frontend_parameters tuneto;
  125. struct dvb_frontend_event ev;
  126. /* discard stale QPSK events */
  127. while (1) {
  128. if (ioctl(fefd, FE_GET_EVENT, &ev) == -1)
  129. break;
  130. }
  131. tuneto.frequency = ifreq;
  132. tuneto.inversion = INVERSION_AUTO;
  133. tuneto.u.qpsk.symbol_rate = sr;
  134. tuneto.u.qpsk.fec_inner = FEC_AUTO;
  135. if (ioctl(fefd, FE_SET_FRONTEND, &tuneto) == -1) {
  136. perror("FE_SET_FRONTEND failed");
  137. return FALSE;
  138. }
  139. return TRUE;
  140. }
  141. static int monitor_frontend (int fe_fd, int dvr, int human_readable)
  142. {
  143. (void)dvr;
  144. fe_status_t status;
  145. uint16_t snr, signal;
  146. uint32_t ber, uncorrected_blocks;
  147. int timeout = 0;
  148. do {
  149. if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1)
  150. perror("FE_READ_STATUS failed");
  151. /* some frontends might not support all these ioctls, thus we
  152. * avoid printing errors
  153. */
  154. if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1)
  155. signal = -2;
  156. if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1)
  157. snr = -2;
  158. if (ioctl(fe_fd, FE_READ_BER, &ber) == -1)
  159. ber = -2;
  160. if (ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks) == -1)
  161. uncorrected_blocks = -2;
  162. if (human_readable) {
  163. printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ",
  164. status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks);
  165. } else {
  166. printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ",
  167. status, signal, snr, ber, uncorrected_blocks);
  168. }
  169. if (status & FE_HAS_LOCK)
  170. printf("FE_HAS_LOCK");
  171. printf("\n");
  172. if (exit_after_tuning && ((status & FE_HAS_LOCK) || (++timeout >= 10)))
  173. break;
  174. usleep(1000000);
  175. } while (1);
  176. return 0;
  177. }
  178. static
  179. int zap_to(unsigned int adapter, unsigned int frontend, unsigned int demux,
  180. unsigned int sat_no, unsigned int freq, unsigned int pol,
  181. unsigned int sr, unsigned int vpid, unsigned int apid, int sid,
  182. int dvr, int rec_psi, int bypass, int human_readable)
  183. {
  184. char fedev[128], dmxdev[128], auddev[128];
  185. static int fefd, dmxfda, dmxfdv, audiofd = -1, patfd, pmtfd;
  186. int pmtpid;
  187. uint32_t ifreq, mstd;
  188. int hiband, result;
  189. if (!fefd) {
  190. snprintf(fedev, sizeof(fedev), FRONTENDDEVICE, adapter, frontend);
  191. snprintf(dmxdev, sizeof(dmxdev), DEMUXDEVICE, adapter, demux);
  192. snprintf(auddev, sizeof(auddev), AUDIODEVICE, adapter, demux);
  193. printf("using '%s' and '%s'\n", fedev, dmxdev);
  194. if ((fefd = open(fedev, O_RDWR | O_NONBLOCK)) < 0) {
  195. perror("opening frontend failed");
  196. return FALSE;
  197. }
  198. if (check_frontend(fefd, FE_QPSK, &mstd) < 0) {
  199. close(fefd);
  200. return FALSE;
  201. }
  202. /* TODO! Some frontends need to be explicit delivery system */
  203. if ((dmxfdv = open(dmxdev, O_RDWR)) < 0) {
  204. perror("opening video demux failed");
  205. close(fefd);
  206. return FALSE;
  207. }
  208. if ((dmxfda = open(dmxdev, O_RDWR)) < 0) {
  209. perror("opening audio demux failed");
  210. close(fefd);
  211. return FALSE;
  212. }
  213. if (dvr == 0) /* DMX_OUT_DECODER */
  214. audiofd = open(auddev, O_RDWR);
  215. if (rec_psi) {
  216. if ((patfd = open(dmxdev, O_RDWR)) < 0) {
  217. perror("opening pat demux failed");
  218. close(audiofd);
  219. close(dmxfda);
  220. close(dmxfdv);
  221. close(fefd);
  222. return FALSE;
  223. }
  224. if ((pmtfd = open(dmxdev, O_RDWR)) < 0) {
  225. perror("opening pmt demux failed");
  226. close(patfd);
  227. close(audiofd);
  228. close(dmxfda);
  229. close(dmxfdv);
  230. close(fefd);
  231. return FALSE;
  232. }
  233. }
  234. }
  235. hiband = 0;
  236. if (lnb_type.switch_val && lnb_type.high_val && freq >= lnb_type.switch_val)
  237. hiband = 1;
  238. if (hiband)
  239. ifreq = freq - lnb_type.high_val;
  240. else {
  241. if (freq < lnb_type.low_val)
  242. ifreq = lnb_type.low_val - freq;
  243. else
  244. ifreq = freq - lnb_type.low_val;
  245. }
  246. result = FALSE;
  247. if (diseqc(fefd, sat_no, pol, hiband))
  248. if (do_tune(fefd, ifreq, sr))
  249. if (set_pesfilter(dmxfdv, vpid, DMX_PES_VIDEO, dvr))
  250. if (audiofd >= 0)
  251. (void)ioctl(audiofd, AUDIO_SET_BYPASS_MODE, bypass);
  252. if (set_pesfilter(dmxfda, apid, DMX_PES_AUDIO, dvr)) {
  253. if (rec_psi) {
  254. pmtpid = get_pmt_pid(dmxdev, sid);
  255. if (pmtpid < 0) {
  256. result = FALSE;
  257. }
  258. if (pmtpid == 0) {
  259. fprintf(stderr,"couldn't find pmt-pid for sid %04x\n",sid);
  260. result = FALSE;
  261. }
  262. if (set_pesfilter(patfd, 0, DMX_PES_OTHER, dvr))
  263. if (set_pesfilter(pmtfd, pmtpid, DMX_PES_OTHER, dvr))
  264. result = TRUE;
  265. } else {
  266. result = TRUE;
  267. }
  268. }
  269. monitor_frontend (fefd, dvr, human_readable);
  270. if (!interactive) {
  271. close(patfd);
  272. close(pmtfd);
  273. if (audiofd >= 0)
  274. close(audiofd);
  275. close(dmxfda);
  276. close(dmxfdv);
  277. close(fefd);
  278. }
  279. return result;
  280. }
  281. static int read_channels(const char *filename, int list_channels,
  282. uint32_t chan_no, const char *chan_name,
  283. unsigned int adapter, unsigned int frontend,
  284. unsigned int demux, int dvr, int rec_psi,
  285. int bypass, int human_readable)
  286. {
  287. FILE *cfp;
  288. char buf[4096];
  289. char inp[256];
  290. char *field, *tmp, *p;
  291. unsigned int line;
  292. unsigned int freq, pol, sat_no, sr, vpid, apid, sid;
  293. int ret;
  294. again:
  295. line = 0;
  296. if (!(cfp = fopen(filename, "r"))) {
  297. fprintf(stderr, "error opening channel list '%s': %d %m\n",
  298. filename, errno);
  299. return FALSE;
  300. }
  301. if (interactive) {
  302. fprintf(stderr, "\n>>> ");
  303. if (!fgets(inp, sizeof(inp), stdin)) {
  304. printf("\n");
  305. return -1;
  306. }
  307. if (inp[0] == '-' && inp[1] == 'n') {
  308. chan_no = strtoul(inp+2, NULL, 0);
  309. chan_name = NULL;
  310. if (!chan_no) {
  311. fprintf(stderr, "bad channel number\n");
  312. goto again;
  313. }
  314. } else {
  315. p = strchr(inp, '\n');
  316. if (p)
  317. *p = '\0';
  318. chan_name = inp;
  319. chan_no = 0;
  320. }
  321. }
  322. while (!feof(cfp)) {
  323. if (fgets(buf, sizeof(buf), cfp)) {
  324. line++;
  325. if (chan_no && chan_no != line)
  326. continue;
  327. tmp = buf;
  328. field = strsep(&tmp, ":");
  329. if (!field)
  330. goto syntax_err;
  331. if (list_channels) {
  332. printf("%03u %s\n", line, field);
  333. continue;
  334. }
  335. if (chan_name && strcasecmp(chan_name, field) != 0)
  336. continue;
  337. printf("zapping to %d '%s':\n", line, field);
  338. if (!(field = strsep(&tmp, ":")))
  339. goto syntax_err;
  340. freq = strtoul(field, NULL, 0);
  341. if (!(field = strsep(&tmp, ":")))
  342. goto syntax_err;
  343. pol = (field[0] == 'h' ? 0 : 1);
  344. if (!(field = strsep(&tmp, ":")))
  345. goto syntax_err;
  346. sat_no = strtoul(field, NULL, 0);
  347. if (!(field = strsep(&tmp, ":")))
  348. goto syntax_err;
  349. sr = strtoul(field, NULL, 0) * 1000;
  350. if (!(field = strsep(&tmp, ":")))
  351. goto syntax_err;
  352. vpid = strtoul(field, NULL, 0);
  353. if (!vpid)
  354. vpid = 0x1fff;
  355. if (!(field = strsep(&tmp, ":")))
  356. goto syntax_err;
  357. p = strchr(field, ';');
  358. if (p) {
  359. *p = '\0';
  360. p++;
  361. if (bypass) {
  362. if (!p || !*p)
  363. goto syntax_err;
  364. field = p;
  365. }
  366. }
  367. apid = strtoul(field, NULL, 0);
  368. if (!apid)
  369. apid = 0x1fff;
  370. if (!(field = strsep(&tmp, ":")))
  371. goto syntax_err;
  372. sid = strtoul(field, NULL, 0);
  373. printf("sat %u, frequency = %u MHz %c, symbolrate %u, "
  374. "vpid = 0x%04x, apid = 0x%04x sid = 0x%04x\n",
  375. sat_no, freq, pol ? 'V' : 'H', sr, vpid, apid, sid);
  376. fclose(cfp);
  377. ret = zap_to(adapter,
  378. frontend,
  379. demux,
  380. sat_no,
  381. freq * 1000,
  382. pol,
  383. sr,
  384. vpid,
  385. apid,
  386. sid,
  387. dvr,
  388. rec_psi,
  389. bypass,
  390. human_readable);
  391. if (interactive)
  392. goto again;
  393. if (ret)
  394. return TRUE;
  395. return FALSE;
  396. syntax_err:
  397. fprintf(stderr, "syntax error in line %u: '%s'\n", line, buf);
  398. } else if (ferror(cfp)) {
  399. fprintf(stderr, "error reading channel list '%s': %d %m\n", filename, errno);
  400. fclose(cfp);
  401. return FALSE;
  402. } else {
  403. break;
  404. }
  405. }
  406. fclose(cfp);
  407. if (!list_channels) {
  408. fprintf(stderr, "channel not found\n");
  409. if (!interactive)
  410. return FALSE;
  411. }
  412. if (interactive)
  413. goto again;
  414. return TRUE;
  415. }
  416. void bad_usage(char *pname, int prlnb)
  417. {
  418. int i;
  419. struct lnb_types_st *lnbp;
  420. char **cp;
  421. if (!prlnb) {
  422. fprintf (stderr, usage_str, pname);
  423. } else {
  424. i = 0;
  425. fprintf(stderr, "-l <lnb-type> or -l low[,high[,switch]] in Mhz\nwhere <lnb-type> is:\n");
  426. while(NULL != (lnbp = lnb_enum(i))) {
  427. fprintf (stderr, "%s\n", lnbp->name);
  428. for (cp = lnbp->desc; *cp ; cp++) {
  429. fprintf (stderr, " %s\n", *cp);
  430. }
  431. i++;
  432. }
  433. }
  434. }
  435. int main(int argc, char *argv[])
  436. {
  437. const char *home;
  438. char chanfile[2 * PATH_MAX];
  439. int list_channels = 0;
  440. unsigned int chan_no = 0;
  441. const char *chan_name = NULL;
  442. unsigned int adapter = 0, frontend = 0, demux = 0, dvr = 0, rec_psi = 0;
  443. int bypass = 0;
  444. int opt, copt = 0;
  445. int human_readable = 0;
  446. lnb_type = *lnb_enum(0);
  447. while ((opt = getopt(argc, argv, "Hhqrpn:a:f:d:c:l:xib")) != -1) {
  448. switch (opt) {
  449. case '?':
  450. case 'h':
  451. default:
  452. bad_usage(argv[0], 0);
  453. case 'b':
  454. bypass = 1;
  455. break;
  456. case 'q':
  457. list_channels = 1;
  458. break;
  459. case 'r':
  460. dvr = 1;
  461. break;
  462. case 'n':
  463. chan_no = strtoul(optarg, NULL, 0);
  464. break;
  465. case 'a':
  466. adapter = strtoul(optarg, NULL, 0);
  467. break;
  468. case 'f':
  469. frontend = strtoul(optarg, NULL, 0);
  470. break;
  471. case 'p':
  472. rec_psi = 1;
  473. break;
  474. case 'd':
  475. demux = strtoul(optarg, NULL, 0);
  476. break;
  477. case 'c':
  478. copt = 1;
  479. strncpy(chanfile, optarg, sizeof(chanfile));
  480. break;
  481. case 'l':
  482. if (lnb_decode(optarg, &lnb_type) < 0) {
  483. bad_usage(argv[0], 1);
  484. return -1;
  485. }
  486. break;
  487. case 'x':
  488. exit_after_tuning = 1;
  489. break;
  490. case 'H':
  491. human_readable = 1;
  492. break;
  493. case 'i':
  494. interactive = 1;
  495. exit_after_tuning = 1;
  496. }
  497. }
  498. lnb_type.low_val *= 1000; /* convert to kiloherz */
  499. lnb_type.high_val *= 1000; /* convert to kiloherz */
  500. lnb_type.switch_val *= 1000; /* convert to kiloherz */
  501. if (optind < argc)
  502. chan_name = argv[optind];
  503. if (chan_name && chan_no) {
  504. bad_usage(argv[0], 0);
  505. return -1;
  506. }
  507. if (list_channels && (chan_name || chan_no)) {
  508. bad_usage(argv[0], 0);
  509. return -1;
  510. }
  511. if (!list_channels && !chan_name && !chan_no && !interactive) {
  512. bad_usage(argv[0], 0);
  513. return -1;
  514. }
  515. if (!copt) {
  516. if (!(home = getenv("HOME"))) {
  517. fprintf(stderr, "error: $HOME not set\n");
  518. return TRUE;
  519. }
  520. snprintf(chanfile,
  521. sizeof(chanfile),
  522. "%s/.szap/%i/%s",
  523. home,
  524. adapter,
  525. CHANNEL_FILE);
  526. if (access(chanfile, R_OK))
  527. snprintf(chanfile,
  528. sizeof(chanfile),
  529. "%s/.szap/%s",
  530. home,
  531. CHANNEL_FILE);
  532. }
  533. printf("reading channels from file '%s'\n", chanfile);
  534. if (rec_psi)
  535. dvr=1;
  536. if (!read_channels(chanfile,
  537. list_channels,
  538. chan_no,
  539. chan_name,
  540. adapter,
  541. frontend,
  542. demux,
  543. dvr,
  544. rec_psi,
  545. bypass,
  546. human_readable))
  547. return TRUE;
  548. return FALSE;
  549. }