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