From f38d8c6b5715d425d07c9b719dd0e886231d0df3 Mon Sep 17 00:00:00 2001 From: Tom Aarts Date: Thu, 22 Aug 2024 10:55:32 -0500 Subject: [PATCH] first integration of SPI for Pi5 using ioctl. Submitted to factilitate discussion --- libraries/pi4j-library-kernel/pom.xml | 360 +++++++++++ .../java/com/pi4j/library/kernel/README.md | 26 + .../java/com/pi4j/library/kernel/SPIapi.java | 130 ++++ .../com/pi4j/library/kernel/SPIapiImpl.java | 164 +++++ .../com/pi4j/library/kernel/SPIapiIntrf.java | 62 ++ .../kernel/util/NativeLibraryLoader.java | 255 ++++++++ .../pi4j/library/kernel/util/StringUtil.java | 602 ++++++++++++++++++ .../src/main/java/module-info.java | 17 + .../src/main/native/Makefile | 69 ++ .../src/main/native/build-docker.sh | 59 ++ .../src/main/native/build-libpi4j.sh | 72 +++ .../src/main/native/build-prerequisites.sh | 130 ++++ .../src/main/native/build.sh | 132 ++++ .../native/com_pi4j_library_kernel_internal.c | 355 +++++++++++ .../native/com_pi4j_library_kernel_internal.h | 129 ++++ .../test/java/com/pi4j/test/SPIapiTest.java | 199 ++++++ libraries/pi4j-library/pom.xml | 1 + plugins/pi4j-plugin-rp1spi/pom.xml | 113 ++++ .../com/pi4j/plugin/rp1spi/Rp1SpiPlugin.java | 56 ++ .../pi4j/plugin/rp1spi/provider/Rp1Spi.java | 232 +++++++ .../rp1spi/provider/Rp1SpiProvider.java | 24 + .../rp1spi/provider/Rp1SpiProviderImpl.java | 34 + .../src/main/java/module-info.java | 20 + .../com.pi4j.extension.Plugin | 1 + plugins/pi4j-plugin/pom.xml | 1 + pom.xml | 13 +- 26 files changed, 3255 insertions(+), 1 deletion(-) create mode 100644 libraries/pi4j-library-kernel/pom.xml create mode 100644 libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/README.md create mode 100644 libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapi.java create mode 100644 libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiImpl.java create mode 100644 libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiIntrf.java create mode 100644 libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/NativeLibraryLoader.java create mode 100644 libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/StringUtil.java create mode 100644 libraries/pi4j-library-kernel/src/main/java/module-info.java create mode 100644 libraries/pi4j-library-kernel/src/main/native/Makefile create mode 100755 libraries/pi4j-library-kernel/src/main/native/build-docker.sh create mode 100755 libraries/pi4j-library-kernel/src/main/native/build-libpi4j.sh create mode 100755 libraries/pi4j-library-kernel/src/main/native/build-prerequisites.sh create mode 100755 libraries/pi4j-library-kernel/src/main/native/build.sh create mode 100644 libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.c create mode 100644 libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.h create mode 100644 libraries/pi4j-library-kernel/src/test/java/com/pi4j/test/SPIapiTest.java create mode 100644 plugins/pi4j-plugin-rp1spi/pom.xml create mode 100644 plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/Rp1SpiPlugin.java create mode 100644 plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1Spi.java create mode 100644 plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProvider.java create mode 100644 plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProviderImpl.java create mode 100644 plugins/pi4j-plugin-rp1spi/src/main/java/module-info.java create mode 100644 plugins/pi4j-plugin-rp1spi/src/main/resources/META-INF.services/com.pi4j.extension.Plugin diff --git a/libraries/pi4j-library-kernel/pom.xml b/libraries/pi4j-library-kernel/pom.xml new file mode 100644 index 00000000..90387f32 --- /dev/null +++ b/libraries/pi4j-library-kernel/pom.xml @@ -0,0 +1,360 @@ + + + 4.0.0 + + + + pi4j-library + com.pi4j + 2.6.1-SNAPSHOT + ../pi4j-library/pom.xml + + + + pi4j-library-kernel + Pi4J :: LIBRARY :: JNA Wrapper for Kernel Library + Pi4J wrapper for the Kernel library + jar + + + + 5.6.0 + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + net.java.dev.jna + jna-platform + ${jna.version} + + + net.java.dev.jna + jna + ${jna.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + + + + + + + + ${project.build.directory} + false + + LICENSE.txt + NOTICE.txt + README.md + lib/armhf/pi4j-kernel/libpi4j-kernel.so + lib/aarch64/pi4j-kernel/libpi4j-kernel.so + + + + src/main/resources + false + + + + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-native-jna-file--exist + + enforce + + prepare-package + + + + + + ${project.build.outputDirectory}/lib/armhf/pi4j-kernel/libpi4j-kernel.so + + ${project.build.outputDirectory}/lib/aarch64/pi4j-kernel/libpi4j-kernel.so + + + + true + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + transfer-compiled-pi4j-jar + install + + run + + + + + + + + + + + + + + + + + + + + + + + pi4j-attach-native + package + + run + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + runtime + com.pi4j + ${project.build.directory}/dependencies + false + false + true + + + + + + + + + + + + + + + + + default + + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + download-jna-dependency + validate + + copy + + + + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + armhf + so + true + ${project.build.directory}/lib/armhf/pi4j-kernel + libpi4j-kernel.so + + + + + + + ${project.groupId} + ${project.artifactId} + ${project.version} + so + aarch64 + true + ${project.build.directory}/lib/aarch64/pi4j-kernel + libpi4j-kernel.so + + + + + + + + + + + + + + + + + + native + + + release-build + + + + + + maven-antrun-plugin + + + + pi4j-prepare-native + generate-resources + + + + + + + + + + + + + + run + + + + + + + + pi4j-build-native + generate-resources + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/README.md b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/README.md new file mode 100644 index 00000000..bdfd0e9b --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/README.md @@ -0,0 +1,26 @@ + + +Creation +Application code: +SPIapiImpl functionsV1 = SPIapiIntrf.createSPIapiImpl(); + + return(SPIapiImpl.newInstance("libpi4j-kernel.so", "pi4j-kernel")); + SPIapiImpl newInstance called with lib and so + impl created + impl calls its initialize( uses name .so and library name ) + The initialize sets functionsNative reference to + the SO +Next create and initialize the spiApi_t structure. + Call functionsV1.spiApi_init opens SPI path and sets some SPI details. + + +Operation. +functionsV1.spiApi_write(SPIapi.spiApi_t spiApi, byte[] b, int offset, int len, int speed); + In the Impl method. it uses the contained NativeFunc pointer to all the C code + C linkage uses JNA to pass control to C library, upon completion returns to java test case + + + + + + diff --git a/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapi.java b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapi.java new file mode 100644 index 00000000..098e93d8 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapi.java @@ -0,0 +1,130 @@ +package com.pi4j.library.kernel; + + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.util.Console; +import com.sun.jna.*; +import com.sun.jna.Structure.*; + + +public class SPIapi { + + // Console console = new Console(); + // Context pi4j = Pi4J.newAutoContext(); + + + + + + + @FieldOrder({"returnEnumGood","returnEnumFail"}) + public static class spiApi_return_t extends Structure { + public int return_code; + } + + @FieldOrder({"waldo","where"}) + public static class spiApi_test extends Structure { + public int waldo; + public int where; + } + + + @FieldOrder({"max_freq","spi_FD", "bus", "cs", "dev"}) + public static class spiApi_t extends Structure{ + public int max_freq; + public int spi_FD; + public int bus; + public int cs; // + public spiApi_device_p.ByReference dev; + + public spiApi_t() { + super(); + } + } + + @FieldOrder({"driver_mode","CEx","CExResrv","AuxSPI","wire","bits","firstBitTX","firstBitRX","wSize"}) + public static class spiApi_device_p extends Structure { + public int driver_mode; + public int CEx; // Chip enable low:0 + public int CExResrv; // chip enable SPI1 Not used + public int AuxSPI; // Auxiliary SPI if 1, not used + public int wire; // 0:not 3 wire, 1 is 3 wire + public int bits; // bits before MISO begins, valid wire is 3 + public int firstBitTX; // 1, LSB sent first, 0 LSB first + public int firstBitRX; // 1, LSB received first, 0 LSB first + public int wSize; // 0 : 8 bit word. range 0-32 + + public spiApi_device_p(Pointer peer) { + super(peer); + } + + public spiApi_device_p() { + super(); + } + + //ALIGN_NONE + public static class ByReference extends spiApi_device_p implements Structure.ByReference { + + }; + + public static class ByValue extends spiApi_device_p implements Structure.ByValue { + }; + } + + + /* + typedef struct { + uint32_t type; +#define RPI_HWVER_TYPE_UNKNOWN 0 +#define RPI_HWVER_TYPE_PI1 1 +#define RPI_HWVER_TYPE_PI2 2 +#define RPI_HWVER_TYPE_PI4 3 + uint32_t hwver; + uint32_t periph_base; + uint32_t videocore_base; + char *desc; +} rpi_hw_t; + */ + + + + + +/*typedef struct spiApi_device +{ + int driver_mode; + volatile uint8_t *pxl_raw; + volatile dma_t *dma; + volatile pwm_t *pwm; + volatile pcm_t *pcm; + int spi_fd; + volatile dma_cb_t *dma_cb; + uint32_t dma_cb_addr; + volatile gpio_t *gpio; + volatile cm_clk_t *cm_clk; + videocore_mbox_t mbox; + int max_count; +} spiApi_device_t; */ + + + // pointer + @FieldOrder({"spi_mode","spi_FD"}) + public static class spiApi_device_p_old extends Structure{ + public int spi_mode; + public int spi_FD; + // public Pointer p; + + + /* public spiApi_device_p() { + super(); + // handle fixed-size array fields correctly + ensureAllocated(); + }*/ + } + + + + + + } diff --git a/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiImpl.java b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiImpl.java new file mode 100644 index 00000000..649c1acc --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiImpl.java @@ -0,0 +1,164 @@ +package com.pi4j.library.kernel; + +import com.pi4j.library.kernel.util.NativeLibraryLoader; +import com.pi4j.library.kernel.util.StringUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import com.sun.jna.Native; +import com.sun.jna.NativeLibrary; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; + +import static com.sun.jna.NativeLibrary.getInstance; + + +public class SPIapiImpl implements SPIapiIntrf { + + SPIapiIntrf functionsNative = null; + + + public int x = 42; + public SPIapiImpl(String fileName) throws java.io.IOException { + } + + public SPIapiImpl() { + } + + static { + // Load the platform library + NativeLibraryLoader.load("libpi4j-kernel.so", "pi4j-kernel"); +// System.setProperty("pi4j.library.path", "/lib/aarch64/pi4j-kernel"); + System.setProperty("pi4j.library.path", "/home/pi/Pi4J_V2/pi4j-v2/libraries/pi4j-library-kernel/target/lib/aarch64/pi4j-kernel"); + + } + + + public static SPIapiImpl newInstance(String fileName, String libName) throws IOException { + SPIapiImpl impl = new SPIapiImpl(); + impl.initialize(fileName, libName); + return(impl); + } + + @Override + //SPIapi.spiApi_return_t + public int spiApi_render_test(SPIapi.spiApi_test spiApi) { + int ret = jna_rc.x0; + ret = this.functionsNative.spiApi_render_test(spiApi); + + return ret; + } + + + + + + @Override + public int spiApi_render(SPIapi.spiApi_t spiApi) { + return 0; + } + + @Override + public int spiApi_init(SPIapi.spiApi_t spiApi) { + int ret = 0; + ret = this.functionsNative.spiApi_init(spiApi); + + return ret; + } + + + public int spiApi_write(SPIapi.spiApi_t spiApi, byte[] b, int offset, int len, int speed){ + // todo test len within buffer + Objects.checkFromIndexSize(offset, len, b.length); + return this.functionsNative.spiApi_write(spiApi, b, offset,len, speed); + } + + @Override + public int spiApi_write_b(SPIapi.spiApi_t spiApi, byte b, int speed) { + return 0; + } + + @Override + public int spiApi_read_b(SPIapi.spiApi_t spiApi, byte b, int speed) { + return 0; + } + + @Override + public int spiApi_read(SPIapi.spiApi_t spiApi, byte[] buffer, int offset, int length, int speed) { + Objects.checkFromIndexSize(offset, length, buffer.length); + return 0; + } + + @Override + public int transfer(SPIapi.spiApi_t spiApi, byte[] write, int writeOffset, byte[] read, int readOffset, int numberOfBytes, int speed) { + Objects.checkFromIndexSize(writeOffset, numberOfBytes, write.length); + Objects.checkFromIndexSize(readOffset, numberOfBytes, read.length); + return 0; + } + + public void initialize(String fileName, String libName) throws java.io.IOException { + // the following allows my usage of the JNA code, rather than cloning my own version + Path workingDirectory=Paths.get(".").toAbsolutePath(); + String classPath = System.getProperty("java.class.path"); + System.out.println("properties : " + System.getProperties()); + + System.out.println("classpath" + classPath); + + System.out.println("working DIR " + workingDirectory); + // get CPU architecture from system properties + String osArch = System.getProperty("os.arch").toLowerCase(); + + // sanitize CPU architecture string + switch (osArch) { + case "arm": + osArch = "armhf"; + break; + case "arm64": + osArch = "aarch64"; + break; + case "aarch64": + break; + default: + throw new IllegalStateException("Pi4J has detected and UNKNOWN/UNSUPPORTED 'os.arch' : [" + + osArch + "]; only 'arm|armhf' and 'arm64|aarch64' are supported."); + } + // String possibleJnaPath = workingDirectory+"/lib/" +osArch + "/pi4j-kernel/"; + // String possibleJnaPath = "/lib/" + osArch + "/" + libName + "/" + fileName; + String possibleJnaPath = "/lib/" +osArch + "/pi4j-kernel/"; + // Also, handle the user relying upon "pi4j.library.path" to locate so's + String pi4jLibpath = System.getProperty("pi4j.library.path"); + + if(StringUtil.isNotNullOrEmpty(pi4jLibpath, true)){ + possibleJnaPath += ":"+pi4jLibpath; + System.out.println("libPath " + pi4jLibpath); + } + System.out.println(" possible JNA " + possibleJnaPath); +// TO_DO + // Determine the JNA path in the NativeLibrarylOADER + System.setProperty("jna.library.path", possibleJnaPath); + NativeLibraryLoader.load(fileName, libName); + NativeLibrary lib = NativeLibrary.getInstance(fileName); //SPIapiIntrf.getCLibName(LIBPROPFILE)); + SPIapiIntrf INSTANCE = Native.load( SPIapiIntrf.class); + this.functionsNative = INSTANCE; //Native.loadLibrary(extractFile(fileName), String.valueOf(SPIapiIntrf.class)); + } + + + static final int DEFAULT_BUFFER_SIZE = 8192; + + private static void copyInputStreamToFile(InputStream inputStream, File file) + throws com.pi4j.io.exception.IOException, java.io.IOException { + // append = false + try (FileOutputStream outputStream = new FileOutputStream(file, false)) { + int read; + byte[] bytes = new byte[DEFAULT_BUFFER_SIZE]; + while ((read = inputStream.read(bytes)) != -1) { + outputStream.write(bytes, 0, read); + } + } + + } + +} \ No newline at end of file diff --git a/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiIntrf.java b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiIntrf.java new file mode 100644 index 00000000..8bc0fa74 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/SPIapiIntrf.java @@ -0,0 +1,62 @@ +package com.pi4j.library.kernel; + +import com.sun.jna.Library; + +import java.io.IOException; + +public interface SPIapiIntrf extends Library { + + static interface jna_rc{ + public static final int x0 = 0; + public static final int x1 = 1; + public static final int x2 = 2; + public static final int x3 = 3; + public static final int x4 = 4; + public static final int x5 = 5; + public static final int x6 = 6; + public static final int x7 = 7; + public static final int x9 = 9; + public static final int x10 = 10; + public static final int x11 = 11; + public static final int x12 = 12; + public static final int x13 = 13; + public static final int x14 = 14; + public static final int xCount = 16; + } + int spiApi_render_test(SPIapi.spiApi_test spiApi); // TODO * SPIapi.spiApi_return_t + + + + + int spiApi_write(SPIapi.spiApi_t spiApi, byte[] b, int offset, int len, int speed); + + public int spiApi_write_b(SPIapi.spiApi_t spiApi, byte b, int speed); + + public int spiApi_read_b(SPIapi.spiApi_t spiApi, byte b, int speed) ; + + public int spiApi_read(SPIapi.spiApi_t spiApi, byte[] buffer, int offset, int length, int speed) ; + + public int transfer(SPIapi.spiApi_t spiApi, byte[] write, int writeOffset, byte[] read, int readOffset, int numberOfBytes, int speed); + + + + + + int spiApi_init(SPIapi.spiApi_t spiApi); + int spiApi_render(SPIapi.spiApi_t spiApi); // TODO * + + + public static SPIapiImpl newNativeInstance(String soName, String libName) throws java.io.IOException { + SPIapiImpl rval = new SPIapiImpl(soName); + rval.initialize(soName, libName); + return(rval); + } + + public static SPIapiImpl createSPIapiImpl() throws IOException { + return(SPIapiImpl.newInstance("libpi4j-kernel.so", "pi4j-kernel")); + } + + } + + + diff --git a/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/NativeLibraryLoader.java b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/NativeLibraryLoader.java new file mode 100644 index 00000000..70073658 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/NativeLibraryLoader.java @@ -0,0 +1,255 @@ +package com.pi4j.library.kernel.util; + + +//import com.sun.jna.NativeLibrary; +import com.sun.jna.NativeLibrary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Set; +import java.util.TreeSet; + +public class NativeLibraryLoader { + + private static final Set loadedLibraries = new TreeSet<>(); + protected static final Logger logger = LoggerFactory.getLogger(NativeLibraryLoader.class); + private static boolean initialized; + + // private constructor + private NativeLibraryLoader() { + // forbid object construction + } + + public static synchronized void load(String fileName, String libName) { + // check for debug property; if found enable all logging levels + if (!initialized) { + initialized = true; + } + + // first, make sure that this library has not already been previously loaded + if (loadedLibraries.contains(fileName)) { + logger.warn("Library [" + fileName + "] has already been loaded; no need to load again."); + // NativeLibrary lib = NativeLibraryLoader.getInstance(fileName); + return; + } + + // cache loaded library + loadedLibraries.add(fileName); + + // determine if there is an overriding library path defined for native libraries + String libpath = System.getProperty("pi4j.library.path"); + if(StringUtil.isNotNullOrEmpty(libpath, true)) { + + // if the overriding library path is set to "system", then attempt to use the system resolved library paths + if (libpath.equalsIgnoreCase("system")) { + logger.debug("Attempting to load library using {pi4j.library.path} system resolved library name: [" + libName + "]"); + try { + // load library from JVM system library path; based on library name + System.loadLibrary(libName); + } + catch (Exception ex){ + //throw this error + throw new UnsatisfiedLinkError("Pi4J was unable load the native library [" + + libName + "] from the system defined library path. The system property 'pi4j.library.path' is defined as [" + + libpath + "]. You can alternatively define the 'pi4j.library.path' " + + "system property to override this behavior and specify an absolute library path." + + "; UNDERLYING EXCEPTION: [" + ex.getClass().getName() + "]=" + ex.getMessage()); + } + } + + // if the overriding library path is set to "local", then attempt to use the JAR local path to resolve library + else if (libpath.equalsIgnoreCase("local")) { + // get local directory path of JAR file + try { + libpath = NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + } catch (URISyntaxException e) { + logger.error(e.getMessage(), e); + libpath = "."; + } + // build path based on lib directory and lib filename + String path = Paths.get(libpath, fileName).toString(); + logger.debug("Attempting to load library using {pi4j.library.path} defined path: [" + path + "]"); + try { + // load library from local path of this JAR file + System.load(path); + } + catch (Exception ex){ + //throw this error + throw new UnsatisfiedLinkError("Pi4J was unable load the native library [" + + libName + "] from the user defined library path. The system property 'pi4j.library.path' is defined as [" + + libpath + "]. Please make sure the defined the 'pi4j.library.path' " + + "system property contains the correct absolute library path." + + "; UNDERLYING EXCEPTION: [" + ex.getClass().getName() + "]=" + ex.getMessage()); + } + } + + // if the overriding library path is set to something else, then attempt to use the defined path to resolve library + else { + // build path based on lib directory and lib filename + String path = Paths.get(libpath, fileName).toString(); + logger.debug("Attempting to load library using {pi4j.library.path} defined path: [" + path + "]"); + try { + // load library from user defined absolute path provided via pi4j.library.path} + System.load(path); + } catch (UnsatisfiedLinkError ex) { + String exceptMessage; + // no guarantee the except pertains to ELF miss-match so check MSG content + if (ex.getMessage().contains("wrong ELF class")) { + exceptMessage = "Pi4J was unable to link the native library [" + + path + "] embedded inside this JAR [" + + NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath() + + "]. The exception indicates a mismatch of architecture. armhf/ELFCLASS32 aarch64/ELFCLASS64 \n" + + " UNDERLYING EXCEPTION: [" + ex.getClass().getName() + "]=" + ex.getMessage(); + } else { + exceptMessage = "Pi4J was unable to extract and load the native library [" + + path + "] from the embedded resources inside this JAR [" + + NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath() + + "]. to a temporary location on this system. You can alternatively define the 'pi4j.library.path' " + + "system property to override this behavior and specify the library path.\n" + + " UNDERLYING EXCEPTION: [" + ex.getClass().getName() + "]=" + ex.getMessage(); + } + throw new UnsatisfiedLinkError(exceptMessage); + } catch (Exception ex) { + throw new UnsatisfiedLinkError("Pi4J was unable to extract and load the native library [" + + path + "] from the embedded resources inside this JAR [" + + NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath() + + "]. to a temporary location on this system. You can alternatively define the 'pi4j.library.path' " + + "system property to override this behavior and specify the library path.\n" + + " UNDERLYING EXCEPTION: [" + ex.getClass().getName() + "]=" + ex.getMessage()); + } + } + } + + // if there is no overriding library path defined, then attempt to load native library from embedded resource + else { + // get CPU architecture from system properties + String osArch = System.getProperty("os.arch").toLowerCase(); + + // sanitize CPU architecture string + switch (osArch) { + case "arm": + osArch = "armhf"; + break; + case "arm64": + osArch = "aarch64"; + break; + case "aarch64": + break; + default: + throw new IllegalStateException("Pi4J has detected and UNKNOWN/UNSUPPORTED 'os.arch' : [" + + osArch + "]; only 'arm|armhf' and 'arm64|aarch64' are supported."); + } + + // include the CPU architecture in the embedded path + String path = "/lib/" + osArch + "/" + libName + "/" + fileName; + logger.debug("Attempting to load library [" + fileName + "] using path: [" + path + "]"); + try { + loadLibraryFromClasspath(path); + logger.debug("Library [" + fileName + "] loaded successfully using embedded resource file: [" + path + "]"); + } catch (UnsatisfiedLinkError e) { + logger.error("Unable to load [" + fileName + "] using path: [" + path + "]", e); + String exceptMessage; + // no guarantee the except pertains to ELF miss-match so check MSG content + if (e.getMessage().contains("wrong ELF class")) { + exceptMessage = "Pi4J was unable to link the native library [" + + path + "] embedded inside this JAR [" + + NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath() + + "]. The exception indicates a mismatch of architecture. armhf/ELFCLASS32 aarch64/ELFCLASS64 \n" + + " All native libraries must be of architecture " + osArch + " \n" + + " UNDERLYING EXCEPTION: [" + e.getClass().getName() + "]=" + e.getMessage(); + } else { + exceptMessage = "Pi4J was unable to extract and load the native library [" + + path + "] from the embedded resources inside this JAR [" + + NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath() + + "]. to a temporary location on this system. You can alternatively define the 'pi4j.library.path' " + + "system property to override this behavior and specify the library path.\n" + + " UNDERLYING EXCEPTION: [" + e.getClass().getName() + "]=" + e.getMessage(); + } + throw new UnsatisfiedLinkError(exceptMessage); + } catch (Exception e) { + logger.error("Unable to load [" + fileName + "] using path: [" + path + "]", e); + throw new UnsatisfiedLinkError("Pi4J was unable to extract and load the native library [" + + path + "] from the embedded resources inside this JAR [" + + NativeLibraryLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath() + + "]. to a temporary location on this system. You can alternatively define the 'pi4j.library.path' " + + "system property to override this behavior and specify the library path.\n" + + " UNDERLYING EXCEPTION: [" + e.getClass().getName() + "]=" + e.getMessage()); + } + } + } + + /** + * Loads library from classpath + * + * The file from classpath is copied into system temporary directory and then loaded. The temporary file is + * deleted after exiting. Method uses String as filename because the pathname is + * "abstract", not system-dependent. + * + * @param path + * The file path in classpath as an absolute path, e.g. /package/File.ext (could be inside jar) + * @throws IOException + * If temporary file creation or read/write operation fails + * @throws IllegalArgumentException + * If source file (param path) does not exist + * @throws IllegalArgumentException + * If the path is not absolute or if the filename is shorter than three characters (restriction + * of {@see File#createTempFile(java.lang.String, java.lang.String)}). + */ + public static void loadLibraryFromClasspath(String path) throws IOException { + Path inputPath = Paths.get(path); + + String absPath = String.valueOf(String.valueOf(inputPath.toAbsolutePath())); + System.out.println("\n\nabsPath : " + absPath + " \n\n"); + + + if (!inputPath.isAbsolute()) { + throw new IllegalArgumentException("The path has to be absolute, but found: " + inputPath); + } + + String fileNameFull = inputPath.getFileName().toString(); + int dotIndex = fileNameFull.indexOf('.'); + if (dotIndex < 0 || dotIndex >= fileNameFull.length() - 1) { + throw new IllegalArgumentException("The path has to end with a file name and extension, but found: " + fileNameFull); + } + + String fileName = fileNameFull.substring(0, dotIndex); + String extension = fileNameFull.substring(dotIndex); + + Path target = Files.createTempFile(fileName, extension); + File targetFile = target.toFile(); + targetFile.deleteOnExit(); + + try (InputStream source = NativeLibraryLoader.class.getResourceAsStream(inputPath.toString())) { + if (source == null) { + throw new FileNotFoundException("File " + inputPath + " was not found in classpath."); + } + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } + // Finally, load the library + System.load(target.toAbsolutePath().toString()); + } + + + public static final NativeLibrary getInstanceX(String libraryName){ + NativeLibrary the_Lib = null; + // first, make sure that this library has already been previously loaded, if NOT, error + if (!loadedLibraries.contains(libraryName)) { + logger.error("Library [" + libraryName + "] has NOT already been loaded;"); + System.exit(100); + } + synchronized (loadedLibraries){ + + } + return the_Lib; + } + +} diff --git a/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/StringUtil.java b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/StringUtil.java new file mode 100644 index 00000000..245d34b0 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/com/pi4j/library/kernel/util/StringUtil.java @@ -0,0 +1,602 @@ +package com.pi4j.library.kernel.util; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +public class StringUtil { + + /** Constant EMPTY="" */ + public static final String EMPTY = ""; + /** Constant DEFAULT_PAD_CHAR=' ' */ + public static final char DEFAULT_PAD_CHAR = ' '; + + /** + *

isNullOrEmpty.

+ * + * @param data a {@link String} object. + * @param trim a boolean. + * @return a boolean. + */ + public static boolean isNullOrEmpty(String data, boolean trim){ + if(data == null) + return true; + + // trim if requested + String test = data; + if(trim) + test = data.trim(); + + return (test.length() <= 0); + } + + /** + *

isNullOrEmpty.

+ * + * @param data a {@link String} object. + * @return a boolean. + */ + public static boolean isNullOrEmpty(String data){ + return isNullOrEmpty(data, false); + } + + /** + *

isNotNullOrEmpty.

+ * + * @param data a {@link String} object. + * @return a boolean. + */ + public static boolean isNotNullOrEmpty(String data){ + return isNotNullOrEmpty(data, false); + } + + /** + *

isNotNullOrEmpty.

+ * + * @param data a {@link String} object. + * @param trim a boolean. + * @return a boolean. + */ + public static boolean isNotNullOrEmpty(String data, boolean trim){ + return !(isNullOrEmpty(data, trim)); + } + + /** + *

setIfNullOrEmpty.

+ * + * @param data a {@link String} object. + * @param replacement a {@link String} object. + * @param trim a boolean. + * @return a {@link String} object. + */ + public static String setIfNullOrEmpty(String data, String replacement, boolean trim){ + if(isNullOrEmpty(data, trim)) { + return replacement; + } + return data; + } + + /** + *

setIfNullOrEmpty.

+ * + * @param data a {@link String} object. + * @param replacement a {@link String} object. + * @return a {@link String} object. + */ + public static String setIfNullOrEmpty(String data, String replacement){ + return setIfNullOrEmpty(data, replacement, false); + } + + /** + *

contains.

+ * + * @param source a {@link String} object. + * @param target a {@link String} object. + * @return a boolean. + */ + public static boolean contains(String source, String target) { + return (null != source && null != target && source.contains(target)); + } + + /** + *

contains.

+ * + * @param source a {@link String} object. + * @param targets an array of {@link String} objects. + * @return a boolean. + */ + public static boolean contains(String source, String[] targets) { + if (null != source && null != targets) { + for(var target : targets) { + if (source.contains(target)) { + return true; + } + } + } + return false; + } + + /** + *

contains.

+ * + * @param sources an array of {@link String} objects. + * @param target a {@link String} object. + * @return a boolean. + */ + public static boolean contains(String[] sources, String target) { + if (null != sources && null != target) { + for (var source : sources) { + if(contains(source, target)) + return true; + } + } + return false; + } + + /** + *

contains.

+ * + * @param sources an array of {@link String} objects. + * @param targets an array of {@link String} objects. + * @return a boolean. + */ + public static boolean contains(String[] sources, String[] targets) { + if (null != sources && null != targets) { + for (var source : sources) { + if(contains(source, targets)) + return true; + } + } + return false; + } + + /** + *

create.

+ * + * @param length a int. + * @return a {@link String} object. + */ + public static String create(int length) { + return create(DEFAULT_PAD_CHAR, length); + } + + /** + *

create.

+ * + * @param c a char. + * @param length a int. + * @return a {@link String} object. + */ + public static String create(char c, int length) { + StringBuilder sb = new StringBuilder(length); + for(var index = 0; index < length; index++) + sb.append(c); + return sb.toString(); + } + + /** + *

create.

+ * + * @param s a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String create(String s, int length) { + StringBuilder sb = new StringBuilder(length * s.length()); + for(var index = 0; index < length; index++) + sb.append(s); + return sb.toString(); + } + + /** + *

repeat.

+ * + * @param c a char. + * @param length a int. + * @return a {@link String} object. + */ + public static String repeat(char c, int length) { + return create(c, length); + } + + /** + *

repeat.

+ * + * @param s a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String repeat(String s, int length) { + return create(s, length); + } + + /** + *

padLeft.

+ * + * @param data a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String padLeft(String data, int length) { + return padLeft(data, DEFAULT_PAD_CHAR, length); + } + + /** + *

padLeft.

+ * + * @param data a {@link String} object. + * @param pad a char. + * @param length a int. + * @return a {@link String} object. + */ + public static String padLeft(String data, char pad, int length) { + var sb = new StringBuilder(data.length() + length); + for(var index = 0; index < length; index++) + sb.append(pad); + sb.append(data); + return sb.toString(); + } + + /** + *

padLeft.

+ * + * @param data a {@link String} object. + * @param pad a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String padLeft(String data, String pad, int length) { + var sb = new StringBuilder(data.length() + (length * pad.length())); + for(var index = 0; index < length; index++) + sb.append(pad); + sb.append(data); + return sb.toString(); + } + + /** + *

padRight.

+ * + * @param data a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String padRight(String data, int length) { + return padRight(data, DEFAULT_PAD_CHAR, length); + } + + /** + *

padRight.

+ * + * @param data a {@link String} object. + * @param pad a char. + * @param length a int. + * @return a {@link String} object. + */ + public static String padRight(String data, char pad, int length) { + var sb = new StringBuilder(data.length() + length); + sb.append(data); + for(var index = 0; index < length; index++) + sb.append(pad); + return sb.toString(); + } + + /** + *

padRight.

+ * + * @param data a {@link String} object. + * @param pad a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String padRight(String data, String pad, int length) { + var sb = new StringBuilder(data.length() + (length * pad.length())); + sb.append(data); + for(var index = 0; index < length; index++) + sb.append(pad); + return sb.toString(); + } + + /** + *

pad.

+ * + * @param data a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String pad(String data, int length) { + return pad(data, DEFAULT_PAD_CHAR, length); + } + + /** + *

pad.

+ * + * @param data a {@link String} object. + * @param pad a char. + * @param length a int. + * @return a {@link String} object. + */ + public static String pad(String data, char pad, int length) { + return create(pad, length) + data + create(pad, length); + } + + /** + *

pad.

+ * + * @param data a {@link String} object. + * @param pad a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String pad(String data, String pad, int length) { + return create(pad, length) + data + create(pad, length); + } + + /** + *

padCenter.

+ * + * @param data a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String padCenter(String data, int length) { + return padCenter(data, DEFAULT_PAD_CHAR, length); + } + + /** + *

padCenter.

+ * + * @param data a {@link String} object. + * @param pad a char. + * @param length a int. + * @return a {@link String} object. + */ + public static String padCenter(String data, char pad, int length) { + if(data.length() < length) { + int needed = length - data.length(); + int padNeeded = needed / 2; + StringBuilder result = new StringBuilder(); + result.append(create(pad, padNeeded)); + result.append(data); + result.append(create(pad, padNeeded)); + int remaining = length - result.length(); + result.append(create(pad, remaining)); + return result.toString(); + } + return data; + } + + /** + *

trimLeft.

+ * + * @param data a {@link String} object. + * @return a {@link String} object. + */ + public static String trimLeft(String data) { + return trimLeft(data, DEFAULT_PAD_CHAR); + } + + /** + *

trimLeft.

+ * + * @param data a {@link String} object. + * @param trim a char. + * @return a {@link String} object. + */ + public static String trimLeft(String data, char trim) { + for(var index = 0; index < data.length(); index++) + if(!(data.charAt(index) == trim)) + return data.substring(index); + return EMPTY; + } + + /** + *

trimRight.

+ * + * @param data a {@link String} object. + * @return a {@link String} object. + */ + public static String trimRight(String data) { + return trimRight(data, DEFAULT_PAD_CHAR); + } + + /** + *

trimRight.

+ * + * @param data a {@link String} object. + * @param trim a char. + * @return a {@link String} object. + */ + public static String trimRight(String data, char trim) { + int count = 0; + for(var index = data.length(); index > 0; index--) + if(data.charAt(index-1) == trim) + count++; + else + return data.substring(0, data.length() - count); + return EMPTY; + } + + /** + *

trim.

+ * + * @param data a {@link String} object. + * @return a {@link String} object. + */ + public static String trim(String data) { + return trim(data, DEFAULT_PAD_CHAR); + } + + /** + *

trim.

+ * + * @param data a {@link String} object. + * @param trim a char. + * @return a {@link String} object. + */ + public static String trim(String data, char trim) { + var result = trimLeft(data, trim); + return trimRight(result, trim); + } + + /** + *

center.

+ * + * @param text a {@link String} object. + * @param length a int. + * @return a {@link String} object. + */ + public static String center(String text, int length){ + var out = String.format("%"+length+"s%s%"+length+"s", "",text,""); + var mid = (out.length()/2); + var start = mid - (length/2); + var end = start + length; + return out.substring((int) start, (int) end); + } + + /** + *

concat.

+ * + * @param data a {@link String} object. + * @return a {@link String} object. + */ + public static String concat(String ... data) { + var sb = new StringBuilder(); + for(var d : data){ + sb.append(d); + } + return sb.toString(); + } + + /** + *

appendHexString.

+ * + * @param builder a {@link StringBuilder} object. + * @param byt a byte. + */ + public static void appendHexString(StringBuilder builder, byte byt){ + builder.append(String.format("%02X", byt)); + } + /** + *

toHexString.

+ * + * @param byt a byte. + * @return a {@link String} object. + */ + public static String toHexString(byte byt){ + return String.format("%02X", byt); + } + + /** + *

appendHexString.

+ * + * @param builder a {@link StringBuilder} object. + * @param byt a int. + */ + public static void appendHexString(StringBuilder builder, int byt){ + builder.append(String.format("%02X", (byte)byt)); + } + /** + *

toHexString.

+ * + * @param byt a int. + * @return a {@link String} object. + */ + public static String toHexString(int byt){ + return String.format("%02X", (byte)byt); + } + + /** + *

appendHexString.

+ * + * @param builder a {@link StringBuilder} object. + * @param bytes an array of {@link byte} objects. + */ + public static void appendHexString(StringBuilder builder, byte[] bytes){ + for (byte b : bytes) { + builder.append(String.format("%02X ", b)); + } + } + /** + *

toHexString.

+ * + * @param bytes an array of {@link byte} objects. + * @return a {@link String} object. + */ + public static String toHexString(byte[] bytes){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, bytes); + return sb.toString().trim(); + } + + /** + *

appendHexString.

+ * + * @param builder a {@link StringBuilder} object. + * @param buffer a {@link ByteBuffer} object. + */ + public static void appendHexString(StringBuilder builder, ByteBuffer buffer){ + appendHexString(builder, buffer.array()); + } + /** + *

toHexString.

+ * + * @param buffer a {@link ByteBuffer} object. + * @return a {@link String} object. + */ + public static String toHexString(ByteBuffer buffer){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, buffer); + return sb.toString().trim(); + } + + /** + *

appendHexString.

+ * + * @param builder a {@link StringBuilder} object. + * @param bytes an array of {@link byte} objects. + * @param offset a int. + * @param length a int. + */ + public static void appendHexString(StringBuilder builder, byte[] bytes, int offset, int length){ + appendHexString(builder, Arrays.copyOfRange(bytes, offset, length)); + } + /** + *

toHexString.

+ * + * @param bytes an array of {@link byte} objects. + * @param offset a int. + * @param length a int. + * @return a {@link String} object. + */ + public static String toHexString(byte[] bytes, int offset, int length){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, bytes, offset, length); + return sb.toString().trim(); + } + + /** + *

appendHexString.

+ * + * @param builder a {@link StringBuilder} object. + * @param buffer a {@link ByteBuffer} object. + * @param offset a int. + * @param length a int. + */ + public static void appendHexString(StringBuilder builder, ByteBuffer buffer, int offset, int length){ + appendHexString(builder, buffer.array(), offset, length); + } + /** + *

toHexString.

+ * + * @param buffer a {@link ByteBuffer} object. + * @param offset a int. + * @param length a int. + * @return a {@link String} object. + */ + public static String toHexString(ByteBuffer buffer, int offset, int length){ + StringBuilder sb = new StringBuilder(); + appendHexString(sb, buffer, offset, offset+length); + return sb.toString().trim(); + } + + +} diff --git a/libraries/pi4j-library-kernel/src/main/java/module-info.java b/libraries/pi4j-library-kernel/src/main/java/module-info.java new file mode 100644 index 00000000..0c492d5d --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/java/module-info.java @@ -0,0 +1,17 @@ + +module com.pi4j.library.kernel { + + // SLF4J + requires org.slf4j; + + // PI4J + requires com.pi4j; + + + // JNA + requires jdk.unsupported; + requires com.sun.jna; + + // EXPORTS + exports com.pi4j.library.kernel; + } diff --git a/libraries/pi4j-library-kernel/src/main/native/Makefile b/libraries/pi4j-library-kernel/src/main/native/Makefile new file mode 100644 index 00000000..32ac2ada --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/Makefile @@ -0,0 +1,69 @@ +# +# Makefile: +# pi4j-gpiod - Pi4J Java (JNA) library wrapper for kernel +# + +#DEBUG = -g -O0 +ARCH := armhf +DEBUG = -O3 +CC = $(CROSS_PREFIX)gcc +AR = $(CROSS_PREFIX)ar +RANLIB = $(CROSS_PREFIX)ranlib +SIZE = $(CROSS_PREFIX)size +STRIP = $(CROSS_PREFIX)strip +SHLIB = $(CC) -shared +STRIPLIB = $(STRIP) --strip-unneeded +INCLUDE = -I. \ + -I/$(JAVA_HOME)/include \ + -I/$(JAVA_HOME)/include/linux \ + -I/usr/lib/jvm/jdk-11-oracle/include \ + -I/usr/lib/jvm/jdk-11-oracle/include/linux \ + -I/usr/lib/jvm/jdk-11-oracle-arm32-vfp-hflt/include \ + -I/usr/lib/jvm/jdk-11-oracle-arm32-vfp-hflt/include/linux \ + -I/usr/lib/jvm/java-11-openjdk-amd64/include \ + -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux \ + -I/usr/lib/jvm/java-11-openjdk-armhf/include \ + -I/usr/lib/jvm/java-11-openjdk-armhf/include/linux \ + +CFLAGS := $(DEBUG) -Wall $(INCLUDE) -Winline -pipe $(CARGS) -fPIC +LIBS = -L lib/$(ARCH)/pi4j-kernel + +TARGET=libpi4j-kernel.so + +# Should not alter anything below this line +############################################################################### + +SRC = com_pi4j_library_kernel_internal.c + +OBJ = $(SRC:.c=.o) + +all: $(OBJ) + @echo [LINK with DYNAMICALLY linked libraries] + @$(CC) $(OBJ) -shared -o $(TARGET) $(INCLUDE) $(LIBS) + +.c.o: + @echo [COMPILE] $< + @$(CC) -c $(CFLAGS) $< -o $@ + +clean: + rm -f $(OBJ) $(TARGET) *~ core tags Makefile.bak + +tags: $(SRC) + @echo [ctags] + @ctags $(SRC) + +depend: + makedepend -Y $(SRC) + +install: $(TARGET) + @echo [install] + install -m 0755 -d /usr/local/lib + install -m 0755 -d /usr/local/include + install -m 0644 $(TARGET) /usr/local/lib + +uninstall: + @echo [uninstall] + rm -f /usr/local/lib/$(TARGET) + +# DO NOT DELETE +com_pi4j_library_kernel_internal.o: com_pi4j_library_kernel_internal.h diff --git a/libraries/pi4j-library-kernel/src/main/native/build-docker.sh b/libraries/pi4j-library-kernel/src/main/native/build-docker.sh new file mode 100755 index 00000000..7a840635 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/build-docker.sh @@ -0,0 +1,59 @@ +#!/bin/bash -e +### +# #%L +# ********************************************************************** +# ORGANIZATION : Pi4J +# PROJECT : Pi4J :: JNA Native Binding Library for Kernal +# FILENAME : build-docker.sh +# +# This file is part of the Pi4J project. More information about +# this project can be found here: https://pi4j.com/ +# ********************************************************************** +# %% +# Copyright (C) 2012 - 2019 Pi4J +# %% +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# #L% +### + +echo +echo "**********************************************************************" +echo "**********************************************************************" +echo "* *" +echo "* COMPILE Pi4J NATIVE LIBRARIES USING Pi4J DOCKER BUILDER IMAGE *" +echo "* *" +echo "**********************************************************************" +echo "**********************************************************************" +echo + +# validate compatible that "docker" exists +DOCKER=$(which docker) +if [[ ("$DOCKER" == "") ]]; then + echo "This native build is requires that 'docker' (https://www.docker.com/) is " + echo "installed and running on an Intel/AMD or ARM 64-bit platform." + echo "BUILD ABORTED; REASON: Missing 'docker' executable." + exit +fi + +# set executable permissions on build scripts +chmod +x build.sh + +# ------------------------------------------------------------- +# BUILD NATIVE LIBRARIES USING THE Pi4J DOCKER BUILDER IMAGE +# FOR ARMv6,ARMv7, ARMv8 32-BIT (ARMHF) +# FOR ARMv8 64-BIT (ARM64) +# ------------------------------------------------------------- +docker pull pi4j/pi4j-builder-native:2.0 +docker run --user "$(id -u):$(id -g)" --rm --volume $(pwd):/build pi4j/pi4j-builder-native:2.0 $@ diff --git a/libraries/pi4j-library-kernel/src/main/native/build-libpi4j.sh b/libraries/pi4j-library-kernel/src/main/native/build-libpi4j.sh new file mode 100755 index 00000000..9653d9c9 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/build-libpi4j.sh @@ -0,0 +1,72 @@ +#!/bin/bash -e +### +# #%L +# ********************************************************************** +# ORGANIZATION : Pi4J +# PROJECT : Pi4J :: JNA Native Binding Library for kernel +# FILENAME : build-libpi4j.sh +# +# This file is part of the Pi4J project. More information about +# this project can be found here: https://pi4j.com/ +# ********************************************************************** +# %% +# Copyright (C) 2012 - 2021 Pi4J +# %% +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# #L% +### + +# ---------------------------------------------- +# build latest Pi4J kernel JNA Wrapper Library +# ---------------------------------------------- + +echo +echo "=============================================================================" +echo " STARTED BUILDING Pi4J-Kernel JNA NATIVE LIBRARY: ('${ARCH}/pi4j-kernel/libpi4j-kernel')" +echo "=============================================================================" +echo " - FOR ARCHITECTURE : ${ARCH}" +echo " - USING COMPILER : ${CC}" +echo " - USING CROSS PREFIX : ${CROSS_PREFIX}" +echo "-----------------------------------------------------------------------------" +echo + + +# set default kernel directory if not already defined +if [ -z $KERNEL_DIRECTORY ]; then + KERNEL_DIRECTORY=kernel +fi + +# ------------------------------------------------------ +# BUILD LIBPI4J-KERNEL +# ------------------------------------------------------ +echo +echo "=====================================" +echo "BUILDING: ${ARCH}/pi4j-kernel/libpi4j-kernel" +echo "=====================================" + +# perform compile +mkdir -p lib/${ARCH}/pi4j-kernel +make clean all \ + --always-make \ + CROSS_PREFIX=${CROSS_PREFIX} \ + CC=${CC} \ + ARCH=${ARCH} \ + TARGET=lib/${ARCH}/pi4j-kernel/libpi4j-kernel.so $@ + +echo +echo "-----------------------------------------------------------------------------" +echo " FINISHED BUILDING Pi4J-kernel JNA NATIVE LIBRARY: ('${ARCH}/libpi4j-kernel')" +echo "-----------------------------------------------------------------------------" +echo \ No newline at end of file diff --git a/libraries/pi4j-library-kernel/src/main/native/build-prerequisites.sh b/libraries/pi4j-library-kernel/src/main/native/build-prerequisites.sh new file mode 100755 index 00000000..6e15e612 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/build-prerequisites.sh @@ -0,0 +1,130 @@ +#!/bin/bash +### +# #%L +# ********************************************************************** +# ORGANIZATION : Pi4J +# PROJECT : Pi4J :: JNA Native Binding Library for kernel +# FILENAME : build-prerequisites.sh +# +# This file is part of the Pi4J project. More information about +# this project can be found here: https://pi4j.com/ +# ********************************************************************** +# %% +# Copyright (C) 2012 - 2021 Pi4J +# %% +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# #L% +### +# ---------------------------------- +# install prerequisites +# ---------------------------------- +if [ ! -z "`type apt-get 2>/dev/null;`" ]; then + + # build-essential + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' build-essential|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + sudo apt-get --force-yes --yes install build-essential + else + echo " [PREREQUISITE] 'build-essential' already installed."; + fi + + # GCC + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' gcc|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + sudo apt-get --force-yes --yes install gcc + else + echo " [PREREQUISITE] 'gcc' already installed."; + fi + + # GIT + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' git|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + sudo apt-get --force-yes --yes install git + else + echo " [PREREQUISITE] 'git' already installed."; + fi + + # TREE + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' tree|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + sudo apt-get --force-yes --yes install tree + else + echo " [PREREQUISITE] 'tree' already installed."; + fi + + # gcc-arm-linux-gnueabihf + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' gcc-arm-linux-gnueabihf|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'gcc-arm-linux-gnueabihf'..."; + sudo apt-get --force-yes --yes install gcc-arm-linux-gnueabihf + else + echo " [PREREQUISITE] 'gcc-arm-linux-gnueabihf' already installed."; + fi + + # gcc-aarch64-linux-gnu + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' gcc-aarch64-linux-gnu|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'gcc-aarch64-linux-gnu'..."; + sudo apt-get --force-yes --yes install gcc-aarch64-linux-gnu + else + echo " [PREREQUISITE] 'gcc-aarch64-linux-gnu' already installed."; + fi + + + # autoconf + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' autoconf|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'autoconf'..."; + sudo apt-get --force-yes --yes install autoconf + else + echo " [PREREQUISITE] 'autoconf' already installed."; + fi + + # autoconf-archive + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' autoconf-archive|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'autoconf-archive'..."; + sudo apt-get --force-yes --yes install autoconf-archive + else + echo " [PREREQUISITE] 'autoconf-archive' already installed."; + fi + + # pkg-config + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' pkg-config|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'pkg-config'..."; + sudo apt-get --force-yes --yes install pkg-config + else + echo " [PREREQUISITE] 'pkg-config' already installed."; + fi + + # libtool + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' libtool|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'libtool'..."; + sudo apt-get --force-yes --yes install libtool + else + echo " [PREREQUISITE] 'libtool' already installed."; + fi + + # pkg-config + INSTALLED=$(dpkg-query -W --showformat='${Status}\n' pkg-config|grep "install ok installed") + if [[ "$?" == "1" ]] ; then + echo " [PREREQUISITE] installing 'pkg-config'..."; + sudo apt-get --force-yes --yes install pkg-config + else + echo " [PREREQUISITE] 'pkg-config' already installed."; + fi +fi diff --git a/libraries/pi4j-library-kernel/src/main/native/build.sh b/libraries/pi4j-library-kernel/src/main/native/build.sh new file mode 100755 index 00000000..c2db47a7 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/build.sh @@ -0,0 +1,132 @@ +#!/bin/bash -e +### +# #%L +# ********************************************************************** +# ORGANIZATION : Pi4J +# PROJECT : Pi4J :: JNA Native Binding Library for Kernel +# FILENAME : build.sh +# +# This file is part of the Pi4J project. More information about +# this project can be found here: https://pi4j.com/ +# ********************************************************************** +# %% +# Copyright (C) 2012 - 2021 Pi4J +# %% +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# #L% +### + +echo +echo "**********************************************************************" +echo "* *" +echo "* Pi4J Kernel LIBRARY NATIVE BUILD *" +echo "* *" +echo "**********************************************************************" +echo + +# ------------------------------------------------------ +# VALIDATE BUILD PLATFORM; PRECONDITIONS +# ------------------------------------------------------ + +# validate compatible OS/Kernel +KERNEL=$(uname -s) +if [[ ("$KERNEL" != "Linux") ]]; then + echo "This native build is only supported on Linux-based systems running on an Intel/AMD or ARM 64-bit platform." + echo "BUILD ABORTED; REASON: KERNEL='$KERNEL'; EXPECTED='Linux'" + echo "(NOTE: You can run this build using the Pi4J Docker Builder images from OSX, Windows, Linux.)" + exit 1 +fi + +# validate compatible CPU architecture +ARCHITECTURE=$(uname -m) +if [[ (("$ARCHITECTURE" != "aarch64") && ("$ARCHITECTURE" != "amd64") && ("$ARCHITECTURE" != "x86_64")) ]]; then + echo "This native build is only supported on Linux-based systems running on an Intel/AMD or ARM 64-bit platform." + echo "BUILD ABORTED; REASON: ARCHITECTURE='$ARCHITECTURE'; EXPECTED='aarch64|amd64|x86_64'" + exit 1 +fi + +# ------------------------------------------------------ +# ENSURE DEPENDENCY SCRIPTS ARE EXECUTABLE +# ------------------------------------------------------ +# set executable permissions on build scripts +chmod +x build-prerequisites.sh +chmod +x build-libpi4j.sh + +# ------------------------------------------------------ +# INSTALL BUILD PREREQUISITES +# ------------------------------------------------------ +echo +echo "=====================================" +echo " INSTALLING Pi4J BUILD PREREQUISITES " +echo "=====================================" +echo + +# if running inside a Pi4J Docker Builder image, then there is no need to install prerequisites +if [[ "${PI4J_BUILDER}" != "" ]]; then + echo "Running inside a Pi4J Docker Builder image; [version=${PI4J_BUILDER}; arch=${PI4J_BUILDER_ARCH}]" + echo "No need to check or install build environment prerequisites." +else + # if this is a Linux-based system and a 64-bit Intel/AMD or ARM platform, then we can install the prerequisites + # download and install development prerequisites + ./build-prerequisites.sh +fi + +# ------------------------------------------------------ +# JAVA_HOME ENVIRONMENT VARIABLE +# ------------------------------------------------------ +echo +echo "=========================================" +echo " CHECKING JAVA_HOME ENVIRONMENT VARIABLE " +echo "=========================================" +echo +if [[ -n "$JAVA_HOME" ]]; then + echo "'JAVA_HOME' already defined as: $JAVA_HOME"; +else + export JAVA_HOME=$(readlink -f /usr/bin/javac | sed "s:bin/javac::") + echo "'JAVA_HOME' was not defined; attempting to use: $JAVA_HOME"; +fi + +# ------------------------------------------------------ +# BUILD NATIVE LIBRARIES FOR ARMv6,ARMv7,ARMv8 32-BIT (ARMHF) +# USING THE LOCALLY INSTALLED ARM CROSS-COMPILER +# ------------------------------------------------------ +export CROSS_PREFIX=arm-linux-gnueabihf- +export CC=arm-linux-gnueabihf-gcc +export ARCH=armhf +export HOST=arm-linux-gnueabihf +./build-libpi4j.sh $@ + +# ------------------------------------------------------ +# BUILD NATIVE LIBRARIES FOR ARMv8 64-BIT (ARM64) +# USING THE LOCALLY INSTALLED ARM64 CROSS-COMPILER +# ------------------------------------------------------ +export CROSS_PREFIX=aarch64-linux-gnu- +export CC=aarch64-linux-gnu-gcc +export ARCH=aarch64 +export HOST=aarch64-linux-gnu +./build-libpi4j.sh $@ + +echo "======================================" +echo " Pi4J kernal LIBRARY NATIVE ARTIFACTS " +echo "======================================" +tree -R lib + +echo +echo "**********************************************************************" +echo "* *" +echo "* Pi4J Kernel LIBRARY NATIVE BUILD *" +echo "* *" +echo "**********************************************************************" +echo \ No newline at end of file diff --git a/libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.c b/libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.c new file mode 100644 index 00000000..8d6b7fa0 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.c @@ -0,0 +1,355 @@ + +/* * Copyright (C) 2012 - 2024 Pi4J + * %% +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*- +* #%L +* ********************************************************************** +* ORGANIZATION : Pi4J +* PROJECT : Pi4J :: EXTENSION +* FILENAME : com_pi4j_library_kernel_internal.c +* +* This file is part of the Pi4J project. More information about +* this project can be found here: https://pi4j.com/ +* ********************************************************************** +* %% +*/ + + +#include +#include + +#include + +#include +#include +#include + + + + +#include + + +#include + + + +#include +#include + +#include +#include + +#include "com_pi4j_library_kernel_internal.h" + +#include + + + +#define OSC_FREQ 19200000 // crystal frequency + + + +/** + * Cleanup previously allocated device memory and buffers. + * + * @param com_pi4j_library_kernel_internal com_pi4j_library_kernel_internal instance pointer. + * + * @returns None + */ +void spiApi_cleanup(spiApi_t *spiApi) +{ + fprintf(stderr, "Enter spiApi_cleanup"); + spiApi_device_t *device = spiApi->device; + + + if (device && (spiApi->spi_fd > 0)) + { + close(spiApi->spi_fd); + } + + if (device) { + free(device); + } + spiApi->device = NULL; +} + + + + + +int spi_transfer(spiApi_t *spiApi) +{ + int ret = spiApi_SUCCESS; + struct spi_ioc_transfer tr; + + memset(&tr, 0, sizeof(struct spi_ioc_transfer)); + tr.tx_buf = 0; // todo get buffer from spiApi (unsigned long)spiApi->device->pxl_raw; + tr.rx_buf = 0; + tr.len = 0; // todo get buffer length from spiApi >max_count, spiApi->freq); + + ret = ioctl(spiApi->spi_fd, SPI_IOC_MESSAGE(1), &tr); + if (ret < 1) + { + fprintf(stderr, "Can't send spi message"); + return spiApi_ERROR_SPI_TRANSFER; + } + + return ret; +} + + +/* + * + * Application API Functions + * + */ + +static int spi_init(spiApi_t *spiApi) +{ + fprintf(stderr, "Enter spi_init \n"); + + int spi_fd; + static uint8_t mode; + static uint8_t bits = 8; + uint32_t speed = spiApi->max_freq; + spiApi_device_t *device = spiApi->device; + + +fprintf(stderr, "spiApi \n"); + unsigned char *p = (unsigned char *)spiApi; + int i; + for (i = 0; i < 32 ; i++) + fprintf(stderr, "%02x ", p[i]); + + fprintf(stderr, "\n device %02x \n",device); + p = (unsigned char *)device; + for (i = 0; i < 32 ; i++) + fprintf(stderr, "%02x ", p[i]); + fprintf(stderr, " \n"); + + + // use bus and chip select supplied by user via the provider + + char spiOpen[30]; + int rc = sprintf(spiOpen,"/dev/spidev%d.%d",spiApi->bus, spiApi->cs); + if (rc < 0) { + fprintf(stderr, "Cannot create required string /dev/spidev?.? \n"); + return spiApi_ERROR_SPI_SETUP; + } + spi_fd = open(spiOpen, O_RDWR); + if (spi_fd < 0) { + fprintf(stderr, "Cannot open /dev/spidev%d.%d Review config.txt update\n", spiApi->bus, spiApi->cs); + return spiApi_ERROR_SPI_SETUP; + } + + fprintf(stderr, "C spiApi->max_freq %d\n", spiApi->max_freq); + fprintf(stderr, "C device->spi_fd %d\n", spiApi->spi_fd); + fprintf(stderr, "C device->driver_mode %d\n", device->driver_mode); + fprintf(stderr, "C spiApi->device->driver_mode %d\n", spiApi->device->driver_mode); + + + + spiApi->spi_fd = spi_fd; + mode = device->driver_mode; + + fprintf(stderr, "Opened %s FD %d \n " , spiOpen, spi_fd); + fprintf(stderr, "Opened /dev/%s Device FD %d \n " ,spiOpen, spiApi->spi_fd); + // SPI mode + if (ioctl(spi_fd, SPI_IOC_WR_MODE, &mode) < 0) + { + return spiApi_ERROR_SPI_SETUP; + } + if (ioctl(spi_fd, SPI_IOC_RD_MODE, &mode) < 0) + { + return spiApi_ERROR_SPI_SETUP; + } + + // Bits per word + if (ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) + { + return spiApi_ERROR_SPI_SETUP; + } + if (ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) + { + return spiApi_ERROR_SPI_SETUP; + } + + // Max speed Hz + if (ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) + { + return spiApi_ERROR_SPI_SETUP; + } + if (ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) + { + return spiApi_ERROR_SPI_SETUP; + } + + return spiApi_SUCCESS; +} + +/** + * Shut down and cleanup memory. + * + * @param spiApi spiApi instance pointer. + * + * @returns None + */ +void spiApi_fini(spiApi_t *spiApi) +{ + fprintf(stderr, "Enter spiApi_fini"); + + spiApi_cleanup(spiApi); +} + +/** + * Allocate and initialize memory, buffers, pages,. + * + * @param spiApi spiApi instance pointer. + * + * @returns 0 on success, -1 otherwise. + */ +int spiApi_init(spiApi_t *spiApi) +{ + spiApi_device_t *device = spiApi->device; + + fprintf(stderr," spiApi_init \n"); + + fprintf(stderr, "spiApi \n"); + unsigned char *p = (unsigned char *)spiApi; + int i; + for (i = 0; i < 32 ; i++) + fprintf(stderr, "%02x ", p[i]); + + fprintf(stderr, "\n device %02x \n", device); + p = (unsigned char *)device; + for (i = 0; i < 32 ; i++) + fprintf(stderr, "%02x ", p[i]); + fprintf(stderr, " \n"); + + fprintf(stderr, "\n device driver mode : %02x \n", spiApi->device->driver_mode); + + fprintf(stderr, "\n"); + + int ret = spiApi_SUCCESS; + + // Use the structure allocated in the Java code + // spiApi->device = malloc(sizeof(*spiApi->device)); + if (!spiApi->device) + { + return spiApi_ERROR_OUT_OF_MEMORY; + } + // memset(spiApi->device, 0, sizeof(*spiApi->device)); + device = spiApi->device; + + fprintf(stderr," B4 spiInit -> Driver Mode ??? %d \n", device->driver_mode); + + + fprintf(stderr," yes Driver in SPI Mode \n"); + ret = spi_init(spiApi); + + + return ret; +} + + int spiApi_write_b( spiApi_t * spi, char b, int speed){ + int ret = spiApi_SUCCESS; + return ret; + } + + + int spiApi_read_b( spiApi_t * spi, char b, int speed){ + int ret = spiApi_SUCCESS; + return ret; + } + + int spiApi_read( spiApi_t * spi,const void * buffer, int offset, int length, int speed){ + int ret = spiApi_SUCCESS; + return ret; + } + + + int transfer( spiApi_t * spi,const void * write, int writeOffset,const void * read, int readOffset, int numberOfBytes, int speed){ + int ret = spiApi_SUCCESS; + return ret; + } + + +int spiApi_write( spiApi_t * spi, const void * buf,int offset, int len, int speed){ + fprintf(stderr, ">>> entered spiApi_write \n"); + + fprintf(stderr, "\n Write buffer data len %d, speed %d, pointer %02x \n", len, speed, buf); + + int ret = spiApi_SUCCESS; + uint8_t spiBufTx[len]; + uint8_t spiBufRx[len]; + + unsigned char * p = (unsigned char *)buf; + for(int i = 0; i < len;i++){ + spiBufTx[(i)] = p[i+offset]; + } + for (int i = 0; i < len ; i++) + fprintf(stderr, "%02x ", p[i]); + fprintf(stderr, " \n"); + + + struct spi_ioc_transfer tr; + memset (&tr, 0, sizeof(tr)); + tr.tx_buf = (unsigned long)&spiBufTx; + tr.rx_buf = (unsigned long)&spiBufRx; + tr.len = len; + tr.speed_hz = speed; + tr.delay_usecs = 0; + tr.bits_per_word = 8; + + ret = ioctl(spi->spi_fd, SPI_IOC_MESSAGE(1), &tr); + // int spi_write(struct spi_device * spi, const void * buf, int len, ) + if (ret < 1) + { + fprintf(stderr, "Can't send spi message"); + return spiApi_ERROR_SPI_TRANSFER; + } + return ret; +} + + + +int spiApi_render_test(spiApi_test_t *spiApitest){ + int ret = spiApi_SUCCESS; + int what = spiApitest->waldo; + + fprintf(stderr, "spiApi_test_t \n"); + unsigned char *p = (unsigned char *)spiApitest; + int i; + for (i = 0; i < 32 ; i++) + fprintf(stderr, "%02x ", p[i]); + fprintf(stderr, "\n"); + + fprintf(stderr, "Before spiApi_render_testtt ran %d \n", what); + + what = 21; + spiApitest->waldo = what; + what = spiApitest->waldo; + fprintf(stderr, "After spiApi_render_testttt %d RC %d \n", what, ret); + + spiApitest->where = spiApitest->where *2; +return(ret); +} + + + + + diff --git a/libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.h b/libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.h new file mode 100644 index 00000000..6ff6e79e --- /dev/null +++ b/libraries/pi4j-library-kernel/src/main/native/com_pi4j_library_kernel_internal.h @@ -0,0 +1,129 @@ + + +/* * Copyright (C) 2012 - 2024 Pi4J + * %% +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*- +* #%L +* ********************************************************************** +* ORGANIZATION : Pi4J +* PROJECT : Pi4J :: EXTENSION +* FILENAME : com_pi4j_library_kernel_internal.h +* +* This file is part of the Pi4J project. More information about +* this project can be found here: https://pi4j.com/ +* ********************************************************************** +* %% +*/ + +#ifndef __com_pi4j_library_kernel_internal_H__ +#define __com_pi4j_library_kernel_internal_H__ + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + + +#define spiApi_TARGET_FREQ 800000 // Can go as low as 400000 + + + +typedef uint32_t spiApi_led_t; //< 0xWWRRGGBB + + + + +typedef struct spiApi_test { + int waldo; + int where; + }spiApi_test_t; + + +typedef struct spiApi_device +{ + int driver_mode; + int CEx; // Chip enable low:0 + int CExResrv; // chip enable SPI1 Not used + int AuxSPI; // Auxiliary SPI if 1, not used + int wire; // 0:not 3 wire, 1 is 3 wire + int bits; // bits before MISO begine, valid wire is 3 + int firstBitTX; // 1, LSB sent first, 0 LSB first + int firstBitRX; // 1, LSB received first, 0 LSB first + int wSize; // 0 : 8 bit word. range 0-32 +} spiApi_device_t; + + +typedef struct spiApi +{ + int max_freq; + int spi_fd; + int bus; + int cs; + struct spiApi_device *device; //< Private data for driver use + } spiApi_t; + + + + + int spiApi_SUCCESS = 0; + int spiApi_ERROR_GENERIC = -1; + int spiApi_ERROR_OUT_OF_MEMORY = -2; + int spiApi_ERROR_HW_NOT_SUPPORTED = -3; + int spiApi_ERROR_MEM_LOCK = -4; + int spiApi_ERROR_MMAP = -5; + /*X(-6, spiApi_ERROR_MAP_REGISTERS, "Unable to map registers into userspace"), \ + X(-7, spiApi_ERROR_GPIO_INIT, "Unable to initialize GPIO"), \ + X(-8, spiApi_ERROR_PWM_SETUP, "Unable to initialize PWM"), \ + X(-9, spiApi_ERROR_MAILBOX_DEVICE, "Failed to create mailbox device"), \ + X(-10, spiApi_ERROR_DMA, "DMA error"), \ + X(-11, spiApi_ERROR_ILLEGAL_GPIO, "Selected GPIO not possible"), \ + X(-12, spiApi_ERROR_PCM_SETUP, "Unable to initialize PCM"), \ + */ + int spiApi_ERROR_SPI_SETUP = -13; + int spiApi_ERROR_SPI_TRANSFER = -14; + + + //< Tear it all down +int spiApi_render_test(spiApi_test_t *spiApitest); + + + + +int spiApi_write(spiApi_t * spi, const void * buf, int offset, int len, int speed); + int spiApi_write_b(spiApi_t * spi, char b, int speed); + + + int spiApi_read_b(spiApi_t * spi, char b, int speed); + + int spiApi_read(spiApi_t * spi,const void *buffer, int offset, int length, int speed); + + + int transfer(spiApi_t * spi, const void *write, int writeOffset,const void * read, int readOffset, int numberOfBytes, int speed); + + + + int spi_transfer(spiApi_t *spiApi); + + +int spiApi_init(spiApi_t *spiApi); //< Initialize buffers/hardware +void spiApi_fini(spiApi_t *spiApi); //< Tear it all down + +#ifdef __cplusplus +} +#endif + +#endif /* __com_pi4j_library_kernel_internal_H__ */ diff --git a/libraries/pi4j-library-kernel/src/test/java/com/pi4j/test/SPIapiTest.java b/libraries/pi4j-library-kernel/src/test/java/com/pi4j/test/SPIapiTest.java new file mode 100644 index 00000000..8e1a97c0 --- /dev/null +++ b/libraries/pi4j-library-kernel/src/test/java/com/pi4j/test/SPIapiTest.java @@ -0,0 +1,199 @@ +package com.pi4j.test; + + +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.exception.LifecycleException; +import com.pi4j.library.kernel.SPIapi; +import com.pi4j.library.kernel.SPIapiImpl; +import com.pi4j.library.kernel.SPIapiIntrf; +import com.pi4j.util.Console; +import sun.misc.Signal; +import sun.misc.SignalHandler; + +import java.util.Scanner; + + + + + + + +public class SPIapiTest { + + + public static void waitForInput() { + int rval = 0; + Scanner scan = new Scanner(System.in); + + System.out.println("Hit any key to continue "); + scan.next(); + + } + + public static void main(String[] args) throws Exception { + var console = new Console(); + Context pi4j = Pi4J.newAutoContext(); + + console.title("<-- The Pi4J V2 Project Extension -->", "Use JNA and the Kernel SPI APIs"); + + System.setProperty("pi4j.library.path", "/home/pi/Pi4J_V2/pi4j-v2/libraries/pi4j-library-kernel/target/lib/aarch64/pi4j-kernel"); + + int pixels = 1; + int duty = 0; + int freq = 1; + int duration = 0; + boolean doTest = false; + + Signal.handle(new Signal("INT"), new SignalHandler() { + public void handle(Signal sig) { + System.out.println("Performing ctl-C shutdown"); + try { + pi4j.shutdown(); + } catch (LifecycleException e) { + e.printStackTrace(); + } + // Thread.dumpStack(); + System.exit(2); + } + }); + + for (int i = 0; i < args.length; i++) { + String o = args[i]; + if (o.contentEquals("-duty")) { + String a = args[i + 1]; + duty = Integer.parseInt(a); + i++; + } else if (o.contentEquals("-freq")) { + String a = args[i + 1]; + freq = Integer.parseInt(a); + i++; + } else if (o.contentEquals("-duration")) { + String a = args[i + 1]; + duration = Integer.parseInt(a); + i++; + } else if (o.contentEquals("-test")) { + doTest = true; + } else { + console.println(" !!! Invalid Parm " + o); + console.println(" -duty, -freq, - duration -test"); + System.exit(42); + } + } + + + SPIapiImpl functionsV1 = SPIapiIntrf.createSPIapiImpl(); + SPIapi.spiApi_test dev_test = new SPIapi.spiApi_test(); + dev_test.waldo = 5; + dev_test.where = 20; + + int returnVal = functionsV1.spiApi_render_test(dev_test); + console.println("Render_test v1 : " + dev_test.waldo + " retVal : " + returnVal); + console.println("Render_test v1 : " + dev_test.where + " retVal : " + returnVal); + + + SPIapi.spiApi_t spiApi = new SPIapi.spiApi_t(); + + + spiApi.dev = new SPIapi.spiApi_device_p.ByReference(); + + spiApi.spi_FD = 21; + spiApi.max_freq = 64000000; + spiApi.dev.driver_mode = 0; + spiApi.bus = 0; + spiApi.cs = 0; + + int testcaseSpeed = 8000000; + + console.println("B4 freq : " + spiApi.max_freq); + + console.println("B4 spi_FD : " + spiApi.spi_FD); + + console.println("B4 driver_mode: " + spiApi.dev.driver_mode); + + + int ret = functionsV1.spiApi_init(spiApi); + + console.println("freq : " + spiApi.max_freq); + + console.println("spi_FD : " + spiApi.spi_FD); + + console.println("driver_mode: " + spiApi.dev.driver_mode); + + + console.println("Render v1 : " + functionsV1.spiApi_render(spiApi)); + + byte[] redData = new byte[24+2]; + byte[] blueData = new byte[24]; + // RED + for (int i = 0; i < 8; i++) { + redData[i] = (byte) 0xc0; + } + for (int i = 8; i < 16; i++) { + redData[i] = (byte) 0xf8; + } + + for (int i = 16; i < 24; i++) { + redData[i] = (byte) 0xc0; + } + + byte[] offData = new byte[24]; + for (int i = 0; i < 24; i++) { + offData[i] = (byte) 0xC0; + } + + // blue + for (int i = 0; i < 8; i++) { + blueData[i] = (byte) 0xc0; + } + for (int i = 8; i < 16; i++) { + blueData[i] = (byte) 0xc0; + } + + for (int i = 16; i < 24; i++) { + blueData[i] = (byte) 0xf8; + } + int speed = spiApi.max_freq; + ret = functionsV1.spiApi_write(spiApi, redData, 0, redData.length, testcaseSpeed); + console.println("spi RED write offset=0 ret value : " + ret); + + waitForInput(); + + ret = functionsV1.spiApi_write(spiApi, offData,0, offData.length, testcaseSpeed); + console.println("spi OFF write ret value : " + ret); + waitForInput(); + + // the way buffer size is tested, I made the buffer 2 bytes bigger than needed + // make the call with the correct buff size. Just that in operation + // we are going to start the write at redData[2] + ret = functionsV1.spiApi_write(spiApi, redData, 2, 24, testcaseSpeed); + console.println("spi RED write offset=2 ret value : " + ret); + + waitForInput(); + + ret = functionsV1.spiApi_write(spiApi, offData,0, offData.length, testcaseSpeed); + console.println("spi OFF write ret value : " + ret); + waitForInput(); + + + ret = functionsV1.spiApi_write(spiApi, redData, 0, redData.length, testcaseSpeed); + console.println("spi RED write ret value : " + ret); + waitForInput(); + + ret = functionsV1.spiApi_write(spiApi, offData, 0, offData.length, testcaseSpeed); + console.println("spi OFF write ret value : " + ret); + waitForInput(); + + ret = functionsV1.spiApi_write(spiApi, blueData, 0, blueData.length, testcaseSpeed) ; + console.println("spi BLUE write ret value : " + ret); + waitForInput(); + + ret = functionsV1.spiApi_write(spiApi, offData, 0, offData.length, testcaseSpeed) ; + console.println("spi OFF write ret value : " + ret); + waitForInput(); + + + } + +} + diff --git a/libraries/pi4j-library/pom.xml b/libraries/pi4j-library/pom.xml index 6c5415f3..9ffd9d12 100644 --- a/libraries/pi4j-library/pom.xml +++ b/libraries/pi4j-library/pom.xml @@ -30,6 +30,7 @@ ../pi4j-library-pigpio ../pi4j-library-linuxfs ../pi4j-library-gpiod + ../pi4j-library-kernel diff --git a/plugins/pi4j-plugin-rp1spi/pom.xml b/plugins/pi4j-plugin-rp1spi/pom.xml new file mode 100644 index 00000000..6f6879c1 --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/pom.xml @@ -0,0 +1,113 @@ + + + + com.pi4j + pi4j-plugin + 2.6.1-SNAPSHOT + ../pi4j-plugin/pom.xml + + 4.0.0 + + + pi4j-plugin-rp1spi + Pi4J :: PLUGIN :: RP1 SPI I/O Providers + Pi4J Library Plugin for SPI I/O Providers + jar + + + com.jcraft + jsch + ${jsch.version} + + + com.pi4j + pi4j-library-kernel + ${project.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + process-sources + + copy-dependencies + + + runtime + com.pi4j + ${project.build.directory}/dependencies + false + false + true + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + transfer-compiled-pi4j-jar + install + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/Rp1SpiPlugin.java b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/Rp1SpiPlugin.java new file mode 100644 index 00000000..1ce9d7a8 --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/Rp1SpiPlugin.java @@ -0,0 +1,56 @@ +package com.pi4j.plugin.rp1spi; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import com.pi4j.boardinfo.util.BoardInfoHelper; +import com.pi4j.extension.Plugin; +import com.pi4j.extension.PluginService; +import com.pi4j.plugin.rp1spi.provider.Rp1SpiProvider; + +import com.pi4j.provider.Provider; + + + + +public class Rp1SpiPlugin implements Plugin { + + /** + * Constant NAME=KernelSpi" + */ + public static final String NAME = "KernelSpi"; + /** + * Constant ID="KernelSpi" + */ + public static final String ID = "rp1"; + + /** + * Constant SPI_PROVIDER_NAME="NAME + SPI Provider" + */ + public static final String SPI_PROVIDER_NAME = NAME + " Spi Provider"; + /** + * Constant SPI_OUTPUT_PROVIDER_ID="ID + -spi" + */ + public static final String SPI_PROVIDER_ID = ID + "-spi"; + + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * {@inheritDoc} + */ + @Override + public void initialize(PluginService service) { + + + // create & define supported Linux file system I/O providers that will be exposed to Pi4J via this plugin + Provider[] providers = { + Rp1SpiProvider.newInstance() + }; + + // register the SPI Provider with the plugin service + service.register(providers); + } +} diff --git a/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1Spi.java b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1Spi.java new file mode 100644 index 00000000..806b17dd --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1Spi.java @@ -0,0 +1,232 @@ +package com.pi4j.plugin.rp1spi.provider; + + +import com.pi4j.context.Context; +import com.pi4j.exception.InitializeException; +import com.pi4j.io.spi.*; +import com.pi4j.library.kernel.SPIapi; +import com.pi4j.library.kernel.SPIapiImpl; +import com.pi4j.library.kernel.SPIapiIntrf; + + +public class Rp1Spi extends SpiBase implements Spi { + + protected SPIapiImpl functionsSpi = null; + + protected SPIapi.spiApi_t spiApi = null; + protected static int SPI_BUS_MASK = 0x0100; + protected static int SPI_MODE_MASK = 0x0003; + + /** + *

Constructor for Rp1Spi

+ * + * @param provider a {@link SpiProvider} object. + * @param config a {@link SpiConfig} object. + *

+ * ------------------------------------------------------------------ + * spiFlags consists of the least significant 22 bits. + * ------------------------------------------------------------------ + * 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * b b b b b b R T n n n n W A u2 u1 u0 p2 p1 p0 m m + *

+ * [mm] defines the SPI mode. + *

+ * Mode POL PHA + * 0 0 0 + * 1 0 1 + * 2 1 0 + * 3 1 1 + *

+ * [px] is 0 if CEx is active low (default) and 1 for active high. + * [ux] is 0 if the CEx GPIO is reserved for SPI (default) and 1 otherwise. + * [A] is 0 for the main SPI, 1 for the auxiliary SPI. + * [W] is 0 if the device is not 3-wire, 1 if the device is 3-wire. Main SPI only. + * [nnnn] defines the number of bytes (0-15) to write before switching the MOSI line to MISO to read data. + * This field is ignored if W is not set. Main SPI only. + * [T] is 1 if the least significant bit is transmitted on MOSI first, the default (0) shifts the + * most significant bit out first. Auxiliary SPI only. + * [R] is 1 if the least significant bit is received on MISO first, the default (0) receives the most + * significant bit first. Auxiliary SPI only. + * [bbbbbb] defines the word size in bits (0-32). The default (0) sets 8 bits per word. Auxiliary SPI only. + */ + public Rp1Spi(SpiProvider provider, SpiConfig config) { + super(provider, config); + + // set local reference instance + try { + this.functionsSpi = SPIapiIntrf.createSPIapiImpl(); + } catch (java.io.IOException e) { + throw new RuntimeException(e); + // todo log something... + } + + this.spiApi = new SPIapi.spiApi_t(); + + + this.spiApi.dev = new SPIapi.spiApi_device_p.ByReference(); + + + // get configured SPI bus + SpiBus bus = SpiBus.BUS_0; + + // get configured SPI mode + SpiMode mode = SpiMode.MODE_0; + + int baud = config.baud(); + + int channel = config.getChannel(); + + SpiChipSelect cs = config.getChipSelect(); + + + + // the default value for 'flags' is zero + int flags = 0; + + // to_do Decode all possible flags to value used + // in ioctl. + //TO_do if bits no longet applicable to we warn or error+out ? + // if 'flags' were provided in the SPI config, then accept them + if (config().flags() != null) { + flags = config().flags().intValue(); + } + + // Mode + if(config.modeUserProvided()){ + mode = config.mode(); + flags = (flags & (0xFFFFFFFF ^ SPI_MODE_MASK)); // clear Mode bits + flags |= mode.getMode(); + }else { + int mode_bits = flags & SPI_MODE_MASK; + if(mode_bits == 0b01) { + mode = SpiMode.MODE_1; + } + else if(mode_bits == 0b10) { + mode = SpiMode.MODE_2; + } + else if(mode_bits == 0b11) { + mode = SpiMode.MODE_3; + } + } + + // Bus + if (config.busUserProvided()) { // user provided, overwrite flags + bus = config.bus(); + } + else if ((config.flags() != null)) { // use bus number from flags + if((flags & SPI_BUS_MASK) == 0){ + bus = SpiBus.BUS_0; + } + else{ + bus = SpiBus.BUS_1; + } + } + // update flags value with BUS bit ('A' 0x0000=BUS0; 0x0100=BUS1) + if (bus == SpiBus.BUS_0) { + flags = (flags & (0xFFFFFFFF ^ SPI_BUS_MASK)); // clear AUX bit + } else if (bus == SpiBus.BUS_1) { + flags = (flags & (0xFFFFFFFF ^ SPI_BUS_MASK)) | SPI_BUS_MASK; // set AUX bit + } + + this.spiApi.max_freq = 64000000; + + this.spiApi.bus = bus.getBus(); + + this.spiApi.dev.driver_mode = mode.ordinal(); + + // To_do + // cs needs valid init value + this.spiApi.cs = cs.ordinal(); + + + + // channel + + // cs + + this.functionsSpi.spiApi_init(this.spiApi); + + + // set open state flag + this.isOpen = true; + } + + + /** + * {@inheritDoc} + */ + @Override + public Spi initialize(Context context) throws InitializeException { + super.initialize(context); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public void close() { + // todo ???? call fini ??? + // this.functionsSpi.spiClose(); + super.close(); + } + + // ------------------------------------------------------------------- + // DEVICE TRANSFER FUNCTIONS + // ------------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + @Override + public int transfer(byte[] write, int writeOffset, byte[] read, int readOffset, int numberOfBytes) { + return this.functionsSpi.transfer(this.spiApi, write, writeOffset, read, readOffset, numberOfBytes, this.spiApi.max_freq); + } + + // ------------------------------------------------------------------- + // DEVICE WRITE FUNCTIONS + // ------------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + @Override + public int write(byte b) { + return this.functionsSpi.spiApi_write_b(this.spiApi, b, this.spiApi.max_freq); + } + + /** + * {@inheritDoc} + */ + @Override + public int write(byte[] data, int offset, int length) { + return this.functionsSpi.spiApi_write(this.spiApi, data, offset, length, this.spiApi.max_freq); + } + + + // ------------------------------------------------------------------- + // RAW DEVICE READ FUNCTIONS + // ------------------------------------------------------------------- + + /** + * {@inheritDoc} + */ + @Override + public int read() { + byte b = 0; + this.functionsSpi.spiApi_read_b(this.spiApi, b, this.spiApi.max_freq); + return b; + } + + /** + * {@inheritDoc} + */ + @Override + public int read(byte[] buffer, int offset, int length) { + return this.functionsSpi.spiApi_read(this.spiApi, buffer, offset, length, this.spiApi.max_freq); + } + + +} + + diff --git a/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProvider.java b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProvider.java new file mode 100644 index 00000000..97c30e72 --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProvider.java @@ -0,0 +1,24 @@ +package com.pi4j.plugin.rp1spi.provider; + +import com.pi4j.io.spi.SpiProvider; + + + +import com.pi4j.library.kernel.SPIapi; +import com.pi4j.plugin.rp1spi.Rp1SpiPlugin; + + + +public interface Rp1SpiProvider extends SpiProvider { + /** Constant NAME="Rp1SpiPlugin.SPI_PROVIDER_NAME" */ + String NAME = Rp1SpiPlugin.SPI_PROVIDER_NAME; + /** Constant ID="Rp1SpiPlugin.SPI_PROVIDER_ID" */ + String ID = Rp1SpiPlugin.SPI_PROVIDER_ID; + + + + + static Rp1SpiProvider newInstance() { + return new Rp1SpiProviderImpl(); + } +} diff --git a/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProviderImpl.java b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProviderImpl.java new file mode 100644 index 00000000..9a9a8758 --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/src/main/java/com/pi4j/plugin/rp1spi/provider/Rp1SpiProviderImpl.java @@ -0,0 +1,34 @@ +package com.pi4j.plugin.rp1spi.provider; + +import com.pi4j.boardinfo.util.BoardInfoHelper; +import com.pi4j.io.spi.Spi; +import com.pi4j.io.spi.SpiConfig; +import com.pi4j.io.spi.SpiProviderBase; + +public class Rp1SpiProviderImpl extends SpiProviderBase implements Rp1SpiProvider { + + + public Rp1SpiProviderImpl() { + this.id = ID; + this.name = NAME; + } + + @Override + public int getPriority() { + return BoardInfoHelper.usesRP1() ? 150 : 25; + } + + /** + * {@inheritDoc} + */ + @Override + public Spi create(SpiConfig config) { + //synchronized (this.piGpio) { + + // create new I/O instance based on I/O config + Rp1Spi spi = new Rp1Spi( this, config); + this.context.registry().add(spi); + return spi; + //} + } +} diff --git a/plugins/pi4j-plugin-rp1spi/src/main/java/module-info.java b/plugins/pi4j-plugin-rp1spi/src/main/java/module-info.java new file mode 100644 index 00000000..71181516 --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/src/main/java/module-info.java @@ -0,0 +1,20 @@ + +import com.pi4j.plugin.rp1spi.Rp1SpiPlugin; + + +module com.pi4j.plugin.rp1spi { + requires com.pi4j; + requires com.pi4j.library.kernel; + requires org.slf4j; + requires jdk.unsupported; + + // uses com.pi4j.extension.Plugin; + + exports com.pi4j.plugin.rp1spi; + + exports com.pi4j.plugin.rp1spi.provider; + + provides com.pi4j.extension.Plugin + with com.pi4j.plugin.rp1spi.Rp1SpiPlugin; + +} diff --git a/plugins/pi4j-plugin-rp1spi/src/main/resources/META-INF.services/com.pi4j.extension.Plugin b/plugins/pi4j-plugin-rp1spi/src/main/resources/META-INF.services/com.pi4j.extension.Plugin new file mode 100644 index 00000000..65f522cb --- /dev/null +++ b/plugins/pi4j-plugin-rp1spi/src/main/resources/META-INF.services/com.pi4j.extension.Plugin @@ -0,0 +1 @@ +com.pi4j.plugin.rp1spi.Rp1SpiPlugin \ No newline at end of file diff --git a/plugins/pi4j-plugin/pom.xml b/plugins/pi4j-plugin/pom.xml index c1506784..3fb91e80 100644 --- a/plugins/pi4j-plugin/pom.xml +++ b/plugins/pi4j-plugin/pom.xml @@ -32,6 +32,7 @@ ../pi4j-plugin-raspberrypi ../pi4j-plugin-linuxfs ../pi4j-plugin-gpiod + ../pi4j-plugin-rp1spi diff --git a/pom.xml b/pom.xml index 80cfc484..893c0e50 100644 --- a/pom.xml +++ b/pom.xml @@ -395,7 +395,18 @@ junit-jupiter-engine test - +