Skip to content

Commit

Permalink
Merge pull request #57 from JohnLussmyer/feature/J1939
Browse files Browse the repository at this point in the history
Initial J1939 changes
  • Loading branch information
pschichtel authored Feb 12, 2024
2 parents 7d137f8 + 075f08a commit f788145
Show file tree
Hide file tree
Showing 9 changed files with 1,036 additions and 386 deletions.
Original file line number Diff line number Diff line change
@@ -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);
}
}
26 changes: 26 additions & 0 deletions core/src/main/c/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/c/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
83 changes: 83 additions & 0 deletions core/src/main/c/javacan_socketcan.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/can.h>
#include <linux/can/isotp.h>
#include <linux/can/raw.h>
#include <linux/can/j1939.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
Expand Down Expand Up @@ -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) {
Expand All @@ -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");
Expand Down Expand Up @@ -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;
}

Loading

0 comments on commit f788145

Please sign in to comment.