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 uint8_t 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 uint8_t lastcw
[2][8];
131 uint8_t hexserial
[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(uint8_t *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(uint8_t *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] = (uint8_t)((capmt
[4] & 0xF0) | ((program_info_length
& 0xF00) >> 8));
255 capmt
[5] = (uint8_t)(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
= (uint8_t)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
[0], &demux_orig
->last_cw
[0][0], 8 * sizeof(uint8_t));
297 memcpy(&demux_matrix
->lastcw
[1], &demux_orig
->last_cw
[0][1], 8 * sizeof(uint8_t));
298 demux_matrix
->emm_filter
= (int32_t)demux_orig
->emm_filter
;
299 memcpy(&demux_matrix
->hexserial
, &demux_orig
->hexserial
, 8 * sizeof(uint8_t));
300 demux_matrix
->rdr
= (struct s_reader
*)demux_orig
->rdr
;
301 memcpy(&demux_matrix
->pmt_file
, &demux_orig
->pmt_file
, 30);
302 demux_matrix
->pmt_time
= (int32_t)demux_orig
->pmt_time
;
305 static void mca_ecm_callback(int32_t stream_id
, uint32_t UNUSED(seq
), int32_t cipher_index
, uint32_t caid
, uint8_t *ecm_data
, int32_t l
, uint16_t pid
)
307 cs_log_dbg(D_DVBAPI
, "ecm callback received");
309 openxcas_stream_id
= stream_id
;
310 //openxcas_seq = seq;
311 //openxcas_caid = caid;
312 openxcas_ecm_pid
= pid
;
316 //As soon as we have received a valid CW we lock onto that CAID, otherwise we will have freezers.
317 if(openxcas_caid
&& caid
&& openxcas_caid
!= caid
)
319 cs_log("ignoring caid: %04X, waiting for %04X", caid
, openxcas_caid
);
324 if(l
< 0 || l
> MAX_ECM_SIZE
)
328 if(!(er
= get_ecmtask()))
331 er
->srvid
= openxcas_sid
;
332 er
->caid
= openxcas_caid
;
333 er
->pid
= openxcas_ecm_pid
;
334 er
->prid
= openxcas_provid
;
337 memcpy(er
->ecm
, ecm_data
, er
->ecmlen
);
339 request_cw(dvbapi_client
, er
, 0, 0);
341 openxcas_stop_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
342 openxcas_remove_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
344 openxcas_cipher_idx
= cipher_index
;
352 static void mca_ex_callback(int32_t stream_id
, uint32_t UNUSED(seq
), int32_t idx
, uint32_t pid
, uint8_t *ecm_data
, int32_t l
)
354 cs_log_dbg(D_DVBAPI
, "ex callback received");
356 openxcas_stream_id
= stream_id
;
357 openxcas_ecm_pid
= pid
;
358 openxcas_cipher_idx
= idx
; // is this really cipher_idx?
360 if(l
< 0 || l
> MAX_ECM_SIZE
)
364 if(!(er
= get_ecmtask()))
367 er
->srvid
= openxcas_sid
;
368 er
->caid
= openxcas_caid
;
369 er
->pid
= openxcas_ecm_pid
;
370 er
->prid
= openxcas_provid
;
373 memcpy(er
->ecm
, ecm_data
, er
->ecmlen
);
375 request_cw(dvbapi_client
, er
, 0, 0);
377 if(openxcas_stop_filter_ex(stream_id
, seq
, openxcas_filter_idx
) < 0)
378 { cs_log("unable to stop ex filter"); }
380 { cs_log_dbg(D_DVBAPI
, "ex filter stopped"); }
384 memset(&mask
, 0x00, sizeof(mask
));
385 memset(&comp
, 0x00, sizeof(comp
));
388 comp
[0] = ecm_data
[0] ^ 1;
390 if((openxcas_filter_idx
= openxcas_start_filter_ex(stream_id
, seq
, openxcas_ecm_pid
, mask
, comp
, (void *)azbox_openxcas_ex_callback
)) < 0)
391 { cs_log("unable to start ex filter"); }
393 { cs_log_dbg(D_DVBAPI
, "ex filter started, pid = %x", openxcas_ecm_pid
); }
396 static void *mca_main_thread(void *cli
)
398 struct s_client
*client
= (struct s_client
*) cli
;
399 client
->thread
= pthread_self();
400 SAFE_SETSPECIFIC(getclient
, cli
);
403 struct s_auth
*account
;
405 for(account
= cfg
.account
; account
; account
= account
->next
)
407 if((ok
= is_dvbapi_usr(account
->usr
)))
410 cs_auth_client(client
, ok
? account
: (struct s_auth
*)(-1), "dvbapi");
412 dvbapi_read_priority();
416 while((ret
= openxcas_get_message(&msg
, 0)) >= 0)
422 openxcas_stream_id
= msg
.stream_id
;
423 openxcas_seq
= msg
.sequence
;
424 struct stOpenXCAS_Data data
;
428 case OPENXCAS_SELECT_CHANNEL
:
429 cs_log_dbg(D_DVBAPI
, "OPENXCAS_SELECT_CHANNEL");
431 // parse channel info
432 struct stOpenXCASChannel chan
;
433 memcpy(&chan
, msg
.buf
, msg
.buf_len
);
435 cs_log("channel change: sid = %x, vpid = %x. apid = %x", chan
.service_id
, chan
.v_pid
, chan
.a_pid
);
437 openxcas_video_pid
= chan
.v_pid
;
438 openxcas_audio_pid
= chan
.a_pid
;
439 openxcas_data_pid
= chan
.d_pid
;
441 case OPENXCAS_START_PMT_ECM
:
442 //FIXME: Apparently this is what the original MCA-oscam does
443 cs_log_dbg(D_DVBAPI
, "OPENXCAS_STOP_PMT_ECM");
444 memset(&demux
, 0, sizeof(demux
));
445 memset(&found
, 0, sizeof(found
));
447 cs_log_dbg(D_DVBAPI
, "OPENXCAS_START_PMT_ECM");
450 cs_log_dump_dbg(D_DVBAPI
, msg
.buf
+ 2, msg
.buf_len
- 2, "capmt:");
451 // For some reason the mca sometimes sends duplicate ECMpids,
452 // we remove them here so dvbapi will not try them twice.
453 int new_len
= mca_capmt_remove_duplicates(msg
.buf
+ 2, msg
.buf_len
- 2);
454 if(new_len
< msg
.buf_len
- 2)
455 { cs_log_dump_dbg(D_DVBAPI
, msg
.buf
+ 2, new_len
, "capmt (duplicates removed):"); }
456 int demux_id
= dvbapi_parse_capmt(msg
.buf
+ 2, new_len
, -1, NULL
, 0, 0);
460 memset(&mask
, 0x00, sizeof(mask
));
461 memset(&comp
, 0x00, sizeof(comp
));
468 cs_log("could not parse pmt");
472 //if ((ret = openxcas_add_filter(msg.stream_id, OPENXCAS_FILTER_ECM, 0, 0xffff, openxcas_ecm_pid, mask, comp, (void *)mca_ecm_callback)) < 0)
473 DEMUXMATRIX demux_matrix
;
474 mca_demux_convert(&demux
[demux_id
], &demux_matrix
);
475 if((ret
= mca_write_flt(&demux_matrix
, 0)) < 0)
476 { cs_log("unable to add ecm filter"); }
479 cs_log_dbg(D_DVBAPI
, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid
, 0);
480 cs_log_dbg(D_DVBAPI
, "ecm filter started");
483 //if (!openxcas_create_cipher_ex(msg.stream_id, openxcas_seq, 0, openxcas_ecm_pid, openxcas_video_pid, 0xffff, openxcas_audio_pid, 0xffff, 0xffff, 0xffff))
484 // cs_log("failed to create cipher ex");
486 cs_log_dbg(D_DVBAPI
, "cipher created");
488 case OPENXCAS_STOP_PMT_ECM
:
489 cs_log_dbg(D_DVBAPI
, "OPENXCAS_STOP_PMT_ECM");
490 openxcas_stop_filter(msg
.stream_id
, OPENXCAS_FILTER_ECM
);
491 openxcas_remove_filter(msg
.stream_id
, OPENXCAS_FILTER_ECM
);
492 openxcas_stop_filter_ex(msg
.stream_id
, msg
.sequence
, openxcas_filter_idx
);
493 openxcas_destory_cipher_ex(msg
.stream_id
, msg
.sequence
);
494 memset(&demux
, 0, sizeof(demux
));
495 memset(&found
, 0, sizeof(found
));
497 case OPENXCAS_ECM_CALLBACK
:
498 cs_log_dbg(D_DVBAPI
, "OPENXCAS_ECM_CALLBACK");
499 memcpy(&data
, msg
.buf
, msg
.buf_len
);
501 //openxcas_filter_callback(msg.stream_id, msg.sequence, OPENXCAS_FILTER_ECM, &data);
502 { mca_ecm_callback(msg
.stream_id
, msg
.sequence
, data
.cipher_index
, data
.ca_system_id
, (uint8_t *)&data
.buf
, data
.len
, data
.pid
); }
504 case OPENXCAS_PID_FILTER_CALLBACK
:
505 cs_log_dbg(D_DVBAPI
, "OPENXCAS_PID_FILTER_CALLBACK");
506 memcpy(&data
, msg
.buf
, msg
.buf_len
);
507 //openxcas_filter_callback_ex(msg.stream_id, msg.sequence, (struct stOpenXCAS_Data *)msg.buf);
508 mca_ex_callback(msg
.stream_id
, msg
.sequence
, data
.cipher_index
, data
.pid
, (uint8_t *)&data
.buf
, data
.len
);
511 cs_log_dbg(D_DVBAPI
, "OPENXCAS_QUIT");
516 case OPENXCAS_UKNOWN_MSG
:
518 cs_log_dbg(D_DVBAPI
, "OPENXCAS_UKNOWN_MSG (%d)", msg
.cmd
);
519 //cs_log_dump_dbg(D_DVBAPI, &msg, sizeof(msg), "msg dump:");
524 cs_log("invalid message");
528 void mca_send_dcw(struct s_client
*client
, ECM_REQUEST
*er
)
530 struct s_dvbapi_priority
*delayentry
= dvbapi_check_prio_match(0, demux
[0].pidindex
, 'd');
533 cs_log_dbg(D_DVBAPI
, "send_dcw");
537 if(delayentry
->delay
< 1000)
539 delay
= delayentry
->delay
;
540 cs_log_dbg(D_DVBAPI
, "specific delay: write cw %d ms after ecmrequest", delay
);
543 else if (cfg
.dvbapi_delayer
> 0)
545 delay
= cfg
.dvbapi_delayer
;
546 cs_log_dbg(D_DVBAPI
, "generic delay: write cw %d ms after ecmrequest", delay
);
551 if(cfg
.dvbapi_ecminfo_file
!= 0)
553 dvbapi_write_ecminfo_file(client
, er
, demux
[0].last_cw
[0][0], demux
[0].last_cw
[0][1], 8);
559 for(i
= 0; i
< MAX_DEMUX
; i
++)
562 if(er
->rc
>= E_NOTFOUND
&& !found
[i
])
564 cs_log_dbg(D_DVBAPI
, "cw not found");
566 if(demux
[i
].pidindex
== -1)
567 { dvbapi_try_next_caid(i
, 0, 0); }
569 openxcas_stop_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
570 openxcas_remove_filter(openxcas_stream_id
, OPENXCAS_FILTER_ECM
);
574 memset(&mask
, 0x00, sizeof(mask
));
575 memset(&comp
, 0x00, sizeof(comp
));
580 DEMUXMATRIX demux_matrix
;
581 mca_demux_convert(&demux
[0], &demux_matrix
);
582 if(mca_write_flt(&demux_matrix
, 0) < 0)
583 { cs_log("unable to add ecm filter (0)"); }
586 cs_log_dbg(D_DVBAPI
, "ecm filter added, pid = %x, caid = %x", openxcas_ecm_pid
, 0);
587 cs_log_dbg(D_DVBAPI
, "ecm filter started");
599 memset(nullcw
, 0, 8);
602 for(n
= 0; n
< 2; n
++)
604 // Skip check for BISS1 - cw could be indeed zero
605 // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero
606 if((memcmp(er
->cw
+ (n
* 8), demux
[0].last_cw
[0][0], 8) && memcmp(er
->cw
+ (n
* 8), demux
[0].last_cw
[0][1], 8))
607 && (memcmp(er
->cw
+ (n
* 8), nullcw
, 8) != 0 || caid_is_biss(er
->caid
)))
609 memcpy(demux
[0].last_cw
[0][n
], er
->cw
+ (n
* 8), 8);
610 memcpy(openxcas_cw
+ (n
* 8), er
->cw
+ (n
* 8), 8);
611 if(mca_set_key(openxcas_cw
) < 0)
612 { cs_log("set cw failed"); }
614 { cs_log_dump_dbg(D_DVBAPI
, openxcas_cw
, 16, "write cws to descrambler"); }
619 void *mca_handler(struct s_client
*cl
, uint8_t *mbuf
, int32_t module_idx
)
621 return dvbapi_start_handler(cl
, mbuf
, module_idx
, mca_main_thread
);