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