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

Add CANFD support to Linux CAN #33

Merged
merged 10 commits into from
Sep 18, 2024
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