Merge tag '5.7-rc-smb3-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / drivers / media / pci / ngene / ngene-dvb.c
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
28 static int ci_tsfix = 1;
29 module_param(ci_tsfix, int, 0444);
30 MODULE_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
36 static 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
53 static 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
77 static __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
97 static 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
107 struct 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
120 static 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 */
130 static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER };
131
132 static 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
150 static 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
160 void *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
253 void *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
275 int 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
288 int 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
302 int 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
320 int 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 }