aboutsummaryrefslogtreecommitdiffstats
path: root/util/gnutv/gnutv_ca.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/gnutv/gnutv_ca.c')
-rw-r--r--util/gnutv/gnutv_ca.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/util/gnutv/gnutv_ca.c b/util/gnutv/gnutv_ca.c
new file mode 100644
index 0000000..5830f21
--- /dev/null
+++ b/util/gnutv/gnutv_ca.c
@@ -0,0 +1,404 @@
+/*
+ gnutv utility
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <libdvben50221/en50221_stdcam.h>
+#include "gnutv.h"
+#include "gnutv_ca.h"
+
+
+
+#define MMI_STATE_CLOSED 0
+#define MMI_STATE_OPEN 1
+#define MMI_STATE_ENQ 2
+#define MMI_STATE_MENU 3
+
+static int gnutv_ca_info_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint32_t ca_id_count, uint16_t *ca_ids);
+static int gnutv_ai_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t application_type, uint16_t application_manufacturer,
+ uint16_t manufacturer_code, uint8_t menu_string_length,
+ uint8_t *menu_string);
+
+static int gnutv_mmi_close_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t delay);
+static int gnutv_mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t mmi_mode);
+static int gnutv_mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t blind_answer, uint8_t expected_answer_length,
+ uint8_t *text, uint32_t text_size);
+static int gnutv_mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ 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);
+static void *camthread_func(void* arg);
+
+static struct en50221_transport_layer *tl = NULL;
+static struct en50221_session_layer *sl = NULL;
+static struct en50221_stdcam *stdcam = NULL;
+
+static int ca_resource_connected = 0;
+static int mmi_state = MMI_STATE_CLOSED;
+static int mmi_enq_blind;
+static int mmi_enq_length;
+
+static int camthread_shutdown = 0;
+static pthread_t camthread;
+int moveca = 0;
+int seenpmt = 0;
+int cammenu = 0;
+
+char ui_line[256];
+uint32_t ui_linepos = 0;
+
+
+void gnutv_ca_start(struct gnutv_ca_params *params)
+{
+ // create transport layer
+ tl = en50221_tl_create(1, 16);
+ if (tl == NULL) {
+ fprintf(stderr, "Failed to create transport layer\n");
+ return;
+ }
+
+ // create session layer
+ sl = en50221_sl_create(tl, 16);
+ if (sl == NULL) {
+ fprintf(stderr, "Failed to create session layer\n");
+ en50221_tl_destroy(tl);
+ return;
+ }
+
+ // create the stdcam instance
+ stdcam = en50221_stdcam_create(params->adapter_id, params->caslot_num, tl, sl);
+ if (stdcam == NULL) {
+ en50221_sl_destroy(sl);
+ en50221_tl_destroy(tl);
+ return;
+ }
+
+ // hook up the AI callbacks
+ if (stdcam->ai_resource) {
+ en50221_app_ai_register_callback(stdcam->ai_resource, gnutv_ai_callback, stdcam);
+ }
+
+ // hook up the CA callbacks
+ if (stdcam->ca_resource) {
+ en50221_app_ca_register_info_callback(stdcam->ca_resource, gnutv_ca_info_callback, stdcam);
+ }
+
+ // hook up the MMI callbacks
+ if (params->cammenu) {
+ if (stdcam->mmi_resource) {
+ en50221_app_mmi_register_close_callback(stdcam->mmi_resource, gnutv_mmi_close_callback, stdcam);
+ en50221_app_mmi_register_display_control_callback(stdcam->mmi_resource, gnutv_mmi_display_control_callback, stdcam);
+ en50221_app_mmi_register_enq_callback(stdcam->mmi_resource, gnutv_mmi_enq_callback, stdcam);
+ en50221_app_mmi_register_menu_callback(stdcam->mmi_resource, gnutv_mmi_menu_callback, stdcam);
+ en50221_app_mmi_register_list_callback(stdcam->mmi_resource, gnutv_mmi_menu_callback, stdcam);
+ } else {
+ fprintf(stderr, "CAM Menus are not supported by this interface hardware\n");
+ exit(1);
+ }
+ }
+
+ // any other stuff
+ moveca = params->moveca;
+ cammenu = params->cammenu;
+
+ // start the cam thread
+ pthread_create(&camthread, NULL, camthread_func, NULL);
+}
+
+void gnutv_ca_stop(void)
+{
+ if (stdcam == NULL)
+ return;
+
+ // shutdown the cam thread
+ camthread_shutdown = 1;
+ pthread_join(camthread, NULL);
+
+ // destroy the stdcam
+ if (stdcam->destroy)
+ stdcam->destroy(stdcam, 1);
+
+ // destroy session layer
+ en50221_sl_destroy(sl);
+
+ // destroy transport layer
+ en50221_tl_destroy(tl);
+}
+
+void gnutv_ca_ui(void)
+{
+ // make up polling structure for stdin
+ struct pollfd pollfd;
+ pollfd.fd = 0;
+ pollfd.events = POLLIN|POLLPRI|POLLERR;
+
+ if (stdcam == NULL)
+ return;
+
+ // is there a character?
+ if (poll(&pollfd, 1, 10) != 1)
+ return;
+ if (pollfd.revents & POLLERR)
+ return;
+
+ // try to read the character
+ char c;
+ if (read(0, &c, 1) != 1)
+ return;
+ if (c == '\r') {
+ return;
+ } else if (c == '\n') {
+ switch(mmi_state) {
+ case MMI_STATE_CLOSED:
+ case MMI_STATE_OPEN:
+ if ((ui_linepos == 0) && (ca_resource_connected)) {
+ en50221_app_ai_entermenu(stdcam->ai_resource, stdcam->ai_session_number);
+ }
+ break;
+
+ case MMI_STATE_ENQ:
+ if (ui_linepos == 0) {
+ en50221_app_mmi_answ(stdcam->mmi_resource, stdcam->mmi_session_number,
+ MMI_ANSW_ID_CANCEL, NULL, 0);
+ } else {
+ en50221_app_mmi_answ(stdcam->mmi_resource, stdcam->mmi_session_number,
+ MMI_ANSW_ID_ANSWER, (uint8_t*) ui_line, ui_linepos);
+ }
+ mmi_state = MMI_STATE_OPEN;
+ break;
+
+ case MMI_STATE_MENU:
+ ui_line[ui_linepos] = 0;
+ en50221_app_mmi_menu_answ(stdcam->mmi_resource, stdcam->mmi_session_number,
+ atoi(ui_line));
+ mmi_state = MMI_STATE_OPEN;
+ break;
+ }
+ ui_linepos = 0;
+ } else {
+ if (ui_linepos < (sizeof(ui_line)-1)) {
+ ui_line[ui_linepos++] = c;
+ }
+ }
+}
+
+int gnutv_ca_new_pmt(struct mpeg_pmt_section *pmt)
+{
+ uint8_t capmt[4096];
+ int size;
+
+ if (stdcam == NULL)
+ return -1;
+
+ if (ca_resource_connected) {
+ fprintf(stderr, "Received new PMT - sending to CAM...\n");
+
+ // translate it into a CA PMT
+ int listmgmt = CA_LIST_MANAGEMENT_ONLY;
+ if (seenpmt) {
+ listmgmt = CA_LIST_MANAGEMENT_UPDATE;
+ }
+ seenpmt = 1;
+
+ if ((size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), moveca, listmgmt,
+ CA_PMT_CMD_ID_OK_DESCRAMBLING)) < 0) {
+ fprintf(stderr, "Failed to format PMT\n");
+ return -1;
+ }
+
+ // set it
+ if (en50221_app_ca_pmt(stdcam->ca_resource, stdcam->ca_session_number, capmt, size)) {
+ fprintf(stderr, "Failed to send PMT\n");
+ return -1;
+ }
+
+ // we've seen this PMT
+ return 1;
+ }
+
+ return 0;
+}
+
+void gnutv_ca_new_dvbtime(time_t dvb_time)
+{
+ if (stdcam == NULL)
+ return;
+
+ if (stdcam->dvbtime)
+ stdcam->dvbtime(stdcam, dvb_time);
+}
+
+static void *camthread_func(void* arg)
+{
+ (void) arg;
+ int entered_menu = 0;
+
+ while(!camthread_shutdown) {
+ stdcam->poll(stdcam);
+
+ if ((!entered_menu) && cammenu && ca_resource_connected && stdcam->mmi_resource) {
+ en50221_app_ai_entermenu(stdcam->ai_resource, stdcam->ai_session_number);
+ entered_menu = 1;
+ }
+ }
+
+ return 0;
+}
+
+static int gnutv_ai_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t application_type, uint16_t application_manufacturer,
+ uint16_t manufacturer_code, uint8_t menu_string_length,
+ uint8_t *menu_string)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+
+ fprintf(stderr, "CAM Application type: %02x\n", application_type);
+ fprintf(stderr, "CAM Application manufacturer: %04x\n", application_manufacturer);
+ fprintf(stderr, "CAM Manufacturer code: %04x\n", manufacturer_code);
+ fprintf(stderr, "CAM Menu string: %.*s\n", menu_string_length, menu_string);
+
+ return 0;
+}
+
+static int gnutv_ca_info_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint32_t ca_id_count, uint16_t *ca_ids)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+
+ fprintf(stderr, "CAM supports the following ca system ids:\n");
+ uint32_t i;
+ for(i=0; i< ca_id_count; i++) {
+ fprintf(stderr, " 0x%04x\n", ca_ids[i]);
+ }
+ ca_resource_connected = 1;
+ return 0;
+}
+
+static int gnutv_mmi_close_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t delay)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+ (void) cmd_id;
+ (void) delay;
+
+ // note: not entirely correct as its supposed to delay if asked
+ mmi_state = MMI_STATE_CLOSED;
+ return 0;
+}
+
+static int gnutv_mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t cmd_id, uint8_t mmi_mode)
+{
+ struct en50221_app_mmi_display_reply_details reply;
+ (void) arg;
+ (void) slot_id;
+
+ // don't support any commands but set mode
+ if (cmd_id != MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) {
+ en50221_app_mmi_display_reply(stdcam->mmi_resource, session_number,
+ MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID, &reply);
+ return 0;
+ }
+
+ // we only support high level mode
+ if (mmi_mode != MMI_MODE_HIGH_LEVEL) {
+ en50221_app_mmi_display_reply(stdcam->mmi_resource, session_number,
+ MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE, &reply);
+ return 0;
+ }
+
+ // ack the high level open
+ reply.u.mode_ack.mmi_mode = mmi_mode;
+ en50221_app_mmi_display_reply(stdcam->mmi_resource, session_number,
+ MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK, &reply);
+ mmi_state = MMI_STATE_OPEN;
+ return 0;
+}
+
+static int gnutv_mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ uint8_t blind_answer, uint8_t expected_answer_length,
+ uint8_t *text, uint32_t text_size)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+
+ fprintf(stderr, "%.*s: ", text_size, text);
+ fflush(stdout);
+
+ mmi_enq_blind = blind_answer;
+ mmi_enq_length = expected_answer_length;
+ mmi_state = MMI_STATE_ENQ;
+ return 0;
+}
+
+static int gnutv_mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+ 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)
+{
+ (void) arg;
+ (void) slot_id;
+ (void) session_number;
+ (void) item_raw_length;
+ (void) items_raw;
+
+ fprintf(stderr, "------------------------------\n");
+
+ if (title->text_length) {
+ fprintf(stderr, "%.*s\n", title->text_length, title->text);
+ }
+ if (sub_title->text_length) {
+ fprintf(stderr, "%.*s\n", sub_title->text_length, sub_title->text);
+ }
+
+ uint32_t i;
+ fprintf(stderr, "0. Quit menu\n");
+ for(i=0; i< item_count; i++) {
+ fprintf(stderr, "%i. %.*s\n", i+1, items[i].text_length, items[i].text);
+ }
+
+ if (bottom->text_length) {
+ fprintf(stderr, "%.*s\n", bottom->text_length, bottom->text);
+ }
+ fprintf(stderr, "Enter option: ");
+ fflush(stdout);
+
+ mmi_state = MMI_STATE_MENU;
+ return 0;
+}