/// <summary> /// Sends the DiSEqC command. /// </summary> /// <param name="diSEqC">The DiSEqC command.</param> /// <returns>true if succeeded, otherwise false</returns> public bool SendDiSEqCCommand(byte[] diSEqC) { const int disEqcLen = 16; for (int i = 0; i < 12; ++i) { Marshal.WriteByte(_ptrDiseqc, 4 + i, 0); } Marshal.WriteInt32(_ptrDiseqc, 0, diSEqC.Length); //command len for (int i = 0; i < diSEqC.Length; ++i) { Marshal.WriteByte(_ptrDiseqc, 4 + i, diSEqC[i]); } DVB_MMI.DumpBinary(_ptrDiseqc, 0, disEqcLen); InitStructure((int)THBDA_IOCTL_SET_DiSEqC, _ptrDiseqc, disEqcLen, IntPtr.Zero, 0); bool success = false; if (propertySet != null) { int hr = propertySet.Set(THBDA_TUNER, 0, _ptrOutBuffer2, 0x18, _thbdaBuf, thbdaLen); if (hr != 0) { Log.Log.WriteFile("TwinHan DiSEqC failed 0x{1:X8}", hr); } else { Log.Log.WriteFile("TwinHan DiSEqC succeeded"); success = true; } //Release.ComObject(propertySet); } return(success); }
/// <summary> /// Gets the answer from the CAM after sending the PMT . /// </summary> /// <returns>string containing the CAM answer</returns> public string GetPmtReply() { if (IsCamPresent() == false) { return(""); } for (int i = 0; i < 1024; ++i) { Marshal.WriteByte(_ptrPmt, i, 0); } InitStructure((int)THBDA_IOCTL_CI_GET_PMT_REPLY, IntPtr.Zero, 0, _ptrPmt, 1024); if (propertySet != null) { int hr = propertySet.Set(THBDA_TUNER, 0, _ptrOutBuffer2, 0x18, _thbdaBuf, thbdaLen); int back = Marshal.ReadInt32(_ptrDwBytesReturned); if (hr != 0) { Log.Log.WriteFile("Twinhan: GetPmtReply() failed 0x{0:X}", hr); } Log.Log.WriteFile("Twinhan: GetPmtReply() returned {0} bytes", back); DVB_MMI.DumpBinary(_ptrPmt, 0, back); //Release.ComObject(propertySet); } return(""); }
/// <summary> /// Selects a CI menu entry /// </summary> /// <param name="choice"></param> /// <returns></returns> public bool SelectMenu(byte choice) { Log.Log.Debug("WinTvCi: Select CI Menu entry {0}", choice); byte[] uData = new byte[5]; // default answer length DVB_MMI.CreateMMISelect(choice, ref uData); SendAPDU(uData, uData.Length); // send to cam return(true); }
/// <summary> /// Closes the CI menu of KNC1 card /// </summary> /// <returns></returns> public bool CloseCIMenu() { Log.Log.Debug("WinTvCi: Close CI Menu"); byte[] uData = new byte[5]; // default answer length DVB_MMI.CreateMMIClose(ref uData); SendAPDU(uData, uData.Length); // send to cam return(true); }
/// <summary> /// Sends the PMT to the CAM/CI module /// </summary> /// <param name="caPMT">The caPMT structure.</param> /// <param name="caPMTLen">The caPMT lenght</param> /// <returns>false on failure to send PMT</returns> public bool SendPMT(byte[] caPMT, int caPMTLen) { if (IsCamPresent() == false) { return(true); // Nothing to do } Log.Log.WriteFile("Twinhan: Send PMT, len: {0}", caPMTLen); if (caPMT.Length == 0) { return(false); } Log.Log.WriteFile(" capmt:"); DVB_MMI.DumpBinary(caPMT, 0, caPMTLen); bool suceeded = false; Marshal.Copy(caPMT, 0, _ptrPmt, caPMTLen); InitStructure((int)THBDA_IOCTL_CI_SEND_PMT, _ptrPmt, caPMTLen, IntPtr.Zero, 0); if (propertySet != null) { int failedAttempts = 0; while (true) { int hr = propertySet.Set(THBDA_TUNER, 0, _ptrOutBuffer2, 0x18, _thbdaBuf, thbdaLen); if (hr != 0) { failedAttempts++; Log.Log.WriteFile("Twinhan: CAM failed 0x{0:X}", hr); if (((uint)hr) == (0x8007001F) && failedAttempts < 10) { Log.Log.Debug(" sleep and then retry again, failedAttempts: {0}", failedAttempts); System.Threading.Thread.Sleep(100); continue; } } else { Log.Log.WriteFile("Twinhan: CAM returned ok 0x{0:X}", hr); suceeded = true; } break; } //Release.ComObject(propertySet); } return(suceeded); }
/// <summary> /// Sends an answer after CI request /// </summary> /// <param name="Cancel"></param> /// <param name="Answer"></param> /// <returns></returns> public bool SendMenuAnswer(bool Cancel, String Answer) { if (Answer == null) { Answer = ""; } Log.Log.Debug("WinTvCi: Send Menu Answer: {0}, Cancel: {1}", Answer, Cancel); byte[] uData = new byte[1024]; byte uLength1 = 0; byte uLength2 = 0; DVB_MMI.CreateMMIAnswer(Cancel, Answer, ref uData, ref uLength1, ref uLength2); SendAPDU(uData, uLength1); // send to cam return(true); }
/// <summary> /// Sends a diseqc command and reads a reply /// </summary> /// <param name="reply">The reply.</param> /// <returns>true if succeeded, otherwise false</returns> public bool ReadDiSEqCCommand(out byte[] reply) { reply = new byte[1]; reply[0] = 0; const int disEqcLen = 16; for (int i = 0; i < 16; ++i) { Marshal.WriteByte(_ptrDiseqc, i, 0); } bool success = false; InitStructure((int)THBDA_IOCTL_GET_DiSEqC, IntPtr.Zero, 0, _ptrDiseqc, disEqcLen); if (propertySet != null) { int hr = propertySet.Set(THBDA_TUNER, 0, _ptrOutBuffer2, 0x18, _thbdaBuf, thbdaLen); if (hr != 0) { Log.Log.WriteFile("TwinHan get DiSEqC failed 0x{0:X}", hr); } else { Log.Log.WriteFile("TwinHan get DiSEqC ok 0x{0:X}", hr); } Log.Log.Write("ReadDiSEqCCommand"); DVB_MMI.DumpBinary(_ptrDiseqc, 0, 16); success = true; int bytesReturned = Marshal.ReadInt32(_ptrDiseqc); if (bytesReturned > 0) { reply = new byte[bytesReturned]; for (int i = 0; i < bytesReturned; ++i) { reply[i] = Marshal.ReadByte(_ptrDiseqc, 4 + i); } } //Release.ComObject(propertySet); } return(success); }
/// <summary> /// On APDU callback /// </summary> /// <param name="pUSBCIFilter">CI filter</param> /// <param name="APDU">APDU</param> /// <param name="SizeOfAPDU">Size of APDU</param> /// <returns></returns> public Int32 OnAPDU(IBaseFilter pUSBCIFilter, IntPtr APDU, long SizeOfAPDU) { Int32 MMI_length = (Int32)SizeOfAPDU; Log.Log.Info("WinTvCi OnAPDU: SizeOfAPDU={0}", MMI_length); byte[] bMMI = DVB_MMI.IntPtrToByteArray(APDU, 0, MMI_length); #if DEBUG DVB_MMI.DumpBinary(bMMI, 0, MMI_length); #endif try { MMI.HandleMMI(bMMI, MMI_length); } catch (Exception e) { Log.Log.Write("WinTvCI error:"); Log.Log.Write(e); } return(0); }
/// <summary> /// Sends a MMI object with answer back /// </summary> /// <param name="MMI"></param> /// <returns></returns> private bool SendMMI(MMIInfoStruct MMI) { Marshal.StructureToPtr(MMI, _ptrMMIBuffer, true); int sizeMMI = Marshal.SizeOf(MMI); Log.Log.Debug("SendMMI: size {0}", sizeMMI); DVB_MMI.DumpBinary(_ptrMMIBuffer, 0, sizeMMI); InitStructure((int)THBDA_IOCTL_CI_ANSWER, IntPtr.Zero, 0, _ptrMMIBuffer, sizeMMI); if (propertySet != null) { int hr = propertySet.Set(THBDA_TUNER, 0, _ptrOutBuffer2, 0x18, _thbdaBuf, thbdaLen); if (hr == 0) { Log.Log.Debug("TwinHan: SendMMI successful"); return(true); } Log.Log.Debug("TwinHan: SendMMI failed 0x{0:X}", hr); } return(false); }
/// <summary> /// Reads a MMI object /// </summary> /// <returns></returns> private MMIInfoStruct ReadMMI() { Int32 bytesReturned; MMIInfoStruct MMI; // clear buffer first for (int i = 0; i < 8192; i += 4) { Marshal.WriteInt32(_ptrMMIBuffer, i, 0); } InitStructure((int)THBDA_IOCTL_CI_GET_MMI, IntPtr.Zero, 0, _ptrMMIBuffer, 8192); if (propertySet != null) { int hr = propertySet.Set(THBDA_TUNER, 0, _ptrOutBuffer2, 0x18, _thbdaBuf, thbdaLen); if (hr == 0) { bytesReturned = Marshal.ReadInt32(_ptrDwBytesReturned); Log.Log.Debug("TwinHan: ReadMMI successful, bytes {0}", bytesReturned); DVB_MMI.DumpBinary(_ptrMMIBuffer, 0, bytesReturned); try { MMI = (MMIInfoStruct)Marshal.PtrToStructure(_ptrMMIBuffer, typeof(MMIInfoStruct)); } catch (Exception e) { Log.Log.Write(e); return(null); } return(MMI); } Log.Log.Debug("TwinHan: ReadMMI failed 0x{0:X}", hr); } return(null); }
/// <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); } } }