Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial J1939 changes #57

Merged
merged 3 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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