/// <summary> /// Callback from driver, returning CI menu headers /// </summary> /// <param name="slot">Slot</param> /// <param name="lpszTitle">Title</param> /// <param name="lpszSubTitle">Subtitle</param> /// <param name="lpszBottom">Bottom text</param> /// <param name="nNumChoices">Number of choices</param> /// <param name="pParam">Context pointer</param> public void OnCiMenu(byte slot, String lpszTitle, String lpszSubTitle, String lpszBottom, uint nNumChoices, IntPtr pParam) { try { lock (this) { Log.Log.Debug("OnKncCiMenu slot: {0}", slot); Log.Log.Debug("OnKncCiMenu title: {0}", lpszTitle); Log.Log.Debug("OnKncCiMenu subtitle: {0}", lpszSubTitle); Log.Log.Debug("OnKncCiMenu bottom: {0}", lpszSubTitle); Log.Log.Debug("OnKncCiMenu lpszBottom: {0}", lpszBottom); Log.Log.Debug("OnKncCiMenu nNumChoices:{0}", nNumChoices); if (m_ciMenuCallback != null) { m_ciMenuCallback.OnCiMenu(lpszTitle.ToString(), lpszSubTitle.ToString(), lpszBottom.ToString(), (int)nNumChoices); } } } catch (Exception ex) { Log.Log.Debug("OnKncCiMenu exception: {0}", ex.ToString()); } }
/// <summary> /// Process the MMI and do callbacks. /// </summary> /// <param name="CiMenu"></param> /// <returns></returns> private bool ProcessCamMenu(DD_CAM_MENU_DATA CiMenu) { Log.Log.Debug("Menu received (ID {0} Type {1} Choices {2})", CiMenu.Id, CiMenu.Type, CiMenu.NumChoices); //Log.Log.Debug(" Menu Id = {0}", CiMenu.Id); //Log.Log.Debug(" Menu Type = {0}", CiMenu.Type); //Log.Log.Debug(" Menu Choices = {0}", CiMenu.NumChoices); //Log.Log.Debug(" Menu Length = {0}", CiMenu.Length); if (ciMenuCallbacks == null) { return(false); } switch (CiMenu.Type) { case 1: case 2: ciMenuCallbacks.OnCiMenu(CiMenu.Title, CiMenu.SubTitle, CiMenu.BottomText, CiMenu.NumChoices); int n = 0; foreach (String choice in CiMenu.Choices) { ciMenuCallbacks.OnCiMenuChoice(n++, choice); } break; case 3: case 4: ciMenuCallbacks.OnCiRequest(false, (uint)CiMenu.NumChoices, CiMenu.Title); break; default: Log.Log.Debug("Unknown MMI Type {0}", CiMenu.Type); break; } return(true); }
/// <summary> /// Thread that checks for CI menu /// </summary> private void CiMenuHandler() { Log.Log.Debug("TwinHan: CI handler thread start polling status"); try { while (!StopThread) { uint CIState; uint MMIState; GetCAMStatus(out CIState, out MMIState, true); switch (MMIState) { case 3: // TODO: find proper MMIState codings MMIInfoStruct MMI = ReadMMI(); if (MMI != null) { Log.Log.Debug("TwinHan MMI:"); Log.Log.Debug("Type :{0}", MMI.Type); Log.Log.Debug("Header: {0}", MMI.Header); Log.Log.Debug("SubHeader: {0}", MMI.SubHeader); Log.Log.Debug("ButtomLine: {0}", MMI.BottomLine); Log.Log.Debug("ItemCount: {0}", MMI.ItemCount); Log.Log.Debug("EnqFlag: {0}", MMI.EnqFlag); Log.Log.Debug("Prompt: {0}", MMI.Prompt); Log.Log.Debug("AnswerLength:{0}", MMI.Answer_Text_Length); Log.Log.Debug("Blind_Answer:{0}", MMI.Blind_Answer); // which types do we get??? switch (MMI.Type) { case 0: m_ciMenuCallback.OnCiCloseDisplay(0); break; case 1: if (m_ciMenuCallback != null) { m_ciMenuCallback.OnCiMenu(MMI.Header, MMI.SubHeader, MMI.BottomLine, MMI.ItemCount); for (int m = 0; m < MMI.ItemCount; m++) { // choice number start with 0 m_ciMenuCallback.OnCiMenuChoice(m, MMI.MenuItems[m].MenuItem); } } break; case 3: if (MMI.EnqFlag != 0) { if (m_ciMenuCallback != null) { m_ciMenuCallback.OnCiRequest((MMI.Blind_Answer == 1), (uint)MMI.Answer_Text_Length, MMI.Prompt); } } break; } } break; default: Log.Log.Write("MMI State {0}", (CIState)MMIState); break; } Thread.Sleep(500); } } catch (ThreadAbortException) {} catch (Exception ex) { Log.Log.Debug("TwinHan: error in CiMenuHandler thread\r\n{0}", ex.ToString()); return; } }
/// <summary> /// Handles APDU (MMI) objects and perform callbacks /// </summary> /// <param name="MMI">MMI byte[]</param> /// <param name="MMI_length">length</param> public void HandleMMI(byte[] MMI, int MMI_length) { // parse starting 3 bytes == tag DVB_MMI.MMI_TAGS uMMITag = DVB_MMI.ToMMITag(MMI, 0); // dumping binary APDU #if DEBUG DVB_MMI.DumpBinary(MMI, 0, MMI_length); #endif // calculate length and offset int countLengthBytes; int mmiLength = DVB_MMI.GetLength(MMI, 3 /* bytes for mmi_tag */, out countLengthBytes); int mmiOffset = 3 + countLengthBytes; // 3 bytes mmi tag + 1 byte length field ? #if DEBUG Log.Log.Debug("{0}: MMITag:{1}, MMIObjectLength: {2} ({2:X2}), mmiOffset: {3}", m_cardName, uMMITag, mmiLength, mmiOffset); #endif int offset = 0; // starting with 0; reading whole struct from start if (uMMITag == DVB_MMI.MMI_TAGS.CLOSE) { // Close menu byte nDelay = 0; byte CloseCmd = MMI[mmiOffset + 0]; if (CloseCmd != 0) { nDelay = MMI[mmiOffset + 1]; } if (m_ciMenuCallback != null) { Log.Log.Debug("{0}: OnCiClose()", m_cardName); m_ciMenuCallback.OnCiCloseDisplay(nDelay); } else { Log.Log.Debug("{0}: OnCiCloseDisplay: cannot do callback!", m_cardName); } } if (uMMITag == DVB_MMI.MMI_TAGS.ENQUIRY) { // request input bool bPasswordMode = false; byte answer_text_length = MMI[mmiOffset + 1]; string strText = ""; if ((MMI[mmiOffset + 0] & 0x01) != 0) { bPasswordMode = true; } // mmioffset +4 because there a 2 other bytes before text starts // length is these 2 bytes shorter strText = DVB_MMI.BytesToString(MMI, mmiOffset + 4, mmiLength - mmiOffset - 2); if (m_ciMenuCallback != null) { Log.Log.Debug("{0}: OnCiRequest: bPasswordMode:{1}, answer_text_length:{2}, strText:{3}", m_cardName, bPasswordMode, answer_text_length, strText); m_ciMenuCallback.OnCiRequest(bPasswordMode, answer_text_length, strText); } else { Log.Log.Debug("{0}: OnCiRequest: cannot do callback!", m_cardName); } } if (uMMITag == DVB_MMI.MMI_TAGS.LIST_LAST || uMMITag == DVB_MMI.MMI_TAGS.MENU_LAST || uMMITag == DVB_MMI.MMI_TAGS.MENU_MORE || uMMITag == DVB_MMI.MMI_TAGS.LIST_MORE) { // step forward; begin with offset+1; stop when 0x9F reached // should be modified to offset + mmioffset+1 ? offset++; while (MMI[offset] != (byte)0x9F) { //Log.Log.Debug("Skip to offset {0} value {1:X2}", offset, MMI[offset]); offset++; } uMMITag = DVB_MMI.ToMMITag(MMI, offset); // get next MMI tag Log.Log.Debug("{0}: MMI Parse: Got MENU_LAST, skipped to next block on index: {1}; new Tag {2}", m_cardName, offset, uMMITag); int nChoices = 0; List <string> Choices = new List <string>(); // Always three line with menu info (DVB Standard) // Title Text offset += DVB_MMI.GetCIText(MMI, offset, ref Choices); // Subtitle Text offset += DVB_MMI.GetCIText(MMI, offset, ref Choices); // Bottom Text offset += DVB_MMI.GetCIText(MMI, offset, ref Choices); // first step through the choices, to get info and count them int max = 20; while (max-- > 0) { // if the offset gets to mmi object length then end here if (offset >= mmiLength - 1) { break; } offset += DVB_MMI.GetCIText(MMI, offset, ref Choices); nChoices++; } // when title and choices are ready now, send to client #if DEBUG for (int c = 0; c < Choices.Count; c++) { Log.Log.Debug("{0}: {1} : {2}", m_cardName, c, Choices[c]); } #endif if (m_ciMenuCallback != null) { m_ciMenuCallback.OnCiMenu(Choices[0], Choices[1], Choices[2], nChoices); for (int c = 3; c < Choices.Count; c++) { m_ciMenuCallback.OnCiMenuChoice(c - 3, Choices[c]); } } else { Log.Log.Debug("{0}: OnCiMenu: cannot do callback!", m_cardName); } } }