]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
* Autodetect CPU capabilities and choose best mode for CSA descrambler.
authorAndreas Öman <andreas@lonelycoder.com>
Thu, 26 Nov 2009 20:32:58 +0000 (20:32 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Thu, 26 Nov 2009 20:32:58 +0000 (20:32 +0000)
    Ticket #122

Makefile
configure
debian/changelog
src/ffdecsa/FFdecsa.c
src/ffdecsa/FFdecsa.h
src/ffdecsa/ffdecsa_int.c [new file with mode: 0644]
src/ffdecsa/ffdecsa_interface.c [new file with mode: 0644]
src/ffdecsa/ffdecsa_mmx.c [new file with mode: 0644]
src/ffdecsa/ffdecsa_sse2.c [new file with mode: 0644]
src/main.c

index 99265fb24013d5bcb34b008d42f07f01f66c9cab..061e3252cab409416f590c3adb7a85abbdfb0cbd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -93,13 +93,16 @@ SRCS += src/dvb/dvb.c \
 SRCS += src/cwc.c \
        src/capmt.c \
        src/krypt.c \
-       src/ffdecsa/FFdecsa.c
+       src/ffdecsa/ffdecsa_interface.c \
+       src/ffdecsa/ffdecsa_int.c \
+       src/ffdecsa/ffdecsa_mmx.c \
+       src/ffdecsa/ffdecsa_sse2.c \
 
-ifneq ($(ARCH), ppc)
-${BUILDDIR}/src/ffdecsa/FFdecsa.o : CFLAGS = -mmmx
-endif
 LDFLAGS += -lcrypt
 
+${BUILDDIR}/src/ffdecsa/ffdecsa_mmx.o  : CFLAGS = -mmmx
+${BUILDDIR}/src/ffdecsa/ffdecsa_sse2.o : CFLAGS = -msse2
+
 #
 # Primary web interface
 #
@@ -135,7 +138,7 @@ OBJDIRS+= $(sort $(dir $(BUNDLE_OBJS)))
 # Common CFLAGS for all files
 CFLAGS_com  = -g -funsigned-char -O2 
 CFLAGS_com += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
-CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR} $(CFLAGS_ffdecsa)
+CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR}
 
 all:   ${PROG}
 
index c66e2f70fbc43b4463a042bfe75f3883c00bfaf6..193314bf8622be4b3f70b9a50d07eb9a0a231c81 100755 (executable)
--- a/configure
+++ b/configure
@@ -19,11 +19,6 @@ CPU=generic
 ARCH=`uname -m`
 OSENV="posix"
 PREFIX=/usr/local
-if [ "$ARCH" = "ppc" ] ; then
-  PARALLEL_MODE=PARALLEL_32_INT
-else
-  PARALLEL_MODE=PARALLEL_64_MMX
-fi
 
 show_help(){
   echo "Usage: configure [options]"
@@ -35,9 +30,6 @@ show_help(){
   echo "  --arch=arch              Build for this architecture [$ARCH]"
   echo "  --cpu=cpu                Build and optimize for specific CPU"
   echo "  --cc=CC                  Build using the given compiler"
-  echo "  --ffdecsa-mode=MODE      Select Mode for ffdecsa"
-  echo "                           PARALLEL_64_MMX (default)"
-  echo "                           PARALLEL_128_SSE2"
   echo "  --release                Stage for release"
   exit 1
 }
@@ -59,8 +51,6 @@ for opt do
   ;;
   --cc=*) CC="$optval"
   ;;
-  --ffdecsa-mode=*) PARALLEL_MODE="$optval"
-  ;;
   --enable-?*|--disable-?*)
   eval $(echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g')
   $action $option
@@ -117,11 +107,6 @@ if enabled avahi; then
     fi
 fi
 
-#
-# ffdecsa mode
-# 
-echo >>${CONFIG_MAK} "CFLAGS_ffdecsa = -DPARALLEL_MODE=${PARALLEL_MODE}" 
-
 #
 # configure ffmpeg
 #
index b725af2b1c7c556181bc689e998f16bfca12550e..27a5b6c66d19a7b6c074cdd8ca533610145dc39e 100644 (file)
@@ -25,6 +25,9 @@ hts-tvheadend (2.6) hts; urgency=low
   * Add support for configuring a per-channel pre/post extra time for 
     scheduled recordings. Ticket #104
 
+  * Autodetect CPU capabilities and choose best mode for CSA descrambler.
+    Ticket #122
+
 hts-tvheadend (2.5) hts; urgency=low
 
   * If a previosly detected DVB adapter was not present during startup,
index 3be9855e92b96b22a6f3df717103c1f7a243cd61..74eb13734d6acf0e443b81a98db579ef7c403113 100644 (file)
@@ -23,8 +23,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "FFdecsa.h"
-
 #ifndef NULL
 #define NULL 0
 #endif
 #define PARALLEL_128_SSE     1285
 #define PARALLEL_128_SSE2    1286
 
-//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
-#ifndef PARALLEL_MODE
-#define PARALLEL_MODE PARALLEL_64_MMX
-#endif
-//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
-
 #include "parallel_generic.h"
 //// conditionals
 #if PARALLEL_MODE==PARALLEL_32_4CHAR
@@ -68,6 +60,7 @@
 #include "parallel_032_4charA.h"
 #elif PARALLEL_MODE==PARALLEL_32_INT
 #include "parallel_032_int.h"
+#define FUNC(x) (x ## _32int)
 #elif PARALLEL_MODE==PARALLEL_64_8CHAR
 #include "parallel_064_8char.h"
 #elif PARALLEL_MODE==PARALLEL_64_8CHARA
@@ -78,6 +71,7 @@
 #include "parallel_064_long.h"
 #elif PARALLEL_MODE==PARALLEL_64_MMX
 #include "parallel_064_mmx.h"
+#define FUNC(x) (x ## _64mmx)
 #elif PARALLEL_MODE==PARALLEL_128_16CHAR
 #include "parallel_128_16char.h"
 #elif PARALLEL_MODE==PARALLEL_128_16CHARA
 #include "parallel_128_sse.h"
 #elif PARALLEL_MODE==PARALLEL_128_SSE2
 #include "parallel_128_sse2.h"
+#define FUNC(x) (x ## _128sse2)
 #else
 #error "unknown/undefined parallel mode"
 #endif
 
+
 // stuff depending on conditionals
 
 #define BYTES_PER_GROUP (GROUP_PARALLELISM/8)
@@ -333,7 +329,7 @@ static inline __attribute__((always_inline)) void trasp_8_N (unsigned char *in,u
 //-----block main function
 
 // block group
-static void block_decypher_group(
+static void block_decypher_group (
   batch *kkmulti,       // [In]  kkmulti[0]-kkmulti[55] 56 batches | Key schedule (each batch has repeated equal bytes).
   unsigned char *ib,    // [In]  (ib0,ib1,...ib7)...x32 32*8 bytes | Initialization vector.
   unsigned char *bd,    // [Out] (bd0,bd1,...bd7)...x32 32*8 bytes | Block decipher.
@@ -463,35 +459,6 @@ static void block_decypher_group(
 
 //-----------------------------------EXTERNAL INTERFACE
 
-//-----get internal parallelism
-
-int get_internal_parallelism(void){
-  return GROUP_PARALLELISM;
-}
-
-//-----get suggested cluster size
-
-int get_suggested_cluster_size(void){
-  int r;
-  r=GROUP_PARALLELISM+GROUP_PARALLELISM/10;
-  if(r<GROUP_PARALLELISM+5) r=GROUP_PARALLELISM+5;
-  return r;
-}
-
-//-----key structure
-
-void *get_key_struct(void){
-  struct csa_keys_t *keys=(struct csa_keys_t *)MALLOC(sizeof(struct csa_keys_t));
-  if(keys) {
-    static const unsigned char pk[8] = { 0,0,0,0,0,0,0,0 };
-    set_control_words(keys,pk,pk);
-    }
-  return keys;
-}
-
-void free_key_struct(void *keys){
-  return FREE(keys);
-}
 
 //-----set control words
 
@@ -523,19 +490,69 @@ static void schedule_key(struct csa_key_t *key, const unsigned char *pk){
   }
 }
 
-void set_control_words(void *keys, const unsigned char *ev, const unsigned char *od){
+extern void FUNC(set_control_words)(void *keys, const unsigned char *ev, const unsigned char *od);
+
+void FUNC(set_control_words)(void *keys, const unsigned char *ev, const unsigned char *od)
+{
   schedule_key(&((struct csa_keys_t *)keys)->even,ev);
   schedule_key(&((struct csa_keys_t *)keys)->odd,od);
 }
 
-void set_even_control_word(void *keys, const unsigned char *pk){
+extern void FUNC(set_even_control_word)(void *keys, const unsigned char *pk);
+
+void FUNC(set_even_control_word)(void *keys, const unsigned char *pk)
+{
   schedule_key(&((struct csa_keys_t *)keys)->even,pk);
 }
 
-void set_odd_control_word(void *keys, const unsigned char *pk){
+extern void FUNC(set_odd_control_word)(void *keys, const unsigned char *pk);
+
+void FUNC(set_odd_control_word)(void *keys, const unsigned char *pk){
   schedule_key(&((struct csa_keys_t *)keys)->odd,pk);
 }
 
+//-----get internal parallelism
+
+extern int FUNC(get_internal_parallelism)(void);
+
+int FUNC(get_internal_parallelism)(void)
+{
+  return GROUP_PARALLELISM;
+}
+
+//-----get suggested cluster size
+
+extern int FUNC(get_suggested_cluster_size)(void);
+
+int FUNC(get_suggested_cluster_size)(void)
+{
+  int r;
+  r=GROUP_PARALLELISM+GROUP_PARALLELISM/10;
+  if(r<GROUP_PARALLELISM+5) r=GROUP_PARALLELISM+5;
+  return r;
+}
+
+//-----key structure
+
+extern void *FUNC(get_key_struct)(void);
+void *FUNC(get_key_struct)(void)
+{
+  struct csa_keys_t *keys=(struct csa_keys_t *)MALLOC(sizeof(struct csa_keys_t));
+  if(keys) {
+    static const unsigned char pk[8] = { 0,0,0,0,0,0,0,0 };
+    FUNC(set_control_words)(keys,pk,pk);
+    }
+  return keys;
+}
+
+extern void FUNC(free_key_struct)(void *keys);
+void FUNC(free_key_struct)(void *keys)
+{
+  return FREE(keys);
+}
+
+
+
 //-----get control words
 #if 0
 void get_control_words(void *keys, unsigned char *even, unsigned char *odd){
@@ -546,7 +563,9 @@ void get_control_words(void *keys, unsigned char *even, unsigned char *odd){
 
 //----- decrypt
 
-int decrypt_packets(void *keys, unsigned char **cluster){
+extern int FUNC(decrypt_packets)(void *keys, unsigned char **cluster);
+int FUNC(decrypt_packets)(void *keys, unsigned char **cluster)
+{
   // statistics, currently unused
   int stat_no_scramble=0;
   int stat_reserved=0;
index 1be08e76bf24dc923409fa9bb9b4df4460cb9cba..78df91fed614cb48aa8f795a9de2da5bfb54b100 100644 (file)
@@ -59,4 +59,6 @@ void set_odd_control_word(void *keys, const unsigned char *odd);
 // Please read doc/how_to_use.txt.
 int decrypt_packets(void *keys, unsigned char **cluster);
 
+void ffdecsa_init(void);
+
 #endif
diff --git a/src/ffdecsa/ffdecsa_int.c b/src/ffdecsa/ffdecsa_int.c
new file mode 100644 (file)
index 0000000..8a2b01c
--- /dev/null
@@ -0,0 +1,2 @@
+#define PARALLEL_MODE PARALLEL_32_INT
+#include "FFdecsa.c"
diff --git a/src/ffdecsa/ffdecsa_interface.c b/src/ffdecsa/ffdecsa_interface.c
new file mode 100644 (file)
index 0000000..e600785
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * CPU detection code, extracted from mmx.h
+ * (c)1997-99 by H. Dietz and R. Fisher
+ * Converted to C and improved by Fabrice Bellard.
+ *
+ * This file is part of Tvheadend.
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvhead.h"
+#include "FFdecsa.h"
+
+
+
+typedef struct {
+  int (*get_internal_parallelism)(void);
+  int (*get_suggested_cluster_size)(void);
+  void *(*get_key_struct)(void);
+  void (*free_key_struct)(void *keys);
+  void (*set_control_words)(void *keys, const unsigned char *even, const unsigned char *odd);
+
+  void (*set_even_control_word)(void *keys, const unsigned char *even);
+  void (*set_odd_control_word)(void *keys, const unsigned char *odd);
+  int (*decrypt_packets)(void *keys, unsigned char **cluster);
+
+} csafuncs_t;
+
+
+#define MAKEFUNCS(x) \
+extern int get_internal_parallelism_##x(void);\
+extern int get_suggested_cluster_size_##x(void);\
+extern void *get_key_struct_##x(void);\
+extern void free_key_struct_##x(void *keys);\
+extern void set_control_words_##x(void *keys, const unsigned char *even, const unsigned char *odd);\
+extern void set_even_control_word_##x(void *keys, const unsigned char *even);\
+extern void set_odd_control_word_##x(void *keys, const unsigned char *odd);\
+extern int decrypt_packets_##x(void *keys, unsigned char **cluster);\
+static csafuncs_t funcs_##x = { \
+  &get_internal_parallelism_##x,\
+  &get_suggested_cluster_size_##x,\
+  &get_key_struct_##x,\
+  &free_key_struct_##x,\
+  &set_control_words_##x,\
+  &set_even_control_word_##x,\
+  &set_odd_control_word_##x,\
+  &decrypt_packets_##x\
+};
+
+MAKEFUNCS(32int);
+#if defined(__i386__) || defined(__x86_64__)
+MAKEFUNCS(64mmx);
+MAKEFUNCS(128sse2);
+#endif
+
+static csafuncs_t current;
+
+
+
+
+#if defined(__x86_64__)
+#    define REG_a "rax"
+#    define REG_b "rbx"
+#    define REG_c "rcx"
+#    define REG_d "rdx"
+#    define REG_D "rdi"
+#    define REG_S "rsi"
+#    define PTR_SIZE "8"
+typedef int64_t x86_reg;
+
+#    define REG_SP "rsp"
+#    define REG_BP "rbp"
+#    define REGBP   rbp
+#    define REGa    rax
+#    define REGb    rbx
+#    define REGc    rcx
+#    define REGd    rdx
+#    define REGSP   rsp
+
+#elif defined(__i386__)
+
+#    define REG_a "eax"
+#    define REG_b "ebx"
+#    define REG_c "ecx"
+#    define REG_d "edx"
+#    define REG_D "edi"
+#    define REG_S "esi"
+#    define PTR_SIZE "4"
+typedef int32_t x86_reg;
+
+#    define REG_SP "esp"
+#    define REG_BP "ebp"
+#    define REGBP   ebp
+#    define REGa    eax
+#    define REGb    ebx
+#    define REGc    ecx
+#    define REGd    edx
+#    define REGSP   esp
+#else
+typedef int x86_reg;
+#endif
+
+
+
+/* ebx saving is necessary for PIC. gcc seems unable to see it alone */
+#define cpuid(index,eax,ebx,ecx,edx)\
+    __asm__ volatile\
+        ("mov %%"REG_b", %%"REG_S"\n\t"\
+         "cpuid\n\t"\
+         "xchg %%"REG_b", %%"REG_S\
+         : "=a" (eax), "=S" (ebx),\
+           "=c" (ecx), "=d" (edx)\
+         : "0" (index));
+
+
+
+void
+ffdecsa_init(void)
+{
+  current = funcs_32int;
+
+
+#if defined(__i386__) || defined(__x86_64__)
+
+  int eax, ebx, ecx, edx;
+  int max_std_level, std_caps=0;
+  
+#if defined(__i386__)
+
+  x86_reg a, c;
+  __asm__ volatile (
+                   /* See if CPUID instruction is supported ... */
+                   /* ... Get copies of EFLAGS into eax and ecx */
+                   "pushfl\n\t"
+                   "pop %0\n\t"
+                   "mov %0, %1\n\t"
+
+                   /* ... Toggle the ID bit in one copy and store */
+                   /*     to the EFLAGS reg */
+                   "xor $0x200000, %0\n\t"
+                   "push %0\n\t"
+                   "popfl\n\t"
+
+                   /* ... Get the (hopefully modified) EFLAGS */
+                   "pushfl\n\t"
+                   "pop %0\n\t"
+                   : "=a" (a), "=c" (c)
+                   :
+                   : "cc"
+                   );
+
+  if (a != c) {
+#endif
+    cpuid(0, max_std_level, ebx, ecx, edx);
+
+    if(max_std_level >= 1){
+      cpuid(1, eax, ebx, ecx, std_caps);
+
+      if (std_caps & (1<<26)) {
+       current = funcs_128sse2;
+       tvhlog(LOG_INFO, "CSA", "Using SSE2 128bit parallel descrambling");
+       return;
+      }
+
+      if (std_caps & (1<<23)) {
+       current = funcs_64mmx;
+       tvhlog(LOG_INFO, "CSA", "Using MMX 64bit parallel descrambling");
+       return;
+      }
+    }
+#if defined(__i386__)
+  }
+#endif
+#endif
+
+  tvhlog(LOG_INFO, "CSA", "Using 32bit parallel descrambling");
+}
+
+
+int
+get_internal_parallelism(void)
+{
+  return current.get_internal_parallelism();
+}
+int
+get_suggested_cluster_size(void)
+{
+  return current.get_suggested_cluster_size();
+}
+
+void *
+get_key_struct(void)
+{
+  return current.get_key_struct();
+}
+void
+free_key_struct(void *keys)
+{
+  current.free_key_struct(keys);
+}
+
+void
+set_even_control_word(void *keys, const unsigned char *even)
+{
+  current.set_even_control_word(keys, even);
+}
+
+void
+set_odd_control_word(void *keys, const unsigned char *odd)
+{
+  current.set_odd_control_word(keys, odd);
+}
+
+int
+decrypt_packets(void *keys, unsigned char **cluster)
+{
+  return current.decrypt_packets(keys, cluster);
+}
diff --git a/src/ffdecsa/ffdecsa_mmx.c b/src/ffdecsa/ffdecsa_mmx.c
new file mode 100644 (file)
index 0000000..71cdcc9
--- /dev/null
@@ -0,0 +1,4 @@
+#if defined(__i386__) || defined(__x86_64__)
+#define PARALLEL_MODE PARALLEL_64_MMX
+#include "FFdecsa.c"
+#endif
diff --git a/src/ffdecsa/ffdecsa_sse2.c b/src/ffdecsa/ffdecsa_sse2.c
new file mode 100644 (file)
index 0000000..af5868a
--- /dev/null
@@ -0,0 +1,4 @@
+#if defined(__i386__) || defined(__x86_64__)
+#define PARALLEL_MODE PARALLEL_128_SSE2
+#include "FFdecsa.c"
+#endif
index a380431fee4abedce0a5b0d89ea1cc0ed415415d..9c471bcc732ddf1fb38ba066165168920cfbe0a5 100644 (file)
@@ -58,6 +58,7 @@
 #include "v4l.h"
 #include "parachute.h"
 #include "settings.h"
+#include "ffdecsa/FFdecsa.h"
 
 int running;
 extern const char *htsversion;
@@ -386,6 +387,8 @@ main(int argc, char **argv)
   dvr_init();
 
   htsp_init();
+
+  ffdecsa_init();
   
   if(rawts_input != NULL)
     rawts_init(rawts_input);