diff options
author | Mark Purcell <msp@debian.org> | 2012-01-05 18:02:19 +1100 |
---|---|---|
committer | etobi <git@e-tobi.net> | 2013-09-03 09:48:50 +0200 |
commit | 1b69be1bfeb8a135b83d1b4950bc72cc2f866339 (patch) | |
tree | 0208563f86c100717fa8e9111bc32dea3be3439d /test/lock_s.c | |
parent | cad92a119d718c8bcd70e85865e08e66010f9347 (diff) | |
parent | 76c08672bc6c2984ebd7045a71862099890c9118 (diff) | |
download | linux-dvb-apps-44285667606636cccbe1f43c70dd856dab237b03.tar.gz |
Imported Debian patch 1.1.1+rev1457-1debian/1.1.1+rev1457-1
Diffstat (limited to '')
-rw-r--r-- | test/lock_s.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/test/lock_s.c b/test/lock_s.c new file mode 100644 index 0000000..7bbab59 --- /dev/null +++ b/test/lock_s.c @@ -0,0 +1,531 @@ +/* + * 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 <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 <ctype.h> + +#include <stdint.h> +#include <sys/time.h> + +#include <linux/dvb/frontend.h> + +#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 <lnb-type> or -l low[,high[,switch]] in Mhz\nwhere <lnb-type> 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; +} |