1/* DVB USB compliant Linux driver for the 2 * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module 3 * 4 * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) 5 * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) 6 * 7 * Thanks to GENPIX for the sample code used to implement this module. 8 * 9 * This module is based off the vp7045 and vp702x modules 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the Free 13 * Software Foundation, version 2. 14 * 15 * see Documentation/dvb/README.dvb-usb for more information 16 */ 17#include "gp8psk.h" 18 19struct gp8psk_fe_state { 20 struct dvb_frontend fe; 21 struct dvb_usb_device *d; 22 u8 lock; 23 u16 snr; 24 unsigned long next_status_check; 25 unsigned long status_check_interval; 26}; 27 28static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) 29{ 30 struct gp8psk_fe_state *st = fe->demodulator_priv; 31 u8 status; 32 gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); 33 return status & bmDCtuned; 34} 35 36static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) 37{ 38 struct gp8psk_fe_state *state = fe->demodulator_priv; 39 return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); 40} 41 42static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) 43{ 44 u8 buf[6]; 45 if (time_after(jiffies,st->next_status_check)) { 46 gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1); 47 gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6); 48 st->snr = (buf[1]) << 8 | buf[0]; 49 st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; 50 } 51 return 0; 52} 53 54static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) 55{ 56 struct gp8psk_fe_state *st = fe->demodulator_priv; 57 gp8psk_fe_update_status(st); 58 59 if (st->lock) 60 *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; 61 else 62 *status = 0; 63 64 if (*status & FE_HAS_LOCK) 65 st->status_check_interval = 1000; 66 else 67 st->status_check_interval = 100; 68 return 0; 69} 70 71/* not supported by this Frontend */ 72static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) 73{ 74 (void) fe; 75 *ber = 0; 76 return 0; 77} 78 79/* not supported by this Frontend */ 80static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) 81{ 82 (void) fe; 83 *unc = 0; 84 return 0; 85} 86 87static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) 88{ 89 struct gp8psk_fe_state *st = fe->demodulator_priv; 90 gp8psk_fe_update_status(st); 91 /* snr is reported in dBu*256 */ 92 *snr = st->snr; 93 return 0; 94} 95 96static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) 97{ 98 struct gp8psk_fe_state *st = fe->demodulator_priv; 99 gp8psk_fe_update_status(st); 100 /* snr is reported in dBu*256 */ 101 /* snr / 38.4 ~= 100% strength */ 102 /* snr * 17 returns 100% strength as 65535 */ 103 if (st->snr > 0xf00) 104 *strength = 0xffff; 105 else 106 *strength = (st->snr << 4) + st->snr; /* snr*17 */ 107 return 0; 108} 109 110static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) 111{ 112 tune->min_delay_ms = 800; 113 return 0; 114} 115 116static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) 117{ 118 struct gp8psk_fe_state *state = fe->demodulator_priv; 119 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 120 u8 cmd[10]; 121 u32 freq = c->frequency * 1000; 122 int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); 123 124 deb_fe("%s()\n", __func__); 125 126 cmd[4] = freq & 0xff; 127 cmd[5] = (freq >> 8) & 0xff; 128 cmd[6] = (freq >> 16) & 0xff; 129 cmd[7] = (freq >> 24) & 0xff; 130 131 /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ 132 if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) 133 c->delivery_system = SYS_TURBO; 134 135 switch (c->delivery_system) { 136 case SYS_DVBS: 137 if (c->modulation != QPSK) { 138 deb_fe("%s: unsupported modulation selected (%d)\n", 139 __func__, c->modulation); 140 return -EOPNOTSUPP; 141 } 142 c->fec_inner = FEC_AUTO; 143 break; 144 case SYS_DVBS2: /* kept for backwards compatibility */ 145 deb_fe("%s: DVB-S2 delivery system selected\n", __func__); 146 break; 147 case SYS_TURBO: 148 deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); 149 break; 150 151 default: 152 deb_fe("%s: unsupported delivery system selected (%d)\n", 153 __func__, c->delivery_system); 154 return -EOPNOTSUPP; 155 } 156 157 cmd[0] = c->symbol_rate & 0xff; 158 cmd[1] = (c->symbol_rate >> 8) & 0xff; 159 cmd[2] = (c->symbol_rate >> 16) & 0xff; 160 cmd[3] = (c->symbol_rate >> 24) & 0xff; 161 switch (c->modulation) { 162 case QPSK: 163 if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) 164 if (gp8psk_tuned_to_DCII(fe)) 165 gp8psk_bcm4500_reload(state->d); 166 switch (c->fec_inner) { 167 case FEC_1_2: 168 cmd[9] = 0; break; 169 case FEC_2_3: 170 cmd[9] = 1; break; 171 case FEC_3_4: 172 cmd[9] = 2; break; 173 case FEC_5_6: 174 cmd[9] = 3; break; 175 case FEC_7_8: 176 cmd[9] = 4; break; 177 case FEC_AUTO: 178 cmd[9] = 5; break; 179 default: 180 cmd[9] = 5; break; 181 } 182 if (c->delivery_system == SYS_TURBO) 183 cmd[8] = ADV_MOD_TURBO_QPSK; 184 else 185 cmd[8] = ADV_MOD_DVB_QPSK; 186 break; 187 case PSK_8: /* PSK_8 is for compatibility with DN */ 188 cmd[8] = ADV_MOD_TURBO_8PSK; 189 switch (c->fec_inner) { 190 case FEC_2_3: 191 cmd[9] = 0; break; 192 case FEC_3_4: 193 cmd[9] = 1; break; 194 case FEC_3_5: 195 cmd[9] = 2; break; 196 case FEC_5_6: 197 cmd[9] = 3; break; 198 case FEC_8_9: 199 cmd[9] = 4; break; 200 default: 201 cmd[9] = 0; break; 202 } 203 break; 204 case QAM_16: /* QAM_16 is for compatibility with DN */ 205 cmd[8] = ADV_MOD_TURBO_16QAM; 206 cmd[9] = 0; 207 break; 208 default: /* Unknown modulation */ 209 deb_fe("%s: unsupported modulation selected (%d)\n", 210 __func__, c->modulation); 211 return -EOPNOTSUPP; 212 } 213 214 if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) 215 gp8psk_set_tuner_mode(fe, 0); 216 gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); 217 218 state->lock = 0; 219 state->next_status_check = jiffies; 220 state->status_check_interval = 200; 221 222 return 0; 223} 224 225static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, 226 struct dvb_diseqc_master_cmd *m) 227{ 228 struct gp8psk_fe_state *st = fe->demodulator_priv; 229 230 deb_fe("%s\n",__func__); 231 232 if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0, 233 m->msg, m->msg_len)) { 234 return -EINVAL; 235 } 236 return 0; 237} 238 239static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe, 240 fe_sec_mini_cmd_t burst) 241{ 242 struct gp8psk_fe_state *st = fe->demodulator_priv; 243 u8 cmd; 244 245 deb_fe("%s\n",__func__); 246 247 /* These commands are certainly wrong */ 248 cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; 249 250 if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0, 251 &cmd, 0)) { 252 return -EINVAL; 253 } 254 return 0; 255} 256 257static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) 258{ 259 struct gp8psk_fe_state* state = fe->demodulator_priv; 260 261 if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE, 262 (tone == SEC_TONE_ON), 0, NULL, 0)) { 263 return -EINVAL; 264 } 265 return 0; 266} 267 268static int gp8psk_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) 269{ 270 struct gp8psk_fe_state* state = fe->demodulator_priv; 271 272 if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, 273 voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { 274 return -EINVAL; 275 } 276 return 0; 277} 278 279static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) 280{ 281 struct gp8psk_fe_state* state = fe->demodulator_priv; 282 return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,0); 283} 284 285static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) 286{ 287 struct gp8psk_fe_state* state = fe->demodulator_priv; 288 u8 cmd = sw_cmd & 0x7f; 289 290 if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0, 291 NULL, 0)) { 292 return -EINVAL; 293 } 294 if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), 295 0, NULL, 0)) { 296 return -EINVAL; 297 } 298 299 return 0; 300} 301 302static void gp8psk_fe_release(struct dvb_frontend* fe) 303{ 304 struct gp8psk_fe_state *state = fe->demodulator_priv; 305 kfree(state); 306} 307 308static struct dvb_frontend_ops gp8psk_fe_ops; 309 310struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d) 311{ 312 struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); 313 if (s == NULL) 314 goto error; 315 316 s->d = d; 317 memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); 318 s->fe.demodulator_priv = s; 319 320 goto success; 321error: 322 return NULL; 323success: 324 return &s->fe; 325} 326 327 328static struct dvb_frontend_ops gp8psk_fe_ops = { 329 .delsys = { SYS_DVBS }, 330 .info = { 331 .name = "Genpix DVB-S", 332 .frequency_min = 800000, 333 .frequency_max = 2250000, 334 .frequency_stepsize = 100, 335 .symbol_rate_min = 1000000, 336 .symbol_rate_max = 45000000, 337 .symbol_rate_tolerance = 500, /* ppm */ 338 .caps = FE_CAN_INVERSION_AUTO | 339 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 340 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 341 /* 342 * FE_CAN_QAM_16 is for compatibility 343 * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) 344 */ 345 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC 346 }, 347 348 .release = gp8psk_fe_release, 349 350 .init = NULL, 351 .sleep = NULL, 352 353 .set_frontend = gp8psk_fe_set_frontend, 354 355 .get_tune_settings = gp8psk_fe_get_tune_settings, 356 357 .read_status = gp8psk_fe_read_status, 358 .read_ber = gp8psk_fe_read_ber, 359 .read_signal_strength = gp8psk_fe_read_signal_strength, 360 .read_snr = gp8psk_fe_read_snr, 361 .read_ucblocks = gp8psk_fe_read_unc_blocks, 362 363 .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, 364 .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, 365 .set_tone = gp8psk_fe_set_tone, 366 .set_voltage = gp8psk_fe_set_voltage, 367 .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, 368 .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage 369}; 370