Here is a modified version of Marcel's DVB-S2 patch. I made the following changes: removed #defines for QPSK, QAM_16, etc and added them back into the fe_modulation enum. This was needed for backwards compatibility, as the #define was conflicting with mythtv (which had a method called QPSK) implemented the union in dvb_frontend_parameters_new as static (as per my previous mail). This made it much easier to implement support for DVB-S2 in mythtv reinitialize driver to legacy mode when the fronetend is opened (for write), as otherwise, a legacy app could not access the frontend if an updated app had called SET_STANDARD in the past. filled out the implementation in dvb_frontend.c I have implemented the DVB-S2 driver for the genpix-8psk board (will post it imminently), and have the necessary patch for mythtv available too. Signed off by Alan Nisota <alannisota@xxxxxxxxx> diff -r 436e56df57d3 linux/drivers/media/dvb/dvb-core/dvb_frontend.c --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c Thu Apr 13 18:50:22 2006 -0300 +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c Sat Apr 22 13:38:01 2006 -0700 @@ -94,6 +94,7 @@ struct dvb_frontend_private { /* thread/frontend values */ struct dvb_device *dvbdev; struct dvb_frontend_parameters parameters; + struct dvb_frontend_parameters_new parameters_new; struct dvb_fe_events events; struct semaphore sem; struct list_head list_head; @@ -109,6 +110,8 @@ struct dvb_frontend_private { int tone; int voltage; + int current_standard_set; + fe_type_t current_standard; /* swzigzag values */ unsigned int state; unsigned int bending; @@ -255,12 +258,23 @@ static int dvb_frontend_swzigzag_autotun int autoinversion; int ready = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; - int original_inversion = fepriv->parameters.inversion; - u32 original_frequency = fepriv->parameters.frequency; + int original_inversion; + u32 original_frequency; + + if (fepriv->current_standard_set) { + original_inversion = fepriv->parameters_new.inversion; + original_frequency = fepriv->parameters_new.frequency; + } else { + original_inversion = fepriv->parameters.inversion; + original_frequency = fepriv->parameters.frequency; + } /* are we using autoinversion? */ autoinversion = ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) && - (fepriv->parameters.inversion == INVERSION_AUTO)); + ((fepriv->current_standard_set && + fepriv->parameters_new.inversion == INVERSION_AUTO) || + (! fepriv->current_standard_set && + fepriv->parameters.inversion == INVERSION_AUTO))); /* setup parameters correctly */ while(!ready) { @@ -326,14 +340,24 @@ static int dvb_frontend_swzigzag_autotun fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step); /* set the frontend itself */ - fepriv->parameters.frequency += fepriv->lnb_drift; - if (autoinversion) - fepriv->parameters.inversion = fepriv->inversion; - if (fe->ops->set_frontend) - fe->ops->set_frontend(fe, &fepriv->parameters); - - fepriv->parameters.frequency = original_frequency; - fepriv->parameters.inversion = original_inversion; + if (fepriv->current_standard_set) { + fepriv->parameters_new.frequency += fepriv->lnb_drift; + if (autoinversion) + fepriv->parameters_new.inversion = fepriv->inversion; + if (fe->ops->set_frontend2) + fe->ops->set_frontend2(fe, &fepriv->parameters_new); + fepriv->parameters_new.frequency = original_frequency; + fepriv->parameters_new.inversion = original_inversion; + } else { + fepriv->parameters.frequency += fepriv->lnb_drift; + if (autoinversion) + fepriv->parameters.inversion = fepriv->inversion; + if (fe->ops->set_frontend) + fe->ops->set_frontend(fe, &fepriv->parameters); + + fepriv->parameters.frequency = original_frequency; + fepriv->parameters.inversion = original_inversion; + } fepriv->auto_sub_step++; return 0; @@ -354,8 +378,13 @@ static void dvb_frontend_swzigzag(struct /* 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) { - if (fe->ops->set_frontend) - fe->ops->set_frontend(fe, &fepriv->parameters); + if (fepriv->current_standard_set) { + if (fe->ops->set_frontend2) + fe->ops->set_frontend2(fe, &fepriv->parameters_new); + } else { + if (fe->ops->set_frontend) + fe->ops->set_frontend(fe, &fepriv->parameters); + } fepriv->state = FESTATE_TUNED; } fepriv->delay = 3*HZ; @@ -921,18 +950,21 @@ static int dvb_frontend_ioctl(struct ino /* default values */ switch(fe->ops->info.type) { case FE_QPSK: + case FE_DVB_S: fepriv->min_delay = HZ/20; fepriv->step_size = fepriv->parameters.u.qpsk.symbol_rate / 16000; fepriv->max_drift = fepriv->parameters.u.qpsk.symbol_rate / 2000; break; case FE_QAM: + case FE_DVB_C: fepriv->min_delay = HZ/20; fepriv->step_size = 0; /* no zigzag */ fepriv->max_drift = 0; break; case FE_OFDM: + case FE_DVB_T: fepriv->min_delay = HZ/20; fepriv->step_size = fe->ops->info.frequency_stepsize * 2; fepriv->max_drift = (fe->ops->info.frequency_stepsize * 2) + 1; @@ -940,6 +972,9 @@ static int dvb_frontend_ioctl(struct ino case FE_ATSC: printk("dvb-core: FE_ATSC not handled yet.\n"); break; + case FE_DVB_S2: + printk("dvb-core: ERROR FE_DVB_S2 is handled via FE_SET_FRONTEND2.\n"); + break; } } if (dvb_override_tune_delay > 0) @@ -952,6 +987,101 @@ static int dvb_frontend_ioctl(struct ino err = 0; break; } + case FE_SET_FRONTEND2: { + struct dvb_frontend_tune_settings fetunesettings; + struct dvb_frontend_parameters_new *castedparg; + + if (!fepriv->current_standard_set) { + err = -EINVAL; + break; + } + + castedparg = (struct dvb_frontend_parameters_new *)parg; + memcpy (&fepriv->parameters_new, parg, + sizeof (struct dvb_frontend_parameters_new)); + + memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); + + /* be sure struct union pointer is set - even if it is NULL */ + fetunesettings.parameters_new.u = castedparg->u; + + /* force auto frequency inversion if requested */ + if (dvb_force_auto_inversion) { + fepriv->parameters_new.inversion = INVERSION_AUTO; + fetunesettings.parameters_new.inversion = INVERSION_AUTO; + } + if (fe->ops->info.type == FE_DVB_T || fe->ops->info.type == FE_OFDM) { + /* without hierachical coding code_rate_LP is irrelevant, + * so we tolerate the otherwise invalid FEC_NONE setting */ + if (fepriv->parameters_new.u.ofdm.hierarchy_information == HIERARCHY_NONE && + fepriv->parameters_new.u.ofdm.code_rate_LP == FEC_NONE) + fepriv->parameters_new.u.ofdm.code_rate_LP = FEC_AUTO; + } + + /* get frontend-specific tuning settings */ + if (fe->ops->get_tune_settings && (fe->ops->get_tune_settings(fe, &fetunesettings) == 0)) { + fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000; + fepriv->max_drift = fetunesettings.max_drift; + fepriv->step_size = fetunesettings.step_size; + } else { + /* default values */ + switch(fe->ops->info.type) { + case FE_DVB_S: + case FE_QPSK: + if (fepriv->current_standard != FE_DVB_S) { + err = -EINVAL; + break; + } + fepriv->min_delay = HZ/20; + fepriv->step_size = fepriv->parameters_new.u.qpsk.symbol_rate / 16000; + fepriv->max_drift = fepriv->parameters_new.u.qpsk.symbol_rate / 2000; + break; + case FE_DVB_C: + case FE_QAM: + if (fepriv->current_standard != FE_DVB_C) { + err = -EINVAL; + break; + } + fepriv->min_delay = HZ/20; + fepriv->step_size = 0; /* no zigzag */ + fepriv->max_drift = 0; + break; + case FE_DVB_T: + case FE_OFDM: + if (fepriv->current_standard != FE_DVB_T) { + err = -EINVAL; + break; + } + fepriv->min_delay = HZ/20; + fepriv->step_size = fe->ops->info.frequency_stepsize * 2; + fepriv->max_drift = (fe->ops->info.frequency_stepsize * 2) + 1; + break; + case FE_ATSC: + printk("dvb-core: FE_ATSC not handled yet.\n"); + break; + case FE_DVB_S2: + if (fepriv->current_standard != FE_DVB_S2) { + err = -EINVAL; + break; + } + fepriv->min_delay = HZ/20; + fepriv->step_size = fepriv->parameters_new.u.qpsk2.symbol_rate / 16000; + fepriv->max_drift = fepriv->parameters_new.u.qpsk2.symbol_rate / 2000; + break; + + } + } + if (dvb_override_tune_delay > 0) + fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000; + + fepriv->state = FESTATE_RETUNE; + dvb_frontend_wakeup(fe); + dvb_frontend_add_event(fe, 0); + fepriv->status = 0; + err = 0; + /* if previously used delete old tuning params */ + break; + } case FE_GET_EVENT: err = dvb_frontend_get_event (fe, parg, file->f_flags); @@ -963,6 +1093,48 @@ static int dvb_frontend_ioctl(struct ino err = fe->ops->get_frontend(fe, (struct dvb_frontend_parameters*) parg); } break; + case FE_GET_FRONTEND2: + if (!fepriv->current_standard) { + err = -EINVAL; + break; + } + if (fe->ops->get_frontend2) { + memcpy (parg, &fepriv->parameters_new, sizeof (struct dvb_frontend_parameters_new)); + err = fe->ops->get_frontend2(fe, (struct dvb_frontend_parameters_new*) parg); + } + break; + + case FE_SET_STANDARD: + /* + * current_standard_set and current_standard are reset + * this prevents user from ignoring return value and + * if current_standard would be kept as fallback, users may + * supply corrupted tuning data. + * if the new standard isn't supported no FE_SET_FRONTEND2 + * is possible. it will return EINVAL + */ + fepriv->current_standard_set = 0; + fepriv->current_standard = 0; + { + fe_type_t fetype = (fe_type_t) parg; + if (fetype != FE_DVB_S && fetype != FE_DVB_C && fetype != FE_DVB_T && fetype != FE_DVB_S2 && fetype != FE_ATSC) { + err = -EINVAL; + } else if (fe->ops->set_standard) { + err = fe->ops->set_standard(fe, fetype); + if (!err) { + fepriv->current_standard_set = 1; + fepriv->current_standard = fetype; + } + } + } + break; + + case FE_GET_EXTENDED_INFO: { + struct dvb_fe_caps_extended* info = parg; + memcpy(info, &fe->ops->extended_info, sizeof(struct dvb_fe_caps_extended)); + err = 0; + break; + } case FE_SET_FRONTEND_TUNE_MODE: fepriv->tune_mode_flags = (unsigned long) parg; @@ -1013,6 +1185,10 @@ static int dvb_frontend_open(struct inod fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT; fepriv->tone = -1; fepriv->voltage = -1; + fepriv->current_standard_set = 0; + fepriv->current_standard = 0; + if (fe->ops->set_standard) + fe->ops->set_standard(fe, 0); } return ret; diff -r 436e56df57d3 linux/drivers/media/dvb/dvb-core/dvb_frontend.h --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h Thu Apr 13 18:50:22 2006 -0300 +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h Sat Apr 22 13:38:01 2006 -0700 @@ -45,6 +45,7 @@ struct dvb_frontend_tune_settings { int step_size; int max_drift; struct dvb_frontend_parameters parameters; + struct dvb_frontend_parameters_new parameters_new; }; struct dvb_frontend; @@ -52,6 +53,7 @@ struct dvb_frontend_ops { struct dvb_frontend_ops { struct dvb_frontend_info info; + struct dvb_fe_caps_extended extended_info; void (*release)(struct dvb_frontend* fe); @@ -86,6 +88,10 @@ struct dvb_frontend_ops { int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg); int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); + int (*set_standard)(struct dvb_frontend* fe, fe_type_t type); + int (*get_extended_info)(struct dvb_frontend* fe, struct dvb_fe_caps_extended* extendedinfo); + int (*set_frontend2)(struct dvb_frontend* fe, struct dvb_frontend_parameters_new* params); + int (*get_frontend2)(struct dvb_frontend* fe, struct dvb_frontend_parameters_new* params); }; #define MAX_EVENT 8 diff -r 436e56df57d3 linux/include/linux/dvb/frontend.h --- a/linux/include/linux/dvb/frontend.h Thu Apr 13 18:50:22 2006 -0300 +++ b/linux/include/linux/dvb/frontend.h Sat Apr 22 13:38:01 2006 -0700 @@ -28,14 +28,23 @@ #include <asm/types.h> - +/** + * Usage of fe_type_t enumerations: + * Don't use FE_QPSK, FE_QAM, FE_OFDM any longer in new applications. + * If the FE_HAS_EXTENDED_INFO is set within the fe_caps, + * applications should ignore the fe_type_t returned by the FE_GET_INFO ioctl. + * + */ typedef enum fe_type { - FE_QPSK, - FE_QAM, - FE_OFDM, - FE_ATSC + FE_QPSK = 0, + FE_QAM = 1, + FE_OFDM = 2, + FE_ATSC = 3, + FE_DVB_S = (1 << 2), + FE_DVB_C = (1 << 3), + FE_DVB_T = (1 << 4), + FE_DVB_S2 = (1 << 5), } fe_type_t; - typedef enum fe_caps { FE_IS_STUPID = 0, @@ -62,11 +71,11 @@ typedef enum fe_caps { FE_CAN_HIERARCHY_AUTO = 0x100000, FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, + FE_HAS_EXTENDED_INFO = 0x10000000, FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending) FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output } fe_caps_t; - struct dvb_frontend_info { char name[128]; @@ -82,7 +91,6 @@ struct dvb_frontend_info { fe_caps_t caps; }; - /** * Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for * the meaning of this struct... @@ -126,9 +134,9 @@ typedef enum fe_status { FE_HAS_SYNC = 0x08, /* found sync bytes */ FE_HAS_LOCK = 0x10, /* everything's working... */ FE_TIMEDOUT = 0x20, /* no lock within the last ~2 seconds */ - FE_REINIT = 0x40 /* frontend was reinitialized, */ -} fe_status_t; /* application is recommended to reset */ - /* DiSEqC, tone and parameters */ + FE_REINIT = 0x40 /* legacy cruft, do not use */ +} fe_status_t; + typedef enum fe_spectral_inversion { INVERSION_OFF, @@ -147,21 +155,45 @@ typedef enum fe_code_rate { FEC_6_7, FEC_7_8, FEC_8_9, - FEC_AUTO + FEC_AUTO, + FEC_3_5, + FEC_9_10 } fe_code_rate_t; typedef enum fe_modulation { - QPSK, - QAM_16, - QAM_32, - QAM_64, - QAM_128, - QAM_256, - QAM_AUTO, - VSB_8, - VSB_16 + MOD_QPSK = 0, + QPSK = 0, + MOD_QAM_16 = 1, + QAM_16 = 1, + MOD_QAM_32 = 2, + QAM_32 = 2, + MOD_QAM_64 = 3, + QAM_64 = 3, + MOD_QAM_128 = 4, + QAM_128 = 4, + MOD_QAM_256 = 5, + QAM_256 = 5, + MOD_QAM_AUTO = 6, + QAM_AUTO = 6, + MOD_8VSB = 7, + VSB_8 = 7, + MOD_16VSB = 8, + VSB_16 = 8, + MOD_2VSB = 9, + MOD_4VSB = 10, + MOD_BPSK = 11, + MOD_16APSK = 12, + MOD_32APSK = 13, + MOD_8PSK = 14, + MOD_16PSK = 15, } fe_modulation_t; + +typedef enum fe_rolloff_factor { + ROLLOFF_ALPHA_0_35, + ROLLOFF_ALPHA_0_25, + ROLLOFF_ALPHA_0_20 +} fe_rolloff_factor_t; typedef enum fe_transmit_mode { TRANSMISSION_MODE_2K, @@ -194,23 +226,34 @@ typedef enum fe_hierarchy { HIERARCHY_AUTO } fe_hierarchy_t; - -struct dvb_qpsk_parameters { - __u32 symbol_rate; /* symbol rate in Symbols per second */ +/** + * this struct will be filled by the FE_GET_EXTENDED_INFO ioctl. + * it is a extension to the normal frontend capabilities and provided + * if the dvb_fe_info.caps is having the FE_HAS_EXTENDED_INFO bit set. + */ +struct dvb_fe_caps_extended { + __u32 fecs; /* supported fecs */ + __u32 modulations; /* supported modulations */ + __u32 standards; /* supported frontend_types */ +}; + + +struct dvb_dvbs_parameters { + __u32 symbol_rate; /* symbol rate in symbols per second */ fe_code_rate_t fec_inner; /* forward error correction (see above) */ }; -struct dvb_qam_parameters { - __u32 symbol_rate; /* symbol rate in Symbols per second */ +struct dvb_dvbc_parameters { + __u32 symbol_rate; /* symbol rate in symbols per second */ fe_code_rate_t fec_inner; /* forward error correction (see above) */ fe_modulation_t modulation; /* modulation type (see above) */ }; -struct dvb_vsb_parameters { +struct dvb_atsc_parameters { fe_modulation_t modulation; /* modulation type (see above) */ }; -struct dvb_ofdm_parameters { +struct dvb_dvbt_parameters { fe_bandwidth_t bandwidth; fe_code_rate_t code_rate_HP; /* high priority stream code rate */ fe_code_rate_t code_rate_LP; /* low priority stream code rate */ @@ -220,25 +263,75 @@ struct dvb_ofdm_parameters { fe_hierarchy_t hierarchy_information; }; - +struct dvb_dvbs2_parameters { + __u32 symbol_rate; /* symbol rate in symbols per second */ + fe_code_rate_t fec_inner; /* forward error correction (see above) */ + fe_modulation_t modulation; /* modulation type (see above) */ + fe_rolloff_factor_t rolloff_factor; /* rolloff factor needed for dvb-s2 */ +}; + +/* The following structure is used to allocate enough space in the union + for future expansion. +*/ +struct dvb_private_parameters { + __u32 priv[64]; +}; + +#define dvb_qpsk_parameters dvb_dvbs_parameters +#define dvb_qam_parameters dvb_dvbc_parameters +#define dvb_ofdm_parameters dvb_dvbt_parameters +#define dvb_vsb_parameters dvb_atsc_parameters + +/* just kept for backwards binary compatibility + * deprecated for usage in actual applications + */ struct dvb_frontend_parameters { __u32 frequency; /* (absolute) frequency in Hz for QAM/OFDM/ATSC */ /* intermediate frequency in kHz for QPSK */ fe_spectral_inversion_t inversion; union { - struct dvb_qpsk_parameters qpsk; - struct dvb_qam_parameters qam; - struct dvb_ofdm_parameters ofdm; - struct dvb_vsb_parameters vsb; + struct dvb_dvbs_parameters qpsk; + struct dvb_dvbc_parameters qam; + struct dvb_dvbt_parameters ofdm; + struct dvb_atsc_parameters vsb; } u; -}; - +};// __attribute((__deprecated__)); + +typedef union { + struct dvb_dvbs_parameters qpsk; + struct dvb_dvbc_parameters qam; + struct dvb_dvbt_parameters ofdm; + struct dvb_atsc_parameters vsb; + struct dvb_dvbs2_parameters qpsk2; + struct dvb_private_parameters priv; +} frontend_parameters_union; + +struct dvb_frontend_parameters_new { + __u32 frequency; /* (absolute) frequency in Hz for QAM/OFDM/ATSC */ + /* intermediate frequency in kHz for QPSK */ + fe_spectral_inversion_t inversion; + frontend_parameters_union u; +}; + +/** + * Important: + * FE_GET_EVENT and struct dvb_frontend_event + * are deprecated due to: + * FE_GET_EVENT is a mis-designed ioctl + * informations within dvb_frontend_event will + * always return stale information. + * Applications should: + * - open the frontend device with O_NONBLOCK + * - poll() for events + * - FE_GET_EVENT all pending events to clear the POLLPRI status, + * and throw them away + * - FE_READ_STATUS etc. to get current information + */ struct dvb_frontend_event { fe_status_t status; struct dvb_frontend_parameters parameters; -}; - +}; // __attribute((__deprecated__)); /** * When set, this flag will disable any zigzagging or other "normal" tuning @@ -267,9 +360,36 @@ struct dvb_frontend_event { #define FE_READ_SNR _IOR('o', 72, __u16) #define FE_READ_UNCORRECTED_BLOCKS _IOR('o', 73, __u32) +#define FE_SET_FRONTEND2 _IOW('o', 84, struct dvb_frontend_parameters_new) +#define FE_GET_FRONTEND2 _IOR('o', 85, struct dvb_frontend_parameters_new) + +/** + * next two IOCTLs are deprecated for further use in applications + */ #define FE_SET_FRONTEND _IOW('o', 76, struct dvb_frontend_parameters) #define FE_GET_FRONTEND _IOR('o', 77, struct dvb_frontend_parameters) + + #define FE_SET_FRONTEND_TUNE_MODE _IO('o', 81) /* unsigned int */ + +/** + * use to set the FE_STANDARD - if a tuner supports more than one type. + * e.g. DVB-C/T or DVB-S/S2 combi frontends. after FE_SET_STANDARD was set, + * the drivers has to make sure still to reflect the standards available, + * but capabilities should be adjusted to the selected standard + */ +#define FE_SET_STANDARD _IO('o', 82) /* fe_type_t */ + +/** + * used to query the extended frontend capabilities (see above for details) + */ +#define FE_GET_EXTENDED_INFO _IOR('o', 83, struct dvb_fe_caps_extended) + +/** + * FE_GET_EVENT is deprecated for applications. + * you should use FE_READ_STATUS and if needed the FE_GET_FRONTEND_NEW + * IOCTLs. + */ #define FE_GET_EVENT _IOR('o', 78, struct dvb_frontend_event) #define FE_DISHNETWORK_SEND_LEGACY_CMD _IO('o', 80) /* unsigned int */ _______________________________________________ linux-dvb@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb