1 #define MODULE_LOG_PREFIX "dvbstapi"
5 #if defined(HAVE_DVBAPI) && defined(WITH_STAPI)
7 #include "module-dvbapi.h"
8 #include "module-dvbapi-stapi.h"
9 #include "oscam-client.h"
10 #include "oscam-files.h"
11 #include "oscam-string.h"
12 #include "oscam-time.h"
14 extern int32_t exit_oscam
;
19 uint32_t SessionHandle
;
20 uint32_t SignalHandle
;
22 struct filter_s demux_fd
[MAX_DEMUX
][MAX_FILTER
];
25 struct read_thread_param
32 #define PROCDIR "/proc/stpti4_core/"
34 /* These functions are in liboscam_stapi.a */
35 extern uint32_t oscam_stapi_Capability(char *name
);
36 extern char *oscam_stapi_LibVersion(void);
37 extern uint32_t oscam_stapi_Open(char *name
, uint32_t *sessionhandle
);
38 extern uint32_t oscam_stapi_SignalAllocate(uint32_t sessionhandle
, uint32_t *signalhandle
);
39 extern uint32_t oscam_stapi_FilterAllocate(uint32_t sessionhandle
, uint32_t *filterhandle
);
40 extern uint32_t oscam_stapi_SlotInit(uint32_t sessionhandle
, uint32_t signalhandle
, uint32_t *bufferhandle
, uint32_t *slothandle
, uint16_t pid
);
41 extern uint32_t oscam_stapi_FilterSet(uint32_t filterhandle
, uchar
*filt
, uchar
*mask
);
42 extern uint32_t oscam_stapi_FilterAssociate(uint32_t filterhandle
, uint32_t slothandle
);
43 extern uint32_t oscam_stapi_SlotDeallocate(uint32_t slothandle
);
44 extern uint32_t oscam_stapi_BufferDeallocate(uint32_t bufferhandle
);
45 extern uint32_t oscam_stapi_FilterDeallocate(uint32_t filterhandle
);
46 extern uint32_t oscam_stapi_Close(uint32_t sessionhandle
);
47 extern uint32_t oscam_stapi_CheckVersion(void);
48 extern uint32_t oscam_stapi_DescramblerAssociate(uint32_t deschandle
, uint32_t slot
);
49 extern uint32_t oscam_stapi_DescramblerDisassociate(uint32_t deschandle
, uint32_t slot
);
50 extern uint32_t oscam_stapi_DescramblerAllocate(uint32_t sessionhandle
, uint32_t *deschandle
);
51 extern uint32_t oscam_stapi_DescramblerDeallocate(uint32_t deschandle
);
52 extern uint32_t oscam_stapi_DescramblerSet(uint32_t deschandle
, int32_t parity
, uchar
*cw
);
53 extern uint32_t oscam_stapi_SignalWaitBuffer(uint32_t signalhandle
, uint32_t *qbuffer
, int32_t timeout
);
54 extern uint32_t oscam_stapi_BufferReadSection(uint32_t bufferhandle
, uint32_t *filterlist
, int32_t maxfilter
, uint32_t *filtercount
, int32_t *crc
, uchar
*buf
, int32_t bufsize
, uint32_t *size
);
55 extern uint32_t oscam_stapi_SignalAbort(uint32_t signalhandle
);
56 extern uint32_t oscam_stapi_PidQuery(char *name
, uint16_t pid
);
57 extern uint32_t oscam_stapi_BufferFlush(uint32_t bufferhandle
);
58 extern uint32_t oscam_stapi_SlotClearPid(uint32_t slot
);
61 static void *stapi_read_thread(void *);
62 static int32_t stapi_do_set_filter(int32_t demux_id
, FILTERTYPE
*filter
, uint16_t *pids
, int32_t pidcount
, uchar
*filt
, uchar
*mask
, int32_t dev_id
);
63 static int32_t stapi_do_remove_filter(int32_t demux_id
, FILTERTYPE
*filter
, int32_t dev_id
);
65 // These variables are declared in module-dvbapi.c
66 extern int32_t disable_pmt_files
;
67 extern struct s_dvbapi_priority
*dvbapi_priority
;
68 extern DEMUXTYPE demux
[MAX_DEMUX
];
70 static int32_t stapi_on
;
71 static pthread_mutex_t filter_lock
;
72 static struct STDEVICE dev_list
[PTINUM
];
74 static void stapi_off(void)
78 SAFE_MUTEX_LOCK(&filter_lock
);
80 cs_log("stapi shutdown");
82 disable_pmt_files
= 1;
84 for(i
= 0; i
< MAX_DEMUX
; i
++)
86 dvbapi_stop_descrambling(i
, 0);
89 for(i
= 0; i
< PTINUM
; i
++)
91 if(dev_list
[i
].SessionHandle
> 0)
93 if(dev_list
[i
].SignalHandle
> 0)
95 oscam_stapi_SignalAbort(dev_list
[i
].SignalHandle
);
97 pthread_cancel(dev_list
[i
].thread
);
101 SAFE_MUTEX_UNLOCK(&filter_lock
);
106 int32_t stapi_open(void)
111 struct dirent entry
, *dp
= NULL
;
116 int32_t stapi_priority
= 0;
118 dirp
= opendir(PROCDIR
);
121 cs_log("opendir failed (errno=%d %s)", errno
, strerror(errno
));
125 memset(dev_list
, 0, sizeof(struct STDEVICE
)*PTINUM
);
129 struct s_dvbapi_priority
*p
;
130 for(p
= dvbapi_priority
; p
!= NULL
; p
= p
->next
)
142 cs_log("WARNING: no PTI devices defined, stapi disabled");
146 oscam_stapi_CheckVersion();
149 while(!cs_readdir_r(dirp
, &entry
, &dp
))
153 snprintf(pfad
, sizeof(pfad
), "%s%s", PROCDIR
, dp
->d_name
);
154 if(stat(pfad
, &buf
) != 0)
157 if(!(buf
.st_mode
& S_IFDIR
&& strncmp(dp
->d_name
, ".", 1) != 0))
161 struct s_dvbapi_priority
*p
;
163 for(p
= dvbapi_priority
; p
!= NULL
; p
= p
->next
)
165 if(p
->type
!= 's') { continue; }
166 if(strcmp(dp
->d_name
, p
->devname
) == 0)
175 cs_log("PTI: %s skipped", dp
->d_name
);
179 ErrorCode
= oscam_stapi_Open(dp
->d_name
, &dev_list
[i
].SessionHandle
);
182 cs_log("STPTI_Open ErrorCode: %d", ErrorCode
);
187 //oscam_stapi_Capability(dp->d_name);
189 cs_strncpy(dev_list
[i
].name
, dp
->d_name
, sizeof(dev_list
[i
].name
));
190 cs_log("PTI: %s open %d", dp
->d_name
, i
);
192 ErrorCode
= oscam_stapi_SignalAllocate(dev_list
[i
].SessionHandle
, &dev_list
[i
].SignalHandle
);
194 { cs_log("SignalAllocate: ErrorCode: %d SignalHandle: %x", ErrorCode
, dev_list
[i
].SignalHandle
); }
197 if(i
>= PTINUM
) { break; }
201 if(i
== 0) { return 0; }
203 SAFE_MUTEX_INIT(&filter_lock
, NULL
);
205 for(i
= 0; i
< PTINUM
; i
++)
207 if(dev_list
[i
].SessionHandle
== 0)
210 struct read_thread_param
*para
;
211 if(!cs_malloc(¶
, sizeof(struct read_thread_param
)))
214 para
->cli
= cur_client();
216 int32_t ret
= start_thread("stapi read", stapi_read_thread
, (void *)para
, &dev_list
[i
].thread
, 1, 0);
225 cs_log("liboscam_stapi v.%s initialized", oscam_stapi_LibVersion());
229 int32_t stapi_activate_section_filter(int32_t fd
, uchar
*filter
, uchar
*mask
)
232 while (n
<3 && ret
==852049) {
233 ret
= oscam_stapi_FilterSet(fd
, filter
, mask
);
235 cs_log_dbg(D_DVBAPI
, "Error: oscam_stapi_FilterSet; %d", ret
);
241 cs_log("Error: stapi_activate_section_filter: %d", ret
);
247 int32_t stapi_set_filter(int32_t demux_id
, uint16_t pid
, uchar
*filter
, uchar
*mask
, int32_t num
, char *pmtfile
)
252 uint16_t pids
[1] = { pid
};
253 struct s_dvbapi_priority
*p
;
257 cs_log_dbg(D_DVBAPI
, "No valid pmtfile!");
261 cs_log_dbg(D_DVBAPI
, "pmt file %s demux_id %d", pmtfile
, demux_id
);
263 for(p
= dvbapi_priority
; p
!= NULL
; p
= p
->next
)
265 if(p
->type
!= 's') { continue; } // stapi rule?
266 if(strcmp(pmtfile
, p
->pmtfile
) != 0) { continue; } // same file?
268 for(i
= 0; i
< PTINUM
; i
++)
270 if(strcmp(dev_list
[i
].name
, p
->devname
) == 0 && p
->disablefilter
== 0) // check device name and if filtering is enabled!
272 cs_log_dbg(D_DVBAPI
, "set stapi filter on %s for pid %04X", dev_list
[i
].name
, pids
[0]);
273 ret
= stapi_do_set_filter(demux_id
, &dev_list
[i
].demux_fd
[demux_id
][num
], pids
, 1, filter
, mask
, i
);
274 if(ret
> 0) // success
276 cs_log_dbg(D_DVBAPI
, "%s filter %d set (pid %04X)", dev_list
[i
].name
, num
, pid
);
277 return ret
; // return filternumber
281 cs_log_dbg(D_DVBAPI
, "Error setting new filter for pid %04X on %s!", pid
, dev_list
[i
].name
);
282 return -1; // set return to error
290 cs_log_dbg(D_DVBAPI
, "No matching S: line in oscam.dvbapi for pmtfile %s -> stop descrambling!", pmtfile
);
291 snprintf(dest
, sizeof(dest
), "%s%s", TMPDIR
, demux
[demux_id
].pmt_file
);
292 unlink(dest
); // remove obsolete pmt file
293 dvbapi_stop_descrambling(demux_id
, 0);
298 int32_t stapi_remove_filter(int32_t demux_id
, int32_t num
, char *pmtfile
)
301 struct s_dvbapi_priority
*p
;
303 if(!pmtfile
) { return 0; }
305 for(p
= dvbapi_priority
; p
!= NULL
; p
= p
->next
)
307 if(p
->type
!= 's') { continue; }
308 if(strcmp(pmtfile
, p
->pmtfile
) != 0)
311 for(i
= 0; i
< PTINUM
; i
++)
313 if(strcmp(dev_list
[i
].name
, p
->devname
) == 0 && p
->disablefilter
== 0)
315 ret
= stapi_do_remove_filter(demux_id
, &dev_list
[i
].demux_fd
[demux_id
][num
], i
);
321 cs_log_dbg(D_DVBAPI
, "filter %d removed", num
);
325 cs_log_dbg(D_DVBAPI
, "Error: filter %d was not removed!", num
);
330 static uint32_t check_slot(int32_t dev_id
, uint32_t checkslot
, FILTERTYPE
*skipfilter
)
333 for(d
= 0; d
< MAX_DEMUX
; d
++)
335 for(f
= 0; f
< MAX_FILTER
; f
++)
337 if(skipfilter
&& &dev_list
[dev_id
].demux_fd
[d
][f
] == skipfilter
)
339 for(l
= 0; l
< dev_list
[dev_id
].demux_fd
[d
][f
].NumSlots
; l
++)
341 if(checkslot
== dev_list
[dev_id
].demux_fd
[d
][f
].SlotHandle
[l
])
343 return dev_list
[dev_id
].demux_fd
[d
][f
].BufferHandle
[l
];
352 static int32_t stapi_do_set_filter(int32_t demux_id
, FILTERTYPE
*filter
, uint16_t *pids
, int32_t pidcount
, uchar
*filt
, uchar
*mask
, int32_t dev_id
)
354 uint32_t FilterAssociateError
= 0;
358 filter
->BufferHandle
[0] = 0;
359 filter
->SlotHandle
[0] = 0;
361 if(dev_list
[dev_id
].SessionHandle
== 0) { return 0; }
363 uint32_t FilterAllocateError
= oscam_stapi_FilterAllocate(dev_list
[dev_id
].SessionHandle
, &filter
->fd
);
365 if(FilterAllocateError
!= 0)
367 cs_log("FilterAllocate problem");
372 for(k
= 0; k
< pidcount
; k
++)
374 uint16_t pid
= pids
[k
];
376 uint32_t QuerySlot
= oscam_stapi_PidQuery(dev_list
[dev_id
].name
, pid
);
377 int32_t SlotInit
= 1;
381 uint32_t checkslot
= check_slot(dev_id
, QuerySlot
, NULL
);
384 filter
->SlotHandle
[k
] = QuerySlot
;
385 filter
->BufferHandle
[k
] = checkslot
;
390 cs_log("overtake: clear pid: %d", oscam_stapi_SlotClearPid(QuerySlot
));
397 ret
= oscam_stapi_SlotInit(dev_list
[dev_id
].SessionHandle
, dev_list
[dev_id
].SignalHandle
, &filter
->BufferHandle
[k
], &filter
->SlotHandle
[k
], pid
);
400 FilterAssociateError
= oscam_stapi_FilterAssociate(filter
->fd
, filter
->SlotHandle
[k
]);
404 uint32_t FilterSetError
= oscam_stapi_FilterSet(filter
->fd
, filt
, mask
);
406 if(ret
|| FilterAllocateError
|| FilterAssociateError
|| FilterSetError
)
408 cs_log("set_filter: dev: %d FAl: %d FAs: %d FS: %d",
409 dev_id
, FilterAllocateError
, FilterAssociateError
, FilterSetError
);
410 stapi_do_remove_filter(demux_id
, filter
, dev_id
);
415 return filter
->fd
; // return fd of filter
419 static int32_t stapi_do_remove_filter(int32_t UNUSED(demux_id
), FILTERTYPE
*filter
, int32_t dev_id
)
421 if(filter
->fd
== 0) { return 0; }
423 uint32_t BufferDeallocateError
= 0, SlotDeallocateError
= 0;
425 if(dev_list
[dev_id
].SessionHandle
== 0) { return 0; }
428 for(k
= 0; k
< filter
->NumSlots
; k
++)
430 uint32_t checkslot
= check_slot(dev_id
, filter
->SlotHandle
[k
], filter
);
434 BufferDeallocateError
= oscam_stapi_BufferDeallocate(filter
->BufferHandle
[k
]);
435 SlotDeallocateError
= oscam_stapi_SlotDeallocate(filter
->SlotHandle
[k
]);
438 uint32_t FilterDeallocateError
= oscam_stapi_FilterDeallocate(filter
->fd
);
440 memset(filter
, 0, sizeof(FILTERTYPE
));
442 if(BufferDeallocateError
|| SlotDeallocateError
|| FilterDeallocateError
)
444 cs_log("remove_filter: dev: %d BD: %d SD: %d FDe: %d",
445 dev_id
, BufferDeallocateError
, SlotDeallocateError
, FilterDeallocateError
);
454 static void stapi_cleanup_thread(void *dev
)
456 int32_t dev_index
= (int)dev
;
459 ErrorCode
= oscam_stapi_Close(dev_list
[dev_index
].SessionHandle
);
461 cs_log("liboscam_stapi: PTI %s closed - %d\n", dev_list
[dev_index
].name
, ErrorCode
);
462 dev_list
[dev_index
].SessionHandle
= 0;
465 static void *stapi_read_thread(void *sparam
)
467 int32_t dev_index
, ErrorCode
, i
, j
, CRCValid
;
468 uint32_t QueryBufferHandle
= 0, DataSize
= 0;
471 struct read_thread_param
*para
= sparam
;
472 dev_index
= para
->id
;
474 SAFE_SETSPECIFIC(getclient
, para
->cli
);
475 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, NULL
);
476 pthread_cleanup_push(stapi_cleanup_thread
, (void *) dev_index
);
478 int32_t error_count
= 0;
482 QueryBufferHandle
= 0;
483 ErrorCode
= oscam_stapi_SignalWaitBuffer(dev_list
[dev_index
].SignalHandle
, &QueryBufferHandle
, 1000);
489 case 852042: // ERROR_SIGNAL_ABORTED
490 cs_log("Caught abort signal");
493 case 11: // ERROR_TIMEOUT:
494 //cs_log("timeout %d", dev_index);
495 //TODO: if pidindex == -1 try next
499 if(QueryBufferHandle
!= 0)
501 cs_log("SignalWaitBuffer error: %d", ErrorCode
);
502 oscam_stapi_BufferFlush(QueryBufferHandle
);
505 cs_log("SignalWaitBuffer: index %d ErrorCode: %d - QueryBuffer: %x", dev_index
, ErrorCode
, QueryBufferHandle
);
509 cs_log("Too many errors in reader thread %d, quitting.", dev_index
);
516 uint32_t NumFilterMatches
= 0;
517 int32_t demux_id
= 0, filter_num
= 0;
521 uint32_t MatchedFilterList
[10];
522 ErrorCode
= oscam_stapi_BufferReadSection(QueryBufferHandle
, MatchedFilterList
, 10, &NumFilterMatches
, &CRCValid
, buf
, BUFFLEN
, &DataSize
);
526 cs_log("BufferRead: index: %d ErrorCode: %d", dev_index
, ErrorCode
);
534 SAFE_MUTEX_LOCK(&filter_lock
); // don't use cs_lock() here; multiple threads using same s_client struct
535 for(k
= 0; k
< NumFilterMatches
; k
++)
537 for(i
= 0; i
< MAX_DEMUX
; i
++)
539 for(j
= 0; j
< MAX_FILTER
; j
++)
541 if(dev_list
[dev_index
].demux_fd
[i
][j
].fd
== MatchedFilterList
[k
])
546 dvbapi_process_input(demux_id
, filter_num
, buf
, DataSize
, 0);
551 SAFE_MUTEX_UNLOCK(&filter_lock
);
554 pthread_cleanup_pop(0);
560 #define DISASSOCIATE 0
565 static void stapi_DescramblerAssociate(int32_t demux_id
, uint16_t pid
, int32_t mode
, int32_t n
)
568 int32_t ErrorCode
= 0;
570 if(dev_list
[n
].SessionHandle
== 0) { return; }
572 Slot
= oscam_stapi_PidQuery(dev_list
[n
].name
, pid
);
573 if(!Slot
) { return; }
575 if(demux
[demux_id
].DescramblerHandle
[n
] == 0) { return; }
577 if(mode
== ASSOCIATE
)
580 for(k
= 0; k
< SLOTNUM
; k
++)
582 if(demux
[demux_id
].slot_assc
[n
][k
] == Slot
)
588 ErrorCode
= oscam_stapi_DescramblerAssociate(demux
[demux_id
].DescramblerHandle
[n
], Slot
);
589 cs_log_dbg(D_DVBAPI
, "set pid %04x on %s", pid
, dev_list
[n
].name
);
592 { cs_log("DescramblerAssociate %d", ErrorCode
); }
594 for(k
= 0; k
< SLOTNUM
; k
++)
596 if(demux
[demux_id
].slot_assc
[n
][k
] == 0)
598 demux
[demux_id
].slot_assc
[n
][k
] = Slot
;
605 ErrorCode
= oscam_stapi_DescramblerDisassociate(demux
[demux_id
].DescramblerHandle
[n
], Slot
);
607 { cs_log_dbg(D_DVBAPI
, "DescramblerDisassociate %d", ErrorCode
); }
609 cs_log_dbg(D_DVBAPI
, "unset pid %04x on %s", pid
, dev_list
[n
].name
);
612 for(k
= 0; k
< SLOTNUM
; k
++)
614 if(demux
[demux_id
].slot_assc
[n
][k
] == Slot
)
616 demux
[demux_id
].slot_assc
[n
][k
] = 0;
625 static void stapi_startdescrambler(int32_t demux_id
, int32_t dev_index
, int32_t mode
)
629 if(mode
== DE_START
&& demux
[demux_id
].DescramblerHandle
[dev_index
] == 0)
631 uint32_t DescramblerHandle
= 0;
632 ErrorCode
= oscam_stapi_DescramblerAllocate(dev_list
[dev_index
].SessionHandle
, &DescramblerHandle
);
635 cs_log("DescramblerAllocate: ErrorCode: %d SignalHandle: %x", ErrorCode
, dev_list
[dev_index
].SignalHandle
);
639 demux
[demux_id
].DescramblerHandle
[dev_index
] = DescramblerHandle
;
642 if(mode
== DE_STOP
&& demux
[demux_id
].DescramblerHandle
[dev_index
] > 0)
644 ErrorCode
= oscam_stapi_DescramblerDeallocate(demux
[demux_id
].DescramblerHandle
[dev_index
]);
647 { cs_log("DescramblerDeallocate: ErrorCode: %d", ErrorCode
); }
649 demux
[demux_id
].DescramblerHandle
[dev_index
] = 0;
655 int32_t stapi_set_pid(int32_t demux_id
, int32_t UNUSED(num
), ca_index_t idx
, uint16_t UNUSED(pid
), char *UNUSED(pmtfile
))
659 if(idx
== INDEX_INVALID
)
661 for(n
= 0; n
< PTINUM
; n
++)
663 if(demux
[demux_id
].DescramblerHandle
[n
] == 0) { continue; }
665 cs_log_dbg(D_DVBAPI
, "stop descrambling PTI: %s", dev_list
[n
].name
);
666 stapi_startdescrambler(demux_id
, n
, DE_STOP
);
667 memset(demux
[demux_id
].slot_assc
[n
], 0, sizeof(demux
[demux_id
].slot_assc
[n
]));
674 int32_t stapi_write_cw(int32_t demux_id
, uchar
*cw
, uint16_t *STREAMpids
, int32_t STREAMpidcount
, char *pmtfile
)
676 int32_t ErrorCode
, l
, n
, k
;
677 unsigned char nullcw
[8];
678 memset(nullcw
, 0, 8);
679 char *text
[] = { "even", "odd" };
681 if(!pmtfile
) { return 0; }
683 for(n
= 0; n
< PTINUM
; n
++)
685 if(dev_list
[n
].SessionHandle
== 0) { continue; }
686 if(demux
[demux_id
].DescramblerHandle
[n
] == 0)
688 struct s_dvbapi_priority
*p
;
690 for(p
= dvbapi_priority
; p
!= NULL
; p
= p
->next
)
692 if(p
->type
!= 's') { continue; }
693 if(strcmp(pmtfile
, p
->pmtfile
) != 0)
696 if(strcmp(dev_list
[n
].name
, p
->devname
) == 0)
698 cs_log_dbg(D_DVBAPI
, "start descrambling PTI: %s", dev_list
[n
].name
);
699 stapi_startdescrambler(demux_id
, n
, DE_START
);
704 if(demux
[demux_id
].DescramblerHandle
[n
] == 0) { continue; }
706 for(k
= 0; k
< STREAMpidcount
; k
++)
708 stapi_DescramblerAssociate(demux_id
, STREAMpids
[k
], ASSOCIATE
, n
);
712 int32_t pidnum
= demux
[demux_id
].pidindex
; // get current pidindex used for descrambling
713 ca_index_t idx
= demux
[demux_id
].ECMpids
[pidnum
].index
[0];
715 if(idx
== INDEX_INVALID
) // if no indexer for this pid get one!
717 idx
= dvbapi_get_descindex(demux_id
, pidnum
, 0);
718 cs_log_dbg(D_DVBAPI
, "Demuxer %d PID: %d CAID: %04X ECMPID: %04X is using index %d", demux_id
, pidnum
,
719 demux
[demux_id
].ECMpids
[pidnum
].CAID
, demux
[demux_id
].ECMpids
[pidnum
].ECM_PID
, idx
);
722 for(l
= 0; l
< 2; l
++)
724 if(memcmp(cw
+ (l
* 8), demux
[demux_id
].lastcw
[l
], 8) != 0 && (memcmp(cw
+ (l
* 8), nullcw
, 8) != 0 || demux
[demux_id
].ECMpids
[pidnum
].CAID
== 0x2600))
726 for(n
= 0; n
< PTINUM
; n
++)
728 if(demux
[demux_id
].DescramblerHandle
[n
] == 0) { continue; }
730 ErrorCode
= oscam_stapi_DescramblerSet(demux
[demux_id
].DescramblerHandle
[n
], l
, cw
+ (l
* 8));
732 { cs_log("DescramblerSet: ErrorCode: %d", ErrorCode
); }
734 memcpy(demux
[demux_id
].lastcw
[l
], cw
+ (l
* 8), 8);
735 cs_log_dbg(D_DVBAPI
, "write cw %s index: %d %s", text
[l
], demux_id
, dev_list
[n
].name
);
743 // Needed for compatability with liboscam_stapi.a
745 void cs_log(const char *fmt
, ...)
750 va_start(params
, fmt
);
751 vsnprintf(log_txt
, sizeof(log_txt
), fmt
, params
);
754 cs_log_txt(MODULE_LOG_PREFIX
, "%s", log_txt
);