| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- /*
- * 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;
- }
|