]>
Commit | Line | Data |
---|---|---|
986bd1e5 TO |
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 | * | |
986bd1e5 TO |
24 | */ |
25 | ||
26 | #include "cinergyT2.h" | |
27 | ||
28 | ||
cba862dc | 29 | /* |
986bd1e5 TO |
30 | * convert linux-dvb frontend parameter set into TPS. |
31 | * See ETSI ETS-300744, section 4.6.2, table 9 for details. | |
32 | * | |
33 | * This function is probably reusable and may better get placed in a support | |
34 | * library. | |
35 | * | |
36 | * We replace errornous fields by default TPS fields (the ones with value 0). | |
37 | */ | |
38 | ||
7830bbaf | 39 | static uint16_t compute_tps(struct dtv_frontend_properties *op) |
986bd1e5 | 40 | { |
986bd1e5 TO |
41 | uint16_t tps = 0; |
42 | ||
43 | switch (op->code_rate_HP) { | |
44 | case FEC_2_3: | |
45 | tps |= (1 << 7); | |
46 | break; | |
47 | case FEC_3_4: | |
48 | tps |= (2 << 7); | |
49 | break; | |
50 | case FEC_5_6: | |
51 | tps |= (3 << 7); | |
52 | break; | |
53 | case FEC_7_8: | |
54 | tps |= (4 << 7); | |
55 | break; | |
56 | case FEC_1_2: | |
57 | case FEC_AUTO: | |
58 | default: | |
59 | /* tps |= (0 << 7) */; | |
60 | } | |
61 | ||
62 | switch (op->code_rate_LP) { | |
63 | case FEC_2_3: | |
64 | tps |= (1 << 4); | |
65 | break; | |
66 | case FEC_3_4: | |
67 | tps |= (2 << 4); | |
68 | break; | |
69 | case FEC_5_6: | |
70 | tps |= (3 << 4); | |
71 | break; | |
72 | case FEC_7_8: | |
73 | tps |= (4 << 4); | |
74 | break; | |
75 | case FEC_1_2: | |
76 | case FEC_AUTO: | |
77 | default: | |
78 | /* tps |= (0 << 4) */; | |
79 | } | |
80 | ||
7830bbaf | 81 | switch (op->modulation) { |
986bd1e5 TO |
82 | case QAM_16: |
83 | tps |= (1 << 13); | |
84 | break; | |
85 | case QAM_64: | |
86 | tps |= (2 << 13); | |
87 | break; | |
88 | case QPSK: | |
89 | default: | |
90 | /* tps |= (0 << 13) */; | |
91 | } | |
92 | ||
93 | switch (op->transmission_mode) { | |
94 | case TRANSMISSION_MODE_8K: | |
95 | tps |= (1 << 0); | |
96 | break; | |
97 | case TRANSMISSION_MODE_2K: | |
98 | default: | |
99 | /* tps |= (0 << 0) */; | |
100 | } | |
101 | ||
102 | switch (op->guard_interval) { | |
103 | case GUARD_INTERVAL_1_16: | |
104 | tps |= (1 << 2); | |
105 | break; | |
106 | case GUARD_INTERVAL_1_8: | |
107 | tps |= (2 << 2); | |
108 | break; | |
109 | case GUARD_INTERVAL_1_4: | |
110 | tps |= (3 << 2); | |
111 | break; | |
112 | case GUARD_INTERVAL_1_32: | |
113 | default: | |
114 | /* tps |= (0 << 2) */; | |
115 | } | |
116 | ||
7830bbaf | 117 | switch (op->hierarchy) { |
986bd1e5 TO |
118 | case HIERARCHY_1: |
119 | tps |= (1 << 10); | |
120 | break; | |
121 | case HIERARCHY_2: | |
122 | tps |= (2 << 10); | |
123 | break; | |
124 | case HIERARCHY_4: | |
125 | tps |= (3 << 10); | |
126 | break; | |
127 | case HIERARCHY_NONE: | |
128 | default: | |
129 | /* tps |= (0 << 10) */; | |
130 | } | |
131 | ||
132 | return tps; | |
133 | } | |
134 | ||
135 | struct cinergyt2_fe_state { | |
136 | struct dvb_frontend fe; | |
137 | struct dvb_usb_device *d; | |
0d43c0ff MCC |
138 | |
139 | unsigned char data[64]; | |
140 | struct mutex data_mutex; | |
141 | ||
c2730eef | 142 | struct dvbt_get_status_msg status; |
986bd1e5 TO |
143 | }; |
144 | ||
145 | static int cinergyt2_fe_read_status(struct dvb_frontend *fe, | |
0df289a2 | 146 | enum fe_status *status) |
986bd1e5 TO |
147 | { |
148 | struct cinergyt2_fe_state *state = fe->demodulator_priv; | |
986bd1e5 TO |
149 | int ret; |
150 | ||
0d43c0ff MCC |
151 | mutex_lock(&state->data_mutex); |
152 | state->data[0] = CINERGYT2_EP1_GET_TUNER_STATUS; | |
153 | ||
154 | ret = dvb_usb_generic_rw(state->d, state->data, 1, | |
155 | state->data, sizeof(state->status), 0); | |
156 | if (!ret) | |
157 | memcpy(&state->status, state->data, sizeof(state->status)); | |
158 | mutex_unlock(&state->data_mutex); | |
159 | ||
986bd1e5 TO |
160 | if (ret < 0) |
161 | return ret; | |
162 | ||
163 | *status = 0; | |
164 | ||
0d43c0ff | 165 | if (0xffff - le16_to_cpu(state->status.gain) > 30) |
986bd1e5 | 166 | *status |= FE_HAS_SIGNAL; |
0d43c0ff | 167 | if (state->status.lock_bits & (1 << 6)) |
986bd1e5 | 168 | *status |= FE_HAS_LOCK; |
0d43c0ff | 169 | if (state->status.lock_bits & (1 << 5)) |
986bd1e5 | 170 | *status |= FE_HAS_SYNC; |
0d43c0ff | 171 | if (state->status.lock_bits & (1 << 4)) |
986bd1e5 | 172 | *status |= FE_HAS_CARRIER; |
0d43c0ff | 173 | if (state->status.lock_bits & (1 << 1)) |
986bd1e5 TO |
174 | *status |= FE_HAS_VITERBI; |
175 | ||
176 | if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != | |
177 | (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) | |
178 | *status &= ~FE_HAS_LOCK; | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber) | |
184 | { | |
185 | struct cinergyt2_fe_state *state = fe->demodulator_priv; | |
986bd1e5 | 186 | |
c2730eef | 187 | *ber = le32_to_cpu(state->status.viterbi_error_rate); |
986bd1e5 TO |
188 | return 0; |
189 | } | |
190 | ||
191 | static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) | |
192 | { | |
193 | struct cinergyt2_fe_state *state = fe->demodulator_priv; | |
986bd1e5 | 194 | |
c2730eef | 195 | *unc = le32_to_cpu(state->status.uncorrected_block_count); |
986bd1e5 TO |
196 | return 0; |
197 | } | |
198 | ||
199 | static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe, | |
200 | u16 *strength) | |
201 | { | |
202 | struct cinergyt2_fe_state *state = fe->demodulator_priv; | |
986bd1e5 | 203 | |
c2730eef | 204 | *strength = (0xffff - le16_to_cpu(state->status.gain)); |
986bd1e5 TO |
205 | return 0; |
206 | } | |
207 | ||
208 | static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr) | |
209 | { | |
210 | struct cinergyt2_fe_state *state = fe->demodulator_priv; | |
986bd1e5 | 211 | |
c2730eef | 212 | *snr = (state->status.snr << 8) | state->status.snr; |
986bd1e5 TO |
213 | return 0; |
214 | } | |
215 | ||
216 | static int cinergyt2_fe_init(struct dvb_frontend *fe) | |
217 | { | |
218 | return 0; | |
219 | } | |
220 | ||
221 | static int cinergyt2_fe_sleep(struct dvb_frontend *fe) | |
222 | { | |
223 | deb_info("cinergyt2_fe_sleep() Called\n"); | |
224 | return 0; | |
225 | } | |
226 | ||
227 | static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe, | |
228 | struct dvb_frontend_tune_settings *tune) | |
229 | { | |
230 | tune->min_delay_ms = 800; | |
231 | return 0; | |
232 | } | |
233 | ||
7830bbaf | 234 | static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) |
986bd1e5 | 235 | { |
7830bbaf | 236 | struct dtv_frontend_properties *fep = &fe->dtv_property_cache; |
986bd1e5 | 237 | struct cinergyt2_fe_state *state = fe->demodulator_priv; |
0d43c0ff | 238 | struct dvbt_set_parameters_msg *param; |
986bd1e5 TO |
239 | int err; |
240 | ||
0d43c0ff MCC |
241 | mutex_lock(&state->data_mutex); |
242 | ||
243 | param = (void *)state->data; | |
244 | param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; | |
245 | param->tps = cpu_to_le16(compute_tps(fep)); | |
246 | param->freq = cpu_to_le32(fep->frequency / 1000); | |
247 | param->flags = 0; | |
986bd1e5 | 248 | |
7830bbaf | 249 | switch (fep->bandwidth_hz) { |
aa104b2f | 250 | default: |
7830bbaf | 251 | case 8000000: |
0d43c0ff | 252 | param->bandwidth = 8; |
7830bbaf MCC |
253 | break; |
254 | case 7000000: | |
0d43c0ff | 255 | param->bandwidth = 7; |
7830bbaf MCC |
256 | break; |
257 | case 6000000: | |
0d43c0ff | 258 | param->bandwidth = 6; |
7830bbaf MCC |
259 | break; |
260 | } | |
261 | ||
0d43c0ff MCC |
262 | err = dvb_usb_generic_rw(state->d, state->data, sizeof(*param), |
263 | state->data, 2, 0); | |
986bd1e5 TO |
264 | if (err < 0) |
265 | err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err); | |
266 | ||
0d43c0ff | 267 | mutex_unlock(&state->data_mutex); |
986bd1e5 TO |
268 | return (err < 0) ? err : 0; |
269 | } | |
270 | ||
986bd1e5 TO |
271 | static void cinergyt2_fe_release(struct dvb_frontend *fe) |
272 | { | |
273 | struct cinergyt2_fe_state *state = fe->demodulator_priv; | |
33cef283 | 274 | kfree(state); |
986bd1e5 TO |
275 | } |
276 | ||
bd336e63 | 277 | static const struct dvb_frontend_ops cinergyt2_fe_ops; |
986bd1e5 TO |
278 | |
279 | struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d) | |
280 | { | |
281 | struct cinergyt2_fe_state *s = kzalloc(sizeof( | |
282 | struct cinergyt2_fe_state), GFP_KERNEL); | |
283 | if (s == NULL) | |
284 | return NULL; | |
285 | ||
286 | s->d = d; | |
287 | memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops)); | |
288 | s->fe.demodulator_priv = s; | |
0d43c0ff | 289 | mutex_init(&s->data_mutex); |
986bd1e5 TO |
290 | return &s->fe; |
291 | } | |
292 | ||
293 | ||
bd336e63 | 294 | static const struct dvb_frontend_ops cinergyt2_fe_ops = { |
7830bbaf | 295 | .delsys = { SYS_DVBT }, |
986bd1e5 TO |
296 | .info = { |
297 | .name = DRIVER_NAME, | |
986bd1e5 TO |
298 | .frequency_min = 174000000, |
299 | .frequency_max = 862000000, | |
300 | .frequency_stepsize = 166667, | |
301 | .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | |
302 | | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | |
303 | | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | |
304 | | FE_CAN_FEC_AUTO | FE_CAN_QPSK | |
305 | | FE_CAN_QAM_16 | FE_CAN_QAM_64 | |
306 | | FE_CAN_QAM_AUTO | |
307 | | FE_CAN_TRANSMISSION_MODE_AUTO | |
308 | | FE_CAN_GUARD_INTERVAL_AUTO | |
309 | | FE_CAN_HIERARCHY_AUTO | |
310 | | FE_CAN_RECOVER | |
311 | | FE_CAN_MUTE_TS | |
312 | }, | |
313 | ||
314 | .release = cinergyt2_fe_release, | |
315 | ||
316 | .init = cinergyt2_fe_init, | |
317 | .sleep = cinergyt2_fe_sleep, | |
318 | ||
7830bbaf | 319 | .set_frontend = cinergyt2_fe_set_frontend, |
986bd1e5 TO |
320 | .get_tune_settings = cinergyt2_fe_get_tune_settings, |
321 | ||
322 | .read_status = cinergyt2_fe_read_status, | |
323 | .read_ber = cinergyt2_fe_read_ber, | |
324 | .read_signal_strength = cinergyt2_fe_read_signal_strength, | |
325 | .read_snr = cinergyt2_fe_read_snr, | |
326 | .read_ucblocks = cinergyt2_fe_read_unc_blocks, | |
327 | }; |