X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmedia%2Fdvb%2Fdvb-core%2Fdvb_frontend.c;h=ebc78157b9b8ced38cf637b9c336a0009f868836;hb=d32f60ed54351ebdea8ae6fbfa0d26e93de29252;hp=384b5b8959c1c1e5b6c588cd8e8b10e96354c925;hpb=1fab46f0decd226fcbae73b23d7f8ed478416fbb;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 384b5b8..ebc7815 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -41,12 +40,14 @@ #include "dvb_frontend.h" #include "dvbdev.h" +#include static int dvb_frontend_debug; -static int dvb_shutdown_timeout = 5; +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)."); @@ -58,6 +59,8 @@ 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 seconds on open() for multi-frontend to become available (default:5 seconds)"); #define dprintk if (dvb_frontend_debug) printk @@ -125,6 +128,7 @@ struct dvb_frontend_private { unsigned int step_size; int quality; unsigned int check_wrapped; + enum dvbfe_search algo_status; }; static void dvb_frontend_wakeup(struct dvb_frontend *fe); @@ -136,9 +140,9 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) struct dvb_frontend_event *e; int wp; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); - if (down_interruptible (&events->sem)) + if (mutex_lock_interruptible (&events->mtx)) return; wp = (events->eventw + 1) % MAX_EVENT; @@ -159,7 +163,7 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) events->eventw = wp; - up (&events->sem); + mutex_unlock(&events->mtx); e->status = status; @@ -172,7 +176,7 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dvb_fe_events *events = &fepriv->events; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); if (events->overflow) { events->overflow = 0; @@ -197,7 +201,7 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, return ret; } - if (down_interruptible (&events->sem)) + if (mutex_lock_interruptible (&events->mtx)) return -ERESTARTSYS; memcpy (event, &events->events[events->eventr], @@ -205,20 +209,23 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, events->eventr = (events->eventr + 1) % MAX_EVENT; - up (&events->sem); + mutex_unlock(&events->mtx); return 0; } static void dvb_frontend_init(struct dvb_frontend *fe) { - dprintk ("DVB: initialising frontend %i (%s)...\n", + dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n", fe->dvb->num, + fe->id, fe->ops.info.name); 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); fe->ops.tuner_ops.init(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -238,7 +245,7 @@ static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepr { int q2; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); if (locked) (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256; @@ -330,7 +337,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra dprintk("%s: drift:%i inversion:%i auto_step:%i " "auto_sub_step:%i started_auto_step:%i\n", - __FUNCTION__, fepriv->lnb_drift, fepriv->inversion, + __func__, fepriv->lnb_drift, fepriv->inversion, fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step); /* set the frontend itself */ @@ -510,9 +517,11 @@ static int dvb_frontend_thread(void *data) struct dvb_frontend_private *fepriv = fe->frontend_priv; unsigned long timeout; fe_status_t s; + enum dvbfe_algo algo; + struct dvb_frontend_parameters *params; - dprintk("%s\n", __FUNCTION__); + dprintk("%s\n", __func__); fepriv->check_wrapped = 0; fepriv->quality = 0; @@ -528,7 +537,8 @@ static int dvb_frontend_thread(void *data) up(&fepriv->sem); /* is locked when we enter the thread... */ restart: timeout = wait_event_interruptible_timeout(fepriv->wait_queue, - dvb_frontend_should_wakeup(fe) || kthread_should_stop(), + dvb_frontend_should_wakeup(fe) || kthread_should_stop() + || freezing(current), fepriv->delay); if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { @@ -555,30 +565,88 @@ restart: /* do an iteration of the tuning loop */ if (fe->ops.get_frontend_algo) { - if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) { - /* have we been asked to retune? */ - params = NULL; + algo = fe->ops.get_frontend_algo(fe); + switch (algo) { + case DVBFE_ALGO_HW: + dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__); + params = NULL; /* have we been asked to RETUNE ? */ + if (fepriv->state & FESTATE_RETUNE) { + dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__); params = &fepriv->parameters; fepriv->state = FESTATE_TUNED; } - fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s); - if (s != fepriv->status) { + if (fe->ops.tune) + fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s); + + if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) { + dprintk("%s: state changed, adding current state\n", __func__); dvb_frontend_add_event(fe, s); fepriv->status = s; } - } else + break; + case DVBFE_ALGO_SW: + dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__); dvb_frontend_swzigzag(fe); - } else + break; + case DVBFE_ALGO_CUSTOM: + params = NULL; /* have we been asked to RETUNE ? */ + dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state); + if (fepriv->state & FESTATE_RETUNE) { + dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__); + params = &fepriv->parameters; + 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) { + if (fe->ops.search) { + fepriv->algo_status = fe->ops.search(fe, &fepriv->parameters); + /* 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) { + if (fe->ops.track) + fe->ops.track(fe, &fepriv->parameters); + } else { + fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; + fepriv->delay = HZ / 2; + } + 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; + } + } + break; + default: + dprintk("%s: UNDEFINED ALGO !\n", __func__); + break; + } + } else { dvb_frontend_swzigzag(fe); + } } - if (dvb_shutdown_timeout) { - if (dvb_powerdown_on_sleep) - if (fe->ops.set_voltage) - fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF); + if (dvb_powerdown_on_sleep) { + if (fe->ops.set_voltage) + fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF); if (fe->ops.tuner_ops.sleep) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); fe->ops.tuner_ops.sleep(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -598,7 +666,7 @@ static void dvb_frontend_stop(struct dvb_frontend *fe) { struct dvb_frontend_private *fepriv = fe->frontend_priv; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); fepriv->exit = 1; mb(); @@ -666,7 +734,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) struct dvb_frontend_private *fepriv = fe->frontend_priv; struct task_struct *fe_thread; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); if (fepriv->thread) { if (!fepriv->exit) @@ -686,7 +754,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) mb(); fe_thread = kthread_run(dvb_frontend_thread, fe, - "kdvb-fe-%i", fe->dvb->num); + "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id); if (IS_ERR(fe_thread)) { ret = PTR_ERR(fe_thread); printk("dvb_frontend_start: failed to start kthread (%d)\n", ret); @@ -697,17 +765,35 @@ static int dvb_frontend_start(struct dvb_frontend *fe) return 0; } +static void dvb_frontend_get_frequeny_limits(struct dvb_frontend *fe, + 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) + printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", + fe->dvb->num,fe->id); +} + static int dvb_frontend_check_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *parms) { + u32 freq_min; + u32 freq_max; + /* range check: frequency */ - if ((fe->ops.info.frequency_min && - parms->frequency < fe->ops.info.frequency_min) || - (fe->ops.info.frequency_max && - parms->frequency > fe->ops.info.frequency_max)) { - printk(KERN_WARNING "DVB: frontend %u frequency %u out of range (%u..%u)\n", - fe->dvb->num, parms->frequency, - fe->ops.info.frequency_min, fe->ops.info.frequency_max); + dvb_frontend_get_frequeny_limits(fe, &freq_min, &freq_max); + if ((freq_min && parms->frequency < freq_min) || + (freq_max && parms->frequency > freq_max)) { + printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", + fe->dvb->num, fe->id, parms->frequency, freq_min, freq_max); return -EINVAL; } @@ -717,8 +803,8 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, parms->u.qpsk.symbol_rate < fe->ops.info.symbol_rate_min) || (fe->ops.info.symbol_rate_max && parms->u.qpsk.symbol_rate > fe->ops.info.symbol_rate_max)) { - printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n", - fe->dvb->num, parms->u.qpsk.symbol_rate, + printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", + fe->dvb->num, fe->id, parms->u.qpsk.symbol_rate, fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max); return -EINVAL; } @@ -728,8 +814,8 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, parms->u.qam.symbol_rate < fe->ops.info.symbol_rate_min) || (fe->ops.info.symbol_rate_max && parms->u.qam.symbol_rate > fe->ops.info.symbol_rate_max)) { - printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n", - fe->dvb->num, parms->u.qam.symbol_rate, + printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", + fe->dvb->num, fe->id, parms->u.qam.symbol_rate, fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max); return -EINVAL; } @@ -738,6 +824,534 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } +static struct dtv_cmds_h dtv_cmds[] = { + [DTV_TUNE] = { + .name = "DTV_TUNE", + .cmd = DTV_TUNE, + .set = 1, + }, + [DTV_CLEAR] = { + .name = "DTV_CLEAR", + .cmd = DTV_CLEAR, + .set = 1, + }, + + /* Set */ + [DTV_FREQUENCY] = { + .name = "DTV_FREQUENCY", + .cmd = DTV_FREQUENCY, + .set = 1, + }, + [DTV_BANDWIDTH_HZ] = { + .name = "DTV_BANDWIDTH_HZ", + .cmd = DTV_BANDWIDTH_HZ, + .set = 1, + }, + [DTV_MODULATION] = { + .name = "DTV_MODULATION", + .cmd = DTV_MODULATION, + .set = 1, + }, + [DTV_INVERSION] = { + .name = "DTV_INVERSION", + .cmd = DTV_INVERSION, + .set = 1, + }, + [DTV_DISEQC_MASTER] = { + .name = "DTV_DISEQC_MASTER", + .cmd = DTV_DISEQC_MASTER, + .set = 1, + .buffer = 1, + }, + [DTV_SYMBOL_RATE] = { + .name = "DTV_SYMBOL_RATE", + .cmd = DTV_SYMBOL_RATE, + .set = 1, + }, + [DTV_INNER_FEC] = { + .name = "DTV_INNER_FEC", + .cmd = DTV_INNER_FEC, + .set = 1, + }, + [DTV_VOLTAGE] = { + .name = "DTV_VOLTAGE", + .cmd = DTV_VOLTAGE, + .set = 1, + }, + [DTV_TONE] = { + .name = "DTV_TONE", + .cmd = DTV_TONE, + .set = 1, + }, + [DTV_PILOT] = { + .name = "DTV_PILOT", + .cmd = DTV_PILOT, + .set = 1, + }, + [DTV_ROLLOFF] = { + .name = "DTV_ROLLOFF", + .cmd = DTV_ROLLOFF, + .set = 1, + }, + [DTV_DELIVERY_SYSTEM] = { + .name = "DTV_DELIVERY_SYSTEM", + .cmd = DTV_DELIVERY_SYSTEM, + .set = 1, + }, + [DTV_HIERARCHY] = { + .name = "DTV_HIERARCHY", + .cmd = DTV_HIERARCHY, + .set = 1, + }, + [DTV_CODE_RATE_HP] = { + .name = "DTV_CODE_RATE_HP", + .cmd = DTV_CODE_RATE_HP, + .set = 1, + }, + [DTV_CODE_RATE_LP] = { + .name = "DTV_CODE_RATE_LP", + .cmd = DTV_CODE_RATE_LP, + .set = 1, + }, + [DTV_GUARD_INTERVAL] = { + .name = "DTV_GUARD_INTERVAL", + .cmd = DTV_GUARD_INTERVAL, + .set = 1, + }, + [DTV_TRANSMISSION_MODE] = { + .name = "DTV_TRANSMISSION_MODE", + .cmd = DTV_TRANSMISSION_MODE, + .set = 1, + }, + /* Get */ + [DTV_DISEQC_SLAVE_REPLY] = { + .name = "DTV_DISEQC_SLAVE_REPLY", + .cmd = DTV_DISEQC_SLAVE_REPLY, + .set = 0, + .buffer = 1, + }, + [DTV_API_VERSION] = { + .name = "DTV_API_VERSION", + .cmd = DTV_API_VERSION, + .set = 0, + }, + [DTV_CODE_RATE_HP] = { + .name = "DTV_CODE_RATE_HP", + .cmd = DTV_CODE_RATE_HP, + .set = 0, + }, + [DTV_CODE_RATE_LP] = { + .name = "DTV_CODE_RATE_LP", + .cmd = DTV_CODE_RATE_LP, + .set = 0, + }, + [DTV_GUARD_INTERVAL] = { + .name = "DTV_GUARD_INTERVAL", + .cmd = DTV_GUARD_INTERVAL, + .set = 0, + }, + [DTV_TRANSMISSION_MODE] = { + .name = "DTV_TRANSMISSION_MODE", + .cmd = DTV_TRANSMISSION_MODE, + .set = 0, + }, + [DTV_HIERARCHY] = { + .name = "DTV_HIERARCHY", + .cmd = DTV_HIERARCHY, + .set = 0, + }, +}; + +static void dtv_property_dump(struct dtv_property *tvp) +{ + int i; + + if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) { + printk(KERN_WARNING "%s: tvp.cmd = 0x%08x undefined\n", + __func__, tvp->cmd); + return; + } + + dprintk("%s() tvp.cmd = 0x%08x (%s)\n" + ,__func__ + ,tvp->cmd + ,dtv_cmds[ tvp->cmd ].name); + + if(dtv_cmds[ tvp->cmd ].buffer) { + + dprintk("%s() tvp.u.buffer.len = 0x%02x\n" + ,__func__ + ,tvp->u.buffer.len); + + for(i = 0; i < tvp->u.buffer.len; i++) + dprintk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n" + ,__func__ + ,i + ,tvp->u.buffer.data[i]); + + } else + dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data); +} + +static int is_legacy_delivery_system(fe_delivery_system_t s) +{ + if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) || + (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS) || + (s == SYS_ATSC)) + return 1; + + return 0; +} + +/* Synchronise the legacy tuning parameters into the cache, so that demodulator + * drivers can use a single set_frontend tuning function, regardless of whether + * it's being used for the legacy or new API, reducing code and complexity. + */ +static void dtv_property_cache_sync(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + c->frequency = p->frequency; + c->inversion = p->inversion; + + switch (fe->ops.info.type) { + case FE_QPSK: + c->modulation = QPSK; /* implied for DVB-S in legacy API */ + c->rolloff = ROLLOFF_35;/* implied for DVB-S */ + c->symbol_rate = p->u.qpsk.symbol_rate; + c->fec_inner = p->u.qpsk.fec_inner; + c->delivery_system = SYS_DVBS; + break; + case FE_QAM: + c->symbol_rate = p->u.qam.symbol_rate; + c->fec_inner = p->u.qam.fec_inner; + c->modulation = p->u.qam.modulation; + c->delivery_system = SYS_DVBC_ANNEX_AC; + break; + case FE_OFDM: + if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) + c->bandwidth_hz = 6000000; + else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) + c->bandwidth_hz = 7000000; + else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) + c->bandwidth_hz = 8000000; + else + /* Including BANDWIDTH_AUTO */ + c->bandwidth_hz = 0; + c->code_rate_HP = p->u.ofdm.code_rate_HP; + c->code_rate_LP = p->u.ofdm.code_rate_LP; + c->modulation = p->u.ofdm.constellation; + c->transmission_mode = p->u.ofdm.transmission_mode; + c->guard_interval = p->u.ofdm.guard_interval; + c->hierarchy = p->u.ofdm.hierarchy_information; + c->delivery_system = SYS_DVBT; + break; + case FE_ATSC: + c->modulation = p->u.vsb.modulation; + if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) + c->delivery_system = SYS_ATSC; + else + c->delivery_system = SYS_DVBC_ANNEX_B; + break; + } +} + +/* Ensure the cached values are set correctly in the frontend + * legacy tuning structures, for the advanced tuning API. + */ +static void dtv_property_legacy_params_sync(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_frontend_parameters *p = &fepriv->parameters; + + p->frequency = c->frequency; + p->inversion = c->inversion; + + switch (fe->ops.info.type) { + case FE_QPSK: + dprintk("%s() Preparing QPSK req\n", __func__); + p->u.qpsk.symbol_rate = c->symbol_rate; + p->u.qpsk.fec_inner = c->fec_inner; + c->delivery_system = SYS_DVBS; + break; + case FE_QAM: + dprintk("%s() Preparing QAM req\n", __func__); + p->u.qam.symbol_rate = c->symbol_rate; + p->u.qam.fec_inner = c->fec_inner; + p->u.qam.modulation = c->modulation; + c->delivery_system = SYS_DVBC_ANNEX_AC; + break; + case FE_OFDM: + dprintk("%s() Preparing OFDM req\n", __func__); + if (c->bandwidth_hz == 6000000) + p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + else if (c->bandwidth_hz == 7000000) + p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + else if (c->bandwidth_hz == 8000000) + p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + else + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; + p->u.ofdm.code_rate_HP = c->code_rate_HP; + p->u.ofdm.code_rate_LP = c->code_rate_LP; + p->u.ofdm.constellation = c->modulation; + p->u.ofdm.transmission_mode = c->transmission_mode; + p->u.ofdm.guard_interval = c->guard_interval; + p->u.ofdm.hierarchy_information = c->hierarchy; + c->delivery_system = SYS_DVBT; + break; + case FE_ATSC: + dprintk("%s() Preparing VSB req\n", __func__); + p->u.vsb.modulation = c->modulation; + if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) + c->delivery_system = SYS_ATSC; + else + c->delivery_system = SYS_DVBC_ANNEX_B; + break; + } +} + +/* Ensure the cached values are set correctly in the frontend + * legacy tuning structures, for the legacy tuning API. + */ +static void dtv_property_adv_params_sync(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_frontend_parameters *p = &fepriv->parameters; + + p->frequency = c->frequency; + p->inversion = c->inversion; + + switch(c->modulation) { + case PSK_8: + case APSK_16: + case APSK_32: + case QPSK: + p->u.qpsk.symbol_rate = c->symbol_rate; + p->u.qpsk.fec_inner = c->fec_inner; + break; + default: + break; + } + + if(c->delivery_system == SYS_ISDBT) { + /* Fake out a generic DVB-T request so we pass validation in the ioctl */ + p->frequency = c->frequency; + p->inversion = INVERSION_AUTO; + p->u.ofdm.constellation = QAM_AUTO; + p->u.ofdm.code_rate_HP = FEC_AUTO; + p->u.ofdm.code_rate_LP = FEC_AUTO; + p->u.ofdm.bandwidth = BANDWIDTH_AUTO; + p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + p->u.ofdm.hierarchy_information = HIERARCHY_AUTO; + } +} + +static void dtv_property_cache_submit(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* For legacy delivery systems we don't need the delivery_system to + * be specified, but we populate the older structures from the cache + * so we can call set_frontend on older drivers. + */ + if(is_legacy_delivery_system(c->delivery_system)) { + + dprintk("%s() legacy, modulation = %d\n", __func__, c->modulation); + dtv_property_legacy_params_sync(fe); + + } else { + dprintk("%s() adv, modulation = %d\n", __func__, c->modulation); + + /* For advanced delivery systems / modulation types ... + * we seed the lecacy dvb_frontend_parameters structure + * so that the sanity checking code later in the IOCTL processing + * can validate our basic frequency ranges, symbolrates, modulation + * etc. + */ + dtv_property_adv_params_sync(fe); + } +} + +static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, + unsigned int cmd, void *parg); +static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, + unsigned int cmd, void *parg); + +static int dtv_property_process_get(struct dvb_frontend *fe, + struct dtv_property *tvp, + struct inode *inode, struct file *file) +{ + int r = 0; + + dtv_property_dump(tvp); + + /* Allow the frontend to validate incoming properties */ + if (fe->ops.get_property) + r = fe->ops.get_property(fe, tvp); + + if (r < 0) + return r; + + switch(tvp->cmd) { + case DTV_FREQUENCY: + tvp->u.data = fe->dtv_property_cache.frequency; + break; + case DTV_MODULATION: + tvp->u.data = fe->dtv_property_cache.modulation; + break; + case DTV_BANDWIDTH_HZ: + tvp->u.data = fe->dtv_property_cache.bandwidth_hz; + break; + case DTV_INVERSION: + tvp->u.data = fe->dtv_property_cache.inversion; + break; + case DTV_SYMBOL_RATE: + tvp->u.data = fe->dtv_property_cache.symbol_rate; + break; + case DTV_INNER_FEC: + tvp->u.data = fe->dtv_property_cache.fec_inner; + break; + case DTV_PILOT: + tvp->u.data = fe->dtv_property_cache.pilot; + break; + case DTV_ROLLOFF: + tvp->u.data = fe->dtv_property_cache.rolloff; + break; + case DTV_DELIVERY_SYSTEM: + tvp->u.data = fe->dtv_property_cache.delivery_system; + break; + case DTV_VOLTAGE: + tvp->u.data = fe->dtv_property_cache.voltage; + break; + case DTV_TONE: + tvp->u.data = fe->dtv_property_cache.sectone; + break; + case DTV_API_VERSION: + tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR; + break; + case DTV_CODE_RATE_HP: + tvp->u.data = fe->dtv_property_cache.code_rate_HP; + break; + case DTV_CODE_RATE_LP: + tvp->u.data = fe->dtv_property_cache.code_rate_LP; + break; + case DTV_GUARD_INTERVAL: + tvp->u.data = fe->dtv_property_cache.guard_interval; + break; + case DTV_TRANSMISSION_MODE: + tvp->u.data = fe->dtv_property_cache.transmission_mode; + break; + case DTV_HIERARCHY: + tvp->u.data = fe->dtv_property_cache.hierarchy; + break; + default: + r = -1; + } + + return r; +} + +static int dtv_property_process_set(struct dvb_frontend *fe, + struct dtv_property *tvp, + struct inode *inode, + struct file *file) +{ + int r = 0; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + dtv_property_dump(tvp); + + /* Allow the frontend to validate incoming properties */ + if (fe->ops.set_property) + r = fe->ops.set_property(fe, tvp); + + if (r < 0) + return r; + + switch(tvp->cmd) { + case DTV_CLEAR: + /* Reset a cache of data specific to the frontend here. This does + * not effect hardware. + */ + dprintk("%s() Flushing property cache\n", __func__); + memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties)); + fe->dtv_property_cache.state = tvp->cmd; + fe->dtv_property_cache.delivery_system = SYS_UNDEFINED; + break; + case DTV_TUNE: + /* interpret the cache of data, build either a traditional frontend + * tunerequest so we can pass validation in the FE_SET_FRONTEND + * ioctl. + */ + fe->dtv_property_cache.state = tvp->cmd; + dprintk("%s() Finalised property cache\n", __func__); + dtv_property_cache_submit(fe); + + r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND, + &fepriv->parameters); + break; + case DTV_FREQUENCY: + fe->dtv_property_cache.frequency = tvp->u.data; + break; + case DTV_MODULATION: + fe->dtv_property_cache.modulation = tvp->u.data; + break; + case DTV_BANDWIDTH_HZ: + fe->dtv_property_cache.bandwidth_hz = tvp->u.data; + break; + case DTV_INVERSION: + fe->dtv_property_cache.inversion = tvp->u.data; + break; + case DTV_SYMBOL_RATE: + fe->dtv_property_cache.symbol_rate = tvp->u.data; + break; + case DTV_INNER_FEC: + fe->dtv_property_cache.fec_inner = tvp->u.data; + break; + case DTV_PILOT: + fe->dtv_property_cache.pilot = tvp->u.data; + break; + case DTV_ROLLOFF: + fe->dtv_property_cache.rolloff = tvp->u.data; + break; + case DTV_DELIVERY_SYSTEM: + fe->dtv_property_cache.delivery_system = tvp->u.data; + break; + case DTV_VOLTAGE: + fe->dtv_property_cache.voltage = tvp->u.data; + r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE, + (void *)fe->dtv_property_cache.voltage); + break; + case DTV_TONE: + fe->dtv_property_cache.sectone = tvp->u.data; + r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE, + (void *)fe->dtv_property_cache.sectone); + break; + case DTV_CODE_RATE_HP: + fe->dtv_property_cache.code_rate_HP = tvp->u.data; + break; + case DTV_CODE_RATE_LP: + fe->dtv_property_cache.code_rate_LP = tvp->u.data; + break; + case DTV_GUARD_INTERVAL: + fe->dtv_property_cache.guard_interval = tvp->u.data; + break; + case DTV_TRANSMISSION_MODE: + fe->dtv_property_cache.transmission_mode = tvp->u.data; + break; + case DTV_HIERARCHY: + fe->dtv_property_cache.hierarchy = tvp->u.data; + break; + default: + r = -1; + } + + return r; +} + static int dvb_frontend_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { @@ -746,9 +1360,9 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, struct dvb_frontend_private *fepriv = fe->frontend_priv; int err = -EOPNOTSUPP; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); - if (!fe || fepriv->exit) + if (fepriv->exit) return -ENODEV; if ((file->f_flags & O_ACCMODE) == O_RDONLY && @@ -759,10 +1373,117 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, if (down_interruptible (&fepriv->sem)) return -ERESTARTSYS; + if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) + err = dvb_frontend_ioctl_properties(inode, file, cmd, parg); + else { + fe->dtv_property_cache.state = DTV_UNDEFINED; + err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg); + } + + up(&fepriv->sem); + return err; +} + +static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend *fe = dvbdev->priv; + int err = 0; + + struct dtv_properties *tvps = NULL; + struct dtv_property *tvp = NULL; + int i; + + dprintk("%s\n", __func__); + + if(cmd == FE_SET_PROPERTY) { + tvps = (struct dtv_properties __user *)parg; + + dprintk("%s() properties.num = %d\n", __func__, tvps->num); + dprintk("%s() properties.props = %p\n", __func__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) + return -EINVAL; + + tvp = (struct dtv_property *) kmalloc(tvps->num * + sizeof(struct dtv_property), GFP_KERNEL); + if (!tvp) { + err = -ENOMEM; + goto out; + } + + if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < tvps->num; i++) { + (tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file); + err |= (tvp + i)->result; + } + + if(fe->dtv_property_cache.state == DTV_TUNE) + dprintk("%s() Property cache is full, tuning\n", __func__); + + } else + if(cmd == FE_GET_PROPERTY) { + + tvps = (struct dtv_properties __user *)parg; + + dprintk("%s() properties.num = %d\n", __func__, tvps->num); + dprintk("%s() properties.props = %p\n", __func__, tvps->props); + + /* Put an arbitrary limit on the number of messages that can + * be sent at once */ + if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) + return -EINVAL; + + tvp = (struct dtv_property *) kmalloc(tvps->num * + sizeof(struct dtv_property), GFP_KERNEL); + if (!tvp) { + err = -ENOMEM; + goto out; + } + + if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + for (i = 0; i < tvps->num; i++) { + (tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file); + err |= (tvp + i)->result; + } + + if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { + err = -EFAULT; + goto out; + } + + } else + err = -EOPNOTSUPP; + +out: + kfree(tvp); + return err; +} + +static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend *fe = dvbdev->priv; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + int err = -EOPNOTSUPP; + switch (cmd) { case FE_GET_INFO: { struct dvb_frontend_info* info = parg; memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); + dvb_frontend_get_frequeny_limits(fe, &info->frequency_min, &info->frequency_max); /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't * do it, it is done for it. */ @@ -871,13 +1592,13 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, * initialization, so parg is 8 bits and does not * include the initialization or start bit */ - unsigned long cmd = ((unsigned long) parg) << 1; + unsigned long swcmd = ((unsigned long) parg) << 1; struct timeval nexttime; struct timeval tv[10]; int i; u8 last = 1; if (dvb_frontend_debug) - printk("%s switch command: 0x%04lx\n", __FUNCTION__, cmd); + printk("%s switch command: 0x%04lx\n", __func__, swcmd); do_gettimeofday(&nexttime); if (dvb_frontend_debug) memcpy(&tv[0], &nexttime, sizeof(struct timeval)); @@ -890,18 +1611,18 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, for (i = 0; i < 9; i++) { if (dvb_frontend_debug) do_gettimeofday(&tv[i + 1]); - if ((cmd & 0x01) != last) { + if ((swcmd & 0x01) != last) { /* set voltage to (last ? 13V : 18V) */ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); last = (last) ? 0 : 1; } - cmd = cmd >> 1; + swcmd = swcmd >> 1; if (i != 8) dvb_frontend_sleep_until(&nexttime, 8000); } if (dvb_frontend_debug) { printk("%s(%d): switch delay (should be 32k followed by all 8k\n", - __FUNCTION__, fe->dvb->num); + __func__, fe->dvb->num); for (i = 1; i < 10; i++) printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); } @@ -924,13 +1645,21 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_FRONTEND: { struct dvb_frontend_tune_settings fetunesettings; - if (dvb_frontend_check_parameters(fe, parg) < 0) { - err = -EINVAL; - break; - } + if(fe->dtv_property_cache.state == DTV_TUNE) { + if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) { + err = -EINVAL; + break; + } + } else { + if (dvb_frontend_check_parameters(fe, parg) < 0) { + err = -EINVAL; + break; + } - memcpy (&fepriv->parameters, parg, - sizeof (struct dvb_frontend_parameters)); + memcpy (&fepriv->parameters, parg, + sizeof (struct dvb_frontend_parameters)); + dtv_property_cache_sync(fe, &fepriv->parameters); + } memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); memcpy(&fetunesettings.parameters, parg, @@ -985,6 +1714,10 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000; fepriv->state = FESTATE_RETUNE; + + /* Request the search algorithm to search */ + fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; + dvb_frontend_wakeup(fe); dvb_frontend_add_event(fe, 0); fepriv->status = 0; @@ -1009,17 +1742,17 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, break; }; - up (&fepriv->sem); return err; } + static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait) { struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); poll_wait (file, &fepriv->events.wait_queue, wait); @@ -1034,22 +1767,59 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_adapter *adapter = fe->dvb; int ret; - dprintk ("%s\n", __FUNCTION__); - - if ((ret = dvb_generic_open (inode, file)) < 0) - return ret; + dprintk ("%s\n", __func__); + + if (adapter->mfe_shared) { + mutex_lock (&adapter->mfe_lock); + + if (adapter->mfe_dvbdev == NULL) + adapter->mfe_dvbdev = dvbdev; + + else if (adapter->mfe_dvbdev != dvbdev) { + struct dvb_device + *mfedev = adapter->mfe_dvbdev; + struct dvb_frontend + *mfe = mfedev->priv; + struct dvb_frontend_private + *mfepriv = mfe->frontend_priv; + int mferetry = (dvb_mfe_wait_time << 1); + + mutex_unlock (&adapter->mfe_lock); + while (mferetry-- && (mfedev->users != -1 || + mfepriv->thread != NULL)) { + if(msleep_interruptible(500)) { + if(signal_pending(current)) + return -EINTR; + } + } - if (fe->ops.ts_bus_ctrl) { - if ((ret = fe->ops.ts_bus_ctrl (fe, 1)) < 0) { - dvb_generic_release (inode, file); - return ret; + mutex_lock (&adapter->mfe_lock); + if(adapter->mfe_dvbdev != dvbdev) { + mfedev = adapter->mfe_dvbdev; + mfe = mfedev->priv; + mfepriv = mfe->frontend_priv; + if (mfedev->users != -1 || + mfepriv->thread != NULL) { + mutex_unlock (&adapter->mfe_lock); + return -EBUSY; + } + adapter->mfe_dvbdev = dvbdev; + } } } - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) { + if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0) + goto err0; + } + if ((ret = dvb_generic_open (inode, file)) < 0) + goto err1; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { /* normal tune mode when opened R/W */ fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT; fepriv->tone = -1; @@ -1057,12 +1827,24 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) ret = dvb_frontend_start (fe); if (ret) - dvb_generic_release (inode, file); + goto err2; /* empty event queue */ fepriv->events.eventr = fepriv->events.eventw = 0; } + if (adapter->mfe_shared) + mutex_unlock (&adapter->mfe_lock); + return ret; + +err2: + dvb_generic_release(inode, file); +err1: + if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) + fe->ops.ts_bus_ctrl(fe, 0); +err0: + if (adapter->mfe_shared) + mutex_unlock (&adapter->mfe_lock); return ret; } @@ -1073,25 +1855,27 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) struct dvb_frontend_private *fepriv = fe->frontend_priv; int ret; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); if ((file->f_flags & O_ACCMODE) != O_RDONLY) fepriv->release_jiffies = jiffies; - if (fe->ops.ts_bus_ctrl) - fe->ops.ts_bus_ctrl (fe, 0); - ret = dvb_generic_release (inode, file); - if (dvbdev->users==-1 && fepriv->exit==1) { - fops_put(file->f_op); - file->f_op = NULL; - wake_up(&dvbdev->wait_queue); + if (dvbdev->users == -1) { + if (fepriv->exit == 1) { + fops_put(file->f_op); + file->f_op = NULL; + wake_up(&dvbdev->wait_queue); + } + if (fe->ops.ts_bus_ctrl) + fe->ops.ts_bus_ctrl(fe, 0); } + return ret; } -static struct file_operations dvb_frontend_fops = { +static const struct file_operations dvb_frontend_fops = { .owner = THIS_MODULE, .ioctl = dvb_generic_ioctl, .poll = dvb_frontend_poll, @@ -1111,7 +1895,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb, .kernel_ioctl = dvb_frontend_ioctl }; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); if (mutex_lock_interruptible(&frontend_mutex)) return -ERESTARTSYS; @@ -1126,12 +1910,13 @@ int dvb_register_frontend(struct dvb_adapter* dvb, init_MUTEX (&fepriv->sem); init_waitqueue_head (&fepriv->wait_queue); init_waitqueue_head (&fepriv->events.wait_queue); - init_MUTEX (&fepriv->events.sem); + mutex_init(&fepriv->events.mtx); fe->dvb = dvb; fepriv->inversion = INVERSION_OFF; - printk ("DVB: registering frontend %i (%s)...\n", + printk ("DVB: registering adapter %i frontend %i (%s)...\n", fe->dvb->num, + fe->id, fe->ops.info.name); dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template, @@ -1145,7 +1930,7 @@ EXPORT_SYMBOL(dvb_register_frontend); int dvb_unregister_frontend(struct dvb_frontend* fe) { struct dvb_frontend_private *fepriv = fe->frontend_priv; - dprintk ("%s\n", __FUNCTION__); + dprintk ("%s\n", __func__); mutex_lock(&frontend_mutex); dvb_frontend_stop (fe); @@ -1165,7 +1950,7 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) } EXPORT_SYMBOL(dvb_unregister_frontend); -#ifdef CONFIG_DVB_CORE_ATTACH +#ifdef CONFIG_MEDIA_ATTACH void dvb_frontend_detach(struct dvb_frontend* fe) { void *ptr; @@ -1178,6 +1963,10 @@ void dvb_frontend_detach(struct dvb_frontend* fe) fe->ops.tuner_ops.release(fe); symbol_put_addr(fe->ops.tuner_ops.release); } + if (fe->ops.analog_ops.release) { + fe->ops.analog_ops.release(fe); + symbol_put_addr(fe->ops.analog_ops.release); + } ptr = (void*)fe->ops.release; if (ptr) { fe->ops.release(fe); @@ -1191,6 +1980,8 @@ void dvb_frontend_detach(struct dvb_frontend* fe) fe->ops.release_sec(fe); if (fe->ops.tuner_ops.release) fe->ops.tuner_ops.release(fe); + if (fe->ops.analog_ops.release) + fe->ops.analog_ops.release(fe); if (fe->ops.release) fe->ops.release(fe); }