+/* Display Control Commands */
+
+#define DCC_SET_MMI_MODE 0x01
+#define DCC_DISPLAY_CHARACTER_TABLE_LIST 0x02
+#define DCC_INPUT_CHARACTER_TABLE_LIST 0x03
+#define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS 0x04
+#define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS 0x05
+
+/* MMI Modes */
+
+#define MM_HIGH_LEVEL 0x01
+#define MM_LOW_LEVEL_OVERLAY_GRAPHICS 0x02
+#define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS 0x03
+
+/* Display Reply IDs */
+
+#define DRI_MMI_MODE_ACK 0x01
+#define DRI_LIST_DISPLAY_CHARACTER_TABLES 0x02
+#define DRI_LIST_INPUT_CHARACTER_TABLES 0x03
+#define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS 0x04
+#define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS 0x05
+#define DRI_UNKNOWN_DISPLAY_CONTROL_CMD 0xF0
+#define DRI_UNKNOWN_MMI_MODE 0xF1
+#define DRI_UNKNOWN_CHARACTER_TABLE 0xF2
+
+/* Enquiry Flags */
+
+#define EF_BLIND 0x01
+
+/* Answer IDs */
+
+#define AI_CANCEL 0x00
+#define AI_ANSWER 0x01
+
+typedef struct
+{
+ en50221_mmi_object_t last_object;
+} mmi_t;
+
+/*****************************************************************************
+ * MMISendObject
+ *****************************************************************************/
+static void MMISendObject( access_t *p_access, int i_session_id,
+ en50221_mmi_object_t *p_object )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+ uint8_t *p_data;
+ int i_size, i_tag;
+
+ switch ( p_object->i_object_type )
+ {
+ case EN50221_MMI_ANSW:
+ i_tag = AOT_ANSW;
+ i_size = 1 + strlen( p_object->u.answ.psz_answ );
+ p_data = malloc( i_size );
+ p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
+ strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
+ break;
+
+ case EN50221_MMI_MENU_ANSW:
+ i_tag = AOT_MENU_ANSW;
+ i_size = 1;
+ p_data = malloc( i_size );
+ p_data[0] = p_object->u.menu_answ.i_choice;
+ break;
+
+ default:
+ msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
+ return;
+ }
+
+ APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
+ free( p_data );
+
+ p_sys->pb_slot_mmi_expected[i_slot] = true;
+}
+
+/*****************************************************************************
+ * MMISendClose
+ *****************************************************************************/
+static void MMISendClose( access_t *p_access, int i_session_id )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+
+ APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
+
+ p_sys->pb_slot_mmi_expected[i_slot] = true;
+}
+
+/*****************************************************************************
+ * MMIDisplayReply
+ *****************************************************************************/
+static void MMIDisplayReply( access_t *p_access, int i_session_id )
+{
+ uint8_t p_response[2];
+
+ p_response[0] = DRI_MMI_MODE_ACK;
+ p_response[1] = MM_HIGH_LEVEL;
+
+ APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
+
+ msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
+}
+
+/*****************************************************************************
+ * MMIGetText
+ *****************************************************************************/
+static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
+{
+ int i_tag = APDUGetTag( *pp_apdu, *pi_size );
+ int l;
+ uint8_t *d;
+
+ if ( i_tag != AOT_TEXT_LAST )
+ {
+ msg_Err( p_access, "unexpected text tag: %06x", i_tag );
+ *pi_size = 0;
+ return strdup( "" );
+ }
+
+ d = APDUGetLength( *pp_apdu, &l );
+
+ *pp_apdu += l + 4;
+ *pi_size -= l + 4;
+
+ return dvbsi_to_utf8((char*)d,l);
+}
+
+/*****************************************************************************
+ * MMIHandleEnq
+ *****************************************************************************/
+static void MMIHandleEnq( access_t *p_access, int i_session_id,
+ uint8_t *p_apdu, int i_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+ int l;
+ uint8_t *d = APDUGetLength( p_apdu, &l );
+
+ en50221_MMIFree( &p_mmi->last_object );
+ p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
+ p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
+ d += 2; /* skip answer_text_length because it is not mandatory */
+ l -= 2;
+ p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
+ strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
+ p_mmi->last_object.u.enq.psz_text[l] = '\0';
+
+ msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
+ p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
+ p_sys->pb_slot_mmi_expected[i_slot] = false;
+ p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
+}
+
+/*****************************************************************************
+ * MMIHandleMenu
+ *****************************************************************************/
+static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
+ uint8_t *p_apdu, int i_size )
+{
+ access_sys_t *p_sys = p_access->p_sys;
+ mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
+ int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
+ int l;
+ uint8_t *d = APDUGetLength( p_apdu, &l );
+
+ en50221_MMIFree( &p_mmi->last_object );
+ p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
+ EN50221_MMI_MENU : EN50221_MMI_LIST;
+ p_mmi->last_object.u.menu.i_choices = 0;
+ p_mmi->last_object.u.menu.ppsz_choices = NULL;
+
+ if ( l > 0 )
+ {
+ l--; d++; /* choice_nb */
+
+#define GET_FIELD( x ) \
+ if ( l > 0 ) \
+ { \
+ p_mmi->last_object.u.menu.psz_##x \
+ = MMIGetText( p_access, &d, &l ); \
+ msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s", \
+ p_mmi->last_object.u.menu.psz_##x ); \
+ }
+
+ GET_FIELD( title );
+ GET_FIELD( subtitle );
+ GET_FIELD( bottom );
+#undef GET_FIELD
+
+ while ( l > 0 )
+ {
+ char *psz_text = MMIGetText( p_access, &d, &l );
+ TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
+ p_mmi->last_object.u.menu.ppsz_choices,
+ psz_text );
+ msg_Dbg( p_access, "MMI choice: %s", psz_text );
+ }
+ }
+ p_sys->pb_slot_mmi_expected[i_slot] = false;
+ p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
+}
+