2019-06-04 11:10:54 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2010-03-14 05:10:13 +03:00
|
|
|
/*
|
|
|
|
* ngene-dvb.c: nGene PCIe bridge driver - DVB functions
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2007 Micronas
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
|
|
|
|
* Modifications for new nGene firmware,
|
|
|
|
* support for EEPROM-copying,
|
|
|
|
* support for new dual DVB-S2 card prototype
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/div64.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/byteorder/generic.h>
|
|
|
|
#include <linux/firmware.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
|
|
|
|
#include "ngene.h"
|
|
|
|
|
media: ngene: compensate for TS buffer offset shifts
A possible hardware bug was discovered when using CA addon hardware
attached to the ngene hardware, in that the TS input buffer much likely
will shift and thus become unaligned to 188 byte blocks (a full TS frame)
when things like CA module initialisation (which happens via differing
communication paths) take place. This causes the TS NULL removal in
tsin_exchange() to fail to detect this previously inserted data and thus
causes userspace applications to receive data they didn't sent beforehand
and ultimately cause troubles.
On driver load with an inserted CAM, buffers are fine at first (note that
the driver has to keep the communication running from/to the card by
inserting TS NULL frames, this is done in tsout_exchange() via
FillTSBuffer() - that data is simply sent back by the hardware):
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47
After a few seconds, the CA module is recognised and initialised, which is
signalled by
dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully
This is where the first shift happens (this is always four bytes), buffer
becomes like this:
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f
Next, VDR, TVHeadend or any other CI aware application is started, buffers
will shift by even more bytes. It is believed this is due to the hardware
not handling control and data bytes properly distinct, and control data
having an influence on the actual data stream, which we cannot properly
detect at the driver level.
Workaround this hardware quirk by adding a detection for the TS sync byte
0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL
packet if buffers become unaligned, take note of that offset and apply
that when copying data to the DVB ring buffers. The last <188 bytes from
the hardware buffers are stored in a temp buffer (tsin_buffer), for which
the remainder will be in the beginning of the next hardware buffer (next
iteration of tsin_exchange()). That remainder will be appended to the
temp buffer and finally sent to the DVB ring buffer. The resulting TS
stream is perfectly fine, and the TS NULL packets inserted by the driver
which are sent back are properly removed. The resulting offset is being
clamped to 188 byte segments (one TS packet). Though this can result in
a repeated TS packet if the overall offset grows beyond this (and it
will grow only on CA initialisation), this is still way better than
unaligned TS frames and data sent to userspace that just isn't supposed
to be there.
This compensation can be toggled by the ci_tsfix modparam, which defaults
to 1 (enabled). In the case of problems, this can be turned off by setting
the parameter to 0 to restore the old behaviour.
Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2018-03-06 20:46:37 +03:00
|
|
|
static int ci_tsfix = 1;
|
|
|
|
module_param(ci_tsfix, int, 0444);
|
2018-04-22 19:06:52 +03:00
|
|
|
MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifts in conjunction with CI expansions (default: 1/enabled)");
|
2010-03-14 05:10:13 +03:00
|
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
/* COMMAND API interface ****************************************************/
|
|
|
|
/****************************************************************************/
|
|
|
|
|
2014-08-21 00:43:22 +04:00
|
|
|
static ssize_t ts_write(struct file *file, const char __user *buf,
|
2011-01-10 12:36:15 +03:00
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct ngene_channel *chan = dvbdev->priv;
|
|
|
|
struct ngene *dev = chan->dev;
|
|
|
|
|
|
|
|
if (wait_event_interruptible(dev->tsout_rbuf.queue,
|
|
|
|
dvb_ringbuffer_free
|
|
|
|
(&dev->tsout_rbuf) >= count) < 0)
|
|
|
|
return 0;
|
|
|
|
|
2014-09-04 03:44:04 +04:00
|
|
|
dvb_ringbuffer_write_user(&dev->tsout_rbuf, buf, count);
|
2011-01-10 12:36:15 +03:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2014-08-21 00:43:22 +04:00
|
|
|
static ssize_t ts_read(struct file *file, char __user *buf,
|
2011-01-10 12:36:15 +03:00
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct ngene_channel *chan = dvbdev->priv;
|
|
|
|
struct ngene *dev = chan->dev;
|
|
|
|
int left, avail;
|
|
|
|
|
|
|
|
left = count;
|
|
|
|
while (left) {
|
|
|
|
if (wait_event_interruptible(
|
|
|
|
dev->tsin_rbuf.queue,
|
|
|
|
dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0)
|
|
|
|
return -EAGAIN;
|
|
|
|
avail = dvb_ringbuffer_avail(&dev->tsin_rbuf);
|
|
|
|
if (avail > left)
|
|
|
|
avail = left;
|
|
|
|
dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail);
|
|
|
|
left -= avail;
|
|
|
|
buf += avail;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2018-02-25 20:06:56 +03:00
|
|
|
static __poll_t ts_poll(struct file *file, poll_table *wait)
|
|
|
|
{
|
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct ngene_channel *chan = dvbdev->priv;
|
|
|
|
struct ngene *dev = chan->dev;
|
|
|
|
struct dvb_ringbuffer *rbuf = &dev->tsin_rbuf;
|
|
|
|
struct dvb_ringbuffer *wbuf = &dev->tsout_rbuf;
|
|
|
|
__poll_t mask = 0;
|
|
|
|
|
|
|
|
poll_wait(file, &rbuf->queue, wait);
|
|
|
|
poll_wait(file, &wbuf->queue, wait);
|
|
|
|
|
|
|
|
if (!dvb_ringbuffer_empty(rbuf))
|
|
|
|
mask |= EPOLLIN | EPOLLRDNORM;
|
|
|
|
if (dvb_ringbuffer_free(wbuf) >= 188)
|
|
|
|
mask |= EPOLLOUT | EPOLLWRNORM;
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2011-01-10 12:36:15 +03:00
|
|
|
static const struct file_operations ci_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.read = ts_read,
|
|
|
|
.write = ts_write,
|
|
|
|
.open = dvb_generic_open,
|
|
|
|
.release = dvb_generic_release,
|
2018-02-25 20:06:56 +03:00
|
|
|
.poll = ts_poll,
|
|
|
|
.mmap = NULL,
|
2011-01-10 12:36:15 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
struct dvb_device ngene_dvbdev_ci = {
|
2018-02-25 20:06:56 +03:00
|
|
|
.priv = NULL,
|
|
|
|
.readers = 1,
|
|
|
|
.writers = 1,
|
|
|
|
.users = 2,
|
2011-01-10 12:36:15 +03:00
|
|
|
.fops = &ci_fops,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-03-14 05:10:13 +03:00
|
|
|
/****************************************************************************/
|
|
|
|
/* DVB functions and API interface ******************************************/
|
|
|
|
/****************************************************************************/
|
|
|
|
|
|
|
|
static void swap_buffer(u32 *p, u32 len)
|
|
|
|
{
|
|
|
|
while (len) {
|
|
|
|
*p = swab32(*p);
|
|
|
|
p++;
|
|
|
|
len -= 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-03 21:04:46 +04:00
|
|
|
/* start of filler packet */
|
|
|
|
static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
|
|
|
|
|
media: ngene: compensate for TS buffer offset shifts
A possible hardware bug was discovered when using CA addon hardware
attached to the ngene hardware, in that the TS input buffer much likely
will shift and thus become unaligned to 188 byte blocks (a full TS frame)
when things like CA module initialisation (which happens via differing
communication paths) take place. This causes the TS NULL removal in
tsin_exchange() to fail to detect this previously inserted data and thus
causes userspace applications to receive data they didn't sent beforehand
and ultimately cause troubles.
On driver load with an inserted CAM, buffers are fine at first (note that
the driver has to keep the communication running from/to the card by
inserting TS NULL frames, this is done in tsout_exchange() via
FillTSBuffer() - that data is simply sent back by the hardware):
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47
After a few seconds, the CA module is recognised and initialised, which is
signalled by
dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully
This is where the first shift happens (this is always four bytes), buffer
becomes like this:
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f
Next, VDR, TVHeadend or any other CI aware application is started, buffers
will shift by even more bytes. It is believed this is due to the hardware
not handling control and data bytes properly distinct, and control data
having an influence on the actual data stream, which we cannot properly
detect at the driver level.
Workaround this hardware quirk by adding a detection for the TS sync byte
0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL
packet if buffers become unaligned, take note of that offset and apply
that when copying data to the DVB ring buffers. The last <188 bytes from
the hardware buffers are stored in a temp buffer (tsin_buffer), for which
the remainder will be in the beginning of the next hardware buffer (next
iteration of tsin_exchange()). That remainder will be appended to the
temp buffer and finally sent to the DVB ring buffer. The resulting TS
stream is perfectly fine, and the TS NULL packets inserted by the driver
which are sent back are properly removed. The resulting offset is being
clamped to 188 byte segments (one TS packet). Though this can result in
a repeated TS packet if the overall offset grows beyond this (and it
will grow only on CA initialisation), this is still way better than
unaligned TS frames and data sent to userspace that just isn't supposed
to be there.
This compensation can be toggled by the ci_tsfix modparam, which defaults
to 1 (enabled). In the case of problems, this can be turned off by setting
the parameter to 0 to restore the old behaviour.
Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2018-03-06 20:46:37 +03:00
|
|
|
static int tsin_find_offset(void *buf, u32 len)
|
|
|
|
{
|
|
|
|
int i, l;
|
|
|
|
|
|
|
|
l = len - sizeof(fill_ts);
|
|
|
|
if (l <= 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0; i < l; i++) {
|
|
|
|
if (((char *)buf)[i] == 0x47) {
|
|
|
|
if (!memcmp(buf + i, fill_ts, sizeof(fill_ts)))
|
|
|
|
return i % 188;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-03-06 20:46:36 +03:00
|
|
|
static inline void tsin_copy_stripped(struct ngene *dev, void *buf)
|
|
|
|
{
|
|
|
|
if (memcmp(buf, fill_ts, sizeof(fill_ts)) != 0) {
|
|
|
|
if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) {
|
|
|
|
dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188);
|
|
|
|
wake_up(&dev->tsin_rbuf.queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-07-03 21:04:46 +04:00
|
|
|
|
2010-03-14 05:10:13 +03:00
|
|
|
void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
|
|
|
|
{
|
|
|
|
struct ngene_channel *chan = priv;
|
2011-01-10 12:36:15 +03:00
|
|
|
struct ngene *dev = chan->dev;
|
media: ngene: compensate for TS buffer offset shifts
A possible hardware bug was discovered when using CA addon hardware
attached to the ngene hardware, in that the TS input buffer much likely
will shift and thus become unaligned to 188 byte blocks (a full TS frame)
when things like CA module initialisation (which happens via differing
communication paths) take place. This causes the TS NULL removal in
tsin_exchange() to fail to detect this previously inserted data and thus
causes userspace applications to receive data they didn't sent beforehand
and ultimately cause troubles.
On driver load with an inserted CAM, buffers are fine at first (note that
the driver has to keep the communication running from/to the card by
inserting TS NULL frames, this is done in tsout_exchange() via
FillTSBuffer() - that data is simply sent back by the hardware):
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47
After a few seconds, the CA module is recognised and initialised, which is
signalled by
dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully
This is where the first shift happens (this is always four bytes), buffer
becomes like this:
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f
Next, VDR, TVHeadend or any other CI aware application is started, buffers
will shift by even more bytes. It is believed this is due to the hardware
not handling control and data bytes properly distinct, and control data
having an influence on the actual data stream, which we cannot properly
detect at the driver level.
Workaround this hardware quirk by adding a detection for the TS sync byte
0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL
packet if buffers become unaligned, take note of that offset and apply
that when copying data to the DVB ring buffers. The last <188 bytes from
the hardware buffers are stored in a temp buffer (tsin_buffer), for which
the remainder will be in the beginning of the next hardware buffer (next
iteration of tsin_exchange()). That remainder will be appended to the
temp buffer and finally sent to the DVB ring buffer. The resulting TS
stream is perfectly fine, and the TS NULL packets inserted by the driver
which are sent back are properly removed. The resulting offset is being
clamped to 188 byte segments (one TS packet). Though this can result in
a repeated TS packet if the overall offset grows beyond this (and it
will grow only on CA initialisation), this is still way better than
unaligned TS frames and data sent to userspace that just isn't supposed
to be there.
This compensation can be toggled by the ci_tsfix modparam, which defaults
to 1 (enabled). In the case of problems, this can be turned off by setting
the parameter to 0 to restore the old behaviour.
Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2018-03-06 20:46:37 +03:00
|
|
|
int tsoff;
|
2010-03-14 05:10:13 +03:00
|
|
|
|
2011-01-10 12:36:15 +03:00
|
|
|
if (flags & DF_SWAP32)
|
|
|
|
swap_buffer(buf, len);
|
2011-07-03 21:04:46 +04:00
|
|
|
|
2011-01-10 12:36:15 +03:00
|
|
|
if (dev->ci.en && chan->number == 2) {
|
media: ngene: compensate for TS buffer offset shifts
A possible hardware bug was discovered when using CA addon hardware
attached to the ngene hardware, in that the TS input buffer much likely
will shift and thus become unaligned to 188 byte blocks (a full TS frame)
when things like CA module initialisation (which happens via differing
communication paths) take place. This causes the TS NULL removal in
tsin_exchange() to fail to detect this previously inserted data and thus
causes userspace applications to receive data they didn't sent beforehand
and ultimately cause troubles.
On driver load with an inserted CAM, buffers are fine at first (note that
the driver has to keep the communication running from/to the card by
inserting TS NULL frames, this is done in tsout_exchange() via
FillTSBuffer() - that data is simply sent back by the hardware):
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47
After a few seconds, the CA module is recognised and initialised, which is
signalled by
dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully
This is where the first shift happens (this is always four bytes), buffer
becomes like this:
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f
Next, VDR, TVHeadend or any other CI aware application is started, buffers
will shift by even more bytes. It is believed this is due to the hardware
not handling control and data bytes properly distinct, and control data
having an influence on the actual data stream, which we cannot properly
detect at the driver level.
Workaround this hardware quirk by adding a detection for the TS sync byte
0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL
packet if buffers become unaligned, take note of that offset and apply
that when copying data to the DVB ring buffers. The last <188 bytes from
the hardware buffers are stored in a temp buffer (tsin_buffer), for which
the remainder will be in the beginning of the next hardware buffer (next
iteration of tsin_exchange()). That remainder will be appended to the
temp buffer and finally sent to the DVB ring buffer. The resulting TS
stream is perfectly fine, and the TS NULL packets inserted by the driver
which are sent back are properly removed. The resulting offset is being
clamped to 188 byte segments (one TS packet). Though this can result in
a repeated TS packet if the overall offset grows beyond this (and it
will grow only on CA initialisation), this is still way better than
unaligned TS frames and data sent to userspace that just isn't supposed
to be there.
This compensation can be toggled by the ci_tsfix modparam, which defaults
to 1 (enabled). In the case of problems, this can be turned off by setting
the parameter to 0 to restore the old behaviour.
Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2018-03-06 20:46:37 +03:00
|
|
|
/* blindly copy buffers if ci_tsfix is disabled */
|
|
|
|
if (!ci_tsfix) {
|
|
|
|
while (len >= 188) {
|
|
|
|
tsin_copy_stripped(dev, buf);
|
|
|
|
|
|
|
|
buf += 188;
|
|
|
|
len -= 188;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ci_tsfix = 1 */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* since the remainder of the TS packet which got cut off
|
|
|
|
* in the previous tsin_exchange() run is at the beginning
|
|
|
|
* of the new TS buffer, append this to the temp buffer and
|
|
|
|
* send it to the DVB ringbuffer afterwards.
|
|
|
|
*/
|
|
|
|
if (chan->tsin_offset) {
|
|
|
|
memcpy(&chan->tsin_buffer[(188 - chan->tsin_offset)],
|
|
|
|
buf, chan->tsin_offset);
|
|
|
|
tsin_copy_stripped(dev, &chan->tsin_buffer);
|
|
|
|
|
|
|
|
buf += chan->tsin_offset;
|
|
|
|
len -= chan->tsin_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* copy TS packets to the DVB ringbuffer and detect new offset
|
|
|
|
* shifts by checking for a valid TS SYNC byte
|
|
|
|
*/
|
2011-07-03 21:04:46 +04:00
|
|
|
while (len >= 188) {
|
media: ngene: compensate for TS buffer offset shifts
A possible hardware bug was discovered when using CA addon hardware
attached to the ngene hardware, in that the TS input buffer much likely
will shift and thus become unaligned to 188 byte blocks (a full TS frame)
when things like CA module initialisation (which happens via differing
communication paths) take place. This causes the TS NULL removal in
tsin_exchange() to fail to detect this previously inserted data and thus
causes userspace applications to receive data they didn't sent beforehand
and ultimately cause troubles.
On driver load with an inserted CAM, buffers are fine at first (note that
the driver has to keep the communication running from/to the card by
inserting TS NULL frames, this is done in tsout_exchange() via
FillTSBuffer() - that data is simply sent back by the hardware):
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47
After a few seconds, the CA module is recognised and initialised, which is
signalled by
dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully
This is where the first shift happens (this is always four bytes), buffer
becomes like this:
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f
Next, VDR, TVHeadend or any other CI aware application is started, buffers
will shift by even more bytes. It is believed this is due to the hardware
not handling control and data bytes properly distinct, and control data
having an influence on the actual data stream, which we cannot properly
detect at the driver level.
Workaround this hardware quirk by adding a detection for the TS sync byte
0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL
packet if buffers become unaligned, take note of that offset and apply
that when copying data to the DVB ring buffers. The last <188 bytes from
the hardware buffers are stored in a temp buffer (tsin_buffer), for which
the remainder will be in the beginning of the next hardware buffer (next
iteration of tsin_exchange()). That remainder will be appended to the
temp buffer and finally sent to the DVB ring buffer. The resulting TS
stream is perfectly fine, and the TS NULL packets inserted by the driver
which are sent back are properly removed. The resulting offset is being
clamped to 188 byte segments (one TS packet). Though this can result in
a repeated TS packet if the overall offset grows beyond this (and it
will grow only on CA initialisation), this is still way better than
unaligned TS frames and data sent to userspace that just isn't supposed
to be there.
This compensation can be toggled by the ci_tsfix modparam, which defaults
to 1 (enabled). In the case of problems, this can be turned off by setting
the parameter to 0 to restore the old behaviour.
Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2018-03-06 20:46:37 +03:00
|
|
|
if (*((char *)buf) != 0x47) {
|
|
|
|
/*
|
|
|
|
* no SYNC header, find new offset shift
|
|
|
|
* (max. 188 bytes, tsoff will be mod 188)
|
|
|
|
*/
|
|
|
|
tsoff = tsin_find_offset(buf, len);
|
|
|
|
if (tsoff > 0) {
|
|
|
|
chan->tsin_offset += tsoff;
|
|
|
|
chan->tsin_offset %= 188;
|
|
|
|
|
|
|
|
buf += tsoff;
|
|
|
|
len -= tsoff;
|
|
|
|
|
|
|
|
dev_info(&dev->pci_dev->dev,
|
|
|
|
"%s(): tsin_offset shift by %d on channel %d\n",
|
|
|
|
__func__, tsoff,
|
|
|
|
chan->number);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* offset corrected. re-check remaining
|
|
|
|
* len for a full TS frame, break and
|
|
|
|
* skip to fragment handling if < 188.
|
|
|
|
*/
|
|
|
|
if (len < 188)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-06 20:46:36 +03:00
|
|
|
tsin_copy_stripped(dev, buf);
|
|
|
|
|
2011-07-03 21:04:46 +04:00
|
|
|
buf += 188;
|
|
|
|
len -= 188;
|
2011-01-10 12:36:15 +03:00
|
|
|
}
|
media: ngene: compensate for TS buffer offset shifts
A possible hardware bug was discovered when using CA addon hardware
attached to the ngene hardware, in that the TS input buffer much likely
will shift and thus become unaligned to 188 byte blocks (a full TS frame)
when things like CA module initialisation (which happens via differing
communication paths) take place. This causes the TS NULL removal in
tsin_exchange() to fail to detect this previously inserted data and thus
causes userspace applications to receive data they didn't sent beforehand
and ultimately cause troubles.
On driver load with an inserted CAM, buffers are fine at first (note that
the driver has to keep the communication running from/to the card by
inserting TS NULL frames, this is done in tsout_exchange() via
FillTSBuffer() - that data is simply sent back by the hardware):
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 47 1f ff 10 6f 6f .... 47 1f ff 10 6f 6f .... 47
After a few seconds, the CA module is recognised and initialised, which is
signalled by
dvb_ca_en50221: dvb_ca adapter X: DVB CAM detected and initialised successfully
This is where the first shift happens (this is always four bytes), buffer
becomes like this:
offset | 0 1 2 3 4 5 .... 188 189 190 191 192 193 .... 376
data | 6f 6f 6f 6f 47 1f .... 6f 6f 6f 6f 47 1f .... 6f
Next, VDR, TVHeadend or any other CI aware application is started, buffers
will shift by even more bytes. It is believed this is due to the hardware
not handling control and data bytes properly distinct, and control data
having an influence on the actual data stream, which we cannot properly
detect at the driver level.
Workaround this hardware quirk by adding a detection for the TS sync byte
0x47 before each TS frame copy, scan for a new SYNC byte and a TS NULL
packet if buffers become unaligned, take note of that offset and apply
that when copying data to the DVB ring buffers. The last <188 bytes from
the hardware buffers are stored in a temp buffer (tsin_buffer), for which
the remainder will be in the beginning of the next hardware buffer (next
iteration of tsin_exchange()). That remainder will be appended to the
temp buffer and finally sent to the DVB ring buffer. The resulting TS
stream is perfectly fine, and the TS NULL packets inserted by the driver
which are sent back are properly removed. The resulting offset is being
clamped to 188 byte segments (one TS packet). Though this can result in
a repeated TS packet if the overall offset grows beyond this (and it
will grow only on CA initialisation), this is still way better than
unaligned TS frames and data sent to userspace that just isn't supposed
to be there.
This compensation can be toggled by the ci_tsfix modparam, which defaults
to 1 (enabled). In the case of problems, this can be turned off by setting
the parameter to 0 to restore the old behaviour.
Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2018-03-06 20:46:37 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if a fragment is left, copy to temp buffer. The remainder
|
|
|
|
* will be appended in the next tsin_exchange() iteration.
|
|
|
|
*/
|
|
|
|
if (len > 0 && len < 188)
|
|
|
|
memcpy(&chan->tsin_buffer, buf, len);
|
|
|
|
|
2011-07-03 21:04:46 +04:00
|
|
|
return NULL;
|
2011-01-10 12:36:15 +03:00
|
|
|
}
|
2011-07-03 21:04:46 +04:00
|
|
|
|
2011-07-03 20:56:28 +04:00
|
|
|
if (chan->users > 0)
|
2010-03-14 05:10:13 +03:00
|
|
|
dvb_dmx_swfilter(&chan->demux, buf, len);
|
2011-07-03 20:56:28 +04:00
|
|
|
|
2010-03-14 05:10:13 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags)
|
|
|
|
{
|
|
|
|
struct ngene_channel *chan = priv;
|
|
|
|
struct ngene *dev = chan->dev;
|
|
|
|
u32 alen;
|
|
|
|
|
|
|
|
alen = dvb_ringbuffer_avail(&dev->tsout_rbuf);
|
|
|
|
alen -= alen % 188;
|
|
|
|
|
|
|
|
if (alen < len)
|
|
|
|
FillTSBuffer(buf + alen, len - alen, flags);
|
|
|
|
else
|
|
|
|
alen = len;
|
|
|
|
dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen);
|
|
|
|
if (flags & DF_SWAP32)
|
|
|
|
swap_buffer((u32 *)buf, alen);
|
|
|
|
wake_up_interruptible(&dev->tsout_rbuf.queue);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed)
|
|
|
|
{
|
|
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
|
|
struct ngene_channel *chan = dvbdmx->priv;
|
|
|
|
|
|
|
|
if (chan->users == 0) {
|
2010-05-16 13:07:07 +04:00
|
|
|
if (!chan->dev->cmd_timeout_workaround || !chan->running)
|
2010-03-14 05:10:13 +03:00
|
|
|
set_transfer(chan, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ++chan->users;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
|
|
|
|
{
|
|
|
|
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
|
|
struct ngene_channel *chan = dvbdmx->priv;
|
|
|
|
|
|
|
|
if (--chan->users)
|
|
|
|
return chan->users;
|
|
|
|
|
2010-05-16 13:07:07 +04:00
|
|
|
if (!chan->dev->cmd_timeout_workaround)
|
|
|
|
set_transfer(chan, 0);
|
2010-03-14 05:10:13 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
|
|
|
|
int (*start_feed)(struct dvb_demux_feed *),
|
|
|
|
int (*stop_feed)(struct dvb_demux_feed *),
|
|
|
|
void *priv)
|
|
|
|
{
|
|
|
|
dvbdemux->priv = priv;
|
|
|
|
|
|
|
|
dvbdemux->filternum = 256;
|
|
|
|
dvbdemux->feednum = 256;
|
|
|
|
dvbdemux->start_feed = start_feed;
|
|
|
|
dvbdemux->stop_feed = stop_feed;
|
|
|
|
dvbdemux->write_to_decoder = NULL;
|
|
|
|
dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
|
|
|
|
DMX_SECTION_FILTERING |
|
|
|
|
DMX_MEMORY_BASED_FILTERING);
|
|
|
|
return dvb_dmx_init(dvbdemux);
|
|
|
|
}
|
|
|
|
|
|
|
|
int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
|
|
|
|
struct dvb_demux *dvbdemux,
|
|
|
|
struct dmx_frontend *hw_frontend,
|
|
|
|
struct dmx_frontend *mem_frontend,
|
|
|
|
struct dvb_adapter *dvb_adapter)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dmxdev->filternum = 256;
|
|
|
|
dmxdev->demux = &dvbdemux->dmx;
|
|
|
|
dmxdev->capabilities = 0;
|
|
|
|
ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
hw_frontend->source = DMX_FRONTEND_0;
|
|
|
|
dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
|
|
|
|
mem_frontend->source = DMX_MEMORY_FE;
|
|
|
|
dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
|
|
|
|
return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
|
|
|
|
}
|