]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
If we are unable to create a demultiplex filter in hardware for a table,
authorAndreas Öman <andreas@lonelycoder.com>
Mon, 22 Jun 2009 21:18:09 +0000 (21:18 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Mon, 22 Jun 2009 21:18:09 +0000 (21:18 +0000)
put the table on a pending queue and cycle through all tables.

Fixes problems with low-end adapters that does not support that many
filter in hardware.

src/dvb/dvb.h
src/dvb/dvb_multiplex.c
src/dvb/dvb_tables.c

index 391cb68647e44781b3af56fdad65e8120da05ee5..fa62c87405f26965e7989b6a58da462994fae6d3 100644 (file)
@@ -56,6 +56,7 @@ typedef struct th_dvb_mux_instance {
 
   time_t tdmi_time;
   LIST_HEAD(, th_dvb_table) tdmi_tables;
+  TAILQ_HEAD(, th_dvb_table) tdmi_table_queue;
 
   enum {
     TDMI_FE_UNKNOWN,
index 132e09dbce219cce1c85081abfe0e94b7cd44ff4..a9856c8d9796776d57c2ef0e7f3678f1c5dbc13e 100644 (file)
@@ -128,6 +128,8 @@ dvb_mux_create(th_dvb_adapter_t *tda, struct dvb_frontend_parameters *fe_param,
   tdmi->tdmi_scan_queue = &tda->tda_scan_queues[DVB_MUX_SCAN_INITIAL];
   TAILQ_INSERT_TAIL(tdmi->tdmi_scan_queue, tdmi, tdmi_scan_link);
 
+  TAILQ_INIT(&tdmi->tdmi_table_queue);
+
   tdmi->tdmi_transport_stream_id = tsid;
   tdmi->tdmi_adapter = tda;
   tdmi->tdmi_network = network ? strdup(network) : NULL;
index 0fec3248df02e6d325541b8032560f9e981b8d46..83232b75913aa4a2f9d78a75ffbfc3cd9974a3d0 100644 (file)
@@ -28,6 +28,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include <linux/dvb/frontend.h>
 #include <linux/dvb/dmx.h>
 
 #define TDT_CRC           0x1
 #define TDT_QUICKREQ      0x2
-#define TDT_FREE_OPAQUE   0x4
-#define TDT_INC_TABLE_HDR 0x8
-
-static int tid_tally;
+#define TDT_INC_TABLE_HDR 0x4
 
+static int tdt_id_tally;
 
 /**
  *
  */
 typedef struct th_dvb_table {
+  /**
+   * Flags, must never be changed after creation.
+   * We inspect it without holding global_lock
+   */
+  int tdt_flags;
+
+  /**
+   * Cycle queue
+   * Tables that did not get a fd or filter in hardware will end up here
+   * waiting for any other table to be received so it can reuse that fd.
+   * Only linked if fd == -1
+   */
+  TAILQ_ENTRY(th_dvb_table) tdt_pending_link;
+
+  /**
+   * File descriptor for filter
+   */
+  int tdt_fd;
+
   LIST_ENTRY(th_dvb_table) tdt_link;
+
   char *tdt_name;
 
   void *tdt_opaque;
   void (*tdt_callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
                       uint8_t tableid, void *opaque);
 
-  int tdt_fd;
 
   int tdt_count;
-  int tdt_id;
   int tdt_pid;
-  int tdt_flags;
+
+  struct dmx_sct_filter_params *tdt_fparams;
+
+  int tdt_id;
+
 } th_dvb_table_t;
 
 
@@ -105,6 +126,95 @@ dvb_table_fastswitch(th_dvb_mux_instance_t *tdmi)
 }
 
 
+/**
+ *
+ */
+static void
+tdt_open_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
+{
+  th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+  struct epoll_event e;
+  
+  assert(tdt->tdt_fd == -1);
+  TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
+
+  tdt->tdt_fd = open(tda->tda_demux_path, O_RDWR);
+
+  if(tdt->tdt_fd != -1) {
+
+    tdt->tdt_id = ++tdt_id_tally;
+
+    e.events = EPOLLIN;
+    e.data.u64 = ((uint64_t)tdt->tdt_fd << 32) | tdt->tdt_id;
+
+    if(epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, tdt->tdt_fd, &e)) {
+      close(tdt->tdt_fd);
+      tdt->tdt_fd = -1;
+    } else {
+      if(ioctl(tdt->tdt_fd, DMX_SET_FILTER, tdt->tdt_fparams)) {
+       close(tdt->tdt_fd);
+       tdt->tdt_fd = -1;
+      }
+    }
+  }
+
+  if(tdt->tdt_fd == -1)
+    TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
+}
+
+
+/**
+ * Close FD for the given table and put table on the pending list
+ */
+static void
+tdt_close_fd(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt)
+{
+  th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
+
+  assert(tdt->tdt_fd != -1);
+
+  epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
+  close(tdt->tdt_fd);
+
+  tdt->tdt_fd = -1;
+  TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
+}
+
+
+/**
+ *
+ */
+static void
+dvb_proc_table(th_dvb_mux_instance_t *tdmi, th_dvb_table_t *tdt, uint8_t *sec,
+              int r)
+{
+  int chkcrc = tdt->tdt_flags & TDT_INC_TABLE_HDR;
+  int tableid, len;
+  uint8_t *ptr;
+
+  /* It seems some hardware (or is it the dvb API?) does not
+     honour the DMX_CHECK_CRC flag, so we check it again */
+  if(chkcrc && psi_crc32(sec, r))
+    return;
+      
+  r -= 3;
+  tableid = sec[0];
+  len = ((sec[1] & 0x0f) << 8) | sec[2];
+  
+  if(len < r)
+    return;
+
+  ptr = &sec[3];
+  if(chkcrc) len -= 4;   /* Strip trailing CRC */
+
+  if(tdt->tdt_flags & TDT_INC_TABLE_HDR)
+    tdt->tdt_callback(tdmi, sec, len + 3, tableid, tdt->tdt_opaque);
+  else
+    tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque);
+  
+  dvb_table_fastswitch(tdmi);
+}
+
 /**
  *
  */
@@ -112,63 +222,48 @@ static void *
 dvb_table_input(void *aux)
 {
   th_dvb_adapter_t *tda = aux;
-  int r, i, tid, fd, tableid, len, x;
+  int r, i, tid, fd, x;
   struct epoll_event ev[1];
-  uint8_t sec[4096], *ptr;
+  uint8_t sec[4096];
   th_dvb_mux_instance_t *tdmi;
   th_dvb_table_t *tdt;
-  int chkcrc;
 
   while(1) {
     x = epoll_wait(tda->tda_table_epollfd, ev, sizeof(ev) / sizeof(ev[0]), -1);
-    for(i = 0; i < x; i++) {
-      
-      chkcrc = ev[i].data.u64 & 1; 
-      fd     = (ev[i].data.u64 >> 16) & 0xffff;
-      tid    = ev[i].data.u64 >> 32;
 
-      if(ev[i].events & EPOLLIN) {
+    for(i = 0; i < x; i++) {
+    
+      tid = ev[i].data.u64 & 0xffffffff;
+      fd  = ev[i].data.u64 >> 32; 
 
-       if((r = read(fd, sec, sizeof(sec))) < 3)
-         continue;
+      if(!(ev[i].events & EPOLLIN))
+       continue;
 
-       /* It seems some hardware (or is it the dvb API?) does not
-          honour the DMX_CHECK_CRC flag, so we check it again */
-       if(chkcrc && psi_crc32(sec, r))
-         continue;
+      if((r = read(fd, sec, sizeof(sec))) < 3)
+       continue;
 
-       r -= 3;
-       tableid = sec[0];
-       len = ((sec[1] & 0x0f) << 8) | sec[2];
-  
-       if(len < r)
-         continue;
+      pthread_mutex_lock(&global_lock);
+      tdmi = tda->tda_mux_current;
+      
+      LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
+       if(tdt->tdt_id == tid)
+         break;
 
-       ptr = &sec[3];
-       if(chkcrc) len -= 4;   /* Strip trailing CRC */
+      if(tdt != NULL) {
+       dvb_proc_table(tdmi, tdt, sec, r);
 
-       pthread_mutex_lock(&global_lock);
+       /* Any tables pending (that wants a filter/fd) */
+       if(TAILQ_FIRST(&tdmi->tdmi_table_queue) != NULL) {
+         tdt_close_fd(tdmi, tdt);
 
-       tdmi = tda->tda_mux_current;
+         tdt = TAILQ_FIRST(&tdmi->tdmi_table_queue);
+         assert(tdt != NULL);
 
-       LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
-         if(tdt->tdt_id == tid)
-           break;
-
-       if(tdt != NULL) {
-         if(tdt->tdt_flags & TDT_INC_TABLE_HDR)
-           tdt->tdt_callback(tdmi, sec, len + 3, tableid, tdt->tdt_opaque);
-         else
-           tdt->tdt_callback(tdmi, ptr, len, tableid, tdt->tdt_opaque);
-         dvb_table_fastswitch(tdmi);
+         tdt_open_fd(tdmi, tdt);
        }
-
-       pthread_mutex_unlock(&global_lock);
-
-      } else {
-       fprintf(stderr, "%s: spurious poll event %x on fd %d\n",
-               tda->tda_identifier, ev[i].events, fd);
       }
+
+      pthread_mutex_unlock(&global_lock);
     }
   }
   return NULL;
@@ -187,19 +282,25 @@ dvb_table_init(th_dvb_adapter_t *tda)
   pthread_create(&ptid, NULL, dvb_table_input, tda);
 }
 
+
 /**
  *
  */
 static void
-dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_table_t *tdt)
+dvb_tdt_destroy(th_dvb_adapter_t *tda, th_dvb_mux_instance_t *tdmi,
+               th_dvb_table_t *tdt)
 {
-  epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
-  if(tdt->tdt_flags & TDT_FREE_OPAQUE)
-    free(tdt->tdt_opaque);
+  LIST_REMOVE(tdt, tdt_link);
+
+  if(tdt->tdt_fd == -1) {
+    TAILQ_REMOVE(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
+  } else {
+    epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_DEL, tdt->tdt_fd, NULL);
+    close(tdt->tdt_fd);
+  }
 
   free(tdt->tdt_name);
-  LIST_REMOVE(tdt, tdt_link);
-  close(tdt->tdt_fd);
+  free(tdt->tdt_fparams);
   free(tdt);
 }
 
@@ -213,26 +314,16 @@ static void
 tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
        void (*callback)(th_dvb_mux_instance_t *tdmi, uint8_t *buf, int len,
                         uint8_t tableid, void *opaque), void *opaque,
-       const char *name, int flags, int pid)
+       const char *name, int flags, int pid, th_dvb_table_t *tdt)
 {
-  th_dvb_adapter_t *tda = tdmi->tdmi_adapter;
-  th_dvb_table_t *tdt;
-  int fd;
-  struct epoll_event e;
+  th_dvb_table_t *t;
 
-  LIST_FOREACH(tdt, &tdmi->tdmi_tables, tdt_link)
-    if(pid == tdt->tdt_pid) {
+  LIST_FOREACH(t, &tdmi->tdmi_tables, tdt_link) {
+    if(pid == t->tdt_pid) {
+      free(tdt);
       free(fparams);
-      if(flags & TDT_FREE_OPAQUE)
-       free(opaque);
       return;
     }
-
-  if((fd = open(tda->tda_demux_path, O_RDWR)) == -1) {
-    free(fparams);
-    if(flags & TDT_FREE_OPAQUE)
-      free(opaque);
-    return;
   }
 
   if(fparams == NULL)
@@ -242,24 +333,21 @@ tdt_add(th_dvb_mux_instance_t *tdmi, struct dmx_sct_filter_params *fparams,
   fparams->flags |= DMX_IMMEDIATE_START;
   fparams->pid = pid;
 
-  tdt = calloc(1, sizeof(th_dvb_table_t));
-  tdt->tdt_fd = fd;
+
+  if(tdt == NULL)
+    tdt = calloc(1, sizeof(th_dvb_table_t));
+
   tdt->tdt_name = strdup(name);
   tdt->tdt_callback = callback;
   tdt->tdt_opaque = opaque;
-  tdt->tdt_id = ++tid_tally;
   tdt->tdt_pid = pid;
   tdt->tdt_flags = flags;
-
-  e.events = EPOLLIN;
-  e.data.u64 = ((uint64_t)tdt->tdt_id << 32) | (fd << 16) | 
-    (flags & TDT_CRC ? 1 : 0);
-
-  epoll_ctl(tda->tda_table_epollfd, EPOLL_CTL_ADD, fd, &e);
-  ioctl(fd, DMX_SET_FILTER, fparams);
-  free(fparams);
+  tdt->tdt_fparams = fparams;
   LIST_INSERT_HEAD(&tdmi->tdmi_tables, tdt, tdt_link);
+  tdt->tdt_fd = -1;
+  TAILQ_INSERT_TAIL(&tdmi->tdmi_table_queue, tdt, tdt_pending_link);
+
+  tdt_open_fd(tdmi, tdt);
 }
 
 
@@ -617,6 +705,7 @@ dvb_pat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
 
 
 typedef struct ca_stream {
+  th_dvb_table_t tdt;
   int cs_caid;
 } ca_stream_t;
 
@@ -661,7 +750,7 @@ dvb_cat_callback(th_dvb_mux_instance_t *tdmi, uint8_t *ptr, int len,
       cs = calloc(1, sizeof(ca_stream_t));
       cs->cs_caid = caid;
       tdt_add(tdmi, NULL, dvb_ca_callback, cs, "CA", 
-             TDT_FREE_OPAQUE | TDT_INC_TABLE_HDR, pid);
+             TDT_INC_TABLE_HDR, pid, &cs->tdt);
       break;
 
     default:
@@ -905,7 +994,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
   fp->filter.filter[0] = 0x00;
   fp->filter.mask[0] = 0xff;
   tdt_add(tdmi, fp, dvb_pat_callback, NULL, "pat", 
-         TDT_QUICKREQ | TDT_CRC, 0);
+         TDT_QUICKREQ | TDT_CRC, 0, NULL);
 
   /* Conditional Access Table */
 
@@ -913,7 +1002,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
   fp->filter.filter[0] = 0x1;
   fp->filter.mask[0] = 0xff;
   tdt_add(tdmi, fp, dvb_cat_callback, NULL, "cat", 
-         TDT_CRC, 1);
+         TDT_CRC, 1, NULL);
 
   /* Network Information Table */
 
@@ -921,7 +1010,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
   fp->filter.filter[0] = 0x40;
   fp->filter.mask[0] = 0xff;
   tdt_add(tdmi, fp, dvb_nit_callback, NULL, "nit", 
-         TDT_QUICKREQ | TDT_CRC, 0x10);
+         TDT_QUICKREQ | TDT_CRC, 0x10, NULL);
 
   /* Service Descriptor Table */
 
@@ -929,13 +1018,13 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
   fp->filter.filter[0] = 0x42;
   fp->filter.mask[0] = 0xff;
   tdt_add(tdmi, fp, dvb_sdt_callback, NULL, "sdt", 
-         TDT_QUICKREQ | TDT_CRC, 0x11);
+         TDT_QUICKREQ | TDT_CRC, 0x11, NULL);
 
   /* Event Information table */
 
   fp = dvb_fparams_alloc();
   tdt_add(tdmi, fp, dvb_eit_callback, NULL, "eit", 
-         TDT_CRC, 0x12);
+         TDT_CRC, 0x12, NULL);
 
   /* Running Status Table */
 
@@ -943,7 +1032,7 @@ dvb_table_add_default(th_dvb_mux_instance_t *tdmi)
   fp->filter.filter[0] = 0x71;
   fp->filter.mask[0] = 0xff;
   tdt_add(tdmi, fp, dvb_rst_callback, NULL, "rst",
-         TDT_CRC, 0x13);
+         TDT_CRC, 0x13, NULL);
 
 }
 
@@ -964,7 +1053,7 @@ dvb_table_add_transport(th_dvb_mux_instance_t *tdmi, th_transport_t *t,
   fp->filter.filter[0] = 0x02;
   fp->filter.mask[0] = 0xff;
   tdt_add(tdmi, fp, dvb_pmt_callback, t, pmtname, 
-         TDT_CRC | TDT_QUICKREQ, pmt_pid);
+         TDT_CRC | TDT_QUICKREQ, pmt_pid, NULL);
 }
 
 
@@ -978,6 +1067,6 @@ dvb_table_flush_all(th_dvb_mux_instance_t *tdmi)
   th_dvb_table_t *tdt;
 
   while((tdt = LIST_FIRST(&tdmi->tdmi_tables)) != NULL)
-    dvb_tdt_destroy(tda, tdt);
+    dvb_tdt_destroy(tda, tdmi, tdt);
   
 }