1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * The Virtual DVB test driver serves as a reference DVB driver and helps |
4 | * validate the existing APIs in the media subsystem. It can also aid |
5 | * developers working on userspace applications. |
6 | * |
7 | * Copyright (C) 2020 Daniel W. S. Almeida |
8 | * Based on the example driver written by Emard <emard@softhome.net> |
9 | */ |
10 | |
11 | #include <linux/errno.h> |
12 | #include <linux/i2c.h> |
13 | #include <linux/init.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> |
16 | #include <linux/printk.h> |
17 | #include <linux/random.h> |
18 | #include <linux/ratelimit.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/string.h> |
21 | #include <linux/workqueue.h> |
22 | |
23 | #include <media/dvb_frontend.h> |
24 | |
25 | #include "vidtv_demod.h" |
26 | |
27 | #define POLL_THRD_TIME 2000 /* ms */ |
28 | |
29 | static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_c_cnr_2_qual[] = { |
30 | /* from libdvbv5 source code, in milli db */ |
31 | { QAM_256, FEC_NONE, 34000, 38000}, |
32 | { QAM_64, FEC_NONE, 30000, 34000}, |
33 | }; |
34 | |
35 | static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s_cnr_2_qual[] = { |
36 | /* from libdvbv5 source code, in milli db */ |
37 | { QPSK, FEC_1_2, 7000, 10000}, |
38 | { QPSK, FEC_2_3, 9000, 12000}, |
39 | { QPSK, FEC_3_4, 10000, 13000}, |
40 | { QPSK, FEC_5_6, 11000, 14000}, |
41 | { QPSK, FEC_7_8, 12000, 15000}, |
42 | }; |
43 | |
44 | static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s2_cnr_2_qual[] = { |
45 | /* from libdvbv5 source code, in milli db */ |
46 | { QPSK, FEC_1_2, 9000, 12000}, |
47 | { QPSK, FEC_2_3, 11000, 14000}, |
48 | { QPSK, FEC_3_4, 12000, 15000}, |
49 | { QPSK, FEC_5_6, 12000, 15000}, |
50 | { QPSK, FEC_8_9, 13000, 16000}, |
51 | { QPSK, FEC_9_10, 13500, 16500}, |
52 | { PSK_8, FEC_2_3, 14500, 17500}, |
53 | { PSK_8, FEC_3_4, 16000, 19000}, |
54 | { PSK_8, FEC_5_6, 17500, 20500}, |
55 | { PSK_8, FEC_8_9, 19000, 22000}, |
56 | }; |
57 | |
58 | static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_t_cnr_2_qual[] = { |
59 | /* from libdvbv5 source code, in milli db*/ |
60 | { QPSK, FEC_1_2, 4100, 5900}, |
61 | { QPSK, FEC_2_3, 6100, 9600}, |
62 | { QPSK, FEC_3_4, 7200, 12400}, |
63 | { QPSK, FEC_5_6, 8500, 15600}, |
64 | { QPSK, FEC_7_8, 9200, 17500}, |
65 | { QAM_16, FEC_1_2, 9800, 11800}, |
66 | { QAM_16, FEC_2_3, 12100, 15300}, |
67 | { QAM_16, FEC_3_4, 13400, 18100}, |
68 | { QAM_16, FEC_5_6, 14800, 21300}, |
69 | { QAM_16, FEC_7_8, 15700, 23600}, |
70 | { QAM_64, FEC_1_2, 14000, 16000}, |
71 | { QAM_64, FEC_2_3, 19900, 25400}, |
72 | { QAM_64, FEC_3_4, 24900, 27900}, |
73 | { QAM_64, FEC_5_6, 21300, 23300}, |
74 | { QAM_64, FEC_7_8, 22000, 24000}, |
75 | }; |
76 | |
77 | static const struct vidtv_demod_cnr_to_qual_s *vidtv_match_cnr_s(struct dvb_frontend *fe) |
78 | { |
79 | const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; |
80 | struct device *dev = fe->dvb->device; |
81 | struct dtv_frontend_properties *c; |
82 | u32 array_size = 0; |
83 | u32 i; |
84 | |
85 | c = &fe->dtv_property_cache; |
86 | |
87 | switch (c->delivery_system) { |
88 | case SYS_DVBT: |
89 | case SYS_DVBT2: |
90 | cnr2qual = vidtv_demod_t_cnr_2_qual; |
91 | array_size = ARRAY_SIZE(vidtv_demod_t_cnr_2_qual); |
92 | break; |
93 | |
94 | case SYS_DVBS: |
95 | cnr2qual = vidtv_demod_s_cnr_2_qual; |
96 | array_size = ARRAY_SIZE(vidtv_demod_s_cnr_2_qual); |
97 | break; |
98 | |
99 | case SYS_DVBS2: |
100 | cnr2qual = vidtv_demod_s2_cnr_2_qual; |
101 | array_size = ARRAY_SIZE(vidtv_demod_s2_cnr_2_qual); |
102 | break; |
103 | |
104 | case SYS_DVBC_ANNEX_A: |
105 | cnr2qual = vidtv_demod_c_cnr_2_qual; |
106 | array_size = ARRAY_SIZE(vidtv_demod_c_cnr_2_qual); |
107 | break; |
108 | |
109 | default: |
110 | dev_warn_ratelimited(dev, |
111 | "%s: unsupported delivery system: %u\n" , |
112 | __func__, |
113 | c->delivery_system); |
114 | break; |
115 | } |
116 | |
117 | for (i = 0; i < array_size; i++) |
118 | if (cnr2qual[i].modulation == c->modulation && |
119 | cnr2qual[i].fec == c->fec_inner) |
120 | return &cnr2qual[i]; |
121 | |
122 | return NULL; /* not found */ |
123 | } |
124 | |
125 | static void vidtv_clean_stats(struct dvb_frontend *fe) |
126 | { |
127 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
128 | |
129 | /* Fill the length of each status counter */ |
130 | |
131 | /* Signal is always available */ |
132 | c->strength.len = 1; |
133 | c->strength.stat[0].scale = FE_SCALE_DECIBEL; |
134 | c->strength.stat[0].svalue = 0; |
135 | |
136 | /* Usually available only after Viterbi lock */ |
137 | c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
138 | c->cnr.stat[0].svalue = 0; |
139 | c->cnr.len = 1; |
140 | |
141 | /* Those depends on full lock */ |
142 | c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
143 | c->pre_bit_error.stat[0].uvalue = 0; |
144 | c->pre_bit_error.len = 1; |
145 | c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
146 | c->pre_bit_count.stat[0].uvalue = 0; |
147 | c->pre_bit_count.len = 1; |
148 | c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
149 | c->post_bit_error.stat[0].uvalue = 0; |
150 | c->post_bit_error.len = 1; |
151 | c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
152 | c->post_bit_count.stat[0].uvalue = 0; |
153 | c->post_bit_count.len = 1; |
154 | c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
155 | c->block_error.stat[0].uvalue = 0; |
156 | c->block_error.len = 1; |
157 | c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; |
158 | c->block_count.stat[0].uvalue = 0; |
159 | c->block_count.len = 1; |
160 | } |
161 | |
162 | static void vidtv_demod_update_stats(struct dvb_frontend *fe) |
163 | { |
164 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
165 | struct vidtv_demod_state *state = fe->demodulator_priv; |
166 | u32 scale; |
167 | |
168 | if (state->status & FE_HAS_LOCK) { |
169 | scale = FE_SCALE_COUNTER; |
170 | c->cnr.stat[0].scale = FE_SCALE_DECIBEL; |
171 | } else { |
172 | scale = FE_SCALE_NOT_AVAILABLE; |
173 | c->cnr.stat[0].scale = scale; |
174 | } |
175 | |
176 | c->pre_bit_error.stat[0].scale = scale; |
177 | c->pre_bit_count.stat[0].scale = scale; |
178 | c->post_bit_error.stat[0].scale = scale; |
179 | c->post_bit_count.stat[0].scale = scale; |
180 | c->block_error.stat[0].scale = scale; |
181 | c->block_count.stat[0].scale = scale; |
182 | |
183 | /* |
184 | * Add a 0.5% of randomness at the signal strength and CNR, |
185 | * and make them different, as we want to have something closer |
186 | * to a real case scenario. |
187 | * |
188 | * Also, usually, signal strength is a negative number in dBm. |
189 | */ |
190 | c->strength.stat[0].svalue = state->tuner_cnr; |
191 | c->strength.stat[0].svalue -= get_random_u32_below(ceil: state->tuner_cnr / 50); |
192 | c->strength.stat[0].svalue -= 68000; /* Adjust to a better range */ |
193 | |
194 | c->cnr.stat[0].svalue = state->tuner_cnr; |
195 | c->cnr.stat[0].svalue -= get_random_u32_below(ceil: state->tuner_cnr / 50); |
196 | } |
197 | |
198 | static int vidtv_demod_read_status(struct dvb_frontend *fe, |
199 | enum fe_status *status) |
200 | { |
201 | struct vidtv_demod_state *state = fe->demodulator_priv; |
202 | const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; |
203 | struct vidtv_demod_config *config = &state->config; |
204 | u16 snr = 0; |
205 | |
206 | /* Simulate random lost of signal due to a bad-tuned channel */ |
207 | cnr2qual = vidtv_match_cnr_s(fe: &state->frontend); |
208 | |
209 | if (cnr2qual && state->tuner_cnr < cnr2qual->cnr_good && |
210 | state->frontend.ops.tuner_ops.get_rf_strength) { |
211 | state->frontend.ops.tuner_ops.get_rf_strength(&state->frontend, |
212 | &snr); |
213 | |
214 | if (snr < cnr2qual->cnr_ok) { |
215 | /* eventually lose the TS lock */ |
216 | if (get_random_u32_below(ceil: 100) < config->drop_tslock_prob_on_low_snr) |
217 | state->status = 0; |
218 | } else { |
219 | /* recover if the signal improves */ |
220 | if (get_random_u32_below(ceil: 100) < |
221 | config->recover_tslock_prob_on_good_snr) |
222 | state->status = FE_HAS_SIGNAL | |
223 | FE_HAS_CARRIER | |
224 | FE_HAS_VITERBI | |
225 | FE_HAS_SYNC | |
226 | FE_HAS_LOCK; |
227 | } |
228 | } |
229 | |
230 | vidtv_demod_update_stats(fe: &state->frontend); |
231 | |
232 | *status = state->status; |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | static int vidtv_demod_read_signal_strength(struct dvb_frontend *fe, |
238 | u16 *strength) |
239 | { |
240 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; |
241 | |
242 | *strength = c->strength.stat[0].uvalue; |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | /* |
248 | * NOTE: |
249 | * This is implemented here just to be used as an example for real |
250 | * demod drivers. |
251 | * |
252 | * Should only be implemented if it actually reads something from the hardware. |
253 | * Also, it should check for the locks, in order to avoid report wrong data |
254 | * to userspace. |
255 | */ |
256 | static int vidtv_demod_get_frontend(struct dvb_frontend *fe, |
257 | struct dtv_frontend_properties *p) |
258 | { |
259 | return 0; |
260 | } |
261 | |
262 | static int vidtv_demod_set_frontend(struct dvb_frontend *fe) |
263 | { |
264 | struct vidtv_demod_state *state = fe->demodulator_priv; |
265 | u32 tuner_status = 0; |
266 | int ret; |
267 | |
268 | if (!fe->ops.tuner_ops.set_params) |
269 | return 0; |
270 | |
271 | fe->ops.tuner_ops.set_params(fe); |
272 | |
273 | /* store the CNR returned by the tuner */ |
274 | ret = fe->ops.tuner_ops.get_rf_strength(fe, &state->tuner_cnr); |
275 | if (ret < 0) |
276 | return ret; |
277 | |
278 | fe->ops.tuner_ops.get_status(fe, &tuner_status); |
279 | state->status = (state->tuner_cnr > 0) ? FE_HAS_SIGNAL | |
280 | FE_HAS_CARRIER | |
281 | FE_HAS_VITERBI | |
282 | FE_HAS_SYNC | |
283 | FE_HAS_LOCK : |
284 | 0; |
285 | |
286 | vidtv_demod_update_stats(fe); |
287 | |
288 | if (fe->ops.i2c_gate_ctrl) |
289 | fe->ops.i2c_gate_ctrl(fe, 0); |
290 | |
291 | return 0; |
292 | } |
293 | |
294 | /* |
295 | * NOTE: |
296 | * This is implemented here just to be used as an example for real |
297 | * demod drivers. |
298 | * |
299 | * Should only be implemented if the demod has support for DVB-S or DVB-S2 |
300 | */ |
301 | static int vidtv_demod_set_tone(struct dvb_frontend *fe, |
302 | enum fe_sec_tone_mode tone) |
303 | { |
304 | return 0; |
305 | } |
306 | |
307 | /* |
308 | * NOTE: |
309 | * This is implemented here just to be used as an example for real |
310 | * demod drivers. |
311 | * |
312 | * Should only be implemented if the demod has support for DVB-S or DVB-S2 |
313 | */ |
314 | static int vidtv_demod_set_voltage(struct dvb_frontend *fe, |
315 | enum fe_sec_voltage voltage) |
316 | { |
317 | return 0; |
318 | } |
319 | |
320 | /* |
321 | * NOTE: |
322 | * This is implemented here just to be used as an example for real |
323 | * demod drivers. |
324 | * |
325 | * Should only be implemented if the demod has support for DVB-S or DVB-S2 |
326 | */ |
327 | static int vidtv_send_diseqc_msg(struct dvb_frontend *fe, |
328 | struct dvb_diseqc_master_cmd *cmd) |
329 | { |
330 | return 0; |
331 | } |
332 | |
333 | /* |
334 | * NOTE: |
335 | * This is implemented here just to be used as an example for real |
336 | * demod drivers. |
337 | * |
338 | * Should only be implemented if the demod has support for DVB-S or DVB-S2 |
339 | */ |
340 | static int vidtv_diseqc_send_burst(struct dvb_frontend *fe, |
341 | enum fe_sec_mini_cmd burst) |
342 | { |
343 | return 0; |
344 | } |
345 | |
346 | static void vidtv_demod_release(struct dvb_frontend *fe) |
347 | { |
348 | struct vidtv_demod_state *state = fe->demodulator_priv; |
349 | |
350 | kfree(objp: state); |
351 | } |
352 | |
353 | static const struct dvb_frontend_ops vidtv_demod_ops = { |
354 | .delsys = { |
355 | SYS_DVBT, |
356 | SYS_DVBT2, |
357 | SYS_DVBC_ANNEX_A, |
358 | SYS_DVBS, |
359 | SYS_DVBS2, |
360 | }, |
361 | |
362 | .info = { |
363 | .name = "Dummy demod for DVB-T/T2/C/S/S2" , |
364 | .frequency_min_hz = 51 * MHz, |
365 | .frequency_max_hz = 2150 * MHz, |
366 | .frequency_stepsize_hz = 62500, |
367 | .frequency_tolerance_hz = 29500 * kHz, |
368 | .symbol_rate_min = 1000000, |
369 | .symbol_rate_max = 45000000, |
370 | |
371 | .caps = FE_CAN_FEC_1_2 | |
372 | FE_CAN_FEC_2_3 | |
373 | FE_CAN_FEC_3_4 | |
374 | FE_CAN_FEC_4_5 | |
375 | FE_CAN_FEC_5_6 | |
376 | FE_CAN_FEC_6_7 | |
377 | FE_CAN_FEC_7_8 | |
378 | FE_CAN_FEC_8_9 | |
379 | FE_CAN_QAM_16 | |
380 | FE_CAN_QAM_64 | |
381 | FE_CAN_QAM_32 | |
382 | FE_CAN_QAM_128 | |
383 | FE_CAN_QAM_256 | |
384 | FE_CAN_QAM_AUTO | |
385 | FE_CAN_QPSK | |
386 | FE_CAN_FEC_AUTO | |
387 | FE_CAN_INVERSION_AUTO | |
388 | FE_CAN_TRANSMISSION_MODE_AUTO | |
389 | FE_CAN_GUARD_INTERVAL_AUTO | |
390 | FE_CAN_HIERARCHY_AUTO, |
391 | }, |
392 | |
393 | .release = vidtv_demod_release, |
394 | |
395 | .set_frontend = vidtv_demod_set_frontend, |
396 | .get_frontend = vidtv_demod_get_frontend, |
397 | |
398 | .read_status = vidtv_demod_read_status, |
399 | .read_signal_strength = vidtv_demod_read_signal_strength, |
400 | |
401 | /* For DVB-S/S2 */ |
402 | .set_voltage = vidtv_demod_set_voltage, |
403 | .set_tone = vidtv_demod_set_tone, |
404 | .diseqc_send_master_cmd = vidtv_send_diseqc_msg, |
405 | .diseqc_send_burst = vidtv_diseqc_send_burst, |
406 | |
407 | }; |
408 | |
409 | static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { |
410 | {"dvb_vidtv_demod" , 0}, |
411 | {} |
412 | }; |
413 | MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); |
414 | |
415 | static int vidtv_demod_i2c_probe(struct i2c_client *client) |
416 | { |
417 | struct vidtv_tuner_config *config = client->dev.platform_data; |
418 | struct vidtv_demod_state *state; |
419 | |
420 | /* allocate memory for the internal state */ |
421 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
422 | if (!state) |
423 | return -ENOMEM; |
424 | |
425 | /* create dvb_frontend */ |
426 | memcpy(&state->frontend.ops, |
427 | &vidtv_demod_ops, |
428 | sizeof(struct dvb_frontend_ops)); |
429 | |
430 | memcpy(&state->config, config, sizeof(state->config)); |
431 | |
432 | state->frontend.demodulator_priv = state; |
433 | i2c_set_clientdata(client, data: state); |
434 | |
435 | vidtv_clean_stats(fe: &state->frontend); |
436 | |
437 | return 0; |
438 | } |
439 | |
440 | static void vidtv_demod_i2c_remove(struct i2c_client *client) |
441 | { |
442 | struct vidtv_demod_state *state = i2c_get_clientdata(client); |
443 | |
444 | kfree(objp: state); |
445 | } |
446 | |
447 | static struct i2c_driver vidtv_demod_i2c_driver = { |
448 | .driver = { |
449 | .name = "dvb_vidtv_demod" , |
450 | .suppress_bind_attrs = true, |
451 | }, |
452 | .probe = vidtv_demod_i2c_probe, |
453 | .remove = vidtv_demod_i2c_remove, |
454 | .id_table = vidtv_demod_i2c_id_table, |
455 | }; |
456 | |
457 | module_i2c_driver(vidtv_demod_i2c_driver); |
458 | |
459 | MODULE_DESCRIPTION("Virtual DVB Demodulator Driver" ); |
460 | MODULE_AUTHOR("Daniel W. S. Almeida" ); |
461 | MODULE_LICENSE("GPL" ); |
462 | |