From bbd9390f47bfbd76ede242b529a3aecd55794e44 Mon Sep 17 00:00:00 2001 From: Klaus Reimer Date: Fri, 12 Oct 2018 08:43:29 +0200 Subject: [PATCH] Implement getPollfds and freePollfds --- src/changes/changes.xml | 2 +- src/main/java/org/usb4java/LibUsb.java | 33 ++++-- src/main/java/org/usb4java/Pollfd.java | 111 ++++++++++++++++++ src/main/java/org/usb4java/Pollfds.java | 102 ++++++++++++++++ .../java/org/usb4java/PollfdsIterator.java | 53 +++++++++ .../java/org/usb4java/LibUsbDeviceTest.java | 99 ++++++++++++++++ 6 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/usb4java/Pollfd.java create mode 100644 src/main/java/org/usb4java/Pollfds.java create mode 100644 src/main/java/org/usb4java/PollfdsIterator.java diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8ba74dd..de229a9 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -19,7 +19,7 @@ Add missing SPEED_SUPER_PLUS constant. - Wrap new libusb functions: setOption, devMemAlloc, devMemFree, interruptEventHandler. + Wrap new libusb functions: setOption, devMemAlloc, devMemFree, interruptEventHandler, getPollfds, freePollfds. Updated to libusb 1.0.22. diff --git a/src/main/java/org/usb4java/LibUsb.java b/src/main/java/org/usb4java/LibUsb.java index c232569..b1ba7a3 100644 --- a/src/main/java/org/usb4java/LibUsb.java +++ b/src/main/java/org/usb4java/LibUsb.java @@ -775,8 +775,6 @@ public final class LibUsb * Some options require one or more arguments to be provided. Consult each option's documentation for specific * requirements. * - * Since libusb version 1.0.22, LIBUSB_API_VERSION >= 0x01000106 - * * @param context * The {@link Context} on which to operate. * @param option @@ -796,8 +794,6 @@ public final class LibUsb * Some options require one or more arguments to be provided. Consult each option's documentation for specific * requirements. * - * Since libusb version 1.0.22, LIBUSB_API_VERSION >= 0x01000106 - * * @param context * The {@link Context} on which to operate. * @param option @@ -1335,8 +1331,6 @@ public final class LibUsb * allocated with this function must be freed with {@link #devMemFree()}. Specifically, this means that the flag * {@link #TRANSFER_FREE_BUFFER} cannot be used to free memory allocated with this function. * - * Since version 1.0.21, LIBUSB_API_VERSION >= 0x01000105 - * * @param handle * A device handle. * @param length @@ -2140,8 +2134,6 @@ public final class LibUsb * This is mainly useful for interrupting a dedicated event handling thread when an application wishes to call * {@link #exit()}. * - * Since version 1.0.21, LIBUSB_API_VERSION >= 0x01000105 - * * @param ctx * The context to operate on, or NULL for the default context. */ @@ -2516,6 +2508,31 @@ public final class LibUsb */ static native void unsetPollfdNotifiersNative(final Context context); + /** + * Retrieve a list of file descriptors that should be polled by your main loop as libusb event sources. + * + * The returned list should be freed with {@link #freePollfds()} when done. The actual list contents must not be + * touched. + * + * As file descriptors are a Unix-specific concept, this function is not available on Windows and will always + * return NULL. + * + * @param context + * The context to operate on, or NULL for the default context. + * @return A list of libusb_pollfd structures, NULL on error, NULL on platforms where the functionality is not + * available. + */ + public static native Pollfds getPollfds(final Context context); + + /** + * Free a list of {@link Pollfd} structures. + * + * This should be called for all pollfd lists allocated with {@link #getPollfds()}. + * + * It is legal to call this function with a NULL pollfd list. In this case, the function will simply return safely. + */ + public static native void freePollfds(final Pollfds pollfds); + /** * Allocate a libusb transfer without support for isochronous transfers. * diff --git a/src/main/java/org/usb4java/Pollfd.java b/src/main/java/org/usb4java/Pollfd.java new file mode 100644 index 0000000..38dd6e1 --- /dev/null +++ b/src/main/java/org/usb4java/Pollfd.java @@ -0,0 +1,111 @@ +/* + * Copyright 2018 Klaus Reimer + * See LICENSE.md for licensing information. + * + * Based on libusb : + * + * Copyright 2001 Johannes Erdfelt + * Copyright 2007-2009 Daniel Drake + * Copyright 2010-2012 Peter Stuge + * Copyright 2008-2013 Nathan Hjelm + * Copyright 2009-2013 Pete Batard + * Copyright 2009-2013 Ludovic Rousseau + * Copyright 2010-2012 Michael Plante + * Copyright 2011-2013 Hans de Goede + * Copyright 2012-2013 Martin Pieuchot + * Copyright 2012-2013 Toby Gray + */ + +package org.usb4java; + +/** + * File descriptor for polling. + * + * @author Klaus Reimer (k@ailis.de) + */ +public final class Pollfd +{ + /** There is data to read */ + public static final int POLLIN = 1; + + /** Writing now will not block. */ + public static final int POLLOUT = 4; + + /** The native pointer to the pollfd structure. */ + private long pollfdPointer; + + /** + * Package-private constructor to prevent manual instantiation. Structures are + * always created by JNI. + */ + Pollfd() + { + // Empty + } + + /** + * Returns the native pointer to the pollfd structure. + * + * @return The native pointer to the pollfd structure. + */ + public long getPointer() + { + return this.pollfdPointer; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = (prime * result) + + (int) (this.pollfdPointer ^ (this.pollfdPointer >>> 32)); + return result; + } + + @Override + public boolean equals(final Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (this.getClass() != obj.getClass()) + { + return false; + } + final Pollfd other = (Pollfd) obj; + if (this.pollfdPointer != other.pollfdPointer) + { + return false; + } + return true; + } + + @Override + public String toString() + { + return String.format("libusb pollfd 0x%x", this.pollfdPointer); + } + + /** + * Returns the numeric file descriptor. + * + * @return The numeric file descriptor. + */ + public native byte fd(); + + /** + * Returns the event flags to poll. + * + * {@link #POLLIN} indicates that you should monitor this file descriptor for becoming ready to read from, and + * {@link #POLLOUT} indicates that you should monitor this file descriptor for non-blocking write readiness. + * + * @return The event flags to poll. + */ + public native short events(); +} diff --git a/src/main/java/org/usb4java/Pollfds.java b/src/main/java/org/usb4java/Pollfds.java new file mode 100644 index 0000000..bb1906d --- /dev/null +++ b/src/main/java/org/usb4java/Pollfds.java @@ -0,0 +1,102 @@ +/* + * Copyright 2018 Klaus Reimer + * See LICENSE.md for licensing information. + */ + +package org.usb4java; + +import java.util.Iterator; + +/** + * List of poll file descriptors as returned by {@link LibUsb#getPollfds(Context)}. + * + * @author Klaus Reimer (k@ailis.de) + */ +public final class Pollfds implements Iterable +{ + /** The native pointer to the pollfd array. */ + private long pollfdsPointer; + + /** The number of file descriptors in the list. */ + private int size; + + /** + * Package-private constructor to prevent manual instantiation. Structures are + * always created by JNI. + */ + Pollfds() + { + // Empty + } + + /** + * Returns the native pointer. + * + * @return The native pointer. + */ + public long getPointer() + { + return this.pollfdsPointer; + } + + /** + * Returns the number of poll file descriptors in the list. + * + * @return The number of poll file descriptors in the list. + */ + public int getSize() + { + return this.size; + } + + /** + * Returns the poll file descriptor with the specified index. + * + * @param index + * The index. + * @return The poll file descriptor or null when index is out of bounds. + */ + public native Pollfd get(final int index); + + @Override + public Iterator iterator() + { + return new PollfdsIterator(this); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = (prime * result) + + (int) (this.pollfdsPointer ^ (this.pollfdsPointer >>> 32)); + return result; + } + + @Override + public boolean equals(final Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (this.getClass() != obj.getClass()) + { + return false; + } + final Pollfds other = (Pollfds) obj; + return this.pollfdsPointer == other.pollfdsPointer; + } + + @Override + public String toString() + { + return String.format("libusb pollfd list 0x%x with size %d", + this.pollfdsPointer, this.size); + } +} diff --git a/src/main/java/org/usb4java/PollfdsIterator.java b/src/main/java/org/usb4java/PollfdsIterator.java new file mode 100644 index 0000000..41a7755 --- /dev/null +++ b/src/main/java/org/usb4java/PollfdsIterator.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Klaus Reimer + * See LICENSE.md for licensing information. + */ + +package org.usb4java; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Iterator for {@link Pollfds}. + * + * @author Klaus Reimer (k@ailis.de) + */ +final class PollfdsIterator implements Iterator +{ + /** The file descriptor list. */ + private final Pollfds pollfds; + + /** The current index. */ + private int nextIndex; + + /** + * Constructor. + * + * @param pollfds + * The file descriptor list list. + */ + PollfdsIterator(final Pollfds pollfds) + { + this.pollfds = pollfds; + } + + @Override + public boolean hasNext() + { + return this.nextIndex < this.pollfds.getSize(); + } + + @Override + public Pollfd next() + { + if (!hasNext()) throw new NoSuchElementException(); + return this.pollfds.get(this.nextIndex++); + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } +} diff --git a/src/test/java/org/usb4java/LibUsbDeviceTest.java b/src/test/java/org/usb4java/LibUsbDeviceTest.java index 08611fa..43366f1 100644 --- a/src/test/java/org/usb4java/LibUsbDeviceTest.java +++ b/src/test/java/org/usb4java/LibUsbDeviceTest.java @@ -9,8 +9,11 @@ import static org.usb4java.test.UsbAssume.assumeUsbTestsEnabled; import static org.usb4java.test.UsbAssume.isUsbTestsEnabled; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeNotNull; import java.nio.ByteBuffer; @@ -1212,4 +1215,100 @@ public class LibUsbDeviceTest LibUsb.exit(null); } } + + /** + * Tests the {@link LibUsb#getPollfds()} and {@link LibUsb#freePollfds()} methods. + */ + @Test + public void testGetAndFreePollfds() + { + assumeUsbTestsEnabled(); + + final Pollfds pollfds = LibUsb.getPollfds(this.context); + try { + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + assertNull(pollfds); + } else { + assertNotNull(pollfds); + assertTrue("Size must be >= 0", pollfds.getSize() >= 0); + int i = 0; + for (Pollfd pollfd : pollfds) { + assertEquals(pollfds.get(i), pollfd); + assertTrue("File descriptor must be > 0", pollfd.fd() > 0); + assertTrue("Events must be > 0", pollfd.events() > 0); + i++; + } + } + } finally { + LibUsb.freePollfds(pollfds); + } + } + + /** + * Tests the {@link LibUsb#getPollfds()} and {@link LibUsb#freePollfds()} methods with the default context. + */ + @Test + public void testGetAndFreePollfdsWithDefaultContext() + { + assumeUsbTestsEnabled(); + + assertEquals(0, LibUsb.init(null)); + final Pollfds pollfds = LibUsb.getPollfds(null); + try { + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + assertNull(pollfds); + } else { + assertNotNull(pollfds); + assertTrue("Size must be >= 0", pollfds.getSize() >= 0); + int i = 0; + for (Pollfd pollfd : pollfds) { + assertEquals(pollfds.get(i), pollfd); + assertTrue("File descriptor must be > 0", pollfd.fd() > 0); + assertTrue("Events must be > 0", pollfd.events() > 0); + i++; + } + } + } finally { + LibUsb.freePollfds(pollfds); + } + } + + /** + * Ensures that {@link LibUsb#freePollfds()} doesn't throw an error when called with null argument. + */ + @Test + public void testFreePollfdsWithNullArgument() + { + LibUsb.freePollfds(null); + } + + /** + * Ensures that {@link LibUsb#freePollfds()} doesn't crash when called twice and throws an IllegalStateException + * instead. + */ + @Test(expected = IllegalStateException.class) + public void testDoubleFreePollfds() + { + assumeFalse(System.getProperty("os.name").toLowerCase().contains("windows")); + assumeUsbTestsEnabled(); + final Pollfds pollfds = LibUsb.getPollfds(this.context); + LibUsb.freePollfds(pollfds); + LibUsb.freePollfds(pollfds); + } + + /** + * Ensures that accessing Pollfds properties after calling {@link LibUsb#freePollfds()} doesn't crash and + * throws an IllegalStateException instead. + */ + @Test(expected = IllegalStateException.class) + public void testPollfdsAccessAfterFree() + { + assumeFalse(System.getProperty("os.name").toLowerCase().contains("windows")); + assumeUsbTestsEnabled(); + assertEquals(0, LibUsb.init(null)); + final Pollfds pollfds = LibUsb.getPollfds(null); + assertNotNull(pollfds); + LibUsb.freePollfds(pollfds); + assertNotNull(pollfds.get(0)); + } }