diff --git a/app/Kconfig b/app/Kconfig index 487753d..75953b5 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -69,6 +69,7 @@ config CANNECTIVITY_LED_GPIO bool "LED support" default y depends on $(dt_compat_any_has_prop,cannectivity-channel,state-gpios) || \ + $(dt_compat_any_has_prop,cannectivity-channel,activity-gpios) || \ ($(dt_alias_enabled,led0) && !$(dt_compat_enabled,cannectivity-channel)) select GPIO select SMF diff --git a/app/boards/usb2canfdv1_stm32g0b1xx.overlay b/app/boards/usb2canfdv1_stm32g0b1xx.overlay new file mode 100644 index 0000000..802027f --- /dev/null +++ b/app/boards/usb2canfdv1_stm32g0b1xx.overlay @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/ { + cannectivity: cannectivity { + compatible = "cannectivity"; + timestamp-counter = <&counter2>; + + channel0 { + compatible = "cannectivity-channel"; + can-controller = <&fdcan1>; + state-gpios = <&gpioa 2 GPIO_ACTIVE_LOW>; + activity-gpios = <&gpioa 0 GPIO_ACTIVE_LOW>, + <&gpioa 1 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&zephyr_udc0 { + gs_usb0: gs_usb0 { + compatible = "gs_usb"; + }; +}; + +&timers2 { + status = "okay"; + st,prescaler = <59>; + + counter2: counter2 { + compatible = "st,stm32-counter"; + status = "okay"; + }; +}; diff --git a/app/dts/bindings/cannectivity.yaml b/app/dts/bindings/cannectivity.yaml index 72c6d56..29d3d38 100644 --- a/app/dts/bindings/cannectivity.yaml +++ b/app/dts/bindings/cannectivity.yaml @@ -70,3 +70,6 @@ child-binding: description: | GPIO to use to control the CAN channel activity LED. This GPIO is driven active when the LED is to be turned on and inactive when the LED is to be turned off. + + If two GPIOs are specified, the GPIO at index 0 will be used for indicating RX activity, and + the GPIO at index 1 will be used for indicating TX activity. diff --git a/app/sample.yaml b/app/sample.yaml index 30a28da..1828c78 100644 --- a/app/sample.yaml +++ b/app/sample.yaml @@ -24,6 +24,7 @@ tests: - frdm_k64f - lpcxpresso55s16 - nucleo_h723zg + - usb2canfdv1 app.cannectivity.sof: depends_on: - usb_device @@ -51,6 +52,7 @@ tests: integration_platforms: - frdm_k64f - lpcxpresso55s16 + - usb2canfdv1 app.cannectivity.usbd_next.sof: depends_on: - usbd diff --git a/app/src/led.c b/app/src/led.c index 5592990..3b3dd43 100644 --- a/app/src/led.c +++ b/app/src/led.c @@ -25,7 +25,6 @@ enum led_state { LED_STATE_NORMAL, LED_STATE_NORMAL_STARTED, LED_STATE_NORMAL_STOPPED, - LED_STATE_NORMAL_ACTIVITY, LED_STATE_IDENTIFY, }; @@ -36,7 +35,15 @@ enum led_event { LED_EVENT_CHANNEL_IDENTIFY_OFF, LED_EVENT_CHANNEL_STARTED, LED_EVENT_CHANNEL_STOPPED, - LED_EVENT_CHANNEL_ACTIVITY, + LED_EVENT_CHANNEL_ACTIVITY_RX, + LED_EVENT_CHANNEL_ACTIVITY_TX, +}; + +/* Activity types */ +enum led_activity { + LED_ACTIVITY_RX = 0U, + LED_ACTIVITY_TX, + LED_ACTIVITY_COUNT, }; /* LED finite-state machine event data type */ @@ -49,18 +56,20 @@ struct led_ctx { struct k_msgq eventq; led_event_t event; uint16_t ch; - k_timepoint_t activity; - int ticks; bool started; struct gpio_dt_spec state_led; - struct gpio_dt_spec activity_led; + int identify_ticks; + k_timepoint_t activity[LED_ACTIVITY_COUNT]; + int ticks[LED_ACTIVITY_COUNT]; + struct gpio_dt_spec activity_led[LED_ACTIVITY_COUNT]; }; /* Devicetree accessor macros */ #define CHANNEL_LED_GPIO_DT_SPEC_GET(node_id) \ { \ .state_led = GPIO_DT_SPEC_GET_OR(node_id, state_gpios, {0}), \ - .activity_led = GPIO_DT_SPEC_GET_OR(node_id, activity_gpios, {0}), \ + .activity_led = {DT_FOREACH_PROP_ELEM_SEP(node_id, activity_gpios, \ + GPIO_DT_SPEC_GET_BY_IDX, (,))}, \ } #define CHANNEL_LED0_GPIO_DT_SPEC_GET() \ @@ -77,6 +86,14 @@ struct led_ctx led_channel_ctx[] = { #endif /* !CANNECTIVITY_DT_HAS_CHANNEL */ }; +/* Helper macros */ +#define LED_CTX_HAS_STATE_LED(_led_ctx) \ + (_led_ctx->state_led.port != NULL) +#define LED_CTX_HAS_ACTIVITY_LED(_led_ctx) \ + (_led_ctx->activity_led[LED_ACTIVITY_RX].port != NULL) +#define LED_CTX_HAS_DUAL_ACTIVITY_LEDS(_led_ctx) \ + (_led_ctx->activity_led[LED_ACTIVITY_TX].port != NULL) + /* Forward declarations */ static const struct smf_state led_states[]; static void led_tick(struct k_timer *timer); @@ -108,42 +125,56 @@ static int led_event_enqueue(uint16_t ch, led_event_t event) return 0; } -static void led_set(struct led_ctx *lctx, bool state, bool activity) +static void led_indicate_state(struct led_ctx *lctx, bool state) { int err; - if (lctx->state_led.port != NULL) { + if (LED_CTX_HAS_STATE_LED(lctx)) { err = gpio_pin_set_dt(&lctx->state_led, state); if (err != 0) { LOG_ERR("failed to turn %s channel %u state LED (err %d)", state ? "on" : "off", lctx->ch, err); } } - - if (lctx->activity_led.port != NULL) { - err = gpio_pin_set_dt(&lctx->activity_led, activity); - if (err != 0) { - LOG_ERR("failed to turn %s channel %u activity LED (err %d)", - activity ? "on" : "off", lctx->ch, err); - } - } } -static void led_toggle(struct led_ctx *lctx) +static void led_indicate_activity(struct led_ctx *lctx, enum led_activity type, bool activity) { + struct gpio_dt_spec *led = NULL; + int value = activity; int err; - if (lctx->state_led.port != NULL) { - err = gpio_pin_toggle_dt(&lctx->state_led); - if (err != 0) { - LOG_ERR("failed to toggle channel %u state LED (err %d)", lctx->ch, err); + switch (type) { + case LED_ACTIVITY_RX: + if (LED_CTX_HAS_ACTIVITY_LED(lctx)) { + led = &lctx->activity_led[LED_ACTIVITY_RX]; } + break; + + case LED_ACTIVITY_TX: + if (LED_CTX_HAS_DUAL_ACTIVITY_LEDS(lctx)) { + led = &lctx->activity_led[LED_ACTIVITY_TX]; + } else if (LED_CTX_HAS_ACTIVITY_LED(lctx)) { + led = &lctx->activity_led[LED_ACTIVITY_RX]; + } + + break; + + default: + __ASSERT_NO_MSG(false); + break; } - if (lctx->activity_led.port != NULL) { - err = gpio_pin_toggle_dt(&lctx->activity_led); + if (led == NULL && lctx->started && LED_CTX_HAS_STATE_LED(lctx)) { + led = &lctx->state_led; + value = !activity; + } + + if (led != NULL) { + err = gpio_pin_set_dt(led, activity); if (err != 0) { - LOG_ERR("failed to toggle channel %u activity LED (err %d)", lctx->ch, err); + LOG_ERR("failed to turn %s channel %u activity LED (err %d)", + activity ? "on" : "off", lctx->ch, err); } } } @@ -158,7 +189,7 @@ static void led_state_normal_run(void *obj) break; default: /* Event ignored */ - }; + } } static void led_state_normal_stopped_entry(void *obj) @@ -170,7 +201,9 @@ static void led_state_normal_stopped_entry(void *obj) return; } - led_set(lctx, false, false); + led_indicate_state(lctx, false); + led_indicate_activity(lctx, LED_ACTIVITY_RX, false); + led_indicate_activity(lctx, LED_ACTIVITY_TX, false); } static void led_state_normal_stopped_run(void *obj) @@ -187,68 +220,49 @@ static void led_state_normal_stopped_run(void *obj) break; default: /* Event ignored */ - }; + } } static void led_state_normal_started_entry(void *obj) { struct led_ctx *lctx = obj; - led_set(lctx, true, false); + led_indicate_state(lctx, true); + led_indicate_activity(lctx, LED_ACTIVITY_RX, false); + led_indicate_activity(lctx, LED_ACTIVITY_TX, false); } static void led_state_normal_started_run(void *obj) { struct led_ctx *lctx = obj; + int i; switch (lctx->event) { case LED_EVENT_TICK: + for (i = 0; i < ARRAY_SIZE(lctx->ticks); i++) { + if (lctx->ticks[i] != 0U) { + lctx->ticks[i]--; + if (lctx->ticks[i] == (LED_TICKS_ACTIVITY / 2U)) { + led_indicate_activity(lctx, i, true); + } else if (lctx->ticks[i] == 0U) { + led_indicate_activity(lctx, i, false); + } + } + } + smf_set_handled(SMF_CTX(lctx)); break; case LED_EVENT_CHANNEL_STOPPED: lctx->started = false; smf_set_state(SMF_CTX(lctx), &led_states[LED_STATE_NORMAL_STOPPED]); break; - case LED_EVENT_CHANNEL_ACTIVITY: - smf_set_state(SMF_CTX(lctx), &led_states[LED_STATE_NORMAL_ACTIVITY]); - break; - default: - /* Event ignored */ - }; -} - -static void led_state_normal_activity_entry(void *obj) -{ - struct led_ctx *lctx = obj; - - lctx->ticks = LED_TICKS_ACTIVITY; -} - -static void led_state_normal_activity_run(void *obj) -{ - struct led_ctx *lctx = obj; - - switch (lctx->event) { - case LED_EVENT_TICK: - lctx->ticks--; - - if (lctx->ticks == (LED_TICKS_ACTIVITY / 2U)) { - if (lctx->activity_led.port != NULL) { - led_set(lctx, true, true); - } else { - led_set(lctx, false, false); - } - - smf_set_handled(SMF_CTX(lctx)); - } else if (lctx->ticks == 0U) { - smf_set_state(SMF_CTX(lctx), &led_states[LED_STATE_NORMAL]); - } - break; - case LED_EVENT_CHANNEL_STARTED: - lctx->started = true; + case LED_EVENT_CHANNEL_ACTIVITY_TX: + lctx->ticks[LED_ACTIVITY_TX] = LED_TICKS_ACTIVITY; + smf_set_handled(SMF_CTX(lctx)); break; - case LED_EVENT_CHANNEL_STOPPED: - lctx->started = false; + case LED_EVENT_CHANNEL_ACTIVITY_RX: + lctx->ticks[LED_ACTIVITY_RX] = LED_TICKS_ACTIVITY; + smf_set_handled(SMF_CTX(lctx)); break; default: /* Event ignored */ @@ -259,20 +273,38 @@ static void led_state_identify_entry(void *obj) { struct led_ctx *lctx = obj; - lctx->ticks = LED_TICKS_IDENTIFY; + lctx->identify_ticks = LED_TICKS_IDENTIFY; - led_set(lctx, true, false); + led_indicate_state(lctx, true); + led_indicate_activity(lctx, LED_ACTIVITY_RX, true); + led_indicate_activity(lctx, LED_ACTIVITY_TX, true); } static void led_state_identify_run(void *obj) { struct led_ctx *lctx = obj; + struct gpio_dt_spec *leds[3]; + int err; + int i; switch (lctx->event) { case LED_EVENT_TICK: - if (--lctx->ticks == 0U) { - led_toggle(lctx); - lctx->ticks = LED_TICKS_IDENTIFY; + if (--lctx->identify_ticks == 0U) { + leds[0] = &lctx->state_led; + leds[1] = &lctx->activity_led[LED_ACTIVITY_RX]; + leds[2] = &lctx->activity_led[LED_ACTIVITY_TX]; + + for (i = 0; i < ARRAY_SIZE(leds); i++) { + if (leds[i]->port != NULL) { + err = gpio_pin_toggle_dt(leds[i]); + if (err != 0) { + LOG_ERR("failed to toggle channel %u LED %d " + "(err %d)", lctx->ch, i, err); + } + } + } + + lctx->identify_ticks = LED_TICKS_IDENTIFY; } break; case LED_EVENT_CHANNEL_STARTED: @@ -286,7 +318,7 @@ static void led_state_identify_run(void *obj) break; default: /* Event ignored */ - }; + } } /* clang-format off */ @@ -306,11 +338,6 @@ static const struct smf_state led_states[] = { NULL, &led_states[LED_STATE_NORMAL], NULL), - [LED_STATE_NORMAL_ACTIVITY] = SMF_CREATE_STATE(led_state_normal_activity_entry, - led_state_normal_activity_run, - NULL, - &led_states[LED_STATE_NORMAL], - NULL), [LED_STATE_IDENTIFY] = SMF_CREATE_STATE(led_state_identify_entry, led_state_identify_run, NULL, @@ -361,14 +388,26 @@ int cannectivity_led_event(const struct device *dev, uint16_t ch, enum gs_usb_ev LOG_DBG("channel %u stopped", ch); led_event = LED_EVENT_CHANNEL_STOPPED; break; - case GS_USB_EVENT_CHANNEL_ACTIVITY: - /* low-pass filter activity events */ - if (!sys_timepoint_expired(lctx->activity)) { + case GS_USB_EVENT_CHANNEL_ACTIVITY_RX: + __fallthrough; + case GS_USB_EVENT_CHANNEL_ACTIVITY_TX: + /* Low-pass filtering of RX/TX activity events is combined if no TX LED */ + led_event = LED_EVENT_CHANNEL_ACTIVITY_RX; + int idx = LED_ACTIVITY_RX; + + if (event == GS_USB_EVENT_CHANNEL_ACTIVITY_TX) { + led_event = LED_EVENT_CHANNEL_ACTIVITY_TX; + + if (LED_CTX_HAS_DUAL_ACTIVITY_LEDS(lctx)) { + idx = LED_ACTIVITY_TX; + } + } + + if (!sys_timepoint_expired(lctx->activity[idx])) { goto skipped; } - lctx->activity = sys_timepoint_calc(K_MSEC(LED_TICK_MS * LED_TICKS_ACTIVITY)); - led_event = LED_EVENT_CHANNEL_ACTIVITY; + lctx->activity[idx] = sys_timepoint_calc(K_MSEC(LED_TICK_MS * LED_TICKS_ACTIVITY)); break; case GS_USB_EVENT_CHANNEL_IDENTIFY_ON: LOG_DBG("identify channel %u on", ch); @@ -442,14 +481,17 @@ int cannectivity_led_init(void) struct led_ctx *lctx; uint16_t ch; int err; + int i; for (ch = 0; ch < ARRAY_SIZE(led_channel_ctx); ch++) { lctx = &led_channel_ctx[ch]; - lctx->ch = ch; - lctx->activity = sys_timepoint_calc(K_NO_WAIT); - if (lctx->state_led.port != NULL) { + for (i = 0; i < ARRAY_SIZE(lctx->activity); i++) { + lctx->activity[i] = sys_timepoint_calc(K_NO_WAIT); + } + + if (LED_CTX_HAS_STATE_LED(lctx)) { if (!gpio_is_ready_dt(&lctx->state_led)) { LOG_ERR("state LED for channel %u not ready", ch); return -ENODEV; @@ -463,17 +505,20 @@ int cannectivity_led_init(void) } } - if (lctx->activity_led.port != NULL) { - if (!gpio_is_ready_dt(&lctx->activity_led)) { - LOG_ERR("activity LED for channel %u not ready", ch); - return -ENODEV; - } + for (i = 0; i < ARRAY_SIZE(lctx->activity_led); i++) { + if (lctx->activity_led[i].port != NULL) { + if (!gpio_is_ready_dt(&lctx->activity_led[i])) { + LOG_ERR("activity LED %d for channel %u not ready", i, ch); + return -ENODEV; + } - err = gpio_pin_configure_dt(&lctx->activity_led, GPIO_OUTPUT_INACTIVE); - if (err != 0) { - LOG_ERR("failed to configure channel %d activity LED GPIO (err %d)", - ch, err); - return err; + err = gpio_pin_configure_dt(&lctx->activity_led[i], + GPIO_OUTPUT_INACTIVE); + if (err != 0) { + LOG_ERR("failed to configure channel %d activity LED %d " + "GPIO (err %d)", ch, i, err); + return err; + } } } diff --git a/include/cannectivity/usb/class/gs_usb.h b/include/cannectivity/usb/class/gs_usb.h index dff440e..436737f 100644 --- a/include/cannectivity/usb/class/gs_usb.h +++ b/include/cannectivity/usb/class/gs_usb.h @@ -517,8 +517,10 @@ enum gs_usb_event { GS_USB_EVENT_CHANNEL_STARTED, /** Channel stopped. */ GS_USB_EVENT_CHANNEL_STOPPED, - /** Channel RX/TX activity. */ - GS_USB_EVENT_CHANNEL_ACTIVITY, + /** Channel RX activity. */ + GS_USB_EVENT_CHANNEL_ACTIVITY_RX, + /** Channel TX activity. */ + GS_USB_EVENT_CHANNEL_ACTIVITY_TX, #if defined(CONFIG_USB_DEVICE_GS_USB_IDENTIFICATION) || defined(CONFIG_USBD_GS_USB_IDENTIFICATION) /** Visual channel identification on. */ GS_USB_EVENT_CHANNEL_IDENTIFY_ON, diff --git a/subsys/usb/device/class/gs_usb.c b/subsys/usb/device/class/gs_usb.c index bf96307..6ab85c5 100644 --- a/subsys/usb/device/class/gs_usb.c +++ b/subsys/usb/device/class/gs_usb.c @@ -943,6 +943,7 @@ static void gs_usb_rx_thread(void *p1, void *p2, void *p3) struct gs_usb_data *data = dev->data; struct gs_usb_channel_data *channel; struct gs_usb_host_frame_hdr *hdr; + enum gs_usb_event event; struct net_buf *buf; uint32_t can_id; uint16_t ch; @@ -958,6 +959,12 @@ static void gs_usb_rx_thread(void *p1, void *p2, void *p3) can_id = hdr->can_id; ch = hdr->channel; + if (hdr->echo_id == GS_USB_HOST_FRAME_ECHO_ID_RX_FRAME) { + event = GS_USB_EVENT_CHANNEL_ACTIVITY_RX; + } else { + event = GS_USB_EVENT_CHANNEL_ACTIVITY_TX; + } + __ASSERT_NO_MSG(ch <= data->nchannels); channel = &data->channels[ch]; @@ -978,8 +985,7 @@ static void gs_usb_rx_thread(void *p1, void *p2, void *p3) } if (data->ops.event != NULL) { - err = data->ops.event(dev, ch, GS_USB_EVENT_CHANNEL_ACTIVITY, - data->user_data); + err = data->ops.event(dev, ch, event, data->user_data); if (err != 0) { LOG_ERR("activity callback for channel %u failed (err %d)", ch, err); diff --git a/subsys/usb/device_next/class/gs_usb.c b/subsys/usb/device_next/class/gs_usb.c index 6dd33b7..422daab 100644 --- a/subsys/usb/device_next/class/gs_usb.c +++ b/subsys/usb/device_next/class/gs_usb.c @@ -1047,6 +1047,7 @@ static void gs_usb_rx_thread(void *p1, void *p2, void *p3) struct gs_usb_data *data = dev->data; struct gs_usb_host_frame_hdr *hdr; struct gs_usb_channel_data *channel; + enum gs_usb_event event; struct net_buf *buf; struct udc_buf_info *bi; uint32_t can_id; @@ -1063,6 +1064,12 @@ static void gs_usb_rx_thread(void *p1, void *p2, void *p3) can_id = hdr->can_id; ch = hdr->channel; + if (hdr->echo_id == GS_USB_HOST_FRAME_ECHO_ID_RX_FRAME) { + event = GS_USB_EVENT_CHANNEL_ACTIVITY_RX; + } else { + event = GS_USB_EVENT_CHANNEL_ACTIVITY_TX; + } + __ASSERT_NO_MSG(ch <= data->nchannels); channel = &data->channels[ch]; @@ -1089,8 +1096,7 @@ static void gs_usb_rx_thread(void *p1, void *p2, void *p3) } if (data->ops.event != NULL) { - err = data->ops.event(dev, ch, GS_USB_EVENT_CHANNEL_ACTIVITY, - data->user_data); + err = data->ops.event(dev, ch, event, data->user_data); if (err != 0) { LOG_ERR("activity callback for channel %u failed (err %d)", ch, err); diff --git a/tests/subsys/usb/gs_usb/host/src/main.c b/tests/subsys/usb/gs_usb/host/src/main.c index e76a7c0..b267371 100644 --- a/tests/subsys/usb/gs_usb/host/src/main.c +++ b/tests/subsys/usb/gs_usb/host/src/main.c @@ -92,8 +92,13 @@ static int event_cb(const struct device *dev, uint16_t ch, enum gs_usb_event eve case GS_USB_EVENT_CHANNEL_STOPPED: LOG_DBG("dev = %s, ch = %u, started = 0, user_data = 0x%08x", dev->name, ch, ud); break; - case GS_USB_EVENT_CHANNEL_ACTIVITY: - LOG_DBG("dev = %s, ch = %u, activity = 1, user_data = 0x%08x", dev->name, ch, ud); + case GS_USB_EVENT_CHANNEL_ACTIVITY_RX: + LOG_DBG("dev = %s, ch = %u, rx activity = 1, user_data = 0x%08x", dev->name, ch, + ud); + break; + case GS_USB_EVENT_CHANNEL_ACTIVITY_TX: + LOG_DBG("dev = %s, ch = %u, tx activity = 1, user_data = 0x%08x", dev->name, ch, + ud); break; case GS_USB_EVENT_CHANNEL_IDENTIFY_ON: LOG_DBG("dev = %s, ch = %u, identify = 1, user_data = 0x%08x", dev->name, ch, ud);