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

vp702x.c (10379B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* DVB USB compliant Linux driver for the TwinhanDTV StarBox USB2.0 DVB-S
      3 * receiver.
      4 *
      5 * Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
      6 *                    Metzler Brothers Systementwicklung GbR
      7 *
      8 * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de>
      9 *
     10 * Thanks to Twinhan who kindly provided hardware and information.
     11 *
     12 * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
     13 */
     14#include "vp702x.h"
     15#include <linux/mutex.h>
     16
     17/* debug */
     18int dvb_usb_vp702x_debug;
     19module_param_named(debug,dvb_usb_vp702x_debug, int, 0644);
     20MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
     21
     22DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
     23
     24struct vp702x_adapter_state {
     25	int pid_filter_count;
     26	int pid_filter_can_bypass;
     27	u8  pid_filter_state;
     28};
     29
     30static int vp702x_usb_in_op_unlocked(struct dvb_usb_device *d, u8 req,
     31				     u16 value, u16 index, u8 *b, int blen)
     32{
     33	int ret;
     34
     35	ret = usb_control_msg(d->udev,
     36		usb_rcvctrlpipe(d->udev, 0),
     37		req,
     38		USB_TYPE_VENDOR | USB_DIR_IN,
     39		value, index, b, blen,
     40		2000);
     41
     42	if (ret < 0) {
     43		warn("usb in operation failed. (%d)", ret);
     44		ret = -EIO;
     45	} else
     46		ret = 0;
     47
     48
     49	deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
     50	debug_dump(b,blen,deb_xfer);
     51
     52	return ret;
     53}
     54
     55int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
     56		     u16 index, u8 *b, int blen)
     57{
     58	int ret;
     59
     60	mutex_lock(&d->usb_mutex);
     61	ret = vp702x_usb_in_op_unlocked(d, req, value, index, b, blen);
     62	mutex_unlock(&d->usb_mutex);
     63
     64	return ret;
     65}
     66
     67static int vp702x_usb_out_op_unlocked(struct dvb_usb_device *d, u8 req,
     68				      u16 value, u16 index, u8 *b, int blen)
     69{
     70	int ret;
     71	deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index);
     72	debug_dump(b,blen,deb_xfer);
     73
     74	if ((ret = usb_control_msg(d->udev,
     75			usb_sndctrlpipe(d->udev,0),
     76			req,
     77			USB_TYPE_VENDOR | USB_DIR_OUT,
     78			value,index,b,blen,
     79			2000)) != blen) {
     80		warn("usb out operation failed. (%d)",ret);
     81		return -EIO;
     82	} else
     83		return 0;
     84}
     85
     86static int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
     87			     u16 index, u8 *b, int blen)
     88{
     89	int ret;
     90
     91	mutex_lock(&d->usb_mutex);
     92	ret = vp702x_usb_out_op_unlocked(d, req, value, index, b, blen);
     93	mutex_unlock(&d->usb_mutex);
     94
     95	return ret;
     96}
     97
     98int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec)
     99{
    100	int ret;
    101
    102	if ((ret = mutex_lock_interruptible(&d->usb_mutex)))
    103		return ret;
    104
    105	ret = vp702x_usb_out_op_unlocked(d, REQUEST_OUT, 0, 0, o, olen);
    106	msleep(msec);
    107	ret = vp702x_usb_in_op_unlocked(d, REQUEST_IN, 0, 0, i, ilen);
    108
    109	mutex_unlock(&d->usb_mutex);
    110	return ret;
    111}
    112
    113static int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o,
    114				int olen, u8 *i, int ilen, int msec)
    115{
    116	struct vp702x_device_state *st = d->priv;
    117	int ret = 0;
    118	u8 *buf;
    119	int buflen = max(olen + 2, ilen + 1);
    120
    121	ret = mutex_lock_interruptible(&st->buf_mutex);
    122	if (ret < 0)
    123		return ret;
    124
    125	if (buflen > st->buf_len) {
    126		buf = kmalloc(buflen, GFP_KERNEL);
    127		if (!buf) {
    128			mutex_unlock(&st->buf_mutex);
    129			return -ENOMEM;
    130		}
    131		info("successfully reallocated a bigger buffer");
    132		kfree(st->buf);
    133		st->buf = buf;
    134		st->buf_len = buflen;
    135	} else {
    136		buf = st->buf;
    137	}
    138
    139	buf[0] = 0x00;
    140	buf[1] = cmd;
    141	memcpy(&buf[2], o, olen);
    142
    143	ret = vp702x_usb_inout_op(d, buf, olen+2, buf, ilen+1, msec);
    144
    145	if (ret == 0)
    146		memcpy(i, &buf[1], ilen);
    147	mutex_unlock(&st->buf_mutex);
    148
    149	return ret;
    150}
    151
    152static int vp702x_set_pld_mode(struct dvb_usb_adapter *adap, u8 bypass)
    153{
    154	int ret;
    155	struct vp702x_device_state *st = adap->dev->priv;
    156	u8 *buf;
    157
    158	mutex_lock(&st->buf_mutex);
    159
    160	buf = st->buf;
    161	memset(buf, 0, 16);
    162
    163	ret = vp702x_usb_in_op(adap->dev, 0xe0, (bypass << 8) | 0x0e,
    164			0, buf, 16);
    165	mutex_unlock(&st->buf_mutex);
    166	return ret;
    167}
    168
    169static int vp702x_set_pld_state(struct dvb_usb_adapter *adap, u8 state)
    170{
    171	int ret;
    172	struct vp702x_device_state *st = adap->dev->priv;
    173	u8 *buf;
    174
    175	mutex_lock(&st->buf_mutex);
    176
    177	buf = st->buf;
    178	memset(buf, 0, 16);
    179	ret = vp702x_usb_in_op(adap->dev, 0xe0, (state << 8) | 0x0f,
    180			0, buf, 16);
    181
    182	mutex_unlock(&st->buf_mutex);
    183
    184	return ret;
    185}
    186
    187static int vp702x_set_pid(struct dvb_usb_adapter *adap, u16 pid, u8 id, int onoff)
    188{
    189	struct vp702x_adapter_state *st = adap->priv;
    190	struct vp702x_device_state *dst = adap->dev->priv;
    191	u8 *buf;
    192
    193	if (onoff)
    194		st->pid_filter_state |=  (1 << id);
    195	else {
    196		st->pid_filter_state &= ~(1 << id);
    197		pid = 0xffff;
    198	}
    199
    200	id = 0x10 + id*2;
    201
    202	vp702x_set_pld_state(adap, st->pid_filter_state);
    203
    204	mutex_lock(&dst->buf_mutex);
    205
    206	buf = dst->buf;
    207	memset(buf, 0, 16);
    208	vp702x_usb_in_op(adap->dev, 0xe0, (((pid >> 8) & 0xff) << 8) | (id), 0, buf, 16);
    209	vp702x_usb_in_op(adap->dev, 0xe0, (((pid     ) & 0xff) << 8) | (id+1), 0, buf, 16);
    210
    211	mutex_unlock(&dst->buf_mutex);
    212
    213	return 0;
    214}
    215
    216
    217static int vp702x_init_pid_filter(struct dvb_usb_adapter *adap)
    218{
    219	struct vp702x_adapter_state *st = adap->priv;
    220	struct vp702x_device_state *dst = adap->dev->priv;
    221	int i;
    222	u8 *b;
    223
    224	st->pid_filter_count = 8;
    225	st->pid_filter_can_bypass = 1;
    226	st->pid_filter_state = 0x00;
    227
    228	vp702x_set_pld_mode(adap, 1); /* bypass */
    229
    230	for (i = 0; i < st->pid_filter_count; i++)
    231		vp702x_set_pid(adap, 0xffff, i, 1);
    232
    233	mutex_lock(&dst->buf_mutex);
    234	b = dst->buf;
    235	memset(b, 0, 10);
    236	vp702x_usb_in_op(adap->dev, 0xb5, 3, 0, b, 10);
    237	vp702x_usb_in_op(adap->dev, 0xb5, 0, 0, b, 10);
    238	vp702x_usb_in_op(adap->dev, 0xb5, 1, 0, b, 10);
    239	mutex_unlock(&dst->buf_mutex);
    240	/*vp702x_set_pld_mode(d, 0); // filter */
    241
    242	return 0;
    243}
    244
    245static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
    246{
    247	return 0;
    248}
    249
    250/* keys for the enclosed remote control */
    251static struct rc_map_table rc_map_vp702x_table[] = {
    252	{ 0x0001, KEY_1 },
    253	{ 0x0002, KEY_2 },
    254};
    255
    256/* remote control stuff (does not work with my box) */
    257static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
    258{
    259/* remove the following return to enabled remote querying */
    260#if 0
    261	u8 *key;
    262	int i;
    263
    264	key = kmalloc(10, GFP_KERNEL);
    265	if (!key)
    266		return -ENOMEM;
    267
    268	vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10);
    269
    270	deb_rc("remote query key: %x %d\n",key[1],key[1]);
    271
    272	if (key[1] == 0x44) {
    273		*state = REMOTE_NO_KEY_PRESSED;
    274		kfree(key);
    275		return 0;
    276	}
    277
    278	for (i = 0; i < ARRAY_SIZE(rc_map_vp702x_table); i++)
    279		if (rc5_custom(&rc_map_vp702x_table[i]) == key[1]) {
    280			*state = REMOTE_KEY_PRESSED;
    281			*event = rc_map_vp702x_table[i].keycode;
    282			break;
    283		}
    284	kfree(key);
    285#endif
    286
    287	return 0;
    288}
    289
    290
    291static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
    292{
    293	u8 i, *buf;
    294	int ret;
    295	struct vp702x_device_state *st = d->priv;
    296
    297	mutex_lock(&st->buf_mutex);
    298	buf = st->buf;
    299	for (i = 6; i < 12; i++) {
    300		ret = vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1,
    301				       &buf[i - 6], 1);
    302		if (ret < 0)
    303			goto err;
    304	}
    305
    306	memcpy(mac, buf, 6);
    307err:
    308	mutex_unlock(&st->buf_mutex);
    309	return ret;
    310}
    311
    312static int vp702x_frontend_attach(struct dvb_usb_adapter *adap)
    313{
    314	u8 buf[10] = { 0 };
    315
    316	vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 0, 7, NULL, 0);
    317
    318	if (vp702x_usb_inout_cmd(adap->dev, GET_SYSTEM_STRING, NULL, 0,
    319				   buf, 10, 10))
    320		return -EIO;
    321
    322	buf[9] = '\0';
    323	info("system string: %s",&buf[1]);
    324
    325	vp702x_init_pid_filter(adap);
    326
    327	adap->fe_adap[0].fe = vp702x_fe_attach(adap->dev);
    328	vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0);
    329
    330	return 0;
    331}
    332
    333static struct dvb_usb_device_properties vp702x_properties;
    334
    335static int vp702x_usb_probe(struct usb_interface *intf,
    336		const struct usb_device_id *id)
    337{
    338	struct dvb_usb_device *d;
    339	struct vp702x_device_state *st;
    340	int ret;
    341
    342	ret = dvb_usb_device_init(intf, &vp702x_properties,
    343				   THIS_MODULE, &d, adapter_nr);
    344	if (ret)
    345		goto out;
    346
    347	st = d->priv;
    348	st->buf_len = 16;
    349	st->buf = kmalloc(st->buf_len, GFP_KERNEL);
    350	if (!st->buf) {
    351		ret = -ENOMEM;
    352		dvb_usb_device_exit(intf);
    353		goto out;
    354	}
    355	mutex_init(&st->buf_mutex);
    356
    357out:
    358	return ret;
    359
    360}
    361
    362static void vp702x_usb_disconnect(struct usb_interface *intf)
    363{
    364	struct dvb_usb_device *d = usb_get_intfdata(intf);
    365	struct vp702x_device_state *st = d->priv;
    366	mutex_lock(&st->buf_mutex);
    367	kfree(st->buf);
    368	mutex_unlock(&st->buf_mutex);
    369	dvb_usb_device_exit(intf);
    370}
    371
    372enum {
    373	VISIONPLUS_VP7021_COLD,
    374	VISIONPLUS_VP7020_COLD,
    375	VISIONPLUS_VP7020_WARM,
    376};
    377
    378static struct usb_device_id vp702x_usb_table[] = {
    379	DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7021_COLD),
    380//	DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7020_COLD),
    381//	DVB_USB_DEV(VISIONPLUS, VISIONPLUS_VP7020_WARM),
    382	{ }
    383};
    384
    385MODULE_DEVICE_TABLE(usb, vp702x_usb_table);
    386
    387static struct dvb_usb_device_properties vp702x_properties = {
    388	.usb_ctrl = CYPRESS_FX2,
    389	.firmware            = "dvb-usb-vp702x-02.fw",
    390	.no_reconnect        = 1,
    391
    392	.size_of_priv     = sizeof(struct vp702x_device_state),
    393
    394	.num_adapters = 1,
    395	.adapter = {
    396		{
    397		.num_frontends = 1,
    398		.fe = {{
    399			.caps             = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
    400
    401			.streaming_ctrl   = vp702x_streaming_ctrl,
    402			.frontend_attach  = vp702x_frontend_attach,
    403
    404			/* parameter for the MPEG2-data transfer */
    405			.stream = {
    406				.type = USB_BULK,
    407				.count = 10,
    408				.endpoint = 0x02,
    409				.u = {
    410					.bulk = {
    411						.buffersize = 4096,
    412					}
    413				}
    414			},
    415		}},
    416			.size_of_priv     = sizeof(struct vp702x_adapter_state),
    417		}
    418	},
    419	.read_mac_address = vp702x_read_mac_addr,
    420
    421	.rc.legacy = {
    422		.rc_map_table       = rc_map_vp702x_table,
    423		.rc_map_size  = ARRAY_SIZE(rc_map_vp702x_table),
    424		.rc_interval      = 400,
    425		.rc_query         = vp702x_rc_query,
    426	},
    427
    428	.num_device_descs = 1,
    429	.devices = {
    430		{ .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)",
    431		  .cold_ids = { &vp702x_usb_table[VISIONPLUS_VP7021_COLD], NULL },
    432		  .warm_ids = { NULL },
    433		},
    434/*		{ .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)",
    435		  .cold_ids = { &vp702x_usb_table[VISIONPLUS_VP7020_COLD], NULL },
    436		  .warm_ids = { &vp702x_usb_table[VISIONPLUS_VP7020_WARM], NULL },
    437		},
    438*/		{ NULL },
    439	}
    440};
    441
    442/* usb specific object needed to register this driver with the usb subsystem */
    443static struct usb_driver vp702x_usb_driver = {
    444	.name		= "dvb_usb_vp702x",
    445	.probe		= vp702x_usb_probe,
    446	.disconnect	= vp702x_usb_disconnect,
    447	.id_table	= vp702x_usb_table,
    448};
    449
    450module_usb_driver(vp702x_usb_driver);
    451
    452MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
    453MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones");
    454MODULE_VERSION("1.0");
    455MODULE_LICENSE("GPL");