341 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ngene-dvb.c: nGene PCIe bridge driver - DVB functions4 *5 * Copyright (C) 2005-2007 Micronas6 *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 prototype11 */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_free45 (&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 off185 * in the previous tsin_exchange() run is at the beginning186 * of the new TS buffer, append this to the temp buffer and187 * 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 offset200 * shifts by checking for a valid TS SYNC byte201 */202 while (len >= 188) {203 if (*((char *)buf) != 0x47) {204 /*205 * no SYNC header, find new offset shift206 * (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 remaining223 * len for a full TS frame, break and224 * 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 remainder239 * 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 else265 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}341