From: <an...@us...> - 2007-09-05 17:23:57
|
Revision: 895 http://mp-plugins.svn.sourceforge.net/mp-plugins/?rev=895&view=rev Author: and-81 Date: 2007-09-05 10:23:54 -0700 (Wed, 05 Sep 2007) Log Message: ----------- Modified Paths: -------------- trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/Driver.cs trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/IrCode.cs trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/IrDecoder.cs trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/Microsoft MCE Transceiver.csproj trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/MicrosoftMceTransceiver.cs trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/Pronto.cs Added Paths: ----------- trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverReplacement.cs trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverVista.cs trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverXP.cs Modified: trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/Driver.cs =================================================================== --- trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/Driver.cs 2007-09-05 13:05:02 UTC (rev 894) +++ trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/Driver.cs 2007-09-05 17:23:54 UTC (rev 895) @@ -11,6 +11,9 @@ namespace MicrosoftMceTransceiver { + /// <summary> + /// Base class for the different MCE device driver access classes. + /// </summary> public abstract class Driver { @@ -111,7 +114,8 @@ struct DeviceInterfaceDetailData { public int Size; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string DevicePath; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string DevicePath; } #endregion Structures @@ -196,12 +200,28 @@ #region Abstract Methods + /// <summary> + /// Start using the device. + /// </summary> public abstract void Start(); + /// <summary> + /// Stop access to the device. + /// </summary> public abstract void Stop(); - + // TODO: Change learn interface to return LearnStatus + /// <summary> + /// Learn an IR Command. + /// </summary> + /// <param name="learnTimeout">How long to wait before aborting learn.</param> + /// <returns>Newly learned IR Command.</returns> public abstract IrCode Learn(int learnTimeout); + /// <summary> + /// Send an IR Command. + /// </summary> + /// <param name="code">IR code data to send.</param> + /// <param name="port">IR port to send to.</param> public abstract void Send(IrCode code, uint port); #endregion Abstract Methods Added: trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverReplacement.cs =================================================================== --- trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverReplacement.cs (rev 0) +++ trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverReplacement.cs 2007-09-05 17:23:54 UTC (rev 895) @@ -0,0 +1,759 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.ServiceProcess; +using System.Text; +using System.Threading; + +using Microsoft.Win32.SafeHandles; + +namespace MicrosoftMceTransceiver +{ + + public class DriverReplacement : Driver + { + + #region Interop + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetOverlappedResult( + SafeFileHandle handle, + ref NativeOverlapped overlapped, + out int bytesTransferred, + bool wait); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern SafeFileHandle CreateFile( + [MarshalAs(UnmanagedType.LPTStr)] string fileName, + [MarshalAs(UnmanagedType.U4)] FileAccessTypes fileAccess, + [MarshalAs(UnmanagedType.U4)] FileShares fileShare, + IntPtr securityAttributes, + [MarshalAs(UnmanagedType.U4)] CreationDisposition creationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes flags, + IntPtr templateFile); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ReadFile( + SafeFileHandle handle, + IntPtr buffer, + int bytesToRead, + out int bytesRead, + ref NativeOverlapped overlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool WriteFile( + SafeFileHandle handle, + byte[] buffer, + int bytesToWrite, + out int bytesWritten, + ref NativeOverlapped overlapped); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CancelIo( + SafeFileHandle handle); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle( + SafeFileHandle handle); + + #endregion Interop + + #region Enumerations + + /// <summary> + /// Type of device in use. + /// This is used to determine the blaster port selection method. + /// </summary> + enum DeviceType + { + /// <summary> + /// Device is a first party Microsoft MCE transceiver. + /// </summary> + Microsoft = 0, + /// <summary> + /// Device is an third party SMK or Topseed MCE transceiver. + /// </summary> + SmkTopseed = 1, + } + + /// <summary> + /// Device input port. + /// </summary> + enum InputPort + { + Receive = 0, + Learning = 1, + } + + /// <summary> + /// Read Thread Mode. + /// </summary> + enum ReadThreadMode + { + Receiving, + Learning, + LearningDone, + LearningFailed, + Stop, + } + + #endregion Enumerations + + #region Constants + + // Vendor ID's for SMK and Topseed devices. + const string VidSMK = "vid_1784"; + const string VidTopseed = "vid_0609"; + + // Device variables + const int DeviceBufferSize = 100; + const int PacketTimeout = 100; + const int WriteSyncTimeout = 5000; + + // Microsoft Port Packets + static readonly byte[][] MicrosoftPorts = new byte[][] + { + new byte[] { 0x9F, 0x08, 0x06 }, // Both + new byte[] { 0x9F, 0x08, 0x04 }, // 1 + new byte[] { 0x9F, 0x08, 0x02 }, // 2 + }; + + // SMK or Topseed Port Packets + static readonly byte[][] SmkTopseedPorts = new byte[][] + { + new byte[] { 0x9F, 0x08, 0x00 }, // Both + new byte[] { 0x9F, 0x08, 0x01 }, // 1 + new byte[] { 0x9F, 0x08, 0x02 }, // 2 + }; + + // Start and Stop Packets + static readonly byte[] StartPacket = { 0x00, 0xFF, 0xAA }; + static readonly byte[] StopPacket = { 0xFF, 0xBB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + // Misc Packets + static readonly byte[] SetCarrierFreqPacket = { 0x9F, 0x06, 0x01, 0x80 }; + static readonly byte[] SetInputPortPacket = { 0x9F, 0x14, 0x00 }; + static readonly byte[] SetTimeoutPacket = { 0x9F, 0x0C, 0x00, 0x00 }; + + #endregion Constants + + #region Variables + + NotifyWindow _notifyWindow; + + SafeFileHandle _readHandle; + SafeFileHandle _writeHandle; + + Thread _readThread; + ReadThreadMode _readThreadMode; + ManualResetEvent _stopReadThread; + + IrCode _learningCode; + + DeviceType _deviceType = DeviceType.Microsoft; + + //StreamWriter _debugFile; + + #endregion Variables + + #region Constructor + + public DriverReplacement(Guid deviceGuid, string devicePath, RemoteCallback remoteCallback, KeyboardCallback keyboardCallback, MouseCallback mouseCallback) + : base(deviceGuid, devicePath, remoteCallback, keyboardCallback, mouseCallback) + { + if (devicePath.IndexOf(VidSMK, StringComparison.InvariantCultureIgnoreCase) != -1 || devicePath.IndexOf(VidTopseed, StringComparison.InvariantCultureIgnoreCase) != -1) + _deviceType = DeviceType.SmkTopseed; + else + _deviceType = DeviceType.Microsoft; + } + + #endregion Constructor + + #region Driver overrides + + public override void Start() + { + //_debugFile = new StreamWriter("\\DriverReplacement.log", false); + //_debugFile.AutoFlush = true; + + _notifyWindow = new NotifyWindow(); + _notifyWindow.Class = _deviceGuid; + + int lastError; + + _readHandle = CreateFile(_devicePath + "\\Pipe01", FileAccessTypes.GenericRead, FileShares.None, IntPtr.Zero, CreationDisposition.OpenExisting, FileAttributes.Overlapped, IntPtr.Zero); + lastError = Marshal.GetLastWin32Error(); + if (_readHandle.IsInvalid) + throw new Win32Exception(lastError); + + _writeHandle = CreateFile(_devicePath + "\\Pipe00", FileAccessTypes.GenericWrite, FileShares.None, IntPtr.Zero, CreationDisposition.OpenExisting, FileAttributes.Overlapped, IntPtr.Zero); + lastError = Marshal.GetLastWin32Error(); + if (_writeHandle.IsInvalid) + throw new Win32Exception(lastError); + + // Initialize device ... + WriteSync(StartPacket); + SetTimeout(PacketTimeout); + SetInputPort(InputPort.Receive); + + StartReadThread(); + + _notifyWindow.Create(); + _notifyWindow.DeviceArrival += new DeviceEventHandler(OnDeviceArrival); + _notifyWindow.DeviceRemoval += new DeviceEventHandler(OnDeviceRemoval); + _notifyWindow.RegisterDeviceRemoval(_readHandle.DangerousGetHandle()); + } + + public override void Stop() + { + WriteSync(StopPacket); + + OnDeviceRemoval(); + + CloseDevice(); + + //_debugFile.Close(); + } + + public override IrCode Learn(int learnTimeout) + { + //_debugFile.WriteLine("Start Learn"); + + _learningCode = new IrCode(); + + SetInputPort(InputPort.Learning); + + int learnStartTick = Environment.TickCount; + _readThreadMode = ReadThreadMode.Learning; + + // Wait for the learning to finish ... + while (_readThreadMode == ReadThreadMode.Learning && Environment.TickCount < learnStartTick + learnTimeout) + Thread.Sleep(100); + + //_debugFile.WriteLine("End Learn"); + + ReadThreadMode modeWas = _readThreadMode; + + _readThreadMode = ReadThreadMode.Receiving; + SetInputPort(InputPort.Receive); + + switch (modeWas) + { + case ReadThreadMode.Learning: + // Timeout. + return null; + + case ReadThreadMode.LearningFailed: + // Failure. + return null; + + case ReadThreadMode.LearningDone: + //_debugFile.WriteLine(_learningCode.ToByteArray()); + + if (_learningCode.FinalizeData()) + return _learningCode; // Success. + else + return null; // Failure. + + default: + return null; + } + } + + public override void Send(IrCode code, uint port) + { + //_debugFile.WriteLine("Send"); + + byte[] portPacket; + switch (_deviceType) + { + case DeviceType.Microsoft: portPacket = MicrosoftPorts[port]; break; + case DeviceType.SmkTopseed: portPacket = SmkTopseedPorts[port]; break; + default: + throw new Exception("Invalid device type"); + } + + //Dump(code.ToByteArray()); + + // Set port + WriteSync(portPacket); + + // Set carrier frequency + WriteSync(CarrierPacket(code)); + + // Send packet + WriteSync(DataPacket(code)); + } + + #endregion Driver overrides + + #region Implementation + + byte[] CarrierPacket(IrCode code) + { + byte[] carrierPacket = new byte[SetCarrierFreqPacket.Length]; + SetCarrierFreqPacket.CopyTo(carrierPacket, 0); + + if (code.Carrier == IrCode.CarrierFrequencyUnknown || code.Carrier == IrCode.CarrierFrequencyDCMode) + return carrierPacket; + + for (int scaler = 1; scaler <= 4; scaler++) + { + int divisor = (10000000 >> (2 * scaler)) / code.Carrier; + + if (divisor <= 0xFF) + { + carrierPacket[2] = (byte)scaler; + carrierPacket[3] = (byte)divisor; + break; + } + } + + return carrierPacket; + } + + byte[] DataPacket(IrCode code) + { + if (code.TimingData.Length == 0) + return null; + + // Construct data bytes into "packet" ... + List<byte> packet = new List<byte>(); + + for (int index = 0; index < code.TimingData.Length; index++) + { + double time = (double)code.TimingData[index]; + + byte duration = (byte)Math.Abs(Math.Round(time / 50)); + bool pulse = (time > 0); + + while (duration > 0x7F) + { + packet.Add((byte)(pulse ? 0xFF : 0x7F)); + + duration -= 0x7F; + } + + packet.Add((byte)(pulse ? 0x80 | duration : duration)); + } + + // Insert byte count markers into packet data bytes ... + int subpackets = (int)Math.Ceiling(packet.Count / (double)4); + + byte[] output = new byte[packet.Count + subpackets + 1]; + + int outputPos = 0; + + for (int packetPos = 0; packetPos < packet.Count; ) + { + byte copyCount = (byte)(packet.Count - packetPos < 4 ? packet.Count - packetPos : 0x04); + + output[outputPos++] = (byte)(0x80 | copyCount); + + for (int index = 0; index < copyCount; index++) + output[outputPos++] = packet[packetPos++]; + } + + output[outputPos] = 0x80; + + return output; + } + + /// <summary> + /// Set the receive buffer timeout. + /// </summary> + /// <param name="timeout">Packet timeout (in milliseconds).</param> + void SetTimeout(int timeout) + { + byte[] timeoutPacket = new byte[SetTimeoutPacket.Length]; + SetTimeoutPacket.CopyTo(timeoutPacket, 0); + + int timeoutSamples = 20 * timeout; + + timeoutPacket[2] = (byte)(timeoutSamples >> 8); + timeoutPacket[3] = (byte)(timeoutSamples % 256); + + WriteSync(timeoutPacket); + } + + void SetInputPort(InputPort port) + { + byte[] inputPortPacket = new byte[SetInputPortPacket.Length]; + SetInputPortPacket.CopyTo(inputPortPacket, 0); + + inputPortPacket[2] = (byte)(port + 1); + + WriteSync(inputPortPacket); + } + + void StartReadThread() + { + _stopReadThread = new ManualResetEvent(false); + _readThreadMode = ReadThreadMode.Receiving; + + _readThread = new Thread(new ThreadStart(ReadThread)); + _readThread.IsBackground = true; + _readThread.Name = "IR Server Microsoft MCE Transceiver Read"; + _readThread.Start(); + } + + void StopReadThread() + { + if (_readThread != null) + { + _readThreadMode = ReadThreadMode.Stop; + _stopReadThread.Set(); + + //_readThread.Abort(); + + if (Thread.CurrentThread != _readThread) + _readThread.Join(); + + _readThread = null; + } + } + + void CloseDevice() + { + if (_readHandle != null) + CloseHandle(_readHandle); + + if (_writeHandle != null) + CloseHandle(_writeHandle); + } + + void OnDeviceArrival() + { + _notifyWindow.UnregisterDeviceArrival(); + + StartReadThread(); + + _notifyWindow.RegisterDeviceRemoval(_readHandle.DangerousGetHandle()); + } + void OnDeviceRemoval() + { + _notifyWindow.UnregisterDeviceRemoval(); + _notifyWindow.RegisterDeviceArrival(); + + StopReadThread(); + } + + void ReadThread() + { + int bytesRead; + TimeSpan sinceLastPacket; + DateTime lastPacketTime = DateTime.Now; + + byte[] packetBytes; + + int lastError; + + NativeOverlapped lpOverlapped = new NativeOverlapped(); + lpOverlapped.InternalLow = IntPtr.Zero; + lpOverlapped.InternalHigh = IntPtr.Zero; + lpOverlapped.OffsetLow = 0; + lpOverlapped.OffsetHigh = 0; + + WaitHandle waitHandle = new ManualResetEvent(false); + SafeHandle safeWaitHandle = waitHandle.SafeWaitHandle; + WaitHandle[] waitHandles = new WaitHandle[] { waitHandle, _stopReadThread }; + + bool success = false; + safeWaitHandle.DangerousAddRef(ref success); + if (!success) + return; + + IntPtr deviceBufferPtr = IntPtr.Zero; + + try + { + deviceBufferPtr = Marshal.AllocHGlobal(DeviceBufferSize); + + while (_readThreadMode != ReadThreadMode.Stop) + { + bytesRead = 0; + + lpOverlapped.EventHandle = safeWaitHandle.DangerousGetHandle(); + + bool readDevice = ReadFile(_readHandle, deviceBufferPtr, DeviceBufferSize, out bytesRead, ref lpOverlapped); + lastError = Marshal.GetLastWin32Error(); + + if (!readDevice) + { + if (lastError != Win32ErrorCodes.ERROR_SUCCESS && lastError != Win32ErrorCodes.ERROR_IO_PENDING) + throw new Win32Exception(lastError); + + while (true) + { + int handle = WaitHandle.WaitAny(waitHandles, PacketTimeout + 50, false); + + if (handle == Win32ErrorCodes.WAIT_TIMEOUT) + continue; + else if (handle == 0) + break; + else if (handle == 1) + throw new Exception("Stop Read Thread"); + else + throw new Exception("Invalid wait handle return"); + } + + bool getOverlapped = GetOverlappedResult(_readHandle, ref lpOverlapped, out bytesRead, true); + lastError = Marshal.GetLastWin32Error(); + + if (!getOverlapped) + { + if (lastError != Win32ErrorCodes.ERROR_SUCCESS) + throw new Win32Exception(lastError); + } + } + + if (bytesRead == 0) + continue; + + sinceLastPacket = DateTime.Now.Subtract(lastPacketTime); + if (sinceLastPacket.TotalMilliseconds >= PacketTimeout) + IrDecoder.DecodeIR(null, null, null, null); + + lastPacketTime = DateTime.Now; + + packetBytes = new byte[bytesRead]; + Marshal.Copy(deviceBufferPtr, packetBytes, 0, bytesRead); + + //Dump(packetBytes); + + int[] timingData; + + switch (_readThreadMode) + { + case ReadThreadMode.Receiving: + { + if (packetBytes[0] >= 0x81 && packetBytes[0] <= 0x8F) + { + timingData = GetTimingDataFromPacket(packetBytes); + if (timingData == null) + break; + + IrDecoder.DecodeIR(timingData, _remoteCallback, _keyboardCallback, _mouseCallback); + } + break; + } + + case ReadThreadMode.Learning: + { + timingData = GetTimingDataFromPacket(packetBytes); + if (timingData == null) + { + if (_learningCode.TimingData.Length > 0) + { + _learningCode = null; + _readThreadMode = ReadThreadMode.LearningFailed; + } + break; + } + + _learningCode.AddTimingData(timingData); + + // 9F 01 02 9F 15 00 BE 80 + int indexOf9F = Array.IndexOf(packetBytes, (byte)0x9F); + while (indexOf9F != -1) + { + if (packetBytes.Length > indexOf9F + 3 && packetBytes[indexOf9F + 1] == 0x15) + { + byte b1 = packetBytes[indexOf9F + 2]; + byte b2 = packetBytes[indexOf9F + 3]; + + int onTime, onCount; + GetIrCodeLengths(_learningCode, out onTime, out onCount); + + double carrierCount = ((b1 * 256) + b2); + + if (carrierCount / onCount < 2) + { + _learningCode.Carrier = IrCode.CarrierFrequencyDCMode; + } + else + { + double carrier = (double)1000000 * carrierCount / onTime; + + if (carrier > 32000) + _learningCode.Carrier = (int)(carrier + 0.05 * carrier - 32000 / 48000); + else + _learningCode.Carrier = (int)carrier; + } + + //_debugFile.WriteLine(String.Format("Carrier Freq ({0:X2}, {1:X2}): {2}", b1, b2, _learningCode.Carrier)); + + _readThreadMode = ReadThreadMode.LearningDone; + break; + } + + if (packetBytes.Length > indexOf9F + 1) + indexOf9F = Array.IndexOf(packetBytes, (byte)0x9F, indexOf9F + 1); + else + { + _readThreadMode = ReadThreadMode.LearningFailed; + break; + } + } + + break; + } + } + + } + } + catch + { + CancelIo(_readHandle); + } + finally + { + if (deviceBufferPtr != IntPtr.Zero) + Marshal.FreeHGlobal(deviceBufferPtr); + + safeWaitHandle.DangerousRelease(); + waitHandle.Close(); + } + } + /* + void Dump(byte[] data) + { + _debugFile.WriteLine("Dump"); + foreach (byte d in data) + _debugFile.Write(String.Format("{0:X2} ", d)); + _debugFile.WriteLine(); + } + */ + void WriteSync(byte[] data) + { + NativeOverlapped lpOverlapped = new NativeOverlapped(); + lpOverlapped.InternalLow = IntPtr.Zero; + lpOverlapped.InternalHigh = IntPtr.Zero; + lpOverlapped.OffsetLow = 0; + lpOverlapped.OffsetHigh = 0; + + int bytesWritten = 0; + + int lastError; + + WaitHandle waitHandle = new ManualResetEvent(false); + SafeHandle safeWaitHandle = waitHandle.SafeWaitHandle; + WaitHandle[] waitHandles = new WaitHandle[] { waitHandle }; + + bool success = false; + safeWaitHandle.DangerousAddRef(ref success); + if (!success) + return; + + try + { + lpOverlapped.EventHandle = safeWaitHandle.DangerousGetHandle(); + + bool writeDevice = WriteFile(_writeHandle, data, data.Length, out bytesWritten, ref lpOverlapped); + lastError = Marshal.GetLastWin32Error(); + + if (!writeDevice) + { + if (lastError != Win32ErrorCodes.ERROR_SUCCESS && lastError != Win32ErrorCodes.ERROR_IO_PENDING) + throw new Win32Exception(lastError); + + int handle = WaitHandle.WaitAny(waitHandles, WriteSyncTimeout, false); + + if (handle == Win32ErrorCodes.WAIT_TIMEOUT) + throw new Exception("Timeout trying to write data to device"); + else if (handle != 0) + throw new Exception("Invalid wait handle return"); + + bool getOverlapped = GetOverlappedResult(_writeHandle, ref lpOverlapped, out bytesWritten, true); + lastError = Marshal.GetLastWin32Error(); + + if (!getOverlapped) + { + if (lastError != Win32ErrorCodes.ERROR_SUCCESS) + throw new Win32Exception(lastError); + } + } + } + catch + { + CancelIo(_writeHandle); + throw; + } + finally + { + safeWaitHandle.DangerousRelease(); + waitHandle.Close(); + } + } + + int[] GetTimingDataFromPacket(byte[] packet) + { + List<int> timingData = new List<int>(); + + int len = 0; + + for (int index = 0; index < packet.Length; ) + { + byte curByte = packet[index]; + + if (curByte == 0x9F) + break; + + if (curByte < 0x81 || curByte > 0x8F) + return null; + + int bytes = curByte & 0x7F; + int j; + + for (j = index + 1; j < index + bytes + 1; j++) + { + curByte = packet[j]; + + if ((curByte & 0x80) != 0) + len += (int)(curByte & 0x7F); + else + len -= (int)curByte; + + if ((curByte & 0x7F) != 0x7F) + { + timingData.Add(len * 50); + len = 0; + } + } + + index = j; + } + + if (len != 0) + timingData.Add(len * 50); + + return timingData.ToArray(); + } + + void GetIrCodeLengths(IrCode code, out int onTime, out int onCount) + { + onTime = 0; + onCount = 0; + + foreach (int time in code.TimingData) + { + if (time > 0) + { + onTime += time; + onCount++; + } + } + } + + #endregion Implementation + + } + +} Added: trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverVista.cs =================================================================== --- trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverVista.cs (rev 0) +++ trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverVista.cs 2007-09-05 17:23:54 UTC (rev 895) @@ -0,0 +1,860 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.ServiceProcess; +using System.Text; +using System.Threading; + +using Microsoft.Win32.SafeHandles; + +namespace MicrosoftMceTransceiver +{ + + public class DriverVista : Driver + { + + #region Constants + + // Device variables + const int DeviceBufferSize = 100; + const int PacketTimeout = 100; + const int WriteSyncTimeout = 5000; + + #endregion Constants + + #region Enumerations + + public enum IoCtrl : uint + { + StartReceive = 0x0F608028, + StopReceive = 0x0F60802C, + GetDetails = 0x0F604004, + GetBlasters = 0x0F604008, + Receive = 0x0F604022, + Transmit = 0x0F608015, + } + + /// <summary> + /// IR Device Capability Flags. + /// </summary> + [Flags] + public enum DeviceCapabilityFlags : uint + { + /// <summary> + /// Hardware supports legacy key signing. + /// </summary> + LegacySigning = 0x0001, + /// <summary> + /// Hardware has unique serial number. + /// </summary> + SerialNumber = 0x0002, + /// <summary> + /// Can hardware flash LED to identify receiver? + /// </summary> + FlashLed = 0x0004, + /// <summary> + /// Is this a legacy device? + /// </summary> + Legacy = 0x0008, + /// <summary> + /// Device can wake from S1. + /// </summary> + WakeS1 = 0x0010, + /// <summary> + /// Device can wake from S2. + /// </summary> + WakeS2 = 0x0020, + /// <summary> + /// Device can wake from S3. + /// </summary> + WakeS3 = 0x0040, + /// <summary> + /// Device can wake from S4. + /// </summary> + WakeS4 = 0x0080, + /// <summary> + /// Device can wake from S5. + /// </summary> + WakeS5 = 0x0100, + } + + [Flags] + public enum TransmitFlags : uint + { + /// <summary> + /// Pulse Mode. + /// </summary> + PulseMode = 0x01, + /// <summary> + /// DC Mode. + /// </summary> + DCMode = 0x02, + } + + /// <summary> + /// Read Thread Mode. + /// </summary> + enum ReadThreadMode + { + Receiving, + Learning, + LearningDone, + LearningFailed, + Stop, + } + + #endregion Enumerations + + #region Structures + + [StructLayout(LayoutKind.Sequential)] + public struct TransmitChunk + { + /// <summary> + /// Next chunk offset. + /// </summary> + public uint OffsetToNextChunk; + /// <summary> + /// Repeat count. + /// </summary> + public uint RepeatCount; + /// <summary> + /// Number of bytes. + /// </summary> + public uint ByteCount; + } + + [StructLayout(LayoutKind.Sequential)] + public struct TransmitParams + { + /// <summary> + /// Bitmask containing ports to transmit on. + /// </summary> + public uint TransmitPortMask; + /// <summary> + /// Carrier period. + /// </summary> + public uint CarrierPeriod; + /// <summary> + /// Transmit Flags. + /// </summary> + [MarshalAs(UnmanagedType.U4)] + public TransmitFlags Flags; + /// <summary> + /// Pulse Size. If Pulse Mode Flag set. + /// </summary> + public uint PulseSize; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ReceiveParams + { + /// <summary> + /// Last packet in block? + /// </summary> + public uint DataEnd; + /// <summary> + /// Number of bytes in block. + /// </summary> + public uint ByteCount; + /// <summary> + /// Carrier frequency of IR received. + /// </summary> + public uint CarrierFrequency; + } + + [StructLayout(LayoutKind.Sequential)] + public struct StartReceiveParams + { + /// <summary> + /// Index of the receiver to use. + /// </summary> + public uint Receiver; + /// <summary> + /// Receive timeout, in milliseconds? + /// </summary> + public uint Timeout; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DeviceCapabilities + { + /// <summary> + /// Protocol version. Currently must be 100 (1.0). + /// </summary> + public uint ProtocolVersion; + /// <summary> + /// Number of transmit ports \x96 0-32. + /// </summary> + public uint TransmitPorts; + /// <summary> + /// Number of receive ports \x96 0-32 (For beanbag, this is two (one for learning, one for normal). + /// </summary> + public uint ReceivePorts; + /// <summary> + /// Bitmask identifying which receivers are learning receivers \x96 low bit is the first receiver, second-low bit is the second receiver, etc ... + /// </summary> + public uint LearningMask; + /// <summary> + /// Device flags. + /// </summary> + [MarshalAs(UnmanagedType.U4)] + public DeviceCapabilityFlags DetailsFlags; + } + + #endregion Structures + + #region Interop + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool DeviceIoControl( + SafeFileHandle handle, + [MarshalAs(UnmanagedType.U4)] IoCtrl ioControlCode, + IntPtr inBuffer, int inBufferSize, + IntPtr outBuffer, int outBufferSize, + out int bytesReturned, + ref NativeOverlapped overlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool DeviceIoControl( + SafeFileHandle handle, + [MarshalAs(UnmanagedType.U4)] IoCtrl ioControlCode, + IntPtr inBuffer, int inBufferSize, + IntPtr outBuffer, int outBufferSize, + out int bytesReturned, + IntPtr overlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetOverlappedResult( + SafeFileHandle handle, + ref NativeOverlapped overlapped, + out int bytesTransferred, + bool wait); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern SafeFileHandle CreateFile( + [MarshalAs(UnmanagedType.LPTStr)] string fileName, + [MarshalAs(UnmanagedType.U4)] FileAccessTypes fileAccess, + [MarshalAs(UnmanagedType.U4)] FileShares fileShare, + IntPtr securityAttributes, + [MarshalAs(UnmanagedType.U4)] CreationDisposition creationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes flags, + IntPtr templateFile); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CancelIo( + SafeFileHandle handle); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle( + SafeFileHandle handle); + + #endregion Interop + + #region Variables + + #region Device Details + + uint _numTxPorts = 0; + uint _numRxPorts = 0; + uint _learnPortMask = 0; + bool _legacyDevice = false; + bool _canFlashLed = false; + + bool[] _blasters; + + uint _receivePort = 0; + uint _learnPort = 0; + + #endregion Device Details + + NotifyWindow _notifyWindow; + + SafeFileHandle _eHomeHandle; + + Thread _readThread; + ReadThreadMode _readThreadMode; + + IrCode _learningCode; + + //StreamWriter _debugFile; + + #endregion Variables + + #region Constructor + + public DriverVista(Guid deviceGuid, string devicePath, RemoteCallback remoteCallback, KeyboardCallback keyboardCallback, MouseCallback mouseCallback) + : base(deviceGuid, devicePath, remoteCallback, keyboardCallback, mouseCallback) + { + + } + + #endregion Constructor + + #region Device Control Functions + + void StartReceive(uint receivePort, uint timeout) + { + int bytesReturned; + + StartReceiveParams structure; + structure.Receiver = receivePort; + structure.Timeout = timeout; + + IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structure)); + + try + { + Marshal.StructureToPtr(structure, structPtr, false); + + IoControlSync(IoCtrl.StartReceive, structPtr, Marshal.SizeOf(structure), IntPtr.Zero, 0, out bytesReturned); + } + catch + { + throw; + } + finally + { + Marshal.FreeHGlobal(structPtr); + } + } + + void StopReceive() + { + int bytesReturned; + IoControlSync(IoCtrl.StopReceive, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned); + } + + void GetDeviceCapabilities() + { + int bytesReturned; + + DeviceCapabilities structure = new DeviceCapabilities(); + + IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(structure)); + + try + { + Marshal.StructureToPtr(structure, structPtr, false); + + IoControlSync(IoCtrl.GetDetails, IntPtr.Zero, 0, structPtr, Marshal.SizeOf(structure), out bytesReturned); + + structure = (DeviceCapabilities)Marshal.PtrToStructure(structPtr, typeof(DeviceCapabilities)); + } + catch + { + throw; + } + finally + { + Marshal.FreeHGlobal(structPtr); + } + + _numTxPorts = structure.TransmitPorts; + _numRxPorts = structure.ReceivePorts; + _learnPortMask = structure.LearningMask; + + int receivePort = FirstLowBit(_learnPortMask); + if (receivePort != -1) + _receivePort = (uint)receivePort; + + int learnPort = FirstHighBit(_learnPortMask); + if (learnPort != -1) + _learnPort = (uint)learnPort; + else + _learnPort = _receivePort; + + DeviceCapabilityFlags flags = structure.DetailsFlags; + _legacyDevice = (int)(flags & DeviceCapabilityFlags.Legacy) != 0; + _canFlashLed = (int)(flags & DeviceCapabilityFlags.FlashLed) != 0; + } + + void GetBlasters() + { + int bytesReturned; + + if (_numTxPorts == 0) + return; + + _blasters = new bool[_numTxPorts]; + for (int i = 0; i < _blasters.Length; i++) + _blasters[i] = false; + + uint data = 0; + + IntPtr pointerToData = Marshal.AllocHGlobal(sizeof(uint)); + + try + { + Marshal.StructureToPtr(data, pointerToData, false); + + IoControlSync(IoCtrl.GetBlasters, IntPtr.Zero, 0, pointerToData, sizeof(uint), out bytesReturned); + + data = (uint)Marshal.PtrToStructure(pointerToData, typeof(uint)); + } + catch + { + throw; + } + finally + { + Marshal.FreeHGlobal(pointerToData); + } + + for (int j = 0; j < _blasters.Length; j++) + _blasters[j] = ((data & (((int)1) << j)) != 0); + } + + void TransmitIR(byte[] irData, int carrier, uint transmitPortMask) + { + int bytesReturned; + + TransmitParams transmitParams = new TransmitParams(); + transmitParams.TransmitPortMask = transmitPortMask; + + if (carrier == IrCode.CarrierFrequencyUnknown) + carrier = IrCode.CarrierFrequencyDefault; + + if (IsPulseMode((uint)carrier)) + { + transmitParams.Flags = TransmitFlags.PulseMode; + transmitParams.PulseSize = (uint)carrier; + } + else + { + //transmitParams.Flags = TransmitFlags.DCMode; + transmitParams.CarrierPeriod = GetCarrierPeriod((uint)carrier); + } + + TransmitChunk transmitChunk = new TransmitChunk(); + transmitChunk.OffsetToNextChunk = 0; + transmitChunk.RepeatCount = 1; + transmitChunk.ByteCount = (uint)irData.Length; + + int bufferSize = irData.Length + Marshal.SizeOf(typeof(TransmitChunk)) + 8; + byte[] buffer = new byte[bufferSize]; + + byte[] rawTransmitChunk = RawSerialize(transmitChunk); + Array.Copy(rawTransmitChunk, buffer, rawTransmitChunk.Length); + + Array.Copy(irData, 0, buffer, rawTransmitChunk.Length, irData.Length); + + IntPtr structurePtr = Marshal.AllocHGlobal(Marshal.SizeOf(transmitParams)); + IntPtr bufferPtr = Marshal.AllocHGlobal(buffer.Length); + + try + { + Marshal.StructureToPtr(transmitParams, structurePtr, true); + + Marshal.Copy(buffer, 0, bufferPtr, buffer.Length); + + IoControlSync(IoCtrl.Transmit, structurePtr, Marshal.SizeOf(typeof(TransmitParams)), bufferPtr, bufferSize, out bytesReturned); + } + catch + { + throw; + } + finally + { + Marshal.FreeHGlobal(structurePtr); + Marshal.FreeHGlobal(bufferPtr); + } + } + + void IoControlSync(IoCtrl ioControlCode, IntPtr inBuffer, int inBufferSize, IntPtr outBuffer, int outBufferSize, out int bytesReturned) + { + NativeOverlapped overlapped; + overlapped.InternalLow = IntPtr.Zero; + overlapped.InternalHigh = IntPtr.Zero; + overlapped.OffsetLow = 0; + overlapped.OffsetHigh = 0; + + try + { + int lastError; + + using (WaitHandle waitHandle = new ManualResetEvent(false)) + { + overlapped.EventHandle = waitHandle.SafeWaitHandle.DangerousGetHandle(); + + if (!DeviceIoControl(_eHomeHandle, ioControlCode, inBuffer, inBufferSize, outBuffer, outBufferSize, out bytesReturned, ref overlapped)) + { + lastError = Marshal.GetLastWin32Error(); + if (lastError != Win32ErrorCodes.ERROR_IO_PENDING) + throw new Win32Exception(lastError); + + waitHandle.WaitOne(); + + if (!GetOverlappedResult(_eHomeHandle, ref overlapped, out bytesReturned, false)) + { + lastError = Marshal.GetLastWin32Error(); + throw new Win32Exception(lastError); + } + } + } + + } + catch + { + CancelIo(_eHomeHandle); + throw; + } + } + + #endregion Device Control Functions + + #region Driver overrides + + public override void Start() + { + //_debugFile = new StreamWriter("\\DriverVista.log", false); + //_debugFile.AutoFlush = true; + + _notifyWindow = new NotifyWindow(); + _notifyWindow.Class = _deviceGuid; + + _eHomeHandle = CreateFile(_devicePath, FileAccessTypes.GenericRead | FileAccessTypes.GenericWrite, FileShares.None, IntPtr.Zero, CreationDisposition.OpenExisting, FileAttributes.Overlapped, IntPtr.Zero); + int lastError = Marshal.GetLastWin32Error(); + if (_eHomeHandle.IsInvalid) + throw new Win32Exception(lastError); + + GetAllDeviceInformation(); + + StartReceive(_receivePort, PacketTimeout); + + _readThreadMode = ReadThreadMode.Receiving; + + StartReadThread(); + + _notifyWindow.Create(); + _notifyWindow.DeviceArrival += new DeviceEventHandler(OnDeviceArrival); + _notifyWindow.DeviceRemoval += new DeviceEventHandler(OnDeviceRemoval); + _notifyWindow.RegisterDeviceRemoval(_eHomeHandle.DangerousGetHandle()); + } + public override void Stop() + { + OnDeviceRemoval(); + + CloseDevice(); + + //_debugFile.Close(); + } + + public override IrCode Learn(int learnTimeout) + { + //_debugFile.WriteLine("Learn"); + + StopReadThread(); + + _learningCode = new IrCode(); + + StartReceive(_learnPort, PacketTimeout); + + _readThreadMode = ReadThreadMode.Learning; + + StartReadThread(); + + int learnStartTick = Environment.TickCount; + + // Wait for the learning to finish ... + while (_readThreadMode == ReadThreadMode.Learning && Environment.TickCount < learnStartTick + learnTimeout) + Thread.Sleep(PacketTimeout); + + //_debugFile.WriteLine("End Learn"); + + ReadThreadMode modeWas = _readThreadMode; + + StopReadThread(); + + StartReceive(_receivePort, PacketTimeout); + + _readThreadMode = ReadThreadMode.Receiving; + + StartReadThread(); + + switch (modeWas) + { + case ReadThreadMode.Learning: + // Timeout. + return null; + + case ReadThreadMode.LearningFailed: + // Failure. + return null; + + case ReadThreadMode.LearningDone: + //_debugFile.WriteLine(_learningCode.ToByteArray()); + + if (_learningCode.FinalizeData()) + return _learningCode; // Success. + else + return null; // Failure. + + default: + return null; + } + } + + public override void Send(IrCode code, uint port) + { + byte[] data = DataPacket(code); + + TransmitIR(data, code.Carrier, port); + } + + #endregion Driver overrides + + #region Implementation + + byte[] DataPacket(IrCode code) + { + if (code.TimingData.Length == 0) + return null; + + byte[] data = new byte[code.TimingData.Length * 4]; + + int dataIndex = 0; + for (int timeIndex = 0; timeIndex < code.TimingData.Length; timeIndex++) + { + uint time = (uint)(50 * (int)Math.Round((double)code.TimingData[timeIndex] / 50)); + + for (int timeShift = 0; timeShift < 4; timeShift++) + { + data[dataIndex++] = (byte)(time & 0xFF); + time >>= 8; + } + } + + return data; + } + + void GetAllDeviceInformation() + { + GetDeviceCapabilities(); + GetBlasters(); + } + + void StartReadThread() + { + _readThread = new Thread(new ThreadStart(ReadThread)); + _readThread.IsBackground = true; + _readThread.Name = "IR Server Microsoft MCE Transceiver Read"; + _readThread.Start(); + } + void StopReadThread() + { + if (_readThread != null) + { + _readThreadMode = ReadThreadMode.Stop; + + _readThread.Abort(); + + if (Thread.CurrentThread != _readThread) + _readThread.Join(); + + _readThread = null; + } + } + + void CloseDevice() + { + if (_eHomeHandle != null) + CloseHandle(_eHomeHandle); + } + + void OnDeviceArrival() + { + _notifyWindow.UnregisterDeviceArrival(); + + StartReceive(_receivePort, PacketTimeout); + + _readThreadMode = ReadThreadMode.Receiving; + + StartReadThread(); + + _notifyWindow.RegisterDeviceRemoval(_eHomeHandle.DangerousGetHandle()); + } + void OnDeviceRemoval() + { + _notifyWindow.UnregisterDeviceRemoval(); + _notifyWindow.RegisterDeviceArrival(); + + StopReadThread(); + } + + void ReadThread() + { + int bytesRead; + TimeSpan sinceLastPacket; + DateTime lastPacketTime = DateTime.Now; + + IntPtr deviceBufferPtr = IntPtr.Zero; + IntPtr receiveParamsPtr = IntPtr.Zero; + + try + { + deviceBufferPtr = Marshal.AllocHGlobal(DeviceBufferSize); + + int receiveParamsSize = Marshal.SizeOf(typeof(ReceiveParams)) + DeviceBufferSize + 8; + receiveParamsPtr = Marshal.AllocHGlobal(receiveParamsSize); + + ReceiveParams receiveParams = new ReceiveParams(); + receiveParams.ByteCount = DeviceBufferSize; + Marshal.StructureToPtr(receiveParams, receiveParamsPtr, false); + + while (_readThreadMode != ReadThreadMode.Stop) + { + IoControlSync(IoCtrl.Receive, IntPtr.Zero, 0, receiveParamsPtr, receiveParamsSize, out bytesRead); + + if (bytesRead > Marshal.SizeOf(receiveParams)) + { + int dataSize = bytesRead; + + bytesRead -= Marshal.SizeOf(receiveParams); + + sinceLastPacket = DateTime.Now.Subtract(lastPacketTime); + if (sinceLastPacket.TotalMilliseconds >= PacketTimeout + 50) + IrDecoder.DecodeIR(null, null, null, null); + + lastPacketTime = DateTime.Now; + + byte[] packetBytes = new byte[bytesRead]; + byte[] dataBytes = new byte[dataSize]; + + Marshal.Copy(receiveParamsPtr, dataBytes, 0, dataSize); + Array.Copy(dataBytes, dataSize - bytesRead, packetBytes, 0, bytesRead); + + int[] timingData = GetTimingDataFromPacket(packetBytes); + + //_debugFile.WriteLine("Received:"); + //Dump(timingData); + + if (_readThreadMode == ReadThreadMode.Learning) + _learningCode.AddTimingData(timingData); + else + IrDecoder.DecodeIR(timingData, _remoteCallback, _keyboardCallback, _mouseCallback); + } + + // Determine carrier frequency when learning ... + if (_readThreadMode == ReadThreadMode.Learning && bytesRead >= Marshal.SizeOf(receiveParams)) + { + ReceiveParams receiveParams2 = (ReceiveParams)Marshal.PtrToStructure(receiveParamsPtr, typeof(ReceiveParams)); + + if (receiveParams2.DataEnd != 0 && receiveParams2.CarrierFrequency != 0) + { + _learningCode.Carrier = (int)receiveParams2.CarrierFrequency; + _readThreadMode = ReadThreadMode.LearningDone; + } + } + } + } + catch + { + CancelIo(_eHomeHandle); + } + finally + { + StopReceive(); + + if (deviceBufferPtr != IntPtr.Zero) + Marshal.FreeHGlobal(deviceBufferPtr); + + if (receiveParamsPtr != IntPtr.Zero) + Marshal.FreeHGlobal(receiveParamsPtr); + } + } + + #endregion Implementation + + #region Misc Methods + + static byte[] RawSerialize(object anything) + { + int rawSize = Marshal.SizeOf(anything); + byte[] rawData = new byte[rawSize]; + + GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned); + IntPtr buffer = handle.AddrOfPinnedObject(); + + Marshal.StructureToPtr(anything, buffer, false); + + handle.Free(); + + return rawData; + } + + static byte ConvertBcdToByte(byte b) + { + return (byte)(((b >> 4) * 10) + (b & 15)); + } + + int FirstHighBit(uint mask) + { + for (int i = 0; i < 32; i++) + if ((mask & (1 << i)) != 0) + return i; + + return -1; + } + int FirstLowBit(uint mask) + { + for (int i = 0; i < 32; i++) + if ((mask & (1 << i)) == 0) + return i; + + return -1; + } + + static uint GetCarrierPeriod(uint carrier) + { + return (uint)(1000000 / carrier); + } + + static bool IsPulseMode(uint carrier) + { + if (carrier > 0 && carrier < 100) + return true; + + return false; + } + + static int[] GetTimingDataFromPacket(byte[] packetBytes) + { + int[] timingData = new int[packetBytes.Length / 4]; + + int timingDataIndex = 0; + + for (int index = 0; index < packetBytes.Length; index += 4) + timingData[timingDataIndex++] = + (int) + (packetBytes[index] + + (packetBytes[index + 1] << 8) + + (packetBytes[index + 2] << 16) + + (packetBytes[index + 3] << 24)); + + return timingData; + } + /* + void Dump(Array array) + { + foreach (object item in array) + { + _debugFile.Write(item); + _debugFile.Write(", "); + } + + _debugFile.WriteLine(); + } + */ + #endregion Misc Methods + + } + +} Added: trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverXP.cs =================================================================== --- trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverXP.cs (rev 0) +++ trunk/plugins/IR Server Suite/IR Server Plugins/Microsoft MCE Transceiver/DriverXP.cs 2007-09-05 17:23:54 UTC (rev 895) @@ -0,0 +1,750 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.ServiceProcess; +using System.Text; +using System.Threading; + +using Microsoft.Win32.SafeHandles; + +namespace MicrosoftMceTransceiver +{ + + public class DriverXP : Driver + { + + #region Interop + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetOverlappedResult( + SafeFileHandle handle, + ref NativeOverlapped overlapped, + out int bytesTransferred, + bool wait); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern SafeFileHandle CreateFile( + [MarshalAs(UnmanagedType.LPTStr)] string fileName, + [MarshalAs(UnmanagedType.U4)] FileAccessTypes fileAccess, + [MarshalAs(UnmanagedType.U4)] FileShares fileShare, + IntPtr securityAttributes, + [MarshalAs(UnmanagedType.U4)] CreationDisposition creationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes flags, + IntPtr templateFile); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ReadFile( + SafeFileHandle handle, + IntPtr buffer, + int bytesToRead, + out int bytesRead, + ref NativeOverlapped overlapped); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool WriteFile( + SafeFileHandle handle, + byte[] buffer, + int bytesToWrite, + out int bytesWritten, + ref NativeOverlapped overlapped); + + [DllImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CancelIo( + SafeFileHandle handle); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle( + SafeFileHandle handle); + + #endregion Interop + + #region Enumerations + + /// <summary> + /// Type of device in use. + /// This is used to determine the blaster port selection method. + /// </summary> + enum DeviceType + { + /// <summary> + /// Device is a first party Microsoft MCE transceiver. + /// </summary> + Microsoft = 0, + /// <summary> + /// Device is an third party SMK or Topseed MCE transceiver. + /// </summary> + SmkTopseed = 1, + } + + /// <summary> + /// Device input port. + /// </summary> + enum InputPort + { + Receive = 0, + Learning = 1, + } + + /// <summary> + /// Read Thread Mode. + /// </summary> + enum ReadThreadMode + { + Receiving, + Learning, + LearningDone, + LearningFailed, + Stop, + } + + #endregion Enumerations + + #region Constants + + // Vendor ID's for SMK and Topseed devices. + const string VidSMK = "vid_1784"; + const string VidTopseed = "vid_0609"; + + // Device variables + const int DeviceBufferSize = 100; + const int PacketTimeout = 100; + const int WriteSyncTimeout = 5000; + + // Microsoft Port Packets + static readonly byte[][] MicrosoftPorts = new byte[][] + { + new byte[] { 0x9F, 0x08, 0x06 }, // Both + new byte[] { 0x9F, 0x08, 0x04 }, // 1 + new byte[] { 0x9F, 0x08, 0x02 }, // 2 + }; + + // SMK or Topseed Port Packets + static readonly byte[][] SmkTopseedPorts = new byte[][] + { + new byte[] { 0x9F, 0x08, 0x00 }, // Both + new byte[] { 0x9F, 0x08, 0x01 }, // 1 + new byte[] { 0x9F, 0x08, 0x02 }, // 2 + }; + + // Start and Stop Packets + static readonly byte[] StartPacket = { 0x00, 0xFF, 0xAA }; + static readonly byte[] StopPacket = { 0xFF, 0xBB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + // Misc Packets + static readonly byte[] SetCarrierFreqPacket = { 0x9F, 0x06, 0x01, 0x80 }; + static readonly byte[] SetInputPortPacket = { 0x9F, 0x14, 0x00 }; + static readonly byte[] SetTimeoutPacket = { 0x9F, 0x0C, 0x00, 0x00 }; + + #endregion Constants + + #region Variables + + NotifyWindow _notifyWindow; + + SafeFileHandle _eHomeHandle; + + Thread _readThread; + ReadThreadMode _readThreadMode; + ManualResetEvent _stopReadThread; + + IrCode _learningCode; + + DeviceType _deviceType = DeviceType.Microsoft; + + //StreamWriter _debugFile; + + #endregion Variables + + #region Constructor + + public DriverXP(Guid deviceGuid, string devicePath, RemoteCallback remoteCallback, KeyboardCallback keyboardCallback, MouseCallback mouseCallback) + : base(deviceGuid, devicePath, remoteCallback, keyboardCallback, mouseCallback) + { + if (devicePath.IndexOf(VidSMK, StringComparison.InvariantCultureIgnoreCase) != -1 || devicePath.IndexOf(VidTopseed, StringComparison.InvariantCultureIgnoreCase) != -1) + _deviceType = DeviceType.SmkTopseed; + else + _deviceType = DeviceType.Microsoft; + } + + #endregion Constructor + + #region Driver overrides + + public override void Start() + { + //_debugFile = new StreamWriter("\\DriverXP.log", false); + //_debugFile.AutoFlush = true; + + _notifyWindow = new NotifyWindow(); + _notifyWindow.Class = _deviceGuid; + + int lastError; + + _eHomeHandle = CreateFile(_devicePath, FileAccessTypes.GenericRead | FileAccessTypes.GenericWrite, FileShares.None, IntPtr.Zero, CreationDisposition.OpenExisting, FileAttributes.Overlapped, IntPtr.Zero); + lastError = Marshal.GetLastWin32Error(); + if (_eHomeHandle.IsInvalid) + throw new Win32Exception(lastError); + + // Initialize device ... + WriteSync(StartPacket); + SetTimeout(PacketTimeout); + SetInputPort(InputPort.Receive); + + StartReadThread(); + + _notifyWindow.Create(); + _notifyWindow.DeviceArrival += new DeviceEventHandler(OnDeviceArrival); + _notifyWindow.DeviceRemoval += new DeviceEventHandler(OnDeviceRemoval); + _notifyWindow.RegisterDeviceRemoval(_eHomeHandle.DangerousGetHandle()); + } + + public override void Stop() + { + WriteSync(StopPacket); + + OnDeviceRemoval(); + + CloseDevice(); + + //_debugFile.Close(); + } + + public override IrCode Learn(int learnTimeout) + { + //_debugFile.WriteLine("Start Learn"); + + _learningCode = new IrCode(); + + SetInputPort(InputPort.Learning); + + int learnStartTick = Environment.TickCount; + _readThreadMode = ReadThreadMode.Learning; + + // Wait for the learning to finish ... + while (_readThreadMode == ReadThreadMode.Learning && Environment.TickCount < learnStartTick + learnTimeout) + Thread.Sleep(100); + + //_debugFile.WriteLine("End Learn"); + + ReadThreadMode modeWas = _readThreadMode; + + _readThreadMode = ReadThreadMode.Receiving; + SetInputPort(InputPort.Receive); + + switch (modeWas) + { + case ReadThreadMode.Learning: + // Timeout. + return null; + + case ReadThreadMode.LearningFailed: + // Failure. + return null; + + case ReadThreadMode.LearningDone: + //_debugFile.WriteLine(_learningCode.ToByteArray()); + + if (_learningCode.FinalizeData()) + return _learningCode; // Success. + else + return null; // Failure. + + default: + return null; + } + } + + public override void Send(IrCode code, uint port) + { + //_debugFile.WriteLine("Send"); + + byte[] portPacket; + switch (_deviceType) + { + case DeviceType.Microsoft: portPacket = MicrosoftPorts[port]; break; + case DeviceType.SmkTopseed: portPacket = SmkTopseedPorts[port]; break; + default: + throw new Exception("Invalid device type"); + } + + //_debugFile.WriteLine(code.ToByteArray()); + + // Set port + WriteSync(portPacket); + + // Set carrier frequency + WriteSync(CarrierPacket(code)); + + // Send packet + WriteSync(DataPacket(code)); + } + + #endregion Driver overrides + + #region Implementation + + byte[] CarrierPacket(IrCode code) + { + byte[] carrierPacket = new byte[SetCarrierFreqPacket.Length]; + SetCarrierFreqPacket.CopyTo(carrierPacket, 0); + + if (code.Carrier == IrCode.CarrierFrequencyUnknown || code.Carrier == IrCode.CarrierFrequencyDCMode) + return carrierPacket; + + for (int scaler = 1; scaler <= 4; scaler++) + { + int divisor = (10000000 >> (2 * scaler)) / code.Carrier; + + if (divisor <= 0xFF) + { + carrierPacket[2] = (byte)scaler; + carrierPacket[3] = (byte)divisor; + break; + } + } + + return carrierPacket; + } + + byte[] DataPacket(IrCode code) + { + if (code.TimingData.Length == 0) + return null; + + // Construct data bytes into "packet" ... + List<byte> packet = new List<byte>(); + + for (int index = 0; index < code.TimingData.Length; index++) + { + double time = (double)code.TimingData[index]; + + byte duration = (byte)Math.Abs(Math.Round(time / 50)); + bool pulse = (time > 0); + + while (duration > 0x7F) + { + packet.Add((byte)(pulse ? 0xFF : 0x7F)); + + duration -= 0x7F; + } + + packet.Add((byte)(pulse ? 0x80 | duration : duration)); + } + + // Insert byte count markers into packet data bytes ... + int subpackets = (int)Math.Ceiling(packet.Count / (double)4); + + byte[] output = new byte[packet.Count + subpackets + 1]; + + int outputPos = 0; + + for (int packetPos = 0; packetPos < packet.Count; ) + { + byte copyCount = (byte)(packet.Count - packetPos < 4 ? packet.Count - packetPos : 0x04); + + output[outputPos++] = (byte)(0x80 | copyCount); + + for (int index = 0; index < copyCount; index++) + output[outputPos++] = packet[packetPos++]; + } + + output[outputPos] = 0x80; + + return output; + } + + /// <summary> + /// Set the receive buffer timeout. + /// </summary> + /// <param name="timeout">Packet timeout (in milliseconds).</param> + void SetTimeout(int timeout) + { + byte[] timeoutPacket = new byte[SetTimeoutPacket.Length]; + SetTimeoutPacket.CopyTo(timeoutPacket, 0); + + int timeoutSamples = 20 * timeout; + + timeoutPacket[2] = (byte)(timeoutSamples >> 8); + timeoutPacket[3] = (byte)(timeoutSamples % 256); + + WriteSync(timeoutPacket); + } + + void SetInputPort(InputPort port) + { + byte[] inputPortPacket = new byte[SetInputPortPacket.Length]; + SetInputPortPacket.CopyTo(inputPortPacket, 0); + + inputPortPacket[2] = (byte)(port + 1); + + WriteSync(inputPortPacket); + } + + void StartReadThread() + { + _stopReadThread = new ManualResetEvent(false); + _readThreadMode = ReadThreadMode.Receiving; + + _readThread = new Th... [truncated message content] |