tune.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. #define _FILE_OFFSET_BITS 64
  2. #define _LARGEFILE_SOURCE 1
  3. #define _LARGEFILE64_SOURCE 1
  4. /******************************** INCLUDE FILES *******************************/
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <sys/ioctl.h>
  8. #include <unistd.h>
  9. #include <stdlib.h>
  10. #include <stdint.h>
  11. #include <string.h>
  12. #include <stdio.h>
  13. #include <fcntl.h>
  14. #include <ctype.h>
  15. #include <errno.h>
  16. #include <signal.h>
  17. #include <linux/dvb/frontend.h>
  18. #include <linux/dvb/dmx.h>
  19. #include "util.h"
  20. /******************************* LOCAL TYPEDEFS ******************************/
  21. /********************************* LOCAL DATA *********************************/
  22. static char FRONTEND_DEV [80];
  23. static char DEMUX_DEV [80];
  24. static char DVR_DEV [80];
  25. static int timeout_flag=0;
  26. static int silent=0,timeout=0;
  27. static int exit_after_tuning;
  28. /******************************** LOCAL DEFINES *******************************/
  29. #define CHANNEL_FILE "channels.conf"
  30. #define ERROR(x...) \
  31. do { \
  32. fprintf(stderr, "ERROR: "); \
  33. fprintf(stderr, x); \
  34. fprintf (stderr, "\n"); \
  35. } while (0)
  36. #define PERROR(x...) \
  37. do { \
  38. fprintf(stderr, "ERROR: "); \
  39. fprintf(stderr, x); \
  40. fprintf (stderr, " (%s)\n", strerror(errno)); \
  41. } while (0)
  42. typedef struct {
  43. char *name;
  44. int value;
  45. } Param;
  46. static const Param inversion_list [] = {
  47. { "INVERSION_OFF", INVERSION_OFF },
  48. { "INVERSION_ON", INVERSION_ON },
  49. { "INVERSION_AUTO", INVERSION_AUTO }
  50. };
  51. static const Param bw_list [] = {
  52. { "BANDWIDTH_6_MHZ", BANDWIDTH_6_MHZ },
  53. { "BANDWIDTH_7_MHZ", BANDWIDTH_7_MHZ },
  54. { "BANDWIDTH_8_MHZ", BANDWIDTH_8_MHZ }
  55. };
  56. static const Param fec_list [] = {
  57. { "FEC_1_2", FEC_1_2 },
  58. { "FEC_2_3", FEC_2_3 },
  59. { "FEC_3_4", FEC_3_4 },
  60. { "FEC_4_5", FEC_4_5 },
  61. { "FEC_5_6", FEC_5_6 },
  62. { "FEC_6_7", FEC_6_7 },
  63. { "FEC_7_8", FEC_7_8 },
  64. { "FEC_8_9", FEC_8_9 },
  65. { "FEC_AUTO", FEC_AUTO },
  66. { "FEC_NONE", FEC_NONE }
  67. };
  68. static const Param guard_list [] = {
  69. {"GUARD_INTERVAL_1_16", GUARD_INTERVAL_1_16},
  70. {"GUARD_INTERVAL_1_32", GUARD_INTERVAL_1_32},
  71. {"GUARD_INTERVAL_1_4", GUARD_INTERVAL_1_4},
  72. {"GUARD_INTERVAL_1_8", GUARD_INTERVAL_1_8},
  73. {"GUARD_INTERVAL_AUTO", GUARD_INTERVAL_AUTO}
  74. };
  75. static const Param hierarchy_list [] = {
  76. { "HIERARCHY_1", HIERARCHY_1 },
  77. { "HIERARCHY_2", HIERARCHY_2 },
  78. { "HIERARCHY_4", HIERARCHY_4 },
  79. { "HIERARCHY_NONE", HIERARCHY_NONE },
  80. { "HIERARCHY_AUTO", HIERARCHY_AUTO }
  81. };
  82. static const Param constellation_list [] = {
  83. { "QPSK", QPSK },
  84. { "QAM_128", QAM_128 },
  85. { "QAM_16", QAM_16 },
  86. { "QAM_256", QAM_256 },
  87. { "QAM_32", QAM_32 },
  88. { "QAM_64", QAM_64 },
  89. { "QAM_AUTO", QAM_AUTO }
  90. };
  91. static const Param transmissionmode_list [] = {
  92. { "TRANSMISSION_MODE_2K", TRANSMISSION_MODE_2K },
  93. { "TRANSMISSION_MODE_8K", TRANSMISSION_MODE_8K },
  94. { "TRANSMISSION_MODE_AUTO", TRANSMISSION_MODE_AUTO }
  95. };
  96. #define LIST_SIZE(x) sizeof(x)/sizeof(Param)
  97. static int parse_param (int fd, const Param * plist, int list_size, int *param)
  98. {
  99. char c;
  100. int character = 0;
  101. int _index = 0;
  102. while (1) {
  103. if (read(fd, &c, 1) < 1)
  104. return -1; /* EOF? */
  105. if ((c == ':' || c == '\n')
  106. && plist->name[character] == '\0')
  107. break;
  108. while (toupper(c) != plist->name[character]) {
  109. _index++;
  110. plist++;
  111. if (_index >= list_size) /* parse error, no valid */
  112. return -2; /* parameter name found */
  113. }
  114. character++;
  115. }
  116. *param = plist->value;
  117. return 0;
  118. }
  119. static int parse_int(int fd, int *val)
  120. {
  121. char number[11]; /* 2^32 needs 10 digits... */
  122. int character = 0;
  123. while (1) {
  124. if (read(fd, &number[character], 1) < 1)
  125. return -1; /* EOF? */
  126. if (number[character] == ':' || number[character] == '\n') {
  127. number[character] = '\0';
  128. break;
  129. }
  130. if (!isdigit(number[character]))
  131. return -2; /* parse error, not a digit... */
  132. character++;
  133. if (character > 10) /* overflow, number too big */
  134. return -3; /* to fit in 32 bit */
  135. };
  136. errno = 0;
  137. *val = strtol(number, NULL, 10);
  138. if (errno == ERANGE)
  139. return -4;
  140. return 0;
  141. }
  142. static int find_channel(int fd, const char *channel)
  143. {
  144. int character = 0;
  145. while (1) {
  146. char c;
  147. if (read(fd, &c, 1) < 1)
  148. return -1; /* EOF! */
  149. if ( '\n' == c ) /* start of line */
  150. character = 0;
  151. else if ( character >= 0 ) { /* we are in the namefield */
  152. if (c == ':' && channel[character] == '\0')
  153. break;
  154. if (toupper(c) == toupper(channel[character]))
  155. character++;
  156. else
  157. character = -1;
  158. }
  159. };
  160. return 0;
  161. }
  162. static
  163. int try_parse_int(int fd, int *val, const char *pname)
  164. {
  165. int err;
  166. err = parse_int(fd, val);
  167. if (err)
  168. ERROR("error while parsing %s (%s)", pname,
  169. err == -1 ? "end of file" :
  170. err == -2 ? "not a number" : "number too big");
  171. return err;
  172. }
  173. static
  174. int try_parse_param(int fd, const Param * plist, int list_size, int *param,
  175. const char *pname)
  176. {
  177. int err;
  178. err = parse_param(fd, plist, list_size, param);
  179. if (err)
  180. ERROR("error while parsing %s (%s)", pname,
  181. err == -1 ? "end of file" : "syntax error");
  182. return err;
  183. }
  184. static int check_fec(fe_code_rate_t *fec)
  185. {
  186. switch (*fec)
  187. {
  188. case FEC_NONE:
  189. *fec = FEC_AUTO;
  190. case FEC_AUTO:
  191. case FEC_1_2:
  192. case FEC_2_3:
  193. case FEC_3_4:
  194. case FEC_5_6:
  195. case FEC_7_8:
  196. return 0;
  197. default:
  198. ;
  199. }
  200. return 1;
  201. }
  202. int parse(const char *fname, const char *channel,
  203. struct dvb_frontend_parameters *frontend, int *vpid, int *apid,
  204. int *sid)
  205. {
  206. int fd;
  207. int err;
  208. int tmp;
  209. if ((fd = open(fname, O_RDONLY | O_NONBLOCK)) < 0) {
  210. PERROR ("could not open file '%s'", fname);
  211. perror ("");
  212. return -1;
  213. }
  214. if (find_channel(fd, channel) < 0) {
  215. ERROR("could not find channel '%s' in channel list", channel);
  216. return -2;
  217. }
  218. if ((err = try_parse_int(fd, &tmp, "frequency")))
  219. return -3;
  220. frontend->frequency = tmp;
  221. if ((err = try_parse_param(fd,
  222. inversion_list, LIST_SIZE(inversion_list),
  223. &tmp, "inversion")))
  224. return -4;
  225. frontend->inversion = tmp;
  226. if ((err = try_parse_param(fd, bw_list, LIST_SIZE(bw_list),
  227. &tmp, "bandwidth")))
  228. return -5;
  229. frontend->u.ofdm.bandwidth = tmp;
  230. if ((err = try_parse_param(fd, fec_list, LIST_SIZE(fec_list),
  231. &tmp, "code_rate_HP")))
  232. return -6;
  233. frontend->u.ofdm.code_rate_HP = tmp;
  234. if (check_fec(&frontend->u.ofdm.code_rate_HP))
  235. return -6;
  236. if ((err = try_parse_param(fd, fec_list, LIST_SIZE(fec_list),
  237. &tmp, "code_rate_LP")))
  238. return -7;
  239. frontend->u.ofdm.code_rate_LP = tmp;
  240. if (check_fec(&frontend->u.ofdm.code_rate_LP))
  241. return -7;
  242. if ((err = try_parse_param(fd, constellation_list,
  243. LIST_SIZE(constellation_list),
  244. &tmp, "constellation")))
  245. return -8;
  246. frontend->u.ofdm.constellation = tmp;
  247. if ((err = try_parse_param(fd, transmissionmode_list,
  248. LIST_SIZE(transmissionmode_list),
  249. &tmp, "transmission_mode")))
  250. return -9;
  251. frontend->u.ofdm.transmission_mode = tmp;
  252. if ((err = try_parse_param(fd, guard_list, LIST_SIZE(guard_list),
  253. &tmp, "guard_interval")))
  254. return -10;
  255. frontend->u.ofdm.guard_interval = tmp;
  256. if ((err = try_parse_param(fd, hierarchy_list,
  257. LIST_SIZE(hierarchy_list),
  258. &tmp, "hierarchy_information")))
  259. return -11;
  260. frontend->u.ofdm.hierarchy_information = tmp;
  261. if ((err = try_parse_int(fd, vpid, "Video PID")))
  262. return -12;
  263. if ((err = try_parse_int(fd, apid, "Audio PID")))
  264. return -13;
  265. if ((err = try_parse_int(fd, sid, "Service ID")))
  266. return -14;
  267. close(fd);
  268. return 0;
  269. }
  270. static int setup_frontend (int fe_fd, struct dvb_frontend_parameters *frontend)
  271. {
  272. int ret;
  273. uint32_t mstd;
  274. if (check_frontend(fe_fd, FE_OFDM, &mstd) < 0) {
  275. close(fe_fd);
  276. return -1;
  277. }
  278. ret = dvbfe_set_delsys(fe_fd, SYS_DVBT);
  279. if (ret) {
  280. PERROR("SET Delsys failed");
  281. return -1;
  282. }
  283. if (silent < 2)
  284. fprintf (stderr,"tuning to %i Hz\n", frontend->frequency);
  285. if (ioctl(fe_fd, FE_SET_FRONTEND, frontend) < 0) {
  286. PERROR("ioctl FE_SET_FRONTEND failed");
  287. return -1;
  288. }
  289. return 0;
  290. }
  291. static void do_timeout(int x)
  292. {
  293. (void)x;
  294. if (timeout_flag == 0) {
  295. timeout_flag = 1;
  296. alarm(2);
  297. signal(SIGALRM, do_timeout);
  298. } else {
  299. /* something has gone wrong ... exit */
  300. exit(1);
  301. }
  302. }
  303. static void print_frontend_stats(int fe_fd, int human_readable)
  304. {
  305. fe_status_t status;
  306. uint16_t snr, _signal;
  307. uint32_t ber, uncorrected_blocks;
  308. ioctl(fe_fd, FE_READ_STATUS, &status);
  309. ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &_signal);
  310. ioctl(fe_fd, FE_READ_SNR, &snr);
  311. ioctl(fe_fd, FE_READ_BER, &ber);
  312. ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks);
  313. if (human_readable) {
  314. printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ",
  315. status, (_signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks);
  316. } else {
  317. fprintf (stderr, "status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ",
  318. status, _signal, snr, ber, uncorrected_blocks);
  319. }
  320. if (status & FE_HAS_LOCK)
  321. fprintf(stderr,"FE_HAS_LOCK");
  322. fprintf(stderr,"\n");
  323. }
  324. static
  325. int monitor_frontend (int fe_fd, int human_readable)
  326. {
  327. fe_status_t status;
  328. do {
  329. ioctl(fe_fd, FE_READ_STATUS, &status);
  330. if (!silent)
  331. print_frontend_stats(fe_fd, human_readable);
  332. if (exit_after_tuning && (status & FE_HAS_LOCK))
  333. break;
  334. usleep(1000000);
  335. } while (!timeout_flag);
  336. if (silent < 2)
  337. print_frontend_stats (fe_fd, human_readable);
  338. return 0;
  339. }
  340. #define BUFLEN (188*256)
  341. static void copy_to_file(int in_fd, int out_fd)
  342. {
  343. char buf[BUFLEN];
  344. int r;
  345. long long int rc = 0LL;
  346. while (timeout_flag==0) {
  347. r=read(in_fd,buf,BUFLEN);
  348. if (r < 0) {
  349. if (errno == EOVERFLOW) {
  350. printf("buffer overrun\n");
  351. continue;
  352. }
  353. PERROR("Read failed");
  354. break;
  355. }
  356. if (write(out_fd,buf,r) < 0) {
  357. PERROR("Write failed");
  358. break;
  359. }
  360. rc+=r;
  361. }
  362. if (silent<2) {
  363. fprintf(stderr, "copied %lld bytes (%lld Kbytes/sec)\n",rc,rc/(1024*timeout));
  364. }
  365. }
  366. static char *usage =
  367. "usage:\n"
  368. " tzap [options] <channel_name>\n"
  369. " zap to channel channel_name (case insensitive)\n"
  370. " -a number : use given adapter (default 0)\n"
  371. " -f number : use given frontend (default 0)\n"
  372. " -d number : use given demux (default 0)\n"
  373. " -c file : read channels list from 'file'\n"
  374. " -x : exit after tuning\n"
  375. " -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n"
  376. " -p : add pat and pmt to TS recording (implies -r)\n"
  377. " -s : only print summary\n"
  378. " -S : run silently (no output)\n"
  379. " -H : human readable output\n"
  380. " -F : set up frontend only, don't touch demux\n"
  381. " -t number : timeout (seconds)\n"
  382. " -o file : output filename (use -o - for stdout)\n"
  383. " -h -? : display this help and exit\n";
  384. int main(int argc, char **argv)
  385. {
  386. struct dvb_frontend_parameters frontend_param;
  387. char *homedir = getenv("HOME");
  388. char *confname = NULL;
  389. char *channel = NULL;
  390. int adapter = 0, frontend = 0, demux = 0, dvr = 0;
  391. int vpid, apid, sid, pmtpid = 0;
  392. int pat_fd, pmt_fd;
  393. int frontend_fd, audio_fd = 0, video_fd = 0, dvr_fd, file_fd;
  394. int opt;
  395. int record = 0;
  396. int frontend_only = 0;
  397. char *filename = NULL;
  398. int human_readable = 0, rec_psi = 0;
  399. while ((opt = getopt(argc, argv, "H?hrpxRsFSn:a:f:d:c:t:o:")) != -1) {
  400. switch (opt) {
  401. case 'a':
  402. adapter = strtoul(optarg, NULL, 0);
  403. break;
  404. case 'f':
  405. frontend = strtoul(optarg, NULL, 0);
  406. break;
  407. case 'd':
  408. demux = strtoul(optarg, NULL, 0);
  409. break;
  410. case 't':
  411. timeout = strtoul(optarg, NULL, 0);
  412. break;
  413. case 'o':
  414. filename = strdup(optarg);
  415. record=1;
  416. /* fall through */
  417. case 'r':
  418. dvr = 1;
  419. break;
  420. case 'p':
  421. rec_psi = 1;
  422. break;
  423. case 'x':
  424. exit_after_tuning = 1;
  425. break;
  426. case 'c':
  427. confname = optarg;
  428. break;
  429. case 's':
  430. silent = 1;
  431. break;
  432. case 'S':
  433. silent = 2;
  434. break;
  435. case 'F':
  436. frontend_only = 1;
  437. break;
  438. case 'H':
  439. human_readable = 1;
  440. break;
  441. case '?':
  442. case 'h':
  443. default:
  444. fprintf (stderr, usage, argv[0]);
  445. return -1;
  446. };
  447. }
  448. if (optind < argc)
  449. channel = argv[optind];
  450. if (!channel) {
  451. fprintf (stderr, usage, argv[0]);
  452. return -1;
  453. }
  454. snprintf(FRONTEND_DEV,
  455. sizeof(FRONTEND_DEV),
  456. "/dev/dvb/adapter%i/frontend%i",
  457. adapter,
  458. frontend);
  459. snprintf(DEMUX_DEV,
  460. sizeof(DEMUX_DEV),
  461. "/dev/dvb/adapter%i/demux%i",
  462. adapter,
  463. demux);
  464. snprintf(DVR_DEV,
  465. sizeof(DVR_DEV),
  466. "/dev/dvb/adapter%i/dvr%i",
  467. adapter,
  468. demux);
  469. if (silent < 2)
  470. fprintf (stderr,"using '%s' and '%s'\n", FRONTEND_DEV, DEMUX_DEV);
  471. if (!confname) {
  472. int len = strlen(homedir) + strlen(CHANNEL_FILE) + 18;
  473. if (!homedir)
  474. ERROR ("$HOME not set");
  475. confname = malloc (len);
  476. snprintf(confname, len, "%s/.tzap/%i/%s", homedir, adapter, CHANNEL_FILE);
  477. if (access (confname, R_OK))
  478. snprintf(confname, len, "%s/.tzap/%s", homedir, CHANNEL_FILE);
  479. }
  480. printf("reading channels from file '%s'\n", confname);
  481. memset(&frontend_param, 0, sizeof(struct dvb_frontend_parameters));
  482. if (parse (confname, channel, &frontend_param, &vpid, &apid, &sid))
  483. return -1;
  484. if ((frontend_fd = open(FRONTEND_DEV, O_RDWR | O_NONBLOCK)) < 0) {
  485. PERROR ("failed opening '%s'", FRONTEND_DEV);
  486. return -1;
  487. }
  488. if (setup_frontend (frontend_fd, &frontend_param) < 0)
  489. return -1;
  490. if (frontend_only)
  491. goto just_the_frontend_dude;
  492. if (rec_psi) {
  493. pmtpid = get_pmt_pid(DEMUX_DEV, sid);
  494. if (pmtpid <= 0) {
  495. fprintf(stderr,"couldn't find pmt-pid for sid %04x\n",sid);
  496. return -1;
  497. }
  498. if ((pat_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  499. perror("opening pat demux failed");
  500. return -1;
  501. }
  502. if (set_pesfilter(pat_fd, 0, DMX_PES_OTHER, dvr) < 0)
  503. return -1;
  504. if ((pmt_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  505. perror("opening pmt demux failed");
  506. return -1;
  507. }
  508. if (set_pesfilter(pmt_fd, pmtpid, DMX_PES_OTHER, dvr) < 0)
  509. return -1;
  510. }
  511. if ((video_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  512. PERROR("failed opening '%s'", DEMUX_DEV);
  513. return -1;
  514. }
  515. if (silent<2)
  516. fprintf(stderr,"video pid 0x%04x, audio pid 0x%04x\n", vpid, apid);
  517. if (set_pesfilter(video_fd, vpid, DMX_PES_VIDEO, dvr) < 0)
  518. return -1;
  519. if ((audio_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  520. PERROR("failed opening '%s'", DEMUX_DEV);
  521. return -1;
  522. }
  523. if (set_pesfilter(audio_fd, apid, DMX_PES_AUDIO, dvr) < 0)
  524. return -1;
  525. signal(SIGALRM,do_timeout);
  526. if (timeout > 0)
  527. alarm(timeout);
  528. if (record) {
  529. if (filename!=NULL) {
  530. if (strcmp(filename,"-")!=0) {
  531. file_fd = open(filename,O_WRONLY|O_LARGEFILE|O_CREAT,0644);
  532. if (file_fd<0) {
  533. PERROR("open of '%s' failed",filename);
  534. return -1;
  535. }
  536. } else {
  537. file_fd=1;
  538. }
  539. } else {
  540. PERROR("Record mode but no filename!");
  541. return -1;
  542. }
  543. if ((dvr_fd = open(DVR_DEV, O_RDONLY)) < 0) {
  544. PERROR("failed opening '%s'", DVR_DEV);
  545. return -1;
  546. }
  547. if (silent<2)
  548. print_frontend_stats(frontend_fd, human_readable);
  549. copy_to_file(dvr_fd,file_fd);
  550. if (silent<2)
  551. print_frontend_stats(frontend_fd, human_readable);
  552. } else {
  553. just_the_frontend_dude:
  554. monitor_frontend(frontend_fd, human_readable);
  555. }
  556. close(pat_fd);
  557. close(pmt_fd);
  558. close(audio_fd);
  559. close(video_fd);
  560. close(frontend_fd);
  561. return 0;
  562. }