[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250512012748.79749-5-damien.riegel@silabs.com>
Date: Sun, 11 May 2025 21:27:37 -0400
From: Damien Riégel <damien.riegel@...abs.com>
To: Andrew Lunn <andrew+netdev@...n.ch>,
"David S . Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>, Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>,
Silicon Labs Kernel Team <linux-devel@...abs.com>,
netdev@...r.kernel.org, devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [RFC net-next 04/15] net: cpc: add protocol header structure and API
CPC frames are composed of a header and optionally a payload. There are
three main frame types currently supported:
- DATA for transmitting payload or regular control frames (like ACKs,
that don't have payload associated with them)
- SYN for connection sequence
- RST for disconnection and errors
Add structure and functions to operate on this header. They will be
leveraged in a future commit where the protocol is actually implemented.
Signed-off-by: Damien Riégel <damien.riegel@...abs.com>
---
drivers/net/cpc/Makefile | 2 +-
drivers/net/cpc/header.c | 237 +++++++++++++++++++++++++++++++++++++++
drivers/net/cpc/header.h | 83 ++++++++++++++
3 files changed, 321 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/cpc/header.c
create mode 100644 drivers/net/cpc/header.h
diff --git a/drivers/net/cpc/Makefile b/drivers/net/cpc/Makefile
index 673a40db424..81c470012c1 100644
--- a/drivers/net/cpc/Makefile
+++ b/drivers/net/cpc/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-cpc-y := endpoint.o interface.o main.o
+cpc-y := endpoint.o header.o interface.o main.o
obj-$(CONFIG_CPC) += cpc.o
diff --git a/drivers/net/cpc/header.c b/drivers/net/cpc/header.c
new file mode 100644
index 00000000000..9f6d637b5ae
--- /dev/null
+++ b/drivers/net/cpc/header.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025, Silicon Laboratories, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/string.h>
+
+#include "header.h"
+
+#define CPC_CONTROL_TYPE_MASK 0xC0
+#define CPC_CONTROL_ACK_MASK BIT(2)
+
+/**
+ * cpc_header_get_type() - Get the frame type.
+ * @hdr_raw: Raw header.
+ * @type: Reference to a frame type.
+ *
+ * Return: True if the type has been successfully decoded, otherwise false.
+ * On success, the output parameter type is assigned.
+ */
+bool cpc_header_get_type(const u8 hdr_raw[CPC_HEADER_SIZE], enum cpc_frame_type *type)
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ switch (FIELD_GET(CPC_CONTROL_TYPE_MASK, hdr->ctrl)) {
+ case CPC_FRAME_TYPE_DATA:
+ *type = CPC_FRAME_TYPE_DATA;
+ break;
+ case CPC_FRAME_TYPE_SYN:
+ *type = CPC_FRAME_TYPE_SYN;
+ break;
+ case CPC_FRAME_TYPE_RST:
+ *type = CPC_FRAME_TYPE_RST;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * cpc_header_get_ep_id() - Get the endpoint id.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Endpoint id.
+ */
+u8 cpc_header_get_ep_id(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return hdr->ep_id;
+}
+
+/**
+ * cpc_header_get_recv_wnd() - Get the receive window.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Receive window.
+ */
+u8 cpc_header_get_recv_wnd(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return hdr->recv_wnd;
+}
+
+/**
+ * cpc_header_get_seq() - Get the sequence number.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Sequence number.
+ */
+u8 cpc_header_get_seq(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return hdr->seq;
+}
+
+/**
+ * cpc_header_get_ack() - Get the acknowledge number.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Acknowledge number.
+ */
+u8 cpc_header_get_ack(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return hdr->ack;
+}
+
+/**
+ * cpc_header_get_req_ack() - Get the request acknowledge frame flag.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Request acknowledge frame flag.
+ */
+bool cpc_header_get_req_ack(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return FIELD_GET(CPC_CONTROL_ACK_MASK, hdr->ctrl);
+}
+
+/**
+ * cpc_header_get_mtu() - Get the maximum transmission unit.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Maximum transmission unit.
+ *
+ * Must only be used over a SYN frame.
+ */
+u16 cpc_header_get_mtu(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return le16_to_cpu(hdr->syn.mtu);
+}
+
+/**
+ * cpc_header_get_payload_len() - Get the payload length.
+ * @hdr_raw: Raw header.
+ *
+ * Return: Payload length.
+ *
+ * Must only be used over a DATA frame.
+ */
+u16 cpc_header_get_payload_len(const u8 hdr_raw[CPC_HEADER_SIZE])
+{
+ const struct cpc_header *hdr = (struct cpc_header *)hdr_raw;
+
+ return le16_to_cpu(hdr->dat.payload_len);
+}
+
+/**
+ * cpc_header_get_ctrl() - Encode parameters into a control byte.
+ * @type: Frame type.
+ * @req_ack: Frame flag indicating a request to be acknowledged.
+ *
+ * Return: Encoded control byte.
+ */
+u8 cpc_header_get_ctrl(enum cpc_frame_type type, bool req_ack)
+{
+ return FIELD_PREP(CPC_CONTROL_TYPE_MASK, type) |
+ FIELD_PREP(CPC_CONTROL_ACK_MASK, req_ack);
+}
+
+/**
+ * cpc_header_get_frames_acked_count() - Get frames to be acknowledged.
+ * @seq: Current sequence number of the endpoint.
+ * @ack: Acknowledge number of the received frame.
+ * @ack_pending_count: Amount of frames pending on an acknowledge.
+ *
+ * Return: Frames to be acknowledged.
+ */
+u8 cpc_header_get_frames_acked_count(u8 seq, u8 ack, u8 ack_pending_count)
+{
+ u8 frames_acked_count;
+ u8 ack_range_min;
+ u8 ack_range_max;
+
+ ack_range_min = seq + 1;
+ ack_range_max = seq + ack_pending_count;
+
+ if (!cpc_header_number_in_range(ack_range_min, ack_range_max, ack))
+ return 0;
+
+ /* Find number of frames acknowledged with ACK number. */
+ if (ack > seq) {
+ frames_acked_count = ack - seq;
+ } else {
+ frames_acked_count = 256 - seq;
+ frames_acked_count += ack;
+ }
+
+ return frames_acked_count;
+}
+
+/**
+ * cpc_header_is_syn_ack_valid() - Check if the provided SYN-ACK valid or not.
+ * @seq: Current sequence number of the endpoint.
+ * @ack: Acknowledge number of the received SYN.
+ *
+ * Return: True if valid, otherwise false.
+ */
+bool cpc_header_is_syn_ack_valid(u8 seq, u8 ack)
+{
+ return !!cpc_header_get_frames_acked_count(seq, ack, 1);
+}
+
+/**
+ * cpc_header_number_in_window() - Test if a number is within a window.
+ * @start: Start of the window.
+ * @end: Window size.
+ * @n: Number to be tested.
+ *
+ * Given the start of the window and its size, test if the number is
+ * in the range [start; start + wnd).
+ *
+ * @return True if start <= n <= start + wnd - 1 (modulo 256), otherwise false.
+ */
+bool cpc_header_number_in_window(u8 start, u8 wnd, u8 n)
+{
+ u8 end;
+
+ if (wnd == 0)
+ return false;
+
+ end = start + wnd - 1;
+
+ return cpc_header_number_in_range(start, end, n);
+}
+
+/**
+ * cpc_header_number_in_range() - Test if a number is between start and end (included).
+ * @start: Lowest limit.
+ * @end: Highest limit inclusively.
+ * @n: Number to be tested.
+ *
+ * @return True if start <= n <= end (modulo 256), otherwise false.
+ */
+bool cpc_header_number_in_range(u8 start, u8 end, u8 n)
+{
+ if (end >= start) {
+ if (n < start || n > end)
+ return false;
+ } else {
+ if (n > end && n < start)
+ return false;
+ }
+
+ return true;
+}
diff --git a/drivers/net/cpc/header.h b/drivers/net/cpc/header.h
new file mode 100644
index 00000000000..c85bcaef345
--- /dev/null
+++ b/drivers/net/cpc/header.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025, Silicon Laboratories, Inc.
+ */
+
+#ifndef __CPC_HEADER_H
+#define __CPC_HEADER_H
+
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+
+#define CPC_HEADER_MAX_RX_WINDOW 255
+#define CPC_HEADER_SIZE 8
+
+/**
+ * enum cpc_frame_type - Describes all possible frame types that can
+ * be received or sent.
+ * @CPC_FRAME_TYPE_DATA: Used to send and control application DATA frames.
+ * @CPC_FRAME_TYPE_SYN: Used to initiate an endpoint connection.
+ * @CPC_FRAME_TYPE_RST: Used to reset the endpoint connection and indicate
+ * that the endpoint is unavailable.
+ */
+enum cpc_frame_type {
+ CPC_FRAME_TYPE_DATA,
+ CPC_FRAME_TYPE_SYN,
+ CPC_FRAME_TYPE_RST,
+};
+
+/**
+ * struct cpc_header - Representation of the CPC header.
+ * @ctrl: Indicates the frame type [7..6] and frame flags [5..0].
+ * Currently only the request acknowledge flag is supported.
+ * This flag indicates if the frame should be acknowledged by
+ * the remote on reception.
+ * @ep_id: Address of the endpoint the frame is destined to.
+ * @recv_wnd: Indicates to the remote how many reception buffers are
+ * available so it can determine how many frames it can send.
+ * @seq: Identifies the frame with a number.
+ * @ack: Indicate the sequence number of the next expected frame from
+ * the remote. When paired with a fast re-transmit flag, it indicates
+ * the sequence number of the frame in error that should be
+ * re-transmitted.
+ * @syn.mtu: On a SYN frame, this represents the maximum transmission unit.
+ * @dat.payload_len: On a DATA frame, this indicates the payload length.
+ */
+struct cpc_header {
+ u8 ctrl;
+ u8 ep_id;
+ u8 recv_wnd;
+ u8 seq;
+ u8 ack;
+ union {
+ u8 extension[3];
+ struct __packed {
+ __le16 mtu;
+ u8 reserved;
+ } syn;
+ struct __packed {
+ __le16 payload_len;
+ u8 reserved;
+ } dat;
+ struct __packed {
+ u8 reserved[3];
+ } rst;
+ };
+} __packed;
+
+bool cpc_header_get_type(const u8 hdr_raw[CPC_HEADER_SIZE], enum cpc_frame_type *type);
+u8 cpc_header_get_ep_id(const u8 hdr_raw[CPC_HEADER_SIZE]);
+u8 cpc_header_get_recv_wnd(const u8 hdr_raw[CPC_HEADER_SIZE]);
+u8 cpc_header_get_seq(const u8 hdr_raw[CPC_HEADER_SIZE]);
+u8 cpc_header_get_ack(const u8 hdr_raw[CPC_HEADER_SIZE]);
+bool cpc_header_get_req_ack(const u8 hdr_raw[CPC_HEADER_SIZE]);
+u16 cpc_header_get_mtu(const u8 hdr_raw[CPC_HEADER_SIZE]);
+u16 cpc_header_get_payload_len(const u8 hdr_raw[CPC_HEADER_SIZE]);
+u8 cpc_header_get_ctrl(enum cpc_frame_type type, bool req_ack);
+
+u8 cpc_header_get_frames_acked_count(u8 seq, u8 ack, u8 ack_pending_count);
+bool cpc_header_is_syn_ack_valid(u8 seq, u8 ack);
+bool cpc_header_number_in_window(u8 start, u8 wnd, u8 n);
+bool cpc_header_number_in_range(u8 start, u8 end, u8 n);
+
+#endif
--
2.49.0
Powered by blists - more mailing lists