// TODO: Has close() race condition.
        public unsafe override int Read(byte[] buffer, int offset, int count)
        {
            Throw.If.OutOfRange(buffer, offset, count);
            UpdateSettings();
            if (count == 0)
            {
                return(0);
            }

            fixed(byte *buffer0 = buffer)
            {
                int startTime = Environment.TickCount, readTimeout = ReadTimeout;

                while (true)
                {
                    int handle = _handle;
                    if (handle < 0)
                    {
                        throw new IOException("Closed.");
                    }

                    var bufferPtr   = (IntPtr)(buffer0 + offset);
                    int bytesToRead = count;

                    var fd = new NativeMethods.pollfd()
                    {
                        fd = handle, events = NativeMethods.pollev.IN
                    };
                    int ret = NativeMethods.retry(() => NativeMethods.poll(ref fd, (IntPtr)1, GetTimeout(startTime, readTimeout)));
                    if (ret < 0)
                    {
                        throw new IOException("Read failed (poll).");
                    }
                    if (ret == 1)
                    {
                        if (fd.revents != NativeMethods.pollev.IN)
                        {
                            throw new IOException(string.Format("Closed during read ({0}).", fd.revents));
                        }

                        int readCount = checked ((int)NativeMethods.retry(() => NativeMethods.read(handle, bufferPtr, (UIntPtr)bytesToRead)));
                        if (readCount <= 0 || readCount > bytesToRead)
                        {
                            throw new IOException("Read failed.");
                        }

                        return(readCount);
                    }
                }
            }
        }
        // TODO: Has close() race condition.
        public unsafe override void Write(byte[] buffer, int offset, int count)
        {
            Throw.If.OutOfRange(buffer, offset, count);
            UpdateSettings();
            if (count == 0)
            {
                return;
            }

            fixed(byte *buffer0 = buffer)
            {
                int startTime = Environment.TickCount, writeTimeout = WriteTimeout;

                for (int bytesWritten = 0; bytesWritten < count;)
                {
                    int handle = _handle;
                    if (handle < 0)
                    {
                        throw new IOException("Closed.");
                    }

                    var bufferPtr    = (IntPtr)(buffer0 + offset + bytesWritten);
                    int bytesToWrite = count - bytesWritten;

                    var fd = new NativeMethods.pollfd()
                    {
                        fd = handle, events = NativeMethods.pollev.OUT
                    };
                    int ret = NativeMethods.retry(() => NativeMethods.poll(ref fd, (IntPtr)1, GetTimeout(startTime, writeTimeout)));
                    if (ret < 0)
                    {
                        throw new IOException("Write failed (poll).");
                    }
                    if (ret == 1)
                    {
                        if (fd.revents != NativeMethods.pollev.OUT)
                        {
                            throw new IOException(string.Format("Closed during write ({0}).", fd.revents));
                        }

                        int writeCount = checked ((int)NativeMethods.retry(() => NativeMethods.write(handle, bufferPtr, (UIntPtr)bytesToWrite)));
                        if (writeCount <= 0 || writeCount > bytesToWrite)
                        {
                            throw new IOException("Write failed.");
                        }
                        bytesWritten += writeCount;
                    }
                }
            }
        }
        unsafe void ReadThread()
        {
            if (!HandleAcquire())
            {
                return;
            }

            try
            {
                lock (_inputQueue)
                {
                    var pfd = new NativeMethods.pollfd();
                    pfd.fd     = _handle;
                    pfd.events = NativeMethods.pollev.IN;

                    while (!_shutdown)
                    {
tryReadAgain:
                        Monitor.Exit(_inputQueue);

                        int ret;
                        try { ret = NativeMethods.poll(ref pfd, (IntPtr)1, 250); }
                        finally { Monitor.Enter(_inputQueue); }
                        if (ret != 1)
                        {
                            continue;
                        }

                        if (0 != (pfd.revents & (NativeMethods.pollev.ERR | NativeMethods.pollev.HUP | NativeMethods.pollev.NVAL)))
                        {
                            break;
                        }

                        if (0 != (pfd.revents & NativeMethods.pollev.IN))
                        {
                            // Linux doesn't provide a Report ID if the device doesn't use one.
                            int inputLength = Device.GetMaxInputReportLength();
                            if (inputLength > 0 && !((LinuxHidDevice)Device).ReportsUseID)
                            {
                                inputLength--;
                            }

                            byte[] inputReport = new byte[inputLength];
                            fixed(byte *inputBytes = inputReport)
                            {
                                var    inputBytesPtr = (IntPtr)inputBytes;
                                IntPtr length        = NativeMethods.retry(() => NativeMethods.read
                                                                               (_handle, inputBytesPtr, (UIntPtr)inputReport.Length));

                                if ((long)length < 0)
                                {
                                    var error = (NativeMethods.error)Marshal.GetLastWin32Error();
                                    if (error != NativeMethods.error.EAGAIN)
                                    {
                                        break;
                                    }
                                    goto tryReadAgain;
                                }

                                Array.Resize(ref inputReport, (int)length);                                 // No Report ID? First byte becomes Report ID 0.
                                if (!((LinuxHidDevice)Device).ReportsUseID)
                                {
                                    inputReport = new byte[1].Concat(inputReport).ToArray();
                                }
                                _inputQueue.Enqueue(inputReport); Monitor.PulseAll(_inputQueue);
                            }
                        }
                    }

                    CommonDisconnected(_inputQueue);
                }
            }
            finally
            {
                HandleRelease();
            }
        }
Example #4
0
        protected override void Run(Action readyCallback)
        {
            IntPtr udev = NativeMethodsLibudev.Instance.udev_new();

            RunAssert(udev != IntPtr.Zero, "HidSharp udev_new failed.");

            try
            {
                IntPtr monitor = NativeMethodsLibudev.Instance.udev_monitor_new_from_netlink(udev, "udev");
                RunAssert(monitor != IntPtr.Zero, "HidSharp udev_monitor_new_from_netlink failed.");

                try
                {
                    int ret;

                    ret = NativeMethodsLibudev.Instance.udev_monitor_filter_add_match_subsystem_devtype(monitor, "hid", null);
                    RunAssert(ret >= 0, "HidSharp udev_monitor_failed_add_match_subsystem_devtype failed.");

                    ret = NativeMethodsLibudev.Instance.udev_monitor_enable_receiving(monitor);
                    RunAssert(ret >= 0, "HidSharp udev_monitor_enable_receiving failed.");

                    int fd = NativeMethodsLibudev.Instance.udev_monitor_get_fd(monitor);
                    RunAssert(fd >= 0, "HidSharp udev_monitor_get_fd failed.");

                    var fds = new NativeMethods.pollfd[1];
                    fds[0].fd     = fd;
                    fds[0].events = NativeMethods.pollev.IN;

                    readyCallback();
                    while (true)
                    {
                        ret = NativeMethods.retry(() => NativeMethods.poll(fds, (IntPtr)1, -1));
                        if (ret < 0)
                        {
                            break;
                        }

                        if (ret == 1)
                        {
                            if (0 != (fds[0].revents & (NativeMethods.pollev.ERR | NativeMethods.pollev.HUP | NativeMethods.pollev.NVAL)))
                            {
                                break;
                            }
                            if (0 != (fds[0].revents & NativeMethods.pollev.IN))
                            {
                                IntPtr device = NativeMethodsLibudev.Instance.udev_monitor_receive_device(monitor);
                                if (device != null)
                                {
                                    NativeMethodsLibudev.Instance.udev_device_unref(device);

                                    DeviceList.Local.RaiseChanged();
                                }
                            }
                        }
                    }
                }
                finally
                {
                    NativeMethodsLibudev.Instance.udev_monitor_unref(monitor);
                }
            }
            finally
            {
                NativeMethodsLibudev.Instance.udev_unref(udev);
            }
        }