commit fa89c89fbe76af299fc57ea895b973b12b1e44ad Author: Your Name Date: Mon Jul 27 15:09:32 2026 +0300 First diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1825667 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build +tmp +boards \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..bfe191b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug with pyOCD (RP2350)", + "type": "cortex-debug", + "request": "launch", + "servertype": "pyocd", + "serverpath": "${workspaceFolder}/../venv/bin/pyocd", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/build/zephyr/zephyr.elf", + "interface": "swd", + "runToEntryPoint": "main", + // "preLaunchTask": "build", + "armToolchainPath": "/usr/bin", + "gdbPath": "/usr/bin/arm-none-eabi-gdb", + "serverArgs": [ + "--target=rp2350", + "--core=0" + ], + "showDevDebugOutput": "raw" + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..53f3e8b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(usb_commands) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..b4cd3a9 --- /dev/null +++ b/Kconfig @@ -0,0 +1,60 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +source "Kconfig.zephyr" + +menu "USB options" + depends on USB_DEVICE_STACK_NEXT + +config USBD_MANUFACTURER + string "USB device manufacturer string" + default "Zephyr Project" + help + USB device manufacturer string. + +config USBD_PRODUCT + string "USB device product string" + default "USBD" + help + USB device product stringa. + +config USBD_VID + hex "USB device Vendor ID" + default 0x2fe3 + help + USB device Vendor ID. The default id (0x2fe3) is associated to + Zephyr Project, you must use your own VID and applications + outside of Zephyr Project. + +config USBD_PID + hex "USB device Product ID" + default 0x0001 + help + USB device Product ID. You must use your own PID + and applications outside of Zephyr Project. + +config USBD_SELF_POWERED + bool "USB device Self-powered attribute" + default y + help + Set the Self-powered attribute in the configuration. + +config USBD_REMOTE_WAKEUP + bool "USB device Remote Wakeup attribute" + help + Set the Remote Wakeup attribute in the configuration. + +config USBD_MAX_POWER + int "USB device bMaxPower value" + default 125 + range 0 250 + help + bMaxPower value in the configuration in 2 mA units. + +config USBD_20_EXTENSION_DESC + bool "Use default USB 2.0 Extension Descriptor" + depends on USBD_BOS_SUPPORT + help + Set bcdUSB value to 0201 and use default USB 2.0 Extension Descriptor. + +endmenu \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..471e33e --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# Zephyr USB Command Handler + +This repository contains the implementation of a USB command handler for the Zephyr RTOS. The system allows devices to communicate via USB using custom command messages. + +### Components + +1. **command_handler.c/h**: Handles incoming commands, processes them, and performs actions based on the command type. +2. **command_message.c/h**: Manages the creation, validation, and logging of command messages, including CRC calculation. +3. **usb.c/h**: Manages USB initialization, data reception via UART, and sending responses. Includes a USB thread. +4. **usb_conf.c/h**: Configures the USB device. + +### Command Structure + +```c +struct command_message_t { + uint8_t prefix; // 0x69 + uint8_t length; + uint8_t id; + uint8_t command; + uint8_t crc; + uint8_t data[160]; +} __attribute__((packed)); +``` + +### CRC Calculation Function + +```c +uint8_t calculate_crc(struct command_message_t *msg) { + uint32_t sum = 0; + uint8_t crc = 0; + + uint8_t *byte_ptr = (uint8_t *)msg; + + for (int i = 0; i < (sizeof(struct command_message_t) - sizeof(msg->data) + msg->length) - 1; i++) { + sum += byte_ptr[i]; + } + + crc = 0x100 - (sum & 0xff); + + return crc; +} +``` + +### Python test script (AI generated) + +**Command Usage:** + +```bash +python3 scripts/led_blink.py +``` + +This script sends LED toggle commands to the device connected via USB. \ No newline at end of file diff --git a/app.overlay b/app.overlay new file mode 100644 index 0000000..6caf5ed --- /dev/null +++ b/app.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,console = &cdc_acm_uart0; + zephyr,shell-uart = &cdc_acm_uart0; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..dfceba2 --- /dev/null +++ b/prj.conf @@ -0,0 +1,33 @@ +CONFIG_GPIO=y +CONFIG_PWM=y + +# Serial +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_UART_LINE_CTRL=y + +# USB +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n +# CONFIG_CDC_ACM_SERIAL_PRODUCT_STRING="USB CDC ACM" +# CONFIG_CDC_ACM_SERIAL_PID= +CONFIG_USBD_VID=0xffff +CONFIG_USBD_PID=0x0420 +CONFIG_USBD_MANUFACTURER="Zephyr Project" +CONFIG_USBD_PRODUCT="USBD sample" +CONFIG_USBD_SELF_POWERED=y +# CONFIG_USBD_REMOTE_WAKEUP= +CONFIG_USBD_MAX_POWER=125 + +# LOG +CONFIG_LOG=y +CONFIG_USBD_CDC_ACM_LOG_LEVEL_OFF=y # This removes a pointless warning +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MODE_IMMEDIATE=y +# CONFIG_USBD_LOG_LEVEL_ERR=y +# CONFIG_UDC_DRIVER_LOG_LEVEL_ERR=y + +# DEBUG +CONFIG_DEBUG_THREAD_INFO=y \ No newline at end of file diff --git a/scripts/led_blink.py b/scripts/led_blink.py new file mode 100644 index 0000000..51cd987 --- /dev/null +++ b/scripts/led_blink.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +import serial +import struct +import threading +import time + +PORT = "/dev/ttyACM0" +BAUDRATE = 115200 + +COMMAND_PREFIX = 0x69 + +COMMAND_ERROR = 0 +COMMAND_ACK = 1 +COMMAND_NACK = 2 +LED = 3 + +DEVICE_ID = 0 + + +def calculate_crc(msg: bytes) -> int: + s = sum(msg) & 0xFF + return (-s) & 0xFF + + +def make_packet(command: int, data: bytes = b"") -> bytes: + length = len(data) + + pkt = bytearray() + pkt.append(COMMAND_PREFIX) + pkt.append(length) + pkt.append(DEVICE_ID) + pkt.append(command) + pkt.append(0) # CRC placeholder + pkt.extend(data) + + pkt[4] = calculate_crc(pkt[:4] + pkt[5:]) + + return bytes(pkt) + + +def verify_crc(packet: bytes) -> bool: + crc = packet[4] + calc = calculate_crc(packet[:4] + packet[5:]) + return crc == calc + + +def packet_size(buf: bytes): + if len(buf) < 2: + return None + return 5 + buf[1] + + +def reader(ser): + rx = bytearray() + + while True: + data = ser.read(64) + if not data: + continue + + rx.extend(data) + + while rx: + # Binary packet? + if rx[0] == COMMAND_PREFIX: + size = packet_size(rx) + if size is None or len(rx) < size: + break + + pkt = bytes(rx[:size]) + del rx[:size] + + if not verify_crc(pkt): + print("RX: Bad CRC:", pkt.hex()) + continue + + length = pkt[1] + dev_id = pkt[2] + cmd = pkt[3] + + if cmd == COMMAND_ACK: + print(f"<-- ACK (device={dev_id})") + + elif cmd == COMMAND_NACK: + print(f"<-- NACK (device={dev_id})") + + elif cmd == COMMAND_ERROR: + print(f"<-- ERROR (device={dev_id})") + + else: + print(f"<-- Command {cmd} len={length}") + + else: + # ASCII log output + idx = rx.find(b'\n') + if idx == -1: + break + + line = rx[:idx + 1] + del rx[:idx + 1] + + try: + print("[LOG]", line.decode().rstrip()) + except UnicodeDecodeError: + print("[RAW]", line.hex()) + + +def main(): + ser = serial.Serial(PORT, BAUDRATE, timeout=0.05) + + threading.Thread(target=reader, args=(ser,), daemon=True).start() + + for i in range(10): + print(f"--> Sending LED command {i + 1}") + ser.write(make_packet(LED)) + time.sleep(0.5) + + print("Done.") + time.sleep(2) + + +if __name__ == "__main__": + main() diff --git a/src/command_handler.c b/src/command_handler.c new file mode 100644 index 0000000..706549f --- /dev/null +++ b/src/command_handler.c @@ -0,0 +1,35 @@ +#include "command_handler.h" +#include "led.h" + +#include + +LOG_MODULE_REGISTER(command_handler, LOG_LEVEL_INF); + +int command_handler(struct command_message_t *msg) { + if (msg == NULL) { + LOG_ERR("Received NULL message pointer"); + return -EINVAL; + } + + LOG_DBG("Processing command: %d, length: %d", msg->command, msg->length); + + switch (msg->command) { + case COMMAND_ERROR: { + LOG_WRN("Received COMMAND_ERROR"); + break; + } + + case LED: { + // Toggle LED with the LED command + led_toggle(); + break; + } + + default: { + LOG_WRN("Unknown command received: %d", msg->command); + return -EINVAL; + } + } + + return 0; +} \ No newline at end of file diff --git a/src/command_handler.h b/src/command_handler.h new file mode 100644 index 0000000..9e0f904 --- /dev/null +++ b/src/command_handler.h @@ -0,0 +1,16 @@ +#ifndef COMMAND_HANDLER_H +#define COMMAND_HANDLER_H + + +#include "command_message.h" + +/** + * @brief Process received command message + * + * @param msg Command message to process + * @return 0 on success, negative errno on failure + */ +int command_handler(struct command_message_t *msg); + + +#endif // COMMAND_HANDLER_H \ No newline at end of file diff --git a/src/command_message.c b/src/command_message.c new file mode 100644 index 0000000..b3d1e77 --- /dev/null +++ b/src/command_message.c @@ -0,0 +1,73 @@ +#include "command_message.h" + +#include +#include + +LOG_MODULE_REGISTER(command_message, LOG_LEVEL_INF); + +void command_message_init(struct command_message_t *msg) { + memset(msg, 0, sizeof(struct command_message_t)); + msg->prefix = COMMAND_PREFIX; + msg->id = COMMAND_ID; +} + +void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[160]) { + // Ensure length doesn't exceed available space + if (length > sizeof(msg->data) - 1) { + return; + } + + command_message_init(msg); + msg->length = length; + msg->command = command; + + // Copy the data + if (data != NULL) { + for (int i = 0; i < msg->length; i++) { + msg->data[i] = data[i]; + } + } + msg->crc = command_calculate_crc(msg); +} + +uint8_t command_calculate_crc(struct command_message_t *msg) { + uint32_t sum = 0; + uint8_t crc = 0; + + uint8_t *byte_ptr = (uint8_t *)msg; + + for (int i = 0; i < (sizeof(struct command_message_t) - sizeof(msg->data) + msg->length) - 1; i++) { + sum += byte_ptr[i]; + } + + crc = 0x100 - (sum & 0xff); + + return crc; +} + +void command_create_ack(struct command_message_t *msg) { + command_create_message(msg, 0, COMMAND_ACK, NULL); +} + +void command_create_nack(struct command_message_t *msg) { + command_create_message(msg, 0, COMMAND_NACK, NULL); +} + +void command_log(struct command_message_t *msg) { + if (msg->length > sizeof(msg->data) - 1) { + LOG_ERR("Message length too long: %d", msg->length); + return; + } + + LOG_INF("Prefix: %d\n\r", msg->prefix); + LOG_INF("Length: %d\n\r", msg->length); + LOG_INF("COMMAND_ID: %d\n\r", msg->id); + LOG_INF("Command: %d\n\r", msg->command); + LOG_INF("Data:\n\r"); + + for (int i = 0; i < msg->length; i++) { + LOG_INF("%d", msg->data[i]); + } + + LOG_INF("CRC: %d\n\r", msg->crc); +} \ No newline at end of file diff --git a/src/command_message.h b/src/command_message.h new file mode 100644 index 0000000..b3dfc14 --- /dev/null +++ b/src/command_message.h @@ -0,0 +1,74 @@ +#ifndef COMMAND_MESSAGE_H +#define COMMAND_MESSAGE_H + + +#include + + +#define COMMAND_PREFIX 0x69 +#define COMMAND_ID 0x00 + +typedef enum { + COMMAND_ERROR, + COMMAND_ACK, + COMMAND_NACK, + LED, +} commands_e; + +struct command_message_t { + uint8_t prefix; + uint8_t length; + uint8_t id; + uint8_t command; + uint8_t crc; + uint8_t data[160]; +} __attribute__((packed)); + +/** + * @brief Initialize command message to default state + * + * @param msg Message to initialize + */ +void command_message_init(struct command_message_t *msg); + +/** + * @brief Create command message with data and CRC + * + * @param msg Message to populate + * @param length Data length in bytes + * @param command Command type + * @param data Data payload + */ +void command_create_message(struct command_message_t *msg, uint8_t length, commands_e command, uint8_t data[160]); + +/** + * @brief Calculate CRC for command message + * + * @param msg Message to calculate CRC for + * @return CRC value + */ +uint8_t command_calculate_crc(struct command_message_t *msg); + +/** + * @brief Create ACK command message + * + * @param msg Message to populate + */ +void command_create_ack(struct command_message_t *msg); + +/** + * @brief Create NACK command message + * + * @param msg Message to populate + */ +void command_create_nack(struct command_message_t *msg); + +/** + * @brief Print the command with LOG + * + * @param msg Message to calculate CRC for + */ +void command_log(struct command_message_t *msg); + + +#endif // COMMAND_MESSAGE_H \ No newline at end of file diff --git a/src/led.c b/src/led.c new file mode 100644 index 0000000..ef7882f --- /dev/null +++ b/src/led.c @@ -0,0 +1,51 @@ +#include "led.h" + +#include +#include + +LOG_MODULE_REGISTER(led, LOG_LEVEL_INF); + +#define LED0_NODE DT_ALIAS(led0) +static const struct gpio_dt_spec led0_gpio = GPIO_DT_SPEC_GET(LED0_NODE, gpios); +static int led0_state = 0; + +int led_init() { + int ret; + + if (!device_is_ready(led0_gpio.port)) { + LOG_ERR("LED0 GPIO device not ready"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&led0_gpio, GPIO_OUTPUT_INACTIVE); + if (ret != 0) { + LOG_ERR("Failed to configure LED0 GPIO: %d", ret); + return ret; + } + + // Turn it off + ret = gpio_pin_set_dt(&led0_gpio, 0); + if (ret != 0) { + LOG_ERR("Failed to initialize LED"); + return ret; + } + + LOG_INF("LED driver initialized (LED0: GPIO %d)", led0_gpio.pin); + + return 0; +} + +int led_set(int state) { + led0_state = state ? 1 : 0; + + int ret = gpio_pin_set_dt(&led0_gpio, led0_state); + if (ret != 0) { + LOG_ERR("Failed to set LED0: %d", ret); + } + + return ret; +} + +int led_toggle() { + return led_set(!led0_state); +} \ No newline at end of file diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..c8c7478 --- /dev/null +++ b/src/led.h @@ -0,0 +1,10 @@ +#ifndef LED_H +#define LED_H + + +int led_init(); +int led_set(int state); +int led_toggle(); + + +#endif // LED_H \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..0f6fd28 --- /dev/null +++ b/src/main.c @@ -0,0 +1,24 @@ +#include "led.h" +#include "usb.h" + +#include +LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); + + +int main(void) { + int ret; + + ret = usb_init(); + if (ret != 0) { + LOG_ERR("Failed to enable USB"); + return 0; + } + + ret = led_init(); + if (ret != 0) { + LOG_ERR("Failed to enable LED"); + return 0; + } + + return 0; +} \ No newline at end of file diff --git a/src/usb.c b/src/usb.c new file mode 100644 index 0000000..87bc988 --- /dev/null +++ b/src/usb.c @@ -0,0 +1,193 @@ +#include "usb.h" +#include "usb_conf.h" +#include "command_handler.h" + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(usb, LOG_LEVEL_DBG); + +// DEVICE +const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); +static struct usbd_context *usb_context; + +// THREAD +static struct k_thread usb_thread_data; +static k_tid_t usb_thread_id = NULL; +#define USB_THREAD_STACK_SIZE 2048 +K_THREAD_STACK_DEFINE(usb_thread_stack, USB_THREAD_STACK_SIZE); + +// RX BUFFER +#define RX_BUF_SIZE 4 +struct rx_buffer_t { + int current_read; + int current_write; + struct k_sem semaphore; + struct command_message_t buffer[RX_BUF_SIZE]; +}; + +static struct rx_buffer_t rx_buf; + + +static void interrupt_handler(const struct device *dev, void *user_data) { + ARG_UNUSED(user_data); + + while (true) { + uart_irq_update(dev); + + if (uart_irq_is_pending(dev) <= 0) { + break; + } + + if (uart_irq_rx_ready(dev)) { + size_t len = sizeof(struct command_message_t); + + int recv_len = uart_fifo_read(dev, (uint8_t *)&rx_buf.buffer[rx_buf.current_write], len); + if (recv_len < 0) { + LOG_ERR("Failed to read UART FIFO"); + recv_len = 0; + }; + + rx_buf.current_write += 1; + + if (rx_buf.current_write >= RX_BUF_SIZE) { + rx_buf.current_write = 0; + rx_buf.current_read = (RX_BUF_SIZE - 1); + } + else { + rx_buf.current_read = (rx_buf.current_write - 1); + } + + // Give semaphore for usb read thread + k_sem_give(&rx_buf.semaphore); + } + } +} + +static void usb_thread(void *p1, void *p2, void *p3) { + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + LOG_INF("USB command processing thread started"); + + while (1) { + // Wait forever for the rx semaphore + k_sem_take(&rx_buf.semaphore, K_FOREVER); + + int read_index = rx_buf.current_read; + + // Check the prefix + if (rx_buf.buffer[read_index].prefix != COMMAND_PREFIX) { + LOG_ERR("COMMAND_PREFIX does not match: %d", rx_buf.buffer[read_index].prefix); + + // Send NACK + struct command_message_t nack_buf; + command_create_nack(&nack_buf); + usb_send_command(&nack_buf); + continue; + } + + // Check the CRC + uint8_t calculated_crc = command_calculate_crc(&rx_buf.buffer[read_index]); + if (calculated_crc != rx_buf.buffer[read_index].crc) { + LOG_ERR("CRC does not match:"); + LOG_ERR("Calculated CRC: %d", calculated_crc); + LOG_ERR("Received CRC: %d", rx_buf.buffer[read_index].prefix); + + // Send NACK + struct command_message_t nack_buf; + command_create_nack(&nack_buf); + usb_send_command(&nack_buf); + + continue; + } + + int ret = command_handler(&rx_buf.buffer[read_index]); + if (ret == 0) { + // Send ACK + struct command_message_t ack_buf; + command_create_ack(&ack_buf); + usb_send_command(&ack_buf); + } + else { + // Send NACK + struct command_message_t nack_buf; + command_create_nack(&nack_buf); + usb_send_command(&nack_buf); + } + } + + LOG_INF("USB command processing thread exiting"); +} + +int usb_init() { + rx_buf.current_read = 0; + rx_buf.current_write = 0; + k_sem_init(&rx_buf.semaphore, 1, 1); + memset(rx_buf.buffer, 0, sizeof(rx_buf.buffer)); + + int ret; + + if (!device_is_ready(uart_dev)) { + LOG_ERR("CDC ACM device not ready"); + return -ENODEV; + } + + usb_context = usb_device_init(NULL); + if (usb_context == NULL) { + LOG_ERR("Failed to initialize USB device"); + return -ENODEV; + } + + if (!usbd_can_detect_vbus(usb_context)) { + ret = usbd_enable(usb_context); + if (ret) { + LOG_ERR("Failed to enable device support"); + return ret; + } + } + k_msleep(100); + + uart_irq_callback_set(uart_dev, interrupt_handler); + uart_irq_rx_enable(uart_dev); + + usb_thread_id = k_thread_create( + &usb_thread_data, + usb_thread_stack, + K_THREAD_STACK_SIZEOF(usb_thread_stack), + usb_thread, + NULL, NULL, NULL, + 5, + 0, + K_NO_WAIT + ); + + if (usb_thread_id == NULL) { + LOG_ERR("Failed to create USB thread"); + return -ENOMEM; + } + + return ret; +} + +int usb_send_command(struct command_message_t *msg) { + int ret = 0; + + if (!device_is_ready(uart_dev)) { + return -ENODEV; + } + + // Message size: prefix + length + id + command + crc + data + size_t msg_size = 5 + msg->length; + uint8_t *msg_bytes = (uint8_t *)msg; + + /* uart_poll_out blocks until sent, ensuring data integrity */ + for (size_t i = 0; i < msg_size; i++) { + uart_poll_out(uart_dev, msg_bytes[i]); + } + + return ret; +} \ No newline at end of file diff --git a/src/usb.h b/src/usb.h new file mode 100644 index 0000000..6ca1e6d --- /dev/null +++ b/src/usb.h @@ -0,0 +1,12 @@ +#ifndef USB_H +#define USB_H + + +#include "command_message.h" + + +int usb_init(); +int usb_send_command(struct command_message_t *msg); + + +#endif // USB_H \ No newline at end of file diff --git a/src/usb_conf.c b/src/usb_conf.c new file mode 100644 index 0000000..91cc5b4 --- /dev/null +++ b/src/usb_conf.c @@ -0,0 +1,184 @@ +#include "usb_conf.h" + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(usb_conf, LOG_LEVEL_DBG); + +/* By default, do not register the USB DFU class DFU mode instance. */ +static const char *const blocklist[] = { + "dfu_dfu", + NULL, +}; + +/* + * Instantiate a context named my_usb_context using the default USB device + * controller, the Zephyr project vendor ID, and the sample product ID. + * Zephyr project vendor ID must not be used outside of Zephyr samples. + */ +USBD_DEVICE_DEFINE(my_usb_context, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + CONFIG_USBD_VID, CONFIG_USBD_PID); + +USBD_DESC_LANG_DEFINE(my_usb_lang); +USBD_DESC_MANUFACTURER_DEFINE(my_usb_mfr, CONFIG_USBD_MANUFACTURER); +USBD_DESC_PRODUCT_DEFINE(my_usb_product, CONFIG_USBD_PRODUCT); +IF_ENABLED(CONFIG_HWINFO, (USBD_DESC_SERIAL_NUMBER_DEFINE(my_usb_serial))); + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); + +static const uint8_t attributes = (IS_ENABLED(CONFIG_USBD_SELF_POWERED) ? + USB_SCD_SELF_POWERED : 0) | + (IS_ENABLED(CONFIG_USBD_REMOTE_WAKEUP) ? + USB_SCD_REMOTE_WAKEUP : 0); + +/* Full speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_fs_config, + attributes, + CONFIG_USBD_MAX_POWER, &fs_cfg_desc); + +/* High speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_hs_config, + attributes, + CONFIG_USBD_MAX_POWER, &hs_cfg_desc); + +#if CONFIG_SAMPLE_USBD_20_EXTENSION_DESC +/* + * This does not yet provide valuable information, but rather serves as an + * example, and will be improved in the future. + */ +static const struct usb_bos_capability_lpm bos_cap_lpm = { + .bLength = sizeof(struct usb_bos_capability_lpm), + .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_BOS_CAPABILITY_EXTENSION, + .bmAttributes = 0UL, +}; + +USBD_DESC_BOS_DEFINE(my_usb_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm); +#endif + +static void usb_fix_code_triple(struct usbd_context *uds_ctx, const enum usbd_speed speed) { + /* Always use class code information from Interface Descriptors */ + if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) || + IS_ENABLED(CONFIG_USBD_MIDI2_CLASS) || + IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS) || + IS_ENABLED(CONFIG_USBD_VIDEO_CLASS)) { + /* + * Class with multiple interfaces have an Interface + * Association Descriptor available, use an appropriate triple + * to indicate it. + */ + usbd_device_set_code_triple(uds_ctx, speed, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } else { + usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); + } +} + +struct usbd_context *usb_device_setup(usbd_msg_cb_t msg_cb) { + int err; + + err = usbd_add_descriptor(&my_usb_context, &my_usb_lang); + if (err) { + LOG_ERR("Failed to initialize language descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&my_usb_context, &my_usb_mfr); + if (err) { + LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&my_usb_context, &my_usb_product); + if (err) { + LOG_ERR("Failed to initialize product descriptor (%d)", err); + return NULL; + } + + IF_ENABLED(CONFIG_HWINFO, ( + err = usbd_add_descriptor(&my_usb_context, &my_usb_serial); + )) + if (err) { + LOG_ERR("Failed to initialize SN descriptor (%d)", err); + return NULL; + } + + if (USBD_SUPPORTS_HIGH_SPEED && + usbd_caps_speed(&my_usb_context) == USBD_SPEED_HS) { + err = usbd_add_configuration(&my_usb_context, USBD_SPEED_HS, + &sample_hs_config); + if (err) { + LOG_ERR("Failed to add High-Speed configuration"); + return NULL; + } + + err = usbd_register_all_classes(&my_usb_context, USBD_SPEED_HS, 1, + blocklist); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + usb_fix_code_triple(&my_usb_context, USBD_SPEED_HS); + } + + err = usbd_add_configuration(&my_usb_context, USBD_SPEED_FS, + &sample_fs_config); + if (err) { + LOG_ERR("Failed to add Full-Speed configuration"); + return NULL; + } + + err = usbd_register_all_classes(&my_usb_context, USBD_SPEED_FS, 1, blocklist); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + usb_fix_code_triple(&my_usb_context, USBD_SPEED_FS); + usbd_self_powered(&my_usb_context, attributes & USB_SCD_SELF_POWERED); + + if (msg_cb != NULL) { + err = usbd_msg_register_cb(&my_usb_context, msg_cb); + if (err) { + LOG_ERR("Failed to register message callback"); + return NULL; + } + } + +#if CONFIG_SAMPLE_USBD_20_EXTENSION_DESC + (void)usbd_device_set_bcd_usb(&my_usb_context, USBD_SPEED_FS, 0x0201); + (void)usbd_device_set_bcd_usb(&my_usb_context, USBD_SPEED_HS, 0x0201); + + err = usbd_add_descriptor(&my_usb_context, &my_usb_usbext); + if (err) { + LOG_ERR("Failed to add USB 2.0 Extension Descriptor"); + return NULL; + } +#endif + + return &my_usb_context; +} + +struct usbd_context *usb_device_init(usbd_msg_cb_t msg_cb) { + int err; + + if (usb_device_setup(msg_cb) == NULL) { + return NULL; + } + + err = usbd_init(&my_usb_context); + if (err) { + LOG_ERR("Failed to initialize device support"); + return NULL; + } + + return &my_usb_context; +} \ No newline at end of file diff --git a/src/usb_conf.h b/src/usb_conf.h new file mode 100644 index 0000000..66ab591 --- /dev/null +++ b/src/usb_conf.h @@ -0,0 +1,11 @@ +#ifndef USB_CONF_H +#define USB_CONF_H + + +#include + +struct usbd_context *usb_device_setup(usbd_msg_cb_t msg_cb); +struct usbd_context *usb_device_init(usbd_msg_cb_t msg_cb); + + +#endif // USB_CONF_H \ No newline at end of file diff --git a/src/zbus_channels.c b/src/zbus_channels.c new file mode 100644 index 0000000..cd9136b --- /dev/null +++ b/src/zbus_channels.c @@ -0,0 +1,21 @@ +#include "zbus_channels.h" +#include "command_message.h" + + +ZBUS_CHAN_DEFINE( + in_command_chan, + struct command_message_t, + NULL, + NULL, + ZBUS_OBSERVERS_EMPTY, + ZBUS_MSG_INIT(0) +); + +ZBUS_CHAN_DEFINE( + out_command_chan, + struct command_message_t, + NULL, + NULL, + ZBUS_OBSERVERS_EMPTY, + ZBUS_MSG_INIT(0) +); \ No newline at end of file diff --git a/src/zbus_channels.h b/src/zbus_channels.h new file mode 100644 index 0000000..939e842 --- /dev/null +++ b/src/zbus_channels.h @@ -0,0 +1,15 @@ +#ifndef ZBUS_CHANNELS_H +#define ZBUS_CHANNELS_H + + +#include +#include + +#include "command_message.h" + + +ZBUS_CHAN_DECLARE(in_command_chan); +ZBUS_CHAN_DECLARE(out_command_chan); + + +#endif // ZBUS_CHANNELS_H \ No newline at end of file