Exemple #1
0
int dvb_fe_diseqc_cmd(struct dvb_v5_fe_parms *p, const unsigned len,
		      const unsigned char *buf)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	struct dvb_diseqc_master_cmd msg;
	int rc;

	if (len > 6)
		return -EINVAL;

	msg.msg_len = len;
	memcpy(msg.msg, buf, len);

	if (parms->p.verbose) {
		int i;
		char log[len * 3 + 20], *p = log;

		p += sprintf(p, _("DiSEqC command: "));
		for (i = 0; i < len; i++)
			p += sprintf (p, "%02x ", buf[i]);
		dvb_log("%s", log);
	}

	rc = xioctl(parms->fd, FE_DISEQC_SEND_MASTER_CMD, &msg);
	if (rc == -1)
		dvb_perror("FE_DISEQC_SEND_MASTER_CMD");
	return rc;
}
Exemple #2
0
int dvb_fe_diseqc_reply(struct dvb_v5_fe_parms *p, unsigned *len, char *buf,
		       int timeout)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	struct dvb_diseqc_slave_reply reply;
	int rc;

	if (*len > 4)
		*len = 4;

	reply.timeout = timeout;
	reply.msg_len = *len;

	if (parms->p.verbose)
		dvb_log("DiSEqC FE_DISEQC_RECV_SLAVE_REPLY");

	rc = xioctl(parms->fd, FE_DISEQC_RECV_SLAVE_REPLY, reply);
	if (rc == -1) {
		dvb_perror("FE_DISEQC_RECV_SLAVE_REPLY");
		return rc;
	}

	*len = reply.msg_len;
	memcpy(buf, reply.msg, reply.msg_len);

	return 0;
}
Exemple #3
0
int dvb_fe_get_event(struct dvb_v5_fe_parms *p)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	struct dvb_frontend_event event;
	fe_status_t status;
	int i;

	if (!parms->p.legacy_fe) {
		dvb_fe_get_parms(&parms->p);
		return dvb_fe_get_stats(&parms->p);
	}

	if (xioctl(parms->fd, FE_GET_EVENT, &event) == -1) {
		dvb_perror("FE_GET_EVENT");
		return errno;
	}
	status = event.status;
	if (parms->p.verbose > 1) {
		dvb_log(_("Status: "));
		for (i = 0; i < ARRAY_SIZE(fe_status_name); i++) {
			if (status & fe_status_name[i].idx)
				dvb_log ("    %s", fe_status_name[i].name);
		}
	}
	dvb_fe_store_stats(parms, DTV_STATUS, FE_SCALE_RELATIVE, 0, status);

	dvb_fe_retrieve_parm(&parms->p, DTV_FREQUENCY, &event.parameters.frequency);
	dvb_fe_retrieve_parm(&parms->p, DTV_INVERSION, &event.parameters.inversion);
	switch (parms->p.current_sys) {
	case SYS_DVBS:
		dvb_fe_retrieve_parm(&parms->p, DTV_SYMBOL_RATE, &event.parameters.u.qpsk.symbol_rate);
		dvb_fe_retrieve_parm(&parms->p, DTV_INNER_FEC, &event.parameters.u.qpsk.fec_inner);
		break;
	case SYS_DVBC_ANNEX_AC:
		dvb_fe_retrieve_parm(&parms->p, DTV_SYMBOL_RATE, &event.parameters.u.qam.symbol_rate);
		dvb_fe_retrieve_parm(&parms->p, DTV_INNER_FEC, &event.parameters.u.qam.fec_inner);
		dvb_fe_retrieve_parm(&parms->p, DTV_MODULATION, &event.parameters.u.qam.modulation);
		break;
	case SYS_ATSC:
	case SYS_ATSCMH:
	case SYS_DVBC_ANNEX_B:
		dvb_fe_retrieve_parm(&parms->p, DTV_MODULATION, &event.parameters.u.vsb.modulation);
		break;
	case SYS_DVBT:
		dvb_fe_retrieve_parm(&parms->p, DTV_BANDWIDTH_HZ, &event.parameters.u.ofdm.bandwidth);
		dvb_fe_retrieve_parm(&parms->p, DTV_CODE_RATE_HP, &event.parameters.u.ofdm.code_rate_HP);
		dvb_fe_retrieve_parm(&parms->p, DTV_CODE_RATE_LP, &event.parameters.u.ofdm.code_rate_LP);
		dvb_fe_retrieve_parm(&parms->p, DTV_MODULATION, &event.parameters.u.ofdm.constellation);
		dvb_fe_retrieve_parm(&parms->p, DTV_TRANSMISSION_MODE, &event.parameters.u.ofdm.transmission_mode);
		dvb_fe_retrieve_parm(&parms->p, DTV_GUARD_INTERVAL, &event.parameters.u.ofdm.guard_interval);
		dvb_fe_retrieve_parm(&parms->p, DTV_HIERARCHY, &event.parameters.u.ofdm.hierarchy_information);
		break;
	default:
		return EINVAL;
	}

	return dvb_fe_get_stats(&parms->p);
}
Exemple #4
0
int dvb_fe_sec_tone(struct dvb_v5_fe_parms *p, fe_sec_tone_mode_t tone)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	int rc;
	if (parms->p.verbose)
		dvb_log( _("DiSEqC TONE: %s"), fe_tone_name[tone] );
	rc = xioctl(parms->fd, FE_SET_TONE, tone);
	if (rc == -1)
		dvb_perror("FE_SET_TONE");
	return rc;
}
Exemple #5
0
int dvb_fe_lnb_high_voltage(struct dvb_v5_fe_parms *p, int on)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	int rc;

	if (on) on = 1;
	if (parms->p.verbose)
		dvb_log( _("DiSEqC HIGH LNB VOLTAGE: %s"), on ? _("ON") : _("OFF") );
	rc = xioctl(parms->fd, FE_ENABLE_HIGH_LNB_VOLTAGE, on);
	if (rc == -1)
		dvb_perror("FE_ENABLE_HIGH_LNB_VOLTAGE");
	return rc;
}
Exemple #6
0
int dvb_fe_diseqc_burst(struct dvb_v5_fe_parms *p, int mini_b)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	fe_sec_mini_cmd_t mini;
	int rc;

	mini = mini_b ? SEC_MINI_B : SEC_MINI_A;

	if (parms->p.verbose)
		dvb_log( _("DiSEqC BURST: %s"), mini_b ? "SEC_MINI_B" : "SEC_MINI_A" );
	rc = xioctl(parms->fd, FE_DISEQC_SEND_BURST, mini);
	if (rc == -1)
		dvb_perror("FE_DISEQC_SEND_BURST");
	return rc;
}
Exemple #7
0
int dvb_fe_sec_voltage(struct dvb_v5_fe_parms *p, int on, int v18)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	fe_sec_voltage_t v;
	int rc;

	if (!on) {
		v = SEC_VOLTAGE_OFF;
		if (parms->p.verbose)
			dvb_log(_("DiSEqC VOLTAGE: OFF"));
	} else {
		v = v18 ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13;
		if (parms->p.verbose)
			dvb_log(_("DiSEqC VOLTAGE: %s"), v18 ? "18" : "13");
	}
	rc = xioctl(parms->fd, FE_SET_VOLTAGE, v);
	if (rc == -1)
		dvb_perror("FE_SET_VOLTAGE");
	return rc;
}
void isdb_desc_partial_reception_init(struct dvb_v5_fe_parms *parms,
			      const uint8_t *buf, struct dvb_desc *desc)
{
	struct isdb_desc_partial_reception *d = (void *)desc;
	unsigned char *p = (unsigned char *)buf;
	size_t len;
	int i;

	d->partial_reception = malloc(d->length);
	if (!d->partial_reception) {
		dvb_perror("Out of memory!");
		return;
	}

	memcpy(d->partial_reception, p, d->length);

	len = d->length / sizeof(d->partial_reception);

	for (i = 0; i < len; i++)
		bswap16(d->partial_reception[i].service_id);
}
Exemple #9
0
int dvb_set_sys(struct dvb_v5_fe_parms *p, fe_delivery_system_t sys)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	struct dtv_property dvb_prop[1];
	struct dtv_properties prop;
	int rc;

	if (sys != parms->p.current_sys) {
		/* Disable LNBf power */
		if (dvb_fe_is_satellite(parms->p.current_sys) &&
		    !dvb_fe_is_satellite(sys))
			dvb_fe_sec_voltage(&parms->p, 0, 0);

		/* Can't change standard with the legacy FE support */
		if (parms->p.legacy_fe)
			return EINVAL;

		dvb_prop[0].cmd = DTV_DELIVERY_SYSTEM;
		dvb_prop[0].u.data = sys;
		prop.num = 1;
		prop.props = dvb_prop;

		if (xioctl(parms->fd, FE_SET_PROPERTY, &prop) == -1) {
			dvb_perror(_("Set delivery system"));
			return errno;
		}
	}

	rc = dvb_add_parms_for_sys(&parms->p, sys);
	if (rc < 0)
		return EINVAL;

	parms->p.current_sys = sys;
	parms->n_props = rc;

	return 0;
}
Exemple #10
0
int dvb_fe_set_parms(struct dvb_v5_fe_parms *p)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	/* Use a temporary copy of the parameters so we can safely perform
	 * adjustments for satellite */
	struct dvb_v5_fe_parms_priv tmp_parms = *parms;

	struct dtv_properties prop;
	struct dvb_frontend_parameters v3_parms;
	uint32_t bw;

	if (parms->p.lna != LNA_AUTO && !parms->p.legacy_fe) {
		struct dvb_v5_fe_parms_priv tmp_lna_parms;

		memset(&prop, 0, sizeof(prop));
		prop.props = tmp_lna_parms.dvb_prop;

		prop.props[0].cmd = DTV_LNA;
		prop.props[0].u.data = parms->p.lna;
		prop.num = 1;
		if (xioctl(parms->fd, FE_SET_PROPERTY, &prop) == -1) {
			dvb_perror(_("Setting LNA"));
			parms->p.lna = LNA_AUTO;
		} else if (parms->p.lna != LNA_AUTO && parms->p.verbose)
			dvb_logdbg(_("LNA is %s"), parms->p.lna ? _("ON") : _("OFF"));
	}

	if (dvb_fe_is_satellite(tmp_parms.p.current_sys)) {
		dvb_sat_set_parms(&tmp_parms.p);
		/*
		 * even though the frequncy prop is kept un-modified here,
		 * a later call to dvb_fe_get_parms() issues FE_GET_PROPERTY
		 * ioctl and overwrites it with the offset-ed value from
		 * the FE. So we need to save the offset here and
		 * re-add it in dvb_fe_get_parms().
		 * note that dvbv5-{scan,zap} utilities call dvb_fe_get_parms()
		 * indirectly from check_frontend() via dvb_fe_get_stats().
		 */
		parms->freq_offset = tmp_parms.freq_offset;
	}

	dvb_setup_delsys_default(p);

	/* Filter out any user DTV_foo property such as DTV_POLARIZATION */
	tmp_parms.n_props = dvb_copy_fe_props(tmp_parms.dvb_prop,
					      tmp_parms.n_props,
					      tmp_parms.dvb_prop);

	memset(&prop, 0, sizeof(prop));
	prop.props = tmp_parms.dvb_prop;
	prop.num = tmp_parms.n_props;
	prop.props[prop.num].cmd = DTV_TUNE;
	prop.num++;

	if (!parms->p.legacy_fe) {
		if (xioctl(parms->fd, FE_SET_PROPERTY, &prop) == -1) {
			dvb_perror("FE_SET_PROPERTY");
			if (parms->p.verbose)
				dvb_fe_prt_parms(&parms->p);
			return -1;
		}
		return 0;
	}
	/* DVBv3 call */
	dvb_fe_retrieve_parm(&tmp_parms.p, DTV_FREQUENCY, &v3_parms.frequency);
	dvb_fe_retrieve_parm(&tmp_parms.p, DTV_INVERSION, &v3_parms.inversion);
	switch (tmp_parms.p.current_sys) {
	case SYS_DVBS:
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_SYMBOL_RATE, &v3_parms.u.qpsk.symbol_rate);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_INNER_FEC, &v3_parms.u.qpsk.fec_inner);
		break;
	case SYS_DVBC_ANNEX_AC:
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_SYMBOL_RATE, &v3_parms.u.qam.symbol_rate);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_INNER_FEC, &v3_parms.u.qam.fec_inner);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_MODULATION, &v3_parms.u.qam.modulation);
		break;
	case SYS_ATSC:
	case SYS_ATSCMH:
	case SYS_DVBC_ANNEX_B:
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_MODULATION, &v3_parms.u.vsb.modulation);
		break;
	case SYS_DVBT:
		for (bw = 0; fe_bandwidth_name[bw] != 0; bw++) {
			if (fe_bandwidth_name[bw] == v3_parms.u.ofdm.bandwidth)
				break;
		}
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_BANDWIDTH_HZ, &bw);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_CODE_RATE_HP, &v3_parms.u.ofdm.code_rate_HP);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_CODE_RATE_LP, &v3_parms.u.ofdm.code_rate_LP);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_MODULATION, &v3_parms.u.ofdm.constellation);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_TRANSMISSION_MODE, &v3_parms.u.ofdm.transmission_mode);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_GUARD_INTERVAL, &v3_parms.u.ofdm.guard_interval);
		dvb_fe_retrieve_parm(&tmp_parms.p, DTV_HIERARCHY, &v3_parms.u.ofdm.hierarchy_information);
		break;
	default:
		return -1;
	}
	if (xioctl(tmp_parms.fd, FE_SET_FRONTEND, &v3_parms) == -1) {
		dvb_perror("FE_SET_FRONTEND");
		if (tmp_parms.p.verbose)
			dvb_fe_prt_parms(&tmp_parms.p);
		return -1;
	}

	return 0;
}
Exemple #11
0
int dvb_fe_get_parms(struct dvb_v5_fe_parms *p)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	int i, n = 0;
	const unsigned int *sys_props;
	struct dtv_properties prop;
	struct dvb_frontend_parameters v3_parms;
	uint32_t bw;

	sys_props = dvb_v5_delivery_system[parms->p.current_sys];
	if (!sys_props)
		return EINVAL;

	while (sys_props[n]) {
		parms->dvb_prop[n].cmd = sys_props[n];
		n++;
	}
	parms->dvb_prop[n].cmd = DTV_DELIVERY_SYSTEM;
	parms->dvb_prop[n].u.data = parms->p.current_sys;
	n++;

	/* Keep it ready for set */
	parms->dvb_prop[n].cmd = DTV_TUNE;
	parms->n_props = n;

	struct dtv_property fe_prop[DTV_MAX_COMMAND];
	n = dvb_copy_fe_props(parms->dvb_prop, n, fe_prop);

	prop.props = fe_prop;
	prop.num = n;
	if (!parms->p.legacy_fe) {
		if (xioctl(parms->fd, FE_GET_PROPERTY, &prop) == -1) {
			dvb_perror("FE_GET_PROPERTY");
			return errno;
		}

		/* copy back params from temporary fe_prop */
		for (i = 0; i < n; i++) {
			if (dvb_fe_is_satellite(p->current_sys)
			    && fe_prop[i].cmd == DTV_FREQUENCY)
				fe_prop[i].u.data += parms->freq_offset;
			dvb_fe_store_parm(&parms->p, fe_prop[i].cmd, fe_prop[i].u.data);
		}

		if (parms->p.verbose) {
			dvb_log(_("Got parameters for %s:"),
			       delivery_system_name[parms->p.current_sys]);
			dvb_fe_prt_parms(&parms->p);
		}
		return 0;
	}
	/* DVBv3 call */
	if (xioctl(parms->fd, FE_GET_FRONTEND, &v3_parms) == -1) {
		dvb_perror("FE_GET_FRONTEND");
		return EINVAL;
	}

	dvb_fe_store_parm(&parms->p, DTV_FREQUENCY, v3_parms.frequency);
	dvb_fe_store_parm(&parms->p, DTV_INVERSION, v3_parms.inversion);
	switch (parms->p.current_sys) {
	case SYS_DVBS:
		dvb_fe_store_parm(&parms->p, DTV_SYMBOL_RATE, v3_parms.u.qpsk.symbol_rate);
		dvb_fe_store_parm(&parms->p, DTV_INNER_FEC, v3_parms.u.qpsk.fec_inner);
		break;
	case SYS_DVBC_ANNEX_A:
		dvb_fe_store_parm(&parms->p, DTV_SYMBOL_RATE, v3_parms.u.qam.symbol_rate);
		dvb_fe_store_parm(&parms->p, DTV_INNER_FEC, v3_parms.u.qam.fec_inner);
		dvb_fe_store_parm(&parms->p, DTV_MODULATION, v3_parms.u.qam.modulation);
		break;
	case SYS_ATSC:
	case SYS_ATSCMH:
	case SYS_DVBC_ANNEX_B:
		dvb_fe_store_parm(&parms->p, DTV_MODULATION, v3_parms.u.vsb.modulation);
		break;
	case SYS_DVBT:
		if (v3_parms.u.ofdm.bandwidth < ARRAY_SIZE(fe_bandwidth_name) -1)
			bw = fe_bandwidth_name[v3_parms.u.ofdm.bandwidth];
		else bw = 0;
		dvb_fe_store_parm(&parms->p, DTV_BANDWIDTH_HZ, bw);
		dvb_fe_store_parm(&parms->p, DTV_CODE_RATE_HP, v3_parms.u.ofdm.code_rate_HP);
		dvb_fe_store_parm(&parms->p, DTV_CODE_RATE_LP, v3_parms.u.ofdm.code_rate_LP);
		dvb_fe_store_parm(&parms->p, DTV_MODULATION, v3_parms.u.ofdm.constellation);
		dvb_fe_store_parm(&parms->p, DTV_TRANSMISSION_MODE, v3_parms.u.ofdm.transmission_mode);
		dvb_fe_store_parm(&parms->p, DTV_GUARD_INTERVAL, v3_parms.u.ofdm.guard_interval);
		dvb_fe_store_parm(&parms->p, DTV_HIERARCHY, v3_parms.u.ofdm.hierarchy_information);
		break;
	default:
		return EINVAL;
	}

	return 0;
}
Exemple #12
0
int dvb_fe_get_stats(struct dvb_v5_fe_parms *p)
{
	struct dvb_v5_fe_parms_priv *parms = (void *)p;
	fe_status_t status = 0;
	uint32_t ber= 0, ucb = 0;
	uint16_t strength = 0, snr = 0;
	int i;
	enum fecap_scale_params scale;

	if (xioctl(parms->fd, FE_READ_STATUS, &status) == -1) {
		dvb_perror("FE_READ_STATUS");
		return EINVAL;
	}
	dvb_fe_store_stats(parms, DTV_STATUS, FE_SCALE_RELATIVE, 0, status);

	/* if lock has obtained, get DVB parameters */
	if (status != parms->stats.prev_status) {
		if ((status & FE_HAS_LOCK) &&
		    parms->stats.prev_status != status)
			dvb_fe_get_parms(&parms->p);
		parms->stats.prev_status = status;
	}

	if (parms->p.has_v5_stats) {
		struct dtv_properties props;

		props.num = DTV_NUM_KERNEL_STATS;
		props.props = parms->stats.prop;

		/* Do a DVBv5.10 stats call */
		if (ioctl(parms->fd, FE_GET_PROPERTY, &props) == -1) {
			if (errno == EAGAIN)
				return 0;
			goto dvbv3_fallback;
		}

		/*
		 * All props with len=0 mean that this device doesn't have any
		 * dvbv5 stats. Try the legacy stats instead.
		 */
		for (i = 0; i < props.num; i++)
			if (parms->stats.prop[i].u.st.len)
				break;
		if (i == props.num)
			goto dvbv3_fallback;

		dvb_fe_update_counters(parms);

		return 0;
	}

dvbv3_fallback:
	/* DVB v3 stats */
	parms->p.has_v5_stats = 0;

	if (ioctl(parms->fd, FE_READ_BER, &ber) == -1)
		scale = FE_SCALE_NOT_AVAILABLE;
	else
		scale = FE_SCALE_RELATIVE;

	/*
	 * BER scale on DVBv3 is not defined - different drivers use
	 * different scales, even weird ones, like multiples of 1/65280
	 */
	dvb_fe_store_stats(parms, DTV_BER, scale, 0, ber);

	if (ioctl(parms->fd, FE_READ_SIGNAL_STRENGTH, &strength) == -1)
		scale = FE_SCALE_NOT_AVAILABLE;
	else
		scale = FE_SCALE_RELATIVE;

	dvb_fe_store_stats(parms, DTV_STAT_SIGNAL_STRENGTH, scale, 0, strength);

	if (ioctl(parms->fd, FE_READ_SNR, &snr) == -1)
		scale = FE_SCALE_NOT_AVAILABLE;
	else
		scale = FE_SCALE_RELATIVE;
	dvb_fe_store_stats(parms, DTV_STAT_CNR, scale, 0, snr);

	if (ioctl(parms->fd, FE_READ_UNCORRECTED_BLOCKS, &ucb) == -1)
		scale = FE_SCALE_NOT_AVAILABLE;
	else
		scale = FE_SCALE_COUNTER;
	dvb_fe_store_stats(parms, DTV_STAT_ERROR_BLOCK_COUNT, scale, 0, snr);

	if (parms->p.verbose > 1) {
		dvb_log(_("Status: "));
		for (i = 0; i < ARRAY_SIZE(fe_status_name); i++) {
			if (status & fe_status_name[i].idx)
				dvb_log ("    %s", fe_status_name[i].name);
		}
		dvb_log(_("BER: %d, Strength: %d, SNR: %d, UCB: %d"),
		       ber, strength, snr, ucb);
	}
	return 0;
}
Exemple #13
0
struct dvb_v5_fe_parms *dvb_fe_open_flags(int adapter, int frontend,
					  unsigned verbose,
					  unsigned use_legacy_call,
					  dvb_logfunc logfunc,
					  int flags)
{
	int fd, i, r;
	char *fname;
	struct dtv_properties dtv_prop;
	struct dvb_v5_fe_parms_priv *parms = NULL;

	libdvbv5_initialize();

	if (logfunc == NULL)
		logfunc = dvb_default_log;

	r = asprintf(&fname, "/dev/dvb/adapter%i/frontend%i", adapter, frontend);
	if (r < 0) {
		logfunc(LOG_ERR, _("asprintf error"));
		return NULL;
	}
	if (!fname) {
		logfunc(LOG_ERR, _("fname calloc: %s"), strerror(errno));
		return NULL;
	}

	fd = open(fname, flags, 0);
	if (fd == -1) {
		logfunc(LOG_ERR, _("%s while opening %s"), strerror(errno), fname);
		free(fname);
		return NULL;
	}
	parms = calloc(sizeof(*parms), 1);
	if (!parms) {
		logfunc(LOG_ERR, _("parms calloc: %s"), strerror(errno));
		close(fd);
		free(fname);
		return NULL;
	}
	parms->fname = fname;
	parms->fd = fd;
	parms->fe_flags = flags;
	parms->p.verbose = verbose;
	parms->p.default_charset = "iso-8859-1";
	parms->p.output_charset = "utf-8";
	parms->p.logfunc = logfunc;
	parms->p.lna = LNA_AUTO;
	parms->p.sat_number = -1;
	parms->p.abort = 0;
	parms->country = COUNTRY_UNKNOWN;

	if (xioctl(fd, FE_GET_INFO, &parms->p.info) == -1) {
		dvb_perror("FE_GET_INFO");
		dvb_v5_free(parms);
		close(fd);
		free(fname);
		return NULL;
	}

	if (verbose) {
		fe_caps_t caps = parms->p.info.caps;

		dvb_log(_("Device %s (%s) capabilities:"),
			parms->p.info.name, fname);
		for (i = 0; i < ARRAY_SIZE(fe_caps_name); i++) {
			if (caps & fe_caps_name[i].idx)
				dvb_log ("     %s", fe_caps_name[i].name);
		}
	}

	parms->dvb_prop[0].cmd = DTV_API_VERSION;
	parms->dvb_prop[1].cmd = DTV_DELIVERY_SYSTEM;

	dtv_prop.num = 2;
	dtv_prop.props = parms->dvb_prop;

	/* Detect a DVBv3 device */
	if (xioctl(fd, FE_GET_PROPERTY, &dtv_prop) == -1) {
		parms->dvb_prop[0].u.data = 0x300;
		parms->dvb_prop[1].u.data = SYS_UNDEFINED;
	}

	parms->p.version = parms->dvb_prop[0].u.data;
	parms->p.current_sys = parms->dvb_prop[1].u.data;
	if (verbose)
		dvb_log (_("DVB API Version %d.%d%s, Current v5 delivery system: %s"),
			parms->p.version / 256,
			parms->p.version % 256,
			use_legacy_call ? _(" (forcing DVBv3 calls)") : "",
			delivery_system_name[parms->p.current_sys]);

	if (parms->p.version < 0x500)
		use_legacy_call = 1;

	if (parms->p.version >= 0x50a)
		parms->p.has_v5_stats = 1;
	else
		parms->p.has_v5_stats = 0;

	if (use_legacy_call || parms->p.version < 0x505) {
		parms->p.legacy_fe = 1;
		switch(parms->p.info.type) {
		case FE_QPSK:
			parms->p.current_sys = SYS_DVBS;
			parms->p.systems[parms->p.num_systems++] = parms->p.current_sys;
			if (parms->p.version < 0x0500)
				break;
			if (parms->p.info.caps & FE_CAN_2G_MODULATION)
				parms->p.systems[parms->p.num_systems++] = SYS_DVBS2;
			if (parms->p.info.caps & FE_CAN_TURBO_FEC)
				parms->p.systems[parms->p.num_systems++] = SYS_TURBO;
			break;
		case FE_QAM:
			parms->p.current_sys = SYS_DVBC_ANNEX_A;
			parms->p.systems[parms->p.num_systems++] = parms->p.current_sys;
			break;
		case FE_OFDM:
			parms->p.current_sys = SYS_DVBT;
			parms->p.systems[parms->p.num_systems++] = parms->p.current_sys;
			if (parms->p.version < 0x0500)
				break;
			if (parms->p.info.caps & FE_CAN_2G_MODULATION)
				parms->p.systems[parms->p.num_systems++] = SYS_DVBT2;
			break;
		case FE_ATSC:
			if (parms->p.info.caps & (FE_CAN_8VSB | FE_CAN_16VSB))
				parms->p.systems[parms->p.num_systems++] = SYS_ATSC;
			if (parms->p.info.caps & (FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO))
				parms->p.systems[parms->p.num_systems++] = SYS_DVBC_ANNEX_B;
			parms->p.current_sys = parms->p.systems[0];
			break;
		}
		if (!parms->p.num_systems) {
			dvb_logerr(_("delivery system not detected"));
			dvb_v5_free(parms);
			close(fd);
			return NULL;
		}
	} else {
		parms->dvb_prop[0].cmd = DTV_ENUM_DELSYS;
		parms->n_props = 1;
		dtv_prop.num = 1;
		dtv_prop.props = parms->dvb_prop;
		if (xioctl(fd, FE_GET_PROPERTY, &dtv_prop) == -1) {
			dvb_perror("FE_GET_PROPERTY");
			dvb_v5_free(parms);
			close(fd);
			return NULL;
		}
		parms->p.num_systems = parms->dvb_prop[0].u.buffer.len;
		for (i = 0; i < parms->p.num_systems; i++)
			parms->p.systems[i] = parms->dvb_prop[0].u.buffer.data[i];

		if (parms->p.num_systems == 0) {
			dvb_logerr(_("driver returned 0 supported delivery systems!"));
			dvb_v5_free(parms);
			close(fd);
			return NULL;
		}
	}

	if (verbose) {
		dvb_log(_("Supported delivery system%s: "),
		       (parms->p.num_systems > 1) ? "s" : "");
		for (i = 0; i < parms->p.num_systems; i++) {
			if (parms->p.systems[i] == parms->p.current_sys)
				dvb_log ("    [%s]",
					delivery_system_name[parms->p.systems[i]]);
			else
				dvb_log ("     %s",
					delivery_system_name[parms->p.systems[i]]);
		}
		if (use_legacy_call || parms->p.version < 0x505)
			dvb_log(_("Warning: new delivery systems like ISDB-T, ISDB-S, DMB-TH, DSS, ATSC-MH will be miss-detected by a DVBv5.4 or earlier API call"));
	}

	/*
	 * Fix a bug at some DVB drivers
	 */
	if (parms->p.current_sys == SYS_UNDEFINED)
		parms->p.current_sys = parms->p.systems[0];

	/* Prepare to use the delivery system */
	parms->n_props = dvb_add_parms_for_sys(&parms->p, parms->p.current_sys);

	if ((flags & O_ACCMODE) == O_RDWR)
		dvb_set_sys(&parms->p, parms->p.current_sys);

	/*
	 * Prepare the status struct - DVBv5.10 parameters should
	 * come first, as they'll be read together.
	 */
	parms->stats.prop[0].cmd = DTV_STAT_SIGNAL_STRENGTH;
	parms->stats.prop[1].cmd = DTV_STAT_CNR;
	parms->stats.prop[2].cmd = DTV_STAT_PRE_ERROR_BIT_COUNT;
	parms->stats.prop[3].cmd = DTV_STAT_PRE_TOTAL_BIT_COUNT;
	parms->stats.prop[4].cmd = DTV_STAT_POST_ERROR_BIT_COUNT;
	parms->stats.prop[5].cmd = DTV_STAT_POST_TOTAL_BIT_COUNT;
	parms->stats.prop[6].cmd = DTV_STAT_ERROR_BLOCK_COUNT;
	parms->stats.prop[7].cmd = DTV_STAT_TOTAL_BLOCK_COUNT;

	/* Now, status and the calculated stats */
	parms->stats.prop[8].cmd = DTV_STATUS;
	parms->stats.prop[9].cmd = DTV_BER;
	parms->stats.prop[10].cmd = DTV_PER;
	parms->stats.prop[11].cmd = DTV_QUALITY;
	parms->stats.prop[12].cmd = DTV_PRE_BER;

	return &parms->p;
}