diff --git a/src/capmt.c b/src/capmt.c
new file mode 100644
index 00000000..c7d686bb
--- /dev/null
+++ b/src/capmt.c
@@ -0,0 +1,865 @@
+/*
+ * tvheadend, CAPMT Server
+ * Copyright (C) 2009
+ *
+ * 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tvhead.h"
+#include "tcp.h"
+#include "psi.h"
+#include "tsdemux.h"
+#include "ffdecsa/FFdecsa.h"
+#include "transports.h"
+#include "capmt.h"
+#include "notify.h"
+
+#include "dtable.h"
+
+// ca_pmt_list_management values:
+#define CAPMT_LIST_MORE 0x00 // append a 'MORE' CAPMT object the list and start receiving the next object
+#define CAPMT_LIST_FIRST 0x01 // clear the list when a 'FIRST' CAPMT object is received, and start receiving the next object
+#define CAPMT_LIST_LAST 0x02 // append a 'LAST' CAPMT object to the list and start working with the list
+#define CAPMT_LIST_ONLY 0x03 // clear the list when an 'ONLY' CAPMT object is received, and start working with the object
+#define CAPMT_LIST_ADD 0x04 // append an 'ADD' CAPMT object to the current list and start working with the updated list
+#define CAPMT_LIST_UPDATE 0x05 // replace an entry in the list with an 'UPDATE' CAPMT object, and start working with the updated list
+
+//ca_pmt_cmd_id values:
+#define CAPMT_CMD_OK_DESCRAMBLING 0x01 // start descrambling the service in this CAPMT object as soon as the list of CAPMT objects is complete
+#define CAPMT_CMD_OK_MMI 0x02 //
+#define CAPMT_CMD_QUERY 0x03 //
+#define CAPMT_CMD_NOT_SELECTED 0x04
+
+#define CW_DUMP(buf, len, format, ...) \
+ printf(format, __VA_ARGS__); int j; for (j = 0; j < len; ++j) printf("%02X ", buf[j]); printf("\n");
+
+/**
+ *
+ */
+TAILQ_HEAD(capmt_queue, capmt);
+LIST_HEAD(capmt_transport_list, capmt_transport);
+LIST_HEAD(capmt_caid_ecm_list, capmt_caid_ecm);
+static struct capmt_queue capmts;
+static pthread_cond_t capmt_config_changed;
+
+/**
+ * capmt descriptor
+ */
+typedef struct capmt_descriptor {
+ uint8_t cad_type;
+ uint8_t cad_length;
+ uint8_t cad_data[16];
+} __attribute__((packed)) capmt_descriptor_t;
+
+/**
+ * capmt header structure
+ */
+typedef struct capmt_header {
+ uint8_t capmt_indicator[6];
+ uint8_t capmt_list_management;
+ uint16_t program_number;
+ unsigned reserved1 : 2;
+ unsigned version_number : 5;
+ unsigned current_next_indicator : 1;
+ unsigned reserved2 : 4;
+ unsigned program_info_length : 12;
+ uint8_t capmt_cmd_id;
+} __attribute__((packed)) capmt_header_t;
+
+/**
+ * caid <-> ecm mapping
+ */
+typedef struct capmt_caid_ecm {
+ /** ca system id */
+ uint16_t cce_caid;
+ /** ecm pid */
+ uint16_t cce_ecmpid;
+ /** last ecm size */
+ uint32_t cce_ecmsize;
+ /** last ecm buffer */
+ uint8_t cce_ecm[256];
+
+ LIST_ENTRY(capmt_caid_ecm) cce_link;
+} capmt_caid_ecm_t;
+
+/**
+ *
+ */
+typedef struct capmt_transport {
+ th_descrambler_t ct_head;
+
+ th_transport_t *ct_transport;
+
+ struct capmt *ct_capmt;
+
+ LIST_ENTRY(capmt_transport) ct_link;
+
+ /**
+ * Sequence number generated on write (based on capmt_seq),
+ * used to pair reply message (i.e when i CT_STATE_WAIT_REPLY)
+ * with capmt_transport
+ */
+ uint16_t ct_seq;
+
+ /**
+ * list of used ca-systems with ids and last ecm
+ */
+ struct capmt_caid_ecm_list ct_caid_ecm;
+
+ /**
+ * Status of the key(s) in ct_keys
+ */
+ enum {
+ CT_UNKNOWN,
+ CT_RESOLVED,
+ CT_FORBIDDEN
+ } ct_keystate;
+
+ void *ct_keys;
+
+ /**
+ * CSA
+ */
+ int ct_cluster_size;
+ uint8_t *ct_tsbcluster;
+ int ct_fill;
+
+} capmt_transport_t;
+
+
+/**
+ *
+ */
+typedef struct capmt {
+ int capmt_fd;
+
+ int capmt_sock_ca0;
+ int capmt_sock;
+
+ int capmt_retry_delay;
+
+ pthread_mutex_t capmt_send_mutex;
+
+ pthread_cond_t capmt_cond;
+
+ pthread_cond_t capmt_writer_cond; /* Used to wakeup writer */
+ int capmt_writer_running;
+
+
+ TAILQ_ENTRY(capmt) capmt_link; /* Linkage protected via global_lock */
+
+ struct capmt_transport_list capmt_transports;
+
+ uint16_t capmt_caid;
+
+ uint16_t capmt_seq;
+
+ uint8_t capmt_key[16];
+
+ uint8_t capmt_buf[256];
+ int capmt_bufptr;
+
+ /* Provder IDs */
+
+// uint32_t capmt_provider_ids[256];
+// int capmt_num_providers;
+
+ /* From configuration */
+
+// uint8_t capmt_confedkey[14];
+// char *capmt_username;
+// char *capmt_password;
+// char *capmt_password_salted; /* salted version */
+// char *capmt_comment;
+ char *capmt_sockfile;
+ char *capmt_hostname;
+ int capmt_port;
+ char *capmt_id;
+
+// const char *capmt_errtxt;
+
+ int capmt_enabled;
+ int capmt_running;
+ int capmt_reconfigure;
+
+ capmt_transport_t *ct;
+} capmt_t;
+
+/**
+ *
+ */
+static int
+capmt_send_msg(capmt_t *capmt, const uint8_t *buf, size_t len)
+{
+ int n;
+
+ pthread_mutex_lock(&capmt->capmt_send_mutex);
+ n = write(capmt->capmt_sock, buf, len);
+ pthread_mutex_unlock(&capmt->capmt_send_mutex);
+
+ return n;
+}
+
+/**
+ * global_lock is held
+ * tht_stream_mutex is held
+ */
+static void
+capmt_transport_destroy(th_descrambler_t *td)
+{
+ tvhlog(LOG_INFO, "capmt", "Removing CAPMT Server from service");
+
+ capmt_transport_t *ct = (capmt_transport_t *)td;
+
+ capmt_t *capmt;
+ TAILQ_FOREACH(capmt, &capmts, capmt_link)
+ if (capmt->ct == ct)
+ capmt->ct = NULL;
+
+ capmt_caid_ecm_t *cce;
+ while (!LIST_EMPTY(&ct->ct_caid_ecm)) { /* List Deletion. */
+ cce = LIST_FIRST(&ct->ct_caid_ecm);
+ LIST_REMOVE(cce, cce_link);
+ free(cce);
+ }
+
+ LIST_REMOVE(td, td_transport_link);
+
+ LIST_REMOVE(ct, ct_link);
+
+ free_key_struct(ct->ct_keys);
+ free(ct->ct_tsbcluster);
+ free(ct);
+}
+
+/**
+ *
+ */
+/*
+static void *
+capmt_thread(void *aux)
+{
+ capmt_transport_t *ct;
+ capmt_t *capmt = aux;
+ int fd, d;
+ char errbuf[100];
+ th_transport_t *t;
+ char hostname[256];
+ int port;
+ struct timespec ts;
+ int attempts = 0;
+
+ pthread_mutex_lock(&global_lock);
+
+ while(capmt->capmt_running) {
+
+ while(capmt->capmt_running && capmt->capmt_enabled == 0)
+ pthread_cond_wait(&capmt->capmt_cond, &global_lock);
+
+ snprintf(hostname, sizeof(hostname), "%s", capmt->capmt_hostname);
+ port = capmt->capmt_port;
+
+ tvhlog(LOG_INFO, "capmt", "Attemping to connect to %s:%d", hostname, port);
+
+ pthread_mutex_unlock(&global_lock);
+
+ fd = tcp_connect(hostname, port, errbuf, sizeof(errbuf), 10);
+
+ pthread_mutex_lock(&global_lock);
+
+ if(fd == -1) {
+ attempts++;
+ tvhlog(LOG_INFO, "capmt",
+ "Connection attempt to %s:%d failed: %s",
+ hostname, port, errbuf);
+ } else {
+
+ if(capmt->capmt_running == 0) {
+ close(fd);
+ break;
+ }
+
+ tvhlog(LOG_INFO, "capmt", "Connected to %s:%d", hostname, port);
+ attempts = 0;
+
+ capmt->capmt_fd = fd;
+ capmt->capmt_reconfigure = 0;
+
+ //capmt_session(capmt);
+
+ capmt->capmt_fd = -1;
+ close(fd);
+ capmt->capmt_caid = 0;
+
+ tvhlog(LOG_INFO, "capmt", "Disconnected from %s", capmt->capmt_hostname);
+ }
+
+ if(subscriptions_active()) {
+ if(attempts == 1)
+ continue; // Retry immediately
+ d = 3;
+ } else {
+ d = 60;
+ }
+
+ ts.tv_sec = time(NULL) + d;
+ ts.tv_nsec = 0;
+
+ tvhlog(LOG_INFO, "capmt",
+ "%s: Automatic connection attempt in in %d seconds",
+ capmt->capmt_hostname, d);
+
+ pthread_cond_timedwait(&capmt_config_changed, &global_lock, &ts);
+ }
+
+ tvhlog(LOG_INFO, "capmt", "%s destroyed", capmt->capmt_hostname);
+
+ while((ct = LIST_FIRST(&capmt->capmt_transports)) != NULL) {
+ t = ct->ct_transport;
+ pthread_mutex_lock(&t->tht_stream_mutex);
+ capmt_transport_destroy(&ct->ct_head);
+ pthread_mutex_unlock(&t->tht_stream_mutex);
+ }
+
+ free((void *)capmt->capmt_password);
+ free((void *)capmt->capmt_password_salted);
+ free((void *)capmt->capmt_username);
+ free((void *)capmt->capmt_hostname);
+ free(capmt);
+
+ pthread_mutex_unlock(&global_lock);
+ return NULL;
+}
+*/
+
+/**
+ *
+ */
+static void
+capmt_table_input(struct th_descrambler *td, struct th_transport *t,
+ struct th_stream *st, uint8_t *data, int len)
+{
+ capmt_transport_t *ct = (capmt_transport_t *)td;
+ capmt_t *capmt = ct->ct_capmt;
+
+ switch(data[0]) {
+ case 0x80:
+ case 0x81:
+ {
+ /* ECM */
+ uint16_t caid = st->st_caid;
+
+ capmt_caid_ecm_t *cce, *cce2;
+ LIST_FOREACH(cce, &ct->ct_caid_ecm, cce_link)
+ if (cce->cce_caid == caid)
+ break;
+
+ if (cce == NULL) {
+ cce = calloc(1, sizeof(capmt_caid_ecm_t));
+ cce->cce_caid = caid;
+ cce->cce_ecmpid = st->st_pid;
+ LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link);
+ }
+
+ if ((cce->cce_ecmsize == len) && !memcmp(cce->cce_ecm, data, len))
+ break; /* key already sent */
+
+ if(capmt->capmt_sock == -1) {
+ // New key, but we are not connected (anymore), can not descramble
+ ct->ct_keystate = CT_UNKNOWN;
+ break;
+ }
+
+ uint16_t sid = t->tht_dvb_service_id;
+ uint16_t ecmpid = st->st_pid;
+ uint16_t transponder = 0;
+
+ cce2 = LIST_FIRST(&ct->ct_caid_ecm);
+ if (caid != cce2->cce_caid)
+ return;
+
+ static uint8_t pmtversion = 1;
+
+ /* buffer for capmt */
+ int pos = 0;
+ uint8_t buf[4094];
+
+ capmt_header_t head = {
+ .capmt_indicator = { 0x9F, 0x80, 0x32, 0x82, 0x00, 0x00 },
+ .capmt_list_management = CAPMT_LIST_ONLY,
+ .program_number = sid,
+ .version_number = 0,
+ .current_next_indicator = 0,
+ .program_info_length = 0,
+ .capmt_cmd_id = CAPMT_CMD_OK_DESCRAMBLING,
+ };
+ memcpy(&buf[pos], &head, sizeof(head));
+ pos += sizeof(head);
+
+ capmt_descriptor_t prd = {
+ .cad_type = 0x81,
+ .cad_length = 0x08,
+ .cad_data = { 0x00, 0x00, 0x00, 0x00,
+ sid >> 8, sid & 0xFF,
+ transponder >> 8, transponder & 0xFF
+ }};
+ memcpy(&buf[pos], &prd, prd.cad_length + 2);
+ pos += prd.cad_length + 2;
+
+ capmt_descriptor_t dmd = {
+ .cad_type = 0x82,
+ .cad_length = 0x02,
+ .cad_data = { 0x01, 0x00 }};
+ memcpy(&buf[pos], &dmd, dmd.cad_length + 2);
+ pos += dmd.cad_length + 2;
+
+ capmt_descriptor_t ecd = {
+ .cad_type = 0x84,
+ .cad_length = 0x02,
+ .cad_data = {
+ ecmpid >> 8, ecmpid & 0xFF }};
+ memcpy(&buf[pos], &ecd, ecd.cad_length + 2);
+ pos += ecd.cad_length + 2;
+
+ LIST_FOREACH(cce2, &ct->ct_caid_ecm, cce_link) {
+ capmt_descriptor_t cad = {
+ .cad_type = 0x09,
+ .cad_length = 0x04,
+ .cad_data = {
+ cce2->cce_caid >> 8, cce2->cce_caid & 0xFF,
+ cce2->cce_ecmpid >> 8 | 0xE0, cce2->cce_ecmpid & 0xFF}};
+ memcpy(&buf[pos], &cad, cad.cad_length + 2);
+ pos += cad.cad_length + 2;
+ }
+
+ uint8_t end[] = {
+ 0x01, 0x0F, 0x00, 0x00, 0x06 };
+ memcpy(&buf[pos], end, sizeof(end));
+ pos += sizeof(end);
+ buf[10] = ((pos - 5 - 12) & 0xF00) >> 8;
+ buf[11] = ((pos - 5 - 12) & 0xFF);
+ buf[4] = ((pos - 6) >> 8);
+ buf[5] = ((pos - 6) & 0xFF);
+
+
+ buf[7] = sid >> 8;
+ buf[8] = sid & 0xFF;
+
+ memcpy(cce->cce_ecm, data, len);
+ cce->cce_ecmsize = len;
+
+ if(ct->ct_keystate != CT_RESOLVED)
+ tvhlog(LOG_INFO, "capmt",
+ "Trying to obtain key for service \"%s\"",t->tht_svcname);
+ //printf("sending capmt\n");
+
+ buf[9] = pmtversion;
+ pmtversion = (pmtversion + 1) & 0x1F;
+
+ /*int j, l;
+ for(j = 0; j < pos;) {
+ printf("%04x:",j);
+ for( l=0 ; l<16 && jct = ct;
+
+ ct->ct_seq = capmt_send_msg(capmt, buf, pos);
+ break;
+ }
+ default:
+ /* EMM */
+ break;
+ }
+}
+
+
+/**
+ *
+ */
+static int
+capmt_descramble(th_descrambler_t *td, th_transport_t *t, struct th_stream *st, uint8_t *tsb)
+{
+ capmt_transport_t *ct = (capmt_transport_t *)td;
+ int r, i;
+ unsigned char *vec[3];
+ uint8_t *t0;
+
+ if(ct->ct_keystate == CT_FORBIDDEN)
+ return 1;
+
+ if(ct->ct_keystate != CT_RESOLVED)
+ return -1;
+
+ memcpy(ct->ct_tsbcluster + ct->ct_fill * 188, tsb, 188);
+ ct->ct_fill++;
+
+ if(ct->ct_fill != ct->ct_cluster_size)
+ return 0;
+
+ ct->ct_fill = 0;
+
+ vec[0] = ct->ct_tsbcluster;
+ vec[1] = ct->ct_tsbcluster + ct->ct_cluster_size * 188;
+ vec[2] = NULL;
+
+ while(1) {
+ t0 = vec[0];
+ r = decrypt_packets(ct->ct_keys, vec);
+ if(r == 0)
+ break;
+ for(i = 0; i < r; i++) {
+ ts_recv_packet2(t, t0);
+ t0 += 188;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Check if our CAID's matches, and if so, link
+ *
+ * global_lock is held
+ */
+void
+capmt_transport_start(th_transport_t *t)
+{
+ capmt_t *capmt;
+ capmt_transport_t *ct;
+ th_descrambler_t *td;
+
+ lock_assert(&global_lock);
+
+ TAILQ_FOREACH(capmt, &capmts, capmt_link) {
+ tvhlog(LOG_INFO, "capmt",
+ "Starting capmt server for service \"%s\"", t->tht_svcname);
+
+ ct = calloc(1, sizeof(capmt_transport_t));
+ ct->ct_cluster_size = get_suggested_cluster_size();
+ ct->ct_tsbcluster = malloc(ct->ct_cluster_size * 188);
+
+ ct->ct_keys = get_key_struct();
+ ct->ct_capmt = capmt;
+ ct->ct_transport = t;
+
+ td = &ct->ct_head;
+ td->td_stop = capmt_transport_destroy;
+ td->td_table = capmt_table_input;
+ td->td_descramble = capmt_descramble;
+ LIST_INSERT_HEAD(&t->tht_descramblers, td, td_transport_link);
+
+ LIST_INSERT_HEAD(&capmt->capmt_transports, ct, ct_link);
+ }
+}
+
+
+/**
+ *
+ */
+static void
+capmt_destroy(capmt_t *capmt)
+{
+ lock_assert(&global_lock);
+ TAILQ_REMOVE(&capmts, capmt, capmt_link);
+ capmt->capmt_running = 0;
+ pthread_cond_signal(&capmt->capmt_cond);
+}
+
+static void* ca0Thread(void* pArg) {
+ capmt_t *capmt = (capmt_t*)pArg;
+ capmt_transport_t *ct;
+ th_transport_t *t;
+ int ret;
+
+ uint8_t invalid[8], buffer[20];
+ memset(invalid, 0, 8);
+
+ while (capmt->capmt_running) {
+ ret = recv(capmt->capmt_sock_ca0, buffer, 18, MSG_WAITALL);
+
+ ct = capmt->ct;
+
+ if (ct == NULL)
+ continue;
+
+ t = ct->ct_transport;
+
+ if (ret < 0) {
+ tvhlog(LOG_ERR, "capmt", "error receiving over socket");
+ // TODO reaction
+ } else if (ret == 0) {
+ // normal socket shutdown
+ tvhlog(LOG_INFO, "capmt", "normal socket shutdown");
+ break;
+ } else if(ret < 18) {
+ if(ct->ct_keystate != CT_FORBIDDEN) {
+ tvhlog(LOG_ERR, "capmt",
+ "Can not descramble service \"%s\", access denied", t->tht_svcname);
+
+ ct->ct_keystate = CT_FORBIDDEN;
+ }
+
+ continue;
+ }
+
+ /* get control words */
+ uint8_t *even = &buffer[2], *odd = &buffer[10];
+
+ if (memcmp(even, invalid, 8))
+ set_even_control_word(ct->ct_keys, even);
+ if (memcmp(odd, invalid, 8))
+ set_odd_control_word(ct->ct_keys, odd);
+
+ if(ct->ct_keystate != CT_RESOLVED)
+ tvhlog(LOG_INFO, "capmt",
+ "Obtained key for service \"%s\"",t->tht_svcname);
+
+ ct->ct_keystate = CT_RESOLVED;
+ }
+
+ return NULL;
+}
+
+/**
+ *
+ */
+static capmt_t *
+capmt_entry_find(const char *id, int create)
+{
+ char buf[20];
+ capmt_t *capmt;
+ static int tally;
+
+ if(id != NULL) {
+ TAILQ_FOREACH(capmt, &capmts, capmt_link)
+ if(!strcmp(capmt->capmt_id, id))
+ return capmt;
+ }
+ if(create == 0)
+ return NULL;
+
+ if(id == NULL) {
+ tally++;
+ snprintf(buf, sizeof(buf), "%d", tally);
+ id = buf;
+ } else {
+ tally = MAX(atoi(id), tally);
+ }
+
+ capmt = calloc(1, sizeof(capmt_t));
+ pthread_cond_init(&capmt->capmt_cond, NULL);
+ pthread_mutex_init(&capmt->capmt_send_mutex, NULL);
+ capmt->capmt_id = strdup(id);
+ capmt->capmt_running = 1;
+
+ /* open connection to camd.socket */
+ capmt->capmt_sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ struct sockaddr_un serv_addr_un;
+ memset(&serv_addr_un, 0, sizeof(serv_addr_un));
+ serv_addr_un.sun_family = AF_LOCAL;
+ snprintf(serv_addr_un.sun_path, sizeof(serv_addr_un.sun_path), "/tmp/camd.socket");
+
+ if (connect(capmt->capmt_sock, (const struct sockaddr*)&serv_addr_un, sizeof(serv_addr_un)) != 0)
+ perror("[CWCServer] ERROR connecting to camd.socket");
+
+ /* open connection to emulated ca0 device */
+ capmt->capmt_sock_ca0 = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ struct sockaddr_in serv_addr;
+ serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ serv_addr.sin_port = htons( (unsigned short int)9000);
+ serv_addr.sin_family = AF_INET;
+
+ if (bind(capmt->capmt_sock_ca0, (const struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0)
+ perror("[CWCServer] ERROR binding to ca0");
+
+ pthread_t tCa0;
+ pthread_create(&tCa0, NULL, ca0Thread, (void*)capmt);
+
+ TAILQ_INSERT_TAIL(&capmts, capmt, capmt_link);
+
+ return capmt;
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+capmt_record_build(capmt_t *capmt)
+{
+ htsmsg_t *e = htsmsg_create_map();
+
+ htsmsg_add_str(e, "id", capmt->capmt_id);
+
+ htsmsg_add_str(e, "sockfile", capmt->capmt_sockfile ?: "");
+ htsmsg_add_str(e, "hostname", capmt->capmt_hostname ?: "");
+ htsmsg_add_u32(e, "port", capmt->capmt_port);
+
+ return e;
+}
+
+/**
+ *
+ */
+static htsmsg_t *
+capmt_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
+{
+ printf("capmt entry update\n");
+ capmt_t *capmt;
+ const char *s;
+ uint32_t u32;
+
+ if((capmt = capmt_entry_find(id, maycreate)) == NULL)
+ return NULL;
+
+ lock_assert(&global_lock);
+
+ if((s = htsmsg_get_str(values, "sockfile")) != NULL) {
+ free(capmt->capmt_sockfile);
+ capmt->capmt_sockfile = strdup(s);
+ }
+
+ if((s = htsmsg_get_str(values, "hostname")) != NULL) {
+ free(capmt->capmt_hostname);
+ capmt->capmt_hostname = strdup(s);
+ }
+
+ if(!htsmsg_get_u32(values, "port", &u32))
+ capmt->capmt_port = u32;
+/*
+ capmt->capmt_reconfigure = 1;
+
+ if(capmt->capmt_fd != -1)
+ shutdown(capmt->capmt_fd, SHUT_RDWR);
+
+ pthread_cond_signal(&capmt->capmt_cond);
+
+ pthread_cond_broadcast(&capmt_config_changed);*/
+
+ return capmt_record_build(capmt);
+}
+
+
+
+/**
+ *
+ */
+static int
+capmt_entry_delete(void *opaque, const char *id)
+{
+ capmt_t *capmt;
+
+ pthread_cond_broadcast(&capmt_config_changed);
+
+ if((capmt = capmt_entry_find(id, 0)) == NULL)
+ return -1;
+ capmt_destroy(capmt);
+ return 0;
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+capmt_entry_get_all(void *opaque)
+{
+ htsmsg_t *r = htsmsg_create_list();
+ capmt_t *capmt;
+
+ TAILQ_FOREACH(capmt, &capmts, capmt_link)
+ htsmsg_add_msg(r, NULL, capmt_record_build(capmt));
+
+ return r;
+}
+
+/**
+ *
+ */
+static htsmsg_t *
+capmt_entry_get(void *opaque, const char *id)
+{
+ capmt_t *capmt;
+
+
+ if((capmt = capmt_entry_find(id, 0)) == NULL)
+ return NULL;
+ return capmt_record_build(capmt);
+}
+
+/**
+ *
+ */
+static htsmsg_t *
+capmt_entry_create(void *opaque)
+{
+ pthread_cond_broadcast(&capmt_config_changed);
+ return capmt_record_build(capmt_entry_find(NULL, 1));
+}
+
+/**
+ *
+ */
+static const dtable_class_t capmt_dtc = {
+ .dtc_record_get = capmt_entry_get,
+ .dtc_record_get_all = capmt_entry_get_all,
+ .dtc_record_create = capmt_entry_create,
+ .dtc_record_update = capmt_entry_update,
+ .dtc_record_delete = capmt_entry_delete,
+ .dtc_read_access = ACCESS_ADMIN,
+ .dtc_write_access = ACCESS_ADMIN,
+};
+
+/**
+ *
+ */
+void
+capmt_init(void)
+{
+ dtable_t *dt;
+
+ TAILQ_INIT(&capmts);
+
+ pthread_cond_init(&capmt_config_changed, NULL);
+
+ dt = dtable_create(&capmt_dtc, "capmt", NULL);
+ dtable_load(dt);
+
+ /* create initial entry */
+ capmt_entry_find(NULL, 1);
+}
+
diff --git a/src/capmt.h b/src/capmt.h
new file mode 100644
index 00000000..94bcf664
--- /dev/null
+++ b/src/capmt.h
@@ -0,0 +1,26 @@
+/*
+ * tvheadend, CAPMT interface
+ * Copyright (C) 2009
+ *
+ * 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 .
+ */
+
+#ifndef CAPMT_H_
+#define CAPMT_H_
+
+void capmt_init(void);
+
+void capmt_transport_start(th_transport_t *t);
+
+#endif /* CAPMT_H_ */
diff --git a/src/ffdecsa/FFdecsa.c b/src/ffdecsa/FFdecsa.c
index 317f0ed3..41ce49dd 100644
--- a/src/ffdecsa/FFdecsa.c
+++ b/src/ffdecsa/FFdecsa.c
@@ -56,7 +56,7 @@
//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
#ifndef PARALLEL_MODE
-#define PARALLEL_MODE PARALLEL_64_MMX
+#define PARALLEL_MODE PARALLEL_128_SSE2
#endif
//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
@@ -571,8 +571,8 @@ int decrypt_packets(void *keys, unsigned char **cluster){
int alive[24];
//icc craziness int pad1=0; //////////align! FIXME
unsigned char *encp[GROUP_PARALLELISM];
- unsigned char stream_in[GROUP_PARALLELISM*8];
- unsigned char stream_out[GROUP_PARALLELISM*8];
+ MEMALIGN unsigned char stream_in[GROUP_PARALLELISM*8];
+ MEMALIGN unsigned char stream_out[GROUP_PARALLELISM*8];
MEMALIGN unsigned char ib[GROUP_PARALLELISM*8];
MEMALIGN unsigned char block_out[GROUP_PARALLELISM*8];
struct stream_regs regs;
diff --git a/src/main.c b/src/main.c
index 3b0fb2cf..a380431f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -48,6 +48,7 @@
#include "subscriptions.h"
#include "serviceprobe.h"
#include "cwc.h"
+#include "capmt.h"
#include "dvr/dvr.h"
#include "htsp.h"
#include "rawtsinput.h"
@@ -378,6 +379,8 @@ main(int argc, char **argv)
cwc_init();
+ capmt_init();
+
epg_init();
dvr_init();
diff --git a/src/transports.c b/src/transports.c
index 1a1cb276..662f122d 100644
--- a/src/transports.c
+++ b/src/transports.c
@@ -41,6 +41,7 @@
#include "packet.h"
#include "channels.h"
#include "cwc.h"
+#include "capmt.h"
#include "notify.h"
#include "serviceprobe.h"
#include "atomic.h"
@@ -257,6 +258,7 @@ transport_start(th_transport_t *t, unsigned int weight, int force_start)
stream_init(st);
cwc_transport_start(t);
+ capmt_transport_start(t);
gtimer_arm(&t->tht_receive_timer, transport_data_timeout, t, 4);
t->tht_feed_status = TRANSPORT_FEED_UNKNOWN;