cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

vidtv_tuner.c (11677B)


      1// SPDX-License-Identifier: GPL-2.0
      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 * The vidtv tuner should support common TV standards such as
      8 * DVB-T/T2/S/S2, ISDB-T and ATSC when completed.
      9 *
     10 * Copyright (C) 2020 Daniel W. S. Almeida
     11 */
     12
     13#include <linux/errno.h>
     14#include <linux/i2c.h>
     15#include <linux/module.h>
     16#include <linux/printk.h>
     17#include <linux/ratelimit.h>
     18#include <linux/slab.h>
     19#include <linux/types.h>
     20
     21#include <media/dvb_frontend.h>
     22
     23#include "vidtv_tuner.h"
     24
     25struct vidtv_tuner_cnr_to_qual_s {
     26	/* attempt to use the same values as libdvbv5 */
     27	u32 modulation;
     28	u32 fec;
     29	u32 cnr_ok;
     30	u32 cnr_good;
     31};
     32
     33static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_c_cnr_2_qual[] = {
     34	/* from libdvbv5 source code, in milli db */
     35	{ QAM_256, FEC_NONE,  34000, 38000},
     36	{ QAM_64,  FEC_NONE,  30000, 34000},
     37};
     38
     39static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s_cnr_2_qual[] = {
     40	/* from libdvbv5 source code, in milli db */
     41	{ QPSK, FEC_1_2,  7000, 10000},
     42	{ QPSK, FEC_2_3,  9000, 12000},
     43	{ QPSK, FEC_3_4, 10000, 13000},
     44	{ QPSK, FEC_5_6, 11000, 14000},
     45	{ QPSK, FEC_7_8, 12000, 15000},
     46};
     47
     48static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s2_cnr_2_qual[] = {
     49	/* from libdvbv5 source code, in milli db */
     50	{ QPSK,  FEC_1_2,   9000,  12000},
     51	{ QPSK,  FEC_2_3,  11000,  14000},
     52	{ QPSK,  FEC_3_4,  12000,  15000},
     53	{ QPSK,  FEC_5_6,  12000,  15000},
     54	{ QPSK,  FEC_8_9,  13000,  16000},
     55	{ QPSK,  FEC_9_10, 13500,  16500},
     56	{ PSK_8, FEC_2_3,  14500,  17500},
     57	{ PSK_8, FEC_3_4,  16000,  19000},
     58	{ PSK_8, FEC_5_6,  17500,  20500},
     59	{ PSK_8, FEC_8_9,  19000,  22000},
     60};
     61
     62static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_t_cnr_2_qual[] = {
     63	/* from libdvbv5 source code, in milli db*/
     64	{   QPSK, FEC_1_2,  4100,  5900},
     65	{   QPSK, FEC_2_3,  6100,  9600},
     66	{   QPSK, FEC_3_4,  7200, 12400},
     67	{   QPSK, FEC_5_6,  8500, 15600},
     68	{   QPSK, FEC_7_8,  9200, 17500},
     69	{ QAM_16, FEC_1_2,  9800, 11800},
     70	{ QAM_16, FEC_2_3, 12100, 15300},
     71	{ QAM_16, FEC_3_4, 13400, 18100},
     72	{ QAM_16, FEC_5_6, 14800, 21300},
     73	{ QAM_16, FEC_7_8, 15700, 23600},
     74	{ QAM_64, FEC_1_2, 14000, 16000},
     75	{ QAM_64, FEC_2_3, 19900, 25400},
     76	{ QAM_64, FEC_3_4, 24900, 27900},
     77	{ QAM_64, FEC_5_6, 21300, 23300},
     78	{ QAM_64, FEC_7_8, 22000, 24000},
     79};
     80
     81/**
     82 * struct vidtv_tuner_hardware_state - Simulate the tuner hardware status
     83 * @asleep: whether the tuner is asleep, i.e whether _sleep() or _suspend() was
     84 * called.
     85 * @lock_status: Whether the tuner has managed to lock on the requested
     86 * frequency.
     87 * @if_frequency: The tuner's intermediate frequency. Hardcoded for the purposes
     88 * of simulation.
     89 * @tuned_frequency: The actual tuned frequency.
     90 * @bandwidth: The actual bandwidth.
     91 *
     92 * This structure is meant to simulate the status of the tuner hardware, as if
     93 * we had a physical tuner hardware.
     94 */
     95struct vidtv_tuner_hardware_state {
     96	bool asleep;
     97	u32 lock_status;
     98	u32 if_frequency;
     99	u32 tuned_frequency;
    100	u32 bandwidth;
    101};
    102
    103/**
    104 * struct vidtv_tuner_dev - The tuner struct
    105 * @fe: A pointer to the dvb_frontend structure allocated by vidtv_demod
    106 * @hw_state: A struct to simulate the tuner's hardware state as if we had a
    107 * physical tuner hardware.
    108 * @config: The configuration used to start the tuner module, usually filled
    109 * by a bridge driver. For vidtv, this is filled by vidtv_bridge before the
    110 * tuner module is probed.
    111 */
    112struct vidtv_tuner_dev {
    113	struct dvb_frontend *fe;
    114	struct vidtv_tuner_hardware_state hw_state;
    115	struct vidtv_tuner_config config;
    116};
    117
    118static struct vidtv_tuner_dev*
    119vidtv_tuner_get_dev(struct dvb_frontend *fe)
    120{
    121	return i2c_get_clientdata(fe->tuner_priv);
    122}
    123
    124static int vidtv_tuner_check_frequency_shift(struct dvb_frontend *fe)
    125{
    126	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    127	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
    128	struct vidtv_tuner_config config  = tuner_dev->config;
    129	u32 *valid_freqs = NULL;
    130	u32 array_sz = 0;
    131	u32 i;
    132	u32 shift;
    133
    134	switch (c->delivery_system) {
    135	case SYS_DVBT:
    136	case SYS_DVBT2:
    137		valid_freqs = config.vidtv_valid_dvb_t_freqs;
    138		array_sz    = ARRAY_SIZE(config.vidtv_valid_dvb_t_freqs);
    139		break;
    140	case SYS_DVBS:
    141	case SYS_DVBS2:
    142		valid_freqs = config.vidtv_valid_dvb_s_freqs;
    143		array_sz    = ARRAY_SIZE(config.vidtv_valid_dvb_s_freqs);
    144		break;
    145	case SYS_DVBC_ANNEX_A:
    146		valid_freqs = config.vidtv_valid_dvb_c_freqs;
    147		array_sz    = ARRAY_SIZE(config.vidtv_valid_dvb_c_freqs);
    148		break;
    149
    150	default:
    151		dev_warn(fe->dvb->device,
    152			 "%s: unsupported delivery system: %u\n",
    153			 __func__,
    154			 c->delivery_system);
    155
    156		return -EINVAL;
    157	}
    158
    159	for (i = 0; i < array_sz; i++) {
    160		if (!valid_freqs[i])
    161			break;
    162		shift = abs(c->frequency - valid_freqs[i]);
    163
    164		if (!shift)
    165			return 0;
    166
    167		/*
    168		 * This will provide a value from 0 to 100 that would
    169		 * indicate how far is the tuned frequency from the
    170		 * right one.
    171		 */
    172		if (shift < config.max_frequency_shift_hz)
    173			return shift * 100 / config.max_frequency_shift_hz;
    174	}
    175
    176	return -EINVAL;
    177}
    178
    179static int
    180vidtv_tuner_get_signal_strength(struct dvb_frontend *fe, u16 *strength)
    181{
    182	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
    183	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    184	const struct vidtv_tuner_cnr_to_qual_s *cnr2qual = NULL;
    185	struct device *dev = fe->dvb->device;
    186	u32 array_size = 0;
    187	s32 shift;
    188	u32 i;
    189
    190	shift = vidtv_tuner_check_frequency_shift(fe);
    191	if (shift < 0) {
    192		tuner_dev->hw_state.lock_status = 0;
    193		*strength = 0;
    194		return 0;
    195	}
    196
    197	switch (c->delivery_system) {
    198	case SYS_DVBT:
    199	case SYS_DVBT2:
    200		cnr2qual   = vidtv_tuner_t_cnr_2_qual;
    201		array_size = ARRAY_SIZE(vidtv_tuner_t_cnr_2_qual);
    202		break;
    203
    204	case SYS_DVBS:
    205		cnr2qual   = vidtv_tuner_s_cnr_2_qual;
    206		array_size = ARRAY_SIZE(vidtv_tuner_s_cnr_2_qual);
    207		break;
    208
    209	case SYS_DVBS2:
    210		cnr2qual   = vidtv_tuner_s2_cnr_2_qual;
    211		array_size = ARRAY_SIZE(vidtv_tuner_s2_cnr_2_qual);
    212		break;
    213
    214	case SYS_DVBC_ANNEX_A:
    215		cnr2qual   = vidtv_tuner_c_cnr_2_qual;
    216		array_size = ARRAY_SIZE(vidtv_tuner_c_cnr_2_qual);
    217		break;
    218
    219	default:
    220		dev_warn_ratelimited(dev,
    221				     "%s: unsupported delivery system: %u\n",
    222				     __func__,
    223				     c->delivery_system);
    224		return -EINVAL;
    225	}
    226
    227	for (i = 0; i < array_size; i++) {
    228		if (cnr2qual[i].modulation != c->modulation ||
    229		    cnr2qual[i].fec != c->fec_inner)
    230			continue;
    231
    232		if (!shift) {
    233			*strength = cnr2qual[i].cnr_good;
    234			return 0;
    235		}
    236		/*
    237		 * Channel tuned at wrong frequency. Simulate that the
    238		 * Carrier S/N ratio is not too good.
    239		 */
    240
    241		*strength = cnr2qual[i].cnr_ok -
    242			    (cnr2qual[i].cnr_good - cnr2qual[i].cnr_ok);
    243		return 0;
    244	}
    245
    246	/*
    247	 * do a linear interpolation between 34dB and 10dB if we can't
    248	 * match against the table
    249	 */
    250	*strength = 34000 - 24000 * shift / 100;
    251	return 0;
    252}
    253
    254static int vidtv_tuner_init(struct dvb_frontend *fe)
    255{
    256	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    257	struct vidtv_tuner_config config  = tuner_dev->config;
    258
    259	msleep_interruptible(config.mock_power_up_delay_msec);
    260
    261	tuner_dev->hw_state.asleep = false;
    262	tuner_dev->hw_state.if_frequency = 5000;
    263
    264	return 0;
    265}
    266
    267static int vidtv_tuner_sleep(struct dvb_frontend *fe)
    268{
    269	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    270
    271	tuner_dev->hw_state.asleep = true;
    272	return 0;
    273}
    274
    275static int vidtv_tuner_suspend(struct dvb_frontend *fe)
    276{
    277	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    278
    279	tuner_dev->hw_state.asleep = true;
    280	return 0;
    281}
    282
    283static int vidtv_tuner_resume(struct dvb_frontend *fe)
    284{
    285	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    286
    287	tuner_dev->hw_state.asleep = false;
    288	return 0;
    289}
    290
    291static int vidtv_tuner_set_params(struct dvb_frontend *fe)
    292{
    293	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    294	struct vidtv_tuner_config config  = tuner_dev->config;
    295	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
    296	s32 shift;
    297
    298	u32 min_freq = fe->ops.tuner_ops.info.frequency_min_hz;
    299	u32 max_freq = fe->ops.tuner_ops.info.frequency_max_hz;
    300	u32 min_bw = fe->ops.tuner_ops.info.bandwidth_min;
    301	u32 max_bw = fe->ops.tuner_ops.info.bandwidth_max;
    302
    303	if (c->frequency < min_freq  || c->frequency > max_freq  ||
    304	    c->bandwidth_hz < min_bw || c->bandwidth_hz > max_bw) {
    305		tuner_dev->hw_state.lock_status = 0;
    306		return -EINVAL;
    307	}
    308
    309	tuner_dev->hw_state.tuned_frequency = c->frequency;
    310	tuner_dev->hw_state.bandwidth = c->bandwidth_hz;
    311	tuner_dev->hw_state.lock_status = TUNER_STATUS_LOCKED;
    312
    313	msleep_interruptible(config.mock_tune_delay_msec);
    314
    315	shift = vidtv_tuner_check_frequency_shift(fe);
    316	if (shift < 0) {
    317		tuner_dev->hw_state.lock_status = 0;
    318		return shift;
    319	}
    320
    321	return 0;
    322}
    323
    324static int vidtv_tuner_set_config(struct dvb_frontend *fe,
    325				  void *priv_cfg)
    326{
    327	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    328
    329	memcpy(&tuner_dev->config, priv_cfg, sizeof(tuner_dev->config));
    330
    331	return 0;
    332}
    333
    334static int vidtv_tuner_get_frequency(struct dvb_frontend *fe,
    335				     u32 *frequency)
    336{
    337	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    338
    339	*frequency = tuner_dev->hw_state.tuned_frequency;
    340
    341	return 0;
    342}
    343
    344static int vidtv_tuner_get_bandwidth(struct dvb_frontend *fe,
    345				     u32 *bandwidth)
    346{
    347	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    348
    349	*bandwidth = tuner_dev->hw_state.bandwidth;
    350
    351	return 0;
    352}
    353
    354static int vidtv_tuner_get_if_frequency(struct dvb_frontend *fe,
    355					u32 *frequency)
    356{
    357	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    358
    359	*frequency = tuner_dev->hw_state.if_frequency;
    360
    361	return 0;
    362}
    363
    364static int vidtv_tuner_get_status(struct dvb_frontend *fe, u32 *status)
    365{
    366	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
    367
    368	*status = tuner_dev->hw_state.lock_status;
    369
    370	return 0;
    371}
    372
    373static const struct dvb_tuner_ops vidtv_tuner_ops = {
    374	.init             = vidtv_tuner_init,
    375	.sleep            = vidtv_tuner_sleep,
    376	.suspend          = vidtv_tuner_suspend,
    377	.resume           = vidtv_tuner_resume,
    378	.set_params       = vidtv_tuner_set_params,
    379	.set_config       = vidtv_tuner_set_config,
    380	.get_bandwidth    = vidtv_tuner_get_bandwidth,
    381	.get_frequency    = vidtv_tuner_get_frequency,
    382	.get_if_frequency = vidtv_tuner_get_if_frequency,
    383	.get_status       = vidtv_tuner_get_status,
    384	.get_rf_strength  = vidtv_tuner_get_signal_strength
    385};
    386
    387static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = {
    388	{"dvb_vidtv_tuner", 0},
    389	{}
    390};
    391MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table);
    392
    393static int vidtv_tuner_i2c_probe(struct i2c_client *client,
    394				 const struct i2c_device_id *id)
    395{
    396	struct vidtv_tuner_config *config = client->dev.platform_data;
    397	struct dvb_frontend *fe           = config->fe;
    398	struct vidtv_tuner_dev *tuner_dev = NULL;
    399
    400	tuner_dev = kzalloc(sizeof(*tuner_dev), GFP_KERNEL);
    401	if (!tuner_dev)
    402		return -ENOMEM;
    403
    404	tuner_dev->fe = config->fe;
    405	i2c_set_clientdata(client, tuner_dev);
    406
    407	memcpy(&fe->ops.tuner_ops,
    408	       &vidtv_tuner_ops,
    409	       sizeof(struct dvb_tuner_ops));
    410
    411	memcpy(&tuner_dev->config, config, sizeof(tuner_dev->config));
    412	fe->tuner_priv = client;
    413
    414	return 0;
    415}
    416
    417static int vidtv_tuner_i2c_remove(struct i2c_client *client)
    418{
    419	struct vidtv_tuner_dev *tuner_dev = i2c_get_clientdata(client);
    420
    421	kfree(tuner_dev);
    422
    423	return 0;
    424}
    425
    426static struct i2c_driver vidtv_tuner_i2c_driver = {
    427	.driver = {
    428		.name                = "dvb_vidtv_tuner",
    429		.suppress_bind_attrs = true,
    430	},
    431	.probe    = vidtv_tuner_i2c_probe,
    432	.remove   = vidtv_tuner_i2c_remove,
    433	.id_table = vidtv_tuner_i2c_id_table,
    434};
    435module_i2c_driver(vidtv_tuner_i2c_driver);
    436
    437MODULE_DESCRIPTION("Virtual DVB Tuner");
    438MODULE_AUTHOR("Daniel W. S. Almeida");
    439MODULE_LICENSE("GPL");