Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / media / pci / ttpci / av7110_ipack.c
diff --git a/kernel/drivers/media/pci/ttpci/av7110_ipack.c b/kernel/drivers/media/pci/ttpci/av7110_ipack.c
new file mode 100644 (file)
index 0000000..699ef8b
--- /dev/null
@@ -0,0 +1,403 @@
+#include "dvb_filter.h"
+#include "av7110_ipack.h"
+#include <linux/string.h>      /* for memcpy() */
+#include <linux/vmalloc.h>
+
+
+void av7110_ipack_reset(struct ipack *p)
+{
+       p->found = 0;
+       p->cid = 0;
+       p->plength = 0;
+       p->flag1 = 0;
+       p->flag2 = 0;
+       p->hlength = 0;
+       p->mpeg = 0;
+       p->check = 0;
+       p->which = 0;
+       p->done = 0;
+       p->count = 0;
+}
+
+
+int av7110_ipack_init(struct ipack *p, int size,
+                     void (*func)(u8 *buf, int size, void *priv))
+{
+       if (!(p->buf = vmalloc(size*sizeof(u8)))) {
+               printk(KERN_WARNING "Couldn't allocate memory for ipack\n");
+               return -ENOMEM;
+       }
+       p->size = size;
+       p->func = func;
+       p->repack_subids = 0;
+       av7110_ipack_reset(p);
+       return 0;
+}
+
+
+void av7110_ipack_free(struct ipack *p)
+{
+       vfree(p->buf);
+}
+
+
+static void send_ipack(struct ipack *p)
+{
+       int off;
+       struct dvb_audio_info ai;
+       int ac3_off = 0;
+       int streamid = 0;
+       int nframes = 0;
+       int f = 0;
+
+       switch (p->mpeg) {
+       case 2:
+               if (p->count < 10)
+                       return;
+               p->buf[3] = p->cid;
+               p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
+               p->buf[5] = (u8)((p->count - 6) & 0x00ff);
+               if (p->repack_subids && p->cid == PRIVATE_STREAM1) {
+                       off = 9 + p->buf[8];
+                       streamid = p->buf[off];
+                       if ((streamid & 0xf8) == 0x80) {
+                               ai.off = 0;
+                               ac3_off = ((p->buf[off + 2] << 8)|
+                                          p->buf[off + 3]);
+                               if (ac3_off < p->count)
+                                       f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off,
+                                                                  p->count - ac3_off, &ai, 0);
+                               if (!f) {
+                                       nframes = (p->count - off - 3 - ac3_off) /
+                                               ai.framesize + 1;
+                                       p->buf[off + 2] = (ac3_off >> 8) & 0xff;
+                                       p->buf[off + 3] = (ac3_off) & 0xff;
+                                       p->buf[off + 1] = nframes;
+                                       ac3_off +=  nframes * ai.framesize - p->count;
+                               }
+                       }
+               }
+               p->func(p->buf, p->count, p->data);
+
+               p->buf[6] = 0x80;
+               p->buf[7] = 0x00;
+               p->buf[8] = 0x00;
+               p->count = 9;
+               if (p->repack_subids && p->cid == PRIVATE_STREAM1
+                   && (streamid & 0xf8) == 0x80) {
+                       p->count += 4;
+                       p->buf[9] = streamid;
+                       p->buf[10] = (ac3_off >> 8) & 0xff;
+                       p->buf[11] = (ac3_off) & 0xff;
+                       p->buf[12] = 0;
+               }
+               break;
+
+       case 1:
+               if (p->count < 8)
+                       return;
+               p->buf[3] = p->cid;
+               p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
+               p->buf[5] = (u8)((p->count - 6) & 0x00ff);
+               p->func(p->buf, p->count, p->data);
+
+               p->buf[6] = 0x0f;
+               p->count = 7;
+               break;
+       }
+}
+
+
+void av7110_ipack_flush(struct ipack *p)
+{
+       if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6)
+               return;
+       p->plength = p->found - 6;
+       p->found = 0;
+       send_ipack(p);
+       av7110_ipack_reset(p);
+}
+
+
+static void write_ipack(struct ipack *p, const u8 *data, int count)
+{
+       u8 headr[3] = { 0x00, 0x00, 0x01 };
+
+       if (p->count < 6) {
+               memcpy(p->buf, headr, 3);
+               p->count = 6;
+       }
+
+       if (p->count + count < p->size){
+               memcpy(p->buf+p->count, data, count);
+               p->count += count;
+       } else {
+               int rest = p->size - p->count;
+               memcpy(p->buf+p->count, data, rest);
+               p->count += rest;
+               send_ipack(p);
+               if (count - rest > 0)
+                       write_ipack(p, data + rest, count - rest);
+       }
+}
+
+
+int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p)
+{
+       int l;
+       int c = 0;
+
+       while (c < count && (p->mpeg == 0 ||
+                            (p->mpeg == 1 && p->found < 7) ||
+                            (p->mpeg == 2 && p->found < 9))
+              &&  (p->found < 5 || !p->done)) {
+               switch (p->found) {
+               case 0:
+               case 1:
+                       if (buf[c] == 0x00)
+                               p->found++;
+                       else
+                               p->found = 0;
+                       c++;
+                       break;
+               case 2:
+                       if (buf[c] == 0x01)
+                               p->found++;
+                       else if (buf[c] == 0)
+                               p->found = 2;
+                       else
+                               p->found = 0;
+                       c++;
+                       break;
+               case 3:
+                       p->cid = 0;
+                       switch (buf[c]) {
+                       case PROG_STREAM_MAP:
+                       case PRIVATE_STREAM2:
+                       case PROG_STREAM_DIR:
+                       case ECM_STREAM     :
+                       case EMM_STREAM     :
+                       case PADDING_STREAM :
+                       case DSM_CC_STREAM  :
+                       case ISO13522_STREAM:
+                               p->done = 1;
+                               /* fall through */
+                       case PRIVATE_STREAM1:
+                       case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+                       case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+                               p->found++;
+                               p->cid = buf[c];
+                               c++;
+                               break;
+                       default:
+                               p->found = 0;
+                               break;
+                       }
+                       break;
+
+               case 4:
+                       if (count-c > 1) {
+                               p->plen[0] = buf[c];
+                               c++;
+                               p->plen[1] = buf[c];
+                               c++;
+                               p->found += 2;
+                               p->plength = (p->plen[0] << 8) | p->plen[1];
+                       } else {
+                               p->plen[0] = buf[c];
+                               p->found++;
+                               return count;
+                       }
+                       break;
+               case 5:
+                       p->plen[1] = buf[c];
+                       c++;
+                       p->found++;
+                       p->plength = (p->plen[0] << 8) | p->plen[1];
+                       break;
+               case 6:
+                       if (!p->done) {
+                               p->flag1 = buf[c];
+                               c++;
+                               p->found++;
+                               if ((p->flag1 & 0xc0) == 0x80)
+                                       p->mpeg = 2;
+                               else {
+                                       p->hlength = 0;
+                                       p->which = 0;
+                                       p->mpeg = 1;
+                                       p->flag2 = 0;
+                               }
+                       }
+                       break;
+
+               case 7:
+                       if (!p->done && p->mpeg == 2) {
+                               p->flag2 = buf[c];
+                               c++;
+                               p->found++;
+                       }
+                       break;
+
+               case 8:
+                       if (!p->done && p->mpeg == 2) {
+                               p->hlength = buf[c];
+                               c++;
+                               p->found++;
+                       }
+                       break;
+               }
+       }
+
+       if (c == count)
+               return count;
+
+       if (!p->plength)
+               p->plength = MMAX_PLENGTH - 6;
+
+       if (p->done || ((p->mpeg == 2 && p->found >= 9) ||
+                       (p->mpeg == 1 && p->found >= 7))) {
+               switch (p->cid) {
+               case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+               case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+               case PRIVATE_STREAM1:
+                       if (p->mpeg == 2 && p->found == 9) {
+                               write_ipack(p, &p->flag1, 1);
+                               write_ipack(p, &p->flag2, 1);
+                               write_ipack(p, &p->hlength, 1);
+                       }
+
+                       if (p->mpeg == 1 && p->found == 7)
+                               write_ipack(p, &p->flag1, 1);
+
+                       if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+                           p->found < 14) {
+                               while (c < count && p->found < 14) {
+                                       p->pts[p->found - 9] = buf[c];
+                                       write_ipack(p, buf + c, 1);
+                                       c++;
+                                       p->found++;
+                               }
+                               if (c == count)
+                                       return count;
+                       }
+
+                       if (p->mpeg == 1 && p->which < 2000) {
+
+                               if (p->found == 7) {
+                                       p->check = p->flag1;
+                                       p->hlength = 1;
+                               }
+
+                               while (!p->which && c < count &&
+                                      p->check == 0xff){
+                                       p->check = buf[c];
+                                       write_ipack(p, buf + c, 1);
+                                       c++;
+                                       p->found++;
+                                       p->hlength++;
+                               }
+
+                               if (c == count)
+                                       return count;
+
+                               if ((p->check & 0xc0) == 0x40 && !p->which) {
+                                       p->check = buf[c];
+                                       write_ipack(p, buf + c, 1);
+                                       c++;
+                                       p->found++;
+                                       p->hlength++;
+
+                                       p->which = 1;
+                                       if (c == count)
+                                               return count;
+                                       p->check = buf[c];
+                                       write_ipack(p, buf + c, 1);
+                                       c++;
+                                       p->found++;
+                                       p->hlength++;
+                                       p->which = 2;
+                                       if (c == count)
+                                               return count;
+                               }
+
+                               if (p->which == 1) {
+                                       p->check = buf[c];
+                                       write_ipack(p, buf + c, 1);
+                                       c++;
+                                       p->found++;
+                                       p->hlength++;
+                                       p->which = 2;
+                                       if (c == count)
+                                               return count;
+                               }
+
+                               if ((p->check & 0x30) && p->check != 0xff) {
+                                       p->flag2 = (p->check & 0xf0) << 2;
+                                       p->pts[0] = p->check;
+                                       p->which = 3;
+                               }
+
+                               if (c == count)
+                                       return count;
+                               if (p->which > 2){
+                                       if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) {
+                                               while (c < count && p->which < 7) {
+                                                       p->pts[p->which - 2] = buf[c];
+                                                       write_ipack(p, buf + c, 1);
+                                                       c++;
+                                                       p->found++;
+                                                       p->which++;
+                                                       p->hlength++;
+                                               }
+                                               if (c == count)
+                                                       return count;
+                                       } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) {
+                                               while (c < count && p->which < 12) {
+                                                       if (p->which < 7)
+                                                               p->pts[p->which - 2] = buf[c];
+                                                       write_ipack(p, buf + c, 1);
+                                                       c++;
+                                                       p->found++;
+                                                       p->which++;
+                                                       p->hlength++;
+                                               }
+                                               if (c == count)
+                                                       return count;
+                                       }
+                                       p->which = 2000;
+                               }
+
+                       }
+
+                       while (c < count && p->found < p->plength + 6) {
+                               l = count - c;
+                               if (l + p->found > p->plength + 6)
+                                       l = p->plength + 6 - p->found;
+                               write_ipack(p, buf + c, l);
+                               p->found += l;
+                               c += l;
+                       }
+                       break;
+               }
+
+
+               if (p->done) {
+                       if (p->found + count - c < p->plength + 6) {
+                               p->found += count - c;
+                               c = count;
+                       } else {
+                               c += p->plength + 6 - p->found;
+                               p->found = p->plength + 6;
+                       }
+               }
+
+               if (p->plength && p->found == p->plength + 6) {
+                       send_ipack(p);
+                       av7110_ipack_reset(p);
+                       if (c < count)
+                               av7110_ipack_instant_repack(buf + c, count - c, p);
+               }
+       }
+       return count;
+}