/* * lock_s - Ultra simple DVB-S lock test application * A minimal lock test application derived from szap.c * for testing purposes * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FEDEVICE "/dev/dvb/adapter%d/frontend%d" static char *usage_str = "\n lock_s ver:0.0.1: Simple utility to test DVB-S signal lock.\n" " \n" " usage: lock_s [[PARAMS] [OPTIONS]]\n" " dvbstune [params] tune to user provided DVB-S parameters\n" " -a number : use given adapter (default 0)\n" " -f number : use given frontend (default 0)\n" " -p params : parameters to be used for tuning\n" " frequency (Mhz) Polarization (v/h) Symbol rate (MSPS)\n" " \n" " -s pos : DiSEqC switch position\n" " -H : human readable output\n" " -l lnb-type (DVB-S Only) (use -l help to print types) or \n" " -l low[,high[,switch]] in Mhz\n"; static char *univ_desc[] = { "Europe", "10800 to 11800 MHz and 11600 to 12700 Mhz", "Dual LO, loband 9750, hiband 10600 MHz", (char *)NULL }; static char *dbs_desc[] = { "Expressvu, North America", "12200 to 12700 MHz", "Single LO, 11250 MHz", (char *)NULL }; static char *standard_desc[] = { "10945 to 11450 Mhz", "Single LO, 10000 Mhz", (char *)NULL }; static char *enhan_desc[] = { "Astra", "10700 to 11700 MHz", "Single LO, 9750 MHz", (char *)NULL }; static char *cband_desc[] = { "Big Dish", "3700 to 4200 MHz", "Single LO, 5150 Mhz", (char *)NULL }; enum sec_bands { SEC_LO_BAND = 0, SEC_HI_BAND = 1, }; struct sec_params { unsigned int freq; enum fe_sec_voltage voltage; unsigned int srate; unsigned int freq_if; int pos; /* DiSEqC sw. pos */ fe_sec_tone_mode_t tone; fe_sec_mini_cmd_t burst; }; struct lnb_types_st { char *name; char **desc; unsigned long low_val; unsigned long high_val; /* zero indicates no hiband */ unsigned long switch_val; /* zero indicates no hiband */ } lnbs[] = { {"UNIVERSAL", univ_desc, 9750, 10600, 11700 }, {"DBS", dbs_desc, 11250, 0, 0 }, {"STANDARD", standard_desc, 10000, 0, 0 }, {"ENHANCED", enhan_desc, 9750, 0, 0 }, {"C-BAND", cband_desc, 5150, 0, 0 }, }; struct diseqc_cmd { struct dvb_diseqc_master_cmd cmd; uint32_t wait; }; int lnb_parse(char *str, struct lnb_types_st *lnbp) { int i; char *cp, *np; memset(lnbp, 0, sizeof (*lnbp)); cp = str; while (*cp && isspace(*cp)) cp++; if (isalpha(*cp)) { for (i = 0; i < (int)(sizeof(lnbs) / sizeof(lnbs[0])); i++) { if (!strcasecmp(lnbs[i].name, cp)) { *lnbp = lnbs[i]; return 1; } } return -1; } if (*cp == '\0' || !isdigit(*cp)) return -1; lnbp->low_val = strtoul(cp, &np, 0); if (lnbp->low_val == 0) return -1; cp = np; while(*cp && (isspace(*cp) || *cp == ',')) cp++; if (*cp == '\0') return 1; if (!isdigit(*cp)) return -1; lnbp->high_val = strtoul(cp, &np, 0); cp = np; while(*cp && (isspace(*cp) || *cp == ',')) cp++; if (*cp == '\0') return 1; if (!isdigit(*cp)) return -1; lnbp->switch_val = strtoul(cp, NULL, 0); return 1; } int params_decode(char *str, char **argv, struct sec_params *params) { unsigned int freq; char *pol; char *cp, *np; cp = str; while (*cp && isspace(*cp)) cp++; /* get frequency */ if (*cp == '\0' || !isdigit(*cp)) return -1; freq = strtoul(cp, &np, 0); params->freq = freq; if (freq == 0) return -1; /* polarization v=13v:0, h=18v:0 */ pol = argv[optind]; /* v/h */ (!strncmp(pol, "v", 1)) ? (params->voltage = 0) : (params->voltage = 1); params->srate = strtoul(argv[optind + 1], NULL, 0); return 1; } /** * lnb_setup(struct lnb_types_st *lnb, unsigned int freq, unsigned int *freq_if) * @lnb : lnb type as described int lnb_types_st * @freq : transponder frequency which user requests * @freq_if : resultant Intermediate Frequency after down conversion */ static int lnb_setup(struct lnb_types_st *lnb, struct sec_params *params) { int ret = 0; if (!lnb) { fprintf(stderr, "Error: lnb_types=NULL\n"); ret = -EINVAL; goto err; } /* TODO! check upper and lower limits from LNB types */ if (!params->freq) { fprintf(stderr, "Error: invalid frequency, f:%d", params->freq); ret = -EINVAL; goto err; } if (lnb->switch_val && lnb->high_val && params->freq >= lnb->switch_val) { /* HI Band */ params->freq_if = params->freq - lnb->high_val; params->tone = SEC_TONE_ON; } else { /* LO Band */ params->tone = SEC_TONE_OFF; if (params->freq < lnb->low_val) params->freq_if = lnb->low_val - params->freq; else params->freq_if = params->freq - lnb->low_val; } err: return ret; } static int diseqc_send_msg(int fd, struct sec_params *params, struct diseqc_cmd *cmd) { int ret = 0; ret = ioctl(fd, FE_SET_TONE, SEC_TONE_OFF); if (ret < 0) { fprintf(stderr, "FE_SET_TONE failed\n"); goto err; } ret = ioctl(fd, FE_SET_VOLTAGE, params->voltage); if (ret < 0) { fprintf(stderr, "FE_SET_VOLTAGE failed, voltage=%d\n", params->voltage); usleep(15 * 1000); goto err; } ret = ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd); if (ret < 0) { perror("FE_DISEQC_SEND_MASTER_CMD failed"); usleep(cmd->wait * 1000); usleep(15 * 1000); goto err; } ret = ioctl(fd, FE_DISEQC_SEND_BURST, params->burst); if (ret < 0) { fprintf(stderr, "FE_DISEQC_SEND_BURST failed, burst=%d\n", params->burst); usleep(15 * 1000); goto err; } ret = ioctl(fd, FE_SET_TONE, params->tone); if (ret < 0) { fprintf(stderr, "FE_SET_TONE failed, tone=%d\n", params->tone); goto err; } err: return ret; } static int diseqc_setup(int fd, struct sec_params *params) { int pos = params->pos; int band = params->tone; fe_sec_voltage_t voltage = params->voltage; int ret; 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 | (((pos * 4) & 0x0f) | (band ? 1 : 0) | (voltage ? 0 : 2)); ret = diseqc_send_msg(fd, params, &cmd); if (ret < 0) { fprintf(stderr, "SEC message send failed, err=%d", ret); return -EIO; } return 0; } static int tune_to(int fd, struct sec_params *sec) { struct dvb_frontend_parameters params; struct dvb_frontend_event ev; int ret; /* discard stale QPSK events */ while (1) { ret = ioctl(fd, FE_GET_EVENT, &ev); if (ret == -1) break; } params.frequency = sec->freq_if; params.inversion = INVERSION_AUTO; params.u.qpsk.symbol_rate = sec->srate; params.u.qpsk.fec_inner = FEC_AUTO; ret = ioctl(fd, FE_SET_FRONTEND, ¶ms); if (ret == -1) { fprintf(stderr, "FE_SET_FRONTEND error=%d\n", ret); return -1; } return 0; } static int frontend_open(int *fd, int adapter, int frontend) { static struct dvb_frontend_info info; char fedev[128]; int ret = 0; snprintf(fedev, sizeof(fedev), FEDEVICE, adapter, frontend); *fd = open(fedev, O_RDWR | O_NONBLOCK); if (*fd < 0) { fprintf(stderr, "Frontend %d open failed\n", frontend); ret = -1; goto err; } ret = ioctl(*fd, FE_GET_INFO, &info); if (ret < 0) { fprintf(stderr, "ioctl FE_GET_INFO failed\n"); goto err; } if (info.type != FE_QPSK) { fprintf(stderr, "frontend device is not a QPSK (DVB-S) device!\n"); ret = -ENODEV; goto err; } return 0; err: fprintf(stderr, "Closing Adapter:%d Frontend:%d\n", adapter, frontend); close(*fd); return ret; } void bad_usage(char *pname, int prlnb) { int i; struct lnb_types_st *lnb; char **cp; if (!prlnb) { fprintf (stderr, usage_str, pname); } else { i = 0; fprintf(stderr, "-l or -l low[,high[,switch]] in Mhz\nwhere is:\n"); while ((lnb = &lnbs[i]) != NULL) { fprintf (stderr, "%s\n", lnb->name); for (cp = lnb->desc; *cp; cp++) fprintf (stderr, " %s\n", *cp); i++; } } } static int check_frontend (int fd, int human_readable) { fe_status_t status; uint16_t snr, signal; uint32_t ber, uncorrected_blocks; int ret; do { ret = ioctl(fd, FE_READ_STATUS, &status); if (ret == -1) { fprintf(stderr, "FE_READ_STATUS failed, err=%d", ret); return -EIO; } /* * some frontends might not support all these ioctls, thus we * avoid printing errors */ ret = ioctl(fd, FE_READ_SIGNAL_STRENGTH, &signal); if (ret == -1) signal = -2; ret = ioctl(fd, FE_READ_SNR, &snr); if (ret == -1) snr = -2; ret = ioctl(fd, FE_READ_BER, &ber); if (ret == -1) ber = -2; ret = ioctl(fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks); if (ret == -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"); usleep(1000000); } while (1); return 0; } /** * basic options needed * @adapter * @frontend * @freq (Mhz) * @pol (13/18) * @srate (kSps) * @pos (0 - 3) * @LNB (low, high, switch) Mhz * */ int main(int argc, char *argv[]) { int opt; int ret, fd, adapter=0, frontend=0, pos; // int simple; struct lnb_types_st lnb; struct sec_params sec; lnb = lnbs[0]; /* default is Universal LNB */ while ((opt = getopt(argc, argv, "Hh:a:f:p:s:l:")) != -1) { switch (opt) { case '?': case 'h': default: /* help */ bad_usage(argv[0], 0); break; case 'a': /* adapter */ adapter = strtoul(optarg, NULL, 0); break; case 'f': /* demodulator (device) */ frontend = strtoul(optarg, NULL, 0); break; case 'p': /* parameters */ if (params_decode(optarg, argv, &sec) < 0) { bad_usage(argv[0], 1); return -1; } break; case 's': /* diseqc position */ pos = strtoul(optarg, NULL, 0); pos % 2 ? (sec.burst = SEC_MINI_B) : (sec.burst = SEC_MINI_A); break; case 'l': if (lnb_parse(optarg, &lnb) < 0) { bad_usage(argv[0], 1); return -1; } break; case 'H': // simple = 1; /* human readable */ break; } } lnb.low_val *= 1000; /* kHz */ lnb.high_val *= 1000; /* kHz */ lnb.switch_val *= 1000; /* kHz */ sec.freq *= 1000; /* kHz */ sec.srate *= 1000; ret = frontend_open(&fd, adapter, frontend); if (ret < 0) { fprintf(stderr, "Adapter:%d Frontend:%d open failed, err=%d\n", adapter, frontend, ret); return -1; } ret = lnb_setup(&lnb, &sec); if (ret < 0) { fprintf(stderr, "LNB setup failed, err=%d\n", ret); fprintf(stderr, "%s", usage_str); goto err; } ret = diseqc_setup(fd, &sec); if (ret < 0) { fprintf(stderr, "SEC setup failed, err=%d\n", ret); goto err;; } ret = tune_to(fd, &sec); if (ret < 0) { fprintf(stderr, "Adapter:%d Frontend:%d tune_to %d %d failed, err=%d\n", adapter, frontend, sec.freq, sec.srate, ret); goto err;; } ret = check_frontend(fd, 1); if (ret < 0) { fprintf(stderr, "check frontend failed\n"); goto err;; } err: return ret; }