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

ngene-dvb.c (8400B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * ngene-dvb.c: nGene PCIe bridge driver - DVB functions
      4 *
      5 * Copyright (C) 2005-2007 Micronas
      6 *
      7 * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
      8 *                         Modifications for new nGene firmware,
      9 *                         support for EEPROM-copying,
     10 *                         support for new dual DVB-S2 card prototype
     11 */
     12
     13#include <linux/module.h>
     14#include <linux/init.h>
     15#include <linux/delay.h>
     16#include <linux/slab.h>
     17#include <linux/poll.h>
     18#include <linux/io.h>
     19#include <asm/div64.h>
     20#include <linux/pci.h>
     21#include <linux/timer.h>
     22#include <linux/byteorder/generic.h>
     23#include <linux/firmware.h>
     24#include <linux/vmalloc.h>
     25
     26#include "ngene.h"
     27
     28static int ci_tsfix = 1;
     29module_param(ci_tsfix, int, 0444);
     30MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifts in conjunction with CI expansions (default: 1/enabled)");
     31
     32/****************************************************************************/
     33/* COMMAND API interface ****************************************************/
     34/****************************************************************************/
     35
     36static ssize_t ts_write(struct file *file, const char __user *buf,
     37			size_t count, loff_t *ppos)
     38{
     39	struct dvb_device *dvbdev = file->private_data;
     40	struct ngene_channel *chan = dvbdev->priv;
     41	struct ngene *dev = chan->dev;
     42
     43	if (wait_event_interruptible(dev->tsout_rbuf.queue,
     44				     dvb_ringbuffer_free
     45				     (&dev->tsout_rbuf) >= count) < 0)
     46		return 0;
     47
     48	dvb_ringbuffer_write_user(&dev->tsout_rbuf, buf, count);
     49
     50	return count;
     51}
     52
     53static ssize_t ts_read(struct file *file, char __user *buf,
     54		       size_t count, loff_t *ppos)
     55{
     56	struct dvb_device *dvbdev = file->private_data;
     57	struct ngene_channel *chan = dvbdev->priv;
     58	struct ngene *dev = chan->dev;
     59	int left, avail;
     60
     61	left = count;
     62	while (left) {
     63		if (wait_event_interruptible(
     64			    dev->tsin_rbuf.queue,
     65			    dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0)
     66			return -EAGAIN;
     67		avail = dvb_ringbuffer_avail(&dev->tsin_rbuf);
     68		if (avail > left)
     69			avail = left;
     70		dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail);
     71		left -= avail;
     72		buf += avail;
     73	}
     74	return count;
     75}
     76
     77static __poll_t ts_poll(struct file *file, poll_table *wait)
     78{
     79	struct dvb_device *dvbdev = file->private_data;
     80	struct ngene_channel *chan = dvbdev->priv;
     81	struct ngene *dev = chan->dev;
     82	struct dvb_ringbuffer *rbuf = &dev->tsin_rbuf;
     83	struct dvb_ringbuffer *wbuf = &dev->tsout_rbuf;
     84	__poll_t mask = 0;
     85
     86	poll_wait(file, &rbuf->queue, wait);
     87	poll_wait(file, &wbuf->queue, wait);
     88
     89	if (!dvb_ringbuffer_empty(rbuf))
     90		mask |= EPOLLIN | EPOLLRDNORM;
     91	if (dvb_ringbuffer_free(wbuf) >= 188)
     92		mask |= EPOLLOUT | EPOLLWRNORM;
     93
     94	return mask;
     95}
     96
     97static const struct file_operations ci_fops = {
     98	.owner   = THIS_MODULE,
     99	.read    = ts_read,
    100	.write   = ts_write,
    101	.open    = dvb_generic_open,
    102	.release = dvb_generic_release,
    103	.poll    = ts_poll,
    104	.mmap    = NULL,
    105};
    106
    107struct dvb_device ngene_dvbdev_ci = {
    108	.priv    = NULL,
    109	.readers = 1,
    110	.writers = 1,
    111	.users   = 2,
    112	.fops    = &ci_fops,
    113};
    114
    115
    116/****************************************************************************/
    117/* DVB functions and API interface ******************************************/
    118/****************************************************************************/
    119
    120static void swap_buffer(u32 *p, u32 len)
    121{
    122	while (len) {
    123		*p = swab32(*p);
    124		p++;
    125		len -= 4;
    126	}
    127}
    128
    129/* start of filler packet */
    130static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
    131
    132static int tsin_find_offset(void *buf, u32 len)
    133{
    134	int i, l;
    135
    136	l = len - sizeof(fill_ts);
    137	if (l <= 0)
    138		return -1;
    139
    140	for (i = 0; i < l; i++) {
    141		if (((char *)buf)[i] == 0x47) {
    142			if (!memcmp(buf + i, fill_ts, sizeof(fill_ts)))
    143				return i % 188;
    144		}
    145	}
    146
    147	return -1;
    148}
    149
    150static inline void tsin_copy_stripped(struct ngene *dev, void *buf)
    151{
    152	if (memcmp(buf, fill_ts, sizeof(fill_ts)) != 0) {
    153		if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
    154			dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
    155			wake_up(&dev->tsin_rbuf.queue);
    156		}
    157	}
    158}
    159
    160void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
    161{
    162	struct ngene_channel *chan = priv;
    163	struct ngene *dev = chan->dev;
    164	int tsoff;
    165
    166	if (flags & DF_SWAP32)
    167		swap_buffer(buf, len);
    168
    169	if (dev->ci.en && chan->number == 2) {
    170		/* blindly copy buffers if ci_tsfix is disabled */
    171		if (!ci_tsfix) {
    172			while (len >= 188) {
    173				tsin_copy_stripped(dev, buf);
    174
    175				buf += 188;
    176				len -= 188;
    177			}
    178			return NULL;
    179		}
    180
    181		/* ci_tsfix = 1 */
    182
    183		/*
    184		 * since the remainder of the TS packet which got cut off
    185		 * in the previous tsin_exchange() run is at the beginning
    186		 * of the new TS buffer, append this to the temp buffer and
    187		 * send it to the DVB ringbuffer afterwards.
    188		 */
    189		if (chan->tsin_offset) {
    190			memcpy(&chan->tsin_buffer[(188 - chan->tsin_offset)],
    191			       buf, chan->tsin_offset);
    192			tsin_copy_stripped(dev, &chan->tsin_buffer);
    193
    194			buf += chan->tsin_offset;
    195			len -= chan->tsin_offset;
    196		}
    197
    198		/*
    199		 * copy TS packets to the DVB ringbuffer and detect new offset
    200		 * shifts by checking for a valid TS SYNC byte
    201		 */
    202		while (len >= 188) {
    203			if (*((char *)buf) != 0x47) {
    204				/*
    205				 * no SYNC header, find new offset shift
    206				 * (max. 188 bytes, tsoff will be mod 188)
    207				 */
    208				tsoff = tsin_find_offset(buf, len);
    209				if (tsoff > 0) {
    210					chan->tsin_offset += tsoff;
    211					chan->tsin_offset %= 188;
    212
    213					buf += tsoff;
    214					len -= tsoff;
    215
    216					dev_info(&dev->pci_dev->dev,
    217						 "%s(): tsin_offset shift by %d on channel %d\n",
    218						 __func__, tsoff,
    219						 chan->number);
    220
    221					/*
    222					 * offset corrected. re-check remaining
    223					 * len for a full TS frame, break and
    224					 * skip to fragment handling if < 188.
    225					 */
    226					if (len < 188)
    227						break;
    228				}
    229			}
    230
    231			tsin_copy_stripped(dev, buf);
    232
    233			buf += 188;
    234			len -= 188;
    235		}
    236
    237		/*
    238		 * if a fragment is left, copy to temp buffer. The remainder
    239		 * will be appended in the next tsin_exchange() iteration.
    240		 */
    241		if (len > 0 && len < 188)
    242			memcpy(&chan->tsin_buffer, buf, len);
    243
    244		return NULL;
    245	}
    246
    247	if (chan->users > 0)
    248		dvb_dmx_swfilter(&chan->demux, buf, len);
    249
    250	return NULL;
    251}
    252
    253void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
    254{
    255	struct ngene_channel *chan = priv;
    256	struct ngene *dev = chan->dev;
    257	u32 alen;
    258
    259	alen = dvb_ringbuffer_avail(&dev->tsout_rbuf);
    260	alen -= alen % 188;
    261
    262	if (alen < len)
    263		FillTSBuffer(buf + alen, len - alen, flags);
    264	else
    265		alen = len;
    266	dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen);
    267	if (flags & DF_SWAP32)
    268		swap_buffer((u32 *)buf, alen);
    269	wake_up_interruptible(&dev->tsout_rbuf.queue);
    270	return buf;
    271}
    272
    273
    274
    275int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed)
    276{
    277	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
    278	struct ngene_channel *chan = dvbdmx->priv;
    279
    280	if (chan->users == 0) {
    281		if (!chan->dev->cmd_timeout_workaround || !chan->running)
    282			set_transfer(chan, 1);
    283	}
    284
    285	return ++chan->users;
    286}
    287
    288int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
    289{
    290	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
    291	struct ngene_channel *chan = dvbdmx->priv;
    292
    293	if (--chan->users)
    294		return chan->users;
    295
    296	if (!chan->dev->cmd_timeout_workaround)
    297		set_transfer(chan, 0);
    298
    299	return 0;
    300}
    301
    302int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
    303			    int (*start_feed)(struct dvb_demux_feed *),
    304			    int (*stop_feed)(struct dvb_demux_feed *),
    305			    void *priv)
    306{
    307	dvbdemux->priv = priv;
    308
    309	dvbdemux->filternum = 256;
    310	dvbdemux->feednum = 256;
    311	dvbdemux->start_feed = start_feed;
    312	dvbdemux->stop_feed = stop_feed;
    313	dvbdemux->write_to_decoder = NULL;
    314	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
    315				      DMX_SECTION_FILTERING |
    316				      DMX_MEMORY_BASED_FILTERING);
    317	return dvb_dmx_init(dvbdemux);
    318}
    319
    320int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
    321			       struct dvb_demux *dvbdemux,
    322			       struct dmx_frontend *hw_frontend,
    323			       struct dmx_frontend *mem_frontend,
    324			       struct dvb_adapter *dvb_adapter)
    325{
    326	int ret;
    327
    328	dmxdev->filternum = 256;
    329	dmxdev->demux = &dvbdemux->dmx;
    330	dmxdev->capabilities = 0;
    331	ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
    332	if (ret < 0)
    333		return ret;
    334
    335	hw_frontend->source = DMX_FRONTEND_0;
    336	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
    337	mem_frontend->source = DMX_MEMORY_FE;
    338	dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
    339	return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
    340}