/// <summary> /// On MMI Closed callback /// </summary> /// <param name="pUSBCIFilter">WinTV CI filter</param> /// <returns></returns> public Int32 OnMMIClosed(IBaseFilter pUSBCIFilter) { Log.Log.Info("WinTvCi OnMMIClosed"); if (m_ciMenuCallback != null) { m_ciMenuCallback.OnCiCloseDisplay(0); } return(0); }
/// <summary> /// Callback from driver to close display /// </summary> /// <param name="slot">Slot</param> /// <param name="nDelay">delay in ms</param> /// <param name="pParam">Context pointer</param> public void OnCiCloseDisplay(byte slot, uint nDelay, IntPtr pParam) { try { lock (this) { Log.Log.Debug("OnKncCiCloseDisplay slot:{0} nDelay:{1}", slot, nDelay); if (m_ciMenuCallback != null) { m_ciMenuCallback.OnCiCloseDisplay((int)nDelay); } } } catch (Exception ex) { Log.Log.Debug("KncCiCloseDisplay exception: {0}", ex.ToString()); } }
/// <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); } } }