czap.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <sys/ioctl.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <stdio.h>
  9. #include <fcntl.h>
  10. #include <ctype.h>
  11. #include <errno.h>
  12. #include <linux/dvb/frontend.h>
  13. #include <linux/dvb/dmx.h>
  14. #include "util.h"
  15. static char FRONTEND_DEV [80];
  16. static char DEMUX_DEV [80];
  17. static int exit_after_tuning;
  18. #define CHANNEL_FILE "channels.conf"
  19. #define ERROR(x...) \
  20. do { \
  21. fprintf(stderr, "ERROR: "); \
  22. fprintf(stderr, x); \
  23. fprintf (stderr, "\n"); \
  24. } while (0)
  25. #define PERROR(x...) \
  26. do { \
  27. fprintf(stderr, "ERROR: "); \
  28. fprintf(stderr, x); \
  29. fprintf (stderr, " (%s)\n", strerror(errno)); \
  30. } while (0)
  31. typedef struct {
  32. char *name;
  33. int value;
  34. } Param;
  35. static const Param inversion_list[] = {
  36. { "INVERSION_OFF", INVERSION_OFF },
  37. { "INVERSION_ON", INVERSION_ON },
  38. { "INVERSION_AUTO", INVERSION_AUTO }
  39. };
  40. static const Param fec_list[] = {
  41. { "FEC_1_2", FEC_1_2 },
  42. { "FEC_2_3", FEC_2_3 },
  43. { "FEC_3_4", FEC_3_4 },
  44. { "FEC_4_5", FEC_4_5 },
  45. { "FEC_5_6", FEC_5_6 },
  46. { "FEC_6_7", FEC_6_7 },
  47. { "FEC_7_8", FEC_7_8 },
  48. { "FEC_8_9", FEC_8_9 },
  49. { "FEC_AUTO", FEC_AUTO },
  50. { "FEC_NONE", FEC_NONE }
  51. };
  52. static const Param modulation_list[] = {
  53. { "QAM_16", QAM_16 },
  54. { "QAM_32", QAM_32 },
  55. { "QAM_64", QAM_64 },
  56. { "QAM_128", QAM_128 },
  57. { "QAM_256", QAM_256 },
  58. { "QAM_AUTO", QAM_AUTO }
  59. };
  60. #define LIST_SIZE(x) sizeof(x)/sizeof(Param)
  61. static
  62. int parse_param(const char *val, const Param * plist, int list_size, int *ok)
  63. {
  64. int i;
  65. for (i = 0; i < list_size; i++) {
  66. if (strcasecmp(plist[i].name, val) == 0) {
  67. *ok = 1;
  68. return plist[i].value;
  69. }
  70. }
  71. *ok = 0;
  72. return -1;
  73. }
  74. static char line_buf[256];
  75. static
  76. char *find_channel(FILE *f, int list_channels, int *chan_no, const char *channel)
  77. {
  78. size_t l;
  79. int lno = 0;
  80. l = channel ? strlen(channel) : 0;
  81. while (!feof(f)) {
  82. if (!fgets(line_buf, sizeof(line_buf), f))
  83. return NULL;
  84. lno++;
  85. if (list_channels) {
  86. printf("%3d %s", lno, line_buf);
  87. }
  88. else if (*chan_no) {
  89. if (*chan_no == lno)
  90. return line_buf;
  91. }
  92. else if ((strncasecmp(channel, line_buf, l) == 0)
  93. && (line_buf[l] == ':')) {
  94. *chan_no = lno;
  95. return line_buf;
  96. }
  97. };
  98. return NULL;
  99. }
  100. int parse(const char *fname, int list_channels, int chan_no, const char *channel,
  101. struct dvb_frontend_parameters *frontend, int *vpid, int *apid, int *sid)
  102. {
  103. FILE *f;
  104. char *chan;
  105. char *name, *inv, *fec, *mod;
  106. int ok;
  107. if ((f = fopen(fname, "r")) == NULL) {
  108. PERROR("could not open file '%s'", fname);
  109. return -1;
  110. }
  111. chan = find_channel(f, list_channels, &chan_no, channel);
  112. fclose(f);
  113. if (list_channels)
  114. return 0;
  115. if (!chan) {
  116. ERROR("could not find channel '%s' in channel list",
  117. channel);
  118. return -2;
  119. }
  120. printf("%3d %s", chan_no, chan);
  121. if ((sscanf(chan, "%m[^:]:%d:%m[^:]:%d:%m[^:]:%m[^:]:%d:%d:%d\n",
  122. &name, &frontend->frequency,
  123. &inv, &frontend->u.qam.symbol_rate,
  124. &fec, &mod, vpid, apid, sid) != 9)
  125. || !name || !inv || !fec | !mod) {
  126. ERROR("cannot parse service data");
  127. return -3;
  128. }
  129. frontend->inversion = parse_param(inv, inversion_list, LIST_SIZE(inversion_list), &ok);
  130. if (!ok) {
  131. ERROR("inversion field syntax '%s'", inv);
  132. return -4;
  133. }
  134. frontend->u.qam.fec_inner = parse_param(fec, fec_list, LIST_SIZE(fec_list), &ok);
  135. if (!ok) {
  136. ERROR("FEC field syntax '%s'", fec);
  137. return -5;
  138. }
  139. frontend->u.qam.modulation = parse_param(mod, modulation_list,
  140. LIST_SIZE(modulation_list), &ok);
  141. if (!ok) {
  142. ERROR("modulation field syntax '%s'", mod);
  143. return -6;
  144. }
  145. printf("%3d %s: f %d, s %d, i %d, fec %d, qam %d, v %#x, a %#x, s %#x \n",
  146. chan_no, name, frontend->frequency, frontend->u.qam.symbol_rate,
  147. frontend->inversion, frontend->u.qam.fec_inner,
  148. frontend->u.qam.modulation, *vpid, *apid, *sid);
  149. free(name);
  150. free(inv);
  151. free(fec);
  152. free(mod);
  153. return 0;
  154. }
  155. static int setup_frontend(int fe_fd, struct dvb_frontend_parameters *frontend)
  156. {
  157. int ret;
  158. uint32_t mstd;
  159. if (check_frontend(fe_fd, FE_QAM, &mstd) < 0) {
  160. close(fe_fd);
  161. return -1;
  162. }
  163. ret = dvbfe_set_delsys(fe_fd, SYS_DVBC_ANNEX_A);
  164. if (ret) {
  165. PERROR("SET Delsys failed");
  166. return -1;
  167. }
  168. if (ioctl(fe_fd, FE_SET_FRONTEND, frontend) < 0) {
  169. PERROR ("ioctl FE_SET_FRONTEND failed");
  170. return -1;
  171. }
  172. return 0;
  173. }
  174. static
  175. int monitor_frontend (int fe_fd, int human_readable)
  176. {
  177. fe_status_t status;
  178. uint16_t snr, signal;
  179. uint32_t ber, uncorrected_blocks;
  180. do {
  181. ioctl(fe_fd, FE_READ_STATUS, &status);
  182. ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal);
  183. ioctl(fe_fd, FE_READ_SNR, &snr);
  184. ioctl(fe_fd, FE_READ_BER, &ber);
  185. ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks);
  186. if (human_readable) {
  187. printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ",
  188. status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks);
  189. } else {
  190. printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ",
  191. status, signal, snr, ber, uncorrected_blocks);
  192. }
  193. if (status & FE_HAS_LOCK)
  194. printf("FE_HAS_LOCK");
  195. usleep(1000000);
  196. printf("\n");
  197. if (exit_after_tuning && (status & FE_HAS_LOCK))
  198. break;
  199. } while (1);
  200. return 0;
  201. }
  202. static const char *usage =
  203. "\nusage: %s [options] -l\n"
  204. " list known channels\n"
  205. " %s [options] {-n channel-number|channel_name}\n"
  206. " zap to channel via number or full name (case insensitive)\n"
  207. " -a number : use given adapter (default 0)\n"
  208. " -f number : use given frontend (default 0)\n"
  209. " -d number : use given demux (default 0)\n"
  210. " -c file : read channels list from 'file'\n"
  211. " -x : exit after tuning\n"
  212. " -H : human readable output\n"
  213. " -r : set up /dev/dvb/adapterX/dvr0 for TS recording\n"
  214. " -p : add pat and pmt to TS recording (implies -r)\n";
  215. int main(int argc, char **argv)
  216. {
  217. struct dvb_frontend_parameters frontend_param;
  218. char *homedir = getenv("HOME");
  219. char *confname = NULL;
  220. char *channel = NULL;
  221. int adapter = 0, frontend = 0, demux = 0, dvr = 0;
  222. int vpid, apid, sid, pmtpid = 0;
  223. int frontend_fd, video_fd, audio_fd, pat_fd, pmt_fd;
  224. int opt, list_channels = 0, chan_no = 0;
  225. int human_readable = 0, rec_psi = 0;
  226. while ((opt = getopt(argc, argv, "Hln:hrn:a:f:d:c:x:p")) != -1) {
  227. switch (opt) {
  228. case 'a':
  229. adapter = strtoul(optarg, NULL, 0);
  230. break;
  231. case 'f':
  232. frontend = strtoul(optarg, NULL, 0);
  233. break;
  234. case 'd':
  235. demux = strtoul(optarg, NULL, 0);
  236. break;
  237. case 'r':
  238. dvr = 1;
  239. break;
  240. case 'l':
  241. list_channels = 1;
  242. break;
  243. case 'n':
  244. chan_no = strtoul(optarg, NULL, 0);
  245. break;
  246. case 'p':
  247. rec_psi = 1;
  248. break;
  249. case 'x':
  250. exit_after_tuning = 1;
  251. break;
  252. case 'H':
  253. human_readable = 1;
  254. break;
  255. case 'c':
  256. confname = optarg;
  257. break;
  258. case '?':
  259. case 'h':
  260. default:
  261. fprintf (stderr, usage, argv[0], argv[0]);
  262. return -1;
  263. };
  264. }
  265. if (optind < argc)
  266. channel = argv[optind];
  267. if (!channel && chan_no <= 0 && !list_channels) {
  268. fprintf (stderr, usage, argv[0], argv[0]);
  269. return -1;
  270. }
  271. if (!homedir)
  272. ERROR("$HOME not set");
  273. snprintf (FRONTEND_DEV, sizeof(FRONTEND_DEV),
  274. "/dev/dvb/adapter%i/frontend%i", adapter, frontend);
  275. snprintf (DEMUX_DEV, sizeof(DEMUX_DEV),
  276. "/dev/dvb/adapter%i/demux%i", adapter, demux);
  277. printf ("using '%s' and '%s'\n", FRONTEND_DEV, DEMUX_DEV);
  278. if (!confname)
  279. {
  280. int len = strlen(homedir) + strlen(CHANNEL_FILE) + 18;
  281. if (!homedir)
  282. ERROR("$HOME not set");
  283. confname = malloc(len);
  284. snprintf(confname, len, "%s/.czap/%i/%s",
  285. homedir, adapter, CHANNEL_FILE);
  286. if (access(confname, R_OK))
  287. snprintf(confname, len, "%s/.czap/%s",
  288. homedir, CHANNEL_FILE);
  289. }
  290. printf("reading channels from file '%s'\n", confname);
  291. memset(&frontend_param, 0, sizeof(struct dvb_frontend_parameters));
  292. if (parse(confname, list_channels, chan_no, channel, &frontend_param, &vpid, &apid, &sid))
  293. return -1;
  294. if (list_channels)
  295. return 0;
  296. if ((frontend_fd = open(FRONTEND_DEV, O_RDWR | O_NONBLOCK)) < 0) {
  297. PERROR("failed opening '%s'", FRONTEND_DEV);
  298. return -1;
  299. }
  300. if (setup_frontend(frontend_fd, &frontend_param) < 0)
  301. return -1;
  302. if (rec_psi) {
  303. pmtpid = get_pmt_pid(DEMUX_DEV, sid);
  304. if (pmtpid <= 0) {
  305. fprintf(stderr,"couldn't find pmt-pid for sid %04x\n",sid);
  306. return -1;
  307. }
  308. if ((pat_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  309. perror("opening pat demux failed");
  310. return -1;
  311. }
  312. if (set_pesfilter(pat_fd, 0, DMX_PES_OTHER, dvr) < 0)
  313. return -1;
  314. if ((pmt_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  315. perror("opening pmt demux failed");
  316. return -1;
  317. }
  318. if (set_pesfilter(pmt_fd, pmtpid, DMX_PES_OTHER, dvr) < 0)
  319. return -1;
  320. }
  321. if ((video_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  322. PERROR("failed opening '%s'", DEMUX_DEV);
  323. return -1;
  324. }
  325. if (set_pesfilter (video_fd, vpid, DMX_PES_VIDEO, dvr) < 0)
  326. return -1;
  327. if ((audio_fd = open(DEMUX_DEV, O_RDWR)) < 0) {
  328. PERROR("failed opening '%s'", DEMUX_DEV);
  329. return -1;
  330. }
  331. if (set_pesfilter (audio_fd, apid, DMX_PES_AUDIO, dvr) < 0)
  332. return -1;
  333. monitor_frontend (frontend_fd, human_readable);
  334. close (pat_fd);
  335. close (pmt_fd);
  336. close (audio_fd);
  337. close (video_fd);
  338. close (frontend_fd);
  339. return 0;
  340. }