// 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(); } }
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); } }