1 #define MODULE_LOG_PREFIX "dvbmca"
4 * dvbapi-support for Matrix Cam Air
6 * The code is based partially on module-dvbapi-azbox.
7 * the (closed source) oscam that comes with the MCA is
8 * apparently based on svn-revision 5124-5242.
9 * DEMUXMATRIX is essentially the old DEMUXTYPE which
10 * has changed since then.
12 * We may be able to implement add/remove-filter
13 * by adding/removing them from DEMUXMATRIX
14 * and reexecute mca_write_flt
15 * In some cases the MCA will send ECMs for multiple PIDS
16 * So it is apparently able to handle multiple filters
18 * @author dirtyharry123
23 #if defined(HAVE_DVBAPI) && defined(WITH_MCA)
25 #include "extapi/openxcas/openxcas_message.h"
27 #include "module-dvbapi.h"
28 #include "module-dvbapi-mca.h"
29 #include "oscam-client.h"
30 #include "oscam-ecm.h"
31 #include "oscam-reader.h"
32 #include "oscam-string.h"
33 #include "oscam-time.h"
39 #define openxcas_start_filter_ex(...) dummy()
40 #define openxcas_stop_filter_ex(...) dummy()
41 #define openxcas_get_message mca_get_message
42 #define azbox_openxcas_ex_callback mca_ex_callback
43 #define openxcas_stop_filter(...) do { } while(0)
44 #define openxcas_remove_filter(...) do { } while(0)
45 #define openxcas_destory_cipher_ex(...) do { } while(0)
47 // These variables are declared in module-dvbapi.c
48 extern void *dvbapi_client
;
49 extern DEMUXTYPE demux
[MAX_DEMUX
];
51 // These are used in module-dvbapi.c
52 int32_t openxcas_provid
;
53 uint16_t openxcas_sid
, openxcas_caid
, openxcas_ecm_pid
;
55 static unsigned char openxcas_cw
[16];
56 static int32_t openxcas_seq
, openxcas_filter_idx
, openxcas_stream_id
, openxcas_cipher_idx
, openxcas_busy
= 0;
57 static uint16_t openxcas_video_pid
, openxcas_audio_pid
, openxcas_data_pid
;
58 static uint8_t found
[MAX_DEMUX
];
60 static int fd_mdvbi
= -1;
61 static int fd_mdesc
= -1;
62 static int fd_mflt
= -1;
64 #define MCA_DVBI "/tmp/mdvbi"
65 #define MCA_DESC "/tmp/mdesc"
66 #define MCA_FLT "/tmp/mflt"
68 enum eOPENXCAS_FILTER_TYPE
70 OPENXCAS_FILTER_UNKNOWN
= 0,
75 #define ECM_PIDS_MATRIX 20
76 #define MAX_FILTER_MATRIX 10
78 struct s_ecmpids_matrix
84 int32_t irdeto_maxindex
;
85 int32_t irdeto_curindex
;
94 typedef struct filter_s_matrix
96 uint32_t fd
; //FilterHandle
103 struct s_emmpids_matrix
111 typedef struct demux_s_matrix
114 FILTERTYPE_MATRIX demux_fd
[MAX_FILTER_MATRIX
];
116 int32_t adapter_index
;
119 struct s_ecmpids_matrix ECMpids
[ECM_PIDS_MATRIX
];
121 struct s_emmpids_matrix EMMpids
[ECM_PIDS_MATRIX
];
122 int32_t STREAMpidcount
;
123 uint16_t STREAMpids
[ECM_PIDS_MATRIX
];
128 uint16_t program_number
;
129 unsigned char lastcw
[2][8];
132 struct s_reader
*rdr
;
137 static int mca_open(void)
139 if((fd_mdvbi
= open(MCA_DVBI
, O_RDONLY
)) < 0)
141 cs_log("can't open \"%s\" (err=%d %s)", MCA_DVBI
, errno
, strerror(errno
));
144 if((fd_mdesc
= open(MCA_DESC
, O_WRONLY
)) < 0)
146 cs_log("can't open \"%s\" (err=%d %s)", MCA_DESC
, errno
, strerror(errno
));
149 if((fd_mflt
= open(MCA_FLT
, O_WRONLY
)) < 0)
151 cs_log("can't open \"%s\" (err=%d %s)", MCA_FLT
, errno
, strerror(errno
));
157 static int mca_exit(void)
159 if((fd_mdvbi
= close(fd_mdvbi
)) < 0)
161 cs_log("can't close \"%s\" (err=%d %s)", MCA_DVBI
, errno
, strerror(errno
));
164 if((fd_mdvbi
= close(fd_mdesc
)) < 0)
166 cs_log("can't close \"%s\" (err=%d %s)", MCA_DESC
, errno
, strerror(errno
));
169 if((fd_mdvbi
= close(fd_mflt
)) < 0)
171 cs_log("can't close \"%s\" (err=%d %s)", MCA_FLT
, errno
, strerror(errno
));
181 cs_log("could not init");
189 cs_log("could not close");
193 static int mca_get_message(openxcas_msg_t
*message
, int timeout
)
196 struct pollfd mdvbi_poll_fd
;
197 mdvbi_poll_fd
.fd
= fd_mdvbi
;
198 mdvbi_poll_fd
.events
= POLLIN
| POLLPRI
;
199 rval
= poll(&mdvbi_poll_fd
, 1, timeout
== 0 ? -1 : timeout
);
200 if((rval
>= 1) && (mdvbi_poll_fd
.revents
& (POLLIN
| POLLPRI
)))
202 rval
= read(fd_mdvbi
, message
, 568);
208 static int mca_write_flt(DEMUXMATRIX
*demux_matrix
, int timeout
)
211 struct pollfd mflt_poll_fd
;
212 mflt_poll_fd
.fd
= fd_mflt
;
213 mflt_poll_fd
.events
= POLLOUT
;
214 rval
= poll(&mflt_poll_fd
, 1, timeout
);
215 if((rval
>= 1) && (mflt_poll_fd
.revents
& POLLOUT
))
217 rval
= write(fd_mflt
, demux_matrix
, sizeof(DEMUXMATRIX
));
223 static int mca_set_key(unsigned char *mca_cw
)
226 struct pollfd mdesc_poll_fd
;
227 mdesc_poll_fd
.fd
= fd_mdesc
;
228 mdesc_poll_fd
.events
= POLLOUT
;
229 rval
= poll(&mdesc_poll_fd
, 1, 0);
230 if((rval
>= 1) && (mdesc_poll_fd
.revents
& POLLOUT
))
232 rval
= write(fd_mdesc
, mca_cw
, 16);
238 static int mca_capmt_remove_duplicates(uchar
*capmt
, int len
)
241 uint16_t descriptor_length
= 0;
242 uint32_t program_info_length
= ((capmt
[4] & 0x0F) << 8) | capmt
[5];
243 for(i
= 7; i
< len
; i
+= descriptor_length
+ 2)
245 descriptor_length
= capmt
[i
+ 1];
246 if(capmt
[i
] != 0x09) { continue; }
247 if(!memcmp(&(capmt
[i
]), &(capmt
[i
+ descriptor_length
+ 2]), descriptor_length
+ 2))
249 memmove(&(capmt
[i
+ descriptor_length
+ 2]), &(capmt
[i
+ (2 * (descriptor_length
+ 2))]), newlen
- (descriptor_length
+ 2));
250 newlen
-= descriptor_length
+ 2;
253 program_info_length
-= (len
- newlen
);
254 capmt
[4] = (uchar
)((capmt
[4] & 0xF0) | ((program_info_length
& 0xF00) >> 8));
255 capmt
[5] = (uchar
)(program_info_length
& 0x0FF);
259 static void mca_demux_convert(DEMUXTYPE
*demux_orig
, DEMUXMATRIX
*demux_matrix
)
262 memset(demux_matrix
, 0, sizeof(DEMUXMATRIX
));
263 demux_matrix
->demux_index
= (int32_t)demux_orig
->demux_index
;
264 for(i
= 0; i
< MAX_FILTER_MATRIX
; ++i
)
266 demux_matrix
->demux_fd
[i
].fd
= (uint32_t) demux_orig
->demux_fd
[i
].fd
;
267 demux_matrix
->demux_fd
[i
].pidindex
= (int32_t) demux_orig
->demux_fd
[i
].pidindex
;
268 demux_matrix
->demux_fd
[i
].pid
= (int32_t) demux_orig
->demux_fd
[i
].pid
;
269 demux_matrix
->demux_fd
[i
].type
= (uint16_t) demux_orig
->demux_fd
[i
].type
;
270 demux_matrix
->demux_fd
[i
].count
= (int32_t) demux_orig
->demux_fd
[i
].count
;
272 demux_matrix
->ca_mask
= (int32_t)demux_orig
->ca_mask
;
273 demux_matrix
->adapter_index
= (int32_t)demux_orig
->adapter_index
;
274 demux_matrix
->socket_fd
= (int32_t)demux_orig
->socket_fd
;
275 demux_matrix
->ECMpidcount
= (int32_t)demux_orig
->ECMpidcount
;
276 for(i
= 0; i
< demux_matrix
->ECMpidcount
; ++i
)
278 demux_matrix
->ECMpids
[i
].CAID
= (uint16_t)demux_orig
->ECMpids
[i
].CAID
;
279 demux_matrix
->ECMpids
[i
].PROVID
= (uint32_t)demux_orig
->ECMpids
[i
].PROVID
;
280 demux_matrix
->ECMpids
[i
].ECM_PID
= (uint16_t)demux_orig
->ECMpids
[i
].ECM_PID
;
281 demux_matrix
->ECMpids
[i
].EMM_PID
= (uint16_t)demux_orig
->ECMpids
[i
].EMM_PID
;
282 demux_matrix
->ECMpids
[i
].irdeto_maxindex
= (int32_t)demux_orig
->ECMpids
[i
].irdeto_maxindex
;
283 demux_matrix
->ECMpids
[i
].irdeto_curindex
= (int32_t)demux_orig
->ECMpids
[i
].irdeto_curindex
;
284 demux_matrix
->ECMpids
[i
].irdeto_cycle
= (int32_t)demux_orig
->ECMpids
[i
].irdeto_cycle
;
285 demux_matrix
->ECMpids
[i
].checked
= (int32_t)demux_orig
->ECMpids
[i
].checked
;
286 demux_matrix
->ECMpids
[i
].status
= (int32_t)demux_orig
->ECMpids
[i
].status
;
287 demux_matrix
->ECMpids
[i
].table
= (unsigned char)demux_orig
->ECMpids
[i
].table
;
288 demux_matrix
->ECMpids
[i
].streams
= (uint32_t)demux_orig
->ECMpids
[i
].streams
;
290 demux_matrix
->STREAMpidcount
= (int32_t)demux
->STREAMpidcount
;
291 memcpy(&demux_matrix
->STREAMpids
, &demux_orig
->STREAMpids
, demux_matrix
->STREAMpidcount
* sizeof(uint16_t));
292 demux_matrix
->pidindex
= (int32_t)demux_orig
->pidindex
;
293 demux_matrix
->curindex
= (int32_t)demux_orig
->curindex
;
294 demux_matrix
->max_status
= (int32_t)demux_orig
->max_status
;
295 demux_matrix
->program_number
= (uint16_t)demux_orig
->program_number
;
296 memcpy(&demux_matrix
->lastcw
, &demux_orig
->lastcw
, 2 * 8 * sizeof(unsigned char));
297 demux_matrix
->emm_filter
= (int32_t)demux_orig
->emm_filter
;
298 memcpy(&demux_matrix
->hexserial
, &demux_orig
->hexserial
, 8 * sizeof(uchar
));
299 demux_matrix
->rdr
= (struct s_reader
*)demux_orig
->rdr
;
300 memcpy(&demux_matrix
->pmt_file
, &demux_orig
->pmt_file
, 30);
301 demux_matrix
->pmt_time
= (int32_t)demux_orig
->pmt_time
;
304 static void mca_ecm_callback(int32_t stream_id
, uint32_t UNUSED(seq
), int32_t cipher_index
, uint32_t caid
, unsigned char *ecm_data
, int32_t l
, uint16_t pid
)
306 cs_log_dbg(D_DVBAPI
, "ecm callback received");
308 openxcas_stream_id
= stream_id
;
309 //openxcas_seq = seq;
310 //openxcas_caid = caid;
311 openxcas_ecm_pid
= pid
;
315 //As soon as we have received a valid CW we lock onto that CAID, otherwise we will have freezers.
316 if(openxcas_caid
&& caid
&& openxcas_caid
!= caid
)
318 cs_log("ignoring caid: %04X, waiting for %04X", caid
, openxcas_caid
);
323 if(l
< 0 || l
> MAX_ECM_SIZE
)
327 if(!(er
= get_ecmtask()))
330 er
->srvid
= openxcas_sid
;
331 er
->caid
= openxcas_caid
;
332 er
->pid
= openxcas_ecm_pid
;
333 er
->prid
= openxcas_provid
;
336 memcpy(er
->ecm
, ecm_data
, er
->ecmlen
);
338 request_cw(dvbapi_client
, er
, 0, 0);
340 openxcas_stop_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
341 openxcas_remove_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
343 openxcas_cipher_idx
= cipher_index
;
351 static void mca_ex_callback(int32_t stream_id
, uint32_t UNUSED(seq
), int32_t idx
, uint32_t pid
, unsigned char *ecm_data
, int32_t l
)
353 cs_log_dbg(D_DVBAPI
, "ex callback received");
355 openxcas_stream_id
= stream_id
;
356 openxcas_ecm_pid
= pid
;
357 openxcas_cipher_idx
= idx
; // is this really cipher_idx?
359 if(l
< 0 || l
> MAX_ECM_SIZE
)
363 if(!(er
= get_ecmtask()))
366 er
->srvid
= openxcas_sid
;
367 er
->caid
= openxcas_caid
;
368 er
->pid
= openxcas_ecm_pid
;
369 er
->prid
= openxcas_provid
;
372 memcpy(er
->ecm
, ecm_data
, er
->ecmlen
);
374 request_cw(dvbapi_client
, er
, 0, 0);
376 if(openxcas_stop_filter_ex(stream_id
, seq
, openxcas_filter_idx
) < 0)
377 { cs_log("unable to stop ex filter"); }
379 { cs_log_dbg(D_DVBAPI
, "ex filter stopped"); }
383 unsigned char mask
[12];
384 unsigned char comp
[12];
385 memset(&mask
, 0x00, sizeof(mask
));
386 memset(&comp
, 0x00, sizeof(comp
));
389 comp
[0] = ecm_data
[0] ^ 1;
391 if((openxcas_filter_idx
= openxcas_start_filter_ex(stream_id
, seq
, openxcas_ecm_pid
, mask
, comp
, (void *)azbox_openxcas_ex_callback
)) < 0)
392 { cs_log("unable to start ex filter"); }
394 { cs_log_dbg(D_DVBAPI
, "ex filter started, pid = %x", openxcas_ecm_pid
); }
397 static void *mca_main_thread(void *cli
)
399 struct s_client
*client
= (struct s_client
*) cli
;
400 client
->thread
= pthread_self();
401 SAFE_SETSPECIFIC(getclient
, cli
);
404 struct s_auth
*account
;
406 for(account
= cfg
.account
; account
; account
= account
->next
)
408 if((ok
= is_dvbapi_usr(account
->usr
)))
411 cs_auth_client(client
, ok
? account
: (struct s_auth
*)(-1), "dvbapi");
413 dvbapi_read_priority();
417 while((ret
= openxcas_get_message(&msg
, 0)) >= 0)
423 openxcas_stream_id
= msg
.stream_id
;
424 openxcas_seq
= msg
.sequence
;
425 struct stOpenXCAS_Data data
;
429 case OPENXCAS_SELECT_CHANNEL
:
430 cs_log_dbg(D_DVBAPI
, "OPENXCAS_SELECT_CHANNEL");
432 // parse channel info
433 struct stOpenXCASChannel chan
;
434 memcpy(&chan
, msg
.buf
, msg
.buf_len
);
436 cs_log("channel change: sid = %x, vpid = %x. apid = %x", chan
.service_id
, chan
.v_pid
, chan
.a_pid
);
438 openxcas_video_pid
= chan
.v_pid
;
439 openxcas_audio_pid
= chan
.a_pid
;
440 openxcas_data_pid
= chan
.d_pid
;
442 case OPENXCAS_START_PMT_ECM
:
443 //FIXME: Apparently this is what the original MCA-oscam does
444 cs_log_dbg(D_DVBAPI
, "OPENXCAS_STOP_PMT_ECM");
445 memset(&demux
, 0, sizeof(demux
));
446 memset(&found
, 0, sizeof(found
));
448 cs_log_dbg(D_DVBAPI
, "OPENXCAS_START_PMT_ECM");
451 cs_log_dump_dbg(D_DVBAPI
, msg
.buf
+ 2, msg
.buf_len
- 2, "capmt:");
452 // For some reason the mca sometimes sends duplicate ECMpids,
453 // we remove them here so dvbapi will not try them twice.
454 int new_len
= mca_capmt_remove_duplicates(msg
.buf
+ 2, msg
.buf_len
- 2);
455 if(new_len
< msg
.buf_len
- 2)
456 { cs_log_dump_dbg(D_DVBAPI
, msg
.buf
+ 2, new_len
, "capmt (duplicates removed):"); }
457 int demux_id
= dvbapi_parse_capmt(msg
.buf
+ 2, new_len
, -1, NULL
, 0, 0, 0, 0);
460 unsigned char mask
[12];
461 unsigned char comp
[12];
462 memset(&mask
, 0x00, sizeof(mask
));
463 memset(&comp
, 0x00, sizeof(comp
));
470 cs_log("could not parse pmt");
474 //if ((ret = openxcas_add_filter(msg.stream_id, OPENXCAS_FILTER_ECM, 0, 0xffff, openxcas_ecm_pid, mask, comp, (void *)mca_ecm_callback)) < 0)
475 DEMUXMATRIX demux_matrix
;
476 mca_demux_convert(&demux
[demux_id
], &demux_matrix
);
477 if((ret
= mca_write_flt(&demux_matrix
, 0)) < 0)
478 { cs_log("unable to add ecm filter"); }
481 cs_log_dbg(D_DVBAPI
, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid
, 0);
482 cs_log_dbg(D_DVBAPI
, "ecm filter started");
485 //if (!openxcas_create_cipher_ex(msg.stream_id, openxcas_seq, 0, openxcas_ecm_pid, openxcas_video_pid, 0xffff, openxcas_audio_pid, 0xffff, 0xffff, 0xffff))
486 // cs_log("failed to create cipher ex");
488 cs_log_dbg(D_DVBAPI
, "cipher created");
490 case OPENXCAS_STOP_PMT_ECM
:
491 cs_log_dbg(D_DVBAPI
, "OPENXCAS_STOP_PMT_ECM");
492 openxcas_stop_filter(msg
.stream_id
, OPENXCAS_FILTER_ECM
);
493 openxcas_remove_filter(msg
.stream_id
, OPENXCAS_FILTER_ECM
);
494 openxcas_stop_filter_ex(msg
.stream_id
, msg
.sequence
, openxcas_filter_idx
);
495 openxcas_destory_cipher_ex(msg
.stream_id
, msg
.sequence
);
496 memset(&demux
, 0, sizeof(demux
));
497 memset(&found
, 0, sizeof(found
));
499 case OPENXCAS_ECM_CALLBACK
:
500 cs_log_dbg(D_DVBAPI
, "OPENXCAS_ECM_CALLBACK");
501 memcpy(&data
, msg
.buf
, msg
.buf_len
);
503 //openxcas_filter_callback(msg.stream_id, msg.sequence, OPENXCAS_FILTER_ECM, &data);
504 { mca_ecm_callback(msg
.stream_id
, msg
.sequence
, data
.cipher_index
, data
.ca_system_id
, (unsigned char *)&data
.buf
, data
.len
, data
.pid
); }
506 case OPENXCAS_PID_FILTER_CALLBACK
:
507 cs_log_dbg(D_DVBAPI
, "OPENXCAS_PID_FILTER_CALLBACK");
508 memcpy(&data
, msg
.buf
, msg
.buf_len
);
509 //openxcas_filter_callback_ex(msg.stream_id, msg.sequence, (struct stOpenXCAS_Data *)msg.buf);
510 mca_ex_callback(msg
.stream_id
, msg
.sequence
, data
.cipher_index
, data
.pid
, (unsigned char *)&data
.buf
, data
.len
);
513 cs_log_dbg(D_DVBAPI
, "OPENXCAS_QUIT");
518 case OPENXCAS_UKNOWN_MSG
:
520 cs_log_dbg(D_DVBAPI
, "OPENXCAS_UKNOWN_MSG (%d)", msg
.cmd
);
521 //cs_log_dump_dbg(D_DVBAPI, &msg, sizeof(msg), "msg dump:");
526 cs_log("invalid message");
530 void mca_send_dcw(struct s_client
*client
, ECM_REQUEST
*er
)
532 struct s_dvbapi_priority
*delayentry
= dvbapi_check_prio_match(0, demux
[0].pidindex
, 'd');
535 cs_log_dbg(D_DVBAPI
, "send_dcw");
539 if(delayentry
->delay
< 1000)
541 delay
= delayentry
->delay
;
542 cs_log_dbg(D_DVBAPI
, "specific delay: write cw %d ms after ecmrequest", delay
);
545 else if (cfg
.dvbapi_delayer
> 0)
547 delay
= cfg
.dvbapi_delayer
;
548 cs_log_dbg(D_DVBAPI
, "generic delay: write cw %d ms after ecmrequest", delay
);
553 dvbapi_write_ecminfo_file(client
, er
, demux
[0].lastcw
[0], demux
[0].lastcw
[1]);
558 for(i
= 0; i
< MAX_DEMUX
; i
++)
561 if(er
->rc
>= E_NOTFOUND
&& !found
[i
])
563 cs_log_dbg(D_DVBAPI
, "cw not found");
565 if(demux
[i
].pidindex
== -1)
566 { dvbapi_try_next_caid(i
, 0, 0); }
568 openxcas_stop_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
569 openxcas_remove_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
571 unsigned char mask
[12];
572 unsigned char comp
[12];
573 memset(&mask
, 0x00, sizeof(mask
));
574 memset(&comp
, 0x00, sizeof(comp
));
579 DEMUXMATRIX demux_matrix
;
580 mca_demux_convert(&demux
[0], &demux_matrix
);
581 if(mca_write_flt(&demux_matrix
, 0) < 0)
582 { cs_log("unable to add ecm filter (0)"); }
585 cs_log_dbg(D_DVBAPI
, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid
, 0);
586 cs_log_dbg(D_DVBAPI
, "ecm filter started");
597 unsigned char nullcw
[8];
598 memset(nullcw
, 0, 8);
601 for(n
= 0; n
< 2; n
++)
603 // 0x2600 used by biss and constant cw could be indeed zero
604 if((memcmp(er
->cw
+ (n
* 8), demux
[0].lastcw
[0], 8) && memcmp(er
->cw
+ (n
* 8), demux
[0].lastcw
[1], 8))
605 && (memcmp(er
->cw
+ (n
* 8), nullcw
, 8) !=0 || er
->caid
== 0x2600))
607 memcpy(demux
[0].lastcw
[n
], er
->cw
+ (n
* 8), 8);
608 memcpy(openxcas_cw
+ (n
* 8), er
->cw
+ (n
* 8), 8);
609 if(mca_set_key(openxcas_cw
) < 0)
610 { cs_log("set cw failed"); }
612 { cs_log_dump_dbg(D_DVBAPI
, openxcas_cw
, 16, "write cws to descrambler"); }
617 void *mca_handler(struct s_client
*cl
, uchar
*mbuf
, int32_t module_idx
)
619 return dvbapi_start_handler(cl
, mbuf
, module_idx
, mca_main_thread
);