diff --git a/core-test/src/test/java/tel/schich/javacan/test/J1939CanSocketTest.java b/core-test/src/test/java/tel/schich/javacan/test/J1939CanSocketTest.java new file mode 100644 index 00000000..dc5f8fc6 --- /dev/null +++ b/core-test/src/test/java/tel/schich/javacan/test/J1939CanSocketTest.java @@ -0,0 +1,99 @@ +/* + * The MIT License + * Copyright © 2018 Phillip Schichtel + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package tel.schich.javacan.test; + +import org.junit.jupiter.api.Test; +import tel.schich.javacan.CanChannels; +import tel.schich.javacan.CanFilter; +import tel.schich.javacan.CanFrame; +import tel.schich.javacan.JavaCAN; +import tel.schich.javacan.J1939CanChannel; +import tel.schich.javacan.platform.linux.LinuxNativeOperationException; + +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Arrays; + +import static java.time.Duration.ofMillis; +import static java.time.Duration.ofSeconds; +import static org.junit.jupiter.api.Assertions.*; +import static tel.schich.javacan.CanFrame.*; +import static tel.schich.javacan.CanSocketOptions.*; +import static tel.schich.javacan.test.CanTestHelper.CAN_INTERFACE; + +class J1939CanSocketTest { + + @Test + void testLoopback() throws Exception { + + try (final J1939CanChannel a = CanChannels.newJ1939Channel()) { + a.bind(CAN_INTERFACE, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, (short) 0x20); + a.connect(CAN_INTERFACE, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, (short) 0x30); + + try (final J1939CanChannel b = CanChannels.newJ1939Channel()) { + b.bind(CAN_INTERFACE, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, (short) 0x30); + b.connect(CAN_INTERFACE, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, (short) 0x20); + b.configureBlocking(false); + + final CanFrame input = CanFrame.create(0x7EB, FD_NO_FLAGS, new byte[] { 0x20, 0x33 }); + a.write(input); + final CanFrame output = b.read(); + assertEquals(input, output); + + // a.setOption(LOOPBACK, false); + } + } + } + + // @Test + void testOwnMessage() throws Exception { + + try (final J1939CanChannel socket = CanChannels.newJ1939Channel()) { + socket.bind(CAN_INTERFACE, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, (short) 0x20); + socket.connect(CAN_INTERFACE, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, (short) 0x30); + + socket.configureBlocking(false); + socket.setOption(RECV_OWN_MSGS, true); + + final CanFrame input = CanFrame.create(0x7EC, FD_NO_FLAGS, new byte[] { 0x20, 0x33 }); + socket.write(input); + final CanFrame output = socket.read(); + assertEquals(input, output); + + socket.setOption(RECV_OWN_MSGS, false); + socket.write(input); + assertThrows(LinuxNativeOperationException.class, socket::read); + } + } + + @Test + void testBufferReuseWithNonZeroBase() { + byte[] data = new byte[MAX_FD_DATA_LENGTH]; + CanFrame frame = CanFrame.createExtended(0x7FFFFF, FD_NO_FLAGS, data); + ByteBuffer buffer = frame.getBuffer(); + + ByteBuffer largeForReuse = JavaCAN.allocateOrdered(2 * J1939CanChannel.FD_MTU); + largeForReuse.position(J1939CanChannel.FD_MTU); + largeForReuse.put(buffer); + largeForReuse.position(J1939CanChannel.FD_MTU); + + CanFrame.create(largeForReuse); + } +} diff --git a/core/src/main/c/common.c b/core/src/main/c/common.c index b6bb6324..ea77abb2 100644 --- a/core/src/main/c/common.c +++ b/core/src/main/c/common.c @@ -42,6 +42,10 @@ inline int create_can_isotp_socket() { return socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP); } +inline int create_can_J1939_socket() { + return socket(PF_CAN, SOCK_DGRAM, CAN_J1939); +} + int bind_can_socket(int sock, uint32_t interface, uint32_t rx, uint32_t tx) { struct sockaddr_can addr = {0}; addr.can_family = AF_CAN; @@ -62,6 +66,28 @@ int connect_can_socket(int sock, uint32_t interface, uint32_t rx, uint32_t tx) { return connect(sock, (const struct sockaddr *) &addr, sizeof(addr)); } +int bind_can_socketJ1939(int sock, uint32_t interface, uint64_t name, uint32_t pgn, uint8_t saddr) { + struct sockaddr_can addr = {0}; + addr.can_family = AF_CAN; + addr.can_ifindex = (int) interface; + addr.can_addr.j1939.name = name; + addr.can_addr.j1939.pgn = pgn; + addr.can_addr.j1939.addr = saddr; + + return bind(sock, (const struct sockaddr *) &addr, sizeof(addr)); +} + +int connect_can_socketJ1939(int sock, uint32_t interface, uint64_t name, uint32_t pgn, uint8_t saddr) { + struct sockaddr_can addr = {0}; + addr.can_family = AF_CAN; + addr.can_ifindex = (int) interface; + addr.can_addr.j1939.name = name; + addr.can_addr.j1939.pgn = pgn; + addr.can_addr.j1939.addr = saddr; + + return connect(sock, (const struct sockaddr *) &addr, sizeof(addr)); +} + int set_timeout(int sock, int type, uint64_t seconds, uint64_t nanos) { socklen_t timeout_len = sizeof(struct timeval); struct timeval timeout; diff --git a/core/src/main/c/common.h b/core/src/main/c/common.h index fb47070f..55134d69 100644 --- a/core/src/main/c/common.h +++ b/core/src/main/c/common.h @@ -33,8 +33,11 @@ int create_can_raw_socket(); int create_can_bcm_socket(); int create_can_isotp_socket(); +int create_can_J1939_socket(); int bind_can_socket(int, uint32_t, uint32_t, uint32_t); int connect_can_socket(int, uint32_t, uint32_t, uint32_t); +int bind_can_socketJ1939(int sock, uint32_t interface, uint64_t name, uint32_t pgn, uint8_t saddr); +int connect_can_socketJ1939(int sock, uint32_t interface, uint64_t name, uint32_t pgn, uint8_t saddr); int set_timeout(int, int, uint64_t, uint64_t); int get_timeout(int, int, uint64_t*); int set_blocking_mode(int, bool); diff --git a/core/src/main/c/javacan_socketcan.c b/core/src/main/c/javacan_socketcan.c index 39aac29e..0d968de3 100644 --- a/core/src/main/c/javacan_socketcan.c +++ b/core/src/main/c/javacan_socketcan.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,14 @@ JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_createIsotpSocket(JNIEn return fd; } +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_createJ1939Socket(JNIEnv *env, jclass class) { + jint fd = create_can_J1939_socket(); + if (fd == -1) { + throw_native_exception(env, "Unable to create J1939 socket"); + } + return fd; +} + JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_bindSocket(JNIEnv *env, jclass class, jint sock, jlong iface, jint rx, jint tx) { jint result = bind_can_socket(sock, (unsigned int) (iface & 0xFFFFFFFF), (uint32_t) rx, (uint32_t) tx); if (result) { @@ -73,6 +82,22 @@ JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_connectSocket(JNIEnv *e return result; } +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_bindSocketJ1939(JNIEnv *env, jclass class, jint sock, jlong iface, jlong name, jint pgn, jshort addr) { + jint result = bind_can_socketJ1939(sock, (unsigned int) (iface & 0xFFFFFFFF), (uint64_t) name, (uint32_t) pgn, (uint16_t) addr); + if (result) { + throw_native_exception(env, "Unable to bind"); + } + return result; +} + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_connectSocketJ1939(JNIEnv *env, jclass class, jint sock, jlong iface, jlong name, jint pgn, jshort addr) { + jint result = connect_can_socketJ1939(sock, (unsigned int) (iface & 0xFFFFFFFF), (uint64_t) name, (uint32_t) pgn, (uint16_t) addr); + if (result) { + throw_native_exception(env, "Unable to connect"); + } + return result; +} + JNIEXPORT void JNICALL Java_tel_schich_javacan_SocketCAN_close(JNIEnv *env, jclass class, jint sock) { if (close(sock)) { throw_native_exception(env, "Unable to close epoll fd"); @@ -426,3 +451,61 @@ JNIEXPORT jobject JNICALL Java_tel_schich_javacan_SocketCAN_getIsotpLlOpts(JNIEn (jbyte)opts.tx_flags ); } + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_setJ1939Promisc(JNIEnv *env, jclass class, jint sock, jint promisc) { + jint result = setsockopt(sock, SOL_CAN_J1939, SO_J1939_PROMISC, &promisc, sizeof(promisc)); + if (result == -1) { + throw_native_exception(env, "Unable to set Promiscuous flag"); + } + return result; +} + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_getJ1939Promisc(JNIEnv *env, jclass class, jint sock) { + int promisc = 0; + socklen_t size = sizeof(promisc); + int result = getsockopt(sock, SOL_CAN_J1939, SO_J1939_PROMISC, &promisc, &size); + if (result) { + throw_native_exception(env, "Unable to get the Promiscuous flag"); + return result; + } + return promisc; +} + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_setJ1939ErrQueue(JNIEnv *env, jclass class, jint sock, jint errqueue) { + jint result = setsockopt(sock, SOL_CAN_J1939, SO_J1939_ERRQUEUE, &errqueue, sizeof(errqueue)); + if (result == -1) { + throw_native_exception(env, "Unable to set Err Queue flag"); + } + return result; +} + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_getJ1939ErrQueue(JNIEnv *env, jclass class, jint sock) { + int errqueue = 0; + socklen_t size = sizeof(errqueue); + int result = getsockopt(sock, SOL_CAN_J1939, SO_J1939_ERRQUEUE, &errqueue, &size); + if (result) { + throw_native_exception(env, "Unable to get the Err Queue flag"); + return result; + } + return errqueue; +} + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_setJ1939SendPrio(JNIEnv *env, jclass class, jint sock, jint sendprio) { + jint result = setsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &sendprio, sizeof(sendprio)); + if (result == -1) { + throw_native_exception(env, "Unable to set Send Priority level"); + } + return result; +} + +JNIEXPORT jint JNICALL Java_tel_schich_javacan_SocketCAN_getJ1939SendPrio(JNIEnv *env, jclass class, jint sock) { + int sendprio = 0; + socklen_t size = sizeof(sendprio); + int result = getsockopt(sock, SOL_CAN_J1939, SO_J1939_SEND_PRIO, &sendprio, &size); + if (result) { + throw_native_exception(env, "Unable to get the Send Priority level"); + return result; + } + return sendprio; +} + diff --git a/core/src/main/java/tel/schich/javacan/CanChannels.java b/core/src/main/java/tel/schich/javacan/CanChannels.java index 2e3ca48f..97766745 100644 --- a/core/src/main/java/tel/schich/javacan/CanChannels.java +++ b/core/src/main/java/tel/schich/javacan/CanChannels.java @@ -1,17 +1,14 @@ /* * The MIT License * Copyright © 2018 Phillip Schichtel - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,160 +28,198 @@ */ public class CanChannels { - private CanChannels() { - } - - /** - * Creates a new {@link tel.schich.javacan.RawCanChannel} without binding it to a device. - * - * @see socket man page - * @return The new channel - * @throws IOException if the native socket could not be created - */ - public static RawCanChannel newRawChannel() throws IOException { + private CanChannels() { + } + + /** + * Creates a new {@link tel.schich.javacan.RawCanChannel} without binding it to a device. + * + * @see socket man page + * @return The new channel + * @throws IOException if the native socket could not be created + */ + public static RawCanChannel newRawChannel() throws IOException { int fd = SocketCAN.createRawSocket(); - return new RawCanChannelImpl(fd); - } - - /** - * Creates a new {@link tel.schich.javacan.RawCanChannel} already bound to the given - * {@link NetworkDevice}. - * - * @see socket man page - * @param device the device to bind to - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static RawCanChannel newRawChannel(NetworkDevice device) throws IOException { - RawCanChannel ch = newRawChannel(); - ch.bind(device); - return ch; - } - - /** - * Creates a new {@link tel.schich.javacan.RawCanChannel} already bound to the given device. - * - * @see socket man page - * @param device the device to bind to - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static RawCanChannel newRawChannel(String device) throws IOException { - return newRawChannel(NetworkDevice.lookup(device)); - } - - /** - * Creates a new {@link BcmCanChannel} without binding it to a device. - * - * @see socket man page - * @return The new channel - * @throws IOException if the native socket could not be created - */ - public static BcmCanChannel newBcmChannel() throws IOException { + return new RawCanChannelImpl(fd); + } + + /** + * Creates a new {@link tel.schich.javacan.RawCanChannel} already bound to the given + * {@link NetworkDevice}. + * + * @see socket man page + * @param device the device to bind to + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static RawCanChannel newRawChannel(NetworkDevice device) throws IOException { + RawCanChannel ch = newRawChannel(); + ch.bind(device); + return ch; + } + + /** + * Creates a new {@link tel.schich.javacan.RawCanChannel} already bound to the given device. + * + * @see socket man page + * @param device the device to bind to + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static RawCanChannel newRawChannel(String device) throws IOException { + return newRawChannel(NetworkDevice.lookup(device)); + } + + /** + * Creates a new {@link tel.schich.javacan.J1939CanChannel} without binding it to a device. + * + * @see socket man page + * @return The new channel + * @throws IOException if the native socket could not be created + */ + public static J1939CanChannel newJ1939Channel() throws IOException { + int fd = SocketCAN.createJ1939Socket(); + return new J1939CanChannelImpl(fd); + } + + /** + * Creates a new {@link tel.schich.javacan.J1939CanChannel} already bound to the given + * {@link NetworkDevice}. + * + * @see socket man page + * @param device the device to bind to + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static J1939CanChannel newJ1939Channel(NetworkDevice device) throws IOException { + J1939CanChannel ch = newJ1939Channel(); + ch.bind(device, J1939CanChannel.J1939_NO_NAME, J1939CanChannel.J1939_NO_PGN, J1939CanChannel.J1939_NO_ADDR); + return ch; + } + + /** + * Creates a new {@link tel.schich.javacan.J1939CanChannel} already bound to the given device. + * + * @see socket man page + * @param device the device to bind to + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static J1939CanChannel newJ1939Channel(String device) throws IOException { + return newJ1939Channel(NetworkDevice.lookup(device)); + } + + /** + * Creates a new {@link BcmCanChannel} without binding it to a device. + * + * @see socket man page + * @return The new channel + * @throws IOException if the native socket could not be created + */ + public static BcmCanChannel newBcmChannel() throws IOException { int fd = SocketCAN.createBcmSocket(); - return new BcmCanChannel(fd); - } - - /** - * Creates a new {@link BcmCanChannel} already bound to the given {@link NetworkDevice}. - * - * @see socket man page - * @param device the device to bind to - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static BcmCanChannel newBcmChannel(NetworkDevice device) throws IOException { - BcmCanChannel ch = newBcmChannel(); - ch.connect(device); - return ch; - } - - /** - * Creates a new {@link BcmCanChannel} already bound to the given device. - * - * @see socket man page - * @param device the device to bind to - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static BcmCanChannel newBcmChannel(String device) throws IOException { - return newBcmChannel(NetworkDevice.lookup(device)); - } - - /** - * Creates a new {@link tel.schich.javacan.IsotpCanChannel} without binding it to a device and addresses. - * - * @see socket man page - * @return The new channel - * @throws IOException if the native socket could not be created - */ - public static IsotpCanChannel newIsotpChannel() throws IOException { + return new BcmCanChannel(fd); + } + + /** + * Creates a new {@link BcmCanChannel} already bound to the given {@link NetworkDevice}. + * + * @see socket man page + * @param device the device to bind to + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static BcmCanChannel newBcmChannel(NetworkDevice device) throws IOException { + BcmCanChannel ch = newBcmChannel(); + ch.connect(device); + return ch; + } + + /** + * Creates a new {@link BcmCanChannel} already bound to the given device. + * + * @see socket man page + * @param device the device to bind to + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static BcmCanChannel newBcmChannel(String device) throws IOException { + return newBcmChannel(NetworkDevice.lookup(device)); + } + + /** + * Creates a new {@link tel.schich.javacan.IsotpCanChannel} without binding it to a device and addresses. + * + * @see socket man page + * @return The new channel + * @throws IOException if the native socket could not be created + */ + public static IsotpCanChannel newIsotpChannel() throws IOException { int fd = SocketCAN.createIsotpSocket(); - return new IsotpCanChannelImpl(fd); - } - - /** - * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given - * {@link NetworkDevice} and {@link tel.schich.javacan.IsotpSocketAddress}es. - * - * @see socket man page - * @param device the device to bind to - * @param rx the receiving address - * @param tx the destination address - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static IsotpCanChannel newIsotpChannel(NetworkDevice device, IsotpSocketAddress rx, IsotpSocketAddress tx) - throws IOException - { - IsotpCanChannel ch = newIsotpChannel(); - ch.bind(device, rx, tx); - return ch; - } - - /** - * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given device and - * {@link tel.schich.javacan.IsotpSocketAddress}es. - * - * @see socket man page - * @param device the device to bind to - * @param rx the receiving address - * @param tx the destination address - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static IsotpCanChannel newIsotpChannel(String device, IsotpSocketAddress rx, IsotpSocketAddress tx) - throws IOException - { - return newIsotpChannel(NetworkDevice.lookup(device), rx, tx); - } - - /** - * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given - * {@link NetworkDevice} and addresses. - * - * @see socket man page - * @param device the device to bind to - * @param rx the receiving address - * @param tx the destination address - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static IsotpCanChannel newIsotpChannel(NetworkDevice device, int rx, int tx) throws IOException { - return newIsotpChannel(device, IsotpSocketAddress.isotpAddress(rx), IsotpSocketAddress.isotpAddress(tx)); - } - - /** - * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given device and addresses. - * - * @see socket man page - * @param device the device to bind to - * @param rx the receiving address - * @param tx the destination address - * @return The new channel - * @throws IOException if the native socket could not be created or not be bound - */ - public static IsotpCanChannel newIsotpChannel(String device, int rx, int tx) throws IOException { - return newIsotpChannel(NetworkDevice.lookup(device), rx, tx); - } + + return new IsotpCanChannelImpl(fd); + } + + /** + * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given + * {@link NetworkDevice} and {@link tel.schich.javacan.IsotpSocketAddress}es. + * + * @see socket man page + * @param device the device to bind to + * @param rx the receiving address + * @param tx the destination address + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static IsotpCanChannel newIsotpChannel(NetworkDevice device, IsotpSocketAddress rx, IsotpSocketAddress tx) + throws IOException { + IsotpCanChannel ch = newIsotpChannel(); + ch.bind(device, rx, tx); + return ch; + } + + /** + * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given device and + * {@link tel.schich.javacan.IsotpSocketAddress}es. + * + * @see socket man page + * @param device the device to bind to + * @param rx the receiving address + * @param tx the destination address + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static IsotpCanChannel newIsotpChannel(String device, IsotpSocketAddress rx, IsotpSocketAddress tx) + throws IOException { + return newIsotpChannel(NetworkDevice.lookup(device), rx, tx); + } + + /** + * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given + * {@link NetworkDevice} and addresses. + * + * @see socket man page + * @param device the device to bind to + * @param rx the receiving address + * @param tx the destination address + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static IsotpCanChannel newIsotpChannel(NetworkDevice device, int rx, int tx) throws IOException { + return newIsotpChannel(device, IsotpSocketAddress.isotpAddress(rx), IsotpSocketAddress.isotpAddress(tx)); + } + + /** + * Creates a new {@link tel.schich.javacan.IsotpCanChannel} already bound to the given device and addresses. + * + * @see socket man page + * @param device the device to bind to + * @param rx the receiving address + * @param tx the destination address + * @return The new channel + * @throws IOException if the native socket could not be created or not be bound + */ + public static IsotpCanChannel newIsotpChannel(String device, int rx, int tx) throws IOException { + return newIsotpChannel(NetworkDevice.lookup(device), rx, tx); + } } diff --git a/core/src/main/java/tel/schich/javacan/CanSocketOptions.java b/core/src/main/java/tel/schich/javacan/CanSocketOptions.java index 5bcdae7a..74a53a69 100644 --- a/core/src/main/java/tel/schich/javacan/CanSocketOptions.java +++ b/core/src/main/java/tel/schich/javacan/CanSocketOptions.java @@ -1,17 +1,14 @@ /* * The MIT License * Copyright © 2018 Phillip Schichtel - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -37,189 +34,263 @@ * This class provides the standard socket options supported by CAN sockets. */ public class CanSocketOptions { - public static final int MAX_FILTERS = 512; - - private CanSocketOptions() {} - - /** - * Option to configure whether to join filters. - */ - public static final SocketOption JOIN_FILTERS = new CanSocketOption<>("JOIN_FILTERS", Boolean.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Boolean val, boolean validate) throws IOException { - SocketCAN.setJoinFilters(sock, val); - } - - @Override - public Boolean get(int sock) throws IOException { - final int result = SocketCAN.getJoinFilters(sock); - return result != 0; - } - }); - - /** - * Option to configure whether loop back frames. - */ - public static final SocketOption LOOPBACK = new CanSocketOption<>("LOOPBACK", Boolean.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Boolean val, boolean validate) throws IOException { - SocketCAN.setLoopback(sock, val); - } - - @Override - public Boolean get(int sock) throws IOException { - final int result = SocketCAN.getLoopback(sock); - return result != 0; - } - }); - - /** - * Option to configure whether to receive outgoing frames back. - */ - public static final SocketOption RECV_OWN_MSGS = new CanSocketOption<>("RECV_OWN_MSGS", Boolean.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Boolean val, boolean validate) throws IOException { - SocketCAN.setReceiveOwnMessages(sock, val); - } - - @Override - public Boolean get(int sock) throws IOException { - final int result = SocketCAN.getReceiveOwnMessages(sock); - return result != 0; - } - }); - - /** - * Option to configure whether to support FD frames. - */ - public static final SocketOption FD_FRAMES = new CanSocketOption<>("FD_FRAMES", Boolean.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Boolean val, boolean validate) throws IOException { - SocketCAN.setAllowFDFrames(sock, val); - } - - @Override - public Boolean get(int sock) throws IOException { - final int result = SocketCAN.getAllowFDFrames(sock); - return result != 0; - } - }); - - /** - * Option to configure the error filter. - * - * @see setsockopt man page - * @see getsockopt man page - */ - public static final SocketOption ERR_FILTER = new CanSocketOption<>("ERR_FILTER", Integer.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Integer val, boolean validate) throws IOException { - SocketCAN.setErrorFilter(sock, val); - } - - @Override - public Integer get(int sock) throws IOException { - return SocketCAN.getErrorFilter(sock); - } - }); - - /** - * Option to configure the CAN filters. - * - * @see setsockopt man page - * @see getsockopt man page - */ - public static final SocketOption FILTER = new CanSocketOption<>("FILTER", CanFilter[].class, new LinuxSocketOptionHandler() { - - @Override - public void set(int sock, CanFilter[] val, boolean validate) throws IOException { - if (validate) { - if (val.length > MAX_FILTERS) { - throw new IllegalArgumentException("A maximum of 512 filters are supported!"); - } - } - ByteBuffer filterData = JavaCAN.allocateOrdered(val.length * CanFilter.BYTES); - for (CanFilter f : val) { - filterData.putInt(f.getId()); - filterData.putInt(f.getMask()); - } - - SocketCAN.setFilters(sock, filterData); - } - - @Override - public CanFilter[] get(int sock) throws IOException { - ByteBuffer filterData = SocketCAN.getFilters(sock); - - filterData.order(ByteOrder.nativeOrder()).rewind(); - int count = filterData.remaining() / CanFilter.BYTES; - CanFilter[] filters = new CanFilter[count]; - for (int i = 0; i < count; i++) { - int id = filterData.getInt(); - int mask = filterData.getInt(); - filters[i] = new CanFilter(id, mask); - } - - return filters; - } - }); - - /** - * Option to configure the send timeout. - * - * @see setsockopt man page - * @see getsockopt man page - */ - public static final SocketOption SO_SNDTIMEO = new CanSocketOption<>("SO_SNDTIMEO", Duration.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Duration val, boolean validate) throws IOException { - SocketCAN.setWriteTimeout(sock, val.getSeconds(), val.getNano()); - } - - @Override - public Duration get(int sock) throws IOException { - final long timeout = SocketCAN.getWriteTimeout(sock); - return Duration.of(timeout, MICROS); - } - }); - - /** - * Option to configure the receive timeout. - * - * @see setsockopt man page - * @see getsockopt man page - */ - public static final SocketOption SO_RCVTIMEO = new CanSocketOption<>("SO_RCVTIMEO", Duration.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Duration val, boolean validate) throws IOException { - SocketCAN.setReadTimeout(sock, val.getSeconds(), val.getNano()); - } - - @Override - public Duration get(int sock) throws IOException { - final long timeout = SocketCAN.getReadTimeout(sock); - return Duration.of(timeout, MICROS); - } - }); - - /** - * Option to configure the size of the receive buffer. - * - * @see setsockopt man page - * @see getsockopt man page - */ - public static final SocketOption SO_RCVBUF = new CanSocketOption<>("SO_RCVBUF", Integer.class, new LinuxSocketOptionHandler() { - @Override - public void set(int sock, Integer val, boolean validate) throws IOException { - if (val <= 0) { - throw new IllegalArgumentException("Buffer size must be positive!"); - } - SocketCAN.setReceiveBufferSize(sock, val); - } - - @Override - public Integer get(int sock) throws IOException { - return SocketCAN.getReceiveBufferSize(sock); - } - }); + public static final int MAX_FILTERS = 512; + + private CanSocketOptions() { + } + + /** + * Option to configure whether to join filters. + */ + public static final SocketOption JOIN_FILTERS = new CanSocketOption<>("JOIN_FILTERS", Boolean.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Boolean val, boolean validate) throws IOException { + SocketCAN.setJoinFilters(sock, val); + } + + @Override + public Boolean get(int sock) throws IOException { + final int result = SocketCAN.getJoinFilters(sock); + return result != 0; + } + }); + + /** + * Option to configure whether loop back frames. + */ + public static final SocketOption LOOPBACK = new CanSocketOption<>("LOOPBACK", Boolean.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Boolean val, boolean validate) throws IOException { + SocketCAN.setLoopback(sock, val); + } + + @Override + public Boolean get(int sock) throws IOException { + final int result = SocketCAN.getLoopback(sock); + return result != 0; + } + }); + + /** + * Option to configure whether to receive outgoing frames back. + */ + public static final SocketOption RECV_OWN_MSGS = new CanSocketOption<>("RECV_OWN_MSGS", Boolean.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Boolean val, boolean validate) throws IOException { + SocketCAN.setReceiveOwnMessages(sock, val); + } + + @Override + public Boolean get(int sock) throws IOException { + final int result = SocketCAN.getReceiveOwnMessages(sock); + return result != 0; + } + }); + + /** + * Option to configure whether to support FD frames. + */ + public static final SocketOption FD_FRAMES = new CanSocketOption<>("FD_FRAMES", Boolean.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Boolean val, boolean validate) throws IOException { + SocketCAN.setAllowFDFrames(sock, val); + } + + @Override + public Boolean get(int sock) throws IOException { + final int result = SocketCAN.getAllowFDFrames(sock); + return result != 0; + } + }); + + /** + * Option to configure the error filter. + * + * @see setsockopt man page + * @see getsockopt man page + */ + public static final SocketOption ERR_FILTER = new CanSocketOption<>("ERR_FILTER", Integer.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Integer val, boolean validate) throws IOException { + SocketCAN.setErrorFilter(sock, val); + } + + @Override + public Integer get(int sock) throws IOException { + return SocketCAN.getErrorFilter(sock); + } + }); + + /** + * Option to configure the CAN filters. + * + * @see setsockopt man page + * @see getsockopt man page + */ + public static final SocketOption FILTER = new CanSocketOption<>("FILTER", CanFilter[].class, new LinuxSocketOptionHandler() { + + @Override + public void set(int sock, CanFilter[] val, boolean validate) throws IOException { + + if (validate) { + + if (val.length > MAX_FILTERS) { + throw new IllegalArgumentException("A maximum of 512 filters are supported!"); + } + } + ByteBuffer filterData = JavaCAN.allocateOrdered(val.length * CanFilter.BYTES); + + for (CanFilter f : val) { + filterData.putInt(f.getId()); + filterData.putInt(f.getMask()); + } + + SocketCAN.setFilters(sock, filterData); + } + + @Override + public CanFilter[] get(int sock) throws IOException { + ByteBuffer filterData = SocketCAN.getFilters(sock); + + filterData.order(ByteOrder.nativeOrder()).rewind(); + int count = filterData.remaining() / CanFilter.BYTES; + CanFilter[] filters = new CanFilter[count]; + + for (int i = 0; i < count; i++) { + int id = filterData.getInt(); + int mask = filterData.getInt(); + filters[i] = new CanFilter(id, mask); + } + + return filters; + } + }); + + /** + * Option to configure the send timeout. + * + * @see setsockopt man page + * @see getsockopt man page + */ + public static final SocketOption SO_SNDTIMEO = new CanSocketOption<>("SO_SNDTIMEO", Duration.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Duration val, boolean validate) throws IOException { + SocketCAN.setWriteTimeout(sock, val.getSeconds(), val.getNano()); + } + + @Override + public Duration get(int sock) throws IOException { + final long timeout = SocketCAN.getWriteTimeout(sock); + return Duration.of(timeout, MICROS); + } + }); + + /** + * Option to configure the receive timeout. + * + * @see setsockopt man page + * @see getsockopt man page + */ + public static final SocketOption SO_RCVTIMEO = new CanSocketOption<>("SO_RCVTIMEO", Duration.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Duration val, boolean validate) throws IOException { + SocketCAN.setReadTimeout(sock, val.getSeconds(), val.getNano()); + } + + @Override + public Duration get(int sock) throws IOException { + final long timeout = SocketCAN.getReadTimeout(sock); + return Duration.of(timeout, MICROS); + } + }); + + /** + * Option to configure the size of the receive buffer. + * + * @see setsockopt man page + * @see getsockopt man page + */ + public static final SocketOption SO_RCVBUF = new CanSocketOption<>("SO_RCVBUF", Integer.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Integer val, boolean validate) throws IOException { + + if (val <= 0) { + throw new IllegalArgumentException("Buffer size must be positive!"); + } + SocketCAN.setReceiveBufferSize(sock, val); + } + + @Override + public Integer get(int sock) throws IOException { + return SocketCAN.getReceiveBufferSize(sock); + } + }); + + /** + * J1939 Option + * When set, j1939 will receive all packets, not just those with a destination + * on the local system. + * default off. (0 is off, 1 is on) + */ + public static final SocketOption SO_J1939_PROMISC = new CanSocketOption<>("SO_J1939_PROMISC", Integer.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Integer val, boolean validate) throws IOException { + + if (val <= 0) { + throw new IllegalArgumentException("Buffer size must be positive!"); + } + SocketCAN.setJ1939Promisc(sock, val); + } + + @Override + public Integer get(int sock) throws IOException { + return SocketCAN.getJ1939Promisc(sock); + } + }); + + /** + * J1939 Option + */ + public static final SocketOption SO_J1939_ERRQUEUE = new CanSocketOption<>("SO_J1939_ERRQUEUE", Integer.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Integer val, boolean validate) throws IOException { + + if (val <= 0) { + throw new IllegalArgumentException("Buffer size must be positive!"); + } + SocketCAN.setJ1939ErrQueue(sock, val); + } + + @Override + public Integer get(int sock) throws IOException { + return SocketCAN.getJ1939ErrQueue(sock); + } + }); + + /** + * J1939 Option + * To set the priority field for outgoing packets, the SO_J1939_SEND_PRIO can + * be changed. This int field specifies the priority that will be used. + * j1939 defines a priority between 0 and 7 inclusive, * with 7 the lowest priority. + * Per default, the priority is set to 6 (conforming J1939). + * This priority socket option operates on the same value that is modified + * with the SOL_SOCKET, SO_PRIORITY socket option, with a difference that SOL_SOCKET/SO_PRIORITY is defined with + * 0 the lowest priority. SOL_CAN_J1939/SO_J1939_SEND_PRIO inverts this value + * for you. + */ + public static final SocketOption SO_J1939_SEND_PRIO = new CanSocketOption<>("SO_J1939_SEND_PRIO", Integer.class, new LinuxSocketOptionHandler() { + @Override + public void set(int sock, Integer val, boolean validate) throws IOException { + + if (val <= 0) { + throw new IllegalArgumentException("Buffer size must be positive!"); + } + SocketCAN.setJ1939SendPrio(sock, val); + } + + @Override + public Integer get(int sock) throws IOException { + return SocketCAN.getJ1939SendPrio(sock); + } + }); } diff --git a/core/src/main/java/tel/schich/javacan/J1939CanChannel.java b/core/src/main/java/tel/schich/javacan/J1939CanChannel.java new file mode 100644 index 00000000..f5944dc0 --- /dev/null +++ b/core/src/main/java/tel/schich/javacan/J1939CanChannel.java @@ -0,0 +1,153 @@ +/* + * The MIT License + * Copyright © 2018 Phillip Schichtel + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package tel.schich.javacan; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static tel.schich.javacan.CanFrame.HEADER_LENGTH; +import static tel.schich.javacan.CanFrame.MAX_DATA_LENGTH; +import static tel.schich.javacan.CanFrame.MAX_FD_DATA_LENGTH; + +public abstract class J1939CanChannel extends AbstractCanChannel { + public J1939CanChannel( int sock) { + super(sock); + } + + public static final long J1939_NO_NAME = 0L; + public static final int J1939_NO_PGN = 0x40000; + public static final short J1939_NO_ADDR = 0xFF; + + /** + * The MTU of a standard non-FD CAN socket. This is also the exact size of a valid non-FD CAN frame. + */ + public static final int MTU = HEADER_LENGTH + MAX_DATA_LENGTH; + + /** + * The MTU of a standard CAN FD socket. This is also the exact size of a valid CAN FD frame. + */ + public static final int FD_MTU = HEADER_LENGTH + MAX_FD_DATA_LENGTH; + + /** + * Binds this channel to a device. The channel will be non-functional until bound. + *

+ * The bind(2) system call assigns the local address, i.e. the source address when sending packages. If a PGN during + * bind(2) is set, it's used as a RX filter. I.e. only packets with a matching PGN are received. If an ADDR or NAME + * is set it is used as a receive filter, too. It will match the destination NAME or ADDR of the incoming packet. + * The NAME filter will work only if appropriate Address Claiming for this name was done on the CAN bus and + * registered/cached by the kernel. + *

+ * Both write(2) and send(2) will send a packet with local address from bind(2) and the remote address from + * connect(2). + * + * @see bind man page + * @see J1939 Documentation + * @param device the device to bind to. + * @param name + * @param pgn Parameter Group Number + * @param addr + * @return fluent interface + * @throws IOException if the bind operation failed. + */ + public abstract J1939CanChannel bind(NetworkDevice device, long name, int pgn, short addr) throws IOException; + + /** + * connect(2) assigns the remote address, i.e. the destination address. The PGN from connect(2) is used as the + * default PGN when sending packets. If ADDR or NAME is set it will be used as the default destination ADDR or NAME. + * Further a set ADDR or NAME during connect(2) is used as a receive filter. It will match the source NAME or ADDR + * of the incoming packet. + * + * @see bind man page + * @see J1939 Documentation + * @param device the device to bind to. + * @param name + * @param pgn Parameter Group Number + * @param addr + * @return fluent interface + * @throws IOException if the bind operation failed. + */ + public abstract J1939CanChannel connect(NetworkDevice device, long name, int pgn, short addr) throws IOException; + + /** + * Reads a CAM frame from the channel by internally allocating a new direct {@link ByteBuffer}. + * + * @see read man page + * @return the CAN frame + * @throws IOException if the IO operations failed or invalid data was read. + */ + public abstract CanFrame read() throws IOException; + + /** + * Reads a CAM frame from the channel using the supplied buffer. + * + * @see read man page + * @param buffer the buffer to read into.The buffer's {@link ByteOrder} will be set to native and it will be + * flipped after the read has been completed. + * @return the CAN frame + * @throws IOException if the IO operations failed, the supplied buffer was insufficient or invalid data was read. + */ + public abstract CanFrame read(ByteBuffer buffer) throws IOException; + + /** + * Reads raw bytes from the channel. + * This method does not apply any checks on the data that has been read or on the supplied buffer. This method + * is primarily intended for downstream libraries that implement their own parsing on the data from the socket. + * + * @see read man page + * @param buffer the buffer to read into.The buffer's {@link ByteOrder} will be set to native and it will be + * flipped after the read has been completed. + * @return the number of bytes + * @throws IOException if the IO operations failed. + */ + public abstract long readUnsafe(ByteBuffer buffer) throws IOException; + + /** + * Writes the given CAN frame. + * + * @see write man page + * @param frame the frame to be written. + * @return fluent interface. + * @throws IOException if the IO operations failed. + */ + public abstract J1939CanChannel write(CanFrame frame) throws IOException; + + /** + * Writes the given buffer in its entirety to the socket. + * This method does not apply any checks on the given buffer. This method is primarily intended for downstream + * libraries + * that create these buffers using other facilities. + * + * @see write man page + * @param buffer the buffer to be written. + * @return the bytes written. + * @throws IOException if the IO operations failed. + */ + public abstract long writeUnsafe(ByteBuffer buffer) throws IOException; + + /** + * Allocates a buffer that is large enough to hold any supported CAN frame. + * + * @return a new buffer ready to be used. + */ + public static ByteBuffer allocateSufficientMemory() { + return JavaCAN.allocateOrdered(FD_MTU + 1); + } +} diff --git a/core/src/main/java/tel/schich/javacan/J1939CanChannelImpl.java b/core/src/main/java/tel/schich/javacan/J1939CanChannelImpl.java new file mode 100644 index 00000000..640aae7d --- /dev/null +++ b/core/src/main/java/tel/schich/javacan/J1939CanChannelImpl.java @@ -0,0 +1,123 @@ +/* + * The MIT License + * Copyright © 2018 Phillip Schichtel + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package tel.schich.javacan; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.NotYetBoundException; + +import tel.schich.javacan.platform.linux.LinuxNativeOperationException; +import tel.schich.javacan.platform.linux.LinuxNetworkDevice; + +/** + * Naming has been adopted from the JDK here (Interface + InterfaceImpl) + */ +final class J1939CanChannelImpl extends J1939CanChannel { + + private volatile NetworkDevice device; + + J1939CanChannelImpl( int sock) { + super(sock); + } + + @Override + public J1939CanChannel bind(NetworkDevice device, long name, int pgn, short addr) throws IOException { + + if (!(device instanceof LinuxNetworkDevice)) { + throw new IllegalArgumentException("Unsupported network device given!"); + } + + try { + SocketCAN.bindSocketJ1939(getSocket(), ((LinuxNetworkDevice) device).getIndex(), name, pgn, addr); + + } catch (LinuxNativeOperationException e) { + throw checkForClosedChannel(e); + } + this.device = device; + return this; + } + + @Override + public J1939CanChannel connect(NetworkDevice device, long name, int pgn, short addr) throws IOException { + + if (!(device instanceof LinuxNetworkDevice)) { + throw new IllegalArgumentException("Unsupported network device given!"); + } + + try { + SocketCAN.connectSocketJ1939(getSocket(), ((LinuxNetworkDevice) device).getIndex(), name, pgn, addr); + + } catch (LinuxNativeOperationException e) { + throw checkForClosedChannel(e); + } + this.device = device; + return this; + } + + @Override + public NetworkDevice getDevice() { + + if (!isBound()) { + throw new NotYetBoundException(); + } + return this.device; + } + + @Override + public boolean isBound() { + return this.device != null; + } + + @Override + public CanFrame read() throws IOException { + int length = MTU; + ByteBuffer frameBuf = JavaCAN.allocateOrdered(length); + return read(frameBuf); + } + + @Override + public CanFrame read(ByteBuffer buffer) throws IOException { + readUnsafe(buffer); + return CanFrame.create(buffer); + } + + @Override + public long readUnsafe(ByteBuffer buffer) throws IOException { + long bytesRead = readSocket(buffer); + buffer.flip(); + return bytesRead; + } + + @Override + public J1939CanChannel write(CanFrame frame) throws IOException { + long written = writeUnsafe(frame.getBuffer()); + + if (written != frame.getSize()) { + throw new IOException("Frame written incompletely!"); + } + + return this; + } + + @Override + public long writeUnsafe(ByteBuffer buffer) throws IOException { + return writeSocket(buffer); + } +} diff --git a/core/src/main/java/tel/schich/javacan/SocketCAN.java b/core/src/main/java/tel/schich/javacan/SocketCAN.java index 8d20658b..6c8234a1 100644 --- a/core/src/main/java/tel/schich/javacan/SocketCAN.java +++ b/core/src/main/java/tel/schich/javacan/SocketCAN.java @@ -1,17 +1,14 @@ /* * The MIT License * Copyright © 2018 Phillip Schichtel - * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,83 +25,143 @@ class SocketCAN { - static { - JavaCAN.initialize(); - } + static { + JavaCAN.initialize(); + } - public static native int createRawSocket() throws LinuxNativeOperationException; + public static native int createRawSocket() throws LinuxNativeOperationException; public static native int createBcmSocket() throws LinuxNativeOperationException; - public static native int createIsotpSocket() throws LinuxNativeOperationException; + public static native int createIsotpSocket() throws LinuxNativeOperationException; - public static native int bindSocket(int sock, long interfaceId, int rx, int tx) throws LinuxNativeOperationException; + public static native int createJ1939Socket() throws LinuxNativeOperationException; + + public static native int bindSocket(int sock, long interfaceId, int rx, int tx) throws LinuxNativeOperationException; public static native int connectSocket(int sock, long interfaceId, int rx, int tx) throws LinuxNativeOperationException; - public static native void close(int sock) throws LinuxNativeOperationException; + public static native int bindSocketJ1939(int sock, long interfaceId, long name, int pgn, short addr) throws LinuxNativeOperationException; + + public static native int connectSocketJ1939(int sock, long interfaceId, long name, int pgn, short addr) throws LinuxNativeOperationException; + + public static native void close(int sock) throws LinuxNativeOperationException; + + public static native int setBlockingMode(int sock, boolean block) throws LinuxNativeOperationException; + + public static native int getBlockingMode(int sock) throws LinuxNativeOperationException; + + public static native int setReadTimeout(int sock, long seconds, long nanos) throws LinuxNativeOperationException; + + public static native long getReadTimeout(int sock) throws LinuxNativeOperationException; + + public static native int setWriteTimeout(int sock, long seconds, long nanos) throws LinuxNativeOperationException; + + public static native long getWriteTimeout(int sock) throws LinuxNativeOperationException; + + public static native int setReceiveBufferSize(int sock, int size) throws LinuxNativeOperationException; - public static native int setBlockingMode(int sock, boolean block) throws LinuxNativeOperationException; + public static native int getReceiveBufferSize(int sock) throws LinuxNativeOperationException; - public static native int getBlockingMode(int sock) throws LinuxNativeOperationException; + public static native long write(int sock, ByteBuffer buf, int offset, int len) throws LinuxNativeOperationException; - public static native int setReadTimeout(int sock, long seconds, long nanos) throws LinuxNativeOperationException; + public static native long read(int sock, ByteBuffer buf, int offset, int len) throws LinuxNativeOperationException; - public static native long getReadTimeout(int sock) throws LinuxNativeOperationException; + public static native int setFilters(int sock, ByteBuffer data) throws LinuxNativeOperationException; - public static native int setWriteTimeout(int sock, long seconds, long nanos) throws LinuxNativeOperationException; + public static native ByteBuffer getFilters(int sock) throws LinuxNativeOperationException; - public static native long getWriteTimeout(int sock) throws LinuxNativeOperationException; + public static native int setLoopback(int sock, boolean enable) throws LinuxNativeOperationException; - public static native int setReceiveBufferSize(int sock, int size) throws LinuxNativeOperationException; + public static native int getLoopback(int sock) throws LinuxNativeOperationException; - public static native int getReceiveBufferSize(int sock) throws LinuxNativeOperationException; + public static native int setReceiveOwnMessages(int sock, boolean enable) throws LinuxNativeOperationException; - public static native long write(int sock, ByteBuffer buf, int offset, int len) throws LinuxNativeOperationException; + public static native int getReceiveOwnMessages(int sock) throws LinuxNativeOperationException; - public static native long read(int sock, ByteBuffer buf, int offset, int len) throws LinuxNativeOperationException; + public static native int setJoinFilters(int sock, boolean enable) throws LinuxNativeOperationException; - public static native int setFilters(int sock, ByteBuffer data) throws LinuxNativeOperationException; + public static native int getJoinFilters(int sock) throws LinuxNativeOperationException; - public static native ByteBuffer getFilters(int sock) throws LinuxNativeOperationException; + public static native int setAllowFDFrames(int sock, boolean enable) throws LinuxNativeOperationException; - public static native int setLoopback(int sock, boolean enable) throws LinuxNativeOperationException; + public static native int getAllowFDFrames(int sock) throws LinuxNativeOperationException; - public static native int getLoopback(int sock) throws LinuxNativeOperationException; + public static native int setErrorFilter(int sock, int mask) throws LinuxNativeOperationException; - public static native int setReceiveOwnMessages(int sock, boolean enable) throws LinuxNativeOperationException; + public static native int getErrorFilter(int sock) throws LinuxNativeOperationException; - public static native int getReceiveOwnMessages(int sock) throws LinuxNativeOperationException; + public static native int setIsotpOpts(int sock, int flags, int frameTxTime, byte extAddress, byte txpadContent, byte rxpadContent, byte rxExtAddress) throws LinuxNativeOperationException; - public static native int setJoinFilters(int sock, boolean enable) throws LinuxNativeOperationException; + public static native IsotpOptions getIsotpOpts(int sock) throws LinuxNativeOperationException; - public static native int getJoinFilters(int sock) throws LinuxNativeOperationException; + public static native int setIsotpRecvFc(int sock, byte bs, byte stmin, byte wftmax) throws LinuxNativeOperationException; - public static native int setAllowFDFrames(int sock, boolean enable) throws LinuxNativeOperationException; + public static native IsotpFlowControlOptions getIsotpRecvFc(int sock) throws LinuxNativeOperationException; - public static native int getAllowFDFrames(int sock) throws LinuxNativeOperationException; + public static native int setIsotpTxStmin(int sock, int txStmin) throws LinuxNativeOperationException; - public static native int setErrorFilter(int sock, int mask) throws LinuxNativeOperationException; + public static native int getIsotpTxStmin(int sock) throws LinuxNativeOperationException; - public static native int getErrorFilter(int sock) throws LinuxNativeOperationException; + public static native int setIsotpRxStmin(int sock, int rxStmin) throws LinuxNativeOperationException; - public static native int setIsotpOpts(int sock, int flags, int frameTxTime, byte extAddress, byte txpadContent, byte rxpadContent, byte rxExtAddress) throws LinuxNativeOperationException; + public static native int getIsotpRxStmin(int sock) throws LinuxNativeOperationException; - public static native IsotpOptions getIsotpOpts(int sock) throws LinuxNativeOperationException; + public static native int setIsotpLlOpts(int sock, byte mtu, byte txDl, byte txFlags) throws LinuxNativeOperationException; - public static native int setIsotpRecvFc(int sock, byte bs, byte stmin, byte wftmax) throws LinuxNativeOperationException; + public static native IsotpLinkLayerOptions getIsotpLlOpts(int sock) throws LinuxNativeOperationException; - public static native IsotpFlowControlOptions getIsotpRecvFc(int sock) throws LinuxNativeOperationException; + /** + * When set, j1939 will receive all packets, not just those with a destination + * on the local system. + * default off. + * + * @param sock + * @param promisc 0 to disable, 1 to enable + * @return + * @throws LinuxNativeOperationException + */ + public static native int setJ1939Promisc(int sock, int promisc) throws LinuxNativeOperationException; - public static native int setIsotpTxStmin(int sock, int txStmin) throws LinuxNativeOperationException; + /** + * When set, j1939 will receive all packets, not just those with a destination + * on the local system. + * + * @param sock + * @return 0 if disabled, 1 if enabled + * @throws LinuxNativeOperationException + */ + public static native int getJ1939Promisc(int sock) throws LinuxNativeOperationException; - public static native int getIsotpTxStmin(int sock) throws LinuxNativeOperationException; + /** + * To set the priority field for outgoing packets, the SO_J1939_SEND_PRIO can + * be changed. This int field specifies the priority that will be used. + * j1939 defines a priority between 0 and 7 inclusive, * with 7 the lowest priority. + * Per default, the priority is set to 6 (conforming J1939). + * This priority socket option operates on the same value that is modified + * with the SOL_SOCKET, SO_PRIORITY socket option, with a difference that SOL_SOCKET/SO_PRIORITY is defined with + * 0 the lowest priority. SOL_CAN_J1939/SO_J1939_SEND_PRIO inverts this value + * for you. + * + * @param sock + * @param sendprio Priority, 0 to 7 + * @return + * @throws LinuxNativeOperationException + */ + public static native int setJ1939SendPrio(int sock, int sendprio) throws LinuxNativeOperationException; - public static native int setIsotpRxStmin(int sock, int rxStmin) throws LinuxNativeOperationException; + /** + * Get the priority field for outgoing packets. This int field specifies the priority that will be used. + * j1939 defines a priority between 0 and 7 inclusive, with 7 the lowest priority. + * + * @param sock + * @return 0 to 7 + * @throws LinuxNativeOperationException + */ + public static native int getJ1939SendPrio(int sock) throws LinuxNativeOperationException; - public static native int getIsotpRxStmin(int sock) throws LinuxNativeOperationException; + public static native int setJ1939ErrQueue(int sock, int recvown) throws LinuxNativeOperationException; - public static native int setIsotpLlOpts(int sock, byte mtu, byte txDl, byte txFlags) throws LinuxNativeOperationException; + public static native int getJ1939ErrQueue(int sock) throws LinuxNativeOperationException; - public static native IsotpLinkLayerOptions getIsotpLlOpts(int sock) throws LinuxNativeOperationException; }