#define MODULE_LOG_PREFIX "dvbapi"

#include "globals.h"

#ifdef HAVE_DVBAPI

#include "module-dvbapi.h"
#include "module-cacheex.h"
#include "module-dvbapi-azbox.h"
#include "module-dvbapi-mca.h"
#include "module-dvbapi-coolapi.h"
#include "module-dvbapi-stapi.h"
#include "module-dvbapi-chancache.h"
#include "module-stat.h"
#include "module-streamrelay.h"
#include "oscam-chk.h"
#include "oscam-client.h"
#include "oscam-config.h"
#include "oscam-ecm.h"
#include "oscam-emm.h"
#include "oscam-files.h"
#include "oscam-net.h"
#include "oscam-reader.h"
#include "oscam-string.h"
#include "oscam-time.h"
#include "oscam-work.h"
#include "reader-irdeto.h"
#include "cscrypt/md5.h"
extern int32_t exit_oscam;

static int32_t dvbapi_listenport_active = 0;

#if defined (__CYGWIN__)
#define F_NOTIFY 0
#define F_SETSIG 0
#define DN_MODIFY 0
#define DN_CREATE 0
#define DN_DELETE 0
#define DN_MULTISHOT 0
#endif

static const char *get_stream_type_txt(uint8_t stream_type)
{
	switch(stream_type)
	{
		case 0x00: return "invalid";
		case 0x01: return "MPEG-1 video";
		case 0x02: return "MPEG-2 video"; // MPEG-1 (constrained parameter) or MPEG-2 video
		case 0x03: return "MPEG-1 audio"; // MP1, MP2, MP3
		case 0x04: return "MPEG-2 audio"; // MP1, MP2, MP3
		case 0x05: return "MPEG-2 private sections";
		case 0x06: return "MPEG-2 PES private data"; // AC-3, Enhanced AC-3, AC-4, DTS(-HD) audio, subtitles, etc (DVB) (depends on descriptor)
		case 0x07: return "MHEG data";
		case 0x08: return "DSM-CC data";
		case 0x09: return "MPEG-2 over ATM data";
		case 0x0A: return "DSM-CC data";
		case 0x0B: return "DSM-CC data";
		case 0x0C: return "DSM-CC data";
		case 0x0D: return "DSM-CC data";
		case 0x0E: return "MPEG-2 auxiliary data";
		case 0x0F: return "MPEG-2 audio"; // AAC
		case 0x10: return "MPEG-4 video";
		case 0x11: return "MPEG-4 audio"; // AAC, HE AAC and AAC v2
		case 0x12: return "MPEG-4 PES data";
		case 0x13: return "MPEG-4 data";
		case 0x14: return "DSM-CC data";
		case 0x15: return "MPEG-7 MetaData";
		case 0x16: return "MPEG-7 MetaData";
		case 0x17: return "MPEG-7 MetaData";
		case 0x18: return "MPEG-7 MetaData";
		case 0x19: return "MPEG-7 MetaData";
		case 0x1A: return "MPEG-2 IPMP data";
		case 0x1B: return "AVC video";
		case 0x1C: return "MPEG-4 audio"; // DST, ALS, SLS
		case 0x24: return "HEVC video";
		case 0x25: return "HEVC subset video";
		case 0x2D: return "MPEG-H 3D audio"; // main stream
		case 0x2E: return "MPEG-H 3D audio"; // auxiliary stream
		case 0x42: return "Chinese video";
		case 0x7F: return "IPMP data";
		case 0x81: return "AC-3 audio (ATSC)"; // with descriptor tag 0x81
		case 0x86: return "SCTE 35 data";
		case 0x87: return "enhanced AC-3 audio (ATSC)";
		//case 0x88: return "DTS-HD audio (ATSC 2.0)"; // fixme: has ATSC 2.0 ever been used?
		//case 0x??: return "AC-4 audio (ATSC 3.0)"; // fixme: add the actual value when it gets published
		//case 0x??: return "MPEG-H 3D audio (ATSC 3.0)"; // fixme: add the actual value when it gets published
		case 0xD1: return "BBC Dirac video";
		case 0xEA: return "VC-1 video";
		default: return "user private";
	}
}

static const char *get_descriptor_tag_txt(uint8_t descriptor_tag)
{
	switch(descriptor_tag)
	{
		// Valid in all MPEG contexts:
		case 0x00: return "reserved";
		case 0x01: return "forbidden";
		case 0x02: return "video stream";
		case 0x03: return "audio stream";
		case 0x04: return "hierarchy";
		case 0x05: return "registration";
		case 0x06: return "data stream alignment";
		case 0x07: return "target background grid";
		case 0x08: return "video window";
		case 0x09: return "CA";
		case 0x0A: return "ISO 639 language";
		case 0x0B: return "system clock";
		case 0x0C: return "multiplex buffer utilization";
		case 0x0D: return "copyright";
		case 0x0E: return "maximum bitrate";
		case 0x0F: return "private data indicator";
		case 0x10: return "smoothing buffer";
		case 0x11: return "STD";
		case 0x12: return "IBP";
		case 0x13: return "DSM-CC carousel identifier";
		case 0x14: return "DSM-CC association tag";
		case 0x15: return "DSM-CC deferred association tags";
		case 0x17: return "DSM-CC NPT reference";
		case 0x18: return "DSM-CC NPT endpoint";
		case 0x19: return "DSM-CC stream mode";
		case 0x1A: return "DSM-CC stream event";
		case 0x1B: return "MPEG-4 video";
		case 0x1C: return "MPEG-4 audio";
		case 0x1D: return "IOD";
		case 0x1E: return "SL";
		case 0x1F: return "FMC";
		case 0x20: return "External ES id";
		case 0x21: return "MuxCode";
		case 0x22: return "FmxBufferSize";
		case 0x23: return "MultiplexBuffer";
		case 0x24: return "Content labeling";
		case 0x25: return "Metadata association";
		case 0x26: return "Metadata";
		case 0x27: return "Metadata STD";
		case 0x28: return "AVC video";
		case 0x29: return "MPEG-2 IPMP";
		case 0x2A: return "AVC timing and HRD";
		case 0x2B: return "MPEG-2 AAC Audio";
		case 0x2C: return "FlexMuxTiming";
		case 0x2D: return "MPEG-4 Text";
		case 0x2E: return "MPEG-4 Audio Extension";
		case 0x2F: return "Auxiliary Video Stream";
		case 0x30: return "SVC Extension";
		case 0x31: return "MVC Extension";
		case 0x32: return "J2K Video";
		case 0x33: return "MVC Operation Point";
		case 0x34: return "MPEG-2 Stereoscopic Video Format";
		case 0x35: return "Stereoscopic Program Info";
		case 0x36: return "Stereoscopic Video Info";
		case 0x37: return "Transport Profile";
		case 0x38: return "HEVC Video";
		case 0x3F: return "MPEG-2 Extension";
		// Valid in DVB context:
		case 0x45: return "VBI data";
		case 0x46: return "VBI teletext";
		case 0x51: return "mosaic";
		case 0x52: return "stream identifier";
		case 0x56: return "teletext";
		case 0x59: return "subtitling"; // with stream type 0x06
		case 0x5F: return "private data specifier";
		case 0x60: return "service move";
		case 0x65: return "scrambling";
		case 0x66: return "data broadcast id";
		case 0x6A: return "AC-3"; // with stream type 0x06
		case 0x6B: return "ancillary data";
		case 0x6F: return "application signalling";
		case 0x70: return "adaptation field data";
		case 0x74: return "related content";
		case 0x78: return "ECM repetition rate";
		case 0x7A: return "enhanced AC-3"; // with stream type 0x06
		case 0x7B: return "DTS"; // with stream type 0x06
		case 0x7C: return "AAC"; // with stream type 0x06
		case 0x7D: return "XAIT location";
		case 0x7F: return "DVB extension";
		// Valid in ATSC context:
		case 0x81: return "AC-3"; // with stream type 0x81
		case 0xCC: return "enhanced AC-3"; // with stream type 0x87
		default: return "user private";
	}
}

static const char *get_extension_descriptor_txt(uint8_t extension_tag)
{
	switch(extension_tag)
	{
		case 0x02: return "CP";
		case 0x06: return "supplementary audio";
		case 0x0E: return "DTS-HD"; // with stream type 0x06
		case 0x0F: return "DTS Neural"; // with stream type 0x06
		case 0x11: return "T2MI";
		case 0x13: return "URI linkage";
		case 0x15: return "AC-4"; // with stream type 0x06
		case 0x18: return "protection message";
		case 0x19: return "audio preselection";
		case 0x20: return "TTML subtitling"; // with stream type 0x06
		default: return "Undefined";
	}
}

void flush_read_fd(int32_t demux_id, int32_t num, int fd)
{
	if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX)
	{
		cs_log_dbg(D_DVBAPI,"Demuxer %d flushing stale input data of filter %d (fd:%d)", demux_id, num + 1, fd);

		fd_set rd;
		struct timeval t;
		char buff[100];

		t.tv_sec = 0;
		t.tv_usec = 0;

		FD_ZERO(&rd);
		FD_SET(fd, &rd);

		while(select(fd + 1, &rd, NULL, NULL, &t) > 0)
		{
			if(read(fd, buff, 100)) { ; }
		}
	}
}

static int dvbapi_ioctl(int fd, uint32_t request, ...)
{
	int ret = 0;
	va_list args;
	va_start(args, request);
	if(!(cfg.dvbapi_boxtype == BOXTYPE_SAMYGO))
	{
		void *param = va_arg(args, void *);
		ret = ioctl(fd, request, param);
	}
	else
	{
		switch(request)
		{
			case DMX_SET_FILTER:
			{
				struct dmx_sct_filter_params *sFP = va_arg(args, struct dmx_sct_filter_params *);
				//fix filter for samygo
				//note: we only have 14 available filter bytes (instead of 16) on samygo
				memmove(&sFP->filter.filter[3], &sFP->filter.filter[1], 13);
				memset(&sFP->filter.filter[1], 0, 2);
				memmove(&sFP->filter.mask[3], &sFP->filter.mask[1], 13);
				memset(&sFP->filter.mask[1], 0, 2);
				// prepare packet
				uint8_t packet[sizeof(request) + sizeof(struct dmx_sct_filter_params)];
				memcpy(&packet, &request, sizeof(request));
				memcpy(&packet[sizeof(request)], sFP, sizeof(struct dmx_sct_filter_params));
				ret = send(fd, packet, sizeof(packet), 0);
				break;
			}

			case DMX_SET_FILTER1:
			{
				cs_log("error: samygo does not support DMX_SET_FILTER1");
				ret = -1;
				break;
			}

			case DMX_STOP:
			{
				ret = send(fd, &request, sizeof(request), 0);
				if(!(ret == 1)){ ret = 1; }
				break;
			}

			case CA_GET_DESCR_INFO:
			{
				ca_descr_info_t *ca_descr_info = va_arg(args, ca_descr_info_t *);
				// preparing packet
				uint8_t packet[sizeof(request) + sizeof(ca_descr_info_t)];
				memcpy(&packet[0], &request, sizeof(request));
				memcpy(&packet[sizeof(request)], ca_descr_info, sizeof(ca_descr_info_t));
				// sending data to UDP
				ret = send(fd, &packet[0], sizeof(packet), 0);
				break;
			}

			case CA_SET_DESCR:
			{
				ca_descr_t *ca_descr = va_arg(args, ca_descr_t *);
				// preparing packet
				uint8_t packet[sizeof(request) + sizeof(ca_descr_t)];
				memcpy(&packet[0], &request, sizeof(request));
				memcpy(&packet[sizeof(request)], ca_descr, sizeof(ca_descr_t));
				// sending data to UDP
				ret = send(fd, &packet[0], sizeof(packet), 0);
				break;
			}

			case CA_SET_PID:
			{
				ca_pid_t *ca_pid2 = va_arg(args, ca_pid_t *);
				// preparing packet
				uint8_t packet[sizeof(request) + sizeof(ca_pid_t)];
				memcpy(&packet[0], &request, sizeof(request));
				memcpy(&packet[sizeof(request)], ca_pid2, sizeof(ca_pid_t));
				// sending data to UDP
				ret = send(fd, &packet[0], sizeof(packet), 0);
				break;
			}

			case CA_SET_DESCR_MODE:
			{
				cs_log("error: samygo does not support CA_SET_DESCR_MODE");
				ret = -1;
				break;
			}

			case CA_SET_DESCR_DATA:
			{
				cs_log("error: samygo does not support CA_SET_DESCR_DATA");
				ret = -1;
				break;
			}
		}

		if(ret > 0) // send() may return larger than 1
		{
			ret = 1;
		}
	}
#if defined(__powerpc__)
	// Old dm500 boxes (ppc old) are using broken kernel, se we need some fixups
	switch (request)
	{
		case DMX_STOP:
		case CA_SET_DESCR:
		case CA_SET_PID:
		ret = 1;
	}
#endif
	// FIXME: Workaround for su980 bug
	// See: https://board.streamboard.tv/?postid=533940
	if(boxtype_is("su980"))
	{
		ret = 1;
	}
	va_end(args);
	return ret;
}

// tunemm_caid_map
#define FROM_TO 0
#define TO_FROM 1

int32_t pausecam = 0, disable_pmt_files = 0;

DEMUXTYPE demux[MAX_DEMUX];
struct s_dvbapi_priority *dvbapi_priority;
struct s_client *dvbapi_client;

const char *boxdesc[] = { "none", "dreambox", "duckbox", "ufs910", "dbox2", "ipbox", "ipbox-pmt",
							"dm7000", "qboxhd", "coolstream", "neumo", "pc", "pc-nodmx", "samygo" };

// when updating devices[BOX_COUNT] make sure to update these index defines
#define BOX_INDEX_QBOXHD 0
#define BOX_INDEX_DREAMBOX_DVBAPI3 1
#define BOX_INDEX_COOLSTREAM 6

static const struct box_devices devices[BOX_COUNT] =
{
	/* QboxHD (dvb-api-3)*/     { "/tmp/virtual_adapter/", "ca%d",         "demux%d",      "/tmp/camd.socket", DVBAPI_3 },
	/* dreambox (dvb-api-3)*/   { "/dev/dvb/adapter%d/",   "ca%d",         "demux%d",      "/tmp/camd.socket", DVBAPI_3 },
	/* wetek (dvb-api-3)*/      { "/dev/dvb%d.",           "ca%d",         "demux%d",      "/tmp/camd.socket", DVBAPI_3 },
	/* dreambox (dvb-api-1)*/   { "/dev/dvb/card%d/",      "ca%d",         "demux%d",      "/tmp/camd.socket", DVBAPI_1 },
	/* neumo (dvb-api-1)*/      { "/dev/",                 "demuxapi",     "demuxapi",     "/tmp/camd.socket", DVBAPI_1 },
#ifdef WITH_STAPI5
	/* sh4      (stapi5)*/      { "/dev/stapi/",           "stpti5_ioctl", "stpti5_ioctl", "/tmp/camd.socket", STAPI    },
#else
	/* sh4      (stapi)*/       { "/dev/stapi/",           "stpti4_ioctl", "stpti4_ioctl", "/tmp/camd.socket", STAPI    },
#endif
	/* coolstream*/             { "/dev/cnxt/",            "null",         "null",         "/tmp/camd.socket", COOLAPI  },
};

static int32_t selected_box = -1;
static int32_t selected_api = -1;
static int32_t maxfilter = MAX_FILTER;
static int32_t dir_fd = -1;
static uint16_t last_client_proto_version = 0;
static char *last_client_name = NULL;
static uint32_t ca_descramblers_total = 0; // total number of available descramblers in box
static uint32_t ca_descramblers_used = 0; // total number of used descramblers during decoding
static int32_t ca_fd[CA_MAX]; // holds fd handle of all ca devices (0 not in use)
static LLIST *ll_activestreampids; // list of all enabled streampids on ca devices
static int32_t assoc_fd[MAX_ASSOC_FD]; // list of all client sockets
static uint8_t ca_soft_csa[MAX_DEMUX] = {0};

bool is_dvbapi_usr(char *usr)
{
	return streq(cfg.dvbapi_usr, usr);
}

struct s_emm_filter
{
	int32_t demux_id;
	uint8_t filter[32];
	uint16_t caid;
	uint32_t provid;
	uint16_t pid;
	uint32_t num;
	struct timeb time_started;
};

static LLIST *ll_emm_active_filter;
static LLIST *ll_emm_inactive_filter;
static LLIST *ll_emm_pending_filter;

int32_t add_emmfilter_to_list(int32_t demux_id, uint8_t *filter, uint16_t caid, uint32_t provid, uint16_t emmpid, int32_t num, bool enable)
{
	if(!ll_emm_active_filter)
	{
		ll_emm_active_filter = ll_create("ll_emm_active_filter");
	}

	if(!ll_emm_inactive_filter)
	{
		ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter");
	}

	if(!ll_emm_pending_filter)
	{
		ll_emm_pending_filter = ll_create("ll_emm_pending_filter");
	}

	struct s_emm_filter *filter_item;
	if(!cs_malloc(&filter_item, sizeof(struct s_emm_filter)))
	{
		return 0;
	}

	filter_item->demux_id = demux_id;
	memcpy(filter_item->filter, filter, 32);
	filter_item->caid = caid;
	filter_item->provid = provid;
	filter_item->pid = emmpid;
	filter_item->num = num;

	if(enable)
	{
		cs_ftime(&filter_item->time_started);
	}
	else
	{
		memset(&filter_item->time_started, 0, sizeof(filter_item->time_started));
	}

	if(num > 0)
	{
		ll_append(ll_emm_active_filter, filter_item);
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d added to active emmfilters (CAID %04X PROVID %06X EMMPID %04X)",
			filter_item->demux_id, filter_item->num, filter_item->caid, filter_item->provid, filter_item->pid);
	}
	else if(num < 0)
	{
		ll_append(ll_emm_pending_filter, filter_item);
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter added to pending emmfilters (CAID %04X PROVID %06X EMMPID %04X)",
			filter_item->demux_id, filter_item->caid, filter_item->provid, filter_item->pid);
	}
	else
	{
		ll_append(ll_emm_inactive_filter, filter_item);
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter added to inactive emmfilters (CAID %04X PROVID %06X EMMPID %04X)",
			filter_item->demux_id, filter_item->caid, filter_item->provid, filter_item->pid);
	}
	return 1;
}

int32_t is_emmfilter_in_list_internal(LLIST *ll, uint8_t *filter, uint16_t emmpid, uint32_t provid, uint16_t caid)
{
	struct s_emm_filter *filter_item;
	LL_ITER itr;
	if(ll_count(ll) > 0)
	{
		itr = ll_iter_create(ll);
		while((filter_item = ll_iter_next(&itr)) != NULL)
		{
			if(!memcmp(filter_item->filter, filter, 32) && (filter_item->pid == emmpid)
				&& (filter_item->provid == provid) && (filter_item->caid == caid))
			{
				return 1;
			}
		}
	}
	return 0;
}

int32_t is_emmfilter_in_list(uint8_t *filter, uint16_t emmpid, uint32_t provid, uint16_t caid)
{
	if(!ll_emm_active_filter)
	{
		ll_emm_active_filter = ll_create("ll_emm_active_filter");
	}

	if(!ll_emm_inactive_filter)
	{
		ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter");
	}

	if(!ll_emm_pending_filter)
	{
		ll_emm_pending_filter = ll_create("ll_emm_pending_filter");
	}

	if(is_emmfilter_in_list_internal(ll_emm_active_filter, filter, emmpid, provid, caid)
		|| is_emmfilter_in_list_internal(ll_emm_inactive_filter, filter, emmpid, provid, caid)
		|| is_emmfilter_in_list_internal(ll_emm_pending_filter, filter, emmpid, provid, caid))
	{
		return 1;
	}
	return 0;
}

struct s_emm_filter *get_emmfilter_by_filternum_internal(LLIST *ll, int32_t demux_id, uint32_t num)
{
	struct s_emm_filter *filter;
	LL_ITER itr;
	if(ll_count(ll) > 0)
	{
		itr = ll_iter_create(ll);
		while((filter = ll_iter_next(&itr)))
		{
			if(filter->demux_id == demux_id && filter->num == num)
			{
				return filter;
			}
		}
	}
	return NULL;
}

struct s_emm_filter *get_emmfilter_by_filternum(int32_t demux_id, uint32_t num)
{
	if(!ll_emm_active_filter)
	{
		ll_emm_active_filter = ll_create("ll_emm_active_filter");
	}

	if(!ll_emm_inactive_filter)
	{
		ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter");
	}

	if(!ll_emm_pending_filter)
	{
		ll_emm_pending_filter = ll_create("ll_emm_pending_filter");
	}

	struct s_emm_filter *emm_filter = NULL;

	emm_filter = get_emmfilter_by_filternum_internal(ll_emm_active_filter, demux_id, num);
	if(emm_filter)
	{
		return emm_filter;
	}

	emm_filter = get_emmfilter_by_filternum_internal(ll_emm_inactive_filter, demux_id, num);
	if(emm_filter)
	{
		return emm_filter;
	}

	emm_filter = get_emmfilter_by_filternum_internal(ll_emm_pending_filter, demux_id, num);
	if(emm_filter)
	{
		return emm_filter;
	}

	return NULL;
}

int8_t remove_emmfilter_from_list_internal(LLIST *ll, int32_t demux_id, uint16_t caid, uint32_t provid, uint16_t pid, uint32_t num)
{
	struct s_emm_filter *filter;
	LL_ITER itr;

	if(ll_count(ll) > 0)
	{
		itr = ll_iter_create(ll);
		while((filter = ll_iter_next(&itr)))
		{
			if(filter->demux_id == demux_id && filter->caid == caid && filter->provid == provid && filter->pid == pid && filter->num == num)
			{
				ll_iter_remove_data(&itr);
				return 1;
			}
		}
	}
	return 0;
}

void remove_emmfilter_from_list(int32_t demux_id, uint16_t caid, uint32_t provid, uint16_t pid, uint32_t num)
{
	if((ll_emm_active_filter && remove_emmfilter_from_list_internal(ll_emm_active_filter, demux_id, caid, provid, pid, num))
		|| (ll_emm_inactive_filter && remove_emmfilter_from_list_internal(ll_emm_inactive_filter, demux_id, caid, provid, pid, num))
		|| (ll_emm_pending_filter && remove_emmfilter_from_list_internal(ll_emm_pending_filter, demux_id, caid, provid, pid, num)))
	{
		return;
	}
}

void dvbapi_net_add_str(uint8_t *packet, int *size, const char *str)
{
	uint8_t *str_len = &packet[*size]; // string length
	*size += 1;
	*str_len = snprintf((char *) &packet[*size], DVBAPI_MAX_PACKET_SIZE - *size, "%s", str);
	*size += *str_len;
}

int32_t dvbapi_net_send(uint32_t request, int32_t socket_fd, uint32_t msgid, int32_t demux_id,
			uint32_t filter_number, uint8_t *data, struct s_client *client, ECM_REQUEST *er, uint16_t client_proto_version)
{
	uint8_t packet[DVBAPI_MAX_PACKET_SIZE]; // maximum possible packet size
	int32_t size = 0;
	uint32_t u32;

	// not connected?
	if(socket_fd <= 0)
	{
		return 0;
	}

	// preparing packet - header
	// in old protocol client expect this first byte as adapter index,
	// changed in the new protocol to be always after request type (opcode)
	if(client_proto_version <= 0)
	{
		packet[size++] = demux[demux_id].adapter_index; // adapter index - 1 byte
	}
	else if(client_proto_version >= 3)
	{
		packet[size++] = 0xa5; // message start
		u32 = htonl(msgid);
		memcpy(&packet[size], &u32, 4);
		size += 4;
	}

	// type of request
	u32 = request;
	if(client_proto_version >= 1)
	{
		u32 = htonl(u32);
	}
	memcpy(&packet[size], &u32, 4);
	size += 4;

	// preparing packet - adapter index for proto >= 1
	if((request != DVBAPI_SERVER_INFO) && client_proto_version >= 1)
	{
		packet[size++] = demux[demux_id].adapter_index; // adapter index - 1 byte
	}

	// struct with data
	switch(request)
	{
		case DVBAPI_SERVER_INFO:
		{
			int16_t proto_version = htons(DVBAPI_PROTOCOL_VERSION); // our protocol version
			char capabilities[128];
			memset(capabilities, 0, sizeof(capabilities));
			memcpy(&packet[size], &proto_version, 2);
			size += 2;
			uint8_t *info_len = &packet[size]; // info string length
			size += 1;
#ifdef WITH_EXTENDED_CW
			if(cfg.dvbapi_extended_cw_api == 1)
			{
				cs_strncat(capabilities, ",e1mk", sizeof(capabilities)); // extended cw, key follows mode - supports CSA, DES, AES128
			}

			if(cfg.dvbapi_extended_cw_api == 2)
			{
				cs_strncat(capabilities, ",e2", sizeof(capabilities)); // usage of DES algo signalled through PID index - CSA and DES only
			}
#endif
			*info_len = snprintf((char *) &packet[size], sizeof(packet) - size, "OSCam %s (%s); %s",
						CS_VERSION, CS_TARGET, capabilities + 1);

			size += *info_len;
			break;
		}

		case DVBAPI_ECM_INFO:
		{
			if(er->rc >= E_NOTFOUND)
			{
				return 0;
			}

			int8_t hops = 0;

			uint16_t sid = htons(er->srvid); // service ID (program number)
			memcpy(&packet[size], &sid, 2);
			size += 2;

			uint16_t caid = htons(er->caid); // CAID
			memcpy(&packet[size], &caid, 2);
			size += 2;

			uint16_t pid = htons(er->pid); // PID
			memcpy(&packet[size], &pid, 2);
			size += 2;

			uint32_t prid = htonl(er->prid); // Provider ID
			memcpy(&packet[size], &prid, 4);
			size += 4;

			uint32_t ecmtime = htonl(client->cwlastresptime); // ECM time
			memcpy(&packet[size], &ecmtime, 4);
			size += 4;

			dvbapi_net_add_str(packet, &size, get_cardsystem_desc_by_caid(er->caid)); // cardsystem name

			switch (er->rc)
			{
				case E_FOUND:
					if(er->selected_reader)
					{
						dvbapi_net_add_str(packet, &size, er->selected_reader->label); // reader
						if(is_network_reader(er->selected_reader))
						{
							dvbapi_net_add_str(packet, &size, er->selected_reader->device); // from
						}
						else
						{
							dvbapi_net_add_str(packet, &size, "local"); // from
						}
						dvbapi_net_add_str(packet, &size, reader_get_type_desc(er->selected_reader, 1)); // protocol
						hops = er->selected_reader->currenthops;
					}
					break;

				case E_CACHE1:
					dvbapi_net_add_str(packet, &size, "Cache");  // reader
					dvbapi_net_add_str(packet, &size, "cache1"); // from
					dvbapi_net_add_str(packet, &size, "none");   // protocol
					break;

				case E_CACHE2:
					dvbapi_net_add_str(packet, &size, "Cache");  // reader
					dvbapi_net_add_str(packet, &size, "cache2"); // from
					dvbapi_net_add_str(packet, &size, "none");   // protocol
					break;

				case E_CACHEEX:
					dvbapi_net_add_str(packet, &size, "Cache");  // reader
					dvbapi_net_add_str(packet, &size, "cache3"); // from
					dvbapi_net_add_str(packet, &size, "none");   // protocol
					break;
			}
			packet[size++] = hops; // hops
			break;
		}

		case DVBAPI_CA_GET_DESCR_INFO:
		{
			int sct_cadescr_info_size = sizeof(ca_descr_info_t);
			if(client_proto_version >= 1)
			{
				ca_descr_info_t *cadescr_info = (ca_descr_info_t *) data;
				cadescr_info->num = htonl(cadescr_info->num);
				cadescr_info->type = htonl(cadescr_info->type);
			}
			memcpy(&packet[size], data, sct_cadescr_info_size);
			size += sct_cadescr_info_size;
			break;
		}

		case DVBAPI_CA_SET_DESCR:
		{
			int sct_cadescr_size = sizeof(ca_descr_t);
			if(client_proto_version >= 1)
			{
				ca_descr_t *cadesc = (ca_descr_t *) data;
				cadesc->index = htonl(cadesc->index);
				cadesc->parity = htonl(cadesc->parity);
			}
			memcpy(&packet[size], data, sct_cadescr_size);
			size += sct_cadescr_size;
			break;
		}

		case DVBAPI_CA_SET_PID:
		{
			int sct_capid_size = sizeof(ca_pid_t);
			if(client_proto_version >= 1)
			{
				ca_pid_t *capid = (ca_pid_t *) data;
				capid->pid = htonl(capid->pid);
				capid->index = htonl(capid->index);
			}
			memcpy(&packet[size], data, sct_capid_size);
			size += sct_capid_size;
			break;
		}

		case DVBAPI_CA_SET_DESCR_MODE:
		{
			int sct_cadescr_mode_size = sizeof(ca_descr_mode_t);
			if(client_proto_version >= 1)
			{
				ca_descr_mode_t *cadesc_mode = (ca_descr_mode_t *) data;
				cadesc_mode->index = htonl(cadesc_mode->index);
				cadesc_mode->algo = htonl(cadesc_mode->algo);
				cadesc_mode->cipher_mode = htonl(cadesc_mode->cipher_mode);
			}
			memcpy(&packet[size], data, sct_cadescr_mode_size);
			size += sct_cadescr_mode_size;
			break;
		}

		case DVBAPI_CA_SET_DESCR_DATA:
		{
			ca_descr_data_t *cadesc_data = (ca_descr_data_t *) data;
			uint32_t length = cadesc_data->length;
			if(client_proto_version >= 1)
			{
				cadesc_data->index = htonl(cadesc_data->index);
				cadesc_data->parity = htonl(cadesc_data->parity);
				cadesc_data->data_type = htonl(cadesc_data->data_type);
				cadesc_data->length = htonl(cadesc_data->length);
			}
			memcpy(&packet[size], &cadesc_data->index, 4);
			memcpy(&packet[size + 4], &cadesc_data->parity, 4);
			memcpy(&packet[size + 8], &cadesc_data->data_type, 4);
			memcpy(&packet[size + 12], &cadesc_data->length, 4);
			memcpy(&packet[size + 16], cadesc_data->data, length);
			size += 16 + length;
			break;
		}

		case DVBAPI_DMX_SET_FILTER:
		case DVBAPI_DMX_STOP:
		{
			int32_t sct_filter_size = sizeof(struct dmx_sct_filter_params);
			packet[size++] = demux_id; // demux id - 1 byte
			packet[size++] = filter_number; // filter number - 1 byte
			if(data) // filter data when starting
			{
				if(client_proto_version >= 1)
				{
					struct dmx_sct_filter_params *fp = (struct dmx_sct_filter_params *)data;
					// adding all dmx_sct_filter_params structure fields
					// one by one to avoid padding problems
					uint16_t pid = htons(fp->pid);
					memcpy(&packet[size], &pid, 2);
					size += 2;
					memcpy(&packet[size], fp->filter.filter, 16);
					size += 16;
					memcpy(&packet[size], fp->filter.mask, 16);
					size += 16;
					memcpy(&packet[size], fp->filter.mode, 16);
					size += 16;
					uint32_t timeout = htonl(fp->timeout);
					memcpy(&packet[size], &timeout, 4);
					size += 4;
					uint32_t flags = htonl(fp->flags);
					memcpy(&packet[size], &flags, 4);
					size += 4;
				}
				else
				{
					memcpy(&packet[size], data, sct_filter_size); // dmx_sct_filter_params struct
					size += sct_filter_size;
				}
			}
			else // pid when stopping
			{
				if(client_proto_version >= 1)
				{
					uint16_t pid = htons(demux[demux_id].demux_fd[filter_number].pid);
					memcpy(&packet[size], &pid, 2);
					size += 2;
				}
				else
				{
					uint16_t pid = demux[demux_id].demux_fd[filter_number].pid;
					packet[size++] = pid >> 8;
					packet[size++] = pid & 0xff;
				}
			}
			break;
		}

		default: // unknown request
		{
			cs_log("ERROR: dvbapi_net_send: invalid request");
			return 0;
		}
	}
	// sending
	cs_log_dump_dbg(D_DVBAPI, packet, size, "Sending packet to dvbapi client (fd=%d):", socket_fd);
	send(socket_fd, &packet, size, MSG_DONTWAIT);
	// always returning success as the client could close socket
	return 0;
}

int32_t dvbapi_set_filter(int32_t demux_id, int32_t api, uint16_t pid, uint16_t caid, uint32_t provid,
			uint8_t *filt, uint8_t *mask, int32_t timeout, int32_t pidindex, int32_t type, int8_t add_to_emm_list)
{
	int32_t ret = -1, n = -1, i, filterfd = -1;

	for(i = 0; i < maxfilter && demux[demux_id].demux_fd[i].fd > 0; i++) { ; }

	if(i >= maxfilter)
	{
		cs_log_dbg(D_DVBAPI, "no free filter");
		return -1;
	}
	n = i;

	if(USE_OPENXCAS)
	{
		if(type == TYPE_ECM)
		{
			openxcas_set_caid(demux[demux_id].ECMpids[pidindex].CAID);
			openxcas_set_ecm_pid(pid);
		}

		demux[demux_id].demux_fd[n].fd = DUMMY_FD;
		demux[demux_id].demux_fd[n].pidindex = pidindex;
		demux[demux_id].demux_fd[n].pid = pid;
		demux[demux_id].demux_fd[n].caid = caid;
		demux[demux_id].demux_fd[n].provid = provid;
		demux[demux_id].demux_fd[n].type = type;
		memcpy(demux[demux_id].demux_fd[n].filter, filt, 16); // copy filter to check later on if receiver delivered accordingly
		memcpy(demux[demux_id].demux_fd[n].mask, mask, 16); // copy mask to check later on if receiver delivered accordingly
		return 1;
	}

	switch(api)
	{
		case DVBAPI_3:
		{
			if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
			{
				ret = filterfd = DUMMY_FD;
			}
			else
			{
				ret = filterfd = dvbapi_open_device(0, demux[demux_id].demux_index, demux[demux_id].adapter_index);
			}

			if(ret < 0)
			{
				return ret; // return if device cant be opened!
			}

			struct dmx_sct_filter_params sFP2;
			memset(&sFP2, 0, sizeof(sFP2));
			sFP2.pid = pid;
			sFP2.timeout = timeout;
			sFP2.flags = DMX_IMMEDIATE_START;

			if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO)
			{
				//DeepThought: on dgs/cubestation and neumo images, perhaps others
				//the following code is needed to descramble
				sFP2.filter.filter[0] = filt[0];
				sFP2.filter.mask[0] = mask[0];
				sFP2.filter.filter[1] = 0;
				sFP2.filter.mask[1] = 0;
				sFP2.filter.filter[2] = 0;
				sFP2.filter.mask[2] = 0;
				memcpy(sFP2.filter.filter + 3, filt + 1, 16 - 3);
				memcpy(sFP2.filter.mask + 3, mask + 1, 16 - 3);

				//DeepThought: in the drivers of the dgs/cubestation and neumo images,
				//dvbapi 1 and 3 are somehow mixed. In the kernel drivers, the DMX_SET_FILTER
				//ioctl expects to receive a dmx_sct_filter_params structure (DVBAPI 3) but
				//due to a bug its sets the "positive mask" wrongly (they should be all 0).
				//On the other hand, the DMX_SET_FILTER1 ioctl also uses the dmx_sct_filter_params
				//structure, which is incorrect (it should be dmxSctFilterParams).
				//The only way to get it right is to call DMX_SET_FILTER1 with the argument
				//expected by DMX_SET_FILTER. Otherwise, the timeout parameter is not passed correctly.
				ret = dvbapi_ioctl(filterfd, DMX_SET_FILTER1, &sFP2);
			}
			else
			{
				memcpy(sFP2.filter.filter, filt, 16);
				memcpy(sFP2.filter.mask, mask, 16);
				if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
				{
					ret = dvbapi_net_send(DVBAPI_DMX_SET_FILTER,
								demux[demux_id].socket_fd,
								0,
								demux_id,
								n,
								(uint8_t *) &sFP2,
								NULL,
								NULL,
								demux[demux_id].client_proto_version);
				}
				else
				{
					ret = dvbapi_ioctl(filterfd, DMX_SET_FILTER, &sFP2);
				}
			}
			break;
		}

		case DVBAPI_1:
		{
			ret = filterfd = dvbapi_open_device(0, demux[demux_id].demux_index, demux[demux_id].adapter_index);
			if(ret < 0) { return ret; } // return if device cant be opened!

			struct dmxSctFilterParams sFP1;
			memset(&sFP1, 0, sizeof(sFP1));
			sFP1.pid = pid;
			sFP1.timeout = timeout;
			sFP1.flags = DMX_IMMEDIATE_START;
			memcpy(sFP1.filter.filter, filt, 16);
			memcpy(sFP1.filter.mask, mask, 16);
			ret = dvbapi_ioctl(filterfd, DMX_SET_FILTER1, &sFP1);
			break;
		}

#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		case STAPI:
		{
			ret = filterfd = stapi_set_filter(demux_id, pid, filt, mask, n, demux[demux_id].pmt_file);
			if(ret <= 0)
			{
				ret = -1; // error setting filter!
			}
			break;
		}
#endif

#if defined WITH_COOLAPI || defined WITH_COOLAPI2
		case COOLAPI:
		{
			ret = filterfd = coolapi_open_device(demux[demux_id].demux_index, demux_id);
			if(ret > 0)
			{
				ret = coolapi_set_filter(filterfd, n, pid, filt, mask, type);
			}
			else
			{
				ret = -1; // fail
			}
			break;
		}
#endif

		default:
			break;
	}

	if(ret != -1) // filter set successful
	{
		// only register if filter was set successful
		demux[demux_id].demux_fd[n].fd       = filterfd;
		demux[demux_id].demux_fd[n].pidindex = pidindex;
		demux[demux_id].demux_fd[n].pid      = pid;
		demux[demux_id].demux_fd[n].caid     = caid;
		demux[demux_id].demux_fd[n].provid   = provid;
		demux[demux_id].demux_fd[n].type     = type;

		// copy filter and mask to check later on if receiver delivered accordingly
		memcpy(demux[demux_id].demux_fd[n].filter, filt, 16);
		memcpy(demux[demux_id].demux_fd[n].mask, mask, 16);

		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d started successfully (caid %04X provid %06X pid %04X)",
					demux_id, n + 1, caid, provid, pid);

		if(type == TYPE_EMM && add_to_emm_list)
		{
			add_emmfilter_to_list(demux_id, filt, caid, provid, pid, n + 1, true);
		}
	}
	else
	{
		cs_log("ERROR: Could not start demux filter (api: %d errno=%d %s)", selected_api, errno, strerror(errno));
	}
	return ret;
}

/*
 * Get number of available descramblers from box. On success, it stores result on
 * global variable "ca_descramblers_total". On failure, it uses a safe default value.
 */
static int32_t dvbapi_get_descrambler_info(void)
{
	// In enigma2 all ca devices are listed under adapter0. In addition we only
	// need to ask one ca device to get the total number of descramblers. In
	// PC installations, there are no ca devices, so we use a predefined value.

	if(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
	{
		ca_descramblers_total = INDEX_MAX_NET;
		return 1; // nothing else to do for PCs
	}

	int32_t fd = 0, ca_offset = 0;
	char device_path[128], device_path2[128];
	struct ca_descr_info descr_info;
	memset(&descr_info, 0, sizeof(descr_info));

	// Use a safe default in case we fail to get the exact number
	ca_descramblers_total = INDEX_MAX_LOCAL;

	if(cfg.dvbapi_boxtype == BOXTYPE_DUCKBOX ||
		cfg.dvbapi_boxtype == BOXTYPE_DBOX2 ||
		cfg.dvbapi_boxtype == BOXTYPE_UFS910)
	{
		ca_offset = 1;
	}

	// Ask device for exact number of ca descramblers
	snprintf(device_path2, sizeof(device_path2), devices[selected_box].ca_device, ca_offset);
	snprintf(device_path, sizeof(device_path), devices[selected_box].path, 0);

	if (!cs_strncat(device_path, device_path2, sizeof(device_path)))
		return 0;

	if((fd = open(device_path, O_RDWR | O_NONBLOCK)) < 0)
	{
		cs_log("ERROR: Can't open device %s (errno=%d %s)", device_path, errno, strerror(errno));
		return 0;
	}

	if(dvbapi_ioctl(fd, CA_GET_DESCR_INFO, &descr_info) < 0)
	{
		cs_log_dbg(D_DVBAPI,"ERROR: ioctl(CA_GET_DESCR_INFO): %s", strerror(errno));
	}

	if(close(fd) < 0)
	{
		cs_log("ERROR: Can't close device %s (errno=%d %s)", device_path, errno, strerror(errno));
	}

	// We got a valid response from device (count the ECD type only)
	if(descr_info.num > 0 && (descr_info.type & 1))
	{
		ca_descramblers_total = descr_info.num;
		cs_log("Detected %s device, total available descramblers: %d", device_path, ca_descramblers_total);
		return 1;
	}

	return 0;
}

static int32_t dvbapi_detect_api(void)
{
#if defined WITH_COOLAPI || defined WITH_COOLAPI2
	selected_api = COOLAPI;
	selected_box = BOX_INDEX_COOLSTREAM;
	disable_pmt_files = 1;
	dvbapi_listenport_active = 0;
	cs_log("Detected Coolstream API");
	return 1;
#else
	if(cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || cfg.dvbapi_boxtype == BOXTYPE_PC)
	{
		selected_api = DVBAPI_3;
		selected_box = BOX_INDEX_DREAMBOX_DVBAPI3;
		dvbapi_listenport_active = cfg.dvbapi_listenport;
		if(dvbapi_listenport_active)
		{
			cs_log("Using TCP listen socket, API forced to DVBAPIv3 (%d), userconfig boxtype: %d",
				selected_api, cfg.dvbapi_boxtype);
		}
		else
		{
			cs_log("Using %s listen socket, API forced to DVBAPIv3 (%d), userconfig boxtype: %d",
				devices[selected_box].cam_socket_path, selected_api, cfg.dvbapi_boxtype);
		}
		return 1;
	}
	else if(cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)
	{
		selected_api = DVBAPI_3;
		selected_box = BOX_INDEX_QBOXHD;
		dvbapi_listenport_active = 0;
		disable_pmt_files = 1;
		cs_log("Using SamyGO dvbapi v0.1");
		return 1;
	}
	else
	{
		dvbapi_listenport_active = 0;
	}

	int32_t i = 0, n = 0, devnum = -1, dmx_fd = 0, filtercount = 0;
	char device_path[128], device_path2[128];
	static LLIST *ll_max_fd;
	ll_max_fd = ll_create("ll_max_fd");
	LL_ITER itr;

	struct s_open_fd
	{
		uint32_t fd;
	};
	struct s_open_fd *open_fd;

	while(i < BOX_COUNT)
	{
		do
		{
			snprintf(device_path2, sizeof(device_path2), devices[i].demux_device, 0);
			snprintf(device_path, sizeof(device_path), devices[i].path, n);

			filtercount = 0;

			if (cs_strncat(device_path, device_path2, sizeof(device_path)))
			{
				while((dmx_fd = open(device_path, O_RDWR | O_NONBLOCK)) > 0 && filtercount < MAX_FILTER)
				{
					filtercount++;
					if(!cs_malloc(&open_fd, sizeof(struct s_open_fd)))
					{
						close(dmx_fd);
						break;
					}
					open_fd->fd = dmx_fd;
					ll_append(ll_max_fd, open_fd);
				}
			}

			if(filtercount > 0)
			{
				itr = ll_iter_create(ll_max_fd);
				while((open_fd = ll_iter_next(&itr)))
				{
					dmx_fd = open_fd->fd;
					do
					{
						;
					}
					while(close(dmx_fd) < 0);
					ll_iter_remove_data(&itr);
				}
				devnum = i;
				selected_api = devices[devnum].api;
				selected_box = devnum;

				if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO)
				{
					selected_api = DVBAPI_3; //DeepThought
				}

#if defined(WITH_STAPI) || defined(WITH_STAPI5)
				if(selected_api == STAPI && stapi_open() == 0)
				{
					cs_log("ERROR: stapi: setting up stapi failed.");
					return 0;
				}
#endif
				maxfilter = filtercount;
				cs_log("Detected %s Api: %d, userconfig boxtype: %d maximum number of filters is %d (oscam limit is %d)",
					device_path, selected_api, cfg.dvbapi_boxtype, filtercount, MAX_FILTER);

#ifdef MODULE_STREAMRELAY
				// Log enabled demuxer fix
				if(cfg.dvbapi_demuxer_fix)
				{
					cs_log("Demuxer fix enabled, try fixing stream relay audio/video sync...");
				}
#endif
			}

			// try at least 8 adapters
			if((strchr(devices[i].path, '%') != NULL) && (n < 8))
			{
				n++;
			}
			else
			{
				n = 0; i++;
			}

		} while(n != 0); // n is set to 0 again if 8 adapters are tried!

		if(devnum != -1)
		{
			break; // check if box detected
		}
	}

	ll_destroy(&ll_max_fd);
	if(devnum == -1)
	{
		return 0;
	}
#endif

	return 1;
}

static int32_t dvbapi_read_device(int32_t dmx_fd, uint8_t *buf, uint32_t length)
{
	int32_t readed;
	uint32_t count = 0;
	struct pollfd pfd[1];

	pfd[0].fd = dmx_fd;
	pfd[0].events = (POLLIN | POLLPRI);

	while(count < length)
	{
		if(poll(pfd, 1, 0)) // fd ready for reading?
		{
			if(pfd[0].revents & (POLLIN | POLLPRI)) // is there data to read?
			{
				readed = read(dmx_fd, &buf[count], length-count);
				if(readed < 0) // error occured while reading
				{
					if(errno == EINTR || errno == EAGAIN) // try again in case of interrupt
					{
						continue;
					}

					cs_log("ERROR: Read error on fd %d (errno=%d %s)", dmx_fd, errno, strerror(errno));
					return (errno == EOVERFLOW ? 0 : -1);
				}

				if(readed > 0) // successful read
				{
					count += readed;
				}

				if(readed == 0 && count > 0) // nothing to read left
				{
					break;
				}
			}
			else
			{
				return -1; // other events than pollin/pri means bad news -> abort!
			}
		}
		else
		{
			break;
		}
	}
	cs_log_dump_dbg(D_TRACE, buf, count, "Received:");
	return count;
}

int32_t dvbapi_open_device(int32_t type, int32_t num, int32_t adapter)
{
	int32_t dmx_fd, ret;
	int32_t ca_offset = 0;
	char device_path[128], device_path2[128];

	if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
	{
		return DUMMY_FD;
	}

	if(type == 0)
	{
		snprintf(device_path2, sizeof(device_path2), devices[selected_box].demux_device, num);
		snprintf(device_path, sizeof(device_path), devices[selected_box].path, adapter);

		if (!cs_strncat(device_path, device_path2, sizeof(device_path)))
			return -1;

	}
	else
	{
		if(cfg.dvbapi_boxtype == BOXTYPE_DUCKBOX
			|| cfg.dvbapi_boxtype == BOXTYPE_DBOX2
			|| cfg.dvbapi_boxtype == BOXTYPE_UFS910)
		{
			ca_offset = 1;
		}

		if(cfg.dvbapi_boxtype == BOXTYPE_QBOXHD
			|| cfg.dvbapi_boxtype == BOXTYPE_PC
			|| cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)
		{
			num = 0;
		}

		snprintf(device_path2, sizeof(device_path2), devices[selected_box].ca_device, num + ca_offset);
		snprintf(device_path, sizeof(device_path), devices[selected_box].path, adapter);

		if (!cs_strncat(device_path, device_path2, sizeof(device_path)))
			return -1;

	}

	if(cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)
	{
		if(type == 0)
		{
			struct sockaddr_un saddr;
			memset(&saddr, 0, sizeof(saddr));
			saddr.sun_family = AF_UNIX;
			cs_strncpy(saddr.sun_path, device_path, sizeof(saddr.sun_path));
			dmx_fd = socket(AF_UNIX, SOCK_STREAM, 0);
			ret = connect(dmx_fd, (struct sockaddr *)&saddr, sizeof(saddr));
			if(ret < 0)
			{
				close(dmx_fd);
			}
		}
		else if(type == 1)
		{
			int32_t udp_port = 9000;
			struct sockaddr_in saddr;
			memset(&saddr, 0, sizeof(saddr));
			saddr.sin_family = AF_INET;
			saddr.sin_port = htons(udp_port + adapter);
			saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
			dmx_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
			set_nonblock(dmx_fd, true);
			ret = connect(dmx_fd, (struct sockaddr *) &saddr, sizeof(saddr));
			if(ret < 0)
			{
				close(dmx_fd);
			}
			cs_log_dbg(D_DVBAPI, "NET DEVICE open (port = %d) fd %d", udp_port + adapter, dmx_fd);
		}
		else
		{
			ret = -1;
		}
	}
	else
	{
		dmx_fd = ret = open(device_path, O_RDWR | O_NONBLOCK);
	}

	if(ret < 0)
	{
		cs_log("ERROR: Can't open device %s (errno=%d %s)", device_path, errno, strerror(errno));
		return -1;
	}
	cs_log_dbg(D_DVBAPI, "Open device %s (fd %d)", device_path, dmx_fd);
	return dmx_fd;
}

uint16_t tunemm_caid_map(uint8_t direct, uint16_t caid, uint16_t srvid)
{
	int32_t i;
	struct s_client *cl = cur_client();
	TUNTAB *ttab = &cl->ttab;

	if(!ttab->ttnum)
	{
		return caid;
	}

	if(direct)
	{
		for(i = 0; i < ttab->ttnum; i++)
		{
			if(caid == ttab->ttdata[i].bt_caidto &&
				(srvid == ttab->ttdata[i].bt_srvid || ttab->ttdata[i].bt_srvid == 0xFFFF || !ttab->ttdata[i].bt_srvid))
			{
				return ttab->ttdata[i].bt_caidfrom;
			}
		}
	}
	else
	{
		for(i = 0; i < ttab->ttnum; i++)
		{
			if(caid == ttab->ttdata[i].bt_caidfrom &&
				(srvid == ttab->ttdata[i].bt_srvid || ttab->ttdata[i].bt_srvid == 0xFFFF || !ttab->ttdata[i].bt_srvid))
			{
				return ttab->ttdata[i].bt_caidto;
			}
		}
	}
	return caid;
}

int32_t dvbapi_stop_filter(int32_t demux_id, int32_t type, uint32_t msgid)
{
#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2)
	// We prevented PAT and PMT from starting, so lets don't close them either.
	if(type == TYPE_PAT || type == TYPE_PMT)
	{
		return 1;
	}
#endif

	int32_t g, error = 0;

	// just stop them all, we don't want to risk leaving
	// any stale filter running due to lowering of maxfilters
	for(g = 0; g < MAX_FILTER; g++)
	{
		if(demux[demux_id].demux_fd[g].type == type)
		{
			if(dvbapi_stop_filternum(demux_id, g, msgid) == -1)
			{
				error = 1;
			}
		}
	}
	return !error; // on error return 0, all ok 1
}

int32_t dvbapi_stop_filternum(int32_t demux_id, int32_t num, uint32_t msgid)
{
	int32_t retfilter = -1, retfd = -1, fd = demux[demux_id].demux_fd[num].fd, try = 0;

	if(USE_OPENXCAS)
	{
		demux[demux_id].demux_fd[num].type = 0;
		demux[demux_id].demux_fd[num].fd = 0;
		return 1; // all ok!
	}

	if(fd > 0)
	{
		do
		{
			errno = 0;
			if(try)
			{
				cs_sleepms(50);
			}
			try++;

			cs_log_dbg(D_DVBAPI, "Demuxer %d stop filter %d try %d (fd: %d api: %d, caid: %04X, provid: %06X, %spid: %04X)",
				demux_id,
				num + 1,
				try,
				fd,
				selected_api,
				demux[demux_id].demux_fd[num].caid,
				demux[demux_id].demux_fd[num].provid,
				demux[demux_id].demux_fd[num].type == TYPE_ECM ? "ecm" : "emm",
				demux[demux_id].demux_fd[num].pid);

			switch(selected_api)
			{
				case DVBAPI_3:
				{
					if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
					{
						retfilter = dvbapi_net_send(DVBAPI_DMX_STOP,
									demux[demux_id].socket_fd,
									msgid,
									demux_id,
									num,
									NULL,
									NULL,
									NULL,
									demux[demux_id].client_proto_version);
					}
					else
					{
						retfilter = dvbapi_ioctl(fd, DMX_STOP, NULL);
					}
					break;
				}

				case DVBAPI_1:
				{
					retfilter = dvbapi_ioctl(fd, DMX_STOP, NULL);
					break;
				}

#if defined(WITH_STAPI) || defined(WITH_STAPI5)
				case STAPI:
				{
					retfilter = stapi_remove_filter(demux_id, num, demux[demux_id].pmt_file);
					if(retfilter != 1) // stapi returns 0 for error, 1 for all ok
					{
						retfilter = -1;
					}
					break;
				}
#endif
#if defined WITH_COOLAPI || defined WITH_COOLAPI2
				case COOLAPI:
				{
					retfilter = coolapi_remove_filter(fd, num);
					if(retfilter >= 0)
					{
						retfd = coolapi_close_device(fd);
					}
					break;
				}
#endif
				default:
					break;
			}

			if(errno == 9) // no error on bad file descriptor
			{
				retfilter = 0;
			}
		} while(retfilter < 0 && try < 10);

#if !defined WITH_COOLAPI && !defined WITH_COOLAPI2 // no fd close for coolapi and stapi, all others do close fd!
		try = 0;
		do
		{
			if(try)
			{
				errno = 0;
				cs_sleepms(50);
			}
			try++;

			// on bad filterfd dont try to close!
			if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX && errno != 9)
			{
				if(selected_api == STAPI)
				{
					retfd = 0; // stapi closes its own filter fd!
				}
				else
				{
					// flush filter input buffer in attempt to avoid overflow receivers internal buffer
					flush_read_fd(demux_id, num, fd);
					retfd = close(fd);
					if(errno == 9)
					{
						retfd = 0; // no error on bad file descriptor
					}
				}
			}
			else
			{
				retfd = 0;
			}

		} while(retfd < 0 && try < 10);
#endif
	}
	else // fd <=0
	{
		return 1; // filter was already killed!
	}

	if(retfilter < 0) // error on remove filter
	{
		cs_log("ERROR: Demuxer %d could not stop Filter %d (fd:%d api:%d errno=%d %s)",
			demux_id, num + 1, fd, selected_api, errno, strerror(errno));
		return retfilter;
	}

	if(retfd < 0) // error on close filter fd
	{
		cs_log("ERROR: Demuxer %d could not close fd of Filter %d (fd=%d api:%d errno=%d %s)",
			demux_id, num + 1, fd, selected_api, errno, strerror(errno));
		return retfd;
	}

	// code below runs only if nothing has gone wrong
	if(demux[demux_id].demux_fd[num].type == TYPE_ECM) // ecm filter stopped: reset index!
	{
		int32_t oldpid = demux[demux_id].demux_fd[num].pidindex;
		int32_t curpid = demux[demux_id].pidindex;

		// workaround: below dont run on stapi since it handles it own pids...
		// stapi need to be better integrated in oscam dvbapi.
		if(selected_api != STAPI)
		{
			int32_t z;
			for(z = 0; z < MAX_STREAM_INDICES; z++)
			{
				uint32_t idx = demux[demux_id].ECMpids[oldpid].index[z];
				demux[demux_id].ECMpids[oldpid].index[z] = INDEX_INVALID;

				if(idx != INDEX_INVALID) // if in use
				{
					int32_t i;
					for(i = 0; i < demux[demux_id].STREAMpidcount; i++)
					{
						int8_t match = 0;

						// check streams of old disabled ecmpid
						if(!demux[demux_id].ECMpids[oldpid].streams
							|| ((demux[demux_id].ECMpids[oldpid].streams & (1 << i)) == (uint) (1 << i)))
						{
							// check if new ecmpid is using same streams
							if(curpid != -1 && (!demux[demux_id].ECMpids[curpid].streams
								|| ((demux[demux_id].ECMpids[curpid].streams & (1 << i)) == (uint) (1 << i))))
							{
								continue; // found same stream on old and new ecmpid -> skip! (and leave it enabled!)
							}
							int32_t pidtobestopped = demux[demux_id].STREAMpids[i];
							int32_t j, k, otherdemuxpid;
							uint32_t otherdemuxidx;

							for(j = 0; j < MAX_DEMUX; j++) // check other demuxers for same streampid with same index
							{
								if(demux[j].program_number == 0 || demux_id == j || demux[j].ca_mask != demux[demux_id].ca_mask)
								{
									continue;
									// skip empty demuxers
									// skip same demuxer
									// skip streampid running on other ca device
								}

								otherdemuxpid = demux[j].pidindex;
								if(otherdemuxpid == -1)
								{
									continue; // Other demuxer not descrambling yet
								}

								int32_t y;
								for(y = 0; y < MAX_STREAM_INDICES; y++)
								{
									otherdemuxidx = demux[j].ECMpids[otherdemuxpid].index[y];
									if(otherdemuxidx == INDEX_INVALID || otherdemuxidx != idx)
									{
										continue; // Other demuxer has no index yet, or index is different
									}

									for(k = 0; k < demux[j].STREAMpidcount; k++)
									{
										if(!demux[j].ECMpids[otherdemuxpid].streams
											|| ((demux[j].ECMpids[otherdemuxpid].streams & (1 << k)) == (uint) (1 << k)))
										{
											if(demux[j].STREAMpids[k] == pidtobestopped)
											{
												continue; // found same streampid enabled with same index
														  // on one or more other demuxers -> skip! (and leave it enabled!)
											}
										}
										match = 1; // matching stream found
									}
								}
							}

							if(!match)
							{
								for(j = 0; j < CA_MAX; j++)
								{
									if(((demux[demux_id].ca_mask & (1 << j)) == (uint32_t) (1 << j)))
									{
										remove_streampid_from_list(j, pidtobestopped, idx);
										break;
									}
								}
							}
						}
					}
				}
			}
		}
	}

	if(demux[demux_id].demux_fd[num].type == TYPE_EMM) // If emm type remove from emm filterlist
	{
		remove_emmfilter_from_list(demux_id,
					demux[demux_id].demux_fd[num].caid,
					demux[demux_id].demux_fd[num].provid,
					demux[demux_id].demux_fd[num].pid,
					num + 1);
	}
	demux[demux_id].demux_fd[num].type = 0;
	demux[demux_id].demux_fd[num].fd = 0;
	return 1; // all ok!
}

void dvbapi_start_filter(int32_t demux_id, int32_t pidindex, uint16_t pid, uint16_t caid,
		uint32_t provid, uint8_t table, uint8_t mask, int32_t timeout, int32_t type)
{
	int32_t i;
	for(i = 0; i < maxfilter; i++) // check if filter is present
	{
		if(demux[demux_id].demux_fd[i].fd > 0 &&
			demux[demux_id].demux_fd[i].pid == pid &&
			demux[demux_id].demux_fd[i].type == type &&
			demux[demux_id].demux_fd[i].filter[0] == table &&
			demux[demux_id].demux_fd[i].mask[0] == mask)
		{
			return;
		}
	}

	uint8_t filter[32];
	memset(filter, 0, 32);
	filter[0] = table;
	filter[16] = mask;

	cs_log_dbg(D_DVBAPI, "Demuxer %d try to start new filter for caid: %04X, provid: %06X, pid: %04X",
				demux_id, caid, provid, pid);

	dvbapi_set_filter(demux_id, selected_api, pid, caid, provid, filter, filter + 16, timeout, pidindex, type, 0);
}

void dvbapi_start_sdt_filter(int32_t demux_id)
{
	dvbapi_start_filter(demux_id, demux[demux_id].pidindex, 0x0011, 0x001, 0x01, 0x42, 0xFF, 0, TYPE_SDT);
	demux[demux_id].sdt_filter = 0;
}

void dvbapi_start_pat_filter(int32_t demux_id)
{
#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2)
	// PAT-Filter breaks API and OSCAM for Coolstream.
	// Don't use it
	return;
#endif
	dvbapi_start_filter(demux_id, demux[demux_id].pidindex, 0x0000, 0x001, 0x01, 0x00, 0xFF, 0, TYPE_PAT);
}

void dvbapi_start_pmt_filter(int32_t demux_id)
{
#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2)
	// PMT-Filter breaks API and OSCAM for Coolstream.
	// Don't use it
	return;
#endif
	uint8_t filter[16], mask[16];
	memset(filter, 0, 16);
	memset(mask, 0, 16);

	filter[0] = 0x02;
	i2b_buf(2, demux[demux_id].program_number, filter + 1); // add srvid to filter since the pid can deliver pmt for multiple srvid
	mask[0] = 0xFF;
	mask[1] = 0xFF;
	mask[2] = 0xFF;

	dvbapi_set_filter(demux_id, selected_api, demux[demux_id].pmtpid, 0x001, 0x01, filter, mask, 0, 0, TYPE_PMT, 0);
}

void dvbapi_start_cat_filter(int32_t demux_id)
{
	dvbapi_start_filter(demux_id, demux[demux_id].pidindex, 0x0001, 0x001, 0x01, 0x01, 0xFF, 0, TYPE_CAT);
	demux[demux_id].emm_filter = 0;
}

void dvbapi_start_emm_filter(int32_t demux_id)
{
	unsigned int j;
	if(!demux[demux_id].EMMpidcount)
	{
		return;
	}

	struct s_csystem_emm_filter *dmx_filter = NULL;
	unsigned int filter_count = 0;
	uint16_t caid, ncaid;
	uint32_t provid;
	struct s_reader *rdr = NULL;
	struct s_client *cl = cur_client();

	if(!cl || !cl->aureader_list)
	{
		return;
	}

	LL_ITER itr = ll_iter_create(cl->aureader_list);
	while((rdr = ll_iter_next(&itr)))
	{
		if(!(rdr->grp & cl->grp))
		{
			continue;
		}

		if(rdr->audisabled || !rdr->enable || (!is_network_reader(rdr) && rdr->card_status != CARD_INSERTED))
		{
			continue;
		}

		const struct s_cardsystem *csystem;
		uint16_t c, match;
		cs_log_dbg(D_DVBAPI, "Demuxer %d matching reader %s against available emmpids -> START!", demux_id, rdr->label);

		for(c = 0; c < demux[demux_id].EMMpidcount; c++)
		{
			caid = ncaid = demux[demux_id].EMMpids[c].CAID;
			if(!caid) continue;

			if(chk_is_betatunnel_caid(caid) == 2)
			{
				ncaid = tunemm_caid_map(FROM_TO, caid, demux[demux_id].program_number);
			}
			provid = demux[demux_id].EMMpids[c].PROVID;

			if(caid == ncaid)
			{
				match = emm_reader_match(rdr, caid, provid);
			}
			else
			{
				match = emm_reader_match(rdr, ncaid, provid);
			}

			if(match)
			{
				if(rdr->typ == R_EMU)
				{
					csystem = rdr->csystem;
				}
				else
				{
					csystem = get_cardsystem_by_caid(caid);
				}
				if(csystem)
				{
					if(caid != ncaid)
					{
						csystem = get_cardsystem_by_caid(ncaid);
						if(csystem && csystem->get_tunemm_filter)
						{
							csystem->get_tunemm_filter(rdr, &dmx_filter, &filter_count);
							cs_log_dbg(D_DVBAPI, "Demuxer %d setting emm filter for betatunnel: %04X -> %04X",
								demux_id, ncaid, caid);
						}
						else
						{
							cs_log_dbg(D_DVBAPI, "Demuxer %d cardsystem for emm filter for caid %04X of reader %s not found",
								demux_id, ncaid, rdr->label);
							continue;
						}
					}
					else if(csystem->get_emm_filter)
					{
						if(rdr->typ == R_EMU)
						{
							csystem->get_emm_filter_adv(rdr, &dmx_filter, &filter_count, caid, provid, demux[demux_id].program_number,
														demux[demux_id].tsid, demux[demux_id].onid, demux[demux_id].ens);
						}
						else
						{
							csystem->get_emm_filter(rdr, &dmx_filter, &filter_count);
						}
					}
				}
				else
				{
					cs_log_dbg(D_DVBAPI, "Demuxer %d cardsystem for emm filter for caid %04X of reader %s not found",
						demux_id, caid, rdr->label);
					continue;
				}

				for(j = 0; j < filter_count ; j++)
				{
					if(dmx_filter[j].enabled == 0)
					{
						continue;
					}

					uint8_t filter[32];
					memset(filter, 0, sizeof(filter)); // reset filter
					uint32_t usefilterbytes = 16; // default use all filters
					memcpy(filter, dmx_filter[j].filter, usefilterbytes);
					memcpy(filter + 16, dmx_filter[j].mask, usefilterbytes);
					int32_t emmtype = dmx_filter[j].type;

					if(filter[0] && (((1 << (filter[0] % 0x80)) & rdr->b_nano) && !((1 << (filter[0] % 0x80)) & rdr->s_nano)))
					{
						cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d blocked by userconfig -> SKIP!",
							demux_id, rdr->label, j + 1, filter_count);
						continue;
					}

					if((rdr->blockemm & emmtype) && !(((1 << (filter[0] % 0x80)) & rdr->s_nano) || (rdr->saveemm & emmtype)))
					{
						cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d blocked by userconfig -> SKIP!",
							demux_id, rdr->label, j + 1, filter_count);
						continue;
					}

					if(demux[demux_id].EMMpids[c].type & emmtype)
					{
						cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d type match -> ENABLE!",
							demux_id, rdr->label, j + 1, filter_count);
						check_add_emmpid(demux_id, filter, c, emmtype);
					}
					else
					{
						cs_log_dbg(D_DVBAPI, "Demuxer %d reader %s emmfilter %d/%d type mismatch -> SKIP!",
							demux_id, rdr->label, j + 1, filter_count);
					}
				}
				// dmx_filter not use below this point;
				NULLFREE(dmx_filter);
				filter_count = 0;
			}
		}
		cs_log_dbg(D_DVBAPI, "Demuxer %d matching reader %s against available emmpids -> DONE!", demux_id, rdr->label);
	}

	if(demux[demux_id].emm_filter == -1) // first run -1
	{
		demux[demux_id].emm_filter = 0;
	}
	cs_log_dbg(D_DVBAPI, "Demuxer %d handles %i emm filters", demux_id, demux[demux_id].emm_filter);
}

void dvbapi_add_ecmpid_int(int32_t demux_id, uint16_t caid, uint16_t ecmpid, uint32_t provid, uint32_t cadata, char *txt)
{
	int32_t n, added = 0;
	int32_t stream = demux[demux_id].STREAMpidcount - 1;

	for(n = 0; n < demux[demux_id].ECMpidcount; n++)
	{
		if(demux[demux_id].ECMpids[n].CAID == caid && demux[demux_id].ECMpids[n].ECM_PID == ecmpid
			&& (!provid || (provid && demux[demux_id].ECMpids[n].PROVID == provid)))
		{
			added = 1;
			if(stream > -1)
			{
				if(!demux[demux_id].ECMpids[n].streams)
				{
					// we already got this caid/ecmpid as global, no need to add the single stream
					cs_log_dbg(D_DVBAPI, "Demuxer %d skipped stream CAID: %04X ECM_PID: %04X PROVID: %06X (Same as ECMPID %d)",
						demux_id, caid, ecmpid, provid, n);
					continue;
				}
				demux[demux_id].ECMpids[n].streams |= (1 << stream);
				cs_log("Demuxer %d added stream to ecmpid %d CAID: %04X ECM_PID: %04X PROVID: %06X",
					demux_id, n, caid, ecmpid, provid);
			}
		}
	}

	if(added == 1)
	{
		return;
	}

	if(demux[demux_id].ECMpidcount >= MAX_ECM_PIDS)
	{
		cs_log("We reached maximum ECMpids: unable to add to demuxer %d ecmpid %d CAID: %04X ECM_PID: %04X PROVID: %06X %s",
			demux_id, demux[demux_id].ECMpidcount, caid, ecmpid, provid, txt);
		return;
	}

	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].ECM_PID = ecmpid;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].CAID = caid;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].PROVID = provid;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].CHID = 0x10000; // reset CHID
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].checked = 0;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].status = 0;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].tries = 0xFE;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].streams = 0; // reset streams!
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].irdeto_curindex = 0xFE; // reset
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].irdeto_maxindex = 0; // reset
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].irdeto_cycle = 0xFE; // reset
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].table = 0;
	demux[demux_id].ECMpids[demux[demux_id].ECMpidcount].cadata = cadata;

	cs_log("Demuxer %d ecmpid %d CAID: %04X ECM_PID: %04X PROVID: %06X %s",
		demux_id, demux[demux_id].ECMpidcount, caid, ecmpid, provid, txt);

	// marker to fetch emms early irdeto needs them!
	if(caid_is_irdeto(caid) || (caid_is_dre(caid) && (provid == 0x11 || provid == 0xFE)))
	{
		demux[demux_id].emmstart.time = 1;
	}
	demux[demux_id].ECMpidcount++;
}

void dvbapi_add_ecmpid(int32_t demux_id, uint16_t caid, uint16_t ecmpid, uint32_t provid, uint32_t cadata, char *txt)
{
	dvbapi_add_ecmpid_int(demux_id, caid, ecmpid, provid, cadata, txt);
	struct s_dvbapi_priority *joinentry;

	for(joinentry = dvbapi_priority; joinentry != NULL; joinentry = joinentry->next)
	{
		if((joinentry->type != 'j')
			|| (joinentry->caid && joinentry->caid != caid)
			|| (joinentry->provid && joinentry->provid != provid)
			|| (joinentry->ecmpid && joinentry->ecmpid != ecmpid)
			|| (joinentry->srvid && joinentry->srvid != demux[demux_id].program_number))
		{
			continue;
		}

		cs_log_dbg(D_DVBAPI, "Join ecmpid %04X@%06X:%04X to %04X@%06X:%04X",
			caid, provid, ecmpid, joinentry->mapcaid, joinentry->mapprovid, joinentry->mapecmpid);
		dvbapi_add_ecmpid_int(demux_id, joinentry->mapcaid, joinentry->mapecmpid, joinentry->mapprovid, 0, txt);
	}
}

void dvbapi_add_emmpid(int32_t demux_id, uint16_t caid, uint16_t emmpid, uint32_t provid, uint32_t cadata, uint8_t type)
{
	char typetext[40];
	char cadatatext[40];
	cs_strncpy(typetext, ":", sizeof(typetext));

	if(type & 0x01) { cs_strncat(typetext, "UNIQUE:", sizeof(typetext)); }
	if(type & 0x02) { cs_strncat(typetext, "SHARED:", sizeof(typetext)); }
	if(type & 0x04) { cs_strncat(typetext, "GLOBAL:", sizeof(typetext)); }
	if(type & 0xF8) { cs_strncat(typetext, "UNKNOWN:", sizeof(typetext)); }

	if(cadata > 0)
	{
		snprintf(cadatatext, 40, " CA DATA %X ", cadata);
	}
	else
	{
		cadatatext[0] = '\t'; cadatatext[1] = '\0';
	}

	if(caid == 0x4AE1 && provid == 0x11 && cadata == 0)
	{
		return;
	}

	uint16_t i;
	for(i = 0; i < demux[demux_id].EMMpidcount; i++)
	{
		if(demux[demux_id].EMMpids[i].PID == emmpid && demux[demux_id].EMMpids[i].CAID == caid &&
			demux[demux_id].EMMpids[i].PROVID == provid && demux[demux_id].EMMpids[i].cadata == cadata)
		{
			if(!(demux[demux_id].EMMpids[i].type & type))
			{
				demux[demux_id].EMMpids[i].type |= type; // register this emm kind to this emmpid
				cs_log_dbg(D_DVBAPI, "Added to existing emmpid %d additional emmtype %s",
					demux[demux_id].EMMpidcount - 1, typetext);
			}
			return;
		}
	}

	if(i < MAX_EMM_PIDS)
	{
		demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].PID = emmpid;
		demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].CAID = caid;
		demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].PROVID = provid;
		demux[demux_id].EMMpids[demux[demux_id].EMMpidcount].type = type;
		demux[demux_id].EMMpids[demux[demux_id].EMMpidcount++].cadata = cadata;
		cs_log_dbg(D_DVBAPI, "Added new emmpid %d CAID: %04X EMM_PID: %04X PROVID: %06X%sTYPE %s",
			demux[demux_id].EMMpidcount - 1, caid, emmpid, provid, cadatatext, typetext);
	}
	else
	{
		cs_log_dbg(D_DVBAPI, "We reached max emmpids: unable to add new emmpid %d CAID: %04X EMM_PID: %04X PROVID: %06X%sTYPE %s",
			demux[demux_id].EMMpidcount - 1, caid, emmpid, provid, cadatatext, typetext);
	}
}

static void dvbapi_parse_cat_ca_descriptor(int32_t demux_id, const uint8_t *buffer, uint8_t descriptor_length)
{
	uint16_t i, ca_system_id, ca_pid;
	uint32_t ca_provider = 0, ca_data = 0;

	if(descriptor_length < 4)
	{
		return; // CA descriptor has a minimum length of 4 bytes
	}

	ca_system_id = b2i(2, buffer);
	ca_pid = b2i(2, buffer + 2) & 0x1FFF;

	if(ca_system_id == 0x0000 || ca_pid == 0x1FFF)
	{
		return; // This is not a valid CAID or EMM pid
	}

	switch(ca_system_id >> 8)
	{
		case 0x01:
		{
			dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, 0, 0, EMM_UNIQUE | EMM_GLOBAL);

			for(i = 5; i < descriptor_length; i += 4)
			{
				ca_pid = b2i(2, buffer + i) & 0x1FFF;
				ca_provider = b2i(2, buffer + i + 2);

				dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_SHARED);
			}
			break;
		}

		case 0x05:
		{
			for(i = 4; i < descriptor_length; i += 2 + buffer[i + 1])
			{
				if(buffer[i] == 0x14)
				{
					ca_provider = (b2i(3, buffer + i + 2) & 0xFFFFF0); // viaccess fixup: don't care about last digit
					dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL);
				}
			}
			break;
		}

		case 0x18:
		{
			if(descriptor_length == 0x07 || descriptor_length == 0x0B)
			{
				for(i = 5; i < 5 + buffer[4]; i += 2)
				{
					ca_provider = b2i(2, buffer + i);
					dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL);
				}
			}
			else
			{
				dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL);
			}
			break;
		}

		case 0x27:
		case 0x4A:
		{
			if(caid_is_bulcrypt(ca_system_id))
			{
				dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, 0, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL);
				break;
			}

			ca_provider = buffer[4];

			if(buffer[4] == 0xFE)
			{
				dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0x102, EMM_GLOBAL);
			}
			else
			{
				if(descriptor_length == 0x0A)
				{
					ca_data = b2i(4, buffer + 6);
				}

				dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, ca_provider, ca_data, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL);
			}
			break;
		}

		default:
			dvbapi_add_emmpid(demux_id, ca_system_id, ca_pid, 0, 0, EMM_UNIQUE | EMM_SHARED | EMM_GLOBAL);
			break;
	}
}

static void dvbapi_parse_cat(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid)
{
#if defined WITH_COOLAPI || defined WITH_COOLAPI2
	// driver sometimes reports error if too many emm filter
	// but adding more ecm filter is no problem
	// ... so ifdef here instead of limiting MAX_FILTER
	demux[demux_id].max_emm_filter = 14;
#else
	if(cfg.dvbapi_requestmode == 1)
	{
		uint16_t ecm_filter_needed = 0, n;
		for(n = 0; n < demux[demux_id].ECMpidcount; n++)
		{
			if(demux[demux_id].ECMpids[n].status > -1)
			{
				ecm_filter_needed++;
			}
		}

		if(maxfilter - ecm_filter_needed <= 0)
		{
			demux[demux_id].max_emm_filter = 0;
		}
		else
		{
			demux[demux_id].max_emm_filter = maxfilter - ecm_filter_needed;
		}
	}
	else
	{
		demux[demux_id].max_emm_filter = maxfilter - 1;
	}
#endif

	uint16_t i;
	uint8_t descriptor_tag, descriptor_length;

	dvbapi_stop_filter(demux_id, TYPE_CAT, msgid);

	for(i = 8; i + 1 < length; i += 2 + descriptor_length)
	{
		descriptor_tag = buffer[i];
		descriptor_length = buffer[i + 1];

		if(descriptor_tag == 0x09) // There should be only CA descriptors here
		{
			dvbapi_parse_cat_ca_descriptor(demux_id, buffer + i + 2, descriptor_length);
		}
	}
}

static pthread_mutex_t lockindex = PTHREAD_MUTEX_INITIALIZER;

uint32_t dvbapi_get_desc_index(int32_t demux_id, int32_t pid, int32_t stream_id)
{
	int32_t i, j, k, fail = 1;
	uint32_t idx = 0;

	if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO)
	{
		sscanf(demux[demux_id].pmt_file, "pmt%3d.tmp", &idx);
		return idx;
	}

	SAFE_MUTEX_LOCK(&lockindex); // to avoid race when readers become responsive!
	while(fail && idx <= INDEX_MAX)
	{
		fail = 0;
		for(i = 0; i < MAX_DEMUX && !fail && idx < INDEX_MAX; i++)
		{
			if(demux[i].program_number == 0)
			{
				continue; // skip empty demuxers
			}

			if(demux[i].ca_mask != demux[demux_id].ca_mask
				&& (!(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)))
			{
				continue; // skip demuxer using other ca device
			}

			for(j = 0; j < demux[i].ECMpidcount && !fail; j++) // search for new unique index
			{
				for(k = 0; k < MAX_STREAM_INDICES; k++)
				{
					if(demux[i].ECMpids[j].index[k] == idx)
					{
						fail = 1;
						idx++;
					}
				}
			}
		}
	}

	if(selected_api == DVBAPI_3 || selected_api == DVBAPI_1)
	{
		// make sure we haven't run out of descramblers
		if(ca_descramblers_used < ca_descramblers_total)
		{
			ca_descramblers_used++; // increase number of descramblers used
		}
		else
		{
			idx = INDEX_INVALID; // we don't have any descramblers left
		}
	}
	else // old style check for STAPI, COOLAPI, etc
	{
		if(idx > ca_descramblers_total)
		{
			idx = INDEX_INVALID; // we don't have any descramblers left
		}
	}

	demux[demux_id].ECMpids[pid].index[stream_id] = idx;
	SAFE_MUTEX_UNLOCK(&lockindex); // and release it!

	return idx;
}

void dvbapi_set_pid(int32_t demux_id, int32_t num, uint32_t idx, bool enable, bool use_des, uint32_t msgid)
{
	int32_t i, currentfd;
	uint16_t streampid = demux[demux_id].STREAMpids[num];
	uint32_t newidx = 0, curidx;
	ca_pid_t ca_pid2;

	if(demux[demux_id].pidindex == -1 && enable)
	{
		return; // no current pid on enable? --> exit
	}

	switch(selected_api)
	{
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		case STAPI:
			if(!enable) idx = INDEX_INVALID;
			stapi_set_pid(demux_id, num, idx, streampid, demux[demux_id].pmt_file); // only used to disable pids!!!
			break;
#endif

#if defined WITH_COOLAPI || defined WITH_COOLAPI2
		case COOLAPI:
			break;
#endif

		default:
			for(i = 0; i < CA_MAX; i++)
			{
				newidx = INDEX_INVALID;
				curidx = idx;

				if(((demux[demux_id].ca_mask & (1 << i)) == (uint32_t) (1 << i)))
				{
					uint32_t action = 0;
					if(enable)
					{
						action = update_streampid_list(i, streampid, curidx, use_des);
					}

					if(!enable)
					{
						action = remove_streampid_from_list(i, streampid, curidx);
					}

					if(action != NO_STREAMPID_LISTED && action != INVALID_STREAMPID_INDEX &&
						action != FOUND_STREAMPID_INDEX && action != ADDED_STREAMPID_INDEX &&
						action != REMOVED_STREAMPID_INDEX)
					{
						// removed last index of this streampid on ca? -> disable this pid with -1 on this ca
						if((action == REMOVED_STREAMPID_LASTINDEX || action == FIRST_STREAMPID_INDEX)
							&& (is_ca_used(i, streampid) == INDEX_INVALID))
						{
							curidx = DVBAPI_INDEX_DISABLE;
						}

						// removed index of streampid that is used to decode on ca -> get a fresh one
						if(action == REMOVED_DECODING_STREAMPID_INDEX || action == FIRST_STREAMPID_INDEX)
						{
							newidx = is_ca_used(i, streampid); // get an active index for this pid and enable it on ca device
							curidx = DVBAPI_INDEX_DISABLE;
						}

						while(curidx != INDEX_INVALID || newidx != INDEX_INVALID)
						{
							memset(&ca_pid2, 0, sizeof(ca_pid2));
							ca_pid2.pid = streampid;
							if(curidx != INDEX_INVALID)
							{
								(curidx == DVBAPI_INDEX_DISABLE) ? (ca_pid2.index = -1) : (ca_pid2.index = curidx);
								cs_log_dbg(D_DVBAPI, "Demuxer %d %s stream %d pid=0x%04x index=%d on ca%d",
										demux_id,
										((enable && curidx != DVBAPI_INDEX_DISABLE) ? "enable" : "disable"),
										num + 1,
										ca_pid2.pid,
										ca_pid2.index,
										i);
								curidx = INDEX_INVALID; // flag this index as handled
							}
							else if(newidx != INDEX_INVALID)
							{
								(newidx == DVBAPI_INDEX_DISABLE) ? (ca_pid2.index = -1) : (ca_pid2.index = newidx);
								cs_log_dbg(D_DVBAPI, "Demuxer %d %s stream %d pid=0x%04x by index=%d on ca%d",
										demux_id,
										((enable && action == FIRST_STREAMPID_INDEX) ? "enable" : "takeover"),
										num + 1,
										ca_pid2.pid,
										ca_pid2.index,
										i);
								newidx = INDEX_INVALID; // flag this takeover / new index as handled
							}
#ifdef WITH_EXTENDED_CW
							if(use_des && cfg.dvbapi_extended_cw_api == 2 && ca_pid2.index != -1)
							{
								ca_pid2.index |= 0x100; // flag DES algo through pid index
							}
#endif
							if(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
							{
								dvbapi_net_send(DVBAPI_CA_SET_PID, demux[demux_id].socket_fd,
										msgid,
										demux_id,
										-1 /*unused*/,
										(uint8_t *) &ca_pid2,
										NULL,
										NULL,
										demux[demux_id].client_proto_version);
							}
							else
							{
								if(ca_soft_csa[demux_id])
								{
									continue;
								}

								currentfd = ca_fd[i];
								if(currentfd <= 0)
								{
									currentfd = dvbapi_open_device(1, i, demux[demux_id].adapter_index);
									ca_fd[i] = currentfd; // save fd of this ca
								}

								if(currentfd > 0)
								{
									if(dvbapi_ioctl(currentfd, CA_SET_PID, &ca_pid2) == -1)
									{
										cs_log_dbg(D_TRACE | D_DVBAPI,"CA_SET_PID ioctl error (errno=%d %s)", errno, strerror(errno));
										remove_streampid_from_list(i, ca_pid2.pid, INDEX_DISABLE_ALL);
									}

									uint32_t result = is_ca_used(i, 0); // check if in use by any pid
									if(result == INDEX_INVALID)
									{
										cs_log_dbg(D_DVBAPI, "Demuxer %d close now unused CA%d device", demux_id, i);
										int32_t ret = close(currentfd);
										if(ret < 0)
										{
											cs_log("ERROR: Could not close demuxer fd (errno=%d %s)", errno, strerror(errno));
										}
										ca_fd[i] = 0;
									}
								}
							}
						}
					}
				}
			}
			break;
	}
	return;
}

void dvbapi_stop_all_descrambling(uint32_t msgid)
{
	int32_t j;

	for(j = 0; j < MAX_DEMUX; j++)
	{
		if(demux[j].program_number == 0)
		{
			continue;
		}

		dvbapi_stop_descrambling(j, msgid);
	}
}

void dvbapi_stop_all_cat_emm_sdt_filtering(uint32_t msgid)
{
	int32_t j;

	for(j = 0; j < MAX_DEMUX; j++)
	{
		if(demux[j].program_number == 0)
		{
			continue;
		}

		dvbapi_stop_filter(j, TYPE_EMM, msgid);
		dvbapi_stop_filter(j, TYPE_SDT, msgid);
		dvbapi_stop_filter(j, TYPE_CAT, msgid);
		demux[j].emm_filter = -1;
	}
}

void dvbapi_stop_descrambling(int32_t demux_id, uint32_t msgid)
{
	int32_t i, j, z;
	if(demux[demux_id].program_number == 0) { return; }
	char channame[CS_SERVICENAME_SIZE];

	i = demux[demux_id].pidindex;
	if(i < 0)
	{
		i = 0;
	}

	demux[demux_id].pidindex = -1; // no ecmpid is to be descrambling since we start stop descrambling!

	get_servicename(dvbapi_client, demux[demux_id].program_number,
			demux[demux_id].ECMpidcount > 0 ? demux[demux_id].ECMpids[i].PROVID : NO_PROVID_VALUE,
			demux[demux_id].ECMpidcount > 0 ? demux[demux_id].ECMpids[i].CAID : NO_CAID_VALUE,
			channame, sizeof(channame));

	cs_log("Demuxer %d stopped descrambling for program %04X (%s)",
			demux_id, demux[demux_id].program_number, channame);

	dvbapi_stop_filter(demux_id, TYPE_EMM, msgid);
	dvbapi_stop_filter(demux_id, TYPE_SDT, msgid);
	dvbapi_stop_filter(demux_id, TYPE_PAT, msgid);
	dvbapi_stop_filter(demux_id, TYPE_PMT, msgid);
	dvbapi_stop_filter(demux_id, TYPE_CAT, msgid);

	for(i = 0; i < demux[demux_id].ECMpidcount && demux[demux_id].ECMpidcount > 0; i++)
	{
		for(j = 0; j < MAX_STREAM_INDICES; j++)
		{
			if(demux[demux_id].ECMpids[i].index[j] == INDEX_INVALID)
			{
				continue;
			}

			// disable streams!
			for(z = 0; z < demux[demux_id].STREAMpidcount; z++)
			{
				dvbapi_set_pid(demux_id, z, demux[demux_id].ECMpids[i].index[j], false, false, msgid); // disable streampid
			}
			demux[demux_id].ECMpids[i].index[j] = INDEX_INVALID;
		}
	}
	dvbapi_stop_filter(demux_id, TYPE_ECM, msgid);

	pthread_mutex_destroy(&demux[demux_id].answerlock);
	memset(&demux[demux_id], 0 , sizeof(DEMUXTYPE));

	SAFE_MUTEX_INIT(&demux[demux_id].answerlock, NULL);
	for(i = 0; i < MAX_ECM_PIDS; i++)
	{
		for(j = 0; j < MAX_STREAM_INDICES; j++)
		{
			demux[demux_id].ECMpids[i].index[j] = INDEX_INVALID;
		}
	}
	demux[demux_id].pidindex = -1;
	demux[demux_id].curindex = -1;

	if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX)
	{
		unlink(ECMINFO_FILE);
	}
	return;
}

int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked, uint32_t msgid)
{
	int32_t started = 0; // in case ecmfilter started = 1
	int32_t fake_ecm = 0;
	ECM_REQUEST *er;
	struct s_reader *rdr;

	if(!(er = get_ecmtask()))
	{
		return started;
	}
	demux[demux_id].ECMpids[pid].checked = checked + 1; // mark this pid as checked!

	struct s_dvbapi_priority *p;
	for(p = dvbapi_priority; p != NULL ; p = p->next)
	{
		if((p->type != 'p')
			|| (p->caid && p->caid != demux[demux_id].ECMpids[pid].CAID)
			|| (p->provid && p->provid != demux[demux_id].ECMpids[pid].PROVID)
			|| (p->ecmpid && p->ecmpid != demux[demux_id].ECMpids[pid].ECM_PID)
			|| (p->srvid && p->srvid != demux[demux_id].program_number)
			|| (p->pidx && p->pidx-1 != pid)
			|| (p->cadata && p->cadata != demux[demux_id].ECMpids[pid].cadata))
		{
			continue;
		}

		// if found chid and first run apply chid filter, on forced pids always apply!
		if(p->type == 'p' && p->chid < 0x10000 && (demux[demux_id].ECMpids[pid].checked == 1 || (p && p->force)))
		{
			if(demux[demux_id].ECMpids[pid].CHID < 0x10000) // channelcache delivered chid
			{
				er->chid = demux[demux_id].ECMpids[pid].CHID;
			}
			else
			{
				er->chid = p->chid; // no channelcache or no chid in use, so use prio chid
				demux[demux_id].ECMpids[pid].CHID = p->chid;
			}
			//cs_log("********* CHID %04X **************", demux[demux_id].ECMpids[pid].CHID);
			break; // we only accept one!
		}
		else
		{
			if(demux[demux_id].ECMpids[pid].CHID < 0x10000) // channelcache delivered chid
			{
				er->chid = demux[demux_id].ECMpids[pid].CHID;
			}
			else // no channelcache or no chid in use
			{
				er->chid = 0;
				demux[demux_id].ECMpids[pid].CHID = 0x10000;
			}
		}
	}

	er->srvid  = demux[demux_id].program_number;
	er->caid   = demux[demux_id].ECMpids[pid].CAID;
	er->pid    = demux[demux_id].ECMpids[pid].ECM_PID;
	er->prid   = demux[demux_id].ECMpids[pid].PROVID;
	er->vpid   = demux[demux_id].ECMpids[pid].VPID;
	er->pmtpid = demux[demux_id].pmtpid;
	er->onid   = demux[demux_id].onid;
	er->tsid   = demux[demux_id].tsid;
	er->ens    = demux[demux_id].ens;
	er->msgid  = msgid;

#ifdef WITH_STAPI5
	cs_strncpy(er->dev_name, dev_list[demux[demux_id].dev_index].name, sizeof(dev_list[demux[demux_id].dev_index].name));
#endif

	for(rdr = first_active_reader; rdr != NULL ; rdr = rdr->next)
	{
		int8_t match = matching_reader(er, rdr); // check for matching reader

		if(p && p->force)
		{
			match = 1; // forced pid always started!
		}

		if(!match) // if this reader does not match, check betatunnel for it
		{
			match = lb_check_auto_betatunnel(er, rdr);
		}

		if(!match && chk_is_betatunnel_caid(er->caid)) // these caids might be tunneled invisible by peers
		{
			match = 1; // so make it a match to try it!
		}

		// check if cache-ex is matching
		if(config_enabled(CS_CACHEEX) && (!match && (cacheex_is_match_alias(dvbapi_client, er))))
		{
			match = 1; // so make it a match to try it!
		}

		// BISS1 and BISS2 mode 1/E or FAKE caid
		// ecm pid is fake, so send out one fake ecm request
		// special treatment: if we asked the cw first without starting a filter,
		// the cw request will be killed due to no ecmfilter started
		if(caid_is_fake(demux[demux_id].ECMpids[pid].CAID) || caid_is_biss_fixed(demux[demux_id].ECMpids[pid].CAID))
		{
			int32_t j, n;
			er->ecmlen = 7;
			er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81
			er->ecm[1] = 0x00;
			er->ecm[2] = 0x04;
			i2b_buf(2, er->srvid, er->ecm + 3);
			i2b_buf(2, er->pmtpid, er->ecm + 5);

			for(j = 0, n = 7; j < demux[demux_id].STREAMpidcount; j++, n += 2)
			{
				i2b_buf(2, demux[demux_id].STREAMpids[j], er->ecm + n);
				er->ecm[2] += 2;
				er->ecmlen += 2;
			}

			er->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging
			er->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid

			i2b_buf(2, er->tsid, er->ecm + er->ecmlen);     // place tsid after the last stream pid
			i2b_buf(2, er->onid, er->ecm + er->ecmlen + 2); // place onid right after tsid
			i2b_buf(4, er->ens, er->ecm + er->ecmlen + 4);  // place namespace at the end of the ecm

			er->ecm[2] += 8;
			er->ecmlen += 8;

			cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X",
				demux_id,
				pid,
				demux[demux_id].ECMpids[pid].CAID,
				demux[demux_id].ECMpids[pid].PROVID,
				demux[demux_id].ECMpids[pid].ECM_PID,
				demux[demux_id].pmtpid,
				demux[demux_id].ECMpids[pid].VPID);

			demux[demux_id].curindex = pid; // set current pid to the fresh started one
			dvbapi_start_filter(demux_id,
						pid,
						demux[demux_id].ECMpids[pid].ECM_PID,
						demux[demux_id].ECMpids[pid].CAID,
						demux[demux_id].ECMpids[pid].PROVID,
						0x80,
						0xF0,
						3000,
						TYPE_ECM);

			started = 1;
			request_cw(dvbapi_client, er, demux_id, 0); // do not register ecm since this try!
			fake_ecm = 1;
			break; // we started an ecmfilter so stop looking for next matching reader!
		}

		if(match) // if matching reader found check for irdeto cas if local irdeto card check if it received emms in last 60 minutes
		{
			if(caid_is_irdeto(er->caid)) // irdeto cas init irdeto_curindex to wait for first index (00)
			{
				if(demux[demux_id].ECMpids[pid].irdeto_curindex == 0xFE)
				{
					demux[demux_id].ECMpids[pid].irdeto_curindex = 0x00;
				}
			}

			if(p && p->chid < 0x10000) // do we prio a certain chid?
			{
				cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X CHID %04X PMTPID %04X VPID %04X",
					demux_id, pid,
					demux[demux_id].ECMpids[pid].CAID,
					demux[demux_id].ECMpids[pid].PROVID,
					demux[demux_id].ECMpids[pid].ECM_PID,
					demux[demux_id].ECMpids[pid].CHID,
					demux[demux_id].pmtpid,
					demux[demux_id].ECMpids[pid].VPID);
			}
			else
			{
				cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X",
					demux_id, pid,
					demux[demux_id].ECMpids[pid].CAID,
					demux[demux_id].ECMpids[pid].PROVID,
					demux[demux_id].ECMpids[pid].ECM_PID,
					demux[demux_id].pmtpid,
					demux[demux_id].ECMpids[pid].VPID);
			}

			demux[demux_id].curindex = pid; // set current pid to the fresh started one
			dvbapi_start_filter(demux_id,
						pid,
						demux[demux_id].ECMpids[pid].ECM_PID,
						demux[demux_id].ECMpids[pid].CAID,
						demux[demux_id].ECMpids[pid].PROVID,
						0x80,
						0xF0,
						3000,
						TYPE_ECM);

			started = 1;
			break; // we started an ecmfilter so stop looking for next matching reader!
		}
	}

	if(demux[demux_id].curindex != pid)
	{
		cs_log("Demuxer %d impossible to descramble PID %d CAID %04X PROVID %06X ECMPID %04X PMTPID %04X (NO MATCHING READER)",
			demux_id, pid,
			demux[demux_id].ECMpids[pid].CAID,
			demux[demux_id].ECMpids[pid].PROVID,
			demux[demux_id].ECMpids[pid].ECM_PID,
			demux[demux_id].pmtpid);

		demux[demux_id].ECMpids[pid].checked = 4; // flag this pid as checked
		demux[demux_id].ECMpids[pid].status = -1; // flag this pid as unusable
		dvbapi_edit_channel_cache(demux_id, pid, 0); // remove this pid from channelcache
	}

	if(!fake_ecm)
	{
		NULLFREE(er);
	}
	return started;
}

struct s_dvbapi_priority *dvbapi_check_prio_match_emmpid(int32_t demux_id, uint16_t caid, uint32_t provid, char type)
{
	struct s_dvbapi_priority *p;
	int32_t i;
	uint16_t ecm_pid = 0;

	for(i = 0; i < demux[demux_id].ECMpidcount; i++)
	{
		if((demux[demux_id].ECMpids[i].CAID == caid) && (demux[demux_id].ECMpids[i].PROVID == provid))
		{
			ecm_pid = demux[demux_id].ECMpids[i].ECM_PID;
			break;
		}
	}

	if(!ecm_pid)
	{
		return NULL;
	}

	for(p = dvbapi_priority; p != NULL; p = p->next)
	{
		if(p->type != type
			|| (p->caid && p->caid != caid)
			|| (p->provid && p->provid != provid)
			|| (p->ecmpid && p->ecmpid != ecm_pid)
			|| (p->srvid && p->srvid != demux[demux_id].program_number)
			|| (p->pidx && p->pidx-1 !=i)
			|| (p->type == 'i' && (p->chid < 0x10000)))
		{
			continue;
		}

		return p;
	}
	return NULL;
}

struct s_dvbapi_priority *dvbapi_check_prio_match(int32_t demux_id, int32_t pidindex, char type)
{
	if(!dvbapi_priority)
	{
		return NULL;
	}

	struct s_dvbapi_priority *p;
	struct s_ecmpid *ecmpid = &demux[demux_id].ECMpids[pidindex];

	for(p = dvbapi_priority; p != NULL; p = p->next)
	{
		if(p->type != type
			|| (p->caid && p->caid != ecmpid->CAID)
			|| (p->provid && p->provid != ecmpid->PROVID)
			|| (p->ecmpid && p->ecmpid != ecmpid->ECM_PID)
			|| (p->srvid && p->srvid != demux[demux_id].program_number)
			|| (p->pidx && p->pidx-1 != pidindex)
			|| (p->chid < 0x10000 && p->chid != ecmpid->CHID))
		{
			continue;
		}

		return p;
	}
	return NULL;
}

void dvbapi_process_emm(int32_t demux_id, int32_t filter_num, uint8_t *buffer, uint32_t len)
{
	EMM_PACKET epg;

	struct s_emm_filter *filter = get_emmfilter_by_filternum(demux_id, filter_num + 1); // 0 is used for pending emmfilters, so everything increase 1
	if(!filter)
	{
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d no filter matches -> SKIP!", demux_id, filter_num + 1);
		return;
	}

	uint32_t provider = filter->provid;
	uint16_t caid = filter->caid;
	struct s_dvbapi_priority *mapentry = dvbapi_check_prio_match_emmpid(filter->demux_id, filter->caid, filter->provid, 'm');
	if(mapentry)
	{
		cs_log_dbg(D_DVBAPI, "Demuxer %d mapping EMM from %04X@%06X to %04X@%06X",
			demux_id, caid, provider, mapentry->mapcaid, mapentry->mapprovid);

		caid = mapentry->mapcaid;
		provider = mapentry->mapprovid;
	}

	memset(&epg, 0, sizeof(epg));
	i2b_buf(2, caid, epg.caid);
	i2b_buf(4, provider, epg.provid);
	epg.emmlen = len > sizeof(epg.emm) ? sizeof(epg.emm) : len;
	memcpy(epg.emm, buffer, epg.emmlen);

	if(config_enabled(READER_IRDETO) && chk_is_betatunnel_caid(caid) == 2)
	{
		uint16_t ncaid = tunemm_caid_map(FROM_TO, caid, demux[demux_id].program_number);
		if(caid != ncaid)
		{
			irdeto_add_emm_header(&epg);
			i2b_buf(2, ncaid, epg.caid);
		}
	}
	do_emm(dvbapi_client, &epg);
}

void dvbapi_read_priority(void)
{
	FILE *fp;
	char token[128], str1[128];
	char type;
	int32_t i, ret, count = 0;
	const char *cs_prio = "oscam.dvbapi";

	fp = fopen(get_config_filename(token, sizeof(token), cs_prio), "r");
	if(!fp)
	{
		cs_log_dbg(D_DVBAPI, "ERROR: Can't open priority file %s", token);
		return;
	}

	if(dvbapi_priority)
	{
		cs_log_dbg(D_DVBAPI, "reread priority file %s", cs_prio);
		struct s_dvbapi_priority *o, *p;
		for(p = dvbapi_priority; p != NULL; p = o)
		{
			o = p->next;
			NULLFREE(p);
		}
		dvbapi_priority = NULL;
	}

	while(fgets(token, sizeof(token), fp))
	{
		// Ignore comments and empty lines
		if(token[0] == '#' || token[0] == '/' || token[0] == '\n' || token[0] == '\r' || token[0] == '\0' || cs_strlen(token) > 100)
		{
			continue;
		}
		memset(str1, 0, 128);

		for(i = 0; i < (int)cs_strlen(token) && token[i] == ' '; i++) { ; }

		if(i == (int)cs_strlen(token) - 1) // empty line or all spaces
		{
			continue;
		}

		for(i = 0; i < (int)cs_strlen(token); i++)
		{
			if(token[i] == '@')
			{
				token[i] = ':';
			}
		}

		for(i = 0; i < (int)cs_strlen(token); i++)
		{
			if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') // if "::" or " :"
			{
				memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); // insert extra position
				token[i + 1] = '0'; // and fill it with NULL
			}

			if(token[i] == '#' || token[i] == '/')
			{
				token[i] = '\0';
				break;
			}
		}

		type = 0;
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		uint32_t disablefilter = 0;
		ret = sscanf(trim(token), "%c: %63s %63s %d", &type, str1, str1 + 64, &disablefilter);
#else
		ret = sscanf(trim(token), "%c: %63s %63s", &type, str1, str1 + 64);
#endif
		type = tolower((uint8_t)type);

		if(ret < 1 || (type != 'p' && type != 'i' && type != 'm' && type != 'd' &&
			type != 's' && type != 'l' && type != 'j' && type != 'a' && type != 'x'))
		{
			//fprintf(stderr, "Warning: line containing %s in %s not recognized, ignoring line\n", token, cs_prio);
			// fprintf would issue the warning to the command line, which is more consistent with other config warnings
			// however it takes OSCam a long time (>4 seconds) to reach this part of the program, so the warnings are
			// reaching tty rather late which leads to confusion. So send the warnings to log file instead
			cs_log_dbg(D_DVBAPI, "WARN: line containing %s in %s not recognized, ignoring...", token, cs_prio);
			continue;
		}

		struct s_dvbapi_priority *entry;
		if(!cs_malloc(&entry, sizeof(struct s_dvbapi_priority)))
		{
			ret = fclose(fp);
			if(ret < 0)
			{
				cs_log("ERROR: Could not close oscam.dvbapi fd (errno=%d %s)", errno, strerror(errno));
			}
			return;
		}

		entry->type = type;
		entry->next = NULL;
		count++;

#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		if(type == 's')
		{
			cs_strncpy(entry->devname, str1, sizeof(entry->devname));
			cs_strncpy(entry->pmtfile, str1 + 64, sizeof(entry->pmtfile));
			entry->disablefilter = disablefilter;
			cs_log_dbg(D_DVBAPI, "stapi prio: ret=%d | %c: %s %s | disable %d",
				ret, type, entry->devname, entry->pmtfile, disablefilter);

			if(!dvbapi_priority)
			{
				dvbapi_priority = entry;
			}
			else
			{
				struct s_dvbapi_priority *p;
				for(p = dvbapi_priority; p->next != NULL; p = p->next) { ; }
				p->next = entry;
			}
			continue;
		}
#endif
		char c_srvid[34];
		c_srvid[0] = '\0';
		uint32_t caid = 0, provid = 0, srvid = 0, ecmpid = 0, cadata = 0;;
		uint32_t chid = 0x10000; //chid=0 is a valid chid

		ret = sscanf(str1, "%4x:%6x:%33[^:]:%4x:%4x:%8x"SCNx16, &caid, &provid, c_srvid, &ecmpid, &chid, &cadata);
		if(ret < 1)
		{
			cs_log("Error in oscam.dvbapi: ret=%d | %c: %04X %06X %s %04X %04X",
				ret, type, caid, provid, c_srvid, ecmpid, chid);
			continue; // skip this entry!
		}
		else
		{
			cs_log_dbg(D_DVBAPI, "Parsing rule: ret=%d | %c: %04X %06X %s %04X %04X %04X",
				ret, type, caid, provid, c_srvid, ecmpid, chid, cadata);
		}

		entry->caid = caid;
		entry->provid = provid;
		entry->ecmpid = ecmpid;
		entry->chid = chid;
		entry->cadata = cadata;
		uint32_t delay = 0, force = 0, mapcaid = 0, mapprovid = 0, mapecmpid = 0, pidx = 0;

		switch(type)
		{
			case 'i':
				ret = sscanf(str1 + 64, "%1d", &pidx);
				entry->pidx = pidx + 1;
				if(ret < 1) entry->pidx = 0;
				break;

			case 'd':
				sscanf(str1 + 64, "%4d", &delay);
				entry->delay = delay;
				break;

			case 'l':
				entry->delay = dyn_word_atob(str1 + 64);
				if(entry->delay == -1) { entry->delay = 0; }
				break;

			case 'p':
				ret = sscanf(str1 + 64, "%1d:%1d", &force, &pidx);
				entry->force = force;
				entry->pidx = pidx + 1;
				if(ret < 2) entry->pidx = 0;
				break;

			case 'm':
				sscanf(str1 + 64, "%4x:%6x", &mapcaid, &mapprovid);
				if(!mapcaid) { mapcaid = 0xFFFF; }
				entry->mapcaid = mapcaid;
				entry->mapprovid = mapprovid;
				break;

			case 'a':
			case 'j':
				sscanf(str1 + 64, "%4x:%6x:%4x", &mapcaid, &mapprovid, &mapecmpid);
				if(!mapcaid) { mapcaid = 0xFFFF; }
				entry->mapcaid = mapcaid;
				entry->mapprovid = mapprovid;
				entry->mapecmpid = mapecmpid;
				break;
		}

		if(c_srvid[0] == '=')
		{
			struct s_srvid *this;
			for(i = 0; i < 16; i++)
			{
				for(this = cfg.srvid[i]; this != NULL; this = this->next)
				{
					if(this->name && strcmp(this->name, c_srvid + 1) == 0)
					{
						struct s_dvbapi_priority *entry2;
						if(!cs_malloc(&entry2, sizeof(struct s_dvbapi_priority)))
						{
							continue;
						}

						memcpy(entry2, entry, sizeof(struct s_dvbapi_priority));
						entry2->srvid = this->srvid;
						cs_log_dbg(D_DVBAPI, "prio srvid: ret=%d | %c: %04X %06X %04X %04X %04X -> map %04X %06X %04X | prio %d | delay %d",
								ret,
								entry2->type,
								entry2->caid,
								entry2->provid,
								entry2->srvid,
								entry2->ecmpid,
								entry2->chid,
								entry2->mapcaid,
								entry2->mapprovid,
								entry2->mapecmpid,
								entry2->force,
								entry2->delay);

						if(!dvbapi_priority)
						{
							dvbapi_priority = entry2;
						}
						else
						{
							struct s_dvbapi_priority *p;
							for(p = dvbapi_priority; p->next != NULL; p = p->next) { ; }
							p->next = entry2;
						}
					}
				}
			}
			NULLFREE(entry);
			continue;
		}
		else
		{
			sscanf(c_srvid, "%4x", &srvid);
			entry->srvid = srvid;
		}
		cs_log_dbg(D_DVBAPI, "prio: ret=%d | %c: %04X %06X %04X %04X %04X -> map %04X %06X %04X | prio %d | delay %d",
				ret,
				entry->type,
				entry->caid,
				entry->provid,
				entry->srvid,
				entry->ecmpid,
				entry->chid,
				entry->mapcaid,
				entry->mapprovid,
				entry->mapecmpid,
				entry->force,
				entry->delay);

		if(!dvbapi_priority)
		{
			dvbapi_priority = entry;
		}
		else
		{
			struct s_dvbapi_priority *p;
			for(p = dvbapi_priority; p->next != NULL; p = p->next) { ; }
			p->next = entry;
		}
	}
	cs_log_dbg(D_DVBAPI, "Read %d entries from %s", count, cs_prio);

	ret = fclose(fp);
	if(ret < 0)
	{
		cs_log("ERROR: Could not close oscam.dvbapi fd (errno=%d %s)", errno, strerror(errno));
	}
	return;
}

void dvbapi_resort_ecmpids(int32_t demux_id)
{
	int32_t n, cache = 0, matching_done = 0, found = -1, match_reader_count = 0, total_reader = 0;
	uint16_t btun_caid = 0;
	struct timeb start,end;
	cs_ftime(&start);

	for(n = 0; n < demux[demux_id].ECMpidcount; n++)
	{
		demux[demux_id].ECMpids[n].status = 0;
		demux[demux_id].ECMpids[n].checked = 0;
		demux[demux_id].ECMpids[n].irdeto_curindex = 0xFE;
		demux[demux_id].ECMpids[n].irdeto_maxindex = 0;
		demux[demux_id].ECMpids[n].irdeto_cycle = 0xFE;
		demux[demux_id].ECMpids[n].tries = 0xFE;
		demux[demux_id].ECMpids[n].table = 0;
	}

	demux[demux_id].max_status = 0;
	demux[demux_id].curindex = -1;
	demux[demux_id].pidindex = -1;

	struct s_reader *rdr;
	int32_t p_order = demux[demux_id].ECMpidcount + 1;
	struct s_dvbapi_priority *prio;

	// handle prio order in oscam.dvbapi + ignore all chids
	for(rdr = first_active_reader; rdr ; rdr = rdr->next)
	{
		total_reader++; // only need to calculate once!
	}

	ECM_REQUEST *er;
	if(!cs_malloc(&er, sizeof(ECM_REQUEST)))
	{
		return;
	}

	for(prio = dvbapi_priority; prio != NULL; prio = prio->next)
	{
		if(prio->type != 'p' && prio->type != 'i')
		{
			continue;
		}

		for(n = 0; n < demux[demux_id].ECMpidcount; n++)
		{
			if(demux[demux_id].ECMpids[n].status == -1) continue; // skip ignores!

			er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID;
			er->prid = demux[demux_id].ECMpids[n].PROVID;
			er->pid = demux[demux_id].ECMpids[n].ECM_PID;
			er->srvid = demux[demux_id].program_number;
			er->client = cur_client();
			btun_caid = chk_on_btun(SRVID_MASK, er->client, er);

			if(prio->type == 'p' && btun_caid)
			{
				er->caid = btun_caid;
			}

			if((prio->caid && (prio->caid != er->caid && prio->caid != er->ocaid))
				|| (prio->provid && prio->provid != er->prid)
				|| (prio->srvid && prio->srvid != er->srvid)
				|| (prio->ecmpid && prio->ecmpid != er->pid)
				|| (prio->pidx && prio->pidx - 1 != n))
			{
				continue;
			}

			if(prio->type == 'p') // check for prio
			{
				if(prio->cadata != 0 && prio->cadata != demux[demux_id].ECMpids[n].cadata)
				{
					continue;
				}

				if(prio->chid < 0x10000)
				{
					demux[demux_id].ECMpids[n].CHID = prio->chid;
				}

				if(prio->force)
				{
					int32_t j;
					for(j = 0; j < demux[demux_id].ECMpidcount; j++)
					{
						demux[demux_id].ECMpids[j].status = -1;
					}

					demux[demux_id].ECMpids[n].status = 1;
					demux[demux_id].ECMpids[n].checked = 0;
					demux[demux_id].max_status = 1;
					demux[demux_id].max_emm_filter = maxfilter - 1;

					cs_log_dbg(D_DVBAPI, "Demuxer %d prio forced%s ecmpid %d %04X@%06X:%04X:%04X (file)",
							demux_id,
							((prio->caid == er->caid && prio->caid != er->ocaid) ? " betatunneled" : ""),
							n,
							demux[demux_id].ECMpids[n].CAID,
							demux[demux_id].ECMpids[n].PROVID,
							demux[demux_id].ECMpids[n].ECM_PID,
							(uint16_t) prio->chid);
					NULLFREE(er);
					return; // go start descrambling since its forced by user!
				}
				else
				{
					if(!demux[demux_id].ECMpids[n].status) // only accept first matching prio from oscam.dvbapi
					{
						demux[demux_id].ECMpids[n].status = total_reader + p_order--;
						matching_done = 1;
						cs_log_dbg(D_DVBAPI, "Demuxer %d prio%s ecmpid %d %04X@%06X:%04X:%04X weight: %d (file)",
								demux_id,
								((prio->caid == er->caid && prio->caid != er->ocaid) ? " betatunneled" : ""),
								n,
								demux[demux_id].ECMpids[n].CAID,
								demux[demux_id].ECMpids[n].PROVID,
								demux[demux_id].ECMpids[n].ECM_PID,
								(uint16_t) prio->chid,
								demux[demux_id].ECMpids[n].status);
					}
					continue; // evaluate next ecmpid
				}
			}

			// check for ignore all chids
			if(prio->type == 'i' && prio->chid == 0x10000 && demux[demux_id].ECMpids[n].status == 0)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d ignore ecmpid %d %04X@%06X:%04X all chids (file)",
						demux_id,
						n,
						demux[demux_id].ECMpids[n].CAID,
						demux[demux_id].ECMpids[n].PROVID,
						demux[demux_id].ECMpids[n].ECM_PID);
				demux[demux_id].ECMpids[n].status = -1;
				continue; // evaluate next ecmpid
			}
		}
	}

	for(n = 0; n < demux[demux_id].ECMpidcount; n++)
	{
		if(demux[demux_id].ECMpids[n].status == -1)
		{
			continue; // skip ignores!
		}

		int32_t nr;
		SIDTAB *sidtab;

		er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID;
		er->prid = demux[demux_id].ECMpids[n].PROVID;
		er->pid = demux[demux_id].ECMpids[n].ECM_PID;
		er->srvid = demux[demux_id].program_number;
		er->client = cur_client();

		btun_caid = chk_on_btun(SRVID_MASK, er->client, er);
		if(btun_caid)
		{
			er->caid = btun_caid;
		}

		match_reader_count = 0;

		for(rdr = first_active_reader; rdr ; rdr = rdr->next)
		{
			if(matching_reader(er, rdr))
			{
				match_reader_count++;
			}
		}

		if(match_reader_count == 0)
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d ignore ecmpid %d %04X@%06X:%04X:%04X (no matching reader)",
					demux_id,
					n,
					demux[demux_id].ECMpids[n].CAID,
					demux[demux_id].ECMpids[n].PROVID,
					demux[demux_id].ECMpids[n].ECM_PID,
					demux[demux_id].ECMpids[n].CHID);
			demux[demux_id].ECMpids[n].status = -1;
			continue; // evaluate next ecmpid
		}
		else // ecmpids with no matching readers are disabled and matching sidtabbits have now highest status
		{
			for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++)
			{
				if(sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid)
				{
					if((cfg.dvbapi_sidtabs.no & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab)))
					{
						demux[demux_id].ECMpids[n].status = -1; //ignore
						cs_log_dbg(D_DVBAPI, "Demuxer %d ignore ecmpid %d %04X@%06X:%04X (service %s pos %d)",
								demux_id,
								n,
								demux[demux_id].ECMpids[n].CAID,
								demux[demux_id].ECMpids[n].PROVID,
								demux[demux_id].ECMpids[n].ECM_PID,
								sidtab->label,
								nr);
						continue; // evaluate next ecmpid
					}
					if((cfg.dvbapi_sidtabs.ok & ((SIDTABBITS) 1 << nr)) && (chk_srvid_match(er, sidtab)))
					{
						demux[demux_id].ECMpids[n].status++; // priority
						cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X weight: %d (service %s pos %d)",
								demux_id,
								n,
								demux[demux_id].ECMpids[n].CAID,
								demux[demux_id].ECMpids[n].PROVID,
								demux[demux_id].ECMpids[n].ECM_PID,
								demux[demux_id].ECMpids[n].status,
								sidtab->label,
								nr);
					}
				}
			}
		}
	}

	// ecmpid with highest prio from oscam.dvbapi has now highest status
	// check all ecmpids and get the highest amount cache-ex and local readers
	int32_t max_local_matching_reader = 0, max_cacheex_reader = 0;

	for(n = 0; n < demux[demux_id].ECMpidcount; n++)
	{
		int32_t count_matching_cacheex_reader = 0, count_matching_local_reader = 0;
		if(demux[demux_id].ECMpids[n].status == -1)
		{
			continue; // skip ignores!
		}

		er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID;
		er->prid = demux[demux_id].ECMpids[n].PROVID;
		er->pid = demux[demux_id].ECMpids[n].ECM_PID;
		er->srvid = demux[demux_id].program_number;
		er->client = cur_client();

		btun_caid = chk_on_btun(SRVID_MASK, er->client, er);
		if(btun_caid)
		{
			er->caid = btun_caid;
		}

		for(rdr = first_active_reader; rdr ; rdr = rdr->next)
		{
			if(matching_reader(er, rdr))
			{
				if(cacheex_reader(rdr))
				{
					count_matching_cacheex_reader++;
				}
				else if(is_localreader(rdr, er))
				{
					count_matching_local_reader++;
				}
			}
		}

		if(max_local_matching_reader < count_matching_local_reader)
		{
			max_local_matching_reader = count_matching_local_reader;
		}

		if(max_cacheex_reader < count_matching_cacheex_reader)
		{
			max_cacheex_reader = count_matching_cacheex_reader;
		}
	}

	if(max_local_matching_reader != 0 || max_cacheex_reader != 0)
	{
		p_order = demux[demux_id].ECMpidcount * 2;
		for(n = 0; n < demux[demux_id].ECMpidcount; n++)
		{
			int32_t count_matching_cacheex_reader = 0, count_matching_local_reader = 0;
			int32_t localprio = 1, cacheexprio = 1;

			if(demux[demux_id].ECMpids[n].status == -1)
			{
				continue; // skip ignores!
			}

			if(cfg.preferlocalcards == 2) // ecmpids with local reader get highest prio
			{
				localprio = max_cacheex_reader + p_order + 1;
			}
			else if(cfg.preferlocalcards == 1) // ecmpids with cacheex reader get highest prio
			{
				cacheexprio = max_local_matching_reader + p_order + 1;
			}

			er->caid = er->ocaid = demux[demux_id].ECMpids[n].CAID;
			er->prid = demux[demux_id].ECMpids[n].PROVID;
			er->pid = demux[demux_id].ECMpids[n].ECM_PID;
			er->srvid = demux[demux_id].program_number;
			er->client = cur_client();

			btun_caid = chk_on_btun(SRVID_MASK, er->client, er);
			if(btun_caid)
			{
				er->caid = btun_caid;
			}

			int32_t oldstatus = demux[demux_id].ECMpids[n].status;
			int32_t anyreader = 0;

			for(rdr = first_active_reader; rdr; rdr = rdr->next)
			{
				if(matching_reader(er, rdr))
				{
					if(cfg.preferlocalcards == 0)
					{
						if(!matching_done)
						{
							demux[demux_id].ECMpids[n].status++;
						}
						anyreader++;
						continue;
					}

					if(cacheex_reader(rdr))
					{
						demux[demux_id].ECMpids[n].status += cacheexprio;
						count_matching_cacheex_reader++;
						cacheexprio = 1;
					}

					if(is_localreader(rdr, er))
					{
						demux[demux_id].ECMpids[n].status += localprio;
						count_matching_local_reader++;
						localprio = 1;
					}
				}
			}

			if(oldstatus != demux[demux_id].ECMpids[n].status)
			{
				if(anyreader)
				{
					cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X:%04X weight: %d (%d readers)",
							demux_id,
							n,
							demux[demux_id].ECMpids[n].CAID,
							demux[demux_id].ECMpids[n].PROVID,
							demux[demux_id].ECMpids[n].ECM_PID,
							demux[demux_id].ECMpids[n].CHID,
							demux[demux_id].ECMpids[n].status,
							anyreader);
				}
				else
				{
					cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X:%04X weight: %d (%d local and %d cacheex readers)",
							demux_id,
							n,
							demux[demux_id].ECMpids[n].CAID,
							demux[demux_id].ECMpids[n].PROVID,
							demux[demux_id].ECMpids[n].ECM_PID,
							demux[demux_id].ECMpids[n].CHID,
							demux[demux_id].ECMpids[n].status,
							count_matching_local_reader,
							count_matching_cacheex_reader);
				}
			}
		}
	}

	struct s_channel_cache *c = NULL;

	for(n = 0; n < demux[demux_id].ECMpidcount && matching_done == 0; n++)
	{
		if(demux[demux_id].ECMpids[n].status == -1)
		{
			continue; // skip ignores!
		}

		c = dvbapi_find_channel_cache(demux_id, n, 0); // find exact channel match
		if(c != NULL)
		{
			found = n;
			cache = 2; // found cache entry with higher priority
			demux[demux_id].ECMpids[n].status++; // prioritize CAIDs which already decoded same caid:provid:srvid

			if(c->chid < 0x10000)
			{
				demux[demux_id].ECMpids[n].CHID = c->chid; // if chid registered in cache -> use it!
			}
			cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X weight: %d (found caid/provid/srvid in cache)",
					demux_id,
					n,
					demux[demux_id].ECMpids[n].CAID,
					demux[demux_id].ECMpids[n].PROVID,
					demux[demux_id].ECMpids[n].ECM_PID,
					demux[demux_id].ECMpids[n].status);
			break;
		}
	}

	if(found == -1)
	{
		// prioritize CAIDs which already decoded same caid:provid
		for(n = 0; n < demux[demux_id].ECMpidcount && matching_done == 0; n++)
		{
			if(demux[demux_id].ECMpids[n].status == -1) continue; // skip ignores!

			c = dvbapi_find_channel_cache(demux_id, n, 1);
			if(c != NULL)
			{
				cache = 1; //found cache entry
				demux[demux_id].ECMpids[n].status++;
				cs_log_dbg(D_DVBAPI, "Demuxer %d prio ecmpid %d %04X@%06X:%04X weight: %d (found caid/provid in cache)",
						demux_id,
						n,
						demux[demux_id].ECMpids[n].CAID,
						demux[demux_id].ECMpids[n].PROVID,
						demux[demux_id].ECMpids[n].ECM_PID,
						demux[demux_id].ECMpids[n].status);
			}
		}
	}

	int32_t max_status = 0;
	int32_t highest_priopid = -1;

	for(n = 0; n < demux[demux_id].ECMpidcount; n++)
	{
		if(demux[demux_id].ECMpids[n].status == -1)
		{
			continue; // skip ignores!
		}

		if(demux[demux_id].ECMpids[n].status > max_status) // find highest prio pid
		{
			max_status = demux[demux_id].ECMpids[n].status;
			highest_priopid = n;
		}

		if(!USE_OPENXCAS) // openxcas doesnt use prio and non-prio run: all are equal!
		{
			if(demux[demux_id].ECMpids[n].status == 0)
			{
				demux[demux_id].ECMpids[n].checked = 2; // set pids with no status to no prio run
			}
		}
	}

	demux[demux_id].max_status = max_status; // register maxstatus

	// Found entry in channelcache that is valid and has exact match on srvid
	if(highest_priopid != -1 && found == highest_priopid && cache == 2)
	{
		for(n = 0; n < demux[demux_id].ECMpidcount; n++)
		{
			if(demux[demux_id].ECMpids[n].status == -1)
			{
				continue; // skip ignores!
			}

			if(n != found)
			{
				// disable non matching pid
				demux[demux_id].ECMpids[n].status = -1;
			}
			else
			{
				demux[demux_id].ECMpids[n].status = 1;
			}
		}
		demux[demux_id].max_emm_filter = maxfilter - 1;
		demux[demux_id].max_status = 1;
		cs_log("Demuxer %d found channel in cache and matching prio -> start descrambling ecmpid %d ", demux_id, found);
	}

	NULLFREE(er);
	cs_ftime(&end);
	int64_t gone = comp_timeb(&end, &start);
	cs_log_dbg(D_DVBAPI, "Demuxer %d sorting the ecmpids took %"PRId64" ms", demux_id, gone);
	return;
}

static void dvbapi_priority_read_entry_add(int32_t demux_id, uint16_t video_pid)
{
	struct s_dvbapi_priority *add_entry;

	for(add_entry = dvbapi_priority; add_entry != NULL; add_entry = add_entry->next)
	{
		// ECM pid is misused to hold PMT pid in case of 'A' rule.
		// Some receivers don't forward the PMT pid, so use the video pid instead
		if(add_entry->type != 'a' || add_entry->srvid != demux[demux_id].program_number
			|| (add_entry->ecmpid && demux[demux_id].pmtpid && add_entry->ecmpid != demux[demux_id].pmtpid)
			|| (add_entry->ecmpid && !demux[demux_id].pmtpid && add_entry->ecmpid != video_pid))
		{
			continue;
		}

		dvbapi_add_ecmpid(demux_id, add_entry->mapcaid, add_entry->mapecmpid, add_entry->mapprovid, 0, "(fake ecm pid)");

		cs_log_dbg(D_DVBAPI, "Demuxer %d added fake ecm pid %04X@%06X:%04X for program %04X", demux_id,
					add_entry->mapcaid, add_entry->mapprovid, add_entry->mapecmpid, demux[demux_id].program_number);
		break;
	}
}

static void dvbapi_priority_read_entry_map(int32_t demux_id)
{
	int32_t j;
	struct s_dvbapi_priority *map_entry;

	for(j = 0; j < demux[demux_id].ECMpidcount; j++)
	{
		map_entry = dvbapi_check_prio_match(demux_id, j, 'm');
		if(map_entry)
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d mapping ecm pid %d from %04X@%06X to %04X@%06X",
					demux_id, j, demux[demux_id].ECMpids[j].CAID, demux[demux_id].ECMpids[j].PROVID,
					map_entry->mapcaid, map_entry->mapprovid);

			demux[demux_id].ECMpids[j].CAID = map_entry->mapcaid;
			demux[demux_id].ECMpids[j].PROVID = map_entry->mapprovid;
		}
	}
}

static void dvbapi_priority_read_entry_extra(int32_t demux_id)
{
	struct s_dvbapi_priority *extra_entry;
	int32_t j, k, l, m, extra_demux_id;

	for(extra_entry = dvbapi_priority; extra_entry != NULL; extra_entry = extra_entry->next)
	{
		if(extra_entry->type != 'x')
		{
			continue;
		}

		for(j = 0; j <= demux[demux_id].ECMpidcount; ++j)
		{
			if((extra_entry->caid && extra_entry->caid != demux[demux_id].ECMpids[j].CAID)
				|| (extra_entry->provid && extra_entry->provid != demux[demux_id].ECMpids[j].PROVID)
				|| (extra_entry->ecmpid && extra_entry->ecmpid != demux[demux_id].ECMpids[j].ECM_PID)
				|| (extra_entry->srvid && extra_entry->srvid != demux[demux_id].program_number))
			{
				continue;
			}

			cs_log("Mapping ecm pid %04X@%06X:%04X:%04X to extra demuxer",
					extra_entry->caid, extra_entry->provid, extra_entry->ecmpid, extra_entry->srvid);

			for(extra_demux_id = 0; extra_demux_id < MAX_DEMUX; extra_demux_id++)
			{
				if(demux[extra_demux_id].program_number != 0)
				{
					continue; // Skip occupied demuxers
				}
			}

			if(extra_demux_id >= MAX_DEMUX)
			{
				cs_log("There is no free demuxer for extra streams");
				continue;
			}

			demux[extra_demux_id].ECMpids[0] = demux[demux_id].ECMpids[j];
			demux[extra_demux_id].ECMpidcount = 1;
			demux[extra_demux_id].STREAMpidcount = 0;
			demux[extra_demux_id].program_number = demux[demux_id].program_number;
			demux[extra_demux_id].pmtpid = demux[demux_id].pmtpid;
			demux[extra_demux_id].demux_index = demux[demux_id].demux_index;
			demux[extra_demux_id].adapter_index = demux[demux_id].adapter_index;
			demux[extra_demux_id].ca_mask = demux[demux_id].ca_mask;
			demux[extra_demux_id].socket_fd = demux[demux_id].socket_fd;
			demux[extra_demux_id].stop_descrambling = false;
			demux[extra_demux_id].rdr = NULL;
			demux[extra_demux_id].curindex = -1;

			// Add streams to extra demux
			for(k = 0; k < demux[demux_id].STREAMpidcount; ++k)
			{
				if(!demux[demux_id].ECMpids[j].streams || (demux[demux_id].ECMpids[j].streams & (1 << k)))
				{
					demux[extra_demux_id].ECMpids[0].streams |= (1 << demux[extra_demux_id].STREAMpidcount);
					demux[extra_demux_id].STREAMpids[demux[extra_demux_id].STREAMpidcount] = demux[demux_id].STREAMpids[k];
					demux[extra_demux_id].STREAMpidsType[demux[extra_demux_id].STREAMpidcount] = demux[demux_id].STREAMpidsType[k];
					++demux[extra_demux_id].STREAMpidcount;

					// Shift stream associations in normal demux because we will remove the stream entirely
					for(l = 0; l < demux[demux_id].ECMpidcount; ++l)
					{
						for(m = k; m < demux[demux_id].STREAMpidcount - 1; ++m)
						{
							if(demux[demux_id].ECMpids[l].streams & (1 << (m + 1)))
							{
								demux[demux_id].ECMpids[l].streams |= (1 << m);
							}
							else
							{
								demux[demux_id].ECMpids[l].streams &= ~(1 << m);
							}
						}
					}

					// Remove stream association from normal demux device
					for(l = k; l < demux[demux_id].STREAMpidcount - 1; ++l)
					{
						demux[demux_id].STREAMpids[l] = demux[demux_id].STREAMpids[l + 1];
						demux[demux_id].STREAMpidsType[l] = demux[demux_id].STREAMpidsType[l + 1];
					}

					--demux[demux_id].STREAMpidcount;
					--k;
				}
			}

			// Remove ecm pid from normal demuxer
			for(k = j; k < demux[demux_id].ECMpidcount; ++k)
			{
				demux[demux_id].ECMpids[k] = demux[demux_id].ECMpids[k + 1];
			}

			--demux[demux_id].ECMpidcount;
			--j;

			if(demux[extra_demux_id].STREAMpidcount <= 0)
			{
				cs_log("Found no streams for extra demuxer. Not starting additional decoding on it.");

				demux[extra_demux_id].program_number = 0;
				demux[extra_demux_id].stop_descrambling = true;
			}

			if(demux[demux_id].STREAMpidcount < 1)
			{
				cs_log("Found no streams for normal demuxer. Not starting additional decoding on it.");
			}
		}
	}
}

static void dvbapi_parse_pmt_ca_descriptor(int32_t demux_id, const uint8_t *buffer, uint8_t descriptor_length)
{
	uint16_t i, ca_system_id, ca_pid;
	uint32_t ca_provider = 0, ca_data = 0;
	char txt[40]; // room for PBM: 8 byte pbm and DATE: date
	memset(txt, 0x00, sizeof(txt));

	if(descriptor_length < 4)
	{
		return; // CA descriptor has a minimum length of 4 bytes
	}

	ca_system_id = b2i(2, buffer);
	ca_pid = b2i(2, buffer + 2) & 0x1FFF;

	if(ca_system_id == 0x0000 || (!caid_is_biss_fixed(ca_system_id) && !caid_is_fake(ca_system_id) && ca_pid == 0x1FFF))
	{
		return; // This is not a valid CAID or ECM pid
	}

	if(caid_is_seca(ca_system_id))
	{
		for(i = 2; i < descriptor_length; i += 15)
		{
			ca_pid = b2i(2, buffer + i) & 0x1FFF;
			ca_provider = b2i(2, buffer + i + 2);

			int8_t year = buffer[i + 13] >> 1;
			int8_t month = (((buffer[i + 13] & 0x01) << 3) | (buffer[i + 14] >> 5));
			int8_t day = buffer[i + 14] & 0x1F;

			snprintf(txt, sizeof(txt), "PBM: ");
			cs_hexdump(0, buffer + i + 5, 8, txt + 5, (2 * 8) + 1); // hexdump 8 byte pbm
			snprintf(txt + 20, sizeof(txt) - 20, " DATE: %d-%d-%d", day, month, year + 1990);

			dvbapi_add_ecmpid(demux_id, ca_system_id, ca_pid, ca_provider, 0, txt);
		}
	}
	else
	{
		if(caid_is_viaccess(ca_system_id) && descriptor_length == 0x0F && buffer[10] == 0x14)
		{
			ca_provider = b2i(3, buffer + 12) & 0xFFFFF0;
		}
		else if(caid_is_nagra(ca_system_id) && descriptor_length == 0x07)
		{
			ca_provider = b2i(2, buffer + 5);
		}
		else if((ca_system_id >> 8 == 0x4A || ca_system_id == 0x2710) && descriptor_length > 0x04)
		{
			ca_provider = buffer[4];

			if(caid_is_dre(ca_system_id) && descriptor_length == 0x0A)
			{
				ca_data = b2i(4, buffer + 6);
				snprintf(txt, 40, "CA DATA: %X", ca_data);
			}
		}

		dvbapi_add_ecmpid(demux_id, ca_system_id, ca_pid, ca_provider, ca_data, txt);
	}
}

static void dvbapi_parse_pmt_descriptors(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint8_t *type)
{
	uint16_t i, j;
	uint8_t descriptor_tag, descriptor_length;

	for(i = 0; i + 1 < length; i += 2 + descriptor_length)
	{
		descriptor_tag = buffer[i];
		descriptor_length = buffer[i + 1];

		cs_log_dbg(D_DVBAPI, "Demuxer %d found %s descriptor (tag: %02X length: %02X)",
			demux_id, get_descriptor_tag_txt(descriptor_tag), descriptor_tag, descriptor_length);

		switch(descriptor_tag)
		{
			case 0x05: // Registration descriptor
			{
				// "HDMV" format identifier is removed
				// OSCam does not need to know about Blu-ray
				const char format_identifiers_audio[10][5] =
				{
					"AC-3", "BSSD", "dmat", "DRA1", "DTS1",
					"DTS2", "DTS3", "EAC3", "mlpa", "Opus",
				};

				for(j = 0; j < 10; j++)
				{
					if(memcmp(buffer + i + 2, format_identifiers_audio[j], 4) == 0)
					{
						*type = STREAM_AUDIO;
						break;
					}
				}
				break;
			}

			case 0x09: // CA descriptor
			{
				dvbapi_parse_pmt_ca_descriptor(demux_id, buffer + i + 2, descriptor_length);
				break;
			}

			case 0x59: // Subtitling descriptor (DVB)
			{
				*type = STREAM_SUBTITLE;
				break;
			}

			case 0x6A: // AC-3 descriptor (DVB)
			case 0x7A: // Enhanced AC-3 descriptor (DVB)
			case 0x7B: // DTS descriptor (DVB)
			case 0x7C: // AAC descriptor (DVB)
			case 0x81: // AC-3 descriptor (ATSC)
			case 0xCC: // Enhanced AC-3 descriptor (ATSC)
			{
				*type = STREAM_AUDIO;
				break;
			}

			case 0x7F: // Extension descriptor (DVB)
			{
				uint8_t extension_descriptor_tag = buffer[i + 2];

				cs_log_dbg(D_DVBAPI, "Demuxer %d found %s descriptor (extension tag: %02X)",
					demux_id, get_extension_descriptor_txt(extension_descriptor_tag), extension_descriptor_tag);

				switch(extension_descriptor_tag)
				{
					case 0x0E: // DTS-HD descriptor (DVB)
					case 0x0F: // DTS Neural descriptor (DVB)
					case 0x15: // AC-4 descriptor (DVB)
						*type = STREAM_AUDIO;
						break;

					case 0x20: // TTML subtitling descriptor (DVB)
						*type = STREAM_SUBTITLE;
						break;

					default:
						*type = STREAM_UNDEFINED;
						break;
				}
				break;
			}

			default:
				break;
		}
	}
}

void request_cw(struct s_client *client, ECM_REQUEST *er, int32_t demux_id, uint8_t delayed_ecm_check)
{
	if(!er) { return; }

	int32_t filternum = dvbapi_set_section_filter(demux_id, er, -1); // set ecm filter to odd -> even and visaversa
	if(filternum < 0)
	{
		cs_log_dbg(D_DVBAPI, "Demuxer %d not requesting cw -> ecm filter was killed!", demux_id);
		NULLFREE(er);
		return;
	}

	if(!delayed_ecm_check) // no delayed ecm check for this filter
	{
		memset(demux[demux_id].demux_fd[filternum].lastecmd5, 0, CS_ECMSTORESIZE); // no ecm delay check: zero it!
	}
	else
	{
		uint8_t md5tmp[MD5_DIGEST_LENGTH];
		MD5(er->ecm, er->ecmlen, md5tmp);

		if(!memcmp(demux[demux_id].demux_fd[filternum].prevecmd5, md5tmp, CS_ECMSTORESIZE))
		{
			if(demux[demux_id].demux_fd[filternum].prevresult < E_NOTFOUND)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d not requesting same ecm again! -> SKIP!", demux_id);
				NULLFREE(er);
				return;
			}
			else
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d requesting same ecm again (previous result was not found!)", demux_id);
			}
		}
		else if(!memcmp(demux[demux_id].demux_fd[filternum].lastecmd5, md5tmp, CS_ECMSTORESIZE))
		{
			if(demux[demux_id].demux_fd[filternum].lastresult < E_NOTFOUND)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d not requesting same ecm again! -> SKIP!", demux_id);
				NULLFREE(er);
				return;
			}
			else
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d requesting same ecm again (previous result was not found!)", demux_id);
			}
		}

		memcpy(demux[demux_id].demux_fd[filternum].prevecmd5, demux[demux_id].demux_fd[filternum].lastecmd5, CS_ECMSTORESIZE);
		demux[demux_id].demux_fd[filternum].prevresult = demux[demux_id].demux_fd[filternum].lastresult;
		memcpy(demux[demux_id].demux_fd[filternum].lastecmd5, md5tmp, CS_ECMSTORESIZE);
		demux[demux_id].demux_fd[filternum].lastresult = 0xFF;
	}

	er->adapter_index = demux[demux_id].adapter_index;
	get_cw(client, er);

#ifdef WITH_DEBUG
	char buf[ECM_FMT_LEN];
	format_ecm(er, buf, ECM_FMT_LEN);
	cs_log_dbg(D_DVBAPI, "Demuxer %d request controlword for ecm %s", demux_id, buf);
#endif
}

void dvbapi_try_next_caid(int32_t demux_id, int8_t checked, uint32_t msgid)
{
	int32_t n, j, found = -1, started = 0;
	int32_t status = demux[demux_id].max_status;

	for(j = status; j >= 0; j--) // largest status first!
	{
		for(n = 0; n < demux[demux_id].ECMpidcount; n++)
		{
			//cs_log_dbg(D_DVBAPI,"Demuxer %d PID %d checked = %d status = %d (searching for pid with status = %d)",
			//	demux_id, n, demux[demux_id].ECMpids[n].checked, demux[demux_id].ECMpids[n].status, j);

			if(demux[demux_id].ECMpids[n].checked == checked && demux[demux_id].ECMpids[n].status == j)
			{
				found = n;
				openxcas_set_provid(demux[demux_id].ECMpids[found].PROVID);
				openxcas_set_caid(demux[demux_id].ECMpids[found].CAID);
				openxcas_set_ecm_pid(demux[demux_id].ECMpids[found].ECM_PID);

				// fixup for cas that need emm first!
				if(caid_is_irdeto(demux[demux_id].ECMpids[found].CAID) || (caid_is_dre(demux[demux_id].ECMpids[found].CAID)
					&& ((demux[demux_id].ECMpids[found].PROVID == 0x11 || demux[demux_id].ECMpids[found].PROVID == 0xFE))))
				{
					demux[demux_id].emmstart.time = 0;
				}

				started = dvbapi_start_descrambling(demux_id, found, checked, msgid);
				if(cfg.dvbapi_requestmode == 0 && started == 1)
				{
					return; // in requestmode 0 we only start 1 ecm request at the time
				}
			}
		}
	}

	if(found == -1 && demux[demux_id].pidindex == -1)
	{
		cs_log("Demuxer %d no suitable readers found that can be used for decoding!", demux_id);
		return;
	}
}

static void dvbapi_parse_pmt_program_info(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint8_t *ca_pmt_cmd_id)
{
	uint16_t i, offset = 0;
	uint8_t descriptor_tag, descriptor_length;

	if(ca_pmt_cmd_id != NULL) // We are on CA PMT parsing
	{
		*ca_pmt_cmd_id = buffer[0];
		offset = 1;
	}

	for(i = offset; i + 1 < length; i += 2 + descriptor_length)
	{
		descriptor_tag = buffer[i];
		descriptor_length = buffer[i + 1];

		if(descriptor_tag == 0x09) // We only care about CA descriptors at program level
		{
			dvbapi_parse_pmt_ca_descriptor(demux_id, buffer + i + 2, descriptor_length);
		}
	}
}

static void dvbapi_parse_pmt_es_info(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint8_t *ca_pmt_cmd_id, uint16_t *video_pid)
{
	uint16_t i, elementary_pid, es_info_length, offset = 0;
	uint8_t stream_type, type;

	for(i = 0; i + 4 < length; i += 5 + es_info_length)
	{
		if(demux[demux_id].STREAMpidcount >= MAX_STREAM_PIDS)
		{
			cs_log("Demuxer %d reached maximum number of elementary streams", demux_id);
			break;
		}

		type = STREAM_UNDEFINED;
		stream_type = buffer[i];
		elementary_pid = b2i(2, buffer + i + 1) & 0x1FFF;
		es_info_length = b2i(2, buffer + i + 3) & 0x0FFF;

		cs_log_dbg(D_DVBAPI,"Demuxer %d found %s stream (type: %02X pid: %04X)",
			demux_id, get_stream_type_txt(stream_type), stream_type, elementary_pid);

		if(es_info_length != 0 && es_info_length < length)
		{
			if(ca_pmt_cmd_id != NULL) // We are on CA PMT parsing
			{
				// Only enigma2, Spark and VDR follow the CA PMT specification ("ca_pmt_cmd_id"
				// shall be present in the ES info loop). For the first two, checking for boxtype
				// "dreambox" is sufficient, but for VDR this is not enough, because it shares
				// the same boxtype with tvheadend. So, for every other box (including VDR and
				// tvheadend), we stick to the old style check based on the value (descriptors
				// with tag 0x00 or 0x01 are not allowed, so this works), while for enigma2 we
				// do a proper check, because the "ca_pmt_cmd_id" can also take greater values.
				if(cfg.dvbapi_boxtype == BOXTYPE_DREAMBOX)
				{
					*ca_pmt_cmd_id = buffer[i + 5]; // It should be identical for all ES and the same as in program info
					offset = 1;
				}
				else
				{
					offset = (buffer[i + 5] <= 0x01) ? 1 : 0;
				}
			}

			// Parse descriptors at ES level
			dvbapi_parse_pmt_descriptors(demux_id, buffer + i + 5 + offset, es_info_length, &type);
		}

		// Get basic stream type (video, audio, subtitle) for each ES pid
		switch(stream_type)
		{
			case 0x01:
			case 0x02:
			case 0x10:
			case 0x1B:
			case 0x20:
			case 0x24:
			case 0x25:
			case 0x42:
			case 0xD1:
			case 0xEA:
				if(*video_pid == 0)
				{
					*video_pid = elementary_pid;
				}
				demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_VIDEO;
				break;

			case 0x03:
			case 0x04:
			case 0x0F:
			case 0x11:
			case 0x1C:
			case 0x2D:
			case 0x2E:
			case 0x81:
				demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_AUDIO;
				break;

			case 0x06:
			//case 0x81: some ATSC AC-3 streams do not contain the AC-3 descriptor!
			case 0x87:
				// Set the type based on the descriptors for these stream types
				demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = type;
				break;

			default:
				demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_UNDEFINED;
				break;
		}

		demux[demux_id].STREAMpids[demux[demux_id].STREAMpidcount] = elementary_pid;
		demux[demux_id].STREAMpidcount++;
	}
}

static void dvbapi_parse_pmt_info(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint16_t pcr_pid, uint8_t *ca_pmt_cmd_id)
{
	uint16_t i, program_info_length, video_pid = 0;

	// Cleanout demuxer from possible stale info
	// (reset ECM pids and streams)
	for(i = 0; i < demux[demux_id].ECMpidcount; i++)
	{
		demux[demux_id].ECMpids[i].streams = 0;
	}

	demux[demux_id].ECMpidcount = 0;
	demux[demux_id].STREAMpidcount = 0;

	// Parse program info
	// In case of CA PMT, read the ca_pmt_cmd_id as well
	program_info_length = b2i(2, buffer) & 0x0FFF;
	if(program_info_length != 0 && program_info_length < length)
	{
		dvbapi_parse_pmt_program_info(demux_id, buffer + 2, program_info_length, ca_pmt_cmd_id);
	}

	// Parse elementary stream info
	// In case of CA PMT, read the ca_pmt_cmd_id for each stream as well
	dvbapi_parse_pmt_es_info(demux_id, buffer + 2 + program_info_length, length - 2 - program_info_length, ca_pmt_cmd_id, &video_pid);

	cs_log("Demuxer %d found %d ECM pids and %d STREAM pids in %sPMT", demux_id,
		demux[demux_id].ECMpidcount, demux[demux_id].STREAMpidcount, ca_pmt_cmd_id != NULL ? "CA " : "");

	// Various retarded boxes misuse the "ca_pmt_cmd_id" value,
	// usually by setting it to zero. If we are on CA PMT parsing,
	// make sure we pass a value we can work with later on.
	if(ca_pmt_cmd_id != NULL)
	{
		*ca_pmt_cmd_id = (*ca_pmt_cmd_id < CA_PMT_CMD_OK_DESCRAMBLING) ? CA_PMT_CMD_OK_DESCRAMBLING : *ca_pmt_cmd_id;
	}

	// If no elementary streams are available, set the PMT pid as the
	// first stream (PMT cannot be encrypted, like it was mentioned
	// in the old comment, so not sure why this is needed...)
	if(demux[demux_id].STREAMpidcount == 0)
	{
		demux[demux_id].STREAMpids[0] = demux[demux_id].pmtpid;
		demux[demux_id].STREAMpidsType[0] = STREAM_VIDEO;
		demux[demux_id].STREAMpidcount++;
		video_pid = demux[demux_id].pmtpid;
	}

	// Fix for channels not listing the video pid inside the PMT: use the pcr pid instead
	if(video_pid == 0 && pcr_pid != 0x1FFF)
	{
		demux[demux_id].STREAMpids[demux[demux_id].STREAMpidcount] = pcr_pid;
		demux[demux_id].STREAMpidsType[demux[demux_id].STREAMpidcount] = STREAM_VIDEO;
		demux[demux_id].STREAMpidcount++;
		video_pid = pcr_pid;

		cs_log("Demuxer %d found no video pid. Using the PCR pid %04X instead", demux_id, pcr_pid);
	}

	// Register found video pid on all ECM pids of this demuxer
	for(i = 0; i < demux[demux_id].ECMpidcount; i++)
	{
		demux[demux_id].ECMpids[i].VPID = video_pid;
	}

	// Search for dvbapi priority entries for this program
	if(dvbapi_priority != NULL)
	{
		dvbapi_priority_read_entry_add(demux_id, video_pid);
		dvbapi_priority_read_entry_map(demux_id);
		dvbapi_priority_read_entry_extra(demux_id);
	}
}

typedef struct demux_parameters
{
	uint8_t  demux_index;
	uint8_t  adapter_index;
	uint32_t ca_mask;
	uint16_t program_number;
	uint16_t pmtpid;
	uint16_t onid;
	uint16_t tsid;
	uint32_t ens;
} demux_parameters_t;

static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *parameters)
{
	parameters->ca_mask = 1;
	parameters->demux_index = 0;
	parameters->adapter_index = 0;
	parameters->pmtpid = 0;
	parameters->program_number = b2i(2, buffer + 1);

	uint16_t program_info_length = b2i(2, buffer + 4) & 0x0FFF;
	uint16_t pos = 7; // 4 + 2 (program_info_length) + 1 (ca_pmt_cmd_id)

	while(pos + 1 < 5 + program_info_length)
	{
		uint8_t descriptor_tag = buffer[pos];
		uint8_t descriptor_length = buffer[pos + 1];

		switch(descriptor_tag)
		{
			case CA:
			{
				break;
			}

			case ENIGMA_NAMESPACE:
			{
				if(descriptor_length == 0x08)
				{
					parameters->ens = b2i(4, buffer + pos + 2);
					parameters->tsid = b2i(2, buffer + pos + 6);
					parameters->onid = b2i(2, buffer + pos + 8);
				}
				break;
			}

			case DEMUX_CA_MASK_ADAPTER:
			{
				if(descriptor_length == 0x02 && (cfg.dvbapi_boxtype == BOXTYPE_PC ||
					cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO))
				{
					parameters->demux_index = buffer[pos + 2]; // Usually 0, but not always
					parameters->adapter_index = buffer[pos + 3]; // Can be 0, 1, 2, ...
					parameters->ca_mask = (1 << parameters->adapter_index); // use adapter_index as ca_mask
				}
				else if(descriptor_length == 0x03 && cfg.dvbapi_boxtype == BOXTYPE_QBOXHD)
				{
					// ca_mask = buffer[pos + 2]; // with STONE 1.0.4 always 0x01
					parameters->demux_index = buffer[pos + 3]; // with STONE 1.0.4 always 0x00
					parameters->adapter_index = buffer[pos + 4]; // with STONE 1.0.4 adapter index can be 0, 1, 2
					parameters->ca_mask = (1 << parameters->adapter_index); // use adapter_index as ca_mask
				}
				else if(descriptor_length == 0x02) // enigma2
				{
					parameters->ca_mask = buffer[pos + 2];
					uint8_t demux_tmp = buffer[pos + 3];

					if(demux_tmp >= 8 && parameters->ca_mask == 0) // openpli based images
					{
						parameters->ca_mask = 1 << demux_tmp;
					}

					if(demux_tmp == 0xFF) // tryfix prismcube (0xFF -> "demux-1" = error!)
					{
						demux_tmp = 0;
					}
					parameters->demux_index = demux_tmp;
				}
				break;
			}

			case ADAPTER_DEVICE:
			{
				if(descriptor_length == 0x01)
				{
					parameters->adapter_index = buffer[pos + 2];
				}
				break;
			}

			case PMT_PID:
			{
				if(descriptor_length == 0x02)
				{
					parameters->pmtpid = b2i(2, buffer + pos + 2);
				}
				break;
			}

			case SERVICE_TYPE_MASK:
				break;

			case DEMUX_DEVICE:
			{
				if(descriptor_length == 0x01)
				{
					parameters->demux_index = buffer[pos + 2];
					parameters->ca_mask = 1 << parameters->demux_index;
				}
				break;
			}

			case CA_DEVICE:
			{
				if(descriptor_length == 0x01)
				{
					parameters->ca_mask = 1 << buffer[pos + 2];
				}
				break;
			}

			default:
			{
				cs_log_dbg(D_DVBAPI, "Skipped unsupported or CA PMT irrelevant descriptor (tag: %02X length: %02X)", descriptor_tag, descriptor_length);
				break;
			}
		}

		pos += 2 + descriptor_length;
	}
}

static void dvbapi_capmt_notify(struct demux_s *dmx)
{
	struct s_client *cl;
	for(cl = first_client->next; cl; cl = cl->next)
	{
		if((cl->typ == 'p' || cl->typ == 'r') && cl->reader && cl->reader->ph.c_capmt)
		{
			struct demux_s *curdemux;
			if(cs_malloc(&curdemux, sizeof(struct demux_s)))
			{
				memcpy(curdemux, dmx, sizeof(struct demux_s));
				add_job(cl, ACTION_READER_CAPMT_NOTIFY, curdemux, sizeof(struct demux_s));
			}
		}
	}
}

static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid)
{
	bool is_powervu = false, start_emm = true;
	char service_name[CS_SERVICENAME_SIZE];

	// The CA PMT should have given us enough info to determine if descrambling
	// is possible. Parsing the (real) PMT is not necessary, unless we have a
	// PowerVu encrypted channel or (for some weird reason) no stream pids at all.
	// Actually, when no streams are available, we set the PMT pid as the 1st
	// stream pid, so we have to check against that. Finally, if the PMT pid is
	// not included in the CA PMT, we start the PAT filter instead.

#ifdef WITH_EXTENDED_CW
	uint8_t i;
	for(i = 0; i < demux[demux_id].ECMpidcount; i++)
	{
		if(caid_is_powervu(demux[demux_id].ECMpids[i].CAID))
		{
			is_powervu = true;
			break;
		}
	}
#endif

	if(demux[demux_id].pmtpid == 0)
	{
		dvbapi_start_pat_filter(demux_id);
	}
	else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu)
	{
		dvbapi_start_pmt_filter(demux_id);
	}

	if(demux[demux_id].running)
	{
		disable_unused_streampids(demux_id); // disable all streampids not in use anymore
	}

	if(!demux[demux_id].running && demux[demux_id].ECMpidcount != 0) // only start demuxer if it wasn't running
	{
		// remove all non important filtering
		// (there are images with limited amount of filters available!)
		dvbapi_stop_all_cat_emm_sdt_filtering(msgid);

		get_servicename(dvbapi_client, demux[demux_id].program_number, demux[demux_id].ECMpids[0].PROVID,
			demux[demux_id].ECMpids[0].CAID, service_name, sizeof(service_name));

		cs_log_dbg(D_DVBAPI, "Demuxer %d started descrambling for program %04X (%s) (fd: %d)",
			demux_id, demux[demux_id].program_number, service_name, demux[demux_id].socket_fd);

		demux[demux_id].running = true; // mark channel as running
		openxcas_set_sid(demux[demux_id].program_number);
		demux[demux_id].decodingtries = -1;
		dvbapi_resort_ecmpids(demux_id);
		dvbapi_try_next_caid(demux_id, 0, msgid);
		cs_sleepms(1);
	}
	else if(demux[demux_id].ECMpidcount == 0) // FTA: do logging and part of ecm handler
	{
		get_servicename(dvbapi_client, demux[demux_id].program_number, NO_PROVID_VALUE, NO_CAID_VALUE,
			service_name, sizeof(service_name));

		cs_log_dbg(D_DVBAPI, "Demuxer %d no descrambling needed for FTA program %04X (%s) (fd: %d)",
			demux_id, demux[demux_id].program_number, service_name, demux[demux_id].socket_fd);

		demux[demux_id].running = false; // reset running flag
		demux[demux_id].pidindex = -1; // reset ecmpid used for descrambling
		dvbapi_stop_filter(demux_id, TYPE_ECM, msgid);

		if(cfg.usrfileflag) // add to user log previous channel + time on channel
		{
			cs_statistics(dvbapi_client);
		}

		dvbapi_client->last_srvid = demux[demux_id].program_number; // set new channel srvid
		dvbapi_client->last_caid = NO_CAID_VALUE; // FTA channels have no caid!
		dvbapi_client->last_provid = NO_PROVID_VALUE; // FTA channels have no provid!
		dvbapi_client->lastswitch = dvbapi_client->last = time((time_t *)0); // reset idle-Time & last switch
	}

#if defined(WITH_COOLAPI) || defined(WITH_COOLAPI2)
		// Don't start and Stop EMM Filters over and over again if we are on FTA
		if(dvbapi_client->last_caid == NO_CAID_VALUE)
		{
			start_emm = false;
		}
#endif

	// only do emm setup if au enabled and not running!
	if(cfg.dvbapi_au > 0 && demux[demux_id].EMMpidcount == 0 && start_emm == true)
	{
		demux[demux_id].emm_filter = -1; // to register first run emmfilter start

		if(demux[demux_id].emmstart.time == 1) // irdeto fetch emm cat direct!
		{
			// trick to let emm fetching start after 30 seconds to speed up zapping
			cs_ftime(&demux[demux_id].emmstart);

			dvbapi_start_cat_filter(demux_id);
		}
		else
		{
			cs_ftime(&demux[demux_id].emmstart); // for all other caids delayed start!
		}
	}
}

int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connfd, char *pmtfile, uint16_t client_proto_version, uint32_t msgid)
{
	int32_t i, demux_id = -1;
	uint8_t ca_pmt_list_management, ca_pmt_cmd_id;
	bool is_update = false;
	demux_parameters_t parameters;
	memset(&parameters, 0, sizeof(parameters));

#if defined WITH_COOLAPI || defined WITH_COOLAPI2
	ca_pmt_list_management = CA_PMT_LIST_ONLY;
#else
	ca_pmt_list_management = buffer[0];
#endif

	// We received a new list of CA PMT objects.
	// Mark all demuxers to stop descrambling, but do not actually stop any of them,
	// until we verify the new list does not contain any previously selected program.
	if(ca_pmt_list_management == CA_PMT_LIST_FIRST || ca_pmt_list_management == CA_PMT_LIST_ONLY)
	{
		for(i = 0; i < MAX_DEMUX; i++)
		{
			// Skip empty demuxers, demuxers belonging to different
			// CA PMT connections or handled by different PMT files.
			if(demux[i].program_number == 0 || demux[i].socket_fd != connfd ||
				(demux[i].socket_fd == -1 && pmtfile && strcmp(demux[i].pmt_file, pmtfile) != 0))
			{
				continue;
			}

			demux[i].stop_descrambling = true; // Mark for deletion if not used again by following CA PMT objects

			cs_log_dbg(D_DVBAPI, "Demuxer %d marked to stop descrambling for program %04X (fd: %d)",
				i, demux[i].program_number, connfd);
		}
	}

	// Read private descriptors inside the CA PMT message
	// in order to get adapter, demux, ca, pmt pid and more.
	get_demux_parameters(buffer, &parameters);

	cs_log_dbg(D_DVBAPI, "Received CA PMT list management %d for program %04X (pmt pid: %04X adapter: %d demux: %d camask: %d)",
		ca_pmt_list_management, parameters.program_number, parameters.pmtpid, parameters.adapter_index,
		parameters.demux_index, parameters.ca_mask);

	// Search current demuxers for having the same program
	// as the one we received in this CA PMT object.
	for(i = 0; i < MAX_DEMUX; i++)
	{
		if(demux[i].program_number == 0)
		{
			continue;
		}

		if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX_PMT)
		{
			parameters.demux_index = i; // fixup for ipbox
		}

		bool full_check = true, matched = false;

		if(config_enabled(WITH_COOLAPI) || config_enabled(WITH_COOLAPI2) || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)
		{
			full_check = false;
		}

		if(full_check)
		{
			matched = (connfd > 0 && demux[i].socket_fd == connfd) && demux[i].program_number == parameters.program_number;
		}
		else
		{
			matched = connfd > 0 && demux[i].program_number == parameters.program_number;
		}

		if(matched)
		{
			if(full_check)
			{
				// In PMT mode 6, when zapping between channels with the same program number and PMT pid
				// (sometimes the case with satellite feeds), as all hardware parameters being the same
				// (adapter, demux, ca_mask, connfd), the new program is considered an existing one (matched).
				// The only reliable way to determine whether we actually have a new program is to compare
				// the enigma namespace, tsid and onid as well.
				if(demux[i].demux_index != parameters.demux_index || demux[i].ca_mask != parameters.ca_mask ||
					demux[i].adapter_index != parameters.adapter_index || demux[i].pmtpid != parameters.pmtpid ||
					demux[i].ens != parameters.ens || demux[i].tsid != parameters.tsid || demux[i].onid != parameters.onid)
				{
					continue;
				}
			}

			// A program update is normally signaled by either a list management:
			// 1. UPDATE for an existing program
			// 2. ADD for an existing program (which according to the specifications should be treated as an UPDATE)
			// 3. ONLY for an existing program (with broken clients, in pmt modes other than 6)
			if(ca_pmt_list_management == CA_PMT_LIST_UPDATE || ca_pmt_list_management == CA_PMT_LIST_ADD
				|| (cfg.dvbapi_pmtmode != 6 && ca_pmt_list_management == CA_PMT_LIST_ONLY))
			{
				is_update = true;
				cs_log("Demuxer %d received CA PMT update for program %04X", i, parameters.program_number);
			}

			cs_log("Demuxer %d continues processing for program %04X", i, demux[i].program_number);
			openxcas_set_sid(parameters.program_number);
			demux[i].stop_descrambling = false; // don't stop current demuxer!
			demux_id = i;
			break; // no need to explore other demuxers since we have a match!
		}
	}

	// We are currently processing the last object of the CA PMT list.
	// We should now stop descrambling all programs not included in this list.
	if(ca_pmt_list_management != CA_PMT_LIST_FIRST && ca_pmt_list_management != CA_PMT_LIST_MORE)
	{
		for(i = 0; i < MAX_DEMUX; i++)
		{
			if(demux[i].program_number == 0)
			{
				continue;
			}

			if(demux[i].stop_descrambling)
			{
				dvbapi_stop_descrambling(i, msgid);
			}
		}
	}

	// We continue reading the CA PMT object only when we get
	// a new program (demux_id == -1) or an updated program.
	if(!(demux_id == -1 || is_update))
	{
		return demux_id;
	}

	// We received a CA PMT object for a new program.
	// Let's find an empty demuxer for it.
	if(demux_id == -1)
	{
		if(ca_pmt_list_management == CA_PMT_LIST_UPDATE)
		{
			cs_log("ERROR: Received CA PMT list update for unknown program");
			return -1;
		}

		for(demux_id = 0; demux_id < MAX_DEMUX; demux_id++)
		{
			if(demux[demux_id].program_number != 0)
			{
				continue; // Skip occupied demuxers
			}

			// Probably this box doesn't send any private descriptor in the
			// CA PMT, so we have to improvise before saving to the demuxer.
			if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX_PMT)
			{
				parameters.ca_mask = demux_id + 1;
				parameters.demux_index = demux_id;
			}

			demux[demux_id].demux_index = parameters.demux_index;
			demux[demux_id].adapter_index = parameters.adapter_index;
			demux[demux_id].ca_mask = parameters.ca_mask;
			demux[demux_id].socket_fd = connfd;
			demux[demux_id].client_proto_version = client_proto_version;
			demux[demux_id].program_number = parameters.program_number;
			demux[demux_id].pmtpid = parameters.pmtpid;
			demux[demux_id].ens = parameters.ens;
			demux[demux_id].tsid = parameters.tsid;
			demux[demux_id].onid = parameters.onid;
			demux[demux_id].stop_descrambling = false;
			demux[demux_id].running = false;
			demux[demux_id].sdt_filter = -1;
			demux[demux_id].rdr = NULL;
			demux[demux_id].msgid = msgid;

			if(pmtfile)
			{
				cs_strncpy(demux[demux_id].pmt_file, pmtfile, sizeof(demux[demux_id].pmt_file));
			}

			break;
		}

		if(demux_id >= MAX_DEMUX)
		{
			cs_log("There is no free demuxer for the new program! Aborting...");
			return -1;
		}
	}

	// We continue parsing the CA PMT info for new or updated programs.
	// For updated programs, we just delete all previous stream pids and
	// ECM pids and start parsing the fresh data.
	dvbapi_parse_pmt_info(demux_id, buffer + 4, length - 4, 0x1FFF, &ca_pmt_cmd_id);

	// Finally, evaluate what response the host requires from OSCam.
	// This allows multiple CA applications to run at the host simultaneously.
	// "OK query" will be implemented at a later stage, when support is first
	// added in enigma2.
	switch(ca_pmt_cmd_id)
	{
		case CA_PMT_CMD_OK_DESCRAMBLING:
		{
			dvbapi_capmt_notify(&demux[demux_id]);
			dvbapi_prepare_descrambling(demux_id, msgid);
			return demux_id;
		}

		case CA_PMT_CMD_QUERY:
		{
			cs_log("Received unsupported CA PMT command ID 'query' for program %04X", demux[demux_id].program_number);
			dvbapi_stop_descrambling(demux_id, msgid); // Clear all data from this demuxer
			return -1;
		}

		case CA_PMT_CMD_NOT_SELECTED:
		{
			cs_log("Program %04X is not selected for descrambling", demux[demux_id].program_number);
			dvbapi_stop_descrambling(demux_id, msgid); // Clear all data from this demuxer
			return -1;
		}

		default:
		{
			cs_log("Received unknown or unsupported CA PMT command ID %02X from host", ca_pmt_cmd_id);
			dvbapi_stop_descrambling(demux_id, msgid);
			return -1;
		}
	}
}

static void dvbapi_parse_pmt(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid)
{
	uint16_t program_number = b2i(2, buffer + 3);
	uint16_t pcr_pid = b2i(2, buffer + 8) & 0x1FFF;

	if(program_number != demux[demux_id].program_number)
	{
		cs_log("Demuxer %d received PMT for undefined program %04X", demux_id, program_number);
		return;
	}

	dvbapi_stop_filter(demux_id, TYPE_PMT, msgid);
	dvbapi_parse_pmt_info(demux_id, buffer + 10, length - 10 - 4, pcr_pid, NULL); // last 4 bytes are the CRC-32
}

static void dvbapi_create_srvid_line(int32_t demux_id, char *buffer, uint32_t buflen)
{
	int32_t i, j, pos = 0;
	uint16_t caid_done[32], cur_caid;
	uint8_t caid_done_count = 0, skip_caid;

	if(demux[demux_id].ECMpidcount == 0)
	{
		snprintf(buffer, buflen, "%04X@%06X", NO_CAID_VALUE, NO_PROVID_VALUE);
		return;
	}

	for(i = 0; i < demux[demux_id].ECMpidcount && i < 32; i++)
	{
		skip_caid = 0;
		for(j = 0; j < caid_done_count; j++)
		{
			if(caid_done[j] == demux[demux_id].ECMpids[i].CAID)
			{
				skip_caid = 1;
				break;
			}
		}

		if(skip_caid)
		{
			continue;
		}

		cur_caid = demux[demux_id].ECMpids[i].CAID;
		pos += snprintf(buffer + pos, buflen - pos, "%s%04X", caid_done_count > 0 ? "," : "", cur_caid == 0 ? NO_CAID_VALUE : cur_caid);

		for(j = i; j < demux[demux_id].ECMpidcount; j++)
		{
			if(demux[demux_id].ECMpids[j].PROVID == 0)
			{
				continue;
			}

			if(cur_caid == demux[demux_id].ECMpids[j].CAID)
			{
				pos += snprintf(buffer + pos, buflen - pos, "@%06X", demux[demux_id].ECMpids[j].PROVID);
			}
		}

		caid_done[caid_done_count] = demux[demux_id].ECMpids[i].CAID;
		caid_done_count++;
	}
}

static void dvbapi_write_sdt_info(int32_t demux_id, const char *provider_name, const char* service_name, const char *service_type)
{
	int8_t did_save_srvid = 0;
	int32_t provid, caid, pidindex;
	char tmp[256], srvid_line[1024];
	FILE *fpsave = NULL;

	pidindex = demux[demux_id].pidindex;
	if(pidindex != -1)
	{
		caid = demux[demux_id].ECMpids[pidindex].CAID;
		provid = demux[demux_id].ECMpids[pidindex].PROVID;
	}
	else
	{
		if(demux[demux_id].ECMpidcount == 0 || demux[demux_id].ECMpids[0].CAID == 0)
		{
			caid = NO_CAID_VALUE;
			provid = NO_PROVID_VALUE;
		}
		else
		{
			caid = demux[demux_id].ECMpids[0].CAID;
			provid = demux[demux_id].ECMpids[0].PROVID;
		}
	}

	if(cs_strlen(provider_name) && caid != NO_CAID_VALUE)
	{
		get_providername_or_null(provid, caid, tmp, sizeof(tmp));

		if(tmp[0] == '\0')
		{
			get_config_filename(tmp, sizeof(tmp), "oscam.provid");

			if((fpsave = fopen(tmp, "a")))
			{
				fprintf(fpsave, "\n%04X@%06X|%s|", caid, provid, provider_name);
				fclose(fpsave);
				init_provid();
			}
		}
	}

	if(cs_strlen(service_name))
	{
		get_servicename_or_null(cur_client(), demux[demux_id].program_number, provid, caid, tmp, sizeof(tmp));

		if(tmp[0] == '\0')
		{
			get_config_filename(tmp, sizeof(tmp), "oscam.srvid2");

			if(!access(tmp, F_OK) && (fpsave = fopen(tmp, "a")))
			{
				if((caid != NO_CAID_VALUE) || (cfg.dvbapi_read_sdt > 1))
				{
					dvbapi_create_srvid_line(demux_id, srvid_line, sizeof(srvid_line));

					if(cfg.dvbapi_write_sdt_prov)
					{
						fprintf(fpsave, "\n%04X:%s|%s|%s||%s", demux[demux_id].program_number, srvid_line, service_name, service_type, provider_name);
					}
					else
					{
						fprintf(fpsave, "\n%04X:%s|%s|%s", demux[demux_id].program_number, srvid_line, service_name, service_type);
					}

					did_save_srvid = 1;
				}
			}
			else
			{
				get_config_filename(tmp, sizeof(tmp), "oscam.srvid");

				if((fpsave = fopen(tmp, "a")))
				{
					if((caid != NO_CAID_VALUE) || (cfg.dvbapi_read_sdt > 1))
					{
						dvbapi_create_srvid_line(demux_id, srvid_line, sizeof(srvid_line));

						if(cfg.dvbapi_write_sdt_prov)
						{
							fprintf(fpsave, "\n%s:%04X|%s|%s|%s", srvid_line, demux[demux_id].program_number, provider_name, service_name, service_type);
						}
						else
						{
							fprintf(fpsave, "\n%s:%04X||%s|%s", srvid_line, demux[demux_id].program_number, service_name, service_type);
						}

						did_save_srvid = 1;
					}
				}
			}

			if(fpsave)
			{
				fclose(fpsave);
			}

			if(did_save_srvid)
			{
				init_srvid();
			}
		}
	}
}

static uint32_t dvbapi_extract_sdt_string(char *buf, uint32_t buflen, const uint8_t *source, uint8_t sourcelen)
{
	uint32_t i, j, offset = 0;
	int8_t iso_mode = -1;
	char *tmpbuf;
	const uint8_t *ptr_in;
	uint8_t *ptr_out;
	size_t in_bytes, out_bytes;

	if(sourcelen == 0)
	{
		buf[0] = '\0';
		return 1;
	}

	if(!cs_malloc(&tmpbuf, buflen))
	{
		return 0;
	}

	if(sourcelen > buflen - 1)
	{
		sourcelen = buflen - 1;
	}

	if(sourcelen > 0 && source[0] < 0x20)
	{
		if(source[0] >= 0x01 && source[0] <= 0x0B && source[0] != 0x08) // ISO/IEC 8859
		{
			offset = 1;
			iso_mode = 4 + source[0];
		}
		else if(source[0] == 0x10) // Dynamically selected part of ISO/IEC 8859
		{
			if(source[1] == 0x00 && source[2] >= 0x01 && source[2] <= 0x0F && source[2] != 0x0C)
			{
				offset = 3;
				iso_mode = source[2];
			}
		}
		else if(source[0] == 0x11) // ISO/IEC 10646
		{
			offset = 1;
			iso_mode = -2;
		}
		// missing: 0x12 KSX1001-2004 (Korean Character Set)
		// missing: 0x13 GB-2312-1980 (Simplified Chinese Character Set)
		// missing: 0x14 Big5 subset of ISO/IEC 10646 (Traditional Chinese)
		else if(source[0] == 0x15) // UTF-8 encoding of ISO/IEC 10646
		{
			offset = 1;
			iso_mode = -3;
		}
		// missing: 0x1F Described by encoding_type_id
		else
		{
			NULLFREE(tmpbuf);
			return 0;
		}
	}

	if(offset >= sourcelen)
	{
		NULLFREE(tmpbuf);
		return 0;
	}

	if(iso_mode >= -1)
	{
		for(i = 0, j = 0; i < sourcelen - offset; i++)
		{
			if(source[offset + i] >= 0x80 && source[offset + i] <= 0x9F)
			{
				continue;
			}

			tmpbuf[j] = source[offset + i];
			j++;
		}

		tmpbuf[j] = '\0';
	}

	ptr_in = (const uint8_t *)tmpbuf;
	in_bytes = cs_strlen(tmpbuf);
	ptr_out = (uint8_t *)buf;
	out_bytes = buflen;

#ifdef READ_SDT_CHARSETS
	if(iso_mode >= -1)
	{
		memset(buf, 0, buflen);
		cs_log_dbg(D_DVBAPI, "sdt-info dbg: iso_mode: %d offset: %u", iso_mode, offset);
		cs_log_dump_dbg(D_DVBAPI, (uint8_t *)tmpbuf, in_bytes, "sdt-info dbg: raw string:");

		if(iso_mode == -1)
		{
			if(ISO6937toUTF8(&ptr_in, &in_bytes, &ptr_out, &out_bytes) == (size_t)(-1))
			{
				cs_log_dbg(D_DVBAPI, "sdt-info error: ISO6937toUTF8 failed");
				NULLFREE(tmpbuf);
				return 0;
			}
		}
		else
		{
			if(ISO8859toUTF8(iso_mode, &ptr_in, &in_bytes, &ptr_out, &out_bytes) == (size_t)(-1))
			{
				cs_log_dbg(D_DVBAPI, "sdt-info error: ISO8859toUTF8 failed");
				NULLFREE(tmpbuf);
				return 0;
			}
		}
	}
#else
	if(iso_mode >= -1)
	{
		cs_strncpy(buf, tmpbuf, buflen);
		cs_log_dbg(D_DVBAPI, "sdt-info warning: your build of oscam does not support iso-to-utf8 conversion, special chars may be corrupted!");
	}
#endif
	else if(iso_mode == -2)
	{
		memset(buf, 0, buflen);
		cs_log_dbg(D_DVBAPI, "sdt-info dbg: iso_mode: %d offset: %u", iso_mode, offset);

		if(UnicodetoUTF8(&ptr_in, &in_bytes, &ptr_out, &out_bytes) == (size_t)(-1))
		{
			cs_log_dbg(D_DVBAPI, "sdt-info error: UnicodetoUTF8 failed");
			NULLFREE(tmpbuf);
			return 0;
		}
	}
	else if(iso_mode == -3) // No conversion, already in UTF-8
	{
		memcpy(buf, source + offset, sourcelen - offset);
		buf[sourcelen - offset] = '\0';
		cs_log_dbg(D_DVBAPI, "sdt-info dbg: iso_mode: -3 offset: %u", offset);
	}

	cs_log_dump_dbg(D_DVBAPI, (uint8_t *)buf, cs_strlen(buf), "sdt-info dbg: encoded string:");
	NULLFREE(tmpbuf);
	return 1;
}

static const char *dvbapi_get_service_type(uint8_t service_type)
{
	switch(service_type)
	{
		case 0x01:
		case 0x0B:
		case 0x11:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1A:
		case 0x1B:
		case 0x1C:
		case 0x1D:
		case 0x1E:
		case 0x1F:
		case 0x20:
			return "TV";

		case 0x02:
		case 0x07:
		case 0x0A:
			return "Radio";

		case 0x03:
			return "Teletext";

		case 0x0C:
			return "Data";

		default:
			return "unknown";
	}
}

static void dvbapi_parse_service_descriptor(int32_t demux_id, const uint8_t *buffer, uint8_t descriptor_length)
{
	uint8_t service_provider_name_length, service_name_length;
	char service_provider_name[64], service_name[64];
	const char *service_type;

	if(descriptor_length < 3)
	{
		return; // Service descriptor has a minimum length of 3 bytes
	}

	service_type = dvbapi_get_service_type(buffer[0]);

	service_provider_name_length = buffer[1];
	if(2 + service_provider_name_length + 1 > descriptor_length)
	{
		return;
	}

	service_name_length = buffer[2 + service_provider_name_length];
	if(2 + service_provider_name_length + 1 + service_name_length > descriptor_length)
	{
		return;
	}

	if(!dvbapi_extract_sdt_string(service_provider_name, sizeof(service_provider_name), buffer + 2, service_provider_name_length) ||
		!dvbapi_extract_sdt_string(service_name, sizeof(service_name), buffer + 2 + service_provider_name_length + 1, service_name_length))
	{
		return;
	}

	cs_log_dbg(D_DVBAPI, "Demuxer %d got service info (provider: %s - name: %s - type: %s)",
		demux_id, service_provider_name, service_name, service_type);

	dvbapi_write_sdt_info(demux_id, service_provider_name, service_name, service_type);
}

static void dvbapi_parse_sdt(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid)
{
	uint8_t descriptor_tag, descriptor_length;
	uint16_t service_id, descriptors_loop_length, i, j;

	if(buffer[0] != 0x42) // SDT sections with table_id value 0x42 describe the actual TS
	{
		return;
	}

	// Get the tsid and onid (in enigma2 STBs we have
	// already received them in the CA PMT message)
	demux[demux_id].tsid = b2i(2, buffer + 3);
	demux[demux_id].onid = b2i(2, buffer + 8);

	for(i = 11; i + 5 < length; i += 5 + descriptors_loop_length)
	{
		service_id = b2i(2, buffer + i);
		descriptors_loop_length = b2i(2, buffer + i + 3) & 0x0FFF;

		if(service_id != demux[demux_id].program_number)
		{
			continue;
		}

		for(j = 0; j + 1 < descriptors_loop_length; j += 2 + descriptor_length)
		{
			descriptor_tag = buffer[i + 5 + j];
			descriptor_length = buffer[i + 5 + j + 1];

			if(descriptor_tag == 0x48)
			{
				dvbapi_parse_service_descriptor(demux_id, buffer + i + 5 + j + 2, descriptor_length);
			}
		}

		dvbapi_stop_filter(demux_id, TYPE_SDT, msgid);
		break;
	}
}

static void dvbapi_parse_pat(int32_t demux_id, const uint8_t *buffer, uint16_t length, uint32_t msgid)
{
	uint16_t i, srvid;

	dvbapi_stop_filter(demux_id, TYPE_PAT, msgid);

	for(i = 8; i + 7 < length; i += 4)
	{
		srvid = b2i(2, buffer + i);
		if(srvid == 0)
		{
			continue;
		}

		if(demux[demux_id].program_number == srvid)
		{
			demux[demux_id].pmtpid = b2i(2, buffer + i + 2) & 0x1FFF;
			dvbapi_start_pmt_filter(demux_id);
			break;
		}
	}
}

int32_t dvbapi_init_listenfd(void)
{
	int32_t clilen, listenfd;
	struct sockaddr_un servaddr;

	memset(&servaddr, 0, sizeof(struct sockaddr_un));
	servaddr.sun_family = AF_UNIX;
	cs_strncpy(servaddr.sun_path, devices[selected_box].cam_socket_path, sizeof(servaddr.sun_path));
	clilen = sizeof(servaddr.sun_family) + cs_strlen(servaddr.sun_path);

	if(((unlink(devices[selected_box].cam_socket_path) < 0) && (errno != ENOENT))
		|| ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
		|| (bind(listenfd, (struct sockaddr *)&servaddr, clilen) < 0)
		|| (listen(listenfd, 5) < 0))
	{
		return 0;
	}

	// change the access right on the camd.socket
	// this will allow oscam to run as root if needed
	// and still allow non root client to connect to the socket
	chmod(devices[selected_box].cam_socket_path, S_IRWXU | S_IRWXG | S_IRWXO);
	return listenfd;
}

int32_t dvbapi_net_init_listenfd(void)
{
	int32_t listenfd;
	struct SOCKADDR servaddr;

	memset(&servaddr, 0, sizeof(servaddr));
	SIN_GET_FAMILY(servaddr) = DEFAULT_AF;
	SIN_GET_ADDR(servaddr) = cfg.dvbapi_srvip;
	SIN_GET_PORT(servaddr) = htons((uint16_t)dvbapi_listenport_active);

	if((listenfd = socket(DEFAULT_AF, SOCK_STREAM, 0)) < 0)
	{
		return 0;
	}
	int32_t opt = 0;

#ifdef IPV6SUPPORT
// azbox toolchain do not have this define
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 26
#endif
	// set the server socket option to listen on IPv4 and IPv6 simultaneously
	setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&opt, sizeof(opt));
#endif

	opt = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
	set_so_reuseport(listenfd);

	if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
	{
		return 0;
	}

	if(listen(listenfd, 5) < 0)
	{
		return 0;
	}
	return listenfd;
}

static pthread_mutex_t event_handler_lock = PTHREAD_MUTEX_INITIALIZER;

void event_handler(int32_t UNUSED(signal))
{
	struct stat pmt_info;
	char dest[1024];
	struct dirent **entries;
	int32_t i, n;
	int32_t pmt_fd = -1;
	uint8_t mbuf[2048]; // dirty fix: larger buffer needed for CA PMT mode 6 with many parallel channels to decode

	if(dvbapi_client != cur_client())
	{
		return;
	}

	SAFE_MUTEX_LOCK(&event_handler_lock);
	if(cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || cfg.dvbapi_boxtype == BOXTYPE_SAMYGO)
	{
		pausecam = 0;
	}
	else
	{
		int32_t standby_fd = open(STANDBY_FILE, O_RDONLY);
		pausecam = (standby_fd > 0) ? 1 : 0;
		if(standby_fd > 0)
		{
			int32_t ret = close(standby_fd);
			if(ret < 0)
			{
				cs_log("ERROR: Could not close standby fd (errno=%d %s)", errno, strerror(errno));
			}
		}
	}

	if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX || cfg.dvbapi_pmtmode == 1)
	{
		SAFE_MUTEX_UNLOCK(&event_handler_lock);
		return;
	}

	for(i = 0; i < MAX_DEMUX; i++)
	{
		if(demux[i].pmt_file[0] != 0)
		{
			snprintf(dest, sizeof(dest), "%s%s", TMPDIR, demux[i].pmt_file);
			pmt_fd = open(dest, O_RDONLY);
			if(pmt_fd > 0)
			{
				if(fstat(pmt_fd, &pmt_info) != 0)
				{
					int32_t ret = close(pmt_fd);
					if(ret < 0)
					{
						cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
					}
					continue;
				}

				if((time_t)pmt_info.st_mtime != demux[i].pmt_time)
				{
					dvbapi_stop_descrambling(i, 0);
				}

				int32_t ret = close(pmt_fd);
				if(ret < 0)
				{
					cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
				}
				continue;
			}
			else
			{
				cs_log("Demuxer %d Unable to open PMT file %s -> stop descrambling!", i, dest);
				dvbapi_stop_descrambling(i, 0);
			}
		}
	}

	if(disable_pmt_files)
	{
		SAFE_MUTEX_UNLOCK(&event_handler_lock);
		return;
	}

	n = scandir(TMPDIR, &entries, NULL, NULL);
	if (n==-1)
	{
		cs_log_dbg(D_DVBAPI, "scandir failed (errno=%d %s)", errno, strerror(errno));
		SAFE_MUTEX_UNLOCK(&event_handler_lock);
		return;
	}
	while(n--)
	{
		if(cs_strlen(entries[n]->d_name) < 7)
		{
			free(entries[n]);
			continue;
		}

		if(strncmp(entries[n]->d_name, "pmt", 3) != 0 || strncmp(entries[n]->d_name + cs_strlen(entries[n]->d_name) - 4, ".tmp", 4) != 0)
		{
			free(entries[n]);
			continue;
		}

#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		struct s_dvbapi_priority *p;
		for(p = dvbapi_priority; p != NULL; p = p->next) // stapi: check if there is a device connected to this pmt file!
		{
			if(p->type != 's') { continue; } // stapi rule?
			if(strcmp(entries[n]->d_name, p->pmtfile) != 0) { continue; } // same file?
			break; // found match!
		}

		if(p == NULL)
		{
			cs_log_dbg(D_DVBAPI, "No matching S: line in oscam.dvbapi for pmtfile %s -> skip!", entries[n]->d_name);
			free(entries[n]);
			continue;
		}
#endif

		if (!cs_strlen(TMPDIR))
		{
			cs_log_dbg(D_DVBAPI, "BUG! cs_strlen(TMPDIR)!!!\n");
			free(entries[n]);
			continue;
		}

		if (!cs_strlen(entries[n]->d_name))
		{
			cs_log_dbg(D_DVBAPI, "BUG! cs_strlen(entries[n]->d_name)!!!\n");
			free(entries[n]);
			continue;
		}

		if((cs_strlen(entries[n]->d_name) + cs_strlen(TMPDIR) - 1) > sizeof(dest))
		{
			cs_log_dbg(D_DVBAPI, "BUG! Sum of the (d_name + TMPDIR) = %u > sizeof(dest) !!!\n", (unsigned int)(cs_strlen(entries[n]->d_name) + cs_strlen(TMPDIR) - 1));
			free(entries[n]);
			continue;
		}
		else
		{
			memcpy(dest, TMPDIR, cs_strlen(TMPDIR));
			memcpy(dest + cs_strlen(TMPDIR), entries[n]->d_name, cs_strlen(entries[n]->d_name));
			dest[cs_strlen(TMPDIR) + cs_strlen(entries[n]->d_name)] = '\0';
			pmt_fd = open(dest, O_RDONLY);
		}

		if(pmt_fd < 0)
		{
			free(entries[n]);
			continue;
		}

		if(fstat(pmt_fd, &pmt_info) != 0)
		{
			int32_t ret = close(pmt_fd);
			if(ret < 0)
			{
				cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
			}
			free(entries[n]);
			continue;
		}

		int32_t found = 0;

		for(i = 0; i < MAX_DEMUX; i++)
		{
			if(strcmp(demux[i].pmt_file, entries[n]->d_name) == 0)
			{
				if((time_t)pmt_info.st_mtime == demux[i].pmt_time)
				{
					found = 1;
					break;
				}
			}
		}

		if(found)
		{
			int32_t ret = close(pmt_fd);
			if(ret < 0)
			{
				cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
			}
			free(entries[n]);
			continue;
		}
		cs_log_dbg(D_DVBAPI, "found pmt file %s", dest);
		cs_sleepms(100);

		uint32_t len = read(pmt_fd, mbuf, sizeof(mbuf));
		int32_t ret = close(pmt_fd);

		if(ret < 0)
		{
			cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
		}

		if(len < 1)
		{
			cs_log_dbg(D_DVBAPI, "pmt file %s have invalid len!", dest);
			free(entries[n]);
			continue;
		}

		int32_t demux_id;
#ifdef QBOXHD
		uint32_t j1, j2;

		// QboxHD pmt.tmp is the full capmt written as a string of hex values
		// pmt.tmp must be longer than 3 bytes (6 hex chars) and even length
		if((len < 6) || ((len % 2) != 0) || ((len / 2) > sizeof(dest)))
		{
			cs_log_dbg(D_DVBAPI, "error parsing QboxHD pmt.tmp, incorrect length");
			free(entries[n]);
			continue;
		}

		for(j2 = 0, j1 = 0; j2 < len; j2 += 2, j1++)
		{
			unsigned int tmp;
			if(sscanf((char *)mbuf + j2, "%02X", &tmp) != 1)
			{
				cs_log_dbg(D_DVBAPI, "error parsing QboxHD pmt.tmp, data not valid in position %d", j2);
				SAFE_MUTEX_UNLOCK(&event_handler_lock);
				return;
			}
			else
			{
				memcpy(dest + j1, &tmp, 4);
			}
		}

		cs_log_dump_dbg(D_DVBAPI, (uint8_t *)dest, len / 2, "QboxHD pmt.tmp:");
		demux_id = dvbapi_parse_capmt((uint8_t *)dest + 4, (len / 2) - 4, -1, entries[n]->d_name, 0, 0);
#else
		if(len > sizeof(dest))
		{
			cs_log_dbg(D_DVBAPI, "event_handler() dest buffer is to small for pmt data!");
			free(entries[n]);
			continue;
		}

		if(len < 16)
		{
			cs_log_dbg(D_DVBAPI, "event_handler() received pmt is too small! (%d < 16 bytes!)", len);
			free(entries[n]);
			continue;
		}

		cs_log_dump_dbg(D_DVBAPI, mbuf, len, "PMT file:"); // Original PMT file

		// Do some tidying on the PMT file to make it compatible with the CA PMT parser
		dest[0] = CA_PMT_LIST_ONLY;
		memcpy(dest + 1, mbuf + 3, 2); // program_number
		uint16_t pmt_program_info_length = b2i(2, mbuf + 10) & 0x0FFF;
		i2b_buf(2, pmt_program_info_length + 1, (uint8_t *)dest + 4);
		dest[6] = CA_PMT_CMD_OK_DESCRAMBLING;
		memcpy(dest + 7, mbuf + 12, len - 12 - 4);

		cs_log_dump_dbg(D_DVBAPI, (uint8_t *)dest, 7 + len - 12 - 4, "CA PMT:"); // Actual CA PMT message
		demux_id = dvbapi_parse_capmt((uint8_t *)dest, 7 + len - 12 - 4, -1, entries[n]->d_name, 0, 0);
#endif

		if(demux_id >= 0)
		{
			cs_strncpy(demux[demux_id].pmt_file, entries[n]->d_name, sizeof(demux[demux_id].pmt_file));
			demux[demux_id].pmt_time = (time_t)pmt_info.st_mtime;
		}
		free(entries[n]);

		if(cfg.dvbapi_pmtmode == 3)
		{
			disable_pmt_files = 1;
			break;
		}
	}
	free(entries);
	SAFE_MUTEX_UNLOCK(&event_handler_lock);
}

void *dvbapi_event_thread(void *cli)
{
	struct s_client *client = (struct s_client *) cli;
	SAFE_SETSPECIFIC(getclient, client);
	set_thread_name(__func__);

	while(!exit_oscam)
	{
		cs_sleepms(750);
		event_handler(0);
	}
	return NULL;
}

void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer, int32_t len, uint32_t msgid)
{
	struct s_ecmpid *curpid = NULL;
	int32_t pid = demux[demux_id].demux_fd[filter_num].pidindex;
	uint16_t filtertype = demux[demux_id].demux_fd[filter_num].type;
	uint16_t sctlen = SCT_LEN(buffer);

	if(sctlen < 4)
	{
		cs_log_dbg(D_DVBAPI, "Received filter data with invalid section length!");
		return;
	}

	if(len < sctlen)
	{
		cs_log_dbg(D_DVBAPI, "Received filter data with total length 0x%03X but section length is 0x%03X -> invalid length!", len, sctlen);
		return;
	}

	if(demux_id < 0 || demux_id >= MAX_DEMUX)
	{
		cs_log("dvbapi_process_input(): error - received invalid demux_id (%d)", demux_id);
		return;
	}

	if(filter_num < 0 || filter_num >= MAX_FILTER)
	{
		cs_log("dvbapi_process_input(): error - received invalid filter_num (%d)", filter_num);
		return;
	}

	if(pid != -1 && filtertype == TYPE_ECM)
	{
		curpid = &demux[demux_id].ECMpids[pid];
	}

	int32_t filt_match = filtermatch(buffer, filter_num, demux_id, sctlen); // acts on all filters (sdt/emm/ecm)
	if(!filt_match)
	{
		cs_log_dbg(D_DVBAPI,"Demuxer %d receiver returned data not matching the filter -> delivered filter data discarded!", demux_id);
			return;
	}

	if(curpid && curpid->tries <= 0xF0 && filtertype == TYPE_ECM)
	{
		curpid->irdeto_maxindex = 0;
		curpid->irdeto_curindex = 0xFE;
		curpid->tries = 0xFE; // reset timeout retry flag
		curpid->irdeto_cycle = 0xFE; // reset irdetocycle
		curpid->table = 0;
		curpid->checked = 4; // flag ecmpid as checked
		curpid->status = -1; // flag ecmpid as unusable

		if(pid == demux[demux_id].pidindex)
		{
			// current pid delivered problems so this pid isn't
			// being used to descramble any longer -> clear pidindex
			demux[demux_id].pidindex = -1;

			// remove this pid from channel cache since we had no founds on any ecmpid!
			dvbapi_edit_channel_cache(demux_id, pid, 0);
		}

		dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter!
		return;
	}

	if(filtertype == TYPE_ECM && curpid)
	{
		uint32_t chid = 0x10000;
		int8_t pvu_skip = 0;
		ECM_REQUEST *er;

		if(len != 0) // len = 0 receiver encountered an internal bufferoverflow!
		{
			cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched ECM data (length = 0x%03X):",
				demux_id, filter_num + 1, sctlen);

			if(sctlen > MAX_ECM_SIZE) // ecm too long to handle!
			{
				cs_log_dbg(D_DVBAPI, "Received data with total length 0x%03X but max supported ECM length is 0x%03X -> Please report!",
					sctlen, MAX_ECM_SIZE);

				curpid->tries -= 0x0E;

				return;
			}

			if(!(buffer[0] == 0x80 || buffer[0] == 0x81))
			{
				cs_log_dbg(D_DVBAPI, "Received an ECM with invalid ecmtable ID %02X -> ignoring!", buffer[0]);
				curpid->tries--;

				return;
			}

#ifdef WITH_EMU
			if(caid_is_powervu(curpid->CAID)) // ecm counter for powervu
			{
				pvu_skip = 1;

				if(sctlen - 11 > buffer[9])
				{
					if(buffer[11 + buffer[9]] > curpid->pvu_counter
						|| (curpid->pvu_counter == 255 && buffer[11 + buffer[9]] == 0)
						|| ((curpid->pvu_counter - buffer[11 + buffer[9]]) > 5))
					{
						curpid->pvu_counter = buffer[11 + buffer[9]];
						pvu_skip = 0;
					}
				}
			}
#endif
			// wait for odd / even ecm change (only not for irdeto!)
			if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip)
			{
				if(!(er = get_ecmtask()))
				{
					return;
				}
				er->srvid = demux[demux_id].program_number;

#ifdef WITH_STAPI5
				cs_strncpy(er->dev_name, dev_list[demux[demux_id].dev_index].name, sizeof(dev_list[demux[demux_id].dev_index].name));
#endif
				er->tsid = demux[demux_id].tsid;
				er->onid = demux[demux_id].onid;
				er->pmtpid = demux[demux_id].pmtpid;
				er->ens = demux[demux_id].ens;
				er->caid = curpid->CAID;
				er->pid = curpid->ECM_PID;
				er->prid = curpid->PROVID;
				er->vpid = curpid->VPID;
				er->ecmlen = sctlen;
				memcpy(er->ecm, buffer, er->ecmlen);
				chid = get_subid(er); // fetch chid or fake chid
				er->chid = chid;
				er->msgid = msgid;
				dvbapi_set_section_filter(demux_id, er, filter_num);
				NULLFREE(er);
				return;
			}

			if(caid_is_irdeto(curpid->CAID))
			{
				// 80 70 39 53 04 05 00 88
				// 81 70 41 41 01 06 00 13 00 06 80 38 1F 52 93 D2
				//if(buffer[5]>20) return;
				if(curpid->irdeto_maxindex != buffer[5]) // 6, register max irdeto index
				{
					cs_log_dbg(D_DVBAPI, "Found %d IRDETO ECM CHIDs", buffer[5] + 1);
					curpid->irdeto_maxindex = buffer[5]; // numchids = 7 (0..6)
				}
			}
		}

		if(!(er = get_ecmtask()))
		{
			return;
		}
		er->srvid = demux[demux_id].program_number;

#ifdef WITH_STAPI5
		cs_strncpy(er->dev_name, dev_list[demux[demux_id].dev_index].name, sizeof(dev_list[demux[demux_id].dev_index].name));
#endif

		er->tsid = demux[demux_id].tsid;
		er->onid = demux[demux_id].onid;
		er->pmtpid = demux[demux_id].pmtpid;
		er->ens = demux[demux_id].ens;
		er->caid = curpid->CAID;
		er->pid = curpid->ECM_PID;
		er->prid = curpid->PROVID;
		er->vpid = curpid->VPID;
		er->ecmlen = sctlen;
		memcpy(er->ecm, buffer, er->ecmlen);
		er->msgid = (msgid > 0) ? msgid : demux[demux_id].msgid;
		chid = get_subid(er); // fetch chid or fake chid
		uint32_t fixedprovid = chk_provid(er->ecm, er->caid);

		if(fixedprovid && fixedprovid != er->prid)
		{
			cs_log_dbg(D_DVBAPI, "Fixing provid ecmpid %d from %06X -> %06X", pid, curpid->PROVID, fixedprovid);
			curpid->PROVID = fixedprovid;

			if(!USE_OPENXCAS)
			{
				cs_log_dbg(D_DVBAPI, "Fixing provid filter %d from %06X -> %06X",
					filter_num + 1, demux[demux_id].demux_fd[filter_num].provid, fixedprovid);

				demux[demux_id].demux_fd[filter_num].provid = fixedprovid;
			}
			cs_log_dbg(D_DVBAPI, "Fixing provid ecmrequest from %06X -> %06X", er->prid, fixedprovid);
			er->prid = fixedprovid;
		}
		er->chid = chid;

		// only used on receiver internal buffer overflow
		// to get quickly fresh ecm filterdata otherwise freezing!
		if(len == 0)
		{
			curpid->table = 0;
			dvbapi_set_section_filter(demux_id, er, filter_num);
			NULLFREE(er);
			return;
		}

		if(caid_is_irdeto(curpid->CAID))
		{
			if(curpid->irdeto_curindex != buffer[4]) // old style wrong irdeto index
			{
				if(curpid->irdeto_curindex == 0xFE) // check if this ecmfilter just started up
				{
					// on startup set the current index to the irdeto index of the ecm
					curpid->irdeto_curindex = buffer[4];
				}
				else // we are already running and not interested in this ecm
				{
					if(curpid->table != buffer[0]) // fix for receivers not supporting section filtering
					{
						curpid->table = 0;
					}

					// set ecm filter to odd + even since
					// this ecm doesn't match with current irdeto index
					dvbapi_set_section_filter(demux_id, er, filter_num);

					NULLFREE(er);
					return;
				}
			}
			else // fix for receivers not supporting section filtering
			{
				if(curpid->table == buffer[0])
				{
					NULLFREE(er);
					return;
				}
			}
			cs_log_dbg(D_DVBAPI, "Demuxer %d ECMTYPE %02X CAID %04X PROVID %06X ECMPID %04X IRDETO INDEX %02X MAX INDEX %02X CHID %04X CYCLE %02X VPID %04X",
				demux_id, er->ecm[0], er->caid, er->prid, er->pid, er->ecm[4], er->ecm[5], er->chid, curpid->irdeto_cycle, er->vpid);
		}
		else
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d ECMTYPE %02X CAID %04X PROVID %06X ECMPID %04X FAKECHID %04X (unique part in ecm)",
				demux_id, er->ecm[0], er->caid, er->prid, er->pid, er->chid);
		}

		// check for matching chid (unique ecm part in case of non-irdeto cas)
		// plus added fix for seca2 monthly changing fakechid
		if((curpid->CHID < 0x10000) && !((chid == curpid->CHID) || ((curpid->CAID >> 8 == 0x01) && (chid & 0xF0FF) == (curpid->CHID & 0xF0FF))))
		{
			if(caid_is_irdeto(curpid->CAID))
			{
				// if same: we cycled all indexes but no luck!
				if((curpid->irdeto_cycle < 0xFE) && (curpid->irdeto_cycle == curpid->irdeto_curindex))
				{
					struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(demux_id, pid, 'p');

					// forced pid? keep trying the forced ecmpid, no force kill ecm filter
					if(!forceentry || !forceentry->force)
					{
						if(curpid->checked == 2)
						{
							curpid->checked = 4;
						}

						if(curpid->checked == 1)
						{
							curpid->checked = 2;
							curpid->CHID = 0x10000;
						}

						dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter!
						NULLFREE(er);
						return;
					}
				}
				curpid->irdeto_curindex++; // set check on next index

				if(curpid->irdeto_cycle == 0xFE)
				{
					curpid->irdeto_cycle = buffer[4]; // on startup set to current irdeto index
				}

				if(curpid->irdeto_curindex > curpid->irdeto_maxindex)
				{
					curpid->irdeto_curindex = 0; // check if we reached max irdeto index, if so reset to 0
				}
				curpid->table = 0;

				// set ecm filter to odd + even since
				// this ecm doesn't match with current irdeto index
				dvbapi_set_section_filter(demux_id, er, filter_num);

				NULLFREE(er);
				return;
			}
			else // all non irdeto cas systems
			{
				struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(demux_id, pid, 'p');
				curpid->table = 0;

				// set ecm filter to odd + even since
				// this ecm doesn't match with current irdeto index
				dvbapi_set_section_filter(demux_id, er, filter_num);

				if(forceentry && forceentry->force)
				{
					NULLFREE(er);
					return; // forced pid? keep trying the forced ecmpid!
				}

				if(curpid->checked == 2)
				{
					curpid->checked = 4;
				}

				if(curpid->checked == 1)
				{
					curpid->checked = 2;
					curpid->CHID = 0x10000;
				}

				dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter!
				NULLFREE(er);
				return;
			}
		}

		struct s_dvbapi_priority *p;
		for(p = dvbapi_priority; p != NULL; p = p->next)
		{
			if(p->type != 'l'
				|| (p->caid && p->caid != curpid->CAID)
				|| (p->provid && p->provid != curpid->PROVID)
				|| (p->ecmpid && p->ecmpid != curpid->ECM_PID)
				|| (p->srvid && p->srvid != demux[demux_id].program_number))
			{
				continue;
			}

			if((uint)p->delay == sctlen && p->force < 6)
			{
				p->force++;
				NULLFREE(er);
				return;
			}

			if(p->force >= 6)
			{
				p->force = 0;
			}
		}

		if(!curpid->PROVID)
		{
			curpid->PROVID = chk_provid(buffer, curpid->CAID);
		}

		if(caid_is_irdeto(curpid->CAID)) // irdeto: wait for the correct index
		{
			if(buffer[4] != curpid->irdeto_curindex)
			{
				curpid->table = 0;

				// set ecm filter to odd + even since
				// this ecm doesn't match with current irdeto index
				dvbapi_set_section_filter(demux_id, er, filter_num);

				NULLFREE(er);
				return;
			}
		}

		// we have an ecm with the correct irdeto index (or fakechid)
		for(p = dvbapi_priority; p != NULL ; p = p->next) // check for ignore!
		{
			if((p->type != 'i')
				|| (p->caid && p->caid != curpid->CAID)
				|| (p->provid && p->provid != curpid->PROVID)
				|| (p->ecmpid && p->ecmpid != curpid->ECM_PID)
				|| (p->pidx && p->pidx - 1 != pid)
				|| (p->srvid && p->srvid != demux[demux_id].program_number))
			{
				continue;
			}

			// found an ignore chid match with current ecm -> ignoring this irdeto index
			if(p->type == 'i' && (p->chid < 0x10000 && p->chid == chid))
			{
				curpid->irdeto_curindex++;
				if(curpid->irdeto_cycle == 0xFE)
				{
					curpid->irdeto_cycle = buffer[4]; // on startup set to current irdeto index
				}

				if(curpid->irdeto_curindex > curpid->irdeto_maxindex) // check if curindex is over the max
				{
					curpid->irdeto_curindex = 0;
				}
				curpid->table = 0;

				// irdeto: wait for the correct index + check if we cycled all
				if(caid_is_irdeto(curpid->CAID) && (curpid->irdeto_cycle != curpid->irdeto_curindex))
				{
					// set ecm filter to odd + even since this chid has to be ignored!
					dvbapi_set_section_filter(demux_id, er, filter_num);
				}
				else // this fakechid has to be ignored, kill this filter!
				{
					if(curpid->checked == 2)
					{
						curpid->checked = 4;
					}

					if(curpid->checked == 1)
					{
						curpid->checked = 2;
						curpid->CHID = 0x10000;
					}

					dvbapi_stop_filternum(demux_id, filter_num, msgid); // stop this ecm filter!
				}
				NULLFREE(er);
				return;
			}
		}

		if(er)
		{
			curpid->table = er->ecm[0];
		}

		request_cw(dvbapi_client, er, demux_id, 1); // register this ecm for delayed ecm response check
		return; // end of ecm filterhandling!
	}

	if(filtertype == TYPE_EMM)
	{
		if(len != 0) // len = 0 receiver encountered an internal buffer overflow!
		{
			cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched EMM data (length = 0x%03X):",
				demux_id, filter_num + 1, sctlen);

			if(sctlen > MAX_EMM_SIZE) // emm too long to handle!
			{
				cs_log_dbg(D_DVBAPI, "Received data with total length 0x%03X but max supported EMM length is 0x%03X -> Please report!",
					sctlen, MAX_EMM_SIZE);

				return;
			}
		}
		else
		{
			return; // just skip on internal buffer overflow
		}

#ifdef WITH_EMU
		if(caid_is_director(demux[demux_id].demux_fd[filter_num].caid))
		{
			uint32_t i;
			uint32_t emmhash;

			if(sctlen < 4)
			{
				return;
			}

			for(i = 0; i + 2 < sctlen; i++)
			{
				if(buffer[i] == 0xF0 && (buffer[i + 2] == 0xE1 || buffer[i + 2] == 0xE4))
				{
					emmhash = (buffer[3] << 8) | buffer[sctlen - 2];
					if(demux[demux_id].demux_fd[filter_num].cadata == emmhash)
					{
						return;
					}

					demux[demux_id].demux_fd[filter_num].cadata = emmhash;
					dvbapi_process_emm(demux_id, filter_num, buffer, sctlen);
					return;
				}
			}
			return;
		}
#endif
		// fix to handle more than one irdeto emm packet
		uint8_t *pbuf = buffer;
		int32_t done = 0;
		int32_t unhandled = len;

		while(len > done)
		{
			pbuf += done;
			sctlen = SCT_LEN(pbuf);

			if(unhandled < 4 || (int32_t)sctlen > unhandled || sctlen > MAX_EMM_SIZE || sctlen < 4)
			{
				break;
			}

			dvbapi_process_emm(demux_id, filter_num, pbuf, sctlen);
			done += sctlen;
			unhandled -= sctlen;
		}
	}

	if(filtertype == TYPE_SDT)
	{
		cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched SDT data (length = 0x%03X):",
			demux_id, filter_num + 1, sctlen);

		dvbapi_parse_sdt(demux_id, buffer, sctlen, msgid);
	}

	if(filtertype == TYPE_PAT)
	{
		cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched PAT data (length = 0x%03X):",
			demux_id, filter_num + 1, sctlen);

		dvbapi_parse_pat(demux_id, buffer, sctlen, msgid);
	}

	if(filtertype == TYPE_PMT)
	{
		cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched PMT data (length = 0x%03X):",
			demux_id, filter_num + 1, sctlen);

		dvbapi_parse_pmt(demux_id, buffer, sctlen, msgid);
	}

	if(filtertype == TYPE_CAT)
	{
		cs_log_dump_dbg(D_DVBAPI, buffer, sctlen, "Demuxer %d filter %d fetched CAT data (length = 0x%03X):",
			demux_id, filter_num + 1, sctlen);

		dvbapi_parse_cat(demux_id, buffer, sctlen, msgid);
	}
}

static int32_t dvbapi_recv(int32_t connfd, uint8_t *mbuf, size_t rlen)
{
	ssize_t len = cs_recv(connfd, mbuf, rlen, MSG_DONTWAIT);

	if((len == -1 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) || (len == 0))
	{
		return -1;
	}

	if(len == -1)
	{
		return 0;
	}

	return len;
}

static uint16_t dvbapi_get_nbof_missing_header_bytes(uint8_t *mbuf, uint16_t mbuf_len, uint32_t msgid_size)
{
	uint16_t commandsize = 4;
	commandsize += msgid_size;
	if(mbuf_len < commandsize)
	{
		return commandsize - mbuf_len;
	}
	else
	{
		mbuf += msgid_size;
		uint32_t opcode = b2i(4, mbuf);

		if((opcode & 0xFFFFF000) == DVBAPI_AOT_CA)
		{
			if(mbuf[3] & 0x80)
			{
				uint32_t size = mbuf[3] & 0x7F;
				if(mbuf_len < (commandsize + size))
				{
					return (commandsize + size) - mbuf_len;
				}
			}
			return 0;
		}
		else
		{
			switch (opcode)
			{
				case DVBAPI_FILTER_DATA:
					commandsize = 9;
					commandsize += msgid_size;
					if(mbuf_len < commandsize)
					{
						return commandsize - mbuf_len;
					}
					return 0;

				case DVBAPI_CLIENT_INFO:
					commandsize = 7;
					commandsize += msgid_size;
					if(mbuf_len < commandsize)
					{
						return commandsize - mbuf_len;
					}
					return 0;

				default:
					return 0;
			}
		}
	}
}

static void set_chunksize_data_len_to_invalid(uint16_t *chunksize, uint16_t *data_len)
{
	(*chunksize) = 1;
	(*data_len) = 1;
}

static void log_packeterror(uint16_t mbuf_len, const char* command)
{
	cs_log("dvbapi_get_packet_size(): error - buffer length (%" PRIu16 ") too short for %s", mbuf_len, command);
}

static bool is_commandsize_valid(uint32_t commandsize, uint16_t mbuf_len, const char* command)
{
	bool isValid = mbuf_len >= commandsize;
	if(!isValid)
	{
		log_packeterror(mbuf_len, command);
	}
	return isValid;
}

static uint8_t get_asn1packetsize(uint8_t *mbuf, uint16_t mbuf_len, const char *command, uint32_t *tmp_data_len)
{
	uint8_t sizebytes = 0;
	uint8_t commandsize = 4;
	*tmp_data_len = mbuf[3] & 0x7F;
	if(mbuf[3] & 0x80)
	{
		sizebytes = *tmp_data_len;
		if(is_commandsize_valid(3 + sizebytes, mbuf_len, command))
		{
			*tmp_data_len = b2i(sizebytes, mbuf + 4);
		}
		else
		{
			return 0;
		}
	}
	return commandsize + sizebytes;
}

static void dvbapi_get_packet_size(uint8_t *mbuf, uint16_t mbuf_len, uint16_t *chunksize, uint16_t *data_len)
{
	//chunksize: size of complete chunk in the buffer (an opcode with the data)
	//data_len: variable for internal data length (eg. for the filter data size, PMT len)
	(*chunksize) = 0;
	(*data_len) = 0;

	if(mbuf_len < 4)
	{
		cs_log("dvbapi_get_packet_size(): error - buffer length (%" PRIu16 ") too short", mbuf_len);
		set_chunksize_data_len_to_invalid(chunksize, data_len);
		return;
	}

	int32_t commandsize = 0;
	char* command = "DVBAPI_UNKNOWN_COMMAND";
	uint32_t tmp_data_len = 0;
	uint32_t opcode = b2i(4, mbuf);

	switch (opcode)
	{
		case DVBAPI_AOT_CA_STOP:
		{
			command = "DVBAPI_AOT_CA_STOP";
			commandsize = get_asn1packetsize(mbuf, mbuf_len, command, &tmp_data_len);
			break;
		}
		case DVBAPI_FILTER_DATA:
		{
			command = "DVBAPI_FILTER_DATA";
			commandsize = 9;
			if(is_commandsize_valid(commandsize, mbuf_len, command))
			{
				tmp_data_len = b2i(2, mbuf + 7) & 0x0FFF;
			}
			break;
		}

		case DVBAPI_CLIENT_INFO:
		{
			command = "DVBAPI_CLIENT_INFO";
			commandsize = 7;
			if(is_commandsize_valid(commandsize, mbuf_len, command))
			{
				tmp_data_len = mbuf[6];
			}
			break;
		}

		default:
		{
			if((opcode & 0xFFFFFF00) == DVBAPI_AOT_CA_PMT)
			{
				command = "DVBAPI_AOT_CA_PMT";
				commandsize = get_asn1packetsize(mbuf, mbuf_len, command, &tmp_data_len);
				break;
			}
			else
			{
				cs_log("Unknown socket command received: 0x%08X", opcode);
			}
			break;
		}
	}

	if(tmp_data_len == 0 || commandsize == 0)
	{
		set_chunksize_data_len_to_invalid(chunksize, data_len);
		return;
	}

	if(tmp_data_len + commandsize > 0xFFFF)
	{
		cs_log("This packet is too big: %d bytes => truncated!", tmp_data_len);
		tmp_data_len = 0xFFFF - commandsize;
	}

	(*data_len) = tmp_data_len;
	(*chunksize) += commandsize + tmp_data_len;

	if(*chunksize > mbuf_len)
	{
		cs_log_dbg(D_DVBAPI, "This %s packet is incomplete => command length is (%" PRIu16 ")", command, *chunksize);
	}
	else
	{
		cs_log_dbg(D_DVBAPI, "This is a %s packet with size %d => lets process it!", command, (*chunksize));
	}
}

static void dvbapi_handlesockmsg(uint8_t *mbuf, uint16_t chunksize, uint16_t data_len, int32_t connfd, uint16_t *client_proto_version)
{
	uint32_t msgid = 0;
	if(*client_proto_version >= 3)
	{
		if(mbuf[0] != 0xa5)
		{
			cs_log("Error: network packet malformed! (no start)");
			return;
		}
		msgid = b2i(4, mbuf + 1);
		mbuf += 5;
	}

	uint32_t opcode = b2i(4, mbuf);
	switch(opcode)
	{
		case DVBAPI_FILTER_DATA:
		{
			int32_t demux_id = mbuf[4];
			int32_t filter_num = mbuf[5];

			if(demux_id < 0 || demux_id >= MAX_DEMUX)
			{
				cs_log("dvbapi_handlesockmsg(): error - received invalid demux_id (%d)", demux_id);
				break;
			}

			if(filter_num < 0 || filter_num >= MAX_FILTER)
			{
				cs_log("dvbapi_handlesockmsg(): error - received invalid filter_num (%d)", filter_num);
				break;
			}
			dvbapi_process_input(demux_id, filter_num, mbuf + 6, data_len + 3, msgid);
			break;
		}

		case DVBAPI_CLIENT_INFO:
		{
			uint16_t client_proto = b2i(2, mbuf + 4);
			NULLFREE(last_client_name);

			if(cs_malloc(&last_client_name, data_len + 1))
			{
				memcpy(last_client_name, &mbuf[7], data_len);
				last_client_name[data_len] = 0;
				cs_log("Client connected: '%s' (protocol version = %" PRIu16 ")", last_client_name, client_proto);
			}
			dvbapi_net_send(DVBAPI_SERVER_INFO, connfd, msgid, -1, -1, NULL, NULL, NULL, client_proto);

			// now the protocol handshake is complete set correct version so all further packets are sent with correct message id.
			(*client_proto_version) = client_proto;

			// setting the global var according to the client
			last_client_proto_version = client_proto;
			break;
		}

		case DVBAPI_AOT_CA_PMT:
		{
			cs_log_dbg(D_DVBAPI,"Received DVBAPI_AOT_CA_PMT object on socket %d:", connfd);
			dvbapi_parse_capmt(mbuf + (chunksize - data_len), data_len, connfd, NULL, *client_proto_version, msgid);
			break;
		}

		case DVBAPI_AOT_CA_STOP:
		{
			cs_log_dbg(D_DVBAPI, "Received DVBAPI_AOT_CA_STOP object on socket %d:", connfd);
			if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX || dvbapi_listenport_active)
			{
				int32_t i;
				int32_t demux_index = mbuf[7];
				for(i = 0; i < MAX_DEMUX; i++)
				{
					// 0xff demux_index is a wildcard => close all related demuxers
					if(demux_index == 0xff)
					{
						if(demux[i].socket_fd == connfd)
						{
							dvbapi_stop_descrambling(i, msgid);
						}
					}
					else if(demux[i].demux_index == demux_index)
					{
						dvbapi_stop_descrambling(i, msgid);
						break;
					}
				}

				// ipbox fix
				if(cfg.dvbapi_boxtype == BOXTYPE_IPBOX)
				{
					// check do we have any demux running on this fd
					int16_t execlose = 1;
					for(i = 0; i < MAX_DEMUX; i++)
					{
						if(demux[i].socket_fd == connfd)
						{
							execlose = 0;
							break;
						}
					}
					if(execlose)
					{
						int32_t ret = close(connfd);
						if(ret < 0)
						{
							cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
						}
					}
				}
			}
			else if(cfg.dvbapi_pmtmode != 6)
			{
				int32_t ret = close(connfd);
				if(ret < 0)
				{
					cs_log("ERROR: Could not close PMT fd (errno=%d %s)", errno, strerror(errno));
				}
			}
			break;
		}
		default:
		{
			if((opcode & 0xFFFFFF00) == DVBAPI_AOT_CA_PMT)
			{
				cs_log_dbg(D_DVBAPI, "Received DVBAPI_AOT_CA_PMT object on socket %d:", connfd);
				dvbapi_parse_capmt(mbuf + (chunksize - data_len), data_len, connfd, NULL, *client_proto_version, msgid);
			}
			else
			{
				cs_log("Unknown socket command received: 0x%08X", opcode);
			}
			break;
		}
	}
}

static bool dvbapi_handlesockdata(int32_t connfd, uint8_t *mbuf, uint16_t mbuf_size, uint16_t unhandled_len, uint16_t *new_unhandled_len, uint16_t *client_proto_version)
{
	int32_t recv_result;
	uint16_t chunksize = 1, data_len = 1;
	uint8_t packet_count = 1;
	uint32_t msgid_size = 0;
	uint16_t missing_header_bytes = 0;
	if(*client_proto_version >= 3)
	{
		msgid_size = 5;
	}

	do
	{
		missing_header_bytes = dvbapi_get_nbof_missing_header_bytes(mbuf, unhandled_len, msgid_size);

		if(missing_header_bytes != 0)
		{
			// read first few bytes so we know packet type and length
			cs_log_dbg(D_TRACE, "%s reading %" PRIu16 " bytes from connection fd %d", (unhandled_len == 0) ? "Try" : "Continue", missing_header_bytes, connfd);

			recv_result = dvbapi_recv(connfd, mbuf + unhandled_len, mbuf_size - unhandled_len);
			if(recv_result < 1)
			{
				(*new_unhandled_len) = unhandled_len;
				return (recv_result != -1);
			}
			else
			{
				unhandled_len += recv_result;
				if(unhandled_len < missing_header_bytes)
				{
					(*new_unhandled_len) = unhandled_len;
					return true;
				}
			}
		}

		cs_log_dump_dbg(D_DVBAPI, mbuf, unhandled_len, "Got packetdata (msgid size: %d, clientprotocol: %d)", msgid_size, *client_proto_version);
		dvbapi_get_packet_size(mbuf+msgid_size, unhandled_len-msgid_size, &chunksize, &data_len);

		chunksize+=msgid_size;
		if(chunksize > mbuf_size)
		{
			cs_log("***** WARNING: SOCKET DATA BUFFER OVERFLOW (%" PRIu16 " bytes), PLEASE REPORT! ****** ", chunksize);
			(*new_unhandled_len) = 0;
			return true;
		}

		if(unhandled_len < chunksize) // we are missing some bytes, try to read them
		{
			cs_log_dbg(D_TRACE, "Continue to read the missing %d bytes from connection fd %d", chunksize - unhandled_len, connfd);
			recv_result = dvbapi_recv(connfd, mbuf + unhandled_len, mbuf_size - unhandled_len);
			if(recv_result < 1)
			{
				(*new_unhandled_len) = unhandled_len;
				return (recv_result != -1);
			}
			else
			{
				unhandled_len += recv_result;
				if(unhandled_len < chunksize)
				{
					(*new_unhandled_len) = unhandled_len;
					return true;
				}
			}
		}

		dvbapi_handlesockmsg(mbuf, chunksize-msgid_size, data_len, connfd, client_proto_version);

		unhandled_len -= chunksize;
		if(unhandled_len > 0)
		{
			memmove(mbuf, mbuf + chunksize, unhandled_len);
		}
	} while(unhandled_len != 0 && packet_count++ < 8);

	cs_log_dbg(D_DVBAPI, "Processing socketdata completed after %d packets with %d bytes left unprocessed", packet_count, unhandled_len);

	(*new_unhandled_len) = unhandled_len;
	return true;
}

static void add_to_assoc_fd(int sock)
{
	uint i;

	for(i = 0; i < MAX_ASSOC_FD; i++)
	{
		if(assoc_fd[i] == sock)
		{
			return; // do not add twice
		}
	}

	for(i = 0; i < MAX_ASSOC_FD; i++)
	{
		if(!assoc_fd[i])
		{
			assoc_fd[i] = sock;
			return;
		}
	}
}

static void del_from_assoc_fd(int sock)
{
	uint i;

	for(i = 0; i < MAX_ASSOC_FD; i++)
	{
		if(assoc_fd[i] == sock)
		{
			assoc_fd[i] = 0;
		}
	}
}

static void *dvbapi_main_local(void *cli)
{
	int32_t i, j, l;
	struct s_client *client = (struct s_client *)cli;
	client->thread = pthread_self();
	SAFE_SETSPECIFIC(getclient, cli);
	dvbapi_client = cli;
	int32_t maxpfdsize = (MAX_DEMUX * maxfilter) + MAX_DEMUX + 2;
	struct pollfd pfd2[maxpfdsize];
	struct timeb start, end; // start time poll, end time poll
#define PMT_SERVER_SOCKET "/tmp/.listen.camd.socket"
	struct sockaddr_un saddr;
	saddr.sun_family = AF_UNIX;
	cs_strncpy(saddr.sun_path, PMT_SERVER_SOCKET, sizeof(saddr.sun_path));
	int32_t rc, pfdcount, g, connfd, clilen;
	int32_t ids[maxpfdsize], fdn[maxpfdsize], type[maxpfdsize];
	struct SOCKADDR servaddr;
	ssize_t len = 0;
	static const uint16_t mbuf_size = 2048;
	uint8_t *mbuf;
	uint16_t unhandled_buf_len[maxpfdsize], unhandled_buf_used[maxpfdsize];
	uint8_t *unhandled_buf[maxpfdsize];
	struct s_auth *account;
	int32_t ok = 0;
	uint16_t client_proto_version[maxpfdsize];

	if(!cs_malloc(&mbuf, sizeof(uint8_t) * mbuf_size))
	{
		return NULL;
	}

	for(i = 0; i < maxpfdsize; i++)
	{
		unhandled_buf[i] = NULL;
		unhandled_buf_len[i] = 0;
		unhandled_buf_used[i] = 0;
		client_proto_version[i] = 0;
	}

	for(account = cfg.account; account != NULL; account = account->next)
	{
		if((ok = is_dvbapi_usr(account->usr)))
		{
			break;
		}
	}

	cs_auth_client(client, ok ? account : (struct s_auth *)(-1), "dvbapi");
	memset(demux, 0, sizeof(demux));

	for(i = 0; i < MAX_DEMUX; i++)
	{
		SAFE_MUTEX_INIT(&demux[i].answerlock, NULL);
		for(j = 0; j < MAX_ECM_PIDS; j++)
		{
			for(l = 0; l < MAX_STREAM_INDICES; l++)
			{
				demux[i].ECMpids[j].index[l] = INDEX_INVALID;
			}
		}
		demux[i].pidindex = -1;
		demux[i].curindex = -1;
	}

	memset(ca_fd, 0, sizeof(ca_fd));
	memset(assoc_fd, 0, sizeof(assoc_fd));
	dvbapi_read_priority();
	dvbapi_load_channel_cache();
	dvbapi_detect_api();

	if(selected_box == -1 || selected_api == -1)
	{
		cs_log("ERROR: Could not detect DVBAPI version.");
		free(mbuf);
		return NULL;
	}

	// detect box type first and then get descrambler info
	dvbapi_get_descrambler_info();

	if(cfg.dvbapi_pmtmode == 1)
	{
		disable_pmt_files = 1;
	}

	int32_t listenfd = -1;
	if(cfg.dvbapi_boxtype != BOXTYPE_IPBOX_PMT &&
		cfg.dvbapi_pmtmode != 2 && cfg.dvbapi_pmtmode != 5 && cfg.dvbapi_pmtmode != 6)
	{
		if(!dvbapi_listenport_active)
		{
			listenfd = dvbapi_init_listenfd();
		}
		else
		{
			listenfd = dvbapi_net_init_listenfd();
		}

		if(listenfd < 1)
		{
			cs_log("ERROR: Could not init socket: (errno=%d: %s)", errno, strerror(errno));
			free(mbuf);
			return NULL;
		}
	}

	for(i = 0; i < MAX_DEMUX; i++) // init all demuxers!
	{
		demux[i].pidindex = -1;
		demux[i].curindex = -1;
	}

	if(cfg.dvbapi_pmtmode != 4 && cfg.dvbapi_pmtmode != 5 && cfg.dvbapi_pmtmode != 6)
	{
		struct sigaction signal_action;
		signal_action.sa_handler = event_handler;
		sigemptyset(&signal_action.sa_mask);
		signal_action.sa_flags = SA_RESTART;
		sigaction(SIGRTMIN + 1, &signal_action, NULL);

		dir_fd = open(TMPDIR, O_RDONLY);
		if(dir_fd >= 0)
		{
			fcntl(dir_fd, F_SETSIG, SIGRTMIN + 1);
			fcntl(dir_fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_DELETE | DN_MULTISHOT);
			event_handler(SIGRTMIN + 1);
		}
	}
	else
	{
		int32_t ret = start_thread("dvbapi event", dvbapi_event_thread, (void *) dvbapi_client, NULL, 1, 0);
		if(ret)
		{
			free(mbuf);
			return NULL;
		}
	}

	if(listenfd != -1)
	{
		pfd2[0].fd = listenfd;
		pfd2[0].events = (POLLIN | POLLPRI);
		type[0] = 1;
	}

#if defined WITH_COOLAPI || defined WITH_COOLAPI2 || defined WITH_NEUTRINO
	int sysret = system("pzapit -rz");
	if(sysret == -1){
		// To avoid not used and correct error handling by not ignoring system return value
	}
#endif
	cs_ftime(&start); // register start time

	while(!exit_oscam)
	{
		if(pausecam) // for dbox2, STAPI or PC in standby mode don't parse any ecm/emm or try to start next filter
		{
			continue;
		}

		if(cfg.dvbapi_pmtmode == 6)
		{
			if(listenfd < 0)
			{
				cs_log("PMT mode 6: Connecting to enigma CA PMT listen socket...");

				// socket init
				if((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
				{
					cs_log("socket error (errno=%d %s)", errno, strerror(errno));
					listenfd = -1;
				}
				else if(connect(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
				{
					cs_log("socket connect error (errno=%d %s)", errno, strerror(errno));
					close(listenfd);
					listenfd = -1;
				}
				else
				{
					pfd2[0].fd = listenfd;
					pfd2[0].events = (POLLIN | POLLPRI);
					type[0] = 1;
					client_proto_version[0] = 0;
					cs_log("PMT mode 6: Successfully connected to CA PMT server (fd %d)", listenfd);
				}
			}

			if(listenfd == -1) // not connected!
			{
				cs_sleepms(1000);
				continue; // start fresh connect attempt!
			}
		}

		pfdcount = (listenfd > -1) ? 1 : 0;

		for(i = 0; i < MAX_ASSOC_FD; i++) // add all associated fds (this should include also demux[X].socket_fd)
		{
			if(assoc_fd[i])
			{
				pfd2[pfdcount].fd = assoc_fd[i];
				pfd2[pfdcount].events = (POLLIN | POLLPRI);
				client_proto_version[pfdcount] = last_client_proto_version;
				type[pfdcount++] = 1;
			}
		}

		for(i = 0; i < MAX_DEMUX; i++)
		{
			if(demux[i].program_number == 0)
			{
				continue; // only evalutate demuxers that have channels assigned
			}

			uint32_t ecmcounter = 0, emmcounter = 0;
			for(g = 0; g < maxfilter; g++)
			{
				if(demux[i].demux_fd[g].fd <= 0)
				{
					continue; // deny obvious invalid fd!
				}

				if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX
					&& selected_api != STAPI && selected_api != COOLAPI)
				{
					pfd2[pfdcount].fd = demux[i].demux_fd[g].fd;
					pfd2[pfdcount].events = (POLLIN | POLLPRI);
					ids[pfdcount] = i;
					fdn[pfdcount] = g;
					type[pfdcount++] = 0;
				}

				// count ecm filters to see if demuxing is possible anyway
#ifdef MODULE_STREAMRELAY
				if(cfg.dvbapi_demuxer_fix)
				{
					if(demux[i].demux_fd[g].type == TYPE_ECM || demux[i].demux_fd[g].type == 3 || demux[i].demux_fd[g].type == 6)
					{
						ecmcounter++;
					}
				}
				else
				{
#endif
					if(demux[i].demux_fd[g].type == TYPE_ECM)
					{
						ecmcounter++;
					}
#ifdef MODULE_STREAMRELAY
				}
#endif

				// count emm filters also
				if(demux[i].demux_fd[g].type == TYPE_EMM)
				{
					emmcounter++;
				}
			}

			// only produce log if something changed
			if(ecmcounter != demux[i].old_ecmfiltercount || emmcounter != demux[i].old_emmfiltercount)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d has %d ecmpids, %d streampids, %d ecmfilters and %d of max %d emmfilters",
					i, demux[i].ECMpidcount, demux[i].STREAMpidcount, ecmcounter, emmcounter, demux[i].max_emm_filter);

				demux[i].old_ecmfiltercount = ecmcounter; // save new amount of ecm filters
				demux[i].old_emmfiltercount = emmcounter; // save new amount of emm filters
			}

			// delayed emm start for non irdeto caids,
			// start emm cat if not already done for this demuxer!
			struct timeb now;
			cs_ftime(&now);
			int64_t gone;
			int8_t do_emm_start = (cfg.dvbapi_au > 0 && demux[i].emm_filter == -1 && demux[i].EMMpidcount == 0 && emmcounter == 0);
			int8_t do_sdt_start = (cfg.dvbapi_read_sdt && demux[i].sdt_filter == -1 && cfg.dvbapi_boxtype != BOXTYPE_SAMYGO);

			if(do_emm_start || do_sdt_start)
			{
				gone = comp_timeb(&now, &demux[i].emmstart);
				if(gone > 20 * 1000)
				{
					if(do_emm_start)
					{
						cs_ftime(&demux[i].emmstart); // trick to let emm fetching start after 30 seconds to speed up zapping
						dvbapi_start_cat_filter(i);
					}
				}

				if(gone > 5 * 1000)
				{
					if(do_sdt_start)
					{
						dvbapi_start_sdt_filter(i);
					}
				}
			}

			// early start for irdeto since they need emm before ecm
			// (pmt emmstart = 1 if detected caid 0x06)
			int32_t emmstarted = demux[i].emm_filter;

			// check every time since share readers might
			// give us new filters due to hexserial change
			if(cfg.dvbapi_au && demux[i].EMMpidcount > 0)
			{
				if(!emmcounter && emmstarted == -1)
				{
					demux[i].emmstart = now;
					dvbapi_start_emm_filter(i); // start emm filtering if emm pids are found
				}
				else
				{
					gone = comp_timeb(&now, &demux[i].emmstart);
					if(gone > 30 * 1000)
					{
						demux[i].emmstart = now;
						dvbapi_start_emm_filter(i); // start emm filtering delayed if filters already were running
						rotate_emmfilter(i); // rotate active emm filters
					}
				}
			}

			// Restart decoding all caids we have ecmpids but no ecm filters!
			if(ecmcounter == 0 && demux[i].ECMpidcount > 0)
			{
				int32_t started = 0;

				// avoid race: not all pids are asked and checked out yet!
				for(g = 0; g < demux[i].ECMpidcount; g++)
				{
					// check if prio run is done
					if(demux[i].ECMpids[g].checked == 0 && demux[i].ECMpids[g].status >= 0)
					{
						dvbapi_try_next_caid(i, 0, 0); // not done, so start next prio pid
						started = 1;
						break;
					}
				}

				if(started)
				{
					continue; // if started a filter proceed with next demuxer
				}

				// all usable pids (with prio) are tried, lets start over again without prio!
				if(g == demux[i].ECMpidcount)
				{
					// avoid race: not all pids are asked and checked out yet!
					for(g = 0; g < demux[i].ECMpidcount; g++)
					{
						// check if noprio run is done
						if(demux[i].ECMpids[g].checked == 2 && demux[i].ECMpids[g].status >= 0)
						{
							demux[i].ECMpids[g].irdeto_curindex = 0xFE;
							demux[i].ECMpids[g].irdeto_maxindex = 0;
							demux[i].ECMpids[g].irdeto_cycle = 0xFE;
							demux[i].ECMpids[g].tries = 0xFE;
							demux[i].ECMpids[g].table = 0;
							demux[i].ECMpids[g].CHID = 0x10000; // remove chid prio

							dvbapi_try_next_caid(i, 2, 0); // not done, so start next no prio pid
							started = 1;
							break;
						}
					}
				}

				if(started)
				{
					continue; // if started a filter proceed with next demuxer
				}

				if(g == demux[i].ECMpidcount) // all usable pids are tried, lets start over again!
				{
					if(demux[i].decodingtries == -1) // first redecoding attempt?
					{
						cs_ftime(&demux[i].decstart);

						// re-init some used things from second run (without prio)
						for(g = 0; g < demux[i].ECMpidcount; g++)
						{
							demux[i].ECMpids[g].checked = 0;
							demux[i].ECMpids[g].irdeto_curindex = 0xFE;
							demux[i].ECMpids[g].irdeto_maxindex = 0;
							demux[i].ECMpids[g].irdeto_cycle = 0xFE;
							demux[i].ECMpids[g].table = 0;
							demux[i].decodingtries = 0;

							// remove this pid from channel cache since we had no founds on any ecmpid!
							dvbapi_edit_channel_cache(i, g, 0);
						}
					}

					uint8_t number_of_enabled_pids = 0;
					demux[i].decodingtries++;
					dvbapi_resort_ecmpids(i);

					for(g = 0; g < demux[i].ECMpidcount; g++) // count number of enabled pids!
					{
						if(demux[i].ECMpids[g].status >= 0) number_of_enabled_pids++;
					}

					if(!number_of_enabled_pids)
					{
						if(demux[i].decodingtries == 10)
						{
							demux[i].decodingtries = 0;
							cs_log("Demuxer %d no enabled matching ecmpids -> decoding is waiting for matching readers!",i);
						}
					}
					else
					{
						cs_ftime(&demux[i].decend);
						demux[i].decodingtries = -1; // reset to first run again!
						gone = comp_timeb(&demux[i].decend, &demux[i].decstart);

						cs_log("Demuxer %d restarting decoding requests after %"PRId64" ms with %d enabled and %d disabled ecmpids!",
							i, gone, number_of_enabled_pids, (demux[i].ECMpidcount-number_of_enabled_pids));

						dvbapi_try_next_caid(i, 0, 0);
					}
				}
			}
		}

		rc = 0;
		while(!(listenfd == -1 && cfg.dvbapi_pmtmode == 6))
		{
			rc = poll(pfd2, pfdcount, 500);
			if(rc < 0) // error occured while polling for fd's with fresh data
			{
				if(errno == EINTR || errno == EAGAIN) // try again in case of interrupt
				{
					continue;
				}
				cs_log("ERROR: error on poll of %d fd's (errno=%d %s)", pfdcount, errno, strerror(errno));
				break;
			}
			else
			{
				break;
			}
		}

		if(rc > 0)
		{
			cs_ftime(&end); // register end time
			int64_t timeout = comp_timeb(&end, &start);
			if(timeout < 0)
			{
				cs_log("*** WARNING: BAD TIME AFFECTING WHOLE OSCAM ECM HANDLING ****");
			}
			cs_log_dbg(D_TRACE, "New events occurred on %d of %d handlers after %"PRId64" ms inactivity", rc, pfdcount, timeout);
			cs_ftime(&start); // register new start time for next poll
		}

		for(i = 0; i < pfdcount && rc > 0; i++)
		{
			if(pfd2[i].revents == 0) { continue; } // skip sockets with no changes
			rc--; //event handled!
			cs_log_dbg(D_TRACE, "Now handling fd %d that reported event %d", pfd2[i].fd, pfd2[i].revents);

			if(pfd2[i].revents & (POLLHUP | POLLNVAL | POLLERR))
			{
				if(type[i] == 1)
				{
					del_from_assoc_fd(pfd2[i].fd);

					for(j = 0; j < MAX_DEMUX; j++)
					{
						// if listenfd closes stop all assigned decoding!
						if(demux[j].socket_fd == pfd2[i].fd)
						{
							dvbapi_stop_descrambling(j, 0);
						}
					}

					int32_t ret = close(pfd2[i].fd);
					if(ret < 0 && errno != 9)
					{
						cs_log("ERROR: Could not close demuxer socket fd (errno=%d %s)", errno, strerror(errno));
					}

					if(pfd2[i].fd == listenfd && cfg.dvbapi_pmtmode == 6)
					{
						listenfd = -1;
					}
					cs_log_dbg(D_DVBAPI, "Socket %d reported hard connection close", pfd2[i].fd);
				}
				else // type = 0
				{
					int32_t demux_id = ids[i];
					int32_t n = fdn[i];

					if(cfg.dvbapi_boxtype != BOXTYPE_SAMYGO)
					{
						// stop filter since its giving errors and wont return anything good
						dvbapi_stop_filternum(demux_id, n, 0);
					}
					else
					{
						int32_t ret, pid;
						uint8_t filter[32];
						struct dmx_sct_filter_params sFP;
						cs_log_dbg(D_DVBAPI, "re-opening connection to demux socket");
						close(demux[demux_id].demux_fd[n].fd);
						demux[demux_id].demux_fd[n].fd = -1;

						ret = dvbapi_open_device(0, demux[demux_id].demux_index, demux[demux_id].adapter_index);
						if(ret != -1)
						{
							demux[demux_id].demux_fd[n].fd = ret;
							pid = demux[demux_id].curindex;
							memset(filter, 0, 32);
							memset(&sFP, 0, sizeof(sFP));
							filter[0] = 0x80;
							filter[16] = 0xF0;
							sFP.pid = demux[demux_id].ECMpids[pid].ECM_PID;
							sFP.timeout = 3000;
							sFP.flags = DMX_IMMEDIATE_START;
							memcpy(sFP.filter.filter, filter, 16);
							memcpy(sFP.filter.mask, filter + 16, 16);
							ret = dvbapi_ioctl(demux[demux_id].demux_fd[n].fd, DMX_SET_FILTER, &sFP);
						}

						if(ret == -1)
						{
							// stop filter since it's giving errors and wont return anything good
							dvbapi_stop_filternum(demux_id, n, 0);
						}
					}
				}
				continue; // continue with other events
			}

			if(pfd2[i].revents & (POLLIN | POLLPRI))
			{
				if(type[i] == 1)
				{
					if(pfd2[i].fd == listenfd)
					{
						if(cfg.dvbapi_pmtmode == 6)
						{
							connfd = listenfd;
							disable_pmt_files = 1;
						}
						else
						{
							clilen = sizeof(servaddr);
							connfd = accept(listenfd, (struct sockaddr *)&servaddr, (socklen_t *)&clilen);
							cs_log_dbg(D_DVBAPI, "new socket connection fd: %d", connfd);

							if(dvbapi_listenport_active)
							{
								// update webif data
								client->ip = SIN_GET_ADDR(servaddr);
								client->port = ntohs(SIN_GET_PORT(servaddr));
							}

							add_to_assoc_fd(connfd);

							if(cfg.dvbapi_pmtmode == 3 || cfg.dvbapi_pmtmode == 0)
							{
								disable_pmt_files = 1;
							}

							if(connfd <= 0)
							{
								cs_log_dbg(D_DVBAPI, "accept() returns error on fd event %d (errno=%d %s)",
									pfd2[i].revents, errno, strerror(errno));
							}
						}
					}
					else
					{
						connfd = pfd2[i].fd;
					}

					//reading and completing data from socket
					if(connfd > 0)
					{
						if(unhandled_buf_used[i])
						{
							memcpy(mbuf, unhandled_buf[i], unhandled_buf_used[i]);
						}

						if(!dvbapi_handlesockdata(connfd, mbuf, mbuf_size, unhandled_buf_used[i], &unhandled_buf_used[i], &client_proto_version[i]))
						{
							unhandled_buf_used[i] = 0;
							client_proto_version[i] = 0; // reset protocol, next client could old protocol.
							last_client_proto_version = 0;
							// client disconnects, stop all assigned decoding
							cs_log_dbg(D_DVBAPI, "Socket %d reported connection close", connfd);
							int active_conn = 0; // other active connections counter

							for(j = 0; j < MAX_DEMUX; j++)
							{
								if(demux[j].socket_fd == connfd)
								{
									dvbapi_stop_descrambling(j, 0);
								}
								else if(demux[j].socket_fd)
								{
									active_conn++;
								}
							}

							// stop polling on this socket
							del_from_assoc_fd(connfd);
							close(connfd);

							// last connection closed
							if(!active_conn && (dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX))
							{
								if(dvbapi_listenport_active)
								{
									// update webif data
									client->ip = get_null_ip();
									client->port = 0;
								}
							}
							continue;
						}

						if(unhandled_buf_used[i])
						{
							if(unhandled_buf_used[i] > unhandled_buf_len[i])
							{
								NULLFREE(unhandled_buf[i]);
								unhandled_buf_len[i] = unhandled_buf_used[i] < 128 ? 128 : unhandled_buf_used[i];
								if(!cs_malloc(&unhandled_buf[i], sizeof(uint8_t) * unhandled_buf_len[i]))
								{
									unhandled_buf_len[i] = 0;
									unhandled_buf_used[i] = 0;
									continue;
								}
							}
							memcpy(unhandled_buf[i], mbuf, unhandled_buf_used[i]);
						}
					}
				}
				else // type == 0
				{
					int32_t demux_id = ids[i];
					int32_t n = fdn[i];

					if((int)demux[demux_id].demux_fd[n].fd != pfd2[i].fd)
					{
						continue; // filter already killed, no need to process this data!
					}

					len = dvbapi_read_device(pfd2[i].fd, mbuf, mbuf_size);
					if(len < 0) // serious filterdata read error
					{
						// stop filter since it's giving errors and won't return anything good
						dvbapi_stop_filternum(demux_id, n, 0);

						maxfilter--; // lower maxfilters to avoid this with new filter setups!
						continue;
					}

					if(!len) // receiver internal filter buffer overflow
					{
						memset(mbuf, 0, mbuf_size);
					}
					dvbapi_process_input(demux_id, n, mbuf, len, 0);
				}
				continue; // continue with other events!
			}
		}
	}

	for(j = 0; j < maxpfdsize; j++)
	{
		NULLFREE(unhandled_buf[j]);
	}
	free(mbuf);

	return NULL;
}

void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t *cw, uint8_t cw_length, uint8_t *iv,
					uint8_t iv_length, enum ca_descr_algo algo, enum ca_descr_cipher_mode cipher_mode, uint32_t msgid)
{
	int8_t n, cw_empty = 0;
	uint8_t null_cw[cw_length];
	ca_descr_t ca_descr;
	ca_descr_mode_t ca_descr_mode;
	ca_descr_data_t ca_descr_data;

	// determine if soft-mode CSA_ALT should be active on this demux
	ca_soft_csa[demux_id] =
		(
			(algo == CW_ALGO_CSA_ALT) &&
			(demux[demux_id].client_proto_version == 3)
#ifdef WITH_EXTENDED_CW
			&& (cfg.dvbapi_extended_cw_api >= 1)
#endif
		);

	memset(null_cw, 0, cw_length);
	memset(&ca_descr, 0, sizeof(ca_descr));
	memset(&ca_descr_mode, 0, sizeof(ca_descr_mode));
	memset(&ca_descr_data, 0, sizeof(ca_descr_data));

	if(memcmp(demux[demux_id].last_cw[stream_id][0], null_cw, cw_length) == 0
		&& memcmp(demux[demux_id].last_cw[stream_id][1], null_cw, cw_length) == 0)
	{
		cw_empty = 1; // to make sure that both cws get written on constantcw
	}

	for(n = 0; n < 2; n++)
	{
		// Check if cw has changed and if new cw is empty (all zeros)
		// Skip check for BISS1 - cw could be indeed zero
		// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
		if((memcmp(cw + (n * cw_length), demux[demux_id].last_cw[stream_id][n], cw_length) != 0 || cw_empty)
			&& (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0 || caid_is_biss(demux[demux_id].ECMpids[pid].CAID)))
		{
			// prepare ca device
			uint32_t idx = dvbapi_ca_set_pid(demux_id, pid, stream_id, (algo == CA_ALGO_DES), msgid);
			if(idx == INDEX_INVALID)
			{
				return; // return on no index!
			}

			// just to make the compiler happy (-Wunused-parameter)
			// (better move the coolapi code to a separate function)
			ca_descr_mode.cipher_mode = cipher_mode;
			ca_descr_data.data = iv;
			ca_descr_data.length = iv_length;

#if defined WITH_COOLAPI || defined WITH_COOLAPI2
			ca_descr.index = idx;
			ca_descr.parity = n;

			memcpy(demux[demux_id].last_cw[stream_id][n], cw + (n * 8), 8);
			memcpy(ca_descr.cw, cw + (n * 8), 8);

			cs_log_dbg(D_DVBAPI, "Demuxer %d write cw%d index: %d (ca_mask %d)",
				demux_id, n, ca_descr.index, demux[demux_id].ca_mask);

			coolapi_write_cw(demux[demux_id].ca_mask, demux[demux_id].STREAMpids, demux[demux_id].STREAMpidcount, &ca_descr);
#else
			int32_t i, j, write_cw = 0;
			uint32_t usedidx, lastidx;

			char lastcw[2 * cw_length + 1];
			char newcw[2 * cw_length + 1];
#ifdef WITH_DEBUG
			if(cs_dblevel & D_DVBAPI)
			{
				cs_hexdump(0, demux[demux_id].last_cw[stream_id][n], cw_length, lastcw, sizeof(lastcw));
				cs_hexdump(0, cw + (n * cw_length), cw_length, newcw, sizeof(newcw));
			}
#endif

			for(i = 0; i < CA_MAX; i++)
			{
				if(!(demux[demux_id].ca_mask & (1 << i)))
				{
					continue; // ca not in use by this demuxer!
				}
				lastidx = INDEX_INVALID;

				for(j = 0; j < demux[demux_id].STREAMpidcount; j++)
				{
					write_cw = 0;
					if(!demux[demux_id].ECMpids[pid].streams || ((demux[demux_id].ECMpids[pid].streams & (1 << j)) == (uint) (1 << j)))
					{
						usedidx = is_ca_used(i, demux[demux_id].STREAMpids[j]);
						if(usedidx != INDEX_INVALID)
						{
							if(idx != usedidx)
							{
								cs_log_dbg(D_DVBAPI,"Demuxer %d ca%d is using index %d for streampid %04X -> skip!",
									demux_id, i, usedidx, demux[demux_id].STREAMpids[j]);
								continue; // if not used for descrambling -> skip!
							}
							else
							{
								if(usedidx == lastidx)
								{
									cs_log_dbg(D_DVBAPI,"Demuxer %d ca%d is using index %d for streampid %04X -> skip, %s part of cw already written!",
										demux_id, i, usedidx, demux[demux_id].STREAMpids[j], (n == 1 ? "even" : "odd"));
									continue;
								}

								cs_log_dbg(D_DVBAPI,"Demuxer %d ca%d is using index %d for streampid %04X -> write %s part of cw!",
									demux_id, i, usedidx, demux[demux_id].STREAMpids[j], (n == 1 ? "even" : "odd"));

								write_cw = 1;
							}
						}
					}

					if(!write_cw)
					{
						continue; // no need to write the cw since this ca isnt using it!
					}

					lastidx = usedidx;
					ca_descr.index = usedidx;
					ca_descr.parity = n;

					memcpy(demux[demux_id].last_cw[stream_id][n], cw + (n * cw_length), cw_length);
					memcpy(ca_descr.cw, cw + (n * 8), 8); // ca_descr is only used for 8 byte CWs

					cs_log_dbg(D_DVBAPI, "Demuxer %d writing %s part (%s) of controlword, replacing expired (%s)",
						demux_id, (n == 1 ? "even" : "odd"), newcw, lastcw);

					cs_log_dbg(D_DVBAPI, "Demuxer %d write cw%d index: %d (ca%d)", demux_id, n, ca_descr.index, i);
#ifdef WITH_EXTENDED_CW
					if(cfg.dvbapi_extended_cw_api == 1) // Set descrambler algorithm and mode
					{
						ca_descr_mode.index = usedidx;
						ca_descr_mode.algo = algo;
						ca_descr_mode.cipher_mode = cipher_mode;

						if((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id])
						{
							dvbapi_net_send(DVBAPI_CA_SET_DESCR_MODE, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/,
									(uint8_t *) &ca_descr_mode, NULL, NULL, demux[demux_id].client_proto_version);
						}
						else
						{
							if(ca_fd[i] <= 0)
							{
								ca_fd[i] = dvbapi_open_device(1, i, demux[demux_id].adapter_index);
								if(ca_fd[i] <= 0) { continue; }
							}

							if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR_MODE, &ca_descr_mode) < 0)
							{
								cs_log("ERROR: ioctl(CA_SET_DESCR_MODE): %s", strerror(errno));
							}
						}
					}

					// Send 16 byte CW and IV for AES128, DVB-CISSA
					if(cfg.dvbapi_extended_cw_api == 1 && algo == CA_ALGO_AES128)
					{
						// First send IV
						ca_descr_data.index = usedidx;
						ca_descr_data.data_type = CA_DATA_IV;
						ca_descr_data.data = iv;
						ca_descr_data.length = iv_length;

						if((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id])
						{
							dvbapi_net_send(DVBAPI_CA_SET_DESCR_DATA, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/,
									(uint8_t *) &ca_descr_data, NULL, NULL, demux[demux_id].client_proto_version);
						}
						else
						{
							if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR_DATA, &ca_descr_data) < 0)
							{
								cs_log("ERROR: ioctl(CA_SET_DESCR_DATA): %s", strerror(errno));
							}
						}

						// Then send CW
						ca_descr_data.index = usedidx;
						ca_descr_data.data_type = CA_DATA_KEY;
						ca_descr_data.data = cw + (n * cw_length);
						ca_descr_data.length = cw_length;
						ca_descr_data.parity = n;

						if((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id])
						{
							dvbapi_net_send(DVBAPI_CA_SET_DESCR_DATA, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/,
									(uint8_t *) &ca_descr_data, NULL, NULL, demux[demux_id].client_proto_version);
						}
						else
						{
							if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR_DATA, &ca_descr_data) < 0)
							{
								cs_log("ERROR: ioctl(CA_SET_DESCR_DATA): %s", strerror(errno));
							}
						}
					}
					else // Send 8 byte CW for DVB-CSA or DES
#endif
					{
						if ((cfg.dvbapi_boxtype == BOXTYPE_PC || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[demux_id])
						{
							dvbapi_net_send(DVBAPI_CA_SET_DESCR, demux[demux_id].socket_fd, msgid, demux_id, -1 /*unused*/,
									(uint8_t *) &ca_descr, NULL, NULL, demux[demux_id].client_proto_version);
						}
						else
						{
							if(ca_fd[i] <= 0)
							{
								ca_fd[i] = dvbapi_open_device(1, i, demux[demux_id].adapter_index);
								if(ca_fd[i] <= 0)
								{
									continue;
								}
							}

							if(dvbapi_ioctl(ca_fd[i], CA_SET_DESCR, &ca_descr) < 0)
							{
								cs_log("ERROR: ioctl(CA_SET_DESCR): %s", strerror(errno));
							}
						}
					}
				}
			}
#endif
		}
	}
	cs_log_dbg(D_DVBAPI, "Using %d of %d total descramblers", ca_descramblers_used, ca_descramblers_total);
}

void delayer(ECM_REQUEST *er, uint32_t delay)
{
	if(delay <= 0) { return; }

	struct timeb tpe;
	cs_ftime(&tpe);
	int64_t gone = comp_timeb(&tpe, &er->tps);

	if(gone < delay)
	{
		cs_log_dbg(D_DVBAPI, "delayer: gone=%"PRId64" ms, cfg=%d ms -> delay=%"PRId64" ms", gone, delay, delay - gone);
		cs_sleepms(delay - gone);
	}
}

void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er)
{
	int32_t i, j, k, handled = 0;
#ifdef MODULE_STREAMRELAY
	uint8_t null_cw8[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
#endif
	for(i = 0; i < MAX_DEMUX; i++)
	{
		uint32_t nocw_write = 0; // 0 = write cw, 1 = dont write cw to hardware demuxer

		// ignore empty demuxers, skip ecm response for other
		// srvid and ecm recponse for different adapter
		if(demux[i].program_number == 0
			|| demux[i].program_number != er->srvid
			|| demux[i].adapter_index != er->adapter_index)
		{
			continue;
		}

#ifdef WITH_STAPI5
		if(strcmp(dev_list[demux[i].dev_index].name, er->dev_name) != 0)
		{
			continue; // skip request if PTI device doesn't match request
		}
#endif

		demux[i].rdr = er->selected_reader;

		for(j = 0; j < demux[i].ECMpidcount; j++) // check for matching ecmpid
		{
			if((demux[i].ECMpids[j].CAID == er->caid || demux[i].ECMpids[j].CAID == er->ocaid)
				&& demux[i].ECMpids[j].ECM_PID == er->pid && demux[i].ECMpids[j].PROVID == er->prid
				&& demux[i].ECMpids[j].VPID == er->vpid)
			{
				break;
			}
		}

		if(j == demux[i].ECMpidcount)
		{
			continue; // ecm response srvid ok but no matching ecmpid, perhaps this for other demuxer
		}

		cs_log_dbg(D_DVBAPI, "Demuxer %d %scontrol word received for PID %d CAID %04X PROVID %06X ECMPID %04X CHID %04X VPID %04X",
			i, (er->rc >= E_NOTFOUND ? "no " : ""), j, er->caid, er->prid, er->pid, er->chid, er->vpid);

		uint32_t status = dvbapi_check_ecm_delayed_delivery(i, er);
		uint32_t comparecw0 = 0, comparecw1 = 0;
		char ecmd5[17 * 3];
#ifdef WITH_DEBUG
		if(cs_dblevel & D_DVBAPI)
		{
			cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5));
		}
#endif
		if(status == 1 && er->rc) // wrong ecmhash
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d not interested in response ecmhash %s (requested different one)", i, ecmd5);
			continue;
		}

		if(status == 2) // no filter
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d not interested in response ecmhash %s (filter already killed)", i, ecmd5);
			continue;
		}

		if(status == 5) // empty cw
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d not interested in response ecmhash %s (delivered cw is empty!)", i, ecmd5);
			nocw_write = 1;

			if(er->rc < E_NOTFOUND)
			{
				er->rc = E_NOTFOUND;
			}
		}

		// 0=matching ecm hash, 2=no filter, 3=table reset, 4=cache-ex response
		// Check only against last_cw[0] (index 0) - No need to check the rest
		// Skip check for BISS1 - cw could be indeed zero
		// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
		if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND && !caid_is_biss(er->caid))
		{
			// check for matching control word
			if(memcmp(er->cw, demux[i].last_cw[0][0], 8) == 0 &&
				memcmp(er->cw + 8, demux[i].last_cw[0][1], 8) == 0)
			{
				comparecw0 = 1;
			}
			else if(memcmp(er->cw, demux[i].last_cw[0][1], 8) == 0 &&
					memcmp(er->cw + 8, demux[i].last_cw[0][0], 8) == 0)
			{
				comparecw1 = 1;
			}

			if(comparecw0 == 1 || comparecw1 == 1)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d duplicate controlword ecm response hash %s (duplicate controlword!)", i, ecmd5);
				nocw_write = 1;
			}
		}

		if(status == 3) // table reset
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d luckyshot new controlword ecm response hash %s (ecm table reset)", i, ecmd5);
		}

		if(status == 4) // no check on cache-ex responses!
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d new controlword from cache-ex reader (no ecmhash check possible)", i);
		}

		handled = 1; // mark this ecm response as handled
		if(er->rc < E_NOTFOUND && cfg.dvbapi_requestmode == 0 && (demux[i].pidindex == -1) && er->caid != 0)
		{
			demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag
			demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdetocycle
			demux[i].pidindex = j; // set current index as *the* pid to descramble
			demux[i].ECMpids[j].checked = 4;

			cs_log_dbg(D_DVBAPI, "Demuxer %d descrambling PID %d CAID %04X PROVID %06X ECMPID %04X CHID %02X VPID %04X",
				i, demux[i].pidindex, er->caid, er->prid, er->pid, er->chid, er->vpid);
		}

		if(er->rc < E_NOTFOUND && cfg.dvbapi_requestmode == 1 && er->caid != 0) // FOUND
		{
			SAFE_MUTEX_LOCK(&demux[i].answerlock); // only process one ecm answer
			if(demux[i].ECMpids[j].checked != 4)
			{
				int32_t t, o, ecmcounter = 0;
				int32_t oldpidindex = demux[i].pidindex;
				demux[i].pidindex = j; // set current ecmpid as the new pid to descramble

				if(oldpidindex != -1)
				{
					for(k = 0; k < MAX_STREAM_INDICES; k++)
					{
						demux[i].ECMpids[j].index[k] = demux[i].ECMpids[oldpidindex].index[k]; // swap index with lower status pid that was descrambling
						demux[i].ECMpids[j].useMultipleIndices = demux[i].ECMpids[oldpidindex].useMultipleIndices;
					}
				}

				// check this pid with control word FOUND for higher status
				for(t = 0; t < demux[i].ECMpidcount; t++)
				{
					if(t != j && demux[i].ECMpids[j].status >= demux[i].ECMpids[t].status)
					{
						// check if ecm filter is in use and
						// stop all ecm filters of lower status pids
						for(o = 0; o < maxfilter; o++)
						{
							if(demux[i].demux_fd[o].fd > 0 && demux[i].demux_fd[o].type == TYPE_ECM
								&& demux[i].demux_fd[o].pidindex == t)
							{
								// ecm filter belongs to lower status pid -> kill!
								dvbapi_stop_filternum(i, o, er->msgid);
							}
						}
						dvbapi_edit_channel_cache(i, t, 0); // remove lower status pid from channel cache
						demux[i].ECMpids[t].checked = 4; // mark index t as low status
					}
				}

				for(o = 0; o < maxfilter; o++)
				{
					if(demux[i].demux_fd[o].type == TYPE_ECM)
					{
						ecmcounter++; // count all ecm filters
					}
				}

				demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag
				demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdeto cycle

				if(ecmcounter == 1) // if total found running ecmfilters is 1 -> we found the "best" pid
				{
					dvbapi_edit_channel_cache(i, j, 1);
					demux[i].ECMpids[j].checked = 4; // mark best pid last ;)
				}
				cs_log_dbg(D_DVBAPI, "Demuxer %d descrambling PID %d CAID %04X PROVID %06X ECMPID %04X CHID %02X VPID %04X",
					i, demux[i].pidindex, er->caid, er->prid, er->pid, er->chid, er->vpid);
			}
			SAFE_MUTEX_UNLOCK(&demux[i].answerlock); // and release it!
		}

		if(er->rc >= E_NOTFOUND) // not found on requestmode 0 + 1
		{
			if(er->rc == E_SLEEPING)
			{
				dvbapi_stop_descrambling(i, er->msgid);
				return;
			}

			struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(i, j, 'p');
			if(forceentry && forceentry->force) // forced pid? keep trying the forced ecmpid!
			{
				// all cas or irdeto cas with forced prio chid
				if(!caid_is_irdeto(er->caid) || forceentry->chid < 0x10000)
				{
					demux[i].ECMpids[j].table = 0;
					dvbapi_set_section_filter(i, er, -1);
					continue;
				}
				else // irdeto cas without chid prio forced
				{
					// init irdeto current index to first one
					if(demux[i].ECMpids[j].irdeto_curindex == 0xFE)
					{
						demux[i].ECMpids[j].irdeto_curindex = 0x00;
					}

					// check for last / max chid
					if(!(demux[i].ECMpids[j].irdeto_curindex + 1 > demux[i].ECMpids[j].irdeto_maxindex))
					{
						cs_log_dbg(D_DVBAPI, "Demuxer %d trying next irdeto chid of FORCED PID %d CAID %04X PROVID %06X ECMPID %04X",
							i, j, er->caid, er->prid, er->pid);

						demux[i].ECMpids[j].irdeto_curindex++; // irdeto index one up
						demux[i].ECMpids[j].table = 0;
						dvbapi_set_section_filter(i, er, -1);
						continue;
					}
				}
			}

			// in case of timeout or fatal LB event give
			// this pid another try but no more than 1 try
			if((er->rc == E_TIMEOUT || (er->rcEx && er->rcEx <= E2_CCCAM_NOCARD))
				&& demux[i].ECMpids[j].tries == 0xFE)
			{
				demux[i].ECMpids[j].tries -= 0x07;
				demux[i].ECMpids[j].table = 0;
				dvbapi_set_section_filter(i, er, -1);
				continue;
			}
			else // all not found responses exception: first timeout response and first fatal loadbalancer response
			{
				demux[i].ECMpids[j].CHID = 0x10000; // get rid of this prio chid since it failed!
				demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry
			}

			if(caid_is_irdeto(er->caid))
			{
				// init irdeto current index to first one
				if(demux[i].ECMpids[j].irdeto_curindex == 0xFE)
				{
					demux[i].ECMpids[j].irdeto_curindex = 0x00;
				}

				// check for last / max chid
				if(!(demux[i].ECMpids[j].irdeto_curindex + 1 > demux[i].ECMpids[j].irdeto_maxindex))
				{
					cs_log_dbg(D_DVBAPI, "Demuxer %d trying next irdeto chid of PID %d CAID %04X PROVID %06X ECMPID %04X VPID %04X",
						i, j, er->caid, er->prid, er->pid, er->vpid);

					demux[i].ECMpids[j].irdeto_curindex++; // irdeto index one up
					demux[i].ECMpids[j].table = 0;
					dvbapi_set_section_filter(i, er, -1);
					continue;
				}
			}

			dvbapi_edit_channel_cache(i, j, 0); // remove this pid from channelcache

			if(demux[i].pidindex == j)
			{
				// current pid delivered a notfound so this pid isn't
				// being used to descramble any longer -> clear pidindex
				demux[i].pidindex = -1;
			}

			demux[i].ECMpids[j].irdeto_maxindex = 0;
			demux[i].ECMpids[j].irdeto_curindex = 0xFE;
			demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag
			demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdetocycle
			demux[i].ECMpids[j].table = 0;
			demux[i].ECMpids[j].checked = 4; // flag ecmpid as checked
			demux[i].ECMpids[j].status = -1; // flag ecmpid as unusable

			int32_t found = 1; // setup for first run
			int32_t filternum = -1;

			while(found > 0) // disable all ecm + emm filters for this notfound
			{
				found = 0;

				filternum = dvbapi_get_filternum(i, er, TYPE_ECM); // get ecm filternumber
				if(filternum > -1) // in case valid filter found
				{
					int32_t fd = demux[i].demux_fd[filternum].fd;
					if(fd > 0) // in case valid fd
					{
						dvbapi_stop_filternum(i, filternum, er->msgid); // stop ecmfilter
						found = 1;
					}
				}

				if(caid_is_irdeto(er->caid)) // in case irdeto cas stop old emm filters
				{
					filternum = dvbapi_get_filternum(i, er, TYPE_EMM); // get emm filternumber
					if(filternum > -1) // in case valid filter found
					{
						int32_t fd = demux[i].demux_fd[filternum].fd;
						if(fd > 0) // in case valid fd
						{
							dvbapi_stop_filternum(i, filternum, er->msgid); // stop emmfilter
							found = 1;
						}
					}
				}
			}
			continue;
		}

		// below this should be only run in case of ecm answer is found
		uint32_t chid = get_subid(er); // derive current chid in case of irdeto, or a unique part of ecm on other cas systems
		demux[i].ECMpids[j].CHID = (chid != 0 ? chid : 0x10000); // if not zero apply, otherwise use no chid value 0x10000
		dvbapi_edit_channel_cache(i, j, 1); // do it here to here after the right CHID is registered

		//dvbapi_set_section_filter(i, er); is not needed anymore (unsure)
		demux[i].ECMpids[j].tries = 0xFE; // reset timeout retry flag
		demux[i].ECMpids[j].irdeto_cycle = 0xFE; // reset irdeto cycle

		// cw was already written by another filter or current pid
		// isn't pid used to descramble so it ends here!
		if(nocw_write || demux[i].pidindex != j)
		{
			continue;
		}

		struct s_dvbapi_priority *delayentry = dvbapi_check_prio_match(i, demux[i].pidindex, 'd');
		uint32_t delay = 0;

		if(delayentry)
		{
			if(delayentry->delay < 1000)
			{
				delay = delayentry->delay;
				cs_log_dbg(D_DVBAPI, "specific delay: write cw %d ms after ecmrequest", delay);
			}
		}
		else if(cfg.dvbapi_delayer > 0)
		{
			delay = cfg.dvbapi_delayer;
			cs_log_dbg(D_DVBAPI, "generic delay: write cw %d ms after ecmrequest", delay);
		}

		delayer(er, delay);

#ifdef MODULE_STREAMRELAY
		bool set_dvbapi_cw = true;
		if((cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(er->caid, &cfg.stream_relay_ctab)) && cfg.stream_relay_enabled)
		{
			// streamserver set cw
			set_dvbapi_cw = !stream_write_cw(er);
		}
		if (set_dvbapi_cw)
#endif
		switch(selected_api)
		{
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		case STAPI:
			stapi_write_cw(i, er->cw, demux[i].STREAMpids, demux[i].STREAMpidcount, demux[i].pmt_file);
			break;
#endif
		default:
			{
#ifdef WITH_EXTENDED_CW
				if(er->cw_ex.mode != demux[i].ECMpids[j].useMultipleIndices)
				{
					uint32_t idx;

					for(k = 0; k < demux[i].STREAMpidcount; k++)
					{
						if(demux[i].ECMpids[j].useMultipleIndices)
						{
							idx = demux[i].ECMpids[j].index[k];
						}
						else
						{
							idx = demux[i].ECMpids[j].index[0];
						}
						dvbapi_set_pid(i, k, idx, false, false, er->msgid); // disable streampid
					}

					for(k = 0; k < MAX_STREAM_INDICES; k++)
					{
						demux[i].ECMpids[j].index[k] = INDEX_INVALID;
					}
				}

				if(er->cw_ex.mode == CW_MODE_MULTIPLE_CW)
				{
					int32_t key_pos_a = 0;
					demux[i].ECMpids[j].useMultipleIndices = 1;

					for(k = 0; k < demux[i].STREAMpidcount; k++)
					{
						if(demux[i].STREAMpidsType[k] == STREAM_VIDEO)
						{
							dvbapi_write_cw(i, j, k, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid);
						}
						else if(demux[i].STREAMpidsType[k] == STREAM_AUDIO)
						{
							if(key_pos_a < 4)
							{
								dvbapi_write_cw(i, j, k, er->cw_ex.audio[key_pos_a], 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid);
								key_pos_a++;
							}
						}
						// Every channel that uses the extended cw has unencrypted subtitle streams,
						// so disable CW writing to save indices for audio streams and recordings.
						//else // Data
						//{
						//	dvbapi_write_cw(i, j, k, er->cw_ex.data, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid);
						//}
					}
				}
				else
				{
					demux[i].ECMpids[j].useMultipleIndices = 0;

					if(er->cw_ex.algo == CW_ALGO_AES128)
					{
						dvbapi_write_cw(i, j, 0, er->cw_ex.session_word, 16, er->cw_ex.data, 16, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid);
					}
					else if(er->cw_ex.algo == CW_ALGO_CSA)
					{
						if(select_csa_alt(er))
						{
							er->cw_ex.algo = CW_ALGO_CSA_ALT;
						}
						dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid);
					}
					else
					{
						dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, er->cw_ex.algo, er->cw_ex.algo_mode, er->msgid);
					}
				}
#else
				cfg.dvbapi_extended_cw_api = 0; // in CSA mode extended_cw_api should be always 0 regardless what user selected!
				dvbapi_write_cw(i, j, 0, er->cw, 8, NULL, 0, CA_ALGO_DVBCSA, CA_MODE_CBC, er->msgid);
#endif
				break;
			}
		}

#ifdef MODULE_STREAMRELAY
#ifdef WITH_EXTENDED_CW
		if(!(set_dvbapi_cw || er->cw_ex.algo == CA_ALGO_AES128))
#else
		if(!(set_dvbapi_cw))
#endif
		{
			if(memcmp(er->cw, null_cw8, 8) != 0)
			{
				memcpy(demux[i].last_cw[0][0], er->cw, 8);
			}
			else
			{
				memcpy(demux[i].last_cw[0][1], er->cw + 8, 8);
			}
		}
#endif

		// reset idle-Time
		client->last = time((time_t *)0); // ********* TO BE FIXED LATER ON ******

		if((dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX) || ca_soft_csa[i])
		{
			dvbapi_net_send(DVBAPI_ECM_INFO, demux[i].socket_fd, er->msgid, i, 0, NULL, client, er, demux[i].client_proto_version);
		}

		if(cfg.dvbapi_ecminfo_file != 0 && cfg.dvbapi_boxtype != BOXTYPE_SAMYGO)
		{
#ifdef WITH_EXTENDED_CW
			// Only print CWs for index 0 in ecm.info file
			if(er->cw_ex.algo == CA_ALGO_AES128)
			{
				dvbapi_write_ecminfo_file(client, er, demux[i].last_cw[0][0], demux[i].last_cw[0][1], 16);
			}
			else
			{
				dvbapi_write_ecminfo_file(client, er, demux[i].last_cw[0][0], demux[i].last_cw[0][1], 8);
			}
#else
			dvbapi_write_ecminfo_file(client, er, demux[i].last_cw[0][0], demux[i].last_cw[0][1], 8);
#endif
		}
	}

	if(handled == 0)
	{
		cs_log_dbg(D_DVBAPI, "Unhandled ECM response received for CAID %04X PROVID %06X ECMPID %04X CHID %04X VPID %04X",
			er->caid, er->prid, er->pid, er->chid, er->vpid);
	}
}

static int8_t isValidCW(uint8_t *cw)
{
	uint8_t i;
	for(i = 0; i < 16; i += 4)
	{
		if(((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff) != cw[i + 3])
		{
			return 0;
		}
	}
	return 1;
}

void dvbapi_write_ecminfo_file(struct s_client *client, ECM_REQUEST *er, uint8_t *lastcw0, uint8_t *lastcw1, uint8_t cw_length)
{
#define ECMINFO_TYPE_OSCAM    0
#define ECMINFO_TYPE_OSCAM_MS 1
#define ECMINFO_TYPE_WICARDD  2
#define ECMINFO_TYPE_MGCAMD   3
#define ECMINFO_TYPE_CCCAM    4
#define ECMINFO_TYPE_CAMD3    5
#define ECMINFO_TYPE_GBOX     6

	FILE *ecmtxt = fopen(ECMINFO_FILE, "w");
	if(ecmtxt != NULL && er->rc < E_NOTFOUND)
	{
		char tmp[49]; // holds 16 byte cw - (2 hex digits + 1 space) * 16 byte + string termination)
		const char *reader_name = NULL, *from_name = NULL, *proto_name = NULL, *from_device= NULL ;
		int8_t hops = 0;
		int32_t from_port = 0;
		char system_name[64];
		const char *const_system_name = get_cardsystem_desc_by_caid(er->caid);

		cs_strncpy(system_name, const_system_name, sizeof(system_name));
		system_name[0] = (char)toupper((int)system_name[0]);

		if(cfg.dvbapi_ecminfo_type <= ECMINFO_TYPE_WICARDD)
		{
			if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_WICARDD)
			{
				fprintf(ecmtxt, "system: %s\n", system_name);
			}

			fprintf(ecmtxt, "caid: 0x%04X\npid: 0x%04X\n", er->caid, er->pid);

			if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_WICARDD)
			{
				fprintf(ecmtxt, "prov: %06X\n", (uint) er->prid);
			}
			else
			{
				fprintf(ecmtxt, "prov: 0x%06X\n", (uint) er->prid);
			}

			fprintf(ecmtxt, "chid: 0x%04X\n", er->chid);
		}
		else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_MGCAMD)
		{
			fprintf(ecmtxt, "===== %s ECM on CaID 0x%04X, pid 0x%04X =====\nprov: %06X\n",
				system_name, er->caid, er->pid, (uint) er->prid);
		}
		else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CCCAM)
		{
			char provider_name[128];
			get_providername(er->prid, er->caid, provider_name, sizeof(provider_name));

			if(provider_name[0])
			{
				fprintf(ecmtxt, "system: %s\ncaid: 0x%04X\nprovider: %s\nprovid: 0x%06X\npid: 0x%04X\n",
					system_name, er->caid, provider_name, (uint) er->prid, er->pid);
			}
			else
			{
				fprintf(ecmtxt, "system: %s\ncaid: 0x%04X\nprovid: 0x%06X\npid: 0x%04X\n",
					system_name, er->caid, (uint) er->prid, er->pid);
			}
		}
		else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CAMD3)
		{
			fprintf(ecmtxt, "CAID 0x%04X, PID 0x%04X, PROVIDER 0x%06X\n",
				er->caid, er->pid, (uint) er->prid);
		}
#ifdef MODULE_GBOX
		else if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_GBOX)
		{
			fprintf(ecmtxt, "===== %s ECM on CaID 0x%04X, pid 0x%04X, sid 0x%04X =====\nprov: %04X, slot: %d, level: %d, dist: %d\nprovider: %06X\n",
				system_name, er->caid, er->pid, er->srvid, er->selected_reader->gbox_cw_src_peer, er->selected_reader->gbox_crd_slot_lev >> 4,
				er->selected_reader->gbox_crd_slot_lev & 0xf, er->selected_reader->currenthops, (uint) er->prid);
		}
#endif

		switch(er->rc)
		{
			case E_FOUND:
				if(er->selected_reader)
				{
					reader_name = er->selected_reader->label;
					if(is_network_reader(er->selected_reader))
					{
						from_name = er->selected_reader->device;
						from_port = er->selected_reader->r_port;
					}
					else
					{
						from_name = "local";
						from_device = er->selected_reader->device;
					}
					proto_name = reader_get_type_desc(er->selected_reader, 1);
					hops = er->selected_reader->currenthops;
				}
				else
				{
					reader_name = "none";
					from_name = "local";
					proto_name = "none";
				}
				break;

			case E_CACHE1:
				reader_name = "Cache";
				from_name = "cache1";
				proto_name = "none";
				break;

			case E_CACHE2:
				reader_name = "Cache";
				from_name = "cache2";
				proto_name = "none";
				break;

			case E_CACHEEX:
				reader_name = "Cache";
				from_name = "cache3";
				proto_name = "none";
				break;
		}

		if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_GBOX)
		{
			switch(er->rc)
			{
				case E_FOUND:
					if(er->selected_reader)
					{
						if(is_network_reader(er->selected_reader))
						{
							fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\n",
								reader_name, from_name, from_port, proto_name);
						}
						else
						{
							fprintf(ecmtxt, "reader: %s\nfrom: %s - %s\nprotocol: %s\n",
								reader_name, from_name, from_device, proto_name);
						}
					}
					break;

				case E_CACHE1:
				case E_CACHE2:
				case E_CACHEEX:
					fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\n",
						reader_name, from_name, from_port, proto_name);
					break;
			}
				fprintf(ecmtxt, "ecm time: %.3f\n", (float) client->cwlastresptime / 1000);
		}

		if(cfg.dvbapi_ecminfo_type <= ECMINFO_TYPE_OSCAM_MS)
		{
			switch(er->rc)
			{
				case E_FOUND:
					if(er->selected_reader)
					{
						if(is_network_reader(er->selected_reader))
						{
							fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\nhops: %d\n",
								reader_name, from_name, from_port, proto_name, hops);
						}
						else
						{
							fprintf(ecmtxt, "reader: %s\nfrom: %s - %s\nprotocol: %s\nhops: %d\n",
								reader_name, from_name, from_device, proto_name, hops);
						}
					}
					break;

				case E_CACHE1:
				case E_CACHE2:
				case E_CACHEEX:
					fprintf(ecmtxt, "reader: %s\nfrom: %s:%d\nprotocol: %s\n",
						reader_name, from_name, from_port, proto_name);
					break;
			}

			if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_OSCAM)
			{
				fprintf(ecmtxt, "ecm time: %.3f\n", (float) client->cwlastresptime / 1000);
			}
			else
			{
				fprintf(ecmtxt, "ecm time: %d\n", client->cwlastresptime);
			}
		}

		if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CAMD3)
		{
			fprintf(ecmtxt, "FROM: %s\n", reader_name);
			fprintf(ecmtxt, "CW0: %s\n", cs_hexdump(1, lastcw0, cw_length, tmp, sizeof(tmp)));
			fprintf(ecmtxt, "CW1: %s\n", cs_hexdump(1, lastcw1, cw_length, tmp, sizeof(tmp)));
		}
		else
		{
			fprintf(ecmtxt, "cw0: %s\n", cs_hexdump(1, lastcw0, cw_length, tmp, sizeof(tmp)));
			fprintf(ecmtxt, "cw1: %s\n", cs_hexdump(1, lastcw1, cw_length, tmp, sizeof(tmp)));
		}

		if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_WICARDD || cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_MGCAMD)
		{
			time_t walltime;
			struct tm lt;
			char timebuf[32];

			if(cw_length == 8) // only check checksum for 8 byte CWs
			{
				fprintf(ecmtxt, "Signature %s\n", (isValidCW(lastcw0) || isValidCW(lastcw1)) ? "OK" : "NOK");
			}
			else
			{
				fprintf(ecmtxt, "Signature %s\n", "OK");
			}

			if(reader_name != NULL)
			{
				fprintf(ecmtxt, "source: %s (%s at %s:%d)\n", reader_name, proto_name, from_name, from_port);
			}

			walltime = cs_time();
			localtime_r(&walltime, &lt);

			if(strftime(timebuf, 32, "%a %b %d %H:%M:%S %Y", &lt) != 0)
			{
				fprintf(ecmtxt, "%d msec -- %s\n", client->cwlastresptime, timebuf);
			}
		}

		if(cfg.dvbapi_ecminfo_type == ECMINFO_TYPE_CCCAM)
		{
			if(reader_name != NULL)
			{
				fprintf(ecmtxt, "using: %s\naddress: %s:%d\nhops: %d\n",
					proto_name, from_name, from_port, hops);
			}
			fprintf(ecmtxt, "ecm time: %d\n", client->cwlastresptime);
		}
	}

	if(ecmtxt)
	{
		int32_t ret = fclose(ecmtxt);
		if(ret < 0)
		{
			cs_log("ERROR: Could not close ecmtxt fd (errno=%d %s)", errno, strerror(errno));
		}
		ecmtxt = NULL;
	}
}


void *dvbapi_start_handler(struct s_client *cl, uint8_t *UNUSED(mbuf), int32_t module_idx, void *(*_main_func)(void *))
{
	// cs_log("dvbapi loaded fd=%d", idx);
	if(cfg.dvbapi_enabled == 1)
	{
		cl = create_client(get_null_ip());
		cl->module_idx = module_idx;
		cl->typ = 'c';

		int32_t ret = start_thread("dvbapi handler", _main_func, (void *)cl, &cl->thread, 1, 0);
		if(ret)
		{
			return NULL;
		}
	}
	return NULL;
}

void *dvbapi_handler(struct s_client *cl, uint8_t *mbuf, int32_t module_idx)
{
	return dvbapi_start_handler(cl, mbuf, module_idx, dvbapi_main_local);
}

int32_t dvbapi_set_section_filter(int32_t demux_id, ECM_REQUEST *er, int32_t n)
{
	if(!er) { return -1; }

	if(USE_OPENXCAS || (selected_api != DVBAPI_3 && selected_api != DVBAPI_1 && selected_api != STAPI) // only valid for dvbapi3, dvbapi1 and STAPI
		|| (cfg.dvbapi_boxtype == BOXTYPE_IPBOX || cfg.dvbapi_boxtype == BOXTYPE_IPBOX_PMT)) // reported buggy using sectionfiltering after 1~4 hours -> for now disabled!
	{
		return 0;
	}

	if(n == -1)
	{
		n = dvbapi_get_filternum(demux_id, er, TYPE_ECM);
	}

	if(n < 0) // in case no valid filter found;
	{
		return -1;
	}

	int32_t fd = demux[demux_id].demux_fd[n].fd;
	if(fd < 1) // in case no valid fd
	{
		return -1;
	}

	uint8_t filter[16];
	uint8_t mask[16];
	memset(filter, 0, 16);
	memset(mask, 0, 16);
	struct s_ecmpid *curpid = NULL;

	int32_t pid = demux[demux_id].demux_fd[n].pidindex;
	if(pid == -1)
	{
		return -1;
	}
	curpid = &demux[demux_id].ECMpids[pid];

	if(curpid->table != er->ecm[0] && curpid->table != 0)
	{
		return -1; // if current ecmtype differs from latest requested ecmtype do not apply section filtering!
	}

	uint8_t ecmfilter = 0;

	if(er->ecm[0] == 0x80)
	{
		ecmfilter = 0x81; // current processed ecm is even, next will be filtered for odd
	}
	else
	{
		ecmfilter = 0x80; // current processed ecm is odd, next will be filtered for even
	}

	if(curpid->table != 0) // cycle ecmtype from odd to even or even to odd
	{
		filter[0] = ecmfilter; // only accept new ecms (if previous odd, filter for even and vice versa)
		mask[0] = 0xFF;
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set ecmtable to %s (CAID %04X PROVID %06X FD %d)",
			demux_id, n + 1, (ecmfilter == 0x80 ? "EVEN" : "ODD"), curpid->CAID, curpid->PROVID, fd);
	}
	else // not decoding right now so we are interessted in all ecm types!
	{
		filter[0] = 0x80; // set filter to wait for any ecms
		mask[0] = 0xF0;
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set ecmtable to ODD+EVEN (CAID %04X PROVID %06X FD %d)",
			demux_id, n + 1, curpid->CAID, curpid->PROVID, fd);
	}

	uint32_t offset = 0, extramask = 0xFF;
	struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match(demux_id, pid, 'p');
	//cs_log("**** curpid->CHID %04X, checked = %d, er->chid = %04X *****", curpid->CHID, curpid->checked, er->chid);
	// checked 4 to make sure we dont set chid filter and no such ecm in dvbstream except for forced pids!

	if(curpid->CHID < 0x10000 && (curpid->checked == 4 || (forceentry && forceentry->force)))
	{
		switch(er->caid >> 8)
		{
			case 0x01: // seca
				offset = 7;
				extramask = 0xF0;
				break;

			case 0x05: // viaccess
				offset = 8;
				break;

			case 0x06: // irdeto
				offset = 6;
				break;

			case 0x09: // videoguard
				offset = 11;
				break;

			case 0x4A: // DRE-Crypt, Bulcrypt, Tongang and others?
				if(!caid_is_bulcrypt(er->caid))
				{
					offset = 6;
				}
				break;
		}
	}

	int32_t irdetomatch = 1; // check if wanted irdeto index is the one the delivers current chid!
	if(caid_is_irdeto(curpid->CAID))
	{
		if(curpid->irdeto_curindex == er->ecm[4]) { irdetomatch = 1; } // ok apply chid filtering
		else { irdetomatch = 0; } // skip chid filtering but apply irdeto index filtering
	}

	if(offset && irdetomatch) // we have a cas with chid or unique part in checked ecm
	{
		i2b_buf(2, curpid->CHID, filter + (offset - 2));
		mask[(offset - 2)] = 0xFF & extramask; // additional mask seca2 chid can be FC10 or FD10 varies each month so only apply F?10
		mask[(offset - 1)] = 0xFF;
		cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set chid to %04X on fd %d", demux_id, n + 1, curpid->CHID, fd);
	}
	else
	{
		// on irdeto we can always apply irdeto index filtering!
		if(caid_is_irdeto(curpid->CAID) && (curpid->irdeto_curindex < 0xFE))
		{
			filter[2] = curpid->irdeto_curindex;
			mask[2] = 0xFF;
			cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set irdetoindex to %d on fd %d",
				demux_id, n + 1, curpid->irdeto_curindex, fd);
		}
		else // all other cas systems also cas systems without chid or unique ecm part
		{
			cs_log_dbg(D_DVBAPI, "Demuxer %d Filter %d set chid to ANY CHID on fd %d", demux_id, n + 1, fd);
		}
	}

	int32_t ret = dvbapi_activate_section_filter(demux_id, n, fd, curpid->ECM_PID, filter, mask, er->msgid);
	if(ret < 0) // something went wrong setting filter!
	{
		cs_log("Demuxer %d Filter %d (fd %d) error setting section filtering -> stop filter!", demux_id, n + 1, fd);

		ret = dvbapi_stop_filternum(demux_id, n, er->msgid);
		if(ret == -1)
		{
			cs_log("Demuxer %d Filter %d (fd %d) stopping filter failed -> kill all filters of this demuxer!", demux_id, n + 1, fd);
			dvbapi_stop_filter(demux_id, TYPE_EMM, er->msgid);
			dvbapi_stop_filter(demux_id, TYPE_ECM, er->msgid);
		}
		return -1;
	}
	return n;
}

int32_t dvbapi_activate_section_filter(int32_t demux_id, int32_t num, int32_t fd, int32_t pid, uint8_t *filter, uint8_t *mask, uint32_t msgid)
{
	int32_t ret = -1;

	switch(selected_api)
	{
		case DVBAPI_3:
		{
			struct dmx_sct_filter_params sFP2;
			memset(&sFP2, 0, sizeof(sFP2));
			sFP2.pid = pid;
			sFP2.timeout = 0;
			sFP2.flags = DMX_IMMEDIATE_START;

			if(cfg.dvbapi_boxtype == BOXTYPE_NEUMO)
			{
				//DeepThought: on dgs/cubestation and neumo images, perhaps others
				//the following code is needed to descramble
				sFP2.filter.filter[0] = filter[0];
				sFP2.filter.mask[0] = mask[0];
				sFP2.filter.filter[1] = 0;
				sFP2.filter.mask[1] = 0;
				sFP2.filter.filter[2] = 0;
				sFP2.filter.mask[2] = 0;
				memcpy(sFP2.filter.filter + 3, filter + 1, 16 - 3);
				memcpy(sFP2.filter.mask + 3, mask + 1, 16 - 3);

				//DeepThought: in the drivers of the dgs/cubestation and neumo images,
				//dvbapi 1 and 3 are somehow mixed. In the kernel drivers, the DMX_SET_FILTER
				//ioctl expects to receive a dmx_sct_filter_params structure (DVBAPI 3) but
				//due to a bug its sets the "positive mask" wrongly (they should be all 0).
				//On the other hand, the DMX_SET_FILTER1 ioctl also uses the dmx_sct_filter_params
				//structure, which is incorrect (it should be dmxSctFilterParams).
				//The only way to get it right is to call DMX_SET_FILTER1 with the argument
				//expected by DMX_SET_FILTER. Otherwise, the timeout parameter is not passed correctly.

				ret = dvbapi_ioctl(fd, DMX_SET_FILTER1, &sFP2);
			}
			else
			{
				memcpy(sFP2.filter.filter, filter, 16);
				memcpy(sFP2.filter.mask, mask, 16);

				if(dvbapi_listenport_active || cfg.dvbapi_boxtype == BOXTYPE_PC_NODMX)
				{
					ret = dvbapi_net_send(DVBAPI_DMX_SET_FILTER,
								demux[demux_id].socket_fd,
								msgid,
								demux_id,
								num,
								(uint8_t *) &sFP2,
								NULL,
								NULL,
								demux[demux_id].client_proto_version);
				}
				else
				{
					ret = dvbapi_ioctl(fd, DMX_SET_FILTER, &sFP2);
				}
			}
			break;
		}

		case DVBAPI_1:
		{
			struct dmxSctFilterParams sFP1;
			memset(&sFP1, 0, sizeof(sFP1));
			sFP1.pid = pid;
			sFP1.timeout = 0;
			sFP1.flags = DMX_IMMEDIATE_START;
			memcpy(sFP1.filter.filter, filter, 16);
			memcpy(sFP1.filter.mask, mask, 16);
			ret = dvbapi_ioctl(fd, DMX_SET_FILTER1, &sFP1);
			break;
		}

#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		case STAPI:
		{
			ret = stapi_activate_section_filter(fd, filter, mask);
			break;
		}
#endif
		// Isn't implemented in COOLAPI-1 (legacy)
#if defined WITH_COOLAPI2
		case COOLAPI:
		{
			int32_t n = coolapi_get_filter_num(fd);
			if(n < 0)
			{
				return n;
			}
			coolapi_set_filter(fd, n, pid, filter, mask, TYPE_ECM);
			break;
		}
#endif

		default:
			break;
	}

	if(ret != -1) // only change filter/mask for comparing if box returned no errors!
	{
		// copy filter and mask to check later on if receiver delivered accordingly
		memcpy(demux[demux_id].demux_fd[num].filter, filter, 16);
		memcpy(demux[demux_id].demux_fd[num].mask, mask, 16);
	}
	return ret;
}

int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er)
{
	int32_t ret = 0;
	int32_t filternum = dvbapi_get_filternum(demux_id, er, TYPE_ECM);
	char nullcw[CS_ECMSTORESIZE];
	memset(nullcw, 0, CS_ECMSTORESIZE);

	if(filternum < 0) // if no matching filter act like ecm response is delayed
	{
		return 2;
	}

	if(memcmp(demux[demux_id].demux_fd[filternum].lastecmd5, nullcw, CS_ECMSTORESIZE))
	{
		demux[demux_id].demux_fd[filternum].lastresult = er->rc; // save last result
		char ecmd5[17 * 3];
#ifdef WITH_DEBUG
		if(cs_dblevel & D_DVBAPI)
		{
			cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5));
		}
#endif
		cs_log_dbg(D_DVBAPI, "Demuxer %d requested controlword for ecm %s on fd %d",
			demux_id, ecmd5, demux[demux_id].demux_fd[filternum].fd);

		uint8_t md5tmp[MD5_DIGEST_LENGTH];
		MD5(er->ecm, er->ecmlen, md5tmp);

		// 1 = no response on the ecm we request last for this fd!
		ret = (memcmp(demux[demux_id].demux_fd[filternum].lastecmd5, md5tmp, CS_ECMSTORESIZE) != 0 ? 1 : 0);
	}

	// Check for null cw
	// Skip check for BISS1 - cw could be indeed zero
	// Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
	if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0 && !caid_is_biss(er->caid))
	{
		return 5;
	}

	struct s_ecmpid *curpid = NULL;
	int32_t pid = demux[demux_id].demux_fd[filternum].pidindex;

	if(pid !=-1)
	{
		curpid = &demux[demux_id].ECMpids[pid];
		if(curpid->table == 0) // on change table act like ecm response is found
		{
			return 3;
		}
	}

	if(er->rc == E_CACHEEX) // on cache-ex response act like ecm response is found
	{
		return 4;
	}

	return ret;
}

int32_t dvbapi_get_filternum(int32_t demux_id, ECM_REQUEST *er, int32_t type)
{
	if(!er) { return -1; }

	int32_t n;
	int32_t fd = -1;

	for(n = 0; n < maxfilter; n++) // determine fd
	{
		// check for valid and right type (ecm or emm)
		if(demux[demux_id].demux_fd[n].fd > 0 && demux[demux_id].demux_fd[n].type == type)
		{
			if(type == TYPE_ECM && er->srvid != demux[demux_id].program_number)
			{
				continue;
			}

			if((demux[demux_id].demux_fd[n].pid == er->pid) && ((demux[demux_id].demux_fd[n].provid == er->prid)
				|| demux[demux_id].demux_fd[n].provid == 0 || er->prid == 0) && ((demux[demux_id].demux_fd[n].caid == er->caid)
				|| (demux[demux_id].demux_fd[n].caid == er->ocaid))) // current ecm pid?
			{
				fd = demux[demux_id].demux_fd[n].fd; // found!
				if(demux[demux_id].demux_fd[n].caid == er->ocaid)
				{
					// clear ecmd5 hash since betatunneled ecms hash different!
					memset(demux[demux_id].demux_fd[n].lastecmd5, 0, CS_ECMSTORESIZE);
				}
				break;
			}
		}
	}

	if(fd > 0 && demux[demux_id].demux_fd[n].provid == 0)
	{
		demux[demux_id].demux_fd[n].provid = er->prid; // hack to fill in provid into demuxer
	}
	return (fd > 0 ? n : fd); // return -1(fd) on not found, on found return filternumber(n)
}

uint32_t dvbapi_ca_set_pid(int32_t demux_id, int32_t pid, int32_t stream_id, bool use_des, uint32_t msgid)
{
	uint32_t idx;
	int32_t n;

	if(pid == -1 || pid > demux[demux_id].ECMpidcount)
	{
		return INDEX_INVALID;
	}

	if(demux[demux_id].ECMpids[pid].useMultipleIndices)
	{
		n = stream_id;
		idx = demux[demux_id].ECMpids[pid].index[n];

		if(idx == INDEX_INVALID) // if we have no index for this pid, get one!
		{
			idx = dvbapi_get_desc_index(demux_id, pid, n);
			if(idx == INDEX_INVALID)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X has no free index",
					demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID);

				return INDEX_INVALID;
			}

			cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d for stream %d",
				demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID, idx, n);
		}

		if(!demux[demux_id].ECMpids[pid].streams || ((demux[demux_id].ECMpids[pid].streams & (1 << n)) == (uint) (1 << n)))
		{
			dvbapi_set_pid(demux_id, n, idx, true, use_des, msgid); // enable stream pid
		}
		else
		{
			dvbapi_set_pid(demux_id, n, idx, false, false, msgid); // disable stream pid
		}
	}
	else
	{
		idx = demux[demux_id].ECMpids[pid].index[0];

		if(idx == INDEX_INVALID) // if we have no index for this pid, get one!
		{
			idx = dvbapi_get_desc_index(demux_id, pid, 0);
			if(idx == INDEX_INVALID)
			{
				cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X has no free index",
					demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID);

				return INDEX_INVALID;
			}

			cs_log_dbg(D_DVBAPI, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d",
					demux_id, pid, demux[demux_id].ECMpids[pid].CAID, demux[demux_id].ECMpids[pid].ECM_PID, idx);
		}

		for(n = 0; n < demux[demux_id].STREAMpidcount; n++)
		{
			if(!demux[demux_id].ECMpids[pid].streams || ((demux[demux_id].ECMpids[pid].streams & (1 << n)) == (uint) (1 << n)))
			{
				dvbapi_set_pid(demux_id, n, idx, true, use_des, 0); // enable stream pid
			}
			else
			{
				dvbapi_set_pid(demux_id, n, idx, false, false, 0); // disable stream pid
			}
		}
	}
	return idx; // return ca index
}

int8_t update_streampid_list(uint8_t cadevice, uint16_t pid, uint32_t idx, bool use_des)
{
	struct s_streampid *listitem, *newlistitem;
	LL_ITER itr;
	if(!ll_activestreampids)
	{
		ll_activestreampids = ll_create("ll_activestreampids");
	}

	if(idx >= INDEX_MAX)
	{
		return INVALID_STREAMPID_INDEX;
	}

	if(ll_count(ll_activestreampids) > 0)
	{
		itr = ll_iter_create(ll_activestreampids);
		while((listitem = ll_iter_next(&itr)))
		{
			if(cadevice == listitem->cadevice && pid == listitem->streampid)
			{
				if((listitem->activeindexers & (1 << idx)) == (uint64_t) (1 << idx))
				{
#ifdef WITH_EXTENDED_CW
					if(cfg.dvbapi_extended_cw_api == 2 && use_des != listitem->use_des)
					{
						listitem->use_des = use_des;
						return FIRST_STREAMPID_INDEX;
					}
#endif
					return FOUND_STREAMPID_INDEX; // match found
				}
				else
				{
					listitem->activeindexers |= (1 << idx); // ca + pid found but not this index -> add this index
					cs_log_dbg(D_DVBAPI, "Added existing streampid %04X with new index %d to ca%d", pid, idx, cadevice);
#ifdef WITH_EXTENDED_CW
					if(cfg.dvbapi_extended_cw_api == 2 && use_des != listitem->use_des)
					{
						listitem->use_des = use_des;
						return FIRST_STREAMPID_INDEX;
					}
#endif
					return ADDED_STREAMPID_INDEX;
				}
			}
		}
	}

	if(!cs_malloc(&newlistitem, sizeof(struct s_streampid)))
	{
		return FIRST_STREAMPID_INDEX; // not sure if this is correct
	}

	newlistitem->cadevice = cadevice;
	newlistitem->streampid = pid;
	newlistitem->activeindexers = (1 << idx);
	newlistitem->caindex = idx; // set this index as used to decode on ca device
	newlistitem->use_des = use_des;

	ll_append(ll_activestreampids, newlistitem);
	cs_log_dbg(D_DVBAPI, "Added new streampid %04X with index %d to ca%d", pid, idx, cadevice);

	return FIRST_STREAMPID_INDEX;
}

int8_t remove_streampid_from_list(uint8_t cadevice, uint16_t pid, uint32_t idx)
{
	struct s_streampid *listitem;
	int8_t removed = 0;
	LL_ITER itr;

	if(!ll_activestreampids)
	{
		return NO_STREAMPID_LISTED;
	}

	if(idx >= INDEX_MAX)
	{
		return INVALID_STREAMPID_INDEX;
	}

	if(ll_count(ll_activestreampids) > 0)
	{
		itr = ll_iter_create(ll_activestreampids);
		while((listitem = ll_iter_next(&itr)))
		{
			if(cadevice == listitem->cadevice && pid == listitem->streampid)
			{
				if(idx == INDEX_DISABLE_ALL)
				{
					listitem->activeindexers = 0;
					removed = 1;
				}
				else if((listitem->activeindexers & (1 << idx)) == (uint64_t) (1 << idx))
				{
					listitem->activeindexers &= ~(1 << idx); // flag it as disabled for this index
					removed = 1;
				}

				if(removed)
				{
					cs_log_dbg(D_DVBAPI, "Remove streampid %04X using indexer %d from ca%d", pid, idx, cadevice);
				}

				if(listitem->activeindexers == 0 && removed == 1) // all indexers disabled? -> remove pid from list!
				{
					ll_iter_remove_data(&itr);
					cs_log_dbg(D_DVBAPI, "Removed last indexer of streampid %04X from ca%d", pid, cadevice);
					ca_descramblers_used = count_active_indexers();
					return REMOVED_STREAMPID_LASTINDEX;
				}
				else if(removed == 1)
				{
					if(idx != INDEX_DISABLE_ALL && idx != listitem->caindex)
					{
						return REMOVED_STREAMPID_INDEX;
					}
					else
					{
						listitem->caindex = INDEX_INVALID;
						cs_log_dbg(D_DVBAPI, "Streampid %04X index %d was used for decoding on ca%d", pid, idx, cadevice);
						return REMOVED_DECODING_STREAMPID_INDEX;
					}
				}
				return INVALID_STREAMPID_INDEX;
			}
		}
	}
	return NO_STREAMPID_LISTED;
}

void disable_unused_streampids(int16_t demux_id)
{
	int32_t ecmpid = demux[demux_id].pidindex;

	if(ecmpid == -1 // no active ecmpid!
		|| !ll_activestreampids
		|| selected_api == STAPI // stapi handles pids itself!
		|| ll_count(ll_activestreampids) == 0) // no items in list?
	{
		return;
	}

	int32_t j;
	if(demux[demux_id].ECMpids[ecmpid].useMultipleIndices == 0)
	{
		uint32_t idx = demux[demux_id].ECMpids[ecmpid].index[0];
		int32_t i, n;
		struct s_streampid *listitem;

		// search for old enabled streampids on
		// all ca devices that have to be disabled
		for(i = 0; i < CA_MAX && idx != INDEX_INVALID; i++)
		{
			if(!((demux[demux_id].ca_mask & (1 << i)) == (uint32_t) (1 << i)))
			{
				continue; // ca is not used by this demuxer
			}

			LL_ITER itr;
			itr = ll_iter_create(ll_activestreampids);

			while((listitem = ll_iter_next(&itr)))
			{
				if(i != listitem->cadevice)
				{
					continue; // ca doesn't match
				}

				if(!((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx))))
				{
					continue; // index doesn't match
				}

				for(n = 0; n < demux[demux_id].STREAMpidcount; n++)
				{
					if(demux[demux_id].ECMpidcount == 0) // FTA? -> disable stream!
					{
						n = demux[demux_id].STREAMpidcount;
						break;
					}

					// check if pid matches with current stream pid on demuxer
					if(listitem->streampid == demux[demux_id].STREAMpids[n])
					{
						break;
					}
				}

				if(n == demux[demux_id].STREAMpidcount) // no match found
				{
					demux[demux_id].STREAMpids[n] = listitem->streampid; // put it here temporarily!
					dvbapi_set_pid(demux_id, n, idx, false, false, 0); // disable this unused streampid
					demux[demux_id].STREAMpids[n] = 0; // remove it from temp position!
				}
			}

			// ECMpidcount != 0 -> skip enabling on fta
			for(n = 0; n < demux[demux_id].STREAMpidcount && demux[demux_id].ECMpidcount != 0; n++)
			{
				ll_iter_reset(&itr);
				if(!demux[demux_id].ECMpids[ecmpid].streams || ((demux[demux_id].ECMpids[ecmpid].streams & (1 << n)) == (uint) (1 << n)))
				{
					while((listitem = ll_iter_next(&itr)))
					{
						if(i != listitem->cadevice) // ca doesn't match
						{
							continue;
						}

						if(!((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx)))) // index doesn't match
						{
							continue;
						}

						// check if pid matches with current streampid on demuxer
						if(listitem->streampid == demux[demux_id].STREAMpids[n])
						{
							break;
						}
					}

					if(!listitem) // if streampid not listed -> enable it!
					{
						dvbapi_set_pid(demux_id, n, idx, true, false, 0); // enable streampid
					}
				}
			}
		}
	}
	else
	{
		uint32_t idx = INDEX_INVALID;
		int32_t i, n;
		uint8_t skip;
		struct s_streampid *listitem;

		// search for old enabled streampids
		// on all ca devices that have to be disabled
		for(i = 0; i < CA_MAX && idx != INDEX_INVALID; i++)
		{
			if(!((demux[demux_id].ca_mask & (1 << i)) == (uint32_t) (1 << i)))
			{
				continue; // continue if ca is unused by this demuxer
			}

			LL_ITER itr;
			itr = ll_iter_create(ll_activestreampids);

			while((listitem = ll_iter_next(&itr)))
			{
				if(i != listitem->cadevice)
				{
					continue; // ca doesn't match
				}

				for(skip = 1, j = 0; j < MAX_STREAM_INDICES; j++)
				{
					idx = demux[demux_id].ECMpids[ecmpid].index[j];
					if(idx == INDEX_INVALID)
					{
						continue;
					}

					// index match
					if((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx)))
					{
						skip = 0;
						break;
					}
				}

				if(skip)
				{
					continue;
				}

				for(n = 0; n < demux[demux_id].STREAMpidcount; n++)
				{
					if(demux[demux_id].ECMpidcount == 0) // FTA? -> disable stream!
					{
						n = demux[demux_id].STREAMpidcount;
						break;
					}

					// check if pid matches with current streampid on demuxer
					if(listitem->streampid == demux[demux_id].STREAMpids[n])
					{
						break;
					}
				}

				if(n == demux[demux_id].STREAMpidcount)
				{
					demux[demux_id].STREAMpids[n] = listitem->streampid; // put it temp here!
					dvbapi_set_pid(demux_id, n, idx, false, false, 0); // no match found so disable this now unused streampid
					demux[demux_id].STREAMpids[n] = 0; // remove temp!
				}
			}

			// ECMpidcount != 0 -> skip enabling on fta
			for(n = 0; n < demux[demux_id].STREAMpidcount && demux[demux_id].ECMpidcount != 0; n++)
			{
				ll_iter_reset(&itr);
				if(!demux[demux_id].ECMpids[ecmpid].streams || ((demux[demux_id].ECMpids[ecmpid].streams & (1 << n)) == (uint) (1 << n)))
				{
					while((listitem = ll_iter_next(&itr)))
					{
						if(i != listitem->cadevice) // ca doesn't match
						{
							continue;
						}

						for(skip = 1, j = 0; j < MAX_STREAM_INDICES; j++)
						{
							idx = demux[demux_id].ECMpids[ecmpid].index[j];
							if(idx == INDEX_INVALID)
							{
								continue;
							}

							if((listitem->activeindexers & (1 << (idx))) == (uint64_t) (1 << (idx)))
							{
								skip = 0; // index match
								break;
							}
						}

						if(skip)
						{
							continue;
						}

						// check if pid matches with current streampid on demuxer
						if(listitem->streampid == demux[demux_id].STREAMpids[n])
						{
							break;
						}
					}

					if(!listitem) // if streampid not listed -> enable it!
					{
						dvbapi_set_pid(demux_id, n, idx, true, false, 0); // enable streampid
					}
				}
			}
		}
	}
}

uint32_t is_ca_used(uint8_t cadevice, int32_t pid)
{
	struct s_streampid *listitem;
	LL_ITER itr;
	if(!ll_activestreampids)
	{
		return INDEX_INVALID;
	}

	if(ll_count(ll_activestreampids) > 0)
	{
		itr = ll_iter_create(ll_activestreampids);
		while((listitem = ll_iter_next(&itr)))
		{
			// if pid is 0, we match ca device only
			if(listitem->cadevice != cadevice || (pid && listitem->streampid != pid))
			{
				continue;
			}

			uint32_t i = 0;
			while(listitem->caindex == INDEX_INVALID && i < INDEX_MAX)
			{
				if((listitem->activeindexers & (1 << i)) == (uint64_t) (1 << i))
				{
					listitem->caindex = i; // set fresh one
					cs_log_dbg(D_DVBAPI, "Streampid %04X is now using index %d for decoding on ca%d", pid, i, cadevice);
					break;
				}
				i++;
			}

			if(listitem->caindex == INDEX_INVALID)
			{
				ll_iter_remove_data(&itr);
				return INDEX_INVALID;
			}
			return listitem->caindex;
		}
	}
	return INDEX_INVALID; // no indexer found for this pid!
}

uint32_t count_active_indexers(void)
{
	uint i, usecounter = 0;
	struct s_streampid *listitem;
	LL_ITER itr;

	if(!ll_activestreampids)
	{
		return 0;
	}

	bool indexer_in_use[ca_descramblers_total];
	memset(&indexer_in_use, 0, sizeof(indexer_in_use));

	if(ll_count(ll_activestreampids) > 0)
	{
		itr = ll_iter_create(ll_activestreampids);
		while((listitem = ll_iter_next(&itr)))
		{
			if(listitem->caindex != INDEX_INVALID && listitem->caindex < INDEX_MAX)
			{
				indexer_in_use[listitem->caindex] = true;
			}
		}

		for(i = 0; i < ca_descramblers_total; i++)
		{
			if(indexer_in_use[i] == true)
			{
				usecounter++;
			}
		}
	}

	return usecounter;
}

uint16_t dvbapi_get_client_proto_version(void)
{
	return last_client_proto_version;
}

const char *dvbapi_get_client_name(void)
{
	return last_client_name ? last_client_name : "";
}

void check_add_emmpid(int32_t demux_id, uint8_t *filter, int32_t l, int32_t emmtype)
{
	if(l < 0) { return; }

	uint32_t typtext_idx = 0;
	int32_t ret = -1;
	const char *typtext[] = { "UNIQUE", "SHARED", "GLOBAL", "UNKNOWN" };

	while(((emmtype >> typtext_idx) & 0x01) == 0 && typtext_idx < sizeof(typtext) / sizeof(const char *))
	{
		++typtext_idx;
	}

	// filter already in list?
	if(is_emmfilter_in_list(filter, demux[demux_id].EMMpids[l].PID, demux[demux_id].EMMpids[l].PROVID, demux[demux_id].EMMpids[l].CAID))
	{
		cs_log_dbg(D_DVBAPI, "Demuxer %d duplicate emm filter type %s, emmpid: 0x%04X, emmcaid: %04X, emmprovid: %06X -> SKIPPED!",
					demux_id,
					typtext[typtext_idx],
					demux[demux_id].EMMpids[l].PID,
					demux[demux_id].EMMpids[l].CAID,
					demux[demux_id].EMMpids[l].PROVID);
		return;
	}

	if(demux[demux_id].emm_filter < demux[demux_id].max_emm_filter) // can this filter be started?
	{
		// try to activate this emmfilter
		ret = dvbapi_set_filter(demux_id,
					selected_api,
					demux[demux_id].EMMpids[l].PID,
					demux[demux_id].EMMpids[l].CAID,
					demux[demux_id].EMMpids[l].PROVID,
					filter,
					filter + 16,
					0,
					demux[demux_id].pidindex,
					TYPE_EMM, 1);
	}

	if(ret != -1) // -1 if maxfilter reached or filter start error!
	{
		if(demux[demux_id].emm_filter == -1) // -1: first run of emm filtering on this demuxer
		{
			demux[demux_id].emm_filter = 0;
		}
		demux[demux_id].emm_filter++; // increase total active filters

		cs_log_dump_dbg(D_DVBAPI, filter, 32, "Demuxer %d started emm filter type %s, pid: 0x%04X",
			demux_id, typtext[typtext_idx], demux[demux_id].EMMpids[l].PID);
		return;
	}
	else // not set successful, so add it to the list for try again later on!
	{
		add_emmfilter_to_list(demux_id, filter, demux[demux_id].EMMpids[l].CAID, demux[demux_id].EMMpids[l].PROVID, demux[demux_id].EMMpids[l].PID, 0, false);
		cs_log_dump_dbg(D_DVBAPI, filter, 32, "Demuxer %d added inactive emm filter type %s, pid: 0x%04X",
			demux_id, typtext[typtext_idx], demux[demux_id].EMMpids[l].PID);
	}
	return;
}

void rotate_emmfilter(int32_t demux_id)
{
	// emm filter iteration
	if(!ll_emm_active_filter)
	{
		ll_emm_active_filter = ll_create("ll_emm_active_filter");
	}

	if(!ll_emm_inactive_filter)
	{
		ll_emm_inactive_filter = ll_create("ll_emm_inactive_filter");
	}

	if(!ll_emm_pending_filter)
	{
		ll_emm_pending_filter = ll_create("ll_emm_pending_filter");
	}

	uint32_t filter_count = ll_count(ll_emm_active_filter) + ll_count(ll_emm_inactive_filter);
	if(demux[demux_id].max_emm_filter > 0 && ll_count(ll_emm_inactive_filter) > 0 && filter_count > demux[demux_id].max_emm_filter)
	{
		int32_t filter_queue = ll_count(ll_emm_inactive_filter);
		int32_t stopped = 0, started = 0;
		struct timeb now;
		cs_ftime(&now);
		struct s_emm_filter *filter_item;
		LL_ITER itr;
		itr = ll_iter_create(ll_emm_active_filter);

		while((filter_item = ll_iter_next(&itr)) != NULL)
		{
			if(!ll_count(ll_emm_inactive_filter) || started == filter_queue)
			{
				break;
			}

			int64_t gone = comp_timeb(&now, &filter_item->time_started);
			if(gone > 45 * 1000)
			{
				struct s_dvbapi_priority *forceentry = dvbapi_check_prio_match_emmpid(filter_item->demux_id, filter_item->caid, filter_item->provid, 'p');
				if(!forceentry || (forceentry && !forceentry->force))
				{
					// stop active filter and add to pending list
					dvbapi_stop_filternum(filter_item->demux_id, filter_item->num - 1, 0);
					ll_iter_remove_data(&itr);
					add_emmfilter_to_list(filter_item->demux_id, filter_item->filter, filter_item->caid, filter_item->provid, filter_item->pid, -1, false);
					stopped++;
				}
			}

			int32_t ret;
			if(stopped > started) // we have room for new filters, try to start an inactive emmfilter!
			{
				struct s_emm_filter *filter_item2;
				LL_ITER itr2 = ll_iter_create(ll_emm_inactive_filter);
				while((filter_item2 = ll_iter_next(&itr2)))
				{
					ret = dvbapi_set_filter(filter_item2->demux_id,
								selected_api,
								filter_item2->pid,
								filter_item2->caid,
								filter_item2->provid,
								filter_item2->filter,
								filter_item2->filter + 16,
								0,
								demux[filter_item2->demux_id].pidindex,
								TYPE_EMM, 1);
					if(ret != -1)
					{
						ll_iter_remove_data(&itr2);
						started++;
						break;
					}
				}
			}
		}

		itr = ll_iter_create(ll_emm_pending_filter);
		while((filter_item = ll_iter_next(&itr)) != NULL) // move pending filters to inactive
		{
			add_emmfilter_to_list(filter_item->demux_id, filter_item->filter, filter_item->caid, filter_item->provid, filter_item->pid, 0, false);
			ll_iter_remove_data(&itr);
		}
	}
}

int32_t filtermatch(uint8_t *buffer, int32_t filter_num, int32_t demux_id, int32_t len)
{
	int32_t i, k, match;
	uint8_t flt, mask;

	match = 1;
	for(i = 0, k = 0; i < 16 && match; i++, k++)
	{
		mask = demux[demux_id].demux_fd[filter_num].mask[i];
		if(k == 1) // skip len bytes
		{
			k += 2;
		}

		if(!mask)
		{
			continue;
		}

		flt = (demux[demux_id].demux_fd[filter_num].filter[i]&mask);
		cs_log_dbg(D_DVBAPI,"Demuxer %d filter%d[%d] = %02X, filter mask[%d] = %02X, flt&mask = %02X , buffer[%d] = %02X, buffer[%d] & mask = %02X",
			demux_id, filter_num + 1, i, demux[demux_id].demux_fd[filter_num].filter[i], i, mask, flt&mask, k, buffer[k], k, buffer[k] & mask);

		if(k <= len)
		{
			match = (flt == (buffer[k] & mask));
		}
		else
		{
			match = 0;
		}
	}
	return (match && i == 16); // 0 = delivered data does not match with filter, 1 = delivered data matches with filter
}

/*
 * protocol structure
 */
void module_dvbapi(struct s_module *ph)
{
	ph->desc = "dvbapi";
	ph->type = MOD_CONN_SERIAL;
	ph->listenertype = LIS_DVBAPI;
#if defined(WITH_AZBOX)
	ph->s_handler = azbox_handler;
	ph->send_dcw = azbox_send_dcw;
#elif defined(WITH_MCA)
	ph->s_handler = mca_handler;
	ph->send_dcw = mca_send_dcw;
	selected_box = selected_api = 0; // HACK: This fixes incorrect warning about out of bounds array access in functionas that are not even called when WITH_MCA is defined
#else
	ph->s_handler = dvbapi_handler;
	ph->send_dcw = dvbapi_send_dcw;
#endif
}
#endif // HAVE_DVBAPI
