brintos

brintos / linux-shallow public Read only

0
0
Text · 12.0 KiB · 505f96f Raw
462 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * The Virtual DVB test driver serves as a reference DVB driver and helps4 * validate the existing APIs in the media subsystem. It can also aid5 * developers working on userspace applications.6 *7 * Copyright (C) 2020 Daniel W. S. Almeida8 * 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 29static 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 35static 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 44static 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 58static 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 77static 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 125static 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 162static 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 closer186	 * 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(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(state->tuner_cnr / 50);196}197 198static 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(&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(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(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(&state->frontend);231 232	*status = state->status;233 234	return 0;235}236 237static 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 real250 * 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 data254 * to userspace.255 */256static int vidtv_demod_get_frontend(struct dvb_frontend *fe,257				    struct dtv_frontend_properties *p)258{259	return 0;260}261 262static 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 real297 * demod drivers.298 *299 * Should only be implemented if the demod has support for DVB-S or DVB-S2300 */301static 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 real310 * demod drivers.311 *312 * Should only be implemented if the demod has support for DVB-S or DVB-S2313 */314static 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 real323 * demod drivers.324 *325 * Should only be implemented if the demod has support for DVB-S or DVB-S2326 */327static 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 real336 * demod drivers.337 *338 * Should only be implemented if the demod has support for DVB-S or DVB-S2339 */340static int vidtv_diseqc_send_burst(struct dvb_frontend *fe,341				   enum fe_sec_mini_cmd burst)342{343	return 0;344}345 346static void vidtv_demod_release(struct dvb_frontend *fe)347{348	struct vidtv_demod_state *state = fe->demodulator_priv;349 350	kfree(state);351}352 353static 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 409static const struct i2c_device_id vidtv_demod_i2c_id_table[] = {410	{ "dvb_vidtv_demod" },411	{}412};413MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table);414 415static 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(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, state);434 435	vidtv_clean_stats(&state->frontend);436 437	return 0;438}439 440static void vidtv_demod_i2c_remove(struct i2c_client *client)441{442	struct vidtv_demod_state *state = i2c_get_clientdata(client);443 444	kfree(state);445}446 447static 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 457module_i2c_driver(vidtv_demod_i2c_driver);458 459MODULE_DESCRIPTION("Virtual DVB Demodulator Driver");460MODULE_AUTHOR("Daniel W. S. Almeida");461MODULE_LICENSE("GPL");462