drivers: can: provide default callback to can_send() if NULL

Provide a default, internal callback to CAN controller drivers
implementation of can_send() if no callback was provided by the caller.

This allows for simplifying the CAN driver implementations of can_send() as
these no longer need special handling for callback/no callback operation.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
diff --git a/drivers/can/can_common.c b/drivers/can/can_common.c
index f34b22a..4eafb0f 100644
--- a/drivers/can/can_common.c
+++ b/drivers/can/can_common.c
@@ -17,6 +17,44 @@
 /* CAN sync segment is always one time quantum */
 #define CAN_SYNC_SEG 1
 
+struct can_tx_default_cb_ctx {
+	struct k_sem done;
+	int status;
+};
+
+static void can_tx_default_cb(const struct device *dev, int error, void *user_data)
+{
+	struct can_tx_default_cb_ctx *ctx = user_data;
+
+	ctx->status = error;
+	k_sem_give(&ctx->done);
+}
+
+int z_impl_can_send(const struct device *dev, const struct can_frame *frame,
+		    k_timeout_t timeout, can_tx_callback_t callback,
+		    void *user_data)
+{
+	const struct can_driver_api *api = (const struct can_driver_api *)dev->api;
+
+	if (callback == NULL) {
+		struct can_tx_default_cb_ctx ctx;
+		int err;
+
+		k_sem_init(&ctx.done, 0, 1);
+
+		err = api->send(dev, frame, timeout, can_tx_default_cb, &ctx);
+		if (err != 0) {
+			return err;
+		}
+
+		k_sem_take(&ctx.done, K_FOREVER);
+
+		return ctx.status;
+	}
+
+	return api->send(dev, frame, timeout, callback, user_data);
+}
+
 static void can_msgq_put(const struct device *dev, struct can_frame *frame, void *user_data)
 {
 	struct k_msgq *msgq = (struct k_msgq *)user_data;
diff --git a/include/zephyr/drivers/can.h b/include/zephyr/drivers/can.h
index 3f64bb6..419b720 100644
--- a/include/zephyr/drivers/can.h
+++ b/include/zephyr/drivers/can.h
@@ -348,6 +348,9 @@
 /**
  * @brief Callback API upon sending a CAN frame
  * See @a can_send() for argument description
+ *
+ * @note From a driver perspective `callback` will never be `NULL` as a default callback will be
+ * provided if none is provided by the caller. This allows for simplifying the driver handling.
  */
 typedef int (*can_send_t)(const struct device *dev,
 			  const struct can_frame *frame,
@@ -1095,15 +1098,6 @@
 		       k_timeout_t timeout, can_tx_callback_t callback,
 		       void *user_data);
 
-static inline int z_impl_can_send(const struct device *dev, const struct can_frame *frame,
-				  k_timeout_t timeout, can_tx_callback_t callback,
-				  void *user_data)
-{
-	const struct can_driver_api *api = (const struct can_driver_api *)dev->api;
-
-	return api->send(dev, frame, timeout, callback, user_data);
-}
-
 /** @} */
 
 /**