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;