1 /*
2 * Tvheadend - Linux DVB CA
3 *
4 * Copyright (C) 2015 Damjan Marion
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "tvheadend.h"
21 #include "linuxdvb_private.h"
22 #include "notify.h"
23
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <assert.h>
30 #include <linux/dvb/dmx.h>
31 #include <linux/dvb/frontend.h>
32 #include <linux/dvb/ca.h>
33
34 #include "descrambler/caid.h"
35 #include "descrambler/dvbcam.h"
36
37 static void linuxdvb_ca_monitor ( void *aux );
38
39 typedef enum {
40 CA_SLOT_STATE_EMPTY = 0,
41 CA_SLOT_STATE_MODULE_PRESENT,
42 CA_SLOT_STATE_MODULE_READY
43 } ca_slot_state_t;
44
45 const static char *
ca_slot_state2str(ca_slot_state_t v)46 ca_slot_state2str(ca_slot_state_t v)
47 {
48 switch(v) {
49 case CA_SLOT_STATE_EMPTY: return "slot empty";
50 case CA_SLOT_STATE_MODULE_PRESENT: return "module present";
51 case CA_SLOT_STATE_MODULE_READY: return "module ready";
52 };
53 return "UNKNOWN";
54 }
55
56 const static char *
ca_pmt_list_mgmt2str(uint8_t v)57 ca_pmt_list_mgmt2str(uint8_t v)
58 {
59 switch(v) {
60 case CA_LIST_MANAGEMENT_MORE: return "more";
61 case CA_LIST_MANAGEMENT_FIRST: return "first";
62 case CA_LIST_MANAGEMENT_LAST: return "last";
63 case CA_LIST_MANAGEMENT_ONLY: return "only";
64 case CA_LIST_MANAGEMENT_ADD: return "add";
65 case CA_LIST_MANAGEMENT_UPDATE: return "update";
66 }
67 return "UNKNOWN";
68 }
69
70 const static char *
ca_pmt_cmd_id2str(uint8_t v)71 ca_pmt_cmd_id2str(uint8_t v)
72 {
73 switch(v) {
74 case CA_PMT_CMD_ID_OK_DESCRAMBLING: return "ok_descrambling";
75 case CA_PMT_CMD_ID_OK_MMI: return "ok_mmi";
76 case CA_PMT_CMD_ID_QUERY: return "query";
77 case CA_PMT_CMD_ID_NOT_SELECTED: return "not_selected";
78 }
79 return "UNKNOWN";
80 }
81
82 struct linuxdvb_ca_capmt {
83 TAILQ_ENTRY(linuxdvb_ca_capmt) lcc_link;
84 int len;
85 uint8_t *data;
86 uint8_t slot;
87 uint8_t list_mgmt;
88 uint8_t cmd_id;
89 };
90
91 /*
92 * ts101699 and CI+ 1.3 definitions
93 */
94 #define TS101699_APP_AI_RESOURCEID MKRID(2, 1, 2)
95 #define CIPLUS13_APP_AI_RESOURCEID MKRID(2, 1, 3)
96
97 typedef enum {
98 CIPLUS13_DATA_RATE_72_MBPS = 0,
99 CIPLUS13_DATA_RATE_96_MBPS = 1,
100 } ciplus13_data_rate_t;
101
102 static int
ciplus13_app_ai_data_rate_info(linuxdvb_ca_t * lca,ciplus13_data_rate_t rate)103 ciplus13_app_ai_data_rate_info(linuxdvb_ca_t *lca, ciplus13_data_rate_t rate)
104 {
105 uint8_t data[] = {0x9f, 0x80, 0x24, 0x01, (uint8_t) rate};
106
107 /* only version 3 (CI+ 1.3) supports data_rate_info */
108 if (lca->lca_ai_version != 3)
109 return 0;
110
111 tvhinfo(LS_EN50221, "setting CI+ CAM data rate to %s Mbps", rate ? "96":"72");
112
113 return en50221_sl_send_data(lca->lca_sl, lca->lca_ai_session_number, data, sizeof(data));
114 }
115
116 static void
linuxdvb_ca_class_changed(idnode_t * in)117 linuxdvb_ca_class_changed ( idnode_t *in )
118 {
119 linuxdvb_adapter_t *la = ((linuxdvb_ca_t*)in)->lca_adapter;
120 linuxdvb_adapter_changed(la);
121 }
122
123 static void
linuxdvb_ca_class_enabled_notify(void * p,const char * lang)124 linuxdvb_ca_class_enabled_notify ( void *p, const char *lang )
125 {
126 linuxdvb_ca_t *lca = (linuxdvb_ca_t *) p;
127
128 if (lca->lca_enabled) {
129 if (lca->lca_ca_fd < 0) {
130 lca->lca_ca_fd = tvh_open(lca->lca_ca_path, O_RDWR | O_NONBLOCK, 0);
131 tvhtrace(LS_LINUXDVB, "opening ca%u %s (fd %d)",
132 lca->lca_number, lca->lca_ca_path, lca->lca_ca_fd);
133 if (lca->lca_ca_fd >= 0)
134 mtimer_arm_rel(&lca->lca_monitor_timer, linuxdvb_ca_monitor, lca, ms2mono(250));
135 }
136 } else {
137 tvhtrace(LS_LINUXDVB, "closing ca%u %s (fd %d)",
138 lca->lca_number, lca->lca_ca_path, lca->lca_ca_fd);
139
140 mtimer_disarm(&lca->lca_monitor_timer);
141
142 if (lca->lca_en50221_thread_running) {
143 lca->lca_en50221_thread_running = 0;
144 pthread_join(lca->lca_en50221_thread, NULL);
145 }
146
147 if (lca->lca_ca_fd >= 0) {
148 if (ioctl(lca->lca_ca_fd, CA_RESET, NULL))
149 tvherror(LS_LINUXDVB, "unable to reset ca%u %s",
150 lca->lca_number, lca->lca_ca_path);
151
152 close(lca->lca_ca_fd);
153 lca->lca_ca_fd = -1;
154 }
155
156 idnode_notify_title_changed(&lca->lca_id, lang);
157 }
158 }
159
160 static void
linuxdvb_ca_class_high_bitrate_notify(void * p,const char * lang)161 linuxdvb_ca_class_high_bitrate_notify ( void *p, const char *lang )
162 {
163 linuxdvb_ca_t *lca = (linuxdvb_ca_t *) p;
164 ciplus13_app_ai_data_rate_info(lca, lca->lca_high_bitrate_mode ?
165 CIPLUS13_DATA_RATE_96_MBPS :
166 CIPLUS13_DATA_RATE_72_MBPS);
167 }
168
169 static const char *
linuxdvb_ca_class_get_title(idnode_t * in,const char * lang)170 linuxdvb_ca_class_get_title ( idnode_t *in, const char *lang )
171 {
172 linuxdvb_ca_t *lca = (linuxdvb_ca_t *) in;
173 static char buf[256];
174 if (!lca->lca_enabled)
175 snprintf(buf, sizeof(buf), "ca%u: disabled", lca->lca_number);
176 else if (lca->lca_state == CA_SLOT_STATE_EMPTY)
177 snprintf(buf, sizeof(buf), "ca%u: slot empty", lca->lca_number);
178 else
179 snprintf(buf, sizeof(buf), "ca%u: %s (%s)", lca->lca_number,
180 lca->lca_cam_menu_string, lca->lca_state_str);
181
182 return buf;
183 }
184
185 static const void *
linuxdvb_ca_class_active_get(void * obj)186 linuxdvb_ca_class_active_get ( void *obj )
187 {
188 static int active;
189 linuxdvb_ca_t *lca = (linuxdvb_ca_t*)obj;
190 active = !!lca->lca_enabled;
191 return &active;
192 }
193
194 const idclass_t linuxdvb_ca_class =
195 {
196 .ic_class = "linuxdvb_ca",
197 .ic_caption = N_("Linux DVB CA"),
198 .ic_changed = linuxdvb_ca_class_changed,
199 .ic_get_title = linuxdvb_ca_class_get_title,
200 .ic_properties = (const property_t[]) {
201 {
202 .type = PT_BOOL,
203 .id = "active",
204 .name = N_("Active"),
205 .opts = PO_RDONLY | PO_NOSAVE | PO_NOUI,
206 .get = linuxdvb_ca_class_active_get,
207 },
208 {
209 .type = PT_BOOL,
210 .id = "enabled",
211 .name = N_("Enabled"),
212 .desc = N_("Enable/disable the device."),
213 .off = offsetof(linuxdvb_ca_t, lca_enabled),
214 .notify = linuxdvb_ca_class_enabled_notify,
215 },
216 {
217 .type = PT_BOOL,
218 .id = "high_bitrate_mode",
219 .name = N_("High bitrate mode (CI+ CAMs only)"),
220 .desc = N_("Allow high bitrate mode (CI+ CAMs only)."),
221 .off = offsetof(linuxdvb_ca_t, lca_high_bitrate_mode),
222 .notify = linuxdvb_ca_class_high_bitrate_notify,
223 },
224 {
225 .type = PT_BOOL,
226 .id = "pin_reply",
227 .name = N_("Reply to CAM PIN inquiries"),
228 .desc = N_("Reply to PIN inquiries."),
229 .off = offsetof(linuxdvb_ca_t, lca_pin_reply),
230 .opts = PO_ADVANCED,
231 },
232 {
233 .type = PT_STR,
234 .id = "pin",
235 .name = N_("PIN"),
236 .desc = N_("The PIN to use."),
237 .off = offsetof(linuxdvb_ca_t, lca_pin_str),
238 .opts = PO_ADVANCED | PO_PASSWORD,
239 .def.s = "1234",
240 },
241 {
242 .type = PT_STR,
243 .id = "pin_match",
244 .name = N_("PIN inquiry match string"),
245 .desc = N_("PIN inquiry match string."),
246 .off = offsetof(linuxdvb_ca_t, lca_pin_match_str),
247 .opts = PO_ADVANCED,
248 .def.s = "PIN",
249 },
250 {
251 .type = PT_INT,
252 .id = "capmt_interval",
253 .name = N_("CAPMT interval (ms)"),
254 .desc = N_("CAPMT interval (in ms)."),
255 .off = offsetof(linuxdvb_ca_t, lca_capmt_interval),
256 .opts = PO_ADVANCED,
257 .def.i = 100,
258 },
259 {
260 .type = PT_INT,
261 .id = "capmt_query_interval",
262 .name = N_("CAPMT query interval (ms)"),
263 .desc = N_("CAPMT query interval (ms)."),
264 .off = offsetof(linuxdvb_ca_t, lca_capmt_query_interval),
265 .opts = PO_ADVANCED,
266 .def.i = 1200,
267 },
268 {
269 .type = PT_BOOL,
270 .id = "query_before_ok_descrambling",
271 .name = N_("Send CAPMT query"),
272 .desc = N_("Send CAPMT OK query before descrambling."),
273 .off = offsetof(linuxdvb_ca_t, lca_capmt_query),
274 .opts = PO_ADVANCED,
275 },
276 {
277 .type = PT_STR,
278 .id = "ca_path",
279 .name = N_("Device path"),
280 .desc = N_("Path used by the device."),
281 .opts = PO_RDONLY | PO_NOSAVE,
282 .off = offsetof(linuxdvb_ca_t, lca_ca_path),
283 },
284 {
285 .type = PT_STR,
286 .id = "slot_state",
287 .name = N_("Slot state"),
288 .desc = N_("The CAM slot status."),
289 .opts = PO_RDONLY | PO_NOSAVE,
290 .off = offsetof(linuxdvb_ca_t, lca_state_str),
291 },
292 {}
293 }
294 };
295
296 static uint32_t
297 resource_ids[] = { EN50221_APP_RM_RESOURCEID,
298 EN50221_APP_MMI_RESOURCEID,
299 EN50221_APP_DVB_RESOURCEID,
300 EN50221_APP_CA_RESOURCEID,
301 EN50221_APP_DATETIME_RESOURCEID,
302 EN50221_APP_AI_RESOURCEID,
303 TS101699_APP_AI_RESOURCEID,
304 CIPLUS13_APP_AI_RESOURCEID};
305
306 static int
en50221_app_unknown_message(void * arg,uint8_t slot_id,uint16_t session_num,uint32_t resource_id,uint8_t * data,uint32_t data_length)307 en50221_app_unknown_message(void *arg, uint8_t slot_id,
308 uint16_t session_num, uint32_t resource_id,
309 uint8_t *data, uint32_t data_length)
310 {
311 tvhtrace(LS_EN50221, "unknown message slot_id %u, session_num %u, resource_id %x",
312 slot_id, session_num, resource_id);
313 tvhlog_hexdump(LS_EN50221, data, data_length);
314 return 0;
315 }
316
317 static int
linuxdvb_ca_lookup_cb(void * arg,uint8_t slot_id,uint32_t requested_rid,en50221_sl_resource_callback * callback_out,void ** arg_out,uint32_t * connected_rid)318 linuxdvb_ca_lookup_cb (void * arg, uint8_t slot_id, uint32_t requested_rid,
319 en50221_sl_resource_callback *callback_out,
320 void **arg_out, uint32_t *connected_rid)
321 {
322 linuxdvb_ca_t * lca = arg;
323
324 switch (requested_rid) {
325 case EN50221_APP_RM_RESOURCEID:
326 *callback_out = (en50221_sl_resource_callback) en50221_app_rm_message;
327 *arg_out = lca->lca_rm_resource;
328 *connected_rid = EN50221_APP_RM_RESOURCEID;
329 break;
330 case EN50221_APP_AI_RESOURCEID:
331 case TS101699_APP_AI_RESOURCEID:
332 case CIPLUS13_APP_AI_RESOURCEID:
333 *callback_out = (en50221_sl_resource_callback) en50221_app_ai_message;
334 *arg_out = lca->lca_ai_resource;
335 *connected_rid = requested_rid;
336 break;
337 case EN50221_APP_CA_RESOURCEID:
338 *callback_out = (en50221_sl_resource_callback) en50221_app_ca_message;
339 *arg_out = lca->lca_ca_resource;
340 *connected_rid = EN50221_APP_CA_RESOURCEID;
341 break;
342 case EN50221_APP_DATETIME_RESOURCEID:
343 *callback_out = (en50221_sl_resource_callback) en50221_app_datetime_message;
344 *arg_out = lca->lca_dt_resource;
345 *connected_rid = EN50221_APP_DATETIME_RESOURCEID;
346 break;
347 case EN50221_APP_MMI_RESOURCEID:
348 *callback_out = (en50221_sl_resource_callback) en50221_app_mmi_message;
349 *arg_out = lca->lca_mmi_resource;
350 *connected_rid = EN50221_APP_MMI_RESOURCEID;
351 break;
352 default:
353 tvhtrace(LS_EN50221, "lookup cb for unknown resource id %x on slot %u",
354 requested_rid, slot_id);
355 *callback_out = (en50221_sl_resource_callback) en50221_app_unknown_message;
356 *arg_out = lca;
357 *connected_rid = requested_rid;
358 }
359 return 0;
360 }
361
362 static int
linuxdvb_ca_session_cb(void * arg,int reason,uint8_t slot_id,uint16_t session_num,uint32_t rid)363 linuxdvb_ca_session_cb (void *arg, int reason, uint8_t slot_id,
364 uint16_t session_num, uint32_t rid)
365 {
366 linuxdvb_ca_t * lca = arg;
367
368 if (reason == S_SCALLBACK_REASON_CAMCONNECTING) {
369 tvhtrace(LS_EN50221, "0x%08x session %u connecting", rid, session_num);
370 return 0;
371 }
372
373 if (reason == S_SCALLBACK_REASON_CAMCONNECTED) {
374 tvhtrace(LS_EN50221, "0x%08x session %u connected", rid, session_num);
375 switch(rid) {
376 case EN50221_APP_RM_RESOURCEID:
377 en50221_app_rm_enq(lca->lca_rm_resource, session_num);
378 break;
379 case EN50221_APP_AI_RESOURCEID:
380 case TS101699_APP_AI_RESOURCEID:
381 case CIPLUS13_APP_AI_RESOURCEID:
382 lca->lca_ai_version = rid & 0x3f;
383 lca->lca_ai_session_number = session_num;
384 en50221_app_ai_enquiry(lca->lca_ai_resource, session_num);
385 ciplus13_app_ai_data_rate_info(lca, lca->lca_high_bitrate_mode ?
386 CIPLUS13_DATA_RATE_96_MBPS :
387 CIPLUS13_DATA_RATE_72_MBPS );
388 break;
389 case EN50221_APP_CA_RESOURCEID:
390 en50221_app_ca_info_enq(lca->lca_ca_resource, session_num);
391 lca->lca_ca_session_number = session_num;
392 break;
393 case EN50221_APP_MMI_RESOURCEID:
394 case EN50221_APP_DATETIME_RESOURCEID:
395 break;
396 default:
397 tvhtrace(LS_EN50221, "session %u with unknown rid 0x%08x is connected",
398 session_num, rid);
399 }
400 return 0;
401 }
402
403 if (reason == S_SCALLBACK_REASON_CLOSE) {
404 tvhtrace(LS_EN50221, "0x%08x session %u close", rid, session_num);
405 switch(rid) {
406 case EN50221_APP_CA_RESOURCEID:
407 dvbcam_unregister_cam(lca, 0);
408 lca->lca_cam_menu_string[0] = 0;
409 }
410 return 0;
411 }
412
413 if (reason == S_SCALLBACK_REASON_TC_CONNECT) {
414 tvhtrace(LS_EN50221, "0x%08x session %u tc connect", rid, session_num);
415 return 0;
416 }
417
418 tvhtrace(LS_EN50221, "unhandled session callback - reason %d slot_id %u "
419 "session_num %u resource_id %x", reason, slot_id, session_num, rid);
420 return 0;
421 }
422
423 static int
linuxdvb_ca_rm_enq_cb(void * arg,uint8_t slot_id,uint16_t session_num)424 linuxdvb_ca_rm_enq_cb(void *arg, uint8_t slot_id, uint16_t session_num)
425 {
426 linuxdvb_ca_t * lca = arg;
427
428 tvhtrace(LS_EN50221, "rm enq callback received for slot %d", slot_id);
429
430 if (en50221_app_rm_reply(lca->lca_rm_resource, session_num,
431 sizeof(resource_ids)/4, resource_ids))
432 {
433 tvherror(LS_EN50221, "failed to send rm reply to slot %u session %u",
434 slot_id, session_num);
435 }
436
437 return 0;
438 }
439
440 static int
linuxdvb_ca_rm_reply_cb(void * arg,uint8_t slot_id,uint16_t session_num,uint32_t resource_id_count,uint32_t * _resource_ids)441 linuxdvb_ca_rm_reply_cb(void *arg, uint8_t slot_id, uint16_t session_num,
442 uint32_t resource_id_count, uint32_t *_resource_ids)
443 {
444 linuxdvb_ca_t * lca = arg;
445
446 tvhtrace(LS_EN50221, "rm reply cb received for slot %d, count %u", slot_id,
447 resource_id_count);
448
449 uint32_t i;
450 for(i=0; i< resource_id_count; i++) {
451 tvhtrace(LS_EN50221, "CAM provided resource id: %08x", _resource_ids[i]);
452 }
453
454 if (en50221_app_rm_changed(lca->lca_rm_resource, session_num)) {
455 tvherror(LS_EN50221, "failed to send RM reply on slot %u", slot_id);
456 }
457
458 return 0;
459 }
460
461 static int
linuxdvb_ca_rm_changed_cb(void * arg,uint8_t slot_id,uint16_t session_num)462 linuxdvb_ca_rm_changed_cb(void *arg, uint8_t slot_id,
463 uint16_t session_num)
464 {
465 linuxdvb_ca_t * lca = arg;
466 tvhtrace(LS_EN50221, "rm changed cb received for slot %d", slot_id);
467
468 if (en50221_app_rm_enq(lca->lca_rm_resource, session_num)) {
469 tvherror(LS_EN50221, "failed to send ENQ to slot %d", slot_id);
470 }
471
472 return 0;
473 }
474
475 static int
linuxdvb_ca_dt_enquiry_cb(void * arg,uint8_t slot_id,uint16_t session_num,uint8_t response_int)476 linuxdvb_ca_dt_enquiry_cb(void *arg, uint8_t slot_id, uint16_t session_num,
477 uint8_t response_int)
478 {
479 linuxdvb_ca_t * lca = arg;
480 tvhtrace(LS_EN50221, "datetime enquiry cb received for slot %d", slot_id);
481
482 if (en50221_app_datetime_send(lca->lca_dt_resource, session_num, time(NULL), -1)) {
483 tvherror(LS_EN50221, "Failed to send datetime to slot %d", slot_id);
484 }
485
486 return 0;
487 }
488
489 static int
linuxdvb_ca_ai_callback(void * arg,uint8_t slot_id,uint16_t session_num,uint8_t app_type,uint16_t app_manufacturer,uint16_t manufacturer_code,uint8_t menu_string_len,uint8_t * menu_string)490 linuxdvb_ca_ai_callback(void *arg, uint8_t slot_id, uint16_t session_num,
491 uint8_t app_type, uint16_t app_manufacturer,
492 uint16_t manufacturer_code, uint8_t menu_string_len,
493 uint8_t *menu_string)
494 {
495 linuxdvb_ca_t * lca = arg;
496
497 tvhinfo(LS_EN50221, "CAM slot %u: Application type: %02x, manufacturer: %04x,"
498 " Manufacturer code: %04x", slot_id, app_type, app_manufacturer,
499 manufacturer_code);
500
501 tvhinfo(LS_EN50221, "CAM slot %u: Menu string: %.*s", slot_id,
502 menu_string_len, menu_string);
503
504 snprintf(lca->lca_cam_menu_string, sizeof(lca->lca_cam_menu_string),
505 "%.*s", menu_string_len, menu_string);
506
507 idnode_notify_title_changed(&lca->lca_id, NULL);
508
509 return 0;
510 }
511
512 static int
linuxdvb_ca_ca_info_callback(void * arg,uint8_t slot_id,uint16_t session_num,uint32_t ca_id_count,uint16_t * ca_ids)513 linuxdvb_ca_ca_info_callback(void *arg, uint8_t slot_id, uint16_t session_num,
514 uint32_t ca_id_count, uint16_t *ca_ids)
515 {
516 linuxdvb_ca_t * lca = arg;
517 uint32_t i, j;
518 char buf[256];
519 size_t c = 0;
520
521 dvbcam_unregister_cam(lca, 0);
522 dvbcam_register_cam(lca, 0, ca_ids, ca_id_count);
523
524 for (i = 0; i < ca_id_count; ) {
525 for (j = 0, buf[0] = '\0'; j < 4 && i < ca_id_count; i++, j++)
526 tvh_strlcatf(buf, sizeof(buf), c, " %04X (%s)",
527 ca_ids[i], caid2name(ca_ids[i]));
528 tvhinfo(LS_EN50221, "CAM slot %u supported CAIDs: %s", slot_id, buf);
529 }
530
531 return 0;
532 }
533
534 static int
linuxdvb_ca_ca_pmt_reply_cb(void * arg,uint8_t slot_id,uint16_t session_num,struct en50221_app_pmt_reply * reply,uint32_t reply_size)535 linuxdvb_ca_ca_pmt_reply_cb(void *arg, uint8_t slot_id, uint16_t session_num,
536 struct en50221_app_pmt_reply *reply,
537 uint32_t reply_size)
538 {
539 const char *str;
540
541 switch (reply->CA_enable) {
542 case 0x01: str = "possible"; break;
543 case 0x02: str = "possible under conditions (purchase dialogue)"; break;
544 case 0x03: str = "possible under conditions (technical dialogue)"; break;
545 case 0x71: str = "not possible (because no entitlement)"; break;
546 case 0x73: str = "not possible (for technical reasons)"; break;
547 default: str = "state unknown (unknown value received)";
548 }
549
550 tvhinfo(LS_EN50221, "CAM slot %u: descrambling %s", slot_id, str);
551
552 return 0;
553 }
554
555 static int
linuxdvb_ca_mmi_close_cb(void * arg,uint8_t slot_id,uint16_t session_num,uint8_t cmd_id,uint8_t delay)556 linuxdvb_ca_mmi_close_cb(void *arg, uint8_t slot_id, uint16_t session_num,
557 uint8_t cmd_id, uint8_t delay)
558 {
559 tvhtrace(LS_EN50221, "mmi close cb received for slot %u session_num %u "
560 "cmd_id 0x%02x delay %u" , slot_id, session_num, cmd_id, delay);
561
562 return 0;
563 }
564
565 static int
linuxdvb_ca_mmi_display_ctl_cb(void * arg,uint8_t slot_id,uint16_t session_num,uint8_t cmd_id,uint8_t mmi_mode)566 linuxdvb_ca_mmi_display_ctl_cb(void *arg, uint8_t slot_id, uint16_t session_num,
567 uint8_t cmd_id, uint8_t mmi_mode)
568 {
569 linuxdvb_ca_t * lca = arg;
570
571 tvhtrace(LS_EN50221, "mmi display ctl cb received for slot %u session_num %u "
572 "cmd_id 0x%02x mmi_mode %u" , slot_id, session_num, cmd_id, mmi_mode);
573
574 if (cmd_id == MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) {
575 struct en50221_app_mmi_display_reply_details d;
576
577 d.u.mode_ack.mmi_mode = mmi_mode;
578 if (en50221_app_mmi_display_reply(lca->lca_mmi_resource, session_num,
579 MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK, &d)) {
580 tvherror(LS_EN50221,"Slot %u: Failed to send MMI mode ack reply", slot_id);
581 }
582 }
583
584 return 0;
585 }
586
587 static int
linuxdvb_ca_mmi_enq_cb(void * arg,uint8_t slot_id,uint16_t session_num,uint8_t blind_answ,uint8_t exp_answ_len,uint8_t * text,uint32_t text_size)588 linuxdvb_ca_mmi_enq_cb(void *arg, uint8_t slot_id, uint16_t session_num,
589 uint8_t blind_answ, uint8_t exp_answ_len,
590 uint8_t *text, uint32_t text_size)
591 {
592 linuxdvb_ca_t * lca = arg;
593 char buffer[256];
594
595 snprintf(buffer, sizeof(buffer), "%.*s", text_size, text);
596
597 tvhnotice(LS_EN50221, "MMI enquiry from CAM in slot %u: %s (%s%u digits)",
598 slot_id, buffer, blind_answ ? "blind " : "" , exp_answ_len);
599
600 if (lca->lca_pin_reply &&
601 (strlen((char *) lca->lca_pin_str) == exp_answ_len) &&
602 strstr((char *) buffer, lca->lca_pin_match_str))
603 {
604 tvhtrace(LS_EN50221, "answering to PIN enquiry");
605 en50221_app_mmi_answ(lca->lca_mmi_resource, session_num,
606 MMI_ANSW_ID_ANSWER, (uint8_t *) lca->lca_pin_str,
607 exp_answ_len);
608 }
609
610 en50221_app_mmi_close(lca->lca_mmi_resource, session_num,
611 MMI_CLOSE_MMI_CMD_ID_IMMEDIATE, 0);
612
613 return 0;
614 }
615
616 static int
linuxdvb_ca_mmi_menu_cb(void * arg,uint8_t slot_id,uint16_t session_num,struct en50221_app_mmi_text * title,struct en50221_app_mmi_text * sub_title,struct en50221_app_mmi_text * bottom,uint32_t item_count,struct en50221_app_mmi_text * items,uint32_t item_raw_length,uint8_t * items_raw)617 linuxdvb_ca_mmi_menu_cb(void *arg, uint8_t slot_id, uint16_t session_num,
618 struct en50221_app_mmi_text *title,
619 struct en50221_app_mmi_text *sub_title,
620 struct en50221_app_mmi_text *bottom,
621 uint32_t item_count, struct en50221_app_mmi_text *items,
622 uint32_t item_raw_length, uint8_t *items_raw)
623 {
624 linuxdvb_ca_t * lca = arg;
625
626 tvhnotice(LS_EN50221, "MMI menu from CAM in the slot %u:", slot_id);
627 tvhnotice(LS_EN50221, " title: %.*s", title->text_length, title->text);
628 tvhnotice(LS_EN50221, " subtitle: %.*s", sub_title->text_length, sub_title->text);
629
630 uint32_t i;
631 for(i=0; i< item_count; i++) {
632 tvhnotice(LS_EN50221, " item %i: %.*s", i+1, items[i].text_length, items[i].text);
633 }
634 tvhnotice(LS_EN50221, " bottom: %.*s", bottom->text_length, bottom->text);
635
636 /* cancel menu */
637 en50221_app_mmi_close(lca->lca_mmi_resource, session_num,
638 MMI_CLOSE_MMI_CMD_ID_IMMEDIATE, 0);
639 return 0;
640 }
641
642 static int
linuxdvb_ca_app_mmi_list_cb(void * arg,uint8_t slot_id,uint16_t session_num,struct en50221_app_mmi_text * title,struct en50221_app_mmi_text * sub_title,struct en50221_app_mmi_text * bottom,uint32_t item_count,struct en50221_app_mmi_text * items,uint32_t item_raw_length,uint8_t * items_raw)643 linuxdvb_ca_app_mmi_list_cb(void *arg, uint8_t slot_id, uint16_t session_num,
644 struct en50221_app_mmi_text *title,
645 struct en50221_app_mmi_text *sub_title,
646 struct en50221_app_mmi_text *bottom,
647 uint32_t item_count, struct en50221_app_mmi_text *items,
648 uint32_t item_raw_length, uint8_t *items_raw)
649 {
650 linuxdvb_ca_t * lca = arg;
651
652 tvhnotice(LS_EN50221, "MMI list from CAM in the slot %u:", slot_id);
653 tvhnotice(LS_EN50221, " title: %.*s", title->text_length, title->text);
654 tvhnotice(LS_EN50221, " subtitle: %.*s", sub_title->text_length, sub_title->text);
655
656 uint32_t i;
657 for(i=0; i< item_count; i++) {
658 tvhnotice(LS_EN50221, " item %i: %.*s", i+1, items[i].text_length, items[i].text);
659 }
660 tvhnotice(LS_EN50221, " bottom: %.*s", bottom->text_length, bottom->text);
661
662 /* cancel menu */
663 en50221_app_mmi_close(lca->lca_mmi_resource, session_num,
664 MMI_CLOSE_MMI_CMD_ID_IMMEDIATE, 0);
665 return 0;
666 }
667
668 static void *
linuxdvb_ca_en50221_thread(void * aux)669 linuxdvb_ca_en50221_thread ( void *aux )
670 {
671 linuxdvb_ca_t *lca = aux;
672 int slot_id, lasterror = 0;
673
674 lca->lca_tl = en50221_tl_create(5, 32);
675 if (!lca->lca_tl) {
676 tvherror(LS_EN50221, "failed to create transport layer");
677 return NULL;
678 }
679
680 slot_id = en50221_tl_register_slot(lca->lca_tl, lca->lca_ca_fd, 0, 1000, 100);
681 if (slot_id < 0) {
682 tvherror(LS_EN50221, "slot registration failed");
683 return NULL;
684 }
685
686 lca->lca_sl = en50221_sl_create(lca->lca_tl, 256);
687 if (lca->lca_sl == NULL) {
688 tvherror(LS_EN50221, "failed to create session layer");
689 return NULL;
690 }
691
692 // create the sendfuncs
693 lca->lca_sf.arg = lca->lca_sl;
694 lca->lca_sf.send_data = (en50221_send_data) en50221_sl_send_data;
695 lca->lca_sf.send_datav = (en50221_send_datav) en50221_sl_send_datav;
696
697 /* create app resources and assign callbacks */
698 lca->lca_rm_resource = en50221_app_rm_create(&lca->lca_sf);
699 en50221_app_rm_register_enq_callback(lca->lca_rm_resource, linuxdvb_ca_rm_enq_cb, lca);
700 en50221_app_rm_register_reply_callback(lca->lca_rm_resource, linuxdvb_ca_rm_reply_cb, lca);
701 en50221_app_rm_register_changed_callback(lca->lca_rm_resource, linuxdvb_ca_rm_changed_cb, lca);
702
703 lca->lca_dt_resource = en50221_app_datetime_create(&lca->lca_sf);
704 en50221_app_datetime_register_enquiry_callback(lca->lca_dt_resource, linuxdvb_ca_dt_enquiry_cb, lca);
705
706 lca->lca_ai_resource = en50221_app_ai_create(&lca->lca_sf);
707 en50221_app_ai_register_callback(lca->lca_ai_resource, linuxdvb_ca_ai_callback, lca);
708
709 lca->lca_ca_resource = en50221_app_ca_create(&lca->lca_sf);
710 en50221_app_ca_register_info_callback(lca->lca_ca_resource, linuxdvb_ca_ca_info_callback, lca);
711 en50221_app_ca_register_pmt_reply_callback(lca->lca_ca_resource, linuxdvb_ca_ca_pmt_reply_cb, lca);
712
713 lca->lca_mmi_resource = en50221_app_mmi_create(&lca->lca_sf);
714 en50221_app_mmi_register_close_callback(lca->lca_mmi_resource, linuxdvb_ca_mmi_close_cb, lca);
715 en50221_app_mmi_register_display_control_callback(lca->lca_mmi_resource, linuxdvb_ca_mmi_display_ctl_cb, lca);
716 en50221_app_mmi_register_enq_callback(lca->lca_mmi_resource, linuxdvb_ca_mmi_enq_cb, lca);
717 en50221_app_mmi_register_menu_callback(lca->lca_mmi_resource, linuxdvb_ca_mmi_menu_cb, lca);
718 en50221_app_mmi_register_list_callback(lca->lca_mmi_resource, linuxdvb_ca_app_mmi_list_cb, lca);
719
720 en50221_sl_register_lookup_callback(lca->lca_sl, linuxdvb_ca_lookup_cb, lca);
721 en50221_sl_register_session_callback(lca->lca_sl, linuxdvb_ca_session_cb, lca);
722
723 lca->lca_tc = en50221_tl_new_tc(lca->lca_tl, slot_id);
724
725 while (tvheadend_is_running() && lca->lca_en50221_thread_running) {
726 int error;
727 if ((error = en50221_tl_poll(lca->lca_tl)) != 0) {
728 if (error != lasterror) {
729 tvherror(LS_EN50221, "poll error on slot %d [error:%i]",
730 en50221_tl_get_error_slot(lca->lca_tl),
731 en50221_tl_get_error(lca->lca_tl));
732 }
733 lasterror = error;
734 }
735 }
736
737 dvbcam_unregister_cam(lca, 0);
738
739 en50221_tl_destroy_slot(lca->lca_tl, slot_id);
740 en50221_sl_destroy(lca->lca_sl);
741 en50221_tl_destroy(lca->lca_tl);
742 return 0;
743 }
744
745 static void
linuxdvb_ca_monitor(void * aux)746 linuxdvb_ca_monitor ( void *aux )
747 {
748 linuxdvb_ca_t *lca = aux;
749 ca_slot_info_t csi;
750 int state;
751
752 if (lca->lca_ca_fd < 0)
753 return;
754
755 memset(&csi, 0, sizeof(csi));
756
757 if ((ioctl(lca->lca_ca_fd, CA_GET_SLOT_INFO, &csi)) != 0) {
758 tvherror(LS_LINUXDVB, "failed to get CAM slot %u info [e=%s]",
759 csi.num, strerror(errno));
760 }
761 if (csi.flags & CA_CI_MODULE_READY)
762 state = CA_SLOT_STATE_MODULE_READY;
763 else if (csi.flags & CA_CI_MODULE_PRESENT)
764 state = CA_SLOT_STATE_MODULE_PRESENT;
765 else
766 state = CA_SLOT_STATE_EMPTY;
767
768 lca->lca_state_str = ca_slot_state2str(state);
769
770 if (lca->lca_state != state) {
771 tvhnotice(LS_LINUXDVB, "CAM slot %u status changed to %s",
772 csi.num, lca->lca_state_str);
773 idnode_notify_title_changed(&lca->lca_id, NULL);
774 lca->lca_state = state;
775 }
776
777 if ((!lca->lca_en50221_thread_running) &&
778 (state == CA_SLOT_STATE_MODULE_READY)) {
779 lca->lca_en50221_thread_running = 1;
780 tvhthread_create(&lca->lca_en50221_thread, NULL,
781 linuxdvb_ca_en50221_thread, lca, "lnxdvb-ca");
782 } else if (lca->lca_en50221_thread_running &&
783 (state != CA_SLOT_STATE_MODULE_READY)) {
784 lca->lca_en50221_thread_running = 0;
785 pthread_join(lca->lca_en50221_thread, NULL);
786 }
787
788 mtimer_arm_rel(&lca->lca_monitor_timer, linuxdvb_ca_monitor, lca, ms2mono(250));
789 }
790
791 linuxdvb_ca_t *
linuxdvb_ca_create(htsmsg_t * conf,linuxdvb_adapter_t * la,int number,const char * ca_path)792 linuxdvb_ca_create
793 ( htsmsg_t *conf, linuxdvb_adapter_t *la, int number, const char *ca_path)
794 {
795 linuxdvb_ca_t *lca;
796 char id[6];
797 const char *uuid = NULL;
798
799 lca = calloc(1, sizeof(linuxdvb_ca_t));
800 memset(lca, 0, sizeof(linuxdvb_ca_t));
801 lca->lca_number = number;
802 lca->lca_ca_path = strdup(ca_path);
803 lca->lca_ca_fd = -1;
804 lca->lca_capmt_interval = 100;
805 lca->lca_capmt_query_interval = 1200;
806
807 /* Internal config ID */
808 snprintf(id, sizeof(id), "ca%u", number);
809
810 if (conf)
811 conf = htsmsg_get_map(conf, id);
812 if (conf)
813 uuid = htsmsg_get_str(conf, "uuid");
814
815 if (idnode_insert(&lca->lca_id, uuid, &linuxdvb_ca_class, 0)) {
816 free(lca);
817 return NULL;
818 }
819
820 if (conf)
821 idnode_load(&lca->lca_id, conf);
822
823 /* Adapter link */
824 lca->lca_adapter = la;
825 LIST_INSERT_HEAD(&la->la_ca_devices, lca, lca_link);
826
827 TAILQ_INIT(&lca->lca_capmt_queue);
828
829 return lca;
830 }
831
832 static void
linuxdvb_ca_process_capmt_queue(void * aux)833 linuxdvb_ca_process_capmt_queue ( void *aux )
834 {
835 linuxdvb_ca_t *lca = aux;
836 linuxdvb_ca_capmt_t *lcc;
837 struct section *section;
838 struct section_ext *result;
839 struct mpeg_pmt_section *pmt;
840 uint8_t capmt[4096];
841 int size, i;
842
843 lcc = TAILQ_FIRST(&lca->lca_capmt_queue);
844
845 if (!lcc)
846 return;
847
848 if (!(section = section_codec(lcc->data, lcc->len))){
849 tvherror(LS_EN50221, "failed to decode PMT section");
850 goto done;
851 }
852
853 if (!(result = section_ext_decode(section, 0))){
854 tvherror(LS_EN50221, "failed to decode PMT ext_section");
855 goto done;
856 }
857
858 if (!(pmt = mpeg_pmt_section_codec(result))){
859 tvherror(LS_EN50221, "failed to decode PMT");
860 goto done;
861 }
862
863 size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), 0,
864 lcc->list_mgmt, lcc->cmd_id);
865
866 if (size < 0) {
867 tvherror(LS_EN50221, "Failed to format CAPMT");
868 }
869
870 if (en50221_app_ca_pmt(lca->lca_ca_resource, lca->lca_ca_session_number,
871 capmt, size)) {
872 tvherror(LS_EN50221, "Failed to send CAPMT");
873 }
874
875 tvhtrace(LS_EN50221, "%s CAPMT sent (%s)", ca_pmt_cmd_id2str(lcc->cmd_id),
876 ca_pmt_list_mgmt2str(lcc->list_mgmt));
877 tvhlog_hexdump(LS_EN50221, capmt, size);
878
879 done:
880 i = (lcc->cmd_id == CA_PMT_CMD_ID_QUERY) ?
881 lca->lca_capmt_query_interval : lca->lca_capmt_interval;
882
883 TAILQ_REMOVE(&lca->lca_capmt_queue, lcc, lcc_link);
884
885 free(lcc->data);
886 free(lcc);
887
888 if (!TAILQ_EMPTY(&lca->lca_capmt_queue)) {
889 mtimer_arm_rel(&lca->lca_capmt_queue_timer,
890 linuxdvb_ca_process_capmt_queue, lca, ms2mono(i));
891 }
892 }
893
894 void
linuxdvb_ca_enqueue_capmt(linuxdvb_ca_t * lca,uint8_t slot,const uint8_t * ptr,int len,uint8_t list_mgmt,uint8_t cmd_id)895 linuxdvb_ca_enqueue_capmt(linuxdvb_ca_t *lca, uint8_t slot, const uint8_t *ptr,
896 int len, uint8_t list_mgmt, uint8_t cmd_id)
897 {
898 linuxdvb_ca_capmt_t *lcc;
899 int c = 1;
900
901 if (!lca)
902 return;
903
904 if (lca->lca_capmt_query && cmd_id == CA_PMT_CMD_ID_OK_DESCRAMBLING)
905 c = 2;
906
907 while (c--) {
908 lcc = calloc(1, sizeof(*lcc));
909
910 if (!lcc)
911 return;
912
913 lcc->data = malloc(len);
914 lcc->len = len;
915 lcc->slot = slot;
916 lcc->list_mgmt = list_mgmt;
917 lcc->cmd_id = (c ? CA_PMT_CMD_ID_QUERY : cmd_id);
918 memcpy(lcc->data, ptr, len);
919
920 TAILQ_INSERT_TAIL(&lca->lca_capmt_queue, lcc, lcc_link);
921
922 tvhtrace(LS_EN50221, "%s CAPMT enqueued (%s)", ca_pmt_cmd_id2str(lcc->cmd_id),
923 ca_pmt_list_mgmt2str(lcc->list_mgmt));
924 }
925
926 mtimer_arm_rel(&lca->lca_capmt_queue_timer,
927 linuxdvb_ca_process_capmt_queue, lca, ms2mono(50));
928 }
929
linuxdvb_ca_save(linuxdvb_ca_t * lca,htsmsg_t * msg)930 void linuxdvb_ca_save( linuxdvb_ca_t *lca, htsmsg_t *msg )
931 {
932 char id[8], ubuf[UUID_HEX_SIZE];
933 htsmsg_t *m = htsmsg_create_map();
934
935 htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&lca->lca_id, ubuf));
936 idnode_save(&lca->lca_id, m);
937
938 /* Add to list */
939 snprintf(id, sizeof(id), "ca%u", lca->lca_number);
940 htsmsg_add_msg(msg, id, m);
941
942 }
943