1 /*
2  * Copyright (c) 2021-2022 Henrik Brix Andersen <henrik@brixandersen.dk>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/devicetree.h>
9 #include <zephyr/drivers/can.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/kernel.h>
12 
13 /* Devicetree */
14 #define CANBUS_NODE DT_CHOSEN(zephyr_canbus)
15 #define BUTTON_NODE DT_ALIAS(sw0)
16 #define BUTTON_NAME DT_PROP_OR(BUTTON_NODE, label, "sw0")
17 
18 #if DT_NODE_EXISTS(BUTTON_NODE)
19 struct button_callback_context {
20 	struct gpio_callback callback;
21 	struct k_sem sem;
22 };
23 
button_callback(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)24 static void button_callback(const struct device *port, struct gpio_callback *cb,
25 			    gpio_port_pins_t pins)
26 {
27 	struct button_callback_context *ctx =
28 		CONTAINER_OF(cb, struct button_callback_context, callback);
29 
30 	k_sem_give(&ctx->sem);
31 }
32 #endif /* DT_NODE_EXISTS(BUTTON_NODE) */
33 
can_tx_callback(const struct device * dev,int error,void * user_data)34 static void can_tx_callback(const struct device *dev, int error, void *user_data)
35 {
36 	struct k_sem *tx_queue_sem = user_data;
37 
38 	k_sem_give(tx_queue_sem);
39 }
40 
main(void)41 int main(void)
42 {
43 #if DT_NODE_EXISTS(BUTTON_NODE)
44 	const struct gpio_dt_spec btn = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);
45 	struct button_callback_context btn_cb_ctx;
46 #endif /* DT_NODE_EXISTS(BUTTON_NODE) */
47 	const struct device *dev = DEVICE_DT_GET(CANBUS_NODE);
48 	struct k_sem tx_queue_sem;
49 	struct can_frame frame = {0};
50 	int err;
51 
52 	k_sem_init(&tx_queue_sem, CONFIG_SAMPLE_CAN_BABBLING_TX_QUEUE_SIZE,
53 		   CONFIG_SAMPLE_CAN_BABBLING_TX_QUEUE_SIZE);
54 
55 	if (!device_is_ready(dev)) {
56 		printk("CAN device not ready");
57 		return 0;
58 	}
59 
60 	if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_FD_MODE)) {
61 		err = can_set_mode(dev, CAN_MODE_FD);
62 		if (err != 0) {
63 			printk("Error setting CAN FD mode (err %d)", err);
64 			return 0;
65 		}
66 	}
67 
68 	err = can_start(dev);
69 	if (err != 0) {
70 		printk("Error starting CAN controller (err %d)", err);
71 		return 0;
72 	}
73 
74 #if DT_NODE_EXISTS(BUTTON_NODE)
75 	k_sem_init(&btn_cb_ctx.sem, 0, 1);
76 
77 	if (!gpio_is_ready_dt(&btn)) {
78 		printk("button device not ready\n");
79 		return 0;
80 	}
81 
82 	err = gpio_pin_configure_dt(&btn, GPIO_INPUT);
83 	if (err != 0) {
84 		printk("failed to configure button GPIO (err %d)\n", err);
85 		return 0;
86 	}
87 
88 	err = gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE);
89 	if (err != 0) {
90 		printk("failed to configure button interrupt (err %d)\n", err);
91 		return 0;
92 	}
93 
94 	gpio_init_callback(&btn_cb_ctx.callback, button_callback, BIT(btn.pin));
95 	gpio_add_callback(btn.port, &btn_cb_ctx.callback);
96 #endif /* DT_NODE_EXISTS(BUTTON_NODE) */
97 
98 	if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_EXT_ID)) {
99 		frame.flags |= CAN_FRAME_IDE;
100 	}
101 
102 	if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_RTR)) {
103 		frame.flags |= CAN_FRAME_RTR;
104 	}
105 
106 	if (IS_ENABLED(CONFIG_SAMPLE_CAN_BABBLING_FD_MODE)) {
107 		frame.flags |= CAN_FRAME_FDF;
108 	}
109 
110 	frame.id = CONFIG_SAMPLE_CAN_BABBLING_CAN_ID;
111 
112 	printk("babbling on %s with %s (%d-bit) CAN ID 0x%0*x, RTR %d, CAN FD %d\n",
113 	       dev->name,
114 	       (frame.flags & CAN_FRAME_IDE) != 0 ? "extended" : "standard",
115 	       (frame.flags & CAN_FRAME_IDE) != 0 ? 29 : 11,
116 	       (frame.flags & CAN_FRAME_IDE) != 0 ? 8 : 3, frame.id,
117 	       (frame.flags & CAN_FRAME_RTR) != 0 ? 1 : 0,
118 	       (frame.flags & CAN_FRAME_FDF) != 0 ? 1 : 0);
119 
120 #if DT_NODE_EXISTS(BUTTON_NODE)
121 	printk("abort by pressing %s button\n", BUTTON_NAME);
122 #endif /* DT_NODE_EXISTS(BUTTON_NODE) */
123 
124 	while (true) {
125 		if (k_sem_take(&tx_queue_sem, K_MSEC(100)) == 0) {
126 			err = can_send(dev, &frame, K_NO_WAIT, can_tx_callback, &tx_queue_sem);
127 			if (err != 0) {
128 				printk("failed to enqueue CAN frame (err %d)\n", err);
129 			}
130 		}
131 
132 #if DT_NODE_EXISTS(BUTTON_NODE)
133 		if (k_sem_take(&btn_cb_ctx.sem, K_NO_WAIT) == 0) {
134 			printk("button press detected, babbling stopped\n");
135 			return 0;
136 		}
137 #endif /* DT_NODE_EXISTS(BUTTON_NODE) */
138 	}
139 }
140