1/* 2 * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. 3 * 4 * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) 5 * 6 * Based on the dvb-usb-framework code and the 7 * original Terratec Cinergy T2 driver by: 8 * 9 * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and 10 * Holger Waechtler <holger@qanu.de> 11 * 12 * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 27 * 28 */ 29 30#include "cinergyT2.h" 31 32 33/** 34 * convert linux-dvb frontend parameter set into TPS. 35 * See ETSI ETS-300744, section 4.6.2, table 9 for details. 36 * 37 * This function is probably reusable and may better get placed in a support 38 * library. 39 * 40 * We replace errornous fields by default TPS fields (the ones with value 0). 41 */ 42 43static uint16_t compute_tps(struct dtv_frontend_properties *op) 44{ 45 uint16_t tps = 0; 46 47 switch (op->code_rate_HP) { 48 case FEC_2_3: 49 tps |= (1 << 7); 50 break; 51 case FEC_3_4: 52 tps |= (2 << 7); 53 break; 54 case FEC_5_6: 55 tps |= (3 << 7); 56 break; 57 case FEC_7_8: 58 tps |= (4 << 7); 59 break; 60 case FEC_1_2: 61 case FEC_AUTO: 62 default: 63 /* tps |= (0 << 7) */; 64 } 65 66 switch (op->code_rate_LP) { 67 case FEC_2_3: 68 tps |= (1 << 4); 69 break; 70 case FEC_3_4: 71 tps |= (2 << 4); 72 break; 73 case FEC_5_6: 74 tps |= (3 << 4); 75 break; 76 case FEC_7_8: 77 tps |= (4 << 4); 78 break; 79 case FEC_1_2: 80 case FEC_AUTO: 81 default: 82 /* tps |= (0 << 4) */; 83 } 84 85 switch (op->modulation) { 86 case QAM_16: 87 tps |= (1 << 13); 88 break; 89 case QAM_64: 90 tps |= (2 << 13); 91 break; 92 case QPSK: 93 default: 94 /* tps |= (0 << 13) */; 95 } 96 97 switch (op->transmission_mode) { 98 case TRANSMISSION_MODE_8K: 99 tps |= (1 << 0); 100 break; 101 case TRANSMISSION_MODE_2K: 102 default: 103 /* tps |= (0 << 0) */; 104 } 105 106 switch (op->guard_interval) { 107 case GUARD_INTERVAL_1_16: 108 tps |= (1 << 2); 109 break; 110 case GUARD_INTERVAL_1_8: 111 tps |= (2 << 2); 112 break; 113 case GUARD_INTERVAL_1_4: 114 tps |= (3 << 2); 115 break; 116 case GUARD_INTERVAL_1_32: 117 default: 118 /* tps |= (0 << 2) */; 119 } 120 121 switch (op->hierarchy) { 122 case HIERARCHY_1: 123 tps |= (1 << 10); 124 break; 125 case HIERARCHY_2: 126 tps |= (2 << 10); 127 break; 128 case HIERARCHY_4: 129 tps |= (3 << 10); 130 break; 131 case HIERARCHY_NONE: 132 default: 133 /* tps |= (0 << 10) */; 134 } 135 136 return tps; 137} 138 139struct cinergyt2_fe_state { 140 struct dvb_frontend fe; 141 struct dvb_usb_device *d; 142}; 143 144static int cinergyt2_fe_read_status(struct dvb_frontend *fe, 145 enum fe_status *status) 146{ 147 struct cinergyt2_fe_state *state = fe->demodulator_priv; 148 struct dvbt_get_status_msg result; 149 u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; 150 int ret; 151 152 ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result, 153 sizeof(result), 0); 154 if (ret < 0) 155 return ret; 156 157 *status = 0; 158 159 if (0xffff - le16_to_cpu(result.gain) > 30) 160 *status |= FE_HAS_SIGNAL; 161 if (result.lock_bits & (1 << 6)) 162 *status |= FE_HAS_LOCK; 163 if (result.lock_bits & (1 << 5)) 164 *status |= FE_HAS_SYNC; 165 if (result.lock_bits & (1 << 4)) 166 *status |= FE_HAS_CARRIER; 167 if (result.lock_bits & (1 << 1)) 168 *status |= FE_HAS_VITERBI; 169 170 if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != 171 (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) 172 *status &= ~FE_HAS_LOCK; 173 174 return 0; 175} 176 177static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber) 178{ 179 struct cinergyt2_fe_state *state = fe->demodulator_priv; 180 struct dvbt_get_status_msg status; 181 char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; 182 int ret; 183 184 ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, 185 sizeof(status), 0); 186 if (ret < 0) 187 return ret; 188 189 *ber = le32_to_cpu(status.viterbi_error_rate); 190 return 0; 191} 192 193static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) 194{ 195 struct cinergyt2_fe_state *state = fe->demodulator_priv; 196 struct dvbt_get_status_msg status; 197 u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; 198 int ret; 199 200 ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status, 201 sizeof(status), 0); 202 if (ret < 0) { 203 err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n", 204 ret); 205 return ret; 206 } 207 *unc = le32_to_cpu(status.uncorrected_block_count); 208 return 0; 209} 210 211static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe, 212 u16 *strength) 213{ 214 struct cinergyt2_fe_state *state = fe->demodulator_priv; 215 struct dvbt_get_status_msg status; 216 char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; 217 int ret; 218 219 ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, 220 sizeof(status), 0); 221 if (ret < 0) { 222 err("cinergyt2_fe_read_signal_strength() Failed!" 223 " (Error=%d)\n", ret); 224 return ret; 225 } 226 *strength = (0xffff - le16_to_cpu(status.gain)); 227 return 0; 228} 229 230static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr) 231{ 232 struct cinergyt2_fe_state *state = fe->demodulator_priv; 233 struct dvbt_get_status_msg status; 234 char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; 235 int ret; 236 237 ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, 238 sizeof(status), 0); 239 if (ret < 0) { 240 err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret); 241 return ret; 242 } 243 *snr = (status.snr << 8) | status.snr; 244 return 0; 245} 246 247static int cinergyt2_fe_init(struct dvb_frontend *fe) 248{ 249 return 0; 250} 251 252static int cinergyt2_fe_sleep(struct dvb_frontend *fe) 253{ 254 deb_info("cinergyt2_fe_sleep() Called\n"); 255 return 0; 256} 257 258static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe, 259 struct dvb_frontend_tune_settings *tune) 260{ 261 tune->min_delay_ms = 800; 262 return 0; 263} 264 265static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) 266{ 267 struct dtv_frontend_properties *fep = &fe->dtv_property_cache; 268 struct cinergyt2_fe_state *state = fe->demodulator_priv; 269 struct dvbt_set_parameters_msg param; 270 char result[2]; 271 int err; 272 273 param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; 274 param.tps = cpu_to_le16(compute_tps(fep)); 275 param.freq = cpu_to_le32(fep->frequency / 1000); 276 param.flags = 0; 277 278 switch (fep->bandwidth_hz) { 279 default: 280 case 8000000: 281 param.bandwidth = 8; 282 break; 283 case 7000000: 284 param.bandwidth = 7; 285 break; 286 case 6000000: 287 param.bandwidth = 6; 288 break; 289 } 290 291 err = dvb_usb_generic_rw(state->d, 292 (char *)¶m, sizeof(param), 293 result, sizeof(result), 0); 294 if (err < 0) 295 err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err); 296 297 return (err < 0) ? err : 0; 298} 299 300static void cinergyt2_fe_release(struct dvb_frontend *fe) 301{ 302 struct cinergyt2_fe_state *state = fe->demodulator_priv; 303 kfree(state); 304} 305 306static struct dvb_frontend_ops cinergyt2_fe_ops; 307 308struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d) 309{ 310 struct cinergyt2_fe_state *s = kzalloc(sizeof( 311 struct cinergyt2_fe_state), GFP_KERNEL); 312 if (s == NULL) 313 return NULL; 314 315 s->d = d; 316 memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops)); 317 s->fe.demodulator_priv = s; 318 return &s->fe; 319} 320 321 322static struct dvb_frontend_ops cinergyt2_fe_ops = { 323 .delsys = { SYS_DVBT }, 324 .info = { 325 .name = DRIVER_NAME, 326 .frequency_min = 174000000, 327 .frequency_max = 862000000, 328 .frequency_stepsize = 166667, 329 .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 330 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 331 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 332 | FE_CAN_FEC_AUTO | FE_CAN_QPSK 333 | FE_CAN_QAM_16 | FE_CAN_QAM_64 334 | FE_CAN_QAM_AUTO 335 | FE_CAN_TRANSMISSION_MODE_AUTO 336 | FE_CAN_GUARD_INTERVAL_AUTO 337 | FE_CAN_HIERARCHY_AUTO 338 | FE_CAN_RECOVER 339 | FE_CAN_MUTE_TS 340 }, 341 342 .release = cinergyt2_fe_release, 343 344 .init = cinergyt2_fe_init, 345 .sleep = cinergyt2_fe_sleep, 346 347 .set_frontend = cinergyt2_fe_set_frontend, 348 .get_tune_settings = cinergyt2_fe_get_tune_settings, 349 350 .read_status = cinergyt2_fe_read_status, 351 .read_ber = cinergyt2_fe_read_ber, 352 .read_signal_strength = cinergyt2_fe_read_signal_strength, 353 .read_snr = cinergyt2_fe_read_snr, 354 .read_ucblocks = cinergyt2_fe_read_unc_blocks, 355}; 356