Skip to content

Commit

Permalink
Add CANFD support to Linux CAN (#33)
Browse files Browse the repository at this point in the history
* Added CANFD support
* added a way to detect the state of a Virtual Socket CAN.
  • Loading branch information
alaa-az authored Sep 18, 2024
1 parent 94753f1 commit 3a86b07
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 61 deletions.
11 changes: 9 additions & 2 deletions packages/linux_can/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ Future<void> main() async {
// Actually open the device, so we can send/receive frames.
final socket = device.open();

// Send some example CAN frame.
socket.send(CanFrame.standard(id: 0x123, data: [0x01, 0x02, 0x03, 0x04]));
if (socket.isFlexibleDataRate) {
socket.send(CanFrame.standardFd(
id: 0x123,
data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12],
switchBitRate: true));
} else {
// Send some example CAN frame.
socket.send(CanFrame.standard(id: 0x123, data: [0x01, 0x02, 0x03, 0x04]));
}

// Read a CAN frame. This is blocking, i.e. it will wait for a frame to arrive.
//
Expand Down
22 changes: 16 additions & 6 deletions packages/linux_can/lib/src/can_device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,12 @@ class CanDevice {
return _queryAttribute(CanInterfaceAttribute.operState).operState!;
}

/// True if the network interface is up, i.e. [operationalState] is [NetInterfaceOperState.up].
bool get isUp => operationalState == NetInterfaceOperState.up;

/// True if the network interface is up and running.
bool get isUp => switch (operationalState) {
NetInterfaceOperState.up => true,
NetInterfaceOperState.unknown => interfaceFlags.containsAll({NetInterfaceFlag.up, NetInterfaceFlag.running}),
_ => false,
};
/// Some general statistics for this network interface.
///
/// Not yet implemented.
Expand Down Expand Up @@ -226,6 +229,7 @@ class CanDevice {

/// Creates a new CanSocket for sending/receiving frames on this CAN device.
CanSocket open() {
bool isFlexibleDataRate = _platformInterface.isFlexibleDataRateCapable(networkInterface.name);
final fd = _platformInterface.createCanSocket();
try {
_platformInterface.bind(fd, networkInterface.index);
Expand All @@ -242,6 +246,7 @@ class CanDevice {
platformInterface: _platformInterface,
fd: fd,
networkInterface: networkInterface,
isFlexibleDataRate: isFlexibleDataRate,
);
} on Object {
_platformInterface.close(fd);
Expand All @@ -259,17 +264,19 @@ class CanSocket implements Sink<CanFrame> {
required PlatformInterface platformInterface,
required int fd,
required this.networkInterface,
required this.isFlexibleDataRate,
}) : _fd = fd,
_platformInterface = platformInterface;

final PlatformInterface _platformInterface;
final int _fd;
final NetworkInterface networkInterface;
final bool isFlexibleDataRate;
var _open = true;

var _listening = false;
FdHandler? _fdListener;
ffi.Pointer<can_frame>? _fdHandlerBuffer;
ffi.Pointer<canfd_frame>? _fdHandlerBuffer;

void _checkOpen() {
if (!_open) {
Expand Down Expand Up @@ -311,6 +318,9 @@ class CanSocket implements Sink<CanFrame> {
/// happen when sending lots of frames in a short time period. If [block] is false, this will throw a [LinuxError]
/// with errno [EWOULDBLOCK] (value 22) in this case.
Future<void> send(CanFrame frame, {bool block = true}) async {
if (!isFlexibleDataRate && frame is CanFdFrame) {
throw ArgumentError.value(frame, 'frame', 'CAN controller does not support CAN FD.');
}
_checkOpen();

// TODO: Do the blocking in the kernel or in a worker isolate
Expand Down Expand Up @@ -366,7 +376,7 @@ class CanSocket implements Sink<CanFrame> {

final frames = <CanFrameOrError>[];
while (true) {
final frame = PlatformInterface.readStatic(libc, fd, buffer, ffi.sizeOf<can_frame>());
final frame = PlatformInterface.readStatic(libc, fd, buffer, ffi.sizeOf<canfd_frame>());
if (frame != null) {
frames.add(frame);
} else {
Expand All @@ -390,7 +400,7 @@ class CanSocket implements Sink<CanFrame> {
assert(_fdListener == null);
assert(_fdHandlerBuffer == null);

_fdHandlerBuffer = ffi.calloc<can_frame>();
_fdHandlerBuffer = ffi.calloc<canfd_frame>();

_fdListener = await _platformInterface.eventListener.add(
fd: _fd,
Expand Down
113 changes: 108 additions & 5 deletions packages/linux_can/lib/src/data_classes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,22 @@ sealed class CanFrame {
return CanExtendedRemoteFrame(id: id);
}

/// CAN FD Data frame,
/// Standard Frame Format with Flexible Data-rate (CAN FD)
factory CanFrame.standardFd(
{required int id, required List<int> data, bool switchBitRate = false, bool errorStateIndicator = false}) {
return CanFdBaseFrame(
id: id, data: data, flags: CANFD_FDF | (switchBitRate ? CANFD_BRS : 0) | (errorStateIndicator ? CANFD_ESI : 0));
}

/// CAN FD Data frame,
/// Extended Frame Format with Flexible Data-rate (CAN FD)
factory CanFrame.extendedFd(
{required int id, required List<int> data, bool switchBitRate = false, bool errorStateIndicator = false}) {
return CanFdFrameExtended(
id: id, data: data, flags: CANFD_FDF | (switchBitRate ? CANFD_BRS : 0) | (errorStateIndicator ? CANFD_ESI : 0));
}

@override
int get hashCode;

Expand All @@ -458,13 +474,19 @@ sealed class CanExtendedFrame extends CanFrame {
}

sealed class CanDataFrame extends CanFrame {
/// 0 to 8 byte CAN frame payload.
/// 0 to 8 byte CAN frame payload, or 0 to 64 bytes CAN FD frame payload.
List<int> get data;
}

sealed class CanLegacyFrame extends CanFrame {}

sealed class CanFdFrame extends CanFrame {
int get flags;
}

sealed class CanRemoteFrame extends CanFrame {}

class CanStandardDataFrame extends CanFrame implements CanBaseFrame, CanDataFrame {
class CanStandardDataFrame extends CanFrame implements CanLegacyFrame, CanBaseFrame, CanDataFrame {
const CanStandardDataFrame({required this.id, required this.data}) : assert(0 <= data.length && data.length <= 8);

@override
Expand All @@ -491,7 +513,7 @@ class CanStandardDataFrame extends CanFrame implements CanBaseFrame, CanDataFram
String toString() => 'CanStandardDataFrame(id: $id, data: $data)';
}

class CanExtendedDataFrame extends CanFrame implements CanExtendedFrame, CanDataFrame {
class CanExtendedDataFrame extends CanFrame implements CanLegacyFrame, CanExtendedFrame, CanDataFrame {
const CanExtendedDataFrame({required this.id, required this.data}) : assert(id & ~CAN_EFF_MASK == 0);

@override
Expand All @@ -518,7 +540,7 @@ class CanExtendedDataFrame extends CanFrame implements CanExtendedFrame, CanData
String toString() => 'CanExtendedDataFrame(id: $id, data: $data)';
}

class CanStandardRemoteFrame extends CanFrame implements CanBaseFrame, CanRemoteFrame {
class CanStandardRemoteFrame extends CanFrame implements CanLegacyFrame, CanBaseFrame, CanRemoteFrame {
const CanStandardRemoteFrame({required this.id}) : assert(id & ~CAN_SFF_MASK == 0);

@override
Expand All @@ -542,7 +564,7 @@ class CanStandardRemoteFrame extends CanFrame implements CanBaseFrame, CanRemote
String toString() => 'CanStandardRemoteFrame(id: $id)';
}

class CanExtendedRemoteFrame extends CanFrame implements CanExtendedFrame, CanRemoteFrame {
class CanExtendedRemoteFrame extends CanFrame implements CanLegacyFrame, CanExtendedFrame, CanRemoteFrame {
const CanExtendedRemoteFrame({required this.id}) : assert(id & ~CAN_EFF_MASK == 0);

@override
Expand All @@ -566,6 +588,87 @@ class CanExtendedRemoteFrame extends CanFrame implements CanExtendedFrame, CanRe
String toString() => 'CanExtendedRemoteFrame(id: $id)';
}

class CanFdBaseFrame extends CanFrame implements CanBaseFrame, CanDataFrame, CanFdFrame {
const CanFdBaseFrame({required this.id, required this.data, required this.flags})
: assert((id & ~CAN_SFF_MASK == 0) && 0 <= data.length &&
data.length <= 64 &&
(data.length <= 8 ||
data.length == 12 ||
data.length == 16 ||
data.length == 20 ||
data.length == 24 ||
data.length == 32 ||
data.length == 48 ||
data.length == 64));

@override
final int id;

@override
final List<int> data;

@override
final int flags;

@override
final CanFrameFormat format = CanFrameFormat.base;

@override
final CanFrameType type = CanFrameType.data;

@override
int get hashCode => Object.hash(id, flags, data);

@override
bool operator ==(Object other) {
return other is CanFdBaseFrame && id == other.id && flags == other.flags && listsEqual(data, other.data);
}

@override
String toString() => 'CanFdBaseFrame(id: $id, flags: $flags, data: $data)';
}

class CanFdFrameExtended extends CanFrame implements CanExtendedFrame, CanDataFrame, CanFdFrame {
const CanFdFrameExtended({required this.id, required this.data, required this.flags})
: assert(id & ~CAN_EFF_MASK == 0 &&
0 <= data.length &&
data.length <= 64 &&
(data.length <= 8 ||
data.length == 12 ||
data.length == 16 ||
data.length == 20 ||
data.length == 24 ||
data.length == 32 ||
data.length == 48 ||
data.length == 64));

@override
final int id;

@override
final List<int> data;

@override
final int flags;

@override
final CanFrameFormat format = CanFrameFormat.extended;

@override
final CanFrameType type = CanFrameType.data;

@override
int get hashCode => Object.hash(id, flags, data);

@override
bool operator ==(Object other) {
return other is CanFdFrameExtended && id == other.id && flags == other.flags && listsEqual(data, other.data);
}

@override
String toString() => 'CanFdFrameExtended(id: $id, flags: $flags, data: $data)';
}

/// The RFC2863 state of the network interface.
///
/// See: https://docs.kernel.org/networking/operstates.html
Expand Down
Loading

0 comments on commit 3a86b07

Please sign in to comment.