/// <summary>
        /// Synchronously write a packet of bytes to the device.
        /// </summary>
        /// <param name="data">Packet to write to device.</param>
        private void WriteSync(byte[] data)
        {
            Log.Trace("WriteSync({0}): ", data.Length);
            Log.WriteArray(LogLevel.Trace, data);

            if (!_deviceAvailable)
                throw new InvalidOperationException("Device not available");

            WaitHandle waitHandle = new ManualResetEvent(false);
            SafeHandle safeWaitHandle = waitHandle.SafeWaitHandle;
            var waitHandles = new WaitHandle[] { waitHandle };

            try
            {
                int lastError;

                var success = false;
                safeWaitHandle.DangerousAddRef(ref success);
                if (!success)
                    throw new InvalidOperationException("Failed to initialize safe wait handle");

                var overlapped = new DeviceIoOverlapped();
                overlapped.ClearAndSetEvent(safeWaitHandle.DangerousGetHandle());

                int bytesWritten;
                var writeDevice = WriteFile(_writeHandle, data, data.Length, out bytesWritten, overlapped.Overlapped);
                lastError = Marshal.GetLastWin32Error();

                if (writeDevice)
                    return;

                if (lastError != ErrorSuccess && lastError != ErrorIoPending)
                    throw new Win32Exception(lastError);

                var handle = WaitHandle.WaitAny(waitHandles, WriteSyncTimeout, false);

                if (handle == ErrorWaitTimeout)
                    throw new TimeoutException("Timeout trying to write data to device");

                if (handle != 0)
                    throw new InvalidOperationException(String.Format("Invalid wait handle return: {0}", handle));

                var getOverlapped = GetOverlappedResult(_writeHandle, overlapped.Overlapped, out bytesWritten, true);
                lastError = Marshal.GetLastWin32Error();

                if (!getOverlapped && lastError != ErrorSuccess)
                    throw new Win32Exception(lastError);

                Thread.Sleep(PacketTimeout);
            }
            catch
            {
                if (_writeHandle != null)
                    CancelIo(_writeHandle);

                throw;
            }
            finally
            {
                safeWaitHandle.DangerousRelease();
                waitHandle.Close();
            }
        }
        /// <summary>
        /// Device read thread method.
        /// </summary>
        private void ReadThread()
        {
            byte[] packetBytes;

            WaitHandle waitHandle = new ManualResetEvent(false);
            SafeHandle safeWaitHandle = waitHandle.SafeWaitHandle;
            var waitHandles = new WaitHandle[] { waitHandle, _stopReadThread };

            var deviceBufferPtr = IntPtr.Zero;

            var success = false;
            safeWaitHandle.DangerousAddRef(ref success);
            if (!success)
                throw new InvalidOperationException("Failed to initialize safe wait handle");

            try
            {
                var dangerousWaitHandle = safeWaitHandle.DangerousGetHandle();

                var overlapped = new DeviceIoOverlapped();
                overlapped.ClearAndSetEvent(dangerousWaitHandle);

                deviceBufferPtr = Marshal.AllocHGlobal(DeviceBufferSize);

                while (_readThreadMode != ReadThreadMode.Stop)
                {
                    int lastError;
                    int bytesRead;

                    var readDevice = ReadFile(_readHandle, deviceBufferPtr, DeviceBufferSize, out bytesRead,
                                              overlapped.Overlapped);
                    lastError = Marshal.GetLastWin32Error();

                    switch (_readThreadMode)
                    {
                        case ReadThreadMode.Receiving:
                            FireStateChanged(new StateChangedEventArgs(RunningState.Started, ReceivingState.Receiving));
                            break;
                        case ReadThreadMode.Learning:
                            FireStateChanged(new StateChangedEventArgs(RunningState.Started, ReceivingState.Learning));
                            break;
                    }

                    if (!readDevice)
                    {
                        if (lastError != ErrorSuccess && lastError != ErrorIoPending)
                            throw new Win32Exception(lastError);

                        while (true)
                        {
                            var handle = WaitHandle.WaitAny(waitHandles, 2*PacketTimeout, false);

                            if (handle == ErrorWaitTimeout)
                                continue;

                            if (handle == 0)
                                break;

                            if (handle == 1)
                                throw new ThreadInterruptedException("Read thread stopping by request");

                            throw new InvalidOperationException(String.Format("Invalid wait handle return: {0}", handle));
                        }

                        var getOverlapped = GetOverlappedResult(_readHandle, overlapped.Overlapped, out bytesRead, true);
                        lastError = Marshal.GetLastWin32Error();

                        if (!getOverlapped && lastError != ErrorSuccess)
                            throw new Win32Exception(lastError);
                    }

                    if (bytesRead == 0)
                        continue;

                    packetBytes = new byte[bytesRead];
                    Marshal.Copy(deviceBufferPtr, packetBytes, 0, bytesRead);

                    Log.Trace("Received bytes ({0}): ", bytesRead);
                    Log.WriteArray(LogLevel.Trace, packetBytes);

                    int[] timingData = null;

                    if (_decodeCarry != 0 || packetBytes[0] >= 0x81 && packetBytes[0] <= 0x9E)
                    {
                        timingData = GetTimingDataFromPacket(packetBytes);
                    }
                    else
                    {
                        double firmware = 0.0;

                        var indexOfFF = Array.IndexOf(packetBytes, (byte) 0xFF);
                        while (indexOfFF != -1)
                        {
                            if (packetBytes.Length > indexOfFF + 2 && packetBytes[indexOfFF + 1] == 0x0B)
                                // FF 0B XY - Firmware X.Y00
                            {
                                byte b1 = packetBytes[indexOfFF + 2];

                                firmware += (b1 >> 4) + (0.1*(b1 & 0x0F));
                                Log.Debug("Firmware: {0}", firmware);
                            }

                            if (packetBytes.Length > indexOfFF + 2 && packetBytes[indexOfFF + 1] == 0x1B)
                                // FF 1B XY - Firmware 0.0XY
                            {
                                byte b1 = packetBytes[indexOfFF + 2];

                                firmware += (0.01*(b1 >> 4)) + (0.001*(b1 & 0x0F));
                                Log.Debug("Firmware: {0}", firmware);
                            }

                            if (packetBytes.Length > indexOfFF + 1)
                                indexOfFF = Array.IndexOf(packetBytes, (byte) 0xFF, indexOfFF + 1);
                            else
                                break;
                        }
                    }

                    switch (_readThreadMode)
                    {
                        case ReadThreadMode.Receiving:
                            {
                                var code = new IRCode(timingData);
                                code.FinalizeData();
                                FireCodeReceived(new CodeReceivedEventArgs(code));
                                break;
                            }

                        case ReadThreadMode.Learning:
                            {
                                if (timingData == null)
                                {
                                    if (_learningCode.TimingData.Length > 0)
                                    {
                                        _learningCode = null;
                                        _readThreadMode = ReadThreadMode.LearningFailed;
                                    }
                                    break;
                                }

                                if (_learningCode == null)
                                    throw new InvalidOperationException(
                                        "Learning not initialised correctly, _learningCode object is null");

                                _learningCode.AddTimingData(timingData);

                                // Example: 9F 01 02 9F 15 00 BE 80
                                var indexOf9F = Array.IndexOf(packetBytes, (byte) 0x9F);
                                while (indexOf9F != -1)
                                {
                                    if (packetBytes.Length > indexOf9F + 3 && packetBytes[indexOf9F + 1] == 0x15)
                                        // 9F 15 XX XX
                                    {
                                        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.0)
                                        {
                                            _learningCode.Carrier = IRCode.CarrierFrequencyDCMode;
                                        }
                                        else
                                        {
                                            double carrier = 1000000*carrierCount/onTime;

                                            // TODO: Double-Check this calculation.
                                            if (carrier > 32000)
                                            {
                                                _learningCode.Carrier = (int) (carrier + 0.05*carrier - 0.666667);
                                                // was: _learningCode.Carrier = (int) (carrier + 0.05*carrier - 32000/48000);
                                            }
                                            else
                                            {
                                                _learningCode.Carrier = (int) carrier;
                                            }
                                        }

                                        _readThreadMode = ReadThreadMode.LearningDone;
                                        break;
                                    }

                                    if (packetBytes.Length > indexOf9F + 1)
                                    {
                                        indexOf9F = Array.IndexOf(packetBytes, (byte) 0x9F, indexOf9F + 1);
                                    }
                                    else
                                    {
                                        _readThreadMode = ReadThreadMode.LearningFailed;
                                        break;
                                    }
                                }

                                break;
                            }
                    }
                }
                FireStateChanged(new StateChangedEventArgs(RunningState.Stopping));
            }
            catch (ThreadInterruptedException ex)
            {
                Log.Info(ex);

                if (_readHandle != null)
                    CancelIo(_readHandle);
            }
            catch (Exception ex)
            {
                Log.Warn(ex);

                if (_readHandle != null)
                    CancelIo(_readHandle);
            }
            finally
            {
                if (deviceBufferPtr != IntPtr.Zero)
                    Marshal.FreeHGlobal(deviceBufferPtr);

                safeWaitHandle.DangerousRelease();
                waitHandle.Close();
            }

            Log.Debug("Read Thread Ended");
            FireStateChanged(new StateChangedEventArgs(RunningState.Stopped));
        }
Example #3
0
        private void IoControl(IoCtrl ioControlCode, IntPtr inBuffer, int inBufferSize, IntPtr outBuffer,
                               int outBufferSize,
                               out int bytesReturned)
        {
            if (!_deviceAvailable)
                throw new InvalidOperationException("Device not available");

            using (WaitHandle waitHandle = new ManualResetEvent(false))
            {
                SafeHandle safeWaitHandle = waitHandle.SafeWaitHandle;

                var success = false;
                safeWaitHandle.DangerousAddRef(ref success);
                if (!success)
                    throw new InvalidOperationException("Failed to initialize safe wait handle");

                try
                {
                    var dangerousWaitHandle = safeWaitHandle.DangerousGetHandle();

                    var overlapped = new DeviceIoOverlapped();
                    overlapped.ClearAndSetEvent(dangerousWaitHandle);

                    var deviceIoControl = DeviceIoControl(_eHomeHandle, ioControlCode, inBuffer, inBufferSize,
                                                          outBuffer,
                                                          outBufferSize, out bytesReturned, overlapped.Overlapped);

                    var lastError = Marshal.GetLastWin32Error();

                    if (deviceIoControl) return;

                    // Now also handles Operation Aborted and Bad Command errors.
                    switch (lastError)
                    {
                        case ErrorIoPending:
                            waitHandle.WaitOne();

                            var getOverlapped = GetOverlappedResult(_eHomeHandle, overlapped.Overlapped,
                                                                    out bytesReturned, false);
                            lastError = Marshal.GetLastWin32Error();

                            if (!getOverlapped)
                            {
                                if (lastError == ErrorBadCommand)
                                    goto case ErrorBadCommand;
                                if (lastError == ErrorOperationAborted)
                                    goto case ErrorOperationAborted;
                                throw new Win32Exception(lastError);
                            }
                            break;

                        case ErrorBadCommand:
                            if (Thread.CurrentThread == _readThread)
                                //Cause receive restart
                                _deviceReceiveStarted = false;
                            break;

                        case ErrorOperationAborted:
                            if (Thread.CurrentThread != _readThread)
                                throw new Win32Exception(lastError);

                            //Cause receive restart
                            _deviceReceiveStarted = false;
                            break;

                        default:
                            throw new Win32Exception(lastError);
                    }
                }
                catch
                {
                    Log.Warn("IoControl: something went bad with StructToPtr or the other way around");
                    if (_eHomeHandle != null)
                        CancelIo(_eHomeHandle);

                    throw;
                }
                finally
                {
                    safeWaitHandle.DangerousRelease();
                }
            }
        }