Newer
Older
/*
* dvb_frontend.c: DVB frontend tuning interface/thread
*
*
* Copyright (C) 1999-2001 Ralph Metzler
* Marcus Metzler
* Holger Waechtler
* for convergence integrated media GmbH
*
* Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
/* Enables DVBv3 compatibility bits at the headers */
#define __DVB_CORE__
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/semaphore.h>
#include <linux/freezer.h>
#include <linux/jiffies.h>
#include <linux/kthread.h>
#include <asm/processor.h>
#include "dvb_frontend.h"
#include "dvbdev.h"
#include <linux/dvb/version.h>
static int dvb_shutdown_timeout;
static int dvb_force_auto_inversion;
static int dvb_override_tune_delay;
static int dvb_powerdown_on_sleep = 1;
static int dvb_mfe_wait_time = 5;
module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off).");
module_param(dvb_shutdown_timeout, int, 0644);
MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
module_param(dvb_force_auto_inversion, int, 0644);
MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
module_param(dvb_override_tune_delay, int, 0644);
MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
module_param(dvb_powerdown_on_sleep, int, 0644);
MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)");
module_param(dvb_mfe_wait_time, int, 0644);
MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open() for multi-frontend to become available (default:5 seconds)");
#define FESTATE_IDLE 1
#define FESTATE_RETUNE 2
#define FESTATE_TUNING_FAST 4
#define FESTATE_TUNING_SLOW 8
#define FESTATE_TUNED 16
#define FESTATE_ZIGZAG_FAST 32
#define FESTATE_ZIGZAG_SLOW 64
#define FESTATE_DISEQC 128
#define FESTATE_ERROR 256
#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
/*
* FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
* FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
* FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
* FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
* FESTATE_TUNED. The frontend has successfully locked on.
* FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
* FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
* FESTATE_DISEQC. A DISEQC command has just been issued.
* FESTATE_WAITFORLOCK. When we're waiting for a lock.
* FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
* FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
* FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
*/
#define DVB_FE_NO_EXIT 0
#define DVB_FE_NORMAL_EXIT 1
#define DVB_FE_DEVICE_REMOVED 2
static DEFINE_MUTEX(frontend_mutex);

Andrew de Quincey
committed
/* thread/frontend values */
struct dvb_frontend_parameters parameters_out;
struct dvb_fe_events events;
struct semaphore sem;
struct list_head list_head;
wait_queue_head_t wait_queue;
struct task_struct *thread;

Andrew de Quincey
committed
unsigned int exit;
unsigned int wakeup;
unsigned long tune_mode_flags;

Andrew de Quincey
committed
unsigned int delay;

Andrew de Quincey
committed
int tone;
int voltage;

Andrew de Quincey
committed
/* swzigzag values */
unsigned int state;
unsigned int bending;
int lnb_drift;
unsigned int inversion;
unsigned int auto_step;
unsigned int auto_sub_step;
unsigned int started_auto_step;
unsigned int min_delay;
unsigned int max_drift;
unsigned int step_size;
int quality;
unsigned int check_wrapped;
static void dvb_frontend_wakeup(struct dvb_frontend *fe);
static int dtv_get_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p_out);
static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p);
static bool has_get_frontend(struct dvb_frontend *fe)
{
return fe->ops.get_frontend != NULL;
}
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
* Due to DVBv3 API calls, a delivery system should be mapped into one of
* the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC),
* otherwise, a DVBv3 call will fail.
*/
enum dvbv3_emulation_type {
DVBV3_UNKNOWN,
DVBV3_QPSK,
DVBV3_QAM,
DVBV3_OFDM,
DVBV3_ATSC,
};
static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system)
{
switch (delivery_system) {
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
return DVBV3_QAM;
case SYS_DVBS:
case SYS_DVBS2:
case SYS_TURBO:
case SYS_ISDBS:
case SYS_DSS:
return DVBV3_QPSK;
case SYS_DVBT:
case SYS_DVBT2:
case SYS_ISDBT:
return DVBV3_OFDM;
case SYS_ATSC:
case SYS_DVBC_ANNEX_B:
return DVBV3_ATSC;
case SYS_UNDEFINED:
case SYS_ISDBC:
case SYS_DVBH:
case SYS_DAB:
default:
/*
* Doesn't know how to emulate those types and/or
* there's no frontend driver from this type yet
* with some emulation code, so, we're not sure yet how
* to handle them, or they're not compatible with a DVBv3 call.
*/
return DVBV3_UNKNOWN;
}
}
static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
struct dvb_fe_events *events = &fepriv->events;
struct dvb_frontend_event *e;
int wp;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if ((status & FE_HAS_LOCK) && has_get_frontend(fe))
dtv_get_frontend(fe, &fepriv->parameters_out);

Andreas Oberritter
committed
mutex_lock(&events->mtx);

Andreas Oberritter
committed
wp = (events->eventw + 1) % MAX_EVENT;
if (wp == events->eventr) {
events->overflow = 1;
events->eventr = (events->eventr + 1) % MAX_EVENT;
}
e = &events->events[events->eventw];

Andreas Oberritter
committed
e->status = status;
e->parameters = fepriv->parameters_out;

Matthias Kaehlcke
committed
mutex_unlock(&events->mtx);
wake_up_interruptible (&events->wait_queue);
}
static int dvb_frontend_get_event(struct dvb_frontend *fe,
struct dvb_frontend_event *event, int flags)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if (events->overflow) {
events->overflow = 0;
return -EOVERFLOW;
}
if (events->eventw == events->eventr) {
int ret;
if (flags & O_NONBLOCK)
return -EWOULDBLOCK;
up(&fepriv->sem);
ret = wait_event_interruptible (events->wait_queue,
events->eventw != events->eventr);
if (down_interruptible (&fepriv->sem))
return -ERESTARTSYS;
if (ret < 0)
return ret;
}

Andreas Oberritter
committed
mutex_lock(&events->mtx);
*event = events->events[events->eventr];

Matthias Kaehlcke
committed
mutex_unlock(&events->mtx);
static void dvb_frontend_clear_events(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
struct dvb_fe_events *events = &fepriv->events;
mutex_lock(&events->mtx);
events->eventr = events->eventw;
mutex_unlock(&events->mtx);
}
static void dvb_frontend_init(struct dvb_frontend *fe)
{
dev_dbg(fe->dvb->device,
"%s: initialising adapter %i frontend %i (%s)...\n",
__func__, fe->dvb->num, fe->id, fe->ops.info.name);

Patrick Boettcher
committed
if (fe->ops.init)
fe->ops.init(fe);
if (fe->ops.tuner_ops.init) {
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);

Patrick Boettcher
committed
fe->ops.tuner_ops.init(fe);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
void dvb_frontend_reinitialise(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
fepriv->reinitialise = 1;
dvb_frontend_wakeup(fe);
}
EXPORT_SYMBOL(dvb_frontend_reinitialise);

Andrew de Quincey
committed
static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked)

Andrew de Quincey
committed
int q2;
struct dvb_frontend *fe = fepriv->dvbdev->priv;
dev_dbg(fe->dvb->device, "%s:\n", __func__);

Andrew de Quincey
committed
if (locked)
(fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256;
else
(fepriv->quality) = (fepriv->quality * 220 + 0) / 256;

Andrew de Quincey
committed
q2 = fepriv->quality - 128;
q2 *= q2;

Andrew de Quincey
committed
fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128);
}
/**
* Performs automatic twiddling of frontend parameters.
*
* @param fe The frontend concerned.
* @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT
* @returns Number of complete iterations that have been performed.
*/

Andrew de Quincey
committed
static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped)
int fe_set_err = 0;
struct dvb_frontend_private *fepriv = fe->frontend_priv;

Mauro Carvalho Chehab
committed
struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
int original_inversion = c->inversion;
u32 original_frequency = c->frequency;

Patrick Boettcher
committed
autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&

Mauro Carvalho Chehab
committed
(c->inversion == INVERSION_AUTO));
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/* setup parameters correctly */
while(!ready) {
/* calculate the lnb_drift */
fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
/* wrap the auto_step if we've exceeded the maximum drift */
if (fepriv->lnb_drift > fepriv->max_drift) {
fepriv->auto_step = 0;
fepriv->auto_sub_step = 0;
fepriv->lnb_drift = 0;
}
/* perform inversion and +/- zigzag */
switch(fepriv->auto_sub_step) {
case 0:
/* try with the current inversion and current drift setting */
ready = 1;
break;
case 1:
if (!autoinversion) break;
fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
ready = 1;
break;
case 2:
if (fepriv->lnb_drift == 0) break;
fepriv->lnb_drift = -fepriv->lnb_drift;
ready = 1;
break;
case 3:
if (fepriv->lnb_drift == 0) break;
if (!autoinversion) break;
fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
fepriv->lnb_drift = -fepriv->lnb_drift;
ready = 1;
break;
default:
fepriv->auto_step++;
fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */
break;
}
if (!ready) fepriv->auto_sub_step++;
}
/* if this attempt would hit where we started, indicate a complete
* iteration has occurred */
if ((fepriv->auto_step == fepriv->started_auto_step) &&
(fepriv->auto_sub_step == 0) && check_wrapped) {
return 1;
}
dev_dbg(fe->dvb->device, "%s: drift:%i inversion:%i auto_step:%i " \
"auto_sub_step:%i started_auto_step:%i\n",
__func__, fepriv->lnb_drift, fepriv->inversion,
fepriv->auto_step, fepriv->auto_sub_step,
fepriv->started_auto_step);

Mauro Carvalho Chehab
committed
c->frequency += fepriv->lnb_drift;

Mauro Carvalho Chehab
committed
c->inversion = fepriv->inversion;
tmp = *c;

Patrick Boettcher
committed
if (fe->ops.set_frontend)
fe_set_err = fe->ops.set_frontend(fe);

Mauro Carvalho Chehab
committed
*c = tmp;
if (fe_set_err < 0) {
fepriv->state = FESTATE_ERROR;
return fe_set_err;
}

Mauro Carvalho Chehab
committed
c->frequency = original_frequency;
c->inversion = original_inversion;

Andrew de Quincey
committed
static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
{
fe_status_t s = 0;
int retval = 0;

Andrew de Quincey
committed
struct dvb_frontend_private *fepriv = fe->frontend_priv;

Mauro Carvalho Chehab
committed
struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;

Andrew de Quincey
committed
/* if we've got no parameters, just keep idling */
if (fepriv->state & FESTATE_IDLE) {
fepriv->delay = 3*HZ;
fepriv->quality = 0;
return;
}
/* in SCAN mode, we just set the frontend when asked and leave it alone */
if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
if (fepriv->state & FESTATE_RETUNE) {

Mauro Carvalho Chehab
committed
tmp = *c;

Patrick Boettcher
committed
if (fe->ops.set_frontend)
retval = fe->ops.set_frontend(fe);

Mauro Carvalho Chehab
committed
*c = tmp;
if (retval < 0)
fepriv->state = FESTATE_ERROR;
else
fepriv->state = FESTATE_TUNED;

Andrew de Quincey
committed
}
fepriv->delay = 3*HZ;
fepriv->quality = 0;
return;
}
/* get the frontend status */
if (fepriv->state & FESTATE_RETUNE) {
s = 0;
} else {

Patrick Boettcher
committed
if (fe->ops.read_status)
fe->ops.read_status(fe, &s);

Andrew de Quincey
committed
if (s != fepriv->status) {
dvb_frontend_add_event(fe, s);
fepriv->status = s;
}
}
/* if we're not tuned, and we have a lock, move to the TUNED state */
if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
fepriv->state = FESTATE_TUNED;
/* if we're tuned, then we have determined the correct inversion */

Patrick Boettcher
committed
if ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&

Mauro Carvalho Chehab
committed
(c->inversion == INVERSION_AUTO)) {
c->inversion = fepriv->inversion;

Andrew de Quincey
committed
}
return;
}
/* if we are tuned already, check we're still locked */
if (fepriv->state & FESTATE_TUNED) {
dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
/* we're tuned, and the lock is still good... */
if (s & FE_HAS_LOCK) {
return;
} else { /* if we _WERE_ tuned, but now don't have a lock */
fepriv->state = FESTATE_ZIGZAG_FAST;
fepriv->started_auto_step = fepriv->auto_step;
fepriv->check_wrapped = 0;
}
}
/* don't actually do anything if we're in the LOSTLOCK state,
* the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
if ((fepriv->state & FESTATE_LOSTLOCK) &&

Patrick Boettcher
committed
(fe->ops.info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) {

Andrew de Quincey
committed
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
return;
}
/* don't do anything if we're in the DISEQC state, since this
* might be someone with a motorized dish controlled by DISEQC.
* If its actually a re-tune, there will be a SET_FRONTEND soon enough. */
if (fepriv->state & FESTATE_DISEQC) {
dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
return;
}
/* if we're in the RETUNE state, set everything up for a brand
* new scan, keeping the current inversion setting, as the next
* tune is _very_ likely to require the same */
if (fepriv->state & FESTATE_RETUNE) {
fepriv->lnb_drift = 0;
fepriv->auto_step = 0;
fepriv->auto_sub_step = 0;
fepriv->started_auto_step = 0;
fepriv->check_wrapped = 0;
}
/* fast zigzag. */
if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
fepriv->delay = fepriv->min_delay;
/* perform a tune */
retval = dvb_frontend_swzigzag_autotune(fe,
fepriv->check_wrapped);
if (retval < 0) {
return;
} else if (retval) {

Andrew de Quincey
committed
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
/* OK, if we've run out of trials at the fast speed.
* Drop back to slow for the _next_ attempt */
fepriv->state = FESTATE_SEARCHING_SLOW;
fepriv->started_auto_step = fepriv->auto_step;
return;
}
fepriv->check_wrapped = 1;
/* if we've just retuned, enter the ZIGZAG_FAST state.
* This ensures we cannot return from an
* FE_SET_FRONTEND ioctl before the first frontend tune
* occurs */
if (fepriv->state & FESTATE_RETUNE) {
fepriv->state = FESTATE_TUNING_FAST;
}
}
/* slow zigzag */
if (fepriv->state & FESTATE_SEARCHING_SLOW) {
dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
/* Note: don't bother checking for wrapping; we stay in this
* state until we get a lock */
dvb_frontend_swzigzag_autotune(fe, 0);
}
}
static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
if (time_after_eq(jiffies, fepriv->release_jiffies +

Andrew de Quincey
committed
dvb_shutdown_timeout * HZ))
return 1;
return 0;
}
static int dvb_frontend_should_wakeup(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
if (fepriv->wakeup) {
fepriv->wakeup = 0;
return 1;
}
return dvb_frontend_is_exiting(fe);
}
static void dvb_frontend_wakeup(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
fepriv->wakeup = 1;
wake_up_interruptible(&fepriv->wait_queue);
}
static int dvb_frontend_thread(void *data)
{
struct dvb_frontend *fe = data;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
bool re_tune = false;
bool semheld = false;
dev_dbg(fe->dvb->device, "%s:\n", __func__);

Andrew de Quincey
committed
fepriv->check_wrapped = 0;
fepriv->quality = 0;
fepriv->delay = 3*HZ;
fepriv->reinitialise = 0;
dvb_frontend_init(fe);
set_freezable();
while (1) {
up(&fepriv->sem); /* is locked when we enter the thread... */
restart:
wait_event_interruptible_timeout(fepriv->wait_queue,
dvb_frontend_should_wakeup(fe) || kthread_should_stop()
|| freezing(current),
fepriv->delay);
if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
if (!down_interruptible(&fepriv->sem))
semheld = true;
if (try_to_freeze())
goto restart;
if (down_interruptible(&fepriv->sem))
break;
if (fepriv->reinitialise) {
dvb_frontend_init(fe);
if (fe->ops.set_tone && fepriv->tone != -1)

Patrick Boettcher
committed
fe->ops.set_tone(fe, fepriv->tone);
if (fe->ops.set_voltage && fepriv->voltage != -1)

Patrick Boettcher
committed
fe->ops.set_voltage(fe, fepriv->voltage);
fepriv->reinitialise = 0;
}

Andrew de Quincey
committed
/* do an iteration of the tuning loop */
if (fe->ops.get_frontend_algo) {
algo = fe->ops.get_frontend_algo(fe);
switch (algo) {
case DVBFE_ALGO_HW:
dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
if (fepriv->state & FESTATE_RETUNE) {
dev_dbg(fe->dvb->device, "%s: Retune requested, FESTATE_RETUNE\n", __func__);
re_tune = true;
fepriv->state = FESTATE_TUNED;
} else {
re_tune = false;
}
fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s);

Darron Broad
committed
if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
dev_dbg(fe->dvb->device, "%s: state changed, adding current state\n", __func__);
dvb_frontend_add_event(fe, s);
fepriv->status = s;
}
dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
dev_dbg(fe->dvb->device, "%s: Retune requested, FESTAT_RETUNE\n", __func__);
fepriv->state = FESTATE_TUNED;
}
/* Case where we are going to search for a carrier
* User asked us to retune again for some reason, possibly
* requesting a search with a new set of parameters
*/
if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
fepriv->algo_status = fe->ops.search(fe);
/* We did do a search as was requested, the flags are
* now unset as well and has the flags wrt to search.
*/
} else {
fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
}
}
/* Track the carrier if the search was successful */
if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) {
fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
fepriv->delay = HZ / 2;
}
dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
fe->ops.read_status(fe, &s);
if (s != fepriv->status) {
dvb_frontend_add_event(fe, s); /* update event list */
fepriv->status = s;
if (!(s & FE_HAS_LOCK)) {
fepriv->delay = HZ / 10;
fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
} else {
fepriv->delay = 60 * HZ;
}
dev_dbg(fe->dvb->device, "%s: UNDEFINED ALGO !\n", __func__);

Manu Abraham
committed
dvb_frontend_swzigzag(fe);
if (dvb_powerdown_on_sleep) {
if (fe->ops.set_voltage)
fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);

Patrick Boettcher
committed
if (fe->ops.tuner_ops.sleep) {

Devin Heitmueller
committed
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);

Patrick Boettcher
committed
fe->ops.tuner_ops.sleep(fe);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);

Patrick Boettcher
committed
if (fe->ops.sleep)
fe->ops.sleep(fe);
fepriv->thread = NULL;
if (kthread_should_stop())
fepriv->exit = DVB_FE_DEVICE_REMOVED;
else
fepriv->exit = DVB_FE_NO_EXIT;
if (semheld)
up(&fepriv->sem);
dvb_frontend_wakeup(fe);
return 0;
}
static void dvb_frontend_stop(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if (!fepriv->thread)
kthread_stop(fepriv->thread);
sema_init(&fepriv->sem, 1);
fepriv->state = FESTATE_IDLE;
/* paranoia check in case a signal arrived */
if (fepriv->thread)
dev_warn(fe->dvb->device,
"dvb_frontend_stop: warning: thread %p won't exit\n",
fepriv->thread);
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime)
{
return ((curtime.tv_usec < lasttime.tv_usec) ?
1000000 - lasttime.tv_usec + curtime.tv_usec :
curtime.tv_usec - lasttime.tv_usec);
}
EXPORT_SYMBOL(timeval_usec_diff);
static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec)
{
curtime->tv_usec += add_usec;
if (curtime->tv_usec >= 1000000) {
curtime->tv_usec -= 1000000;
curtime->tv_sec++;
}
}
/*
* Sleep until gettimeofday() > waketime + add_usec
* This needs to be as precise as possible, but as the delay is
* usually between 2ms and 32ms, it is done using a scheduled msleep
* followed by usleep (normally a busy-wait loop) for the remainder
*/
void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec)
{
struct timeval lasttime;
s32 delta, newdelta;
timeval_usec_add(waketime, add_usec);
do_gettimeofday(&lasttime);
delta = timeval_usec_diff(lasttime, *waketime);
if (delta > 2500) {
msleep((delta - 1500) / 1000);
do_gettimeofday(&lasttime);
newdelta = timeval_usec_diff(lasttime, *waketime);
delta = (newdelta > delta) ? 0 : newdelta;
}
if (delta > 0)
udelay(delta);
}
EXPORT_SYMBOL(dvb_frontend_sleep_until);
static int dvb_frontend_start(struct dvb_frontend *fe)
{
int ret;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
struct task_struct *fe_thread;
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if (fepriv->thread) {
return 0;
else
dvb_frontend_stop (fe);
}
if (signal_pending(current))
return -EINTR;
if (down_interruptible (&fepriv->sem))
return -EINTR;
fepriv->state = FESTATE_IDLE;
fepriv->thread = NULL;
fe_thread = kthread_run(dvb_frontend_thread, fe,
"kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id);
if (IS_ERR(fe_thread)) {
ret = PTR_ERR(fe_thread);
dev_warn(fe->dvb->device,
"dvb_frontend_start: failed to start kthread (%d)\n",
ret);
fepriv->thread = fe_thread;
static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,

Oliver Endriss
committed
u32 *freq_min, u32 *freq_max)
{
*freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min);
if (fe->ops.info.frequency_max == 0)
*freq_max = fe->ops.tuner_ops.info.frequency_max;
else if (fe->ops.tuner_ops.info.frequency_max == 0)
*freq_max = fe->ops.info.frequency_max;
else
*freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
if (*freq_min == 0 || *freq_max == 0)
dev_warn(fe->dvb->device, "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
fe->dvb->num, fe->id);

Oliver Endriss
committed
}
static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *c = &fe->dtv_property_cache;

Oliver Endriss
committed
u32 freq_min;
u32 freq_max;
/* range check: frequency */
dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max);
if ((freq_min && c->frequency < freq_min) ||
(freq_max && c->frequency > freq_max)) {
dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
fe->dvb->num, fe->id, c->frequency,
freq_min, freq_max);
return -EINVAL;
}
/* range check: symbol rate */
switch (c->delivery_system) {
case SYS_DVBS:
case SYS_DVBS2:
case SYS_TURBO:
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
if ((fe->ops.info.symbol_rate_min &&
c->symbol_rate < fe->ops.info.symbol_rate_min) ||
(fe->ops.info.symbol_rate_max &&
c->symbol_rate > fe->ops.info.symbol_rate_max)) {
dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
fe->dvb->num, fe->id, c->symbol_rate,
fe->ops.info.symbol_rate_min,
fe->ops.info.symbol_rate_max);
return -EINVAL;
}
default:
break;
}
return 0;
}

Patrick Boettcher
committed
static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
{

Andreas Oberritter
committed
struct dtv_frontend_properties *c = &fe->dtv_property_cache;

Patrick Boettcher
committed
int i;
u32 delsys;

Patrick Boettcher
committed
delsys = c->delivery_system;

Andreas Oberritter
committed
memset(c, 0, sizeof(struct dtv_frontend_properties));
c->delivery_system = delsys;

Andreas Oberritter
committed
c->state = DTV_CLEAR;
dev_dbg(fe->dvb->device, "%s: Clearing cache for delivery system %d\n",
__func__, c->delivery_system);

Andreas Oberritter
committed
c->transmission_mode = TRANSMISSION_MODE_AUTO;
c->bandwidth_hz = 0; /* AUTO */

Andreas Oberritter
committed
c->guard_interval = GUARD_INTERVAL_AUTO;
c->hierarchy = HIERARCHY_AUTO;

Andreas Oberritter
committed
c->code_rate_HP = FEC_AUTO;
c->code_rate_LP = FEC_AUTO;
c->fec_inner = FEC_AUTO;
c->rolloff = ROLLOFF_AUTO;
c->voltage = SEC_VOLTAGE_OFF;
c->sectone = SEC_TONE_OFF;
c->pilot = PILOT_AUTO;

Andreas Oberritter
committed
c->isdbt_partial_reception = 0;
c->isdbt_sb_mode = 0;
c->isdbt_sb_subchannel = 0;
c->isdbt_sb_segment_idx = 0;
c->isdbt_sb_segment_count = 0;
c->isdbt_layer_enabled = 0;

Patrick Boettcher
committed
for (i = 0; i < 3; i++) {

Andreas Oberritter
committed
c->layer[i].fec = FEC_AUTO;
c->layer[i].modulation = QAM_AUTO;
c->layer[i].interleaving = 0;
c->layer[i].segment_count = 0;

Patrick Boettcher
committed
}
c->stream_id = NO_STREAM_ID_FILTER;
switch (c->delivery_system) {
case SYS_DVBS:
case SYS_DVBS2:
case SYS_TURBO:
c->modulation = QPSK; /* implied for DVB-S in legacy API */
c->rolloff = ROLLOFF_35;/* implied for DVB-S */
break;
case SYS_ATSC:
c->modulation = VSB_8;
break;
default:
c->modulation = QAM_AUTO;
break;
}

Patrick Boettcher
committed
return 0;
}
#define _DTV_CMD(n, s, b) \
[n] = { \
.name = #n, \
.cmd = n, \
.set = s,\
.buffer = b \
}
static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {

Mauro Carvalho Chehab
committed
_DTV_CMD(DTV_TUNE, 1, 0),
_DTV_CMD(DTV_CLEAR, 1, 0),

Steven Toth
committed
/* Set */

Mauro Carvalho Chehab
committed
_DTV_CMD(DTV_FREQUENCY, 1, 0),
_DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
_DTV_CMD(DTV_MODULATION, 1, 0),
_DTV_CMD(DTV_INVERSION, 1, 0),
_DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
_DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
_DTV_CMD(DTV_INNER_FEC, 1, 0),
_DTV_CMD(DTV_VOLTAGE, 1, 0),
_DTV_CMD(DTV_TONE, 1, 0),