From 941c2a900433f98d0c865a2300900a6ed688722d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 31 Mar 2026 18:45:24 -0400 Subject: [PATCH 01/10] add rp2040 driver --- src/hardware/rp2040/api.c | 278 ++++++++++++++++++++++++ src/hardware/rp2040/protocol.c | 383 +++++++++++++++++++++++++++++++++ src/hardware/rp2040/protocol.h | 76 +++++++ 3 files changed, 737 insertions(+) create mode 100644 src/hardware/rp2040/api.c create mode 100644 src/hardware/rp2040/protocol.c create mode 100644 src/hardware/rp2040/protocol.h diff --git a/src/hardware/rp2040/api.c b/src/hardware/rp2040/api.c new file mode 100644 index 000000000..67596cd12 --- /dev/null +++ b/src/hardware/rp2040/api.c @@ -0,0 +1,278 @@ +/* + * PicoMSO - libsigrok-style host driver API layer + * Copyright (C) 2026 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "protocol.h" + +#include +#include + +typedef int (*picomso_logic_samples_cb)(void *user_data, const uint16_t *samples, size_t sample_count); + +typedef struct { + picomso_protocol_t protocol; + picomso_info_response_t info; + uint32_t capabilities; + unsigned int channel_count; + bool is_open; + bool logic_mode_active; + bool capture_running; +} picomso_driver_t; + +static const char *const logic_channel_names[PICOMSO_DRIVER_CHANNEL_COUNT] = { + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", +}; + +static bool request_is_valid(const picomso_request_capture_request_t *request) +{ + unsigned int i; + + if (request == NULL) { + return false; + } + if (request->total_samples == 0u || request->pre_trigger_samples > request->total_samples) { + return false; + } + + for (i = 0u; i < PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT; ++i) { + if (request->trigger[i].is_enabled > 1u) { + return false; + } + if (request->trigger[i].pin >= PICOMSO_DRIVER_CHANNEL_COUNT) { + return false; + } + switch ((picomso_trigger_match_t)request->trigger[i].match) { + case PICOMSO_TRIGGER_MATCH_LEVEL_LOW: + case PICOMSO_TRIGGER_MATCH_LEVEL_HIGH: + case PICOMSO_TRIGGER_MATCH_EDGE_LOW: + case PICOMSO_TRIGGER_MATCH_EDGE_HIGH: + break; + default: + return false; + } + } + + return true; +} + +void picomso_driver_init(picomso_driver_t *driver, const picomso_transport_t *transport) +{ + if (driver == NULL) { + return; + } + + memset(driver, 0, sizeof(*driver)); + picomso_protocol_init(&driver->protocol, transport); + driver->channel_count = PICOMSO_DRIVER_CHANNEL_COUNT; +} + +picomso_result_t picomso_driver_open(picomso_driver_t *driver) +{ + picomso_result_t result; + + if (driver == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + result = picomso_protocol_get_info(&driver->protocol, &driver->info); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + result = picomso_protocol_get_capabilities(&driver->protocol, &driver->capabilities); + if (result != PICOMSO_RESULT_OK) { + return result; + } + if ((driver->capabilities & PICOMSO_CAP_LOGIC) == 0u) { + return PICOMSO_RESULT_ERR_UNSUPPORTED; + } + + driver->is_open = true; + driver->logic_mode_active = false; + driver->capture_running = false; + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_driver_stop(picomso_driver_t *driver) +{ + picomso_result_t result; + + if (driver == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if (!driver->is_open) { + return PICOMSO_RESULT_OK; + } + + result = picomso_protocol_set_mode(&driver->protocol, PICOMSO_MODE_UNSET); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + driver->logic_mode_active = false; + driver->capture_running = false; + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_driver_close(picomso_driver_t *driver) +{ + picomso_result_t result; + + if (driver == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if (!driver->is_open) { + return PICOMSO_RESULT_OK; + } + + result = picomso_driver_stop(driver); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + driver->is_open = false; + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_driver_start_logic_capture(picomso_driver_t *driver, + const picomso_request_capture_request_t *request) +{ + picomso_status_response_t status; + picomso_result_t result; + + if (driver == NULL || !request_is_valid(request)) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if (!driver->is_open) { + return PICOMSO_RESULT_ERR_STATE; + } + + result = picomso_protocol_set_mode(&driver->protocol, PICOMSO_MODE_LOGIC); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + result = picomso_protocol_get_status(&driver->protocol, &status); + if (result != PICOMSO_RESULT_OK) { + return result; + } + if (status.mode != PICOMSO_MODE_LOGIC || status.capture_state == PICOMSO_CAPTURE_RUNNING) { + return PICOMSO_RESULT_ERR_STATE; + } + + result = picomso_protocol_request_capture(&driver->protocol, request); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + driver->logic_mode_active = true; + driver->capture_running = true; + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_driver_wait_capture_complete(picomso_driver_t *driver, + unsigned int max_polls, + unsigned int poll_interval_ms) +{ + picomso_status_response_t status; + picomso_result_t result; + unsigned int poll; + + if (driver == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if (!driver->is_open || !driver->logic_mode_active || !driver->capture_running) { + return PICOMSO_RESULT_ERR_STATE; + } + + for (poll = 0u; poll < max_polls; ++poll) { + result = picomso_protocol_get_status(&driver->protocol, &status); + if (result != PICOMSO_RESULT_OK) { + return result; + } + if (status.mode != PICOMSO_MODE_LOGIC) { + return PICOMSO_RESULT_ERR_STATE; + } + if (status.capture_state == PICOMSO_CAPTURE_IDLE) { + driver->capture_running = false; + return PICOMSO_RESULT_OK; + } + if (status.capture_state != PICOMSO_CAPTURE_RUNNING) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if (poll_interval_ms > 0u && driver->protocol.transport.wait_ms != NULL && poll + 1u < max_polls) { + if (driver->protocol.transport.wait_ms(driver->protocol.transport.user_data, poll_interval_ms) != 0) { + return PICOMSO_RESULT_ERR_IO; + } + } + } + + return PICOMSO_RESULT_ERR_TIMEOUT; +} + +picomso_result_t picomso_driver_read_logic_capture(picomso_driver_t *driver, + picomso_logic_samples_cb callback, + void *user_data, + size_t *captured_samples) +{ + picomso_logic_block_t block; + uint16_t samples[PICOMSO_DATA_BLOCK_SIZE / sizeof(uint16_t)]; + uint16_t expected_block_id = 0u; + size_t total_samples = 0u; + size_t sample_count; + size_t i; + picomso_result_t result; + + if (driver == NULL || callback == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if (!driver->is_open || !driver->logic_mode_active || driver->capture_running) { + return PICOMSO_RESULT_ERR_STATE; + } + + for (;;) { + result = picomso_protocol_read_data_block(&driver->protocol, &block); + if (result == PICOMSO_RESULT_DONE) { + if (captured_samples != NULL) { + *captured_samples = total_samples; + } + return PICOMSO_RESULT_OK; + } + if (result != PICOMSO_RESULT_OK) { + return result; + } + if (block.block_id != expected_block_id) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if ((block.data_len % sizeof(uint16_t)) != 0u) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + sample_count = block.data_len / sizeof(uint16_t); + for (i = 0u; i < sample_count; ++i) { + samples[i] = (uint16_t)block.data[i * 2u] | ((uint16_t)block.data[i * 2u + 1u] << 8); + } + if (sample_count > 0u && callback(user_data, samples, sample_count) != 0) { + return PICOMSO_RESULT_ERR_CALLBACK; + } + + total_samples += sample_count; + ++expected_block_id; + } +} + +const char *picomso_driver_logic_channel_name(unsigned int index) +{ + if (index >= PICOMSO_DRIVER_CHANNEL_COUNT) { + return NULL; + } + + return logic_channel_names[index]; +} diff --git a/src/hardware/rp2040/protocol.c b/src/hardware/rp2040/protocol.c new file mode 100644 index 000000000..e744964d1 --- /dev/null +++ b/src/hardware/rp2040/protocol.c @@ -0,0 +1,383 @@ +/* + * PicoMSO - libsigrok-style host protocol layer + * Copyright (C) 2026 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include "protocol.h" + +#include + +#define PICOMSO_RESPONSE_TYPE_INFO PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_CAPABILITIES PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_STATUS PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_SET_MODE PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_REQUEST PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_DATA_BLOCK PICOMSO_MSG_DATA_BLOCK + +static uint16_t read_u16_le(const uint8_t *data) +{ + return (uint16_t)data[0] | ((uint16_t)data[1] << 8); +} + +static uint32_t read_u32_le(const uint8_t *data) +{ + return (uint32_t)data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); +} + +static void write_u16_le(uint8_t *data, uint16_t value) +{ + data[0] = (uint8_t)(value & 0xffu); + data[1] = (uint8_t)((value >> 8) & 0xffu); +} + +static void write_u32_le(uint8_t *data, uint32_t value) +{ + data[0] = (uint8_t)(value & 0xffu); + data[1] = (uint8_t)((value >> 8) & 0xffu); + data[2] = (uint8_t)((value >> 16) & 0xffu); + data[3] = (uint8_t)((value >> 24) & 0xffu); +} + +static void clear_error_state(picomso_protocol_t *proto) +{ + proto->last_device_status = (uint8_t)PICOMSO_STATUS_OK; + proto->last_error_text[0] = '\0'; +} + +static void set_error_text(picomso_protocol_t *proto, const char *text, size_t length) +{ + size_t copy_len = length; + + if (copy_len >= sizeof(proto->last_error_text)) { + copy_len = sizeof(proto->last_error_text) - 1u; + } + + if (copy_len > 0u && text != NULL) { + memcpy(proto->last_error_text, text, copy_len); + } + proto->last_error_text[copy_len] = '\0'; +} + +static picomso_result_t send_request(picomso_protocol_t *proto, + uint8_t msg_type, + const uint8_t *payload, + uint16_t payload_len, + uint8_t expected_response_type, + uint8_t *response, + size_t *response_len) +{ + uint8_t request[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t actual_length = 0u; + uint8_t seq; + uint16_t wire_length; + + if (proto == NULL || response == NULL || response_len == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if (proto->transport.control_write == NULL || proto->transport.bulk_read == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + if ((size_t)PICOMSO_PACKET_HEADER_SIZE + payload_len > sizeof(request)) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + clear_error_state(proto); + seq = proto->next_seq++; + + write_u16_le(request, PICOMSO_PACKET_MAGIC); + request[2] = PICOMSO_PROTOCOL_VERSION_MAJOR; + request[3] = PICOMSO_PROTOCOL_VERSION_MINOR; + request[4] = msg_type; + request[5] = seq; + write_u16_le(request + 6, payload_len); + if (payload_len > 0u && payload != NULL) { + memcpy(request + PICOMSO_PACKET_HEADER_SIZE, payload, payload_len); + } + + wire_length = (uint16_t)(PICOMSO_PACKET_HEADER_SIZE + payload_len); + if (proto->transport.control_write(proto->transport.user_data, request, wire_length) != 0) { + return PICOMSO_RESULT_ERR_IO; + } + if (proto->transport.bulk_read(proto->transport.user_data, response, PICOMSO_PROTOCOL_IO_BUFFER_SIZE, &actual_length) != 0) { + return PICOMSO_RESULT_ERR_IO; + } + + if (actual_length < PICOMSO_PACKET_HEADER_SIZE) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if (read_u16_le(response) != PICOMSO_PACKET_MAGIC) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if (response[2] != PICOMSO_PROTOCOL_VERSION_MAJOR) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if (response[5] != seq) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + *response_len = actual_length; + wire_length = read_u16_le(response + 6); + if ((size_t)PICOMSO_PACKET_HEADER_SIZE + wire_length > actual_length) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + if (response[4] == PICOMSO_MSG_ERROR) { + const uint8_t *payload_ptr = response + PICOMSO_PACKET_HEADER_SIZE; + uint8_t msg_len; + + if (wire_length < 2u) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + proto->last_device_status = payload_ptr[0]; + msg_len = payload_ptr[1]; + if ((uint16_t)(2u + msg_len) > wire_length) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + set_error_text(proto, (const char *)(payload_ptr + 2), msg_len); + return PICOMSO_RESULT_ERR_DEVICE; + } + + if (response[4] != expected_response_type) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + return PICOMSO_RESULT_OK; +} + +static picomso_result_t parse_ack_status(const uint8_t *response, size_t response_len) +{ + size_t payload_len; + const uint8_t *payload; + + if (response == NULL || response_len < PICOMSO_PACKET_HEADER_SIZE) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + payload_len = read_u16_le(response + 6); + payload = response + PICOMSO_PACKET_HEADER_SIZE; + if (payload_len == 0u) { + return PICOMSO_RESULT_OK; + } + if (payload_len < 1u || response_len < PICOMSO_PACKET_HEADER_SIZE + payload_len) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if (payload[0] != (uint8_t)PICOMSO_STATUS_OK) { + return PICOMSO_RESULT_ERR_DEVICE; + } + + return PICOMSO_RESULT_OK; +} + +void picomso_protocol_init(picomso_protocol_t *proto, const picomso_transport_t *transport) +{ + if (proto == NULL) { + return; + } + + memset(proto, 0, sizeof(*proto)); + if (transport != NULL) { + proto->transport = *transport; + } + proto->next_seq = 1u; + clear_error_state(proto); +} + +picomso_result_t picomso_protocol_get_info(picomso_protocol_t *proto, picomso_info_response_t *info) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len = 0u; + size_t payload_len; + const uint8_t *payload; + picomso_result_t result; + + if (info == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + result = send_request(proto, PICOMSO_MSG_GET_INFO, NULL, 0u, PICOMSO_RESPONSE_TYPE_INFO, response, &response_len); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + payload_len = read_u16_le(response + 6); + if (payload_len < sizeof(*info)) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + info->protocol_version_major = payload[0]; + info->protocol_version_minor = payload[1]; + memset(info->fw_id, 0, sizeof(info->fw_id)); + memcpy(info->fw_id, payload + 2, sizeof(info->fw_id)); + info->fw_id[sizeof(info->fw_id) - 1u] = '\0'; + + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_protocol_get_capabilities(picomso_protocol_t *proto, uint32_t *capabilities) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len = 0u; + const uint8_t *payload; + picomso_result_t result; + + if (capabilities == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + result = send_request(proto, + PICOMSO_MSG_GET_CAPABILITIES, + NULL, + 0u, + PICOMSO_RESPONSE_TYPE_CAPABILITIES, + response, + &response_len); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + if (read_u16_le(response + 6) < 4u) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + *capabilities = read_u32_le(payload); + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_protocol_get_status(picomso_protocol_t *proto, picomso_status_response_t *status) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len = 0u; + const uint8_t *payload; + picomso_result_t result; + + if (status == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + result = send_request(proto, PICOMSO_MSG_GET_STATUS, NULL, 0u, PICOMSO_RESPONSE_TYPE_STATUS, response, &response_len); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + if (read_u16_le(response + 6) < 2u) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + status->mode = payload[0]; + status->capture_state = payload[1]; + return PICOMSO_RESULT_OK; +} + +picomso_result_t picomso_protocol_set_mode(picomso_protocol_t *proto, picomso_device_mode_t mode) +{ + uint8_t payload[1]; + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len = 0u; + picomso_result_t result; + + payload[0] = (uint8_t)mode; + result = send_request(proto, PICOMSO_MSG_SET_MODE, payload, sizeof(payload), PICOMSO_RESPONSE_TYPE_SET_MODE, response, &response_len); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + return parse_ack_status(response, response_len); +} + +picomso_result_t picomso_protocol_request_capture(picomso_protocol_t *proto, + const picomso_request_capture_request_t *request) +{ + uint8_t payload[sizeof(*request)]; + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len = 0u; + unsigned int i; + picomso_result_t result; + + if (request == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + write_u32_le(payload, request->total_samples); + write_u32_le(payload + 4, request->rate); + write_u32_le(payload + 8, request->pre_trigger_samples); + for (i = 0u; i < PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT; ++i) { + size_t offset = 12u + (size_t)i * 3u; + payload[offset] = request->trigger[i].is_enabled; + payload[offset + 1u] = request->trigger[i].pin; + payload[offset + 2u] = request->trigger[i].match; + } + + result = send_request(proto, + PICOMSO_MSG_REQUEST_CAPTURE, + payload, + (uint16_t)sizeof(payload), + PICOMSO_RESPONSE_TYPE_REQUEST, + response, + &response_len); + if (result != PICOMSO_RESULT_OK) { + return result; + } + + return parse_ack_status(response, response_len); +} + +picomso_result_t picomso_protocol_read_data_block(picomso_protocol_t *proto, picomso_logic_block_t *block) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len = 0u; + uint16_t payload_len; + const uint8_t *payload; + picomso_result_t result; + + if (block == NULL) { + return PICOMSO_RESULT_ERR_ARGUMENT; + } + + memset(block, 0, sizeof(*block)); + result = send_request(proto, + PICOMSO_MSG_READ_DATA_BLOCK, + NULL, + 0u, + PICOMSO_RESPONSE_TYPE_DATA_BLOCK, + response, + &response_len); + if (result == PICOMSO_RESULT_ERR_DEVICE && + proto->last_device_status == (uint8_t)PICOMSO_STATUS_ERR_UNKNOWN && + strcmp(proto->last_error_text, "no finalized capture data") == 0) { + return PICOMSO_RESULT_DONE; + } + if (result != PICOMSO_RESULT_OK) { + return result; + } + + payload_len = read_u16_le(response + 6); + if (payload_len < 4u) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + block->block_id = read_u16_le(payload); + block->data_len = read_u16_le(payload + 2); + if (block->data_len > PICOMSO_DATA_BLOCK_SIZE) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + if ((uint16_t)(4u + block->data_len) > payload_len) { + return PICOMSO_RESULT_ERR_PROTOCOL; + } + + if (block->data_len > 0u) { + memcpy(block->data, payload + 4, block->data_len); + } + + (void)response_len; + return PICOMSO_RESULT_OK; +} diff --git a/src/hardware/rp2040/protocol.h b/src/hardware/rp2040/protocol.h new file mode 100644 index 000000000..b25097e1e --- /dev/null +++ b/src/hardware/rp2040/protocol.h @@ -0,0 +1,76 @@ +/* + * PicoMSO - libsigrok-style host protocol layer + * Copyright (C) 2026 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef PICOMSO_SIGROK_PROTOCOL_H +#define PICOMSO_SIGROK_PROTOCOL_H + +#include +#include +#include + +#include "../../../firmware/protocol/include/protocol.h" +#include "../../../firmware/protocol/include/protocol_packets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PICOMSO_DRIVER_CHANNEL_COUNT 16u +#define PICOMSO_PROTOCOL_IO_BUFFER_SIZE 256u +#define PICOMSO_PROTOCOL_ERROR_TEXT_MAX 64u + +typedef enum { + PICOMSO_RESULT_OK = 0, + PICOMSO_RESULT_DONE = 1, + PICOMSO_RESULT_ERR_ARGUMENT = -1, + PICOMSO_RESULT_ERR_IO = -2, + PICOMSO_RESULT_ERR_PROTOCOL = -3, + PICOMSO_RESULT_ERR_DEVICE = -4, + PICOMSO_RESULT_ERR_UNSUPPORTED = -5, + PICOMSO_RESULT_ERR_STATE = -6, + PICOMSO_RESULT_ERR_CALLBACK = -7, + PICOMSO_RESULT_ERR_TIMEOUT = -8, +} picomso_result_t; + +typedef struct { + int (*control_write)(void *user_data, const uint8_t *data, size_t length); + int (*bulk_read)(void *user_data, uint8_t *data, size_t capacity, size_t *actual_length); + int (*wait_ms)(void *user_data, unsigned int delay_ms); + void *user_data; +} picomso_transport_t; + +typedef struct { + picomso_transport_t transport; + uint8_t next_seq; + uint8_t last_device_status; + char last_error_text[PICOMSO_PROTOCOL_ERROR_TEXT_MAX]; +} picomso_protocol_t; + +typedef struct { + uint16_t block_id; + uint16_t data_len; + uint8_t data[PICOMSO_DATA_BLOCK_SIZE]; +} picomso_logic_block_t; + +void picomso_protocol_init(picomso_protocol_t *proto, const picomso_transport_t *transport); + +picomso_result_t picomso_protocol_get_info(picomso_protocol_t *proto, picomso_info_response_t *info); +picomso_result_t picomso_protocol_get_capabilities(picomso_protocol_t *proto, uint32_t *capabilities); +picomso_result_t picomso_protocol_get_status(picomso_protocol_t *proto, picomso_status_response_t *status); +picomso_result_t picomso_protocol_set_mode(picomso_protocol_t *proto, picomso_device_mode_t mode); +picomso_result_t picomso_protocol_request_capture(picomso_protocol_t *proto, + const picomso_request_capture_request_t *request); +picomso_result_t picomso_protocol_read_data_block(picomso_protocol_t *proto, picomso_logic_block_t *block); + +#ifdef __cplusplus +} +#endif + +#endif From c434e1c8145e8d3aebc01aea50fb538ad43381b5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 1 Apr 2026 09:22:45 -0400 Subject: [PATCH 02/10] add picomso driver --- Makefile.am | 6 + configure.ac | 1 + src/hardware/picomso/api.c | 425 +++++++++++++++++ src/hardware/picomso/protocol.c | 798 ++++++++++++++++++++++++++++++++ src/hardware/picomso/protocol.h | 181 ++++++++ src/hardware/rp2040/api.c | 278 ----------- src/hardware/rp2040/protocol.c | 383 --------------- src/hardware/rp2040/protocol.h | 76 --- 8 files changed, 1411 insertions(+), 737 deletions(-) create mode 100644 src/hardware/picomso/api.c create mode 100644 src/hardware/picomso/protocol.c create mode 100644 src/hardware/picomso/protocol.h delete mode 100644 src/hardware/rp2040/api.c delete mode 100644 src/hardware/rp2040/protocol.c delete mode 100644 src/hardware/rp2040/protocol.h diff --git a/Makefile.am b/Makefile.am index 3b68142f2..6ec71c2c5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -777,6 +777,12 @@ src_libdrivers_la_SOURCES += \ src/hardware/zketech-ebd-usb/protocol.c \ src/hardware/zketech-ebd-usb/api.c endif +if HW_PICOMSO +src_libdrivers_la_SOURCES += \ + src/hardware/picomso/protocol.h \ + src/hardware/picomso/protocol.c \ + src/hardware/picomso/api.c +endif libsigrok_la_LIBADD = src/libdrivers.lo $(SR_EXTRA_LIBS) $(LIBSIGROK_LIBS) libsigrok_la_LDFLAGS = -version-info $(SR_LIB_VERSION) -no-undefined diff --git a/configure.ac b/configure.ac index 024dd4da2..8a951c3d9 100644 --- a/configure.ac +++ b/configure.ac @@ -396,6 +396,7 @@ SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm]) SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm]) SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb]) SR_DRIVER([ZKETECH EBD-USB], [zketech-ebd-usb], [serial_comm]) +SR_DRIVER([PicoMSO], [picomso], [libusb]) ############################### ## Language bindings setup ## diff --git a/src/hardware/picomso/api.c b/src/hardware/picomso/api.c new file mode 100644 index 000000000..4651d493e --- /dev/null +++ b/src/hardware/picomso/api.c @@ -0,0 +1,425 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2026 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "protocol.h" + +static const struct picomso_profile supported_picomso[] = { +{ 0x04b5, 0x2041, "Raspberry Pi", "PicoMSO", NULL, +"Raspberry Pi", "PicoMSO" }, +ALL_ZERO +}; + +static const uint32_t scanopts[] = { +SR_CONF_CONN, +SR_CONF_PROBE_NAMES, +}; + +static const uint32_t drvopts[] = { +SR_CONF_LOGIC_ANALYZER, +}; + +static const uint32_t devopts[] = { +SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, +SR_CONF_CONN | SR_CONF_GET, +SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, +SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, +}; + +static const int32_t trigger_matches[] = { +SR_TRIGGER_ZERO, +SR_TRIGGER_ONE, +SR_TRIGGER_RISING, +SR_TRIGGER_FALLING, +}; + +static const uint64_t samplerates[] = { +SR_KHZ(5), +SR_KHZ(10), +SR_KHZ(20), +SR_KHZ(50), +SR_KHZ(100), +SR_KHZ(200), +SR_KHZ(500), +SR_MHZ(1), +SR_MHZ(2), +SR_MHZ(5), +SR_MHZ(10), +SR_MHZ(20), +SR_MHZ(50), +SR_MHZ(100), +}; + +static const char *channel_names_logic[] = { +"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", +"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", +}; + +static gboolean is_plausible(const struct libusb_device_descriptor *des) +{ +int i; + +for (i = 0; supported_picomso[i].vid; i++) { +if (des->idVendor != supported_picomso[i].vid) +continue; +if (des->idProduct == supported_picomso[i].pid) +return TRUE; +} + +return FALSE; +} + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ +struct drv_context *drvc; +struct dev_context *devc; +struct sr_dev_inst *sdi; +struct sr_usb_dev_inst *usb; +struct sr_channel *ch; +struct sr_channel_group *cg; +struct sr_config *src; +const struct picomso_profile *prof; +GSList *l, *devices, *conn_devices; +struct libusb_device_descriptor des; +libusb_device **devlist; +struct libusb_device_handle *hdl; +int ret, i; +size_t j, ch_max; +const char *conn; +const char *probe_names; +char manufacturer[64], product[64], serial_num[64], connection_id[64]; + +drvc = di->context; + +conn = NULL; +probe_names = NULL; +for (l = options; l; l = l->next) { +src = l->data; +switch (src->key) { +case SR_CONF_CONN: +conn = g_variant_get_string(src->data, NULL); +break; +case SR_CONF_PROBE_NAMES: +probe_names = g_variant_get_string(src->data, NULL); +break; +} +} +if (conn) +conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); +else +conn_devices = NULL; + +devices = NULL; +libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); +for (i = 0; devlist[i]; i++) { +if (conn) { +usb = NULL; +for (l = conn_devices; l; l = l->next) { +usb = l->data; +if (usb->bus == libusb_get_bus_number(devlist[i]) +&& usb->address == libusb_get_device_address(devlist[i])) +break; +} +if (!l) +continue; +} + +libusb_get_device_descriptor(devlist[i], &des); +if (!is_plausible(&des)) +continue; + +if ((ret = libusb_open(devlist[i], &hdl)) < 0) { +sr_warn("Failed to open potential device with VID:PID %04x:%04x: %s.", +des.idVendor, des.idProduct, libusb_error_name(ret)); +continue; +} + +if (des.iManufacturer == 0) { +manufacturer[0] = '\0'; +} else if ((ret = libusb_get_string_descriptor_ascii(hdl, +des.iManufacturer, (unsigned char *)manufacturer, +sizeof(manufacturer))) < 0) { +sr_warn("Failed to get manufacturer string descriptor: %s.", +libusb_error_name(ret)); +libusb_close(hdl); +continue; +} + +if (des.iProduct == 0) { +product[0] = '\0'; +} else if ((ret = libusb_get_string_descriptor_ascii(hdl, +des.iProduct, (unsigned char *)product, +sizeof(product))) < 0) { +sr_warn("Failed to get product string descriptor: %s.", +libusb_error_name(ret)); +libusb_close(hdl); +continue; +} + +if (des.iSerialNumber == 0) { +serial_num[0] = '\0'; +} else if ((ret = libusb_get_string_descriptor_ascii(hdl, +des.iSerialNumber, (unsigned char *)serial_num, +sizeof(serial_num))) < 0) { +sr_warn("Failed to get serial number string descriptor: %s.", +libusb_error_name(ret)); +libusb_close(hdl); +continue; +} + +libusb_close(hdl); + +if (usb_get_port_path(devlist[i], connection_id, +sizeof(connection_id)) < 0) +continue; + +prof = NULL; +for (j = 0; supported_picomso[j].vid; j++) { +if (des.idVendor == supported_picomso[j].vid +&& des.idProduct == supported_picomso[j].pid +&& (!supported_picomso[j].usb_manufacturer +|| !strcmp(manufacturer, +supported_picomso[j].usb_manufacturer)) +&& (!supported_picomso[j].usb_product +|| !strcmp(product, +supported_picomso[j].usb_product))) { +prof = &supported_picomso[j]; +break; +} +} +if (!prof) +continue; + +sdi = g_malloc0(sizeof(struct sr_dev_inst)); +sdi->status = SR_ST_INACTIVE; +sdi->vendor = g_strdup(prof->vendor); +sdi->model = g_strdup(prof->model); +sdi->version = g_strdup(prof->model_version); +sdi->serial_num = g_strdup(serial_num); +sdi->connection_id = g_strdup(connection_id); +sdi->inst_type = SR_INST_USB; +sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), +libusb_get_device_address(devlist[i]), NULL); + +devc = picomso_dev_new(); +devc->profile = prof; +devc->samplerates = samplerates; +devc->num_samplerates = ARRAY_SIZE(samplerates); +sdi->priv = devc; +devices = g_slist_append(devices, sdi); + +ch_max = ARRAY_SIZE(channel_names_logic); +devc->channel_names = sr_parse_probe_names(probe_names, +channel_names_logic, ch_max, ch_max, &ch_max); + +cg = sr_channel_group_new(sdi, "Logic", NULL); +for (j = 0; j < ch_max; j++) { +ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, +devc->channel_names[j]); +cg->channels = g_slist_append(cg->channels, ch); +} +} +libusb_free_device_list(devlist, 1); +g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); + +return std_scan_complete(di, devices); +} + +static void clear_helper(struct dev_context *devc) +{ +g_strfreev(devc->channel_names); +} + +static int dev_clear(const struct sr_dev_driver *di) +{ +return std_dev_clear_with_callback(di, +(std_dev_clear_callback)clear_helper); +} + +static int dev_open(struct sr_dev_inst *sdi) +{ +struct sr_dev_driver *di; +struct dev_context *devc; +int ret; + +di = sdi->driver; +devc = sdi->priv; + +ret = picomso_dev_open(sdi, di); +if (ret != SR_OK) { +sr_err("Unable to open device."); +return ret; +} + +if (devc->cur_samplerate == 0) +devc->cur_samplerate = devc->samplerates[0]; + +return SR_OK; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ +struct sr_usb_dev_inst *usb; + +usb = sdi->conn; +if (!usb->devhdl) +return SR_ERR_BUG; + +sr_info("Closing device on %d.%d (physical %s) interface %d.", +usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); +libusb_release_interface(usb->devhdl, USB_INTERFACE); +libusb_close(usb->devhdl); +usb->devhdl = NULL; + +return SR_OK; +} + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + + (void)cg; + + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_CONN: + if (!sdi->conn) + return SR_ERR_ARG; + usb = sdi->conn; + *data = g_variant_new_printf("%d.%d", usb->bus, usb->address); + break; + case SR_CONF_LIMIT_SAMPLES: + *data = g_variant_new_uint64(devc->limit_samples); + break; + case SR_CONF_SAMPLERATE: + *data = g_variant_new_uint64(devc->cur_samplerate); + break; + case SR_CONF_CAPTURE_RATIO: + *data = g_variant_new_uint64(devc->capture_ratio); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_set(uint32_t key, GVariant *data, +const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ +struct dev_context *devc; +uint64_t value; +int idx; + +(void)cg; + +if (!sdi) +return SR_ERR_ARG; + +devc = sdi->priv; + +switch (key) { +case SR_CONF_SAMPLERATE: +if ((idx = std_u64_idx(data, devc->samplerates, +devc->num_samplerates)) < 0) +return SR_ERR_ARG; +devc->cur_samplerate = devc->samplerates[idx]; +break; +case SR_CONF_LIMIT_SAMPLES: +value = g_variant_get_uint64(data); +if (value == 0 || value > PICOMSO_MAX_TOTAL_SAMPLES) +return SR_ERR_ARG; +devc->limit_samples = value; +break; +case SR_CONF_CAPTURE_RATIO: +value = g_variant_get_uint64(data); +if (value > 100) +return SR_ERR_ARG; +devc->capture_ratio = value; +break; +default: +return SR_ERR_NA; +} + +return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, +const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ +struct dev_context *devc; + +devc = sdi ? sdi->priv : NULL; + +switch (key) { +case SR_CONF_SCAN_OPTIONS: +case SR_CONF_DEVICE_OPTIONS: +if (cg) +return SR_ERR_NA; +return STD_CONFIG_LIST(key, data, sdi, cg, +scanopts, drvopts, devopts); +case SR_CONF_SAMPLERATE: +if (!devc) +return SR_ERR_NA; +*data = std_gvar_samplerates(devc->samplerates, +devc->num_samplerates); +break; +case SR_CONF_TRIGGER_MATCH: +*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); +break; +default: +return SR_ERR_NA; +} + +return SR_OK; +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ +picomso_abort_acquisition(sdi->priv); + +return SR_OK; +} + +static struct sr_dev_driver picomso_driver_info = { +.name = "picomso", +.longname = "PicoMSO mixed-signal logic analyzer", +.api_version = 1, +.init = std_init, +.cleanup = std_cleanup, +.scan = scan, +.dev_list = std_dev_list, +.dev_clear = dev_clear, +.config_get = config_get, +.config_set = config_set, +.config_list = config_list, +.dev_open = dev_open, +.dev_close = dev_close, +.dev_acquisition_start = picomso_start_acquisition, +.dev_acquisition_stop = dev_acquisition_stop, +.context = NULL, +}; +SR_REGISTER_DEV_DRIVER(picomso_driver_info); diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c new file mode 100644 index 000000000..dcd6d7240 --- /dev/null +++ b/src/hardware/picomso/protocol.c @@ -0,0 +1,798 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2026 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "protocol.h" + +#define PICOMSO_RESPONSE_TYPE_INFO PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_CAPABILITIES PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_STATUS PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_SET_MODE PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_REQUEST PICOMSO_MSG_ACK +#define PICOMSO_RESPONSE_TYPE_DATA_BLOCK PICOMSO_MSG_DATA_BLOCK + +#define PICOMSO_READ_DONE 1 + +static uint16_t read_u16_le(const uint8_t *data) +{ + return (uint16_t)data[0] | ((uint16_t)data[1] << 8); +} + +static uint32_t read_u32_le(const uint8_t *data) +{ + return (uint32_t)data[0] + | ((uint32_t)data[1] << 8) + | ((uint32_t)data[2] << 16) + | ((uint32_t)data[3] << 24); +} + +static void write_u16_le(uint8_t *data, uint16_t value) +{ + data[0] = (uint8_t)(value & 0xffu); + data[1] = (uint8_t)((value >> 8) & 0xffu); +} + +static void write_u32_le(uint8_t *data, uint32_t value) +{ + data[0] = (uint8_t)(value & 0xffu); + data[1] = (uint8_t)((value >> 8) & 0xffu); + data[2] = (uint8_t)((value >> 16) & 0xffu); + data[3] = (uint8_t)((value >> 24) & 0xffu); +} + +static void clear_error_state(struct dev_context *devc) +{ + devc->last_device_status = (uint8_t)PICOMSO_STATUS_OK; + devc->last_error_text[0] = '\0'; +} + +static void set_error_text(struct dev_context *devc, + const char *text, size_t length) +{ + size_t copy_len; + + copy_len = length; + if (copy_len >= sizeof(devc->last_error_text)) + copy_len = sizeof(devc->last_error_text) - 1u; + + if (copy_len > 0u && text) + memcpy(devc->last_error_text, text, copy_len); + devc->last_error_text[copy_len] = '\0'; +} + +static int send_request(const struct sr_dev_inst *sdi, + uint8_t msg_type, const uint8_t *payload, uint16_t payload_len, + uint8_t expected_response_type, uint8_t *response, + size_t *response_len) +{ + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + uint8_t request[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + uint8_t seq; + uint16_t wire_length; + int actual_length, ret; + const uint8_t *payload_ptr; + uint8_t msg_len; + + devc = sdi->priv; + usb = sdi->conn; + + if (!response || !response_len || !usb || !usb->devhdl) + return SR_ERR_ARG; + + if ((size_t)PICOMSO_PACKET_HEADER_SIZE + payload_len > sizeof(request)) + return SR_ERR_ARG; + + clear_error_state(devc); + seq = devc->next_seq++; + + write_u16_le(request, PICOMSO_PACKET_MAGIC); + request[2] = PICOMSO_PROTOCOL_VERSION_MAJOR; + request[3] = PICOMSO_PROTOCOL_VERSION_MINOR; + request[4] = msg_type; + request[5] = seq; + write_u16_le(request + 6, payload_len); + + if (payload_len > 0u && payload) + memcpy(request + PICOMSO_PACKET_HEADER_SIZE, payload, payload_len); + + wire_length = (uint16_t)(PICOMSO_PACKET_HEADER_SIZE + payload_len); + + ret = libusb_control_transfer(usb->devhdl, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, + PICOMSO_CTRL_REQUEST_OUT, 0x0000, 0x0000, + request, wire_length, PICOMSO_USB_TIMEOUT_MS); + if (ret < 0 || ret != wire_length) { + sr_err("Unable to send request 0x%02x: %s.", msg_type, + ret < 0 ? libusb_error_name(ret) : "short control write"); + return SR_ERR; + } + + ret = libusb_bulk_transfer(usb->devhdl, PICOMSO_BULK_EP_IN, response, + PICOMSO_PROTOCOL_IO_BUFFER_SIZE, &actual_length, + PICOMSO_USB_TIMEOUT_MS); + if (ret < 0) { + sr_err("Unable to read response for request 0x%02x: %s.", msg_type, + libusb_error_name(ret)); + return SR_ERR; + } + + if ((size_t)actual_length < PICOMSO_PACKET_HEADER_SIZE) + return SR_ERR; + if (read_u16_le(response) != PICOMSO_PACKET_MAGIC) + return SR_ERR; + if (response[2] != PICOMSO_PROTOCOL_VERSION_MAJOR) + return SR_ERR; + if (response[5] != seq) + return SR_ERR; + + *response_len = (size_t)actual_length; + wire_length = read_u16_le(response + 6); + + if ((size_t)PICOMSO_PACKET_HEADER_SIZE + wire_length > (size_t)actual_length) + return SR_ERR; + + if (response[4] == PICOMSO_MSG_ERROR) { + payload_ptr = response + PICOMSO_PACKET_HEADER_SIZE; + + if (wire_length < 2u) + return SR_ERR; + + devc->last_device_status = payload_ptr[0]; + msg_len = payload_ptr[1]; + + if ((uint16_t)(2u + msg_len) > wire_length) + return SR_ERR; + + set_error_text(devc, (const char *)(payload_ptr + 2), msg_len); + return SR_ERR; + } + + if (response[4] != expected_response_type) + return SR_ERR; + + return SR_OK; +} + +static int parse_ack_status(const uint8_t *response, size_t response_len) +{ + size_t payload_len; + const uint8_t *payload; + + if (!response || response_len < PICOMSO_PACKET_HEADER_SIZE) + return SR_ERR; + + payload_len = read_u16_le(response + 6); + payload = response + PICOMSO_PACKET_HEADER_SIZE; + + if (payload_len == 0u) + return SR_OK; + if (payload_len < 1u) + return SR_ERR; + if (response_len < PICOMSO_PACKET_HEADER_SIZE + payload_len) + return SR_ERR; + if (payload[0] != (uint8_t)PICOMSO_STATUS_OK) + return SR_ERR; + + return SR_OK; +} + +static int command_get_info(const struct sr_dev_inst *sdi, + struct picomso_info *info) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len; + size_t payload_len; + const uint8_t *payload; + int ret; + + if (!info) + return SR_ERR_ARG; + + ret = send_request(sdi, PICOMSO_MSG_GET_INFO, NULL, 0u, + PICOMSO_RESPONSE_TYPE_INFO, response, &response_len); + if (ret != SR_OK) + return ret; + + payload_len = read_u16_le(response + 6); + if (payload_len < sizeof(*info)) + return SR_ERR; + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + info->protocol_version_major = payload[0]; + info->protocol_version_minor = payload[1]; + memset(info->fw_id, 0, sizeof(info->fw_id)); + memcpy(info->fw_id, payload + 2, sizeof(info->fw_id)); + info->fw_id[sizeof(info->fw_id) - 1u] = '\0'; + + return SR_OK; +} + +static int command_get_capabilities(const struct sr_dev_inst *sdi, + uint32_t *capabilities) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len; + const uint8_t *payload; + int ret; + + if (!capabilities) + return SR_ERR_ARG; + + ret = send_request(sdi, PICOMSO_MSG_GET_CAPABILITIES, NULL, 0u, + PICOMSO_RESPONSE_TYPE_CAPABILITIES, response, &response_len); + if (ret != SR_OK) + return ret; + + if (read_u16_le(response + 6) < 4u) + return SR_ERR; + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + *capabilities = read_u32_le(payload); + + return SR_OK; +} + +static int command_get_status(const struct sr_dev_inst *sdi, + struct picomso_status *status) +{ + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len; + const uint8_t *payload; + int ret; + + if (!status) + return SR_ERR_ARG; + + ret = send_request(sdi, PICOMSO_MSG_GET_STATUS, NULL, 0u, + PICOMSO_RESPONSE_TYPE_STATUS, response, &response_len); + if (ret != SR_OK) + return ret; + + if (read_u16_le(response + 6) < 2u) + return SR_ERR; + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + status->mode = payload[0]; + status->capture_state = payload[1]; + + return SR_OK; +} + +static int command_set_mode(const struct sr_dev_inst *sdi, + enum picomso_device_mode mode) +{ + uint8_t payload[1]; + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len; + int ret; + + payload[0] = (uint8_t)mode; + + ret = send_request(sdi, PICOMSO_MSG_SET_MODE, payload, sizeof(payload), + PICOMSO_RESPONSE_TYPE_SET_MODE, response, &response_len); + if (ret != SR_OK) + return ret; + + return parse_ack_status(response, response_len); +} + +static int command_request_capture(const struct sr_dev_inst *sdi, + const struct picomso_request_capture *request) +{ + uint8_t payload[12u + PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT * 3u]; + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len; + size_t offset; + unsigned int i; + int ret; + + if (!request) + return SR_ERR_ARG; + + write_u32_le(payload, request->total_samples); + write_u32_le(payload + 4, request->rate); + write_u32_le(payload + 8, request->pre_trigger_samples); + + for (i = 0; i < PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT; i++) { + offset = 12u + (size_t)i * 3u; + payload[offset] = request->trigger[i].is_enabled; + payload[offset + 1u] = request->trigger[i].pin; + payload[offset + 2u] = request->trigger[i].match; + } + + ret = send_request(sdi, PICOMSO_MSG_REQUEST_CAPTURE, payload, + (uint16_t)sizeof(payload), PICOMSO_RESPONSE_TYPE_REQUEST, + response, &response_len); + if (ret != SR_OK) + return ret; + + return parse_ack_status(response, response_len); +} + +static int command_read_data_block(const struct sr_dev_inst *sdi, + struct picomso_data_block *block) +{ + struct dev_context *devc; + uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; + size_t response_len; + uint16_t payload_len; + const uint8_t *payload; + int ret; + + devc = sdi->priv; + + if (!block) + return SR_ERR_ARG; + + memset(block, 0, sizeof(*block)); + + ret = send_request(sdi, PICOMSO_MSG_READ_DATA_BLOCK, NULL, 0u, + PICOMSO_RESPONSE_TYPE_DATA_BLOCK, response, &response_len); + if (ret != SR_OK + && devc->last_device_status == (uint8_t)PICOMSO_STATUS_ERR_UNKNOWN + && !strcmp(devc->last_error_text, "no finalized capture data")) + return PICOMSO_READ_DONE; + if (ret != SR_OK) + return ret; + + payload_len = read_u16_le(response + 6); + if (payload_len < 4u) + return SR_ERR; + + payload = response + PICOMSO_PACKET_HEADER_SIZE; + block->block_id = read_u16_le(payload); + block->data_len = read_u16_le(payload + 2); + + if (block->data_len > PICOMSO_DATA_BLOCK_SIZE) + return SR_ERR; + if ((uint16_t)(4u + block->data_len) > payload_len) + return SR_ERR; + + if (block->data_len > 0u) + memcpy(block->data, payload + 4, block->data_len); + + return SR_OK; +} + +static int trigger_match_to_picomso(enum sr_trigger_matches match, + uint8_t *picomso_match) +{ + switch (match) { + case SR_TRIGGER_ZERO: + *picomso_match = PICOMSO_TRIGGER_MATCH_LEVEL_LOW; + break; + case SR_TRIGGER_ONE: + *picomso_match = PICOMSO_TRIGGER_MATCH_LEVEL_HIGH; + break; + case SR_TRIGGER_RISING: + *picomso_match = PICOMSO_TRIGGER_MATCH_EDGE_HIGH; + break; + case SR_TRIGGER_FALLING: + *picomso_match = PICOMSO_TRIGGER_MATCH_EDGE_LOW; + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int build_capture_request(const struct sr_dev_inst *sdi, + struct picomso_request_capture *request) +{ + struct dev_context *devc; + struct sr_trigger *trigger; + struct sr_trigger_stage *stage; + struct sr_trigger_match *match; + const GSList *l; + uint64_t total_samples; + uint64_t pre_trigger_samples; + unsigned int trigger_index; + int ret; + + devc = sdi->priv; + memset(request, 0, sizeof(*request)); + + total_samples = devc->limit_samples ? + devc->limit_samples : PICOMSO_DEFAULT_LIMIT_SAMPLES; + + if (total_samples == 0 || total_samples > PICOMSO_MAX_TOTAL_SAMPLES) + return SR_ERR_ARG; + if (devc->cur_samplerate == 0) + return SR_ERR_ARG; + if (devc->capture_ratio > 100) + return SR_ERR_ARG; + + pre_trigger_samples = (devc->capture_ratio * total_samples) / 100; + if (pre_trigger_samples > PICOMSO_MAX_PRE_TRIGGER_SAMPLES) + return SR_ERR_ARG; + if (total_samples - pre_trigger_samples > PICOMSO_MAX_POST_TRIGGER_SAMPLES) + return SR_ERR_ARG; + + request->total_samples = (uint32_t)total_samples; + request->rate = (uint32_t)devc->cur_samplerate; + request->pre_trigger_samples = (uint32_t)pre_trigger_samples; + + trigger = sr_session_trigger_get(sdi->session); + if (!trigger) + return SR_OK; + if (g_slist_length(trigger->stages) > 1) + return SR_ERR_NA; + + stage = g_slist_nth_data(trigger->stages, 0); + if (!stage) + return SR_ERR_ARG; + + trigger_index = 0; + for (l = stage->matches; l; l = l->next) { + match = l->data; + + if (!match->match || !match->channel || !match->channel->enabled) + continue; + if (match->channel->index >= NUM_CHANNELS) + return SR_ERR_ARG; + if (trigger_index >= PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT) + return SR_ERR_NA; + + ret = trigger_match_to_picomso(match->match, + &request->trigger[trigger_index].match); + if (ret != SR_OK) + return ret; + + request->trigger[trigger_index].is_enabled = 1u; + request->trigger[trigger_index].pin = match->channel->index; + trigger_index++; + } + + return SR_OK; +} + +static int send_logic_data(struct sr_dev_inst *sdi, + const struct picomso_data_block *block) +{ + struct dev_context *devc; + struct sr_datafeed_logic logic; + struct sr_datafeed_packet packet; + size_t sample_count; + + devc = sdi->priv; + + if ((block->data_len % sizeof(uint16_t)) != 0u) + return SR_ERR; + + sample_count = block->data_len / sizeof(uint16_t); + + if (devc->limit_samples && + devc->sent_samples + sample_count > devc->limit_samples) { + sample_count = (size_t)(devc->limit_samples - devc->sent_samples); + } + + if (sample_count == 0u) + return SR_OK; + + logic.length = sample_count * sizeof(uint16_t); + logic.unitsize = sizeof(uint16_t); + logic.data = (uint8_t *)block->data; + + packet.type = SR_DF_LOGIC; + packet.payload = &logic; + + devc->sent_samples += sample_count; + + return sr_session_send(sdi, &packet); +} + +static void finish_acquisition(struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + + devc = sdi->priv; + if (devc->acq_state == PICOMSO_ACQ_IDLE) + return; + + if (command_set_mode(sdi, PICOMSO_MODE_UNSET) != SR_OK) + sr_dbg("Unable to switch device back to unset mode after acquisition."); + + sr_session_source_remove(sdi->session, -1); + std_session_send_df_end(sdi); + + devc->acq_aborted = FALSE; + devc->acq_state = PICOMSO_ACQ_IDLE; + devc->expected_block_id = 0; + devc->capture_deadline_us = 0; +} + +static int receive_data(int fd, int revents, void *cb_data) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct picomso_status status; + struct picomso_data_block block; + int ret; + + (void)fd; + (void)revents; + + sdi = cb_data; + devc = sdi->priv; + + if (devc->acq_aborted) { + finish_acquisition(sdi); + return FALSE; + } + + if (devc->acq_state == PICOMSO_ACQ_WAITING) { + ret = command_get_status(sdi, &status); + if (ret != SR_OK) { + sr_err("Failed to query PicoMSO capture status."); + finish_acquisition(sdi); + return FALSE; + } + + if (status.mode != PICOMSO_MODE_LOGIC) { + sr_err("Device left logic mode while capture was running."); + finish_acquisition(sdi); + return FALSE; + } + + if (status.capture_state == PICOMSO_CAPTURE_RUNNING) { + if (devc->capture_deadline_us > 0 && + g_get_monotonic_time() > devc->capture_deadline_us) { + sr_err("Timed out waiting for PicoMSO capture completion."); + finish_acquisition(sdi); + return FALSE; + } + return TRUE; + } + + if (status.capture_state != PICOMSO_CAPTURE_IDLE) { + sr_err("Unexpected PicoMSO capture state 0x%02x.", + status.capture_state); + finish_acquisition(sdi); + return FALSE; + } + + devc->acq_state = PICOMSO_ACQ_READING; + } + + while (!devc->acq_aborted) { + ret = command_read_data_block(sdi, &block); + if (ret == PICOMSO_READ_DONE) { + finish_acquisition(sdi); + return FALSE; + } + if (ret != SR_OK) { + sr_err("Failed to read finalized PicoMSO capture data."); + finish_acquisition(sdi); + return FALSE; + } + if (block.block_id != devc->expected_block_id) { + sr_err("Unexpected PicoMSO block id %u, expected %u.", + block.block_id, devc->expected_block_id); + finish_acquisition(sdi); + return FALSE; + } + ret = send_logic_data(sdi, &block); + if (ret != SR_OK) { + sr_err("Failed to forward PicoMSO logic data."); + finish_acquisition(sdi); + return FALSE; + } + devc->expected_block_id++; + } + + finish_acquisition(sdi); + return FALSE; +} + +SR_PRIV int picomso_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di) +{ + libusb_device **devlist; + struct sr_usb_dev_inst *usb; + struct libusb_device_descriptor des; + struct dev_context *devc; + struct drv_context *drvc; + int ret, i, device_count; + char connection_id[64]; + + drvc = di->context; + devc = sdi->priv; + usb = sdi->conn; + + device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + if (device_count < 0) { + sr_err("Failed to get device list: %s.", + libusb_error_name(device_count)); + return SR_ERR; + } + + ret = SR_ERR; + for (i = 0; i < device_count; i++) { + libusb_get_device_descriptor(devlist[i], &des); + + if (des.idVendor != devc->profile->vid || + des.idProduct != devc->profile->pid) + continue; + + if (usb_get_port_path(devlist[i], connection_id, + sizeof(connection_id)) < 0) + continue; + + if (strcmp(sdi->connection_id, connection_id)) + continue; + + ret = libusb_open(devlist[i], &usb->devhdl); + if (ret < 0) { + sr_err("Failed to open device: %s.", libusb_error_name(ret)); + ret = SR_ERR; + break; + } + + if (libusb_has_capability(LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER) && + libusb_kernel_driver_active(usb->devhdl, USB_INTERFACE) == 1) { + ret = libusb_detach_kernel_driver(usb->devhdl, USB_INTERFACE); + if (ret < 0) { + sr_err("Failed to detach kernel driver: %s.", + libusb_error_name(ret)); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + ret = SR_ERR; + break; + } + } + + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != 0) { + sr_err("Unable to claim interface: %s.", + libusb_error_name(ret)); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + ret = SR_ERR; + break; + } + + ret = command_get_info(sdi, &devc->info); + if (ret != SR_OK) { + sr_err("Failed to query PicoMSO device information."); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + break; + } + + ret = command_get_capabilities(sdi, &devc->capabilities); + if (ret != SR_OK) { + sr_err("Failed to query PicoMSO device capabilities."); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + break; + } + + if ((devc->capabilities & PICOMSO_CAP_LOGIC) == 0u) { + sr_err("Connected PicoMSO device does not expose logic capability."); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + ret = SR_ERR; + break; + } + + sr_info("Opened PicoMSO device on %d.%d / %s, firmware %u.%u (%s).", + usb->bus, usb->address, connection_id, + devc->info.protocol_version_major, + devc->info.protocol_version_minor, + devc->info.fw_id); + ret = SR_OK; + break; + } + + libusb_free_device_list(devlist, 1); + + return ret; +} + +SR_PRIV struct dev_context *picomso_dev_new(void) +{ + struct dev_context *devc; + + devc = g_malloc0(sizeof(struct dev_context)); + devc->profile = NULL; + devc->channel_names = NULL; + devc->cur_samplerate = 0; + devc->limit_samples = PICOMSO_DEFAULT_LIMIT_SAMPLES; + devc->capture_ratio = 0; + devc->sent_samples = 0; + devc->next_seq = 1u; + devc->last_device_status = PICOMSO_STATUS_OK; + devc->acq_state = PICOMSO_ACQ_IDLE; + devc->expected_block_id = 0; + devc->capture_deadline_us = 0; + clear_error_state(devc); + + return devc; +} + +SR_PRIV void picomso_abort_acquisition(struct dev_context *devc) +{ + devc->acq_aborted = TRUE; +} + +SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct picomso_request_capture request; + struct picomso_status status; + gint64 capture_time_us; + int ret; + + devc = sdi->priv; + + if (devc->acq_state != PICOMSO_ACQ_IDLE) + return SR_ERR; + + ret = build_capture_request(sdi, &request); + if (ret != SR_OK) + return ret; + + ret = command_set_mode(sdi, PICOMSO_MODE_LOGIC); + if (ret != SR_OK) + return ret; + + ret = command_get_status(sdi, &status); + if (ret != SR_OK) + return ret; + + if (status.mode != PICOMSO_MODE_LOGIC || + status.capture_state == PICOMSO_CAPTURE_RUNNING) + return SR_ERR; + + ret = command_request_capture(sdi, &request); + if (ret != SR_OK) + return ret; + + devc->acq_aborted = FALSE; + devc->acq_state = PICOMSO_ACQ_WAITING; + devc->expected_block_id = 0; + devc->sent_samples = 0; + + capture_time_us = ((gint64)request.total_samples * G_USEC_PER_SEC) + / request.rate; + devc->capture_deadline_us = g_get_monotonic_time() + + capture_time_us + (3 * G_USEC_PER_SEC); + + ret = sr_session_source_add(sdi->session, -1, 0, + PICOMSO_POLL_INTERVAL_MS, receive_data, (void *)sdi); + if (ret != SR_OK) { + devc->acq_state = PICOMSO_ACQ_IDLE; + command_set_mode(sdi, PICOMSO_MODE_UNSET); + return ret; + } + + ret = std_session_send_df_header(sdi); + if (ret != SR_OK) { + sr_session_source_remove(sdi->session, -1); + devc->acq_state = PICOMSO_ACQ_IDLE; + command_set_mode(sdi, PICOMSO_MODE_UNSET); + } + + return ret; +} \ No newline at end of file diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h new file mode 100644 index 000000000..6d478a778 --- /dev/null +++ b/src/hardware/picomso/protocol.h @@ -0,0 +1,181 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2026 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_PICOMSO_PROTOCOL_H +#define LIBSIGROK_HARDWARE_PICOMSO_PROTOCOL_H + +#include +#include +#include +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "picomso" + +#define USB_INTERFACE 0 +#define USB_CONFIGURATION 1 +#define NUM_CHANNELS 16 + +#define PICOMSO_USB_TIMEOUT_MS 500 +#define PICOMSO_POLL_INTERVAL_MS 10 +#define PICOMSO_PROTOCOL_IO_BUFFER_SIZE 256u +#define PICOMSO_PROTOCOL_ERROR_TEXT_MAX 64u + +#define PICOMSO_BULK_EP_IN 0x86 +#define PICOMSO_CTRL_REQUEST_OUT 0x01 + +#define PICOMSO_PROTOCOL_VERSION_MAJOR 0 +#define PICOMSO_PROTOCOL_VERSION_MINOR 3 + +#define PICOMSO_PACKET_MAGIC UINT16_C(0x4D53) +#define PICOMSO_PACKET_HEADER_SIZE 8u + +#define PICOMSO_DEFAULT_LIMIT_SAMPLES 1024u +#define PICOMSO_MAX_PRE_TRIGGER_SAMPLES 1024u +#define PICOMSO_MAX_POST_TRIGGER_SAMPLES 10000u +#define PICOMSO_MAX_TOTAL_SAMPLES \ + (PICOMSO_MAX_PRE_TRIGGER_SAMPLES + PICOMSO_MAX_POST_TRIGGER_SAMPLES) + +#define PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT 4u +#define PICOMSO_DATA_BLOCK_SIZE 64u + +#define PICOMSO_CAP_LOGIC UINT32_C(1 << 0) +#define PICOMSO_CAP_SCOPE UINT32_C(1 << 1) + +enum picomso_msg_type { + PICOMSO_MSG_GET_INFO = 0x01, + PICOMSO_MSG_GET_CAPABILITIES = 0x02, + PICOMSO_MSG_GET_STATUS = 0x03, + PICOMSO_MSG_SET_MODE = 0x04, + PICOMSO_MSG_REQUEST_CAPTURE = 0x05, + PICOMSO_MSG_READ_DATA_BLOCK = 0x06, + + PICOMSO_MSG_ACK = 0x80, + PICOMSO_MSG_ERROR = 0x81, + PICOMSO_MSG_DATA_BLOCK = 0x82, +}; + +enum picomso_status_code { + PICOMSO_STATUS_OK = 0x00, + PICOMSO_STATUS_ERR_UNKNOWN = 0x01, + PICOMSO_STATUS_ERR_BAD_MAGIC = 0x02, + PICOMSO_STATUS_ERR_BAD_LEN = 0x03, + PICOMSO_STATUS_ERR_BAD_MODE = 0x04, + PICOMSO_STATUS_ERR_VERSION = 0x05, +}; + +enum picomso_device_mode { + PICOMSO_MODE_UNSET = 0x00, + PICOMSO_MODE_LOGIC = 0x01, + PICOMSO_MODE_OSCILLOSCOPE = 0x02, +}; + +enum picomso_capture_state { + PICOMSO_CAPTURE_IDLE = 0x00, + PICOMSO_CAPTURE_RUNNING = 0x01, +}; + +enum picomso_trigger_match { + PICOMSO_TRIGGER_MATCH_LEVEL_LOW = 0x00, + PICOMSO_TRIGGER_MATCH_LEVEL_HIGH = 0x01, + PICOMSO_TRIGGER_MATCH_EDGE_LOW = 0x02, + PICOMSO_TRIGGER_MATCH_EDGE_HIGH = 0x03, +}; + +enum picomso_acq_state { + PICOMSO_ACQ_IDLE, + PICOMSO_ACQ_WAITING, + PICOMSO_ACQ_READING, +}; + +struct picomso_profile { + uint16_t vid; + uint16_t pid; + + const char *vendor; + const char *model; + const char *model_version; + + const char *usb_manufacturer; + const char *usb_product; +}; + +struct picomso_trigger_config { + uint8_t is_enabled; + uint8_t pin; + uint8_t match; +}; + +struct picomso_request_capture { + uint32_t total_samples; + uint32_t rate; + uint32_t pre_trigger_samples; + struct picomso_trigger_config trigger[PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT]; +}; + +struct picomso_info { + uint8_t protocol_version_major; + uint8_t protocol_version_minor; + char fw_id[32]; +}; + +struct picomso_status { + uint8_t mode; + uint8_t capture_state; +}; + +struct picomso_data_block { + uint16_t block_id; + uint16_t data_len; + uint8_t data[PICOMSO_DATA_BLOCK_SIZE]; +}; + +struct dev_context { + const struct picomso_profile *profile; + char **channel_names; + + const uint64_t *samplerates; + int num_samplerates; + + uint64_t cur_samplerate; + uint64_t limit_samples; + uint64_t capture_ratio; + uint64_t sent_samples; + + uint32_t capabilities; + struct picomso_info info; + + uint8_t next_seq; + uint8_t last_device_status; + char last_error_text[PICOMSO_PROTOCOL_ERROR_TEXT_MAX]; + + gboolean acq_aborted; + enum picomso_acq_state acq_state; + uint16_t expected_block_id; + gint64 capture_deadline_us; +}; + +SR_PRIV int picomso_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di); +SR_PRIV struct dev_context *picomso_dev_new(void); +SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi); +SR_PRIV void picomso_abort_acquisition(struct dev_context *devc); + +#endif \ No newline at end of file diff --git a/src/hardware/rp2040/api.c b/src/hardware/rp2040/api.c deleted file mode 100644 index 67596cd12..000000000 --- a/src/hardware/rp2040/api.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * PicoMSO - libsigrok-style host driver API layer - * Copyright (C) 2026 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - */ - -#include "protocol.h" - -#include -#include - -typedef int (*picomso_logic_samples_cb)(void *user_data, const uint16_t *samples, size_t sample_count); - -typedef struct { - picomso_protocol_t protocol; - picomso_info_response_t info; - uint32_t capabilities; - unsigned int channel_count; - bool is_open; - bool logic_mode_active; - bool capture_running; -} picomso_driver_t; - -static const char *const logic_channel_names[PICOMSO_DRIVER_CHANNEL_COUNT] = { - "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", - "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", -}; - -static bool request_is_valid(const picomso_request_capture_request_t *request) -{ - unsigned int i; - - if (request == NULL) { - return false; - } - if (request->total_samples == 0u || request->pre_trigger_samples > request->total_samples) { - return false; - } - - for (i = 0u; i < PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT; ++i) { - if (request->trigger[i].is_enabled > 1u) { - return false; - } - if (request->trigger[i].pin >= PICOMSO_DRIVER_CHANNEL_COUNT) { - return false; - } - switch ((picomso_trigger_match_t)request->trigger[i].match) { - case PICOMSO_TRIGGER_MATCH_LEVEL_LOW: - case PICOMSO_TRIGGER_MATCH_LEVEL_HIGH: - case PICOMSO_TRIGGER_MATCH_EDGE_LOW: - case PICOMSO_TRIGGER_MATCH_EDGE_HIGH: - break; - default: - return false; - } - } - - return true; -} - -void picomso_driver_init(picomso_driver_t *driver, const picomso_transport_t *transport) -{ - if (driver == NULL) { - return; - } - - memset(driver, 0, sizeof(*driver)); - picomso_protocol_init(&driver->protocol, transport); - driver->channel_count = PICOMSO_DRIVER_CHANNEL_COUNT; -} - -picomso_result_t picomso_driver_open(picomso_driver_t *driver) -{ - picomso_result_t result; - - if (driver == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - result = picomso_protocol_get_info(&driver->protocol, &driver->info); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - result = picomso_protocol_get_capabilities(&driver->protocol, &driver->capabilities); - if (result != PICOMSO_RESULT_OK) { - return result; - } - if ((driver->capabilities & PICOMSO_CAP_LOGIC) == 0u) { - return PICOMSO_RESULT_ERR_UNSUPPORTED; - } - - driver->is_open = true; - driver->logic_mode_active = false; - driver->capture_running = false; - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_driver_stop(picomso_driver_t *driver) -{ - picomso_result_t result; - - if (driver == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if (!driver->is_open) { - return PICOMSO_RESULT_OK; - } - - result = picomso_protocol_set_mode(&driver->protocol, PICOMSO_MODE_UNSET); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - driver->logic_mode_active = false; - driver->capture_running = false; - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_driver_close(picomso_driver_t *driver) -{ - picomso_result_t result; - - if (driver == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if (!driver->is_open) { - return PICOMSO_RESULT_OK; - } - - result = picomso_driver_stop(driver); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - driver->is_open = false; - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_driver_start_logic_capture(picomso_driver_t *driver, - const picomso_request_capture_request_t *request) -{ - picomso_status_response_t status; - picomso_result_t result; - - if (driver == NULL || !request_is_valid(request)) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if (!driver->is_open) { - return PICOMSO_RESULT_ERR_STATE; - } - - result = picomso_protocol_set_mode(&driver->protocol, PICOMSO_MODE_LOGIC); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - result = picomso_protocol_get_status(&driver->protocol, &status); - if (result != PICOMSO_RESULT_OK) { - return result; - } - if (status.mode != PICOMSO_MODE_LOGIC || status.capture_state == PICOMSO_CAPTURE_RUNNING) { - return PICOMSO_RESULT_ERR_STATE; - } - - result = picomso_protocol_request_capture(&driver->protocol, request); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - driver->logic_mode_active = true; - driver->capture_running = true; - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_driver_wait_capture_complete(picomso_driver_t *driver, - unsigned int max_polls, - unsigned int poll_interval_ms) -{ - picomso_status_response_t status; - picomso_result_t result; - unsigned int poll; - - if (driver == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if (!driver->is_open || !driver->logic_mode_active || !driver->capture_running) { - return PICOMSO_RESULT_ERR_STATE; - } - - for (poll = 0u; poll < max_polls; ++poll) { - result = picomso_protocol_get_status(&driver->protocol, &status); - if (result != PICOMSO_RESULT_OK) { - return result; - } - if (status.mode != PICOMSO_MODE_LOGIC) { - return PICOMSO_RESULT_ERR_STATE; - } - if (status.capture_state == PICOMSO_CAPTURE_IDLE) { - driver->capture_running = false; - return PICOMSO_RESULT_OK; - } - if (status.capture_state != PICOMSO_CAPTURE_RUNNING) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if (poll_interval_ms > 0u && driver->protocol.transport.wait_ms != NULL && poll + 1u < max_polls) { - if (driver->protocol.transport.wait_ms(driver->protocol.transport.user_data, poll_interval_ms) != 0) { - return PICOMSO_RESULT_ERR_IO; - } - } - } - - return PICOMSO_RESULT_ERR_TIMEOUT; -} - -picomso_result_t picomso_driver_read_logic_capture(picomso_driver_t *driver, - picomso_logic_samples_cb callback, - void *user_data, - size_t *captured_samples) -{ - picomso_logic_block_t block; - uint16_t samples[PICOMSO_DATA_BLOCK_SIZE / sizeof(uint16_t)]; - uint16_t expected_block_id = 0u; - size_t total_samples = 0u; - size_t sample_count; - size_t i; - picomso_result_t result; - - if (driver == NULL || callback == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if (!driver->is_open || !driver->logic_mode_active || driver->capture_running) { - return PICOMSO_RESULT_ERR_STATE; - } - - for (;;) { - result = picomso_protocol_read_data_block(&driver->protocol, &block); - if (result == PICOMSO_RESULT_DONE) { - if (captured_samples != NULL) { - *captured_samples = total_samples; - } - return PICOMSO_RESULT_OK; - } - if (result != PICOMSO_RESULT_OK) { - return result; - } - if (block.block_id != expected_block_id) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if ((block.data_len % sizeof(uint16_t)) != 0u) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - sample_count = block.data_len / sizeof(uint16_t); - for (i = 0u; i < sample_count; ++i) { - samples[i] = (uint16_t)block.data[i * 2u] | ((uint16_t)block.data[i * 2u + 1u] << 8); - } - if (sample_count > 0u && callback(user_data, samples, sample_count) != 0) { - return PICOMSO_RESULT_ERR_CALLBACK; - } - - total_samples += sample_count; - ++expected_block_id; - } -} - -const char *picomso_driver_logic_channel_name(unsigned int index) -{ - if (index >= PICOMSO_DRIVER_CHANNEL_COUNT) { - return NULL; - } - - return logic_channel_names[index]; -} diff --git a/src/hardware/rp2040/protocol.c b/src/hardware/rp2040/protocol.c deleted file mode 100644 index e744964d1..000000000 --- a/src/hardware/rp2040/protocol.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * PicoMSO - libsigrok-style host protocol layer - * Copyright (C) 2026 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - */ - -#include "protocol.h" - -#include - -#define PICOMSO_RESPONSE_TYPE_INFO PICOMSO_MSG_ACK -#define PICOMSO_RESPONSE_TYPE_CAPABILITIES PICOMSO_MSG_ACK -#define PICOMSO_RESPONSE_TYPE_STATUS PICOMSO_MSG_ACK -#define PICOMSO_RESPONSE_TYPE_SET_MODE PICOMSO_MSG_ACK -#define PICOMSO_RESPONSE_TYPE_REQUEST PICOMSO_MSG_ACK -#define PICOMSO_RESPONSE_TYPE_DATA_BLOCK PICOMSO_MSG_DATA_BLOCK - -static uint16_t read_u16_le(const uint8_t *data) -{ - return (uint16_t)data[0] | ((uint16_t)data[1] << 8); -} - -static uint32_t read_u32_le(const uint8_t *data) -{ - return (uint32_t)data[0] | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); -} - -static void write_u16_le(uint8_t *data, uint16_t value) -{ - data[0] = (uint8_t)(value & 0xffu); - data[1] = (uint8_t)((value >> 8) & 0xffu); -} - -static void write_u32_le(uint8_t *data, uint32_t value) -{ - data[0] = (uint8_t)(value & 0xffu); - data[1] = (uint8_t)((value >> 8) & 0xffu); - data[2] = (uint8_t)((value >> 16) & 0xffu); - data[3] = (uint8_t)((value >> 24) & 0xffu); -} - -static void clear_error_state(picomso_protocol_t *proto) -{ - proto->last_device_status = (uint8_t)PICOMSO_STATUS_OK; - proto->last_error_text[0] = '\0'; -} - -static void set_error_text(picomso_protocol_t *proto, const char *text, size_t length) -{ - size_t copy_len = length; - - if (copy_len >= sizeof(proto->last_error_text)) { - copy_len = sizeof(proto->last_error_text) - 1u; - } - - if (copy_len > 0u && text != NULL) { - memcpy(proto->last_error_text, text, copy_len); - } - proto->last_error_text[copy_len] = '\0'; -} - -static picomso_result_t send_request(picomso_protocol_t *proto, - uint8_t msg_type, - const uint8_t *payload, - uint16_t payload_len, - uint8_t expected_response_type, - uint8_t *response, - size_t *response_len) -{ - uint8_t request[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t actual_length = 0u; - uint8_t seq; - uint16_t wire_length; - - if (proto == NULL || response == NULL || response_len == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if (proto->transport.control_write == NULL || proto->transport.bulk_read == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - if ((size_t)PICOMSO_PACKET_HEADER_SIZE + payload_len > sizeof(request)) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - clear_error_state(proto); - seq = proto->next_seq++; - - write_u16_le(request, PICOMSO_PACKET_MAGIC); - request[2] = PICOMSO_PROTOCOL_VERSION_MAJOR; - request[3] = PICOMSO_PROTOCOL_VERSION_MINOR; - request[4] = msg_type; - request[5] = seq; - write_u16_le(request + 6, payload_len); - if (payload_len > 0u && payload != NULL) { - memcpy(request + PICOMSO_PACKET_HEADER_SIZE, payload, payload_len); - } - - wire_length = (uint16_t)(PICOMSO_PACKET_HEADER_SIZE + payload_len); - if (proto->transport.control_write(proto->transport.user_data, request, wire_length) != 0) { - return PICOMSO_RESULT_ERR_IO; - } - if (proto->transport.bulk_read(proto->transport.user_data, response, PICOMSO_PROTOCOL_IO_BUFFER_SIZE, &actual_length) != 0) { - return PICOMSO_RESULT_ERR_IO; - } - - if (actual_length < PICOMSO_PACKET_HEADER_SIZE) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if (read_u16_le(response) != PICOMSO_PACKET_MAGIC) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if (response[2] != PICOMSO_PROTOCOL_VERSION_MAJOR) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if (response[5] != seq) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - *response_len = actual_length; - wire_length = read_u16_le(response + 6); - if ((size_t)PICOMSO_PACKET_HEADER_SIZE + wire_length > actual_length) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - if (response[4] == PICOMSO_MSG_ERROR) { - const uint8_t *payload_ptr = response + PICOMSO_PACKET_HEADER_SIZE; - uint8_t msg_len; - - if (wire_length < 2u) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - proto->last_device_status = payload_ptr[0]; - msg_len = payload_ptr[1]; - if ((uint16_t)(2u + msg_len) > wire_length) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - set_error_text(proto, (const char *)(payload_ptr + 2), msg_len); - return PICOMSO_RESULT_ERR_DEVICE; - } - - if (response[4] != expected_response_type) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - return PICOMSO_RESULT_OK; -} - -static picomso_result_t parse_ack_status(const uint8_t *response, size_t response_len) -{ - size_t payload_len; - const uint8_t *payload; - - if (response == NULL || response_len < PICOMSO_PACKET_HEADER_SIZE) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - payload_len = read_u16_le(response + 6); - payload = response + PICOMSO_PACKET_HEADER_SIZE; - if (payload_len == 0u) { - return PICOMSO_RESULT_OK; - } - if (payload_len < 1u || response_len < PICOMSO_PACKET_HEADER_SIZE + payload_len) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if (payload[0] != (uint8_t)PICOMSO_STATUS_OK) { - return PICOMSO_RESULT_ERR_DEVICE; - } - - return PICOMSO_RESULT_OK; -} - -void picomso_protocol_init(picomso_protocol_t *proto, const picomso_transport_t *transport) -{ - if (proto == NULL) { - return; - } - - memset(proto, 0, sizeof(*proto)); - if (transport != NULL) { - proto->transport = *transport; - } - proto->next_seq = 1u; - clear_error_state(proto); -} - -picomso_result_t picomso_protocol_get_info(picomso_protocol_t *proto, picomso_info_response_t *info) -{ - uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t response_len = 0u; - size_t payload_len; - const uint8_t *payload; - picomso_result_t result; - - if (info == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - result = send_request(proto, PICOMSO_MSG_GET_INFO, NULL, 0u, PICOMSO_RESPONSE_TYPE_INFO, response, &response_len); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - payload_len = read_u16_le(response + 6); - if (payload_len < sizeof(*info)) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - payload = response + PICOMSO_PACKET_HEADER_SIZE; - info->protocol_version_major = payload[0]; - info->protocol_version_minor = payload[1]; - memset(info->fw_id, 0, sizeof(info->fw_id)); - memcpy(info->fw_id, payload + 2, sizeof(info->fw_id)); - info->fw_id[sizeof(info->fw_id) - 1u] = '\0'; - - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_protocol_get_capabilities(picomso_protocol_t *proto, uint32_t *capabilities) -{ - uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t response_len = 0u; - const uint8_t *payload; - picomso_result_t result; - - if (capabilities == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - result = send_request(proto, - PICOMSO_MSG_GET_CAPABILITIES, - NULL, - 0u, - PICOMSO_RESPONSE_TYPE_CAPABILITIES, - response, - &response_len); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - if (read_u16_le(response + 6) < 4u) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - payload = response + PICOMSO_PACKET_HEADER_SIZE; - *capabilities = read_u32_le(payload); - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_protocol_get_status(picomso_protocol_t *proto, picomso_status_response_t *status) -{ - uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t response_len = 0u; - const uint8_t *payload; - picomso_result_t result; - - if (status == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - result = send_request(proto, PICOMSO_MSG_GET_STATUS, NULL, 0u, PICOMSO_RESPONSE_TYPE_STATUS, response, &response_len); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - if (read_u16_le(response + 6) < 2u) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - payload = response + PICOMSO_PACKET_HEADER_SIZE; - status->mode = payload[0]; - status->capture_state = payload[1]; - return PICOMSO_RESULT_OK; -} - -picomso_result_t picomso_protocol_set_mode(picomso_protocol_t *proto, picomso_device_mode_t mode) -{ - uint8_t payload[1]; - uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t response_len = 0u; - picomso_result_t result; - - payload[0] = (uint8_t)mode; - result = send_request(proto, PICOMSO_MSG_SET_MODE, payload, sizeof(payload), PICOMSO_RESPONSE_TYPE_SET_MODE, response, &response_len); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - return parse_ack_status(response, response_len); -} - -picomso_result_t picomso_protocol_request_capture(picomso_protocol_t *proto, - const picomso_request_capture_request_t *request) -{ - uint8_t payload[sizeof(*request)]; - uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t response_len = 0u; - unsigned int i; - picomso_result_t result; - - if (request == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - write_u32_le(payload, request->total_samples); - write_u32_le(payload + 4, request->rate); - write_u32_le(payload + 8, request->pre_trigger_samples); - for (i = 0u; i < PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT; ++i) { - size_t offset = 12u + (size_t)i * 3u; - payload[offset] = request->trigger[i].is_enabled; - payload[offset + 1u] = request->trigger[i].pin; - payload[offset + 2u] = request->trigger[i].match; - } - - result = send_request(proto, - PICOMSO_MSG_REQUEST_CAPTURE, - payload, - (uint16_t)sizeof(payload), - PICOMSO_RESPONSE_TYPE_REQUEST, - response, - &response_len); - if (result != PICOMSO_RESULT_OK) { - return result; - } - - return parse_ack_status(response, response_len); -} - -picomso_result_t picomso_protocol_read_data_block(picomso_protocol_t *proto, picomso_logic_block_t *block) -{ - uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; - size_t response_len = 0u; - uint16_t payload_len; - const uint8_t *payload; - picomso_result_t result; - - if (block == NULL) { - return PICOMSO_RESULT_ERR_ARGUMENT; - } - - memset(block, 0, sizeof(*block)); - result = send_request(proto, - PICOMSO_MSG_READ_DATA_BLOCK, - NULL, - 0u, - PICOMSO_RESPONSE_TYPE_DATA_BLOCK, - response, - &response_len); - if (result == PICOMSO_RESULT_ERR_DEVICE && - proto->last_device_status == (uint8_t)PICOMSO_STATUS_ERR_UNKNOWN && - strcmp(proto->last_error_text, "no finalized capture data") == 0) { - return PICOMSO_RESULT_DONE; - } - if (result != PICOMSO_RESULT_OK) { - return result; - } - - payload_len = read_u16_le(response + 6); - if (payload_len < 4u) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - payload = response + PICOMSO_PACKET_HEADER_SIZE; - block->block_id = read_u16_le(payload); - block->data_len = read_u16_le(payload + 2); - if (block->data_len > PICOMSO_DATA_BLOCK_SIZE) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - if ((uint16_t)(4u + block->data_len) > payload_len) { - return PICOMSO_RESULT_ERR_PROTOCOL; - } - - if (block->data_len > 0u) { - memcpy(block->data, payload + 4, block->data_len); - } - - (void)response_len; - return PICOMSO_RESULT_OK; -} diff --git a/src/hardware/rp2040/protocol.h b/src/hardware/rp2040/protocol.h deleted file mode 100644 index b25097e1e..000000000 --- a/src/hardware/rp2040/protocol.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * PicoMSO - libsigrok-style host protocol layer - * Copyright (C) 2026 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - */ - -#ifndef PICOMSO_SIGROK_PROTOCOL_H -#define PICOMSO_SIGROK_PROTOCOL_H - -#include -#include -#include - -#include "../../../firmware/protocol/include/protocol.h" -#include "../../../firmware/protocol/include/protocol_packets.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define PICOMSO_DRIVER_CHANNEL_COUNT 16u -#define PICOMSO_PROTOCOL_IO_BUFFER_SIZE 256u -#define PICOMSO_PROTOCOL_ERROR_TEXT_MAX 64u - -typedef enum { - PICOMSO_RESULT_OK = 0, - PICOMSO_RESULT_DONE = 1, - PICOMSO_RESULT_ERR_ARGUMENT = -1, - PICOMSO_RESULT_ERR_IO = -2, - PICOMSO_RESULT_ERR_PROTOCOL = -3, - PICOMSO_RESULT_ERR_DEVICE = -4, - PICOMSO_RESULT_ERR_UNSUPPORTED = -5, - PICOMSO_RESULT_ERR_STATE = -6, - PICOMSO_RESULT_ERR_CALLBACK = -7, - PICOMSO_RESULT_ERR_TIMEOUT = -8, -} picomso_result_t; - -typedef struct { - int (*control_write)(void *user_data, const uint8_t *data, size_t length); - int (*bulk_read)(void *user_data, uint8_t *data, size_t capacity, size_t *actual_length); - int (*wait_ms)(void *user_data, unsigned int delay_ms); - void *user_data; -} picomso_transport_t; - -typedef struct { - picomso_transport_t transport; - uint8_t next_seq; - uint8_t last_device_status; - char last_error_text[PICOMSO_PROTOCOL_ERROR_TEXT_MAX]; -} picomso_protocol_t; - -typedef struct { - uint16_t block_id; - uint16_t data_len; - uint8_t data[PICOMSO_DATA_BLOCK_SIZE]; -} picomso_logic_block_t; - -void picomso_protocol_init(picomso_protocol_t *proto, const picomso_transport_t *transport); - -picomso_result_t picomso_protocol_get_info(picomso_protocol_t *proto, picomso_info_response_t *info); -picomso_result_t picomso_protocol_get_capabilities(picomso_protocol_t *proto, uint32_t *capabilities); -picomso_result_t picomso_protocol_get_status(picomso_protocol_t *proto, picomso_status_response_t *status); -picomso_result_t picomso_protocol_set_mode(picomso_protocol_t *proto, picomso_device_mode_t mode); -picomso_result_t picomso_protocol_request_capture(picomso_protocol_t *proto, - const picomso_request_capture_request_t *request); -picomso_result_t picomso_protocol_read_data_block(picomso_protocol_t *proto, picomso_logic_block_t *block); - -#ifdef __cplusplus -} -#endif - -#endif From 74c28d2040bb105b2fbf311454a153589504c6d9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 1 Apr 2026 11:29:46 -0400 Subject: [PATCH 03/10] add analog support --- src/hardware/picomso/api.c | 611 +++++++++++++++++--------------- src/hardware/picomso/protocol.c | 160 ++++++++- src/hardware/picomso/protocol.h | 6 + 3 files changed, 475 insertions(+), 302 deletions(-) diff --git a/src/hardware/picomso/api.c b/src/hardware/picomso/api.c index 4651d493e..f0b1ca989 100644 --- a/src/hardware/picomso/api.c +++ b/src/hardware/picomso/api.c @@ -21,274 +21,298 @@ #include "protocol.h" static const struct picomso_profile supported_picomso[] = { -{ 0x04b5, 0x2041, "Raspberry Pi", "PicoMSO", NULL, -"Raspberry Pi", "PicoMSO" }, -ALL_ZERO + { 0x04b5, 0x2041, "Raspberry Pi", "PicoMSO", NULL, + "Raspberry Pi", "PicoMSO" }, + ALL_ZERO }; static const uint32_t scanopts[] = { -SR_CONF_CONN, -SR_CONF_PROBE_NAMES, + SR_CONF_CONN, + SR_CONF_PROBE_NAMES, }; static const uint32_t drvopts[] = { -SR_CONF_LOGIC_ANALYZER, + SR_CONF_LOGIC_ANALYZER, + SR_CONF_OSCILLOSCOPE, }; static const uint32_t devopts[] = { -SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, -SR_CONF_CONN | SR_CONF_GET, -SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, -SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, -SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_CONN | SR_CONF_GET, + SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, + SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, + SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, }; static const int32_t trigger_matches[] = { -SR_TRIGGER_ZERO, -SR_TRIGGER_ONE, -SR_TRIGGER_RISING, -SR_TRIGGER_FALLING, + SR_TRIGGER_ZERO, + SR_TRIGGER_ONE, + SR_TRIGGER_RISING, + SR_TRIGGER_FALLING, }; static const uint64_t samplerates[] = { -SR_KHZ(5), -SR_KHZ(10), -SR_KHZ(20), -SR_KHZ(50), -SR_KHZ(100), -SR_KHZ(200), -SR_KHZ(500), -SR_MHZ(1), -SR_MHZ(2), -SR_MHZ(5), -SR_MHZ(10), -SR_MHZ(20), -SR_MHZ(50), -SR_MHZ(100), + SR_KHZ(5), + SR_KHZ(10), + SR_KHZ(20), + SR_KHZ(50), + SR_KHZ(100), + SR_KHZ(200), + SR_KHZ(500), + SR_MHZ(1), + SR_MHZ(2), + SR_MHZ(5), + SR_MHZ(10), + SR_MHZ(20), + SR_MHZ(50), + SR_MHZ(100), }; static const char *channel_names_logic[] = { -"D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", -"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", +}; + +static const char *channel_names_analog[] = { + "A0", }; static gboolean is_plausible(const struct libusb_device_descriptor *des) { -int i; + int i; -for (i = 0; supported_picomso[i].vid; i++) { -if (des->idVendor != supported_picomso[i].vid) -continue; -if (des->idProduct == supported_picomso[i].pid) -return TRUE; -} + for (i = 0; supported_picomso[i].vid; i++) { + if (des->idVendor != supported_picomso[i].vid) + continue; + if (des->idProduct == supported_picomso[i].pid) + return TRUE; + } -return FALSE; + return FALSE; } static GSList *scan(struct sr_dev_driver *di, GSList *options) { -struct drv_context *drvc; -struct dev_context *devc; -struct sr_dev_inst *sdi; -struct sr_usb_dev_inst *usb; -struct sr_channel *ch; -struct sr_channel_group *cg; -struct sr_config *src; -const struct picomso_profile *prof; -GSList *l, *devices, *conn_devices; -struct libusb_device_descriptor des; -libusb_device **devlist; -struct libusb_device_handle *hdl; -int ret, i; -size_t j, ch_max; -const char *conn; -const char *probe_names; -char manufacturer[64], product[64], serial_num[64], connection_id[64]; - -drvc = di->context; - -conn = NULL; -probe_names = NULL; -for (l = options; l; l = l->next) { -src = l->data; -switch (src->key) { -case SR_CONF_CONN: -conn = g_variant_get_string(src->data, NULL); -break; -case SR_CONF_PROBE_NAMES: -probe_names = g_variant_get_string(src->data, NULL); -break; -} -} -if (conn) -conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); -else -conn_devices = NULL; - -devices = NULL; -libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); -for (i = 0; devlist[i]; i++) { -if (conn) { -usb = NULL; -for (l = conn_devices; l; l = l->next) { -usb = l->data; -if (usb->bus == libusb_get_bus_number(devlist[i]) -&& usb->address == libusb_get_device_address(devlist[i])) -break; -} -if (!l) -continue; -} - -libusb_get_device_descriptor(devlist[i], &des); -if (!is_plausible(&des)) -continue; - -if ((ret = libusb_open(devlist[i], &hdl)) < 0) { -sr_warn("Failed to open potential device with VID:PID %04x:%04x: %s.", -des.idVendor, des.idProduct, libusb_error_name(ret)); -continue; -} - -if (des.iManufacturer == 0) { -manufacturer[0] = '\0'; -} else if ((ret = libusb_get_string_descriptor_ascii(hdl, -des.iManufacturer, (unsigned char *)manufacturer, -sizeof(manufacturer))) < 0) { -sr_warn("Failed to get manufacturer string descriptor: %s.", -libusb_error_name(ret)); -libusb_close(hdl); -continue; -} - -if (des.iProduct == 0) { -product[0] = '\0'; -} else if ((ret = libusb_get_string_descriptor_ascii(hdl, -des.iProduct, (unsigned char *)product, -sizeof(product))) < 0) { -sr_warn("Failed to get product string descriptor: %s.", -libusb_error_name(ret)); -libusb_close(hdl); -continue; -} + struct drv_context *drvc; + struct dev_context *devc; + struct sr_dev_inst *sdi; + struct sr_usb_dev_inst *usb; + struct sr_channel *ch; + struct sr_channel_group *cg; + struct sr_config *src; + const struct picomso_profile *prof; + GSList *l, *devices, *conn_devices; + struct libusb_device_descriptor des; + libusb_device **devlist; + struct libusb_device_handle *hdl; + int ret, i; + size_t j, num_logic_channels, num_analog_channels; + const char *conn; + const char *probe_names; + char manufacturer[64], product[64], serial_num[64], connection_id[64]; + size_t ch_max, ch_idx; + const char *channel_name; + + drvc = di->context; + + conn = NULL; + probe_names = NULL; + for (l = options; l; l = l->next) { + src = l->data; + switch (src->key) { + case SR_CONF_CONN: + conn = g_variant_get_string(src->data, NULL); + break; + case SR_CONF_PROBE_NAMES: + probe_names = g_variant_get_string(src->data, NULL); + break; + } + } -if (des.iSerialNumber == 0) { -serial_num[0] = '\0'; -} else if ((ret = libusb_get_string_descriptor_ascii(hdl, -des.iSerialNumber, (unsigned char *)serial_num, -sizeof(serial_num))) < 0) { -sr_warn("Failed to get serial number string descriptor: %s.", -libusb_error_name(ret)); -libusb_close(hdl); -continue; -} + if (conn) + conn_devices = sr_usb_find(drvc->sr_ctx->libusb_ctx, conn); + else + conn_devices = NULL; + + devices = NULL; + libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist); + for (i = 0; devlist[i]; i++) { + if (conn) { + usb = NULL; + for (l = conn_devices; l; l = l->next) { + usb = l->data; + if (usb->bus == libusb_get_bus_number(devlist[i]) && + usb->address == libusb_get_device_address(devlist[i])) + break; + } + if (!l) + continue; + } + + libusb_get_device_descriptor(devlist[i], &des); + if (!is_plausible(&des)) + continue; + + if ((ret = libusb_open(devlist[i], &hdl)) < 0) { + sr_warn("Failed to open potential device with VID:PID %04x:%04x: %s.", + des.idVendor, des.idProduct, libusb_error_name(ret)); + continue; + } + + if (des.iManufacturer == 0) { + manufacturer[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iManufacturer, (unsigned char *)manufacturer, + sizeof(manufacturer))) < 0) { + sr_warn("Failed to get manufacturer string descriptor: %s.", + libusb_error_name(ret)); + libusb_close(hdl); + continue; + } + + if (des.iProduct == 0) { + product[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iProduct, (unsigned char *)product, + sizeof(product))) < 0) { + sr_warn("Failed to get product string descriptor: %s.", + libusb_error_name(ret)); + libusb_close(hdl); + continue; + } + + if (des.iSerialNumber == 0) { + serial_num[0] = '\0'; + } else if ((ret = libusb_get_string_descriptor_ascii(hdl, + des.iSerialNumber, (unsigned char *)serial_num, + sizeof(serial_num))) < 0) { + sr_warn("Failed to get serial number string descriptor: %s.", + libusb_error_name(ret)); + libusb_close(hdl); + continue; + } + + libusb_close(hdl); + + if (usb_get_port_path(devlist[i], connection_id, + sizeof(connection_id)) < 0) + continue; + + prof = NULL; + for (j = 0; supported_picomso[j].vid; j++) { + if (des.idVendor == supported_picomso[j].vid && + des.idProduct == supported_picomso[j].pid && + (!supported_picomso[j].usb_manufacturer || + !strcmp(manufacturer, supported_picomso[j].usb_manufacturer)) && + (!supported_picomso[j].usb_product || + !strcmp(product, supported_picomso[j].usb_product))) { + prof = &supported_picomso[j]; + break; + } + } + if (!prof) + continue; + + sdi = g_malloc0(sizeof(struct sr_dev_inst)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup(prof->vendor); + sdi->model = g_strdup(prof->model); + sdi->version = g_strdup(prof->model_version); + sdi->serial_num = g_strdup(serial_num); + sdi->connection_id = g_strdup(connection_id); + sdi->inst_type = SR_INST_USB; + sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), + libusb_get_device_address(devlist[i]), NULL); + + devc = picomso_dev_new(); + devc->profile = prof; + devc->samplerates = samplerates; + devc->num_samplerates = ARRAY_SIZE(samplerates); + sdi->priv = devc; + devices = g_slist_append(devices, sdi); + + num_logic_channels = ARRAY_SIZE(channel_names_logic); + num_analog_channels = ARRAY_SIZE(channel_names_analog); + + ch_max = num_logic_channels; + devc->channel_names = sr_parse_probe_names(probe_names, + channel_names_logic, ch_max, ch_max, &ch_max); + + ch_idx = 0; + + cg = sr_channel_group_new(sdi, "Logic", NULL); + for (j = 0; j < num_logic_channels; j++) { + channel_name = devc->channel_names[j]; + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_LOGIC, + TRUE, channel_name); + cg->channels = g_slist_append(cg->channels, ch); + } + + for (j = 0; j < num_analog_channels; j++) { + channel_name = channel_names_analog[j]; + ch = sr_channel_new(sdi, ch_idx++, SR_CHANNEL_ANALOG, + FALSE, channel_name); + + cg = sr_channel_group_new(sdi, channel_name, NULL); + cg->channels = g_slist_append(NULL, ch); + } + } -libusb_close(hdl); - -if (usb_get_port_path(devlist[i], connection_id, -sizeof(connection_id)) < 0) -continue; - -prof = NULL; -for (j = 0; supported_picomso[j].vid; j++) { -if (des.idVendor == supported_picomso[j].vid -&& des.idProduct == supported_picomso[j].pid -&& (!supported_picomso[j].usb_manufacturer -|| !strcmp(manufacturer, -supported_picomso[j].usb_manufacturer)) -&& (!supported_picomso[j].usb_product -|| !strcmp(product, -supported_picomso[j].usb_product))) { -prof = &supported_picomso[j]; -break; -} -} -if (!prof) -continue; - -sdi = g_malloc0(sizeof(struct sr_dev_inst)); -sdi->status = SR_ST_INACTIVE; -sdi->vendor = g_strdup(prof->vendor); -sdi->model = g_strdup(prof->model); -sdi->version = g_strdup(prof->model_version); -sdi->serial_num = g_strdup(serial_num); -sdi->connection_id = g_strdup(connection_id); -sdi->inst_type = SR_INST_USB; -sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), -libusb_get_device_address(devlist[i]), NULL); - -devc = picomso_dev_new(); -devc->profile = prof; -devc->samplerates = samplerates; -devc->num_samplerates = ARRAY_SIZE(samplerates); -sdi->priv = devc; -devices = g_slist_append(devices, sdi); - -ch_max = ARRAY_SIZE(channel_names_logic); -devc->channel_names = sr_parse_probe_names(probe_names, -channel_names_logic, ch_max, ch_max, &ch_max); - -cg = sr_channel_group_new(sdi, "Logic", NULL); -for (j = 0; j < ch_max; j++) { -ch = sr_channel_new(sdi, j, SR_CHANNEL_LOGIC, TRUE, -devc->channel_names[j]); -cg->channels = g_slist_append(cg->channels, ch); -} -} -libusb_free_device_list(devlist, 1); -g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); + libusb_free_device_list(devlist, 1); + g_slist_free_full(conn_devices, (GDestroyNotify)sr_usb_dev_inst_free); -return std_scan_complete(di, devices); + return std_scan_complete(di, devices); } static void clear_helper(struct dev_context *devc) { -g_strfreev(devc->channel_names); + g_strfreev(devc->channel_names); + g_slist_free(devc->enabled_analog_channels); + devc->enabled_analog_channels = NULL; } static int dev_clear(const struct sr_dev_driver *di) { -return std_dev_clear_with_callback(di, -(std_dev_clear_callback)clear_helper); + return std_dev_clear_with_callback(di, + (std_dev_clear_callback)clear_helper); } static int dev_open(struct sr_dev_inst *sdi) { -struct sr_dev_driver *di; -struct dev_context *devc; -int ret; + struct sr_dev_driver *di; + struct dev_context *devc; + int ret; -di = sdi->driver; -devc = sdi->priv; + di = sdi->driver; + devc = sdi->priv; -ret = picomso_dev_open(sdi, di); -if (ret != SR_OK) { -sr_err("Unable to open device."); -return ret; -} + ret = picomso_dev_open(sdi, di); + if (ret != SR_OK) { + sr_err("Unable to open device."); + return ret; + } -if (devc->cur_samplerate == 0) -devc->cur_samplerate = devc->samplerates[0]; + if (devc->cur_samplerate == 0) + devc->cur_samplerate = devc->samplerates[0]; -return SR_OK; + return SR_OK; } static int dev_close(struct sr_dev_inst *sdi) { -struct sr_usb_dev_inst *usb; + struct sr_usb_dev_inst *usb; -usb = sdi->conn; -if (!usb->devhdl) -return SR_ERR_BUG; + usb = sdi->conn; + if (!usb->devhdl) + return SR_ERR_BUG; -sr_info("Closing device on %d.%d (physical %s) interface %d.", -usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); -libusb_release_interface(usb->devhdl, USB_INTERFACE); -libusb_close(usb->devhdl); -usb->devhdl = NULL; + sr_info("Closing device on %d.%d (physical %s) interface %d.", + usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; -return SR_OK; + return SR_OK; } static int config_get(uint32_t key, GVariant **data, @@ -328,98 +352,99 @@ static int config_get(uint32_t key, GVariant **data, } static int config_set(uint32_t key, GVariant *data, -const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { -struct dev_context *devc; -uint64_t value; -int idx; - -(void)cg; - -if (!sdi) -return SR_ERR_ARG; - -devc = sdi->priv; - -switch (key) { -case SR_CONF_SAMPLERATE: -if ((idx = std_u64_idx(data, devc->samplerates, -devc->num_samplerates)) < 0) -return SR_ERR_ARG; -devc->cur_samplerate = devc->samplerates[idx]; -break; -case SR_CONF_LIMIT_SAMPLES: -value = g_variant_get_uint64(data); -if (value == 0 || value > PICOMSO_MAX_TOTAL_SAMPLES) -return SR_ERR_ARG; -devc->limit_samples = value; -break; -case SR_CONF_CAPTURE_RATIO: -value = g_variant_get_uint64(data); -if (value > 100) -return SR_ERR_ARG; -devc->capture_ratio = value; -break; -default: -return SR_ERR_NA; -} + struct dev_context *devc; + uint64_t value; + int idx; + + (void)cg; -return SR_OK; + if (!sdi) + return SR_ERR_ARG; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_SAMPLERATE: + if ((idx = std_u64_idx(data, devc->samplerates, + devc->num_samplerates)) < 0) + return SR_ERR_ARG; + devc->cur_samplerate = devc->samplerates[idx]; + break; + case SR_CONF_LIMIT_SAMPLES: + value = g_variant_get_uint64(data); + if (value == 0 || value > PICOMSO_MAX_TOTAL_SAMPLES) + return SR_ERR_ARG; + devc->limit_samples = value; + break; + case SR_CONF_CAPTURE_RATIO: + value = g_variant_get_uint64(data); + if (value > 100) + return SR_ERR_ARG; + devc->capture_ratio = value; + break; + default: + return SR_ERR_NA; + } + + return SR_OK; } static int config_list(uint32_t key, GVariant **data, -const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { -struct dev_context *devc; - -devc = sdi ? sdi->priv : NULL; - -switch (key) { -case SR_CONF_SCAN_OPTIONS: -case SR_CONF_DEVICE_OPTIONS: -if (cg) -return SR_ERR_NA; -return STD_CONFIG_LIST(key, data, sdi, cg, -scanopts, drvopts, devopts); -case SR_CONF_SAMPLERATE: -if (!devc) -return SR_ERR_NA; -*data = std_gvar_samplerates(devc->samplerates, -devc->num_samplerates); -break; -case SR_CONF_TRIGGER_MATCH: -*data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); -break; -default: -return SR_ERR_NA; -} + struct dev_context *devc; -return SR_OK; + devc = sdi ? sdi->priv : NULL; + + switch (key) { + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + if (cg) + return SR_ERR_NA; + return STD_CONFIG_LIST(key, data, sdi, cg, + scanopts, drvopts, devopts); + case SR_CONF_SAMPLERATE: + if (!devc) + return SR_ERR_NA; + *data = std_gvar_samplerates(devc->samplerates, + devc->num_samplerates); + break; + case SR_CONF_TRIGGER_MATCH: + *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; } static int dev_acquisition_stop(struct sr_dev_inst *sdi) { -picomso_abort_acquisition(sdi->priv); + picomso_abort_acquisition(sdi->priv); -return SR_OK; + return SR_OK; } static struct sr_dev_driver picomso_driver_info = { -.name = "picomso", -.longname = "PicoMSO mixed-signal logic analyzer", -.api_version = 1, -.init = std_init, -.cleanup = std_cleanup, -.scan = scan, -.dev_list = std_dev_list, -.dev_clear = dev_clear, -.config_get = config_get, -.config_set = config_set, -.config_list = config_list, -.dev_open = dev_open, -.dev_close = dev_close, -.dev_acquisition_start = picomso_start_acquisition, -.dev_acquisition_stop = dev_acquisition_stop, -.context = NULL, + .name = "picomso", + .longname = "PicoMSO mixed-signal oscilloscope / logic analyzer", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = dev_open, + .dev_close = dev_close, + .dev_acquisition_start = picomso_start_acquisition, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, }; -SR_REGISTER_DEV_DRIVER(picomso_driver_info); + +SR_REGISTER_DEV_DRIVER(picomso_driver_info); \ No newline at end of file diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index dcd6d7240..19db2c511 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -395,6 +395,58 @@ static int trigger_match_to_picomso(enum sr_trigger_matches match, return SR_OK; } +static int configure_capture_mode(const struct sr_dev_inst *sdi, + enum picomso_device_mode *mode) +{ + struct dev_context *devc; + const GSList *l; + struct sr_channel *ch; + unsigned int enabled_logic; + unsigned int enabled_analog; + + devc = sdi->priv; + + g_slist_free(devc->enabled_analog_channels); + devc->enabled_analog_channels = NULL; + + enabled_logic = 0; + enabled_analog = 0; + + for (l = sdi->channels; l; l = l->next) { + ch = l->data; + + if (!ch->enabled) + continue; + + if (ch->type == SR_CHANNEL_ANALOG) { + enabled_analog++; + devc->enabled_analog_channels = + g_slist_append(devc->enabled_analog_channels, ch); + } else if (ch->type == SR_CHANNEL_LOGIC) { + enabled_logic++; + } + } + + /* + * Keep the first scope-capable backend simple: + * - logic only -> logic mode + * - analog only -> scope mode + * - mixed logic+analog at the same time -> not supported yet + */ + if (enabled_logic > 0 && enabled_analog > 0) { + sr_err("Simultaneous logic and oscilloscope capture is not supported yet."); + return SR_ERR_NA; + } + + if (enabled_analog > 0) { + *mode = PICOMSO_MODE_OSCILLOSCOPE; + return SR_OK; + } + + *mode = PICOMSO_MODE_LOGIC; + return SR_OK; +} + static int build_capture_request(const struct sr_dev_inst *sdi, struct picomso_request_capture *request) { @@ -447,6 +499,8 @@ static int build_capture_request(const struct sr_dev_inst *sdi, if (!match->match || !match->channel || !match->channel->enabled) continue; + if (match->channel->type != SR_CHANNEL_LOGIC) + continue; if (match->channel->index >= NUM_CHANNELS) return SR_ERR_ARG; if (trigger_index >= PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT) @@ -500,6 +554,61 @@ static int send_logic_data(struct sr_dev_inst *sdi, return sr_session_send(sdi, &packet); } +static int send_scope_analog_data(struct sr_dev_inst *sdi, + const struct picomso_data_block *block) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + float samples[PICOMSO_DATA_BLOCK_SIZE / 2]; + size_t sample_count; + size_t i; + uint16_t raw; + + devc = sdi->priv; + + if ((block->data_len % 2u) != 0u) + return SR_ERR; + + sample_count = block->data_len / 2u; + + if (devc->limit_samples && + devc->sent_samples + sample_count > devc->limit_samples) { + sample_count = (size_t)(devc->limit_samples - devc->sent_samples); + } + + if (sample_count == 0u) + return SR_OK; + + for (i = 0; i < sample_count; i++) { + raw = (uint16_t)block->data[2u * i] + | ((uint16_t)block->data[2u * i + 1u] << 8); + + /* If firmware stores 12-bit ADC values in 16-bit words. */ + raw &= 0x0FFFu; + + samples[i] = (3.3f * (float)raw) / 4095.0f; + } + + sr_analog_init(&analog, &encoding, &meaning, &spec, 2); + analog.meaning->channels = devc->enabled_analog_channels; + analog.meaning->mq = SR_MQ_VOLTAGE; + analog.meaning->unit = SR_UNIT_VOLT; + analog.meaning->mqflags = 0; + analog.num_samples = sample_count; + analog.data = samples; + + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + + devc->sent_samples += sample_count; + + return sr_session_send(sdi, &packet); +} + static void finish_acquisition(struct sr_dev_inst *sdi) { struct dev_context *devc; @@ -516,8 +625,12 @@ static void finish_acquisition(struct sr_dev_inst *sdi) devc->acq_aborted = FALSE; devc->acq_state = PICOMSO_ACQ_IDLE; + devc->capture_mode = PICOMSO_MODE_UNSET; devc->expected_block_id = 0; devc->capture_deadline_us = 0; + + g_slist_free(devc->enabled_analog_channels); + devc->enabled_analog_channels = NULL; } static int receive_data(int fd, int revents, void *cb_data) @@ -547,8 +660,8 @@ static int receive_data(int fd, int revents, void *cb_data) return FALSE; } - if (status.mode != PICOMSO_MODE_LOGIC) { - sr_err("Device left logic mode while capture was running."); + if (status.mode != devc->capture_mode) { + sr_err("Device left expected capture mode while acquisition was running."); finish_acquisition(sdi); return FALSE; } @@ -590,12 +703,20 @@ static int receive_data(int fd, int revents, void *cb_data) finish_acquisition(sdi); return FALSE; } - ret = send_logic_data(sdi, &block); + + if (devc->capture_mode == PICOMSO_MODE_LOGIC) + ret = send_logic_data(sdi, &block); + else if (devc->capture_mode == PICOMSO_MODE_OSCILLOSCOPE) + ret = send_scope_analog_data(sdi, &block); + else + ret = SR_ERR; + if (ret != SR_OK) { - sr_err("Failed to forward PicoMSO logic data."); + sr_err("Failed to forward PicoMSO capture data."); finish_acquisition(sdi); return FALSE; } + devc->expected_block_id++; } @@ -687,8 +808,8 @@ SR_PRIV int picomso_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di) break; } - if ((devc->capabilities & PICOMSO_CAP_LOGIC) == 0u) { - sr_err("Connected PicoMSO device does not expose logic capability."); + if ((devc->capabilities & (PICOMSO_CAP_LOGIC | PICOMSO_CAP_SCOPE)) == 0u) { + sr_err("Connected PicoMSO device exposes neither logic nor scope capability."); libusb_release_interface(usb->devhdl, USB_INTERFACE); libusb_close(usb->devhdl); usb->devhdl = NULL; @@ -724,8 +845,10 @@ SR_PRIV struct dev_context *picomso_dev_new(void) devc->next_seq = 1u; devc->last_device_status = PICOMSO_STATUS_OK; devc->acq_state = PICOMSO_ACQ_IDLE; + devc->capture_mode = PICOMSO_MODE_UNSET; devc->expected_block_id = 0; devc->capture_deadline_us = 0; + devc->enabled_analog_channels = NULL; clear_error_state(devc); return devc; @@ -741,6 +864,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) struct dev_context *devc; struct picomso_request_capture request; struct picomso_status status; + enum picomso_device_mode mode; gint64 capture_time_us; int ret; @@ -749,11 +873,27 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (devc->acq_state != PICOMSO_ACQ_IDLE) return SR_ERR; + ret = configure_capture_mode(sdi, &mode); + if (ret != SR_OK) + return ret; + + if (mode == PICOMSO_MODE_OSCILLOSCOPE && + (devc->capabilities & PICOMSO_CAP_SCOPE) == 0u) { + sr_err("This PicoMSO firmware does not expose oscilloscope capability."); + return SR_ERR_NA; + } + + if (mode == PICOMSO_MODE_LOGIC && + (devc->capabilities & PICOMSO_CAP_LOGIC) == 0u) { + sr_err("This PicoMSO firmware does not expose logic capability."); + return SR_ERR_NA; + } + ret = build_capture_request(sdi, &request); if (ret != SR_OK) return ret; - ret = command_set_mode(sdi, PICOMSO_MODE_LOGIC); + ret = command_set_mode(sdi, mode); if (ret != SR_OK) return ret; @@ -761,8 +901,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) return ret; - if (status.mode != PICOMSO_MODE_LOGIC || - status.capture_state == PICOMSO_CAPTURE_RUNNING) + if (status.mode != mode || status.capture_state == PICOMSO_CAPTURE_RUNNING) return SR_ERR; ret = command_request_capture(sdi, &request); @@ -771,6 +910,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) devc->acq_aborted = FALSE; devc->acq_state = PICOMSO_ACQ_WAITING; + devc->capture_mode = mode; devc->expected_block_id = 0; devc->sent_samples = 0; @@ -783,6 +923,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) PICOMSO_POLL_INTERVAL_MS, receive_data, (void *)sdi); if (ret != SR_OK) { devc->acq_state = PICOMSO_ACQ_IDLE; + devc->capture_mode = PICOMSO_MODE_UNSET; command_set_mode(sdi, PICOMSO_MODE_UNSET); return ret; } @@ -791,6 +932,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) { sr_session_source_remove(sdi->session, -1); devc->acq_state = PICOMSO_ACQ_IDLE; + devc->capture_mode = PICOMSO_MODE_UNSET; command_set_mode(sdi, PICOMSO_MODE_UNSET); } diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index 6d478a778..2ac35f0a9 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -32,7 +32,9 @@ #define USB_INTERFACE 0 #define USB_CONFIGURATION 1 + #define NUM_CHANNELS 16 +#define NUM_ANALOG_CHANNELS 1 #define PICOMSO_USB_TIMEOUT_MS 500 #define PICOMSO_POLL_INTERVAL_MS 10 @@ -169,8 +171,12 @@ struct dev_context { gboolean acq_aborted; enum picomso_acq_state acq_state; + enum picomso_device_mode capture_mode; + uint16_t expected_block_id; gint64 capture_deadline_us; + + GSList *enabled_analog_channels; }; SR_PRIV int picomso_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di); From 3fcd5209994cf9157c1ba666659ea995352fe87b Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 1 Apr 2026 12:49:24 -0400 Subject: [PATCH 04/10] add multistream support --- src/hardware/picomso/protocol.c | 165 ++++++++++++++++++++------------ src/hardware/picomso/protocol.h | 41 ++++++-- 2 files changed, 138 insertions(+), 68 deletions(-) diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index 19db2c511..ef1c46772 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -194,6 +194,18 @@ static int parse_ack_status(const uint8_t *response, size_t response_len) return SR_OK; } +static gboolean stream_mask_is_valid(uint8_t streams) +{ + const uint8_t valid_mask = PICOMSO_STREAM_LOGIC | PICOMSO_STREAM_SCOPE; + return (streams & (uint8_t)~valid_mask) == 0u; +} + +static gboolean stream_id_is_valid(uint8_t stream_id) +{ + return stream_id == PICOMSO_STREAM_ID_LOGIC + || stream_id == PICOMSO_STREAM_ID_SCOPE; +} + static int command_get_info(const struct sr_dev_inst *sdi, struct picomso_info *info) { @@ -270,21 +282,27 @@ static int command_get_status(const struct sr_dev_inst *sdi, return SR_ERR; payload = response + PICOMSO_PACKET_HEADER_SIZE; - status->mode = payload[0]; + status->streams = payload[0]; status->capture_state = payload[1]; + if (!stream_mask_is_valid(status->streams)) + return SR_ERR; + return SR_OK; } static int command_set_mode(const struct sr_dev_inst *sdi, - enum picomso_device_mode mode) + uint8_t streams) { uint8_t payload[1]; uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; size_t response_len; int ret; - payload[0] = (uint8_t)mode; + if (!stream_mask_is_valid(streams)) + return SR_ERR_ARG; + + payload[0] = streams; ret = send_request(sdi, PICOMSO_MSG_SET_MODE, payload, sizeof(payload), PICOMSO_RESPONSE_TYPE_SET_MODE, response, &response_len); @@ -354,20 +372,25 @@ static int command_read_data_block(const struct sr_dev_inst *sdi, return ret; payload_len = read_u16_le(response + 6); - if (payload_len < 4u) + if (payload_len < 6u) return SR_ERR; payload = response + PICOMSO_PACKET_HEADER_SIZE; - block->block_id = read_u16_le(payload); - block->data_len = read_u16_le(payload + 2); + block->stream_id = payload[0]; + block->flags = payload[1]; + block->block_id = read_u16_le(payload + 2); + block->data_len = read_u16_le(payload + 4); + + if (!stream_id_is_valid(block->stream_id)) + return SR_ERR; if (block->data_len > PICOMSO_DATA_BLOCK_SIZE) return SR_ERR; - if ((uint16_t)(4u + block->data_len) > payload_len) + if ((uint16_t)(6u + block->data_len) > payload_len) return SR_ERR; if (block->data_len > 0u) - memcpy(block->data, payload + 4, block->data_len); + memcpy(block->data, payload + 6, block->data_len); return SR_OK; } @@ -395,8 +418,8 @@ static int trigger_match_to_picomso(enum sr_trigger_matches match, return SR_OK; } -static int configure_capture_mode(const struct sr_dev_inst *sdi, - enum picomso_device_mode *mode) +static int configure_capture_streams(const struct sr_dev_inst *sdi, + uint8_t *streams) { struct dev_context *devc; const GSList *l; @@ -404,6 +427,9 @@ static int configure_capture_mode(const struct sr_dev_inst *sdi, unsigned int enabled_logic; unsigned int enabled_analog; + if (!streams) + return SR_ERR_ARG; + devc = sdi->priv; g_slist_free(devc->enabled_analog_channels); @@ -411,6 +437,7 @@ static int configure_capture_mode(const struct sr_dev_inst *sdi, enabled_logic = 0; enabled_analog = 0; + *streams = PICOMSO_STREAM_NONE; for (l = sdi->channels; l; l = l->next) { ch = l->data; @@ -427,23 +454,16 @@ static int configure_capture_mode(const struct sr_dev_inst *sdi, } } - /* - * Keep the first scope-capable backend simple: - * - logic only -> logic mode - * - analog only -> scope mode - * - mixed logic+analog at the same time -> not supported yet - */ - if (enabled_logic > 0 && enabled_analog > 0) { - sr_err("Simultaneous logic and oscilloscope capture is not supported yet."); - return SR_ERR_NA; - } + if (enabled_logic > 0) + *streams |= PICOMSO_STREAM_LOGIC; + if (enabled_analog > 0) + *streams |= PICOMSO_STREAM_SCOPE; - if (enabled_analog > 0) { - *mode = PICOMSO_MODE_OSCILLOSCOPE; - return SR_OK; + if (*streams == PICOMSO_STREAM_NONE) { + sr_err("No enabled PicoMSO channels found for acquisition."); + return SR_ERR; } - *mode = PICOMSO_MODE_LOGIC; return SR_OK; } @@ -587,9 +607,7 @@ static int send_scope_analog_data(struct sr_dev_inst *sdi, raw = (uint16_t)block->data[2u * i] | ((uint16_t)block->data[2u * i + 1u] << 8); - /* If firmware stores 12-bit ADC values in 16-bit words. */ raw &= 0x0FFFu; - samples[i] = (3.3f * (float)raw) / 4095.0f; } @@ -617,17 +635,19 @@ static void finish_acquisition(struct sr_dev_inst *sdi) if (devc->acq_state == PICOMSO_ACQ_IDLE) return; - if (command_set_mode(sdi, PICOMSO_MODE_UNSET) != SR_OK) - sr_dbg("Unable to switch device back to unset mode after acquisition."); + if (command_set_mode(sdi, PICOMSO_STREAM_NONE) != SR_OK) + sr_dbg("Unable to switch device back to stream mask 0 after acquisition."); sr_session_source_remove(sdi->session, -1); std_session_send_df_end(sdi); devc->acq_aborted = FALSE; devc->acq_state = PICOMSO_ACQ_IDLE; - devc->capture_mode = PICOMSO_MODE_UNSET; - devc->expected_block_id = 0; + devc->enabled_streams = PICOMSO_STREAM_NONE; + devc->expected_logic_block_id = 0; + devc->expected_scope_block_id = 0; devc->capture_deadline_us = 0; + devc->sent_samples = 0; g_slist_free(devc->enabled_analog_channels); devc->enabled_analog_channels = NULL; @@ -660,8 +680,8 @@ static int receive_data(int fd, int revents, void *cb_data) return FALSE; } - if (status.mode != devc->capture_mode) { - sr_err("Device left expected capture mode while acquisition was running."); + if (status.streams != devc->enabled_streams) { + sr_err("Device left expected stream configuration while acquisition was running."); finish_acquisition(sdi); return FALSE; } @@ -697,27 +717,50 @@ static int receive_data(int fd, int revents, void *cb_data) finish_acquisition(sdi); return FALSE; } - if (block.block_id != devc->expected_block_id) { - sr_err("Unexpected PicoMSO block id %u, expected %u.", - block.block_id, devc->expected_block_id); - finish_acquisition(sdi); - return FALSE; - } - if (devc->capture_mode == PICOMSO_MODE_LOGIC) + switch (block.stream_id) { + case PICOMSO_STREAM_ID_LOGIC: + if (block.block_id != devc->expected_logic_block_id) { + sr_err("Unexpected PicoMSO logic block id %u, expected %u.", + block.block_id, devc->expected_logic_block_id); + finish_acquisition(sdi); + return FALSE; + } + ret = send_logic_data(sdi, &block); - else if (devc->capture_mode == PICOMSO_MODE_OSCILLOSCOPE) + if (ret != SR_OK) { + sr_err("Failed to forward PicoMSO logic capture data."); + finish_acquisition(sdi); + return FALSE; + } + + devc->expected_logic_block_id++; + break; + + case PICOMSO_STREAM_ID_SCOPE: + if (block.block_id != devc->expected_scope_block_id) { + sr_err("Unexpected PicoMSO scope block id %u, expected %u.", + block.block_id, devc->expected_scope_block_id); + finish_acquisition(sdi); + return FALSE; + } + ret = send_scope_analog_data(sdi, &block); - else - ret = SR_ERR; + if (ret != SR_OK) { + sr_err("Failed to forward PicoMSO scope capture data."); + finish_acquisition(sdi); + return FALSE; + } - if (ret != SR_OK) { - sr_err("Failed to forward PicoMSO capture data."); + devc->expected_scope_block_id++; + break; + + default: + sr_err("Received PicoMSO data block with unknown stream id 0x%02x.", + block.stream_id); finish_acquisition(sdi); return FALSE; } - - devc->expected_block_id++; } finish_acquisition(sdi); @@ -845,8 +888,9 @@ SR_PRIV struct dev_context *picomso_dev_new(void) devc->next_seq = 1u; devc->last_device_status = PICOMSO_STATUS_OK; devc->acq_state = PICOMSO_ACQ_IDLE; - devc->capture_mode = PICOMSO_MODE_UNSET; - devc->expected_block_id = 0; + devc->enabled_streams = PICOMSO_STREAM_NONE; + devc->expected_logic_block_id = 0; + devc->expected_scope_block_id = 0; devc->capture_deadline_us = 0; devc->enabled_analog_channels = NULL; clear_error_state(devc); @@ -864,7 +908,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) struct dev_context *devc; struct picomso_request_capture request; struct picomso_status status; - enum picomso_device_mode mode; + uint8_t streams; gint64 capture_time_us; int ret; @@ -873,17 +917,17 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (devc->acq_state != PICOMSO_ACQ_IDLE) return SR_ERR; - ret = configure_capture_mode(sdi, &mode); + ret = configure_capture_streams(sdi, &streams); if (ret != SR_OK) return ret; - if (mode == PICOMSO_MODE_OSCILLOSCOPE && + if ((streams & PICOMSO_STREAM_SCOPE) && (devc->capabilities & PICOMSO_CAP_SCOPE) == 0u) { sr_err("This PicoMSO firmware does not expose oscilloscope capability."); return SR_ERR_NA; } - if (mode == PICOMSO_MODE_LOGIC && + if ((streams & PICOMSO_STREAM_LOGIC) && (devc->capabilities & PICOMSO_CAP_LOGIC) == 0u) { sr_err("This PicoMSO firmware does not expose logic capability."); return SR_ERR_NA; @@ -893,7 +937,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) return ret; - ret = command_set_mode(sdi, mode); + ret = command_set_mode(sdi, streams); if (ret != SR_OK) return ret; @@ -901,7 +945,7 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) return ret; - if (status.mode != mode || status.capture_state == PICOMSO_CAPTURE_RUNNING) + if (status.streams != streams || status.capture_state == PICOMSO_CAPTURE_RUNNING) return SR_ERR; ret = command_request_capture(sdi, &request); @@ -910,8 +954,9 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) devc->acq_aborted = FALSE; devc->acq_state = PICOMSO_ACQ_WAITING; - devc->capture_mode = mode; - devc->expected_block_id = 0; + devc->enabled_streams = streams; + devc->expected_logic_block_id = 0; + devc->expected_scope_block_id = 0; devc->sent_samples = 0; capture_time_us = ((gint64)request.total_samples * G_USEC_PER_SEC) @@ -923,8 +968,8 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) PICOMSO_POLL_INTERVAL_MS, receive_data, (void *)sdi); if (ret != SR_OK) { devc->acq_state = PICOMSO_ACQ_IDLE; - devc->capture_mode = PICOMSO_MODE_UNSET; - command_set_mode(sdi, PICOMSO_MODE_UNSET); + devc->enabled_streams = PICOMSO_STREAM_NONE; + command_set_mode(sdi, PICOMSO_STREAM_NONE); return ret; } @@ -932,8 +977,8 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) { sr_session_source_remove(sdi->session, -1); devc->acq_state = PICOMSO_ACQ_IDLE; - devc->capture_mode = PICOMSO_MODE_UNSET; - command_set_mode(sdi, PICOMSO_MODE_UNSET); + devc->enabled_streams = PICOMSO_STREAM_NONE; + command_set_mode(sdi, PICOMSO_STREAM_NONE); } return ret; diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index 2ac35f0a9..7478a7caa 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -84,10 +84,30 @@ enum picomso_status_code { PICOMSO_STATUS_ERR_VERSION = 0x05, }; -enum picomso_device_mode { - PICOMSO_MODE_UNSET = 0x00, - PICOMSO_MODE_LOGIC = 0x01, - PICOMSO_MODE_OSCILLOSCOPE = 0x02, +/* + * Stream selection mask used by the control plane. + * + * 0x00: no streams enabled + * 0x01: logic stream enabled + * 0x02: scope stream enabled + * 0x03: logic + scope enabled (mixed) + */ +enum picomso_stream_mask { + PICOMSO_STREAM_NONE = 0x00, + PICOMSO_STREAM_LOGIC = 1u << 0, + PICOMSO_STREAM_SCOPE = 1u << 1, +}; + +/* + * Concrete stream identity for a single DATA_BLOCK packet. + * + * A transmitted block belongs to exactly one stream, even when the + * device is configured with multiple enabled streams. + */ +enum picomso_stream_id { + PICOMSO_STREAM_ID_NONE = 0x00, + PICOMSO_STREAM_ID_LOGIC = 0x01, + PICOMSO_STREAM_ID_SCOPE = 0x02, }; enum picomso_capture_state { @@ -140,14 +160,16 @@ struct picomso_info { }; struct picomso_status { - uint8_t mode; + uint8_t streams; uint8_t capture_state; }; struct picomso_data_block { + uint8_t stream_id; + uint8_t flags; uint16_t block_id; uint16_t data_len; - uint8_t data[PICOMSO_DATA_BLOCK_SIZE]; + uint8_t data[PICOMSO_DATA_BLOCK_SIZE]; }; struct dev_context { @@ -171,9 +193,12 @@ struct dev_context { gboolean acq_aborted; enum picomso_acq_state acq_state; - enum picomso_device_mode capture_mode; - uint16_t expected_block_id; + uint8_t enabled_streams; + + uint16_t expected_logic_block_id; + uint16_t expected_scope_block_id; + gint64 capture_deadline_us; GSList *enabled_analog_channels; From aab8edb9efc4241bf2f1f528ddb055c739f60bc4 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 2 Apr 2026 13:30:17 -0400 Subject: [PATCH 05/10] Fix sample count --- src/hardware/picomso/protocol.c | 23 +++++++++++++---------- src/hardware/picomso/protocol.h | 3 ++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index ef1c46772..6c4ded0db 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -555,8 +555,8 @@ static int send_logic_data(struct sr_dev_inst *sdi, sample_count = block->data_len / sizeof(uint16_t); if (devc->limit_samples && - devc->sent_samples + sample_count > devc->limit_samples) { - sample_count = (size_t)(devc->limit_samples - devc->sent_samples); + devc->sent_logic_samples + sample_count > devc->limit_samples) { + sample_count = (size_t)(devc->limit_samples - devc->sent_logic_samples); } if (sample_count == 0u) @@ -569,7 +569,7 @@ static int send_logic_data(struct sr_dev_inst *sdi, packet.type = SR_DF_LOGIC; packet.payload = &logic; - devc->sent_samples += sample_count; + devc->sent_logic_samples += sample_count; return sr_session_send(sdi, &packet); } @@ -596,8 +596,8 @@ static int send_scope_analog_data(struct sr_dev_inst *sdi, sample_count = block->data_len / 2u; if (devc->limit_samples && - devc->sent_samples + sample_count > devc->limit_samples) { - sample_count = (size_t)(devc->limit_samples - devc->sent_samples); + devc->sent_scope_samples + sample_count > devc->limit_samples) { + sample_count = (size_t)(devc->limit_samples - devc->sent_scope_samples); } if (sample_count == 0u) @@ -622,7 +622,7 @@ static int send_scope_analog_data(struct sr_dev_inst *sdi, packet.type = SR_DF_ANALOG; packet.payload = &analog; - devc->sent_samples += sample_count; + devc->sent_scope_samples += sample_count; return sr_session_send(sdi, &packet); } @@ -647,7 +647,8 @@ static void finish_acquisition(struct sr_dev_inst *sdi) devc->expected_logic_block_id = 0; devc->expected_scope_block_id = 0; devc->capture_deadline_us = 0; - devc->sent_samples = 0; + devc->sent_logic_samples = 0; + devc->sent_scope_samples = 0; g_slist_free(devc->enabled_analog_channels); devc->enabled_analog_channels = NULL; @@ -884,7 +885,8 @@ SR_PRIV struct dev_context *picomso_dev_new(void) devc->cur_samplerate = 0; devc->limit_samples = PICOMSO_DEFAULT_LIMIT_SAMPLES; devc->capture_ratio = 0; - devc->sent_samples = 0; + devc->sent_logic_samples = 0; + devc->sent_scope_samples = 0; devc->next_seq = 1u; devc->last_device_status = PICOMSO_STATUS_OK; devc->acq_state = PICOMSO_ACQ_IDLE; @@ -957,12 +959,13 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) devc->enabled_streams = streams; devc->expected_logic_block_id = 0; devc->expected_scope_block_id = 0; - devc->sent_samples = 0; + devc->sent_logic_samples = 0; + devc->sent_scope_samples = 0; capture_time_us = ((gint64)request.total_samples * G_USEC_PER_SEC) / request.rate; devc->capture_deadline_us = g_get_monotonic_time() - + capture_time_us + (3 * G_USEC_PER_SEC); + + capture_time_us + (600 * G_USEC_PER_SEC); ret = sr_session_source_add(sdi->session, -1, 0, PICOMSO_POLL_INTERVAL_MS, receive_data, (void *)sdi); diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index 7478a7caa..6159ce640 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -182,7 +182,8 @@ struct dev_context { uint64_t cur_samplerate; uint64_t limit_samples; uint64_t capture_ratio; - uint64_t sent_samples; + uint64_t sent_logic_samples; + uint64_t sent_scope_samples; uint32_t capabilities; struct picomso_info info; From e1acbcfbbb897fb3ca5156193d931482f1d9facb Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 3 Apr 2026 07:39:30 -0400 Subject: [PATCH 06/10] picomso: fix capture limit semantics and update config lists --- src/hardware/picomso/api.c | 12 +++++++++--- src/hardware/picomso/protocol.c | 14 +++++++++----- src/hardware/picomso/protocol.h | 2 +- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/hardware/picomso/api.c b/src/hardware/picomso/api.c index f0b1ca989..29960e7e7 100644 --- a/src/hardware/picomso/api.c +++ b/src/hardware/picomso/api.c @@ -37,7 +37,7 @@ static const uint32_t drvopts[] = { }; static const uint32_t devopts[] = { - SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_CONN | SR_CONF_GET, SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, @@ -66,6 +66,7 @@ static const uint64_t samplerates[] = { SR_MHZ(20), SR_MHZ(50), SR_MHZ(100), + SR_MHZ(200), }; static const char *channel_names_logic[] = { @@ -374,13 +375,13 @@ static int config_set(uint32_t key, GVariant *data, break; case SR_CONF_LIMIT_SAMPLES: value = g_variant_get_uint64(data); - if (value == 0 || value > PICOMSO_MAX_TOTAL_SAMPLES) + if (value == 0 || value > PICOMSO_MAX_POST_TRIGGER_SAMPLES) return SR_ERR_ARG; devc->limit_samples = value; break; case SR_CONF_CAPTURE_RATIO: value = g_variant_get_uint64(data); - if (value > 100) + if (value > 10) return SR_ERR_ARG; devc->capture_ratio = value; break; @@ -405,6 +406,11 @@ static int config_list(uint32_t key, GVariant **data, return SR_ERR_NA; return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); + case SR_CONF_LIMIT_SAMPLES: + *data = g_variant_new("(tt)", + (guint64)1, + (guint64)PICOMSO_MAX_POST_TRIGGER_SAMPLES); + break; case SR_CONF_SAMPLERATE: if (!devc) return SR_ERR_NA; diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index 6c4ded0db..08ac52082 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -475,28 +475,32 @@ static int build_capture_request(const struct sr_dev_inst *sdi, struct sr_trigger_stage *stage; struct sr_trigger_match *match; const GSList *l; - uint64_t total_samples; + uint64_t post_trigger_samples; uint64_t pre_trigger_samples; + uint64_t total_samples; unsigned int trigger_index; int ret; devc = sdi->priv; memset(request, 0, sizeof(*request)); - total_samples = devc->limit_samples ? + post_trigger_samples = devc->limit_samples ? devc->limit_samples : PICOMSO_DEFAULT_LIMIT_SAMPLES; - if (total_samples == 0 || total_samples > PICOMSO_MAX_TOTAL_SAMPLES) + if (post_trigger_samples == 0 + || post_trigger_samples > PICOMSO_MAX_POST_TRIGGER_SAMPLES) return SR_ERR_ARG; if (devc->cur_samplerate == 0) return SR_ERR_ARG; if (devc->capture_ratio > 100) return SR_ERR_ARG; - pre_trigger_samples = (devc->capture_ratio * total_samples) / 100; + pre_trigger_samples = (devc->capture_ratio * post_trigger_samples) / 100; if (pre_trigger_samples > PICOMSO_MAX_PRE_TRIGGER_SAMPLES) return SR_ERR_ARG; - if (total_samples - pre_trigger_samples > PICOMSO_MAX_POST_TRIGGER_SAMPLES) + + total_samples = pre_trigger_samples + post_trigger_samples; + if (total_samples > PICOMSO_MAX_TOTAL_SAMPLES) return SR_ERR_ARG; request->total_samples = (uint32_t)total_samples; diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index 6159ce640..f7c55f356 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -52,7 +52,7 @@ #define PICOMSO_DEFAULT_LIMIT_SAMPLES 1024u #define PICOMSO_MAX_PRE_TRIGGER_SAMPLES 1024u -#define PICOMSO_MAX_POST_TRIGGER_SAMPLES 10000u +#define PICOMSO_MAX_POST_TRIGGER_SAMPLES 50000u #define PICOMSO_MAX_TOTAL_SAMPLES \ (PICOMSO_MAX_PRE_TRIGGER_SAMPLES + PICOMSO_MAX_POST_TRIGGER_SAMPLES) From 040c9b6bd0fb534132929b31ca2bb2ab82ca9d58 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 4 Apr 2026 05:37:35 -0400 Subject: [PATCH 07/10] libsigrok: reject analog capture rates above 2 MS/s --- src/hardware/picomso/protocol.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index 08ac52082..f1a975fe0 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -927,6 +927,12 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) return ret; + if ((streams & PICOMSO_STREAM_SCOPE) && + devc->cur_samplerate > SR_MHZ(2)) { + sr_err("Analog stream requested with samplerate above 2 MHz."); + return SR_ERR_ARG; + } + if ((streams & PICOMSO_STREAM_SCOPE) && (devc->capabilities & PICOMSO_CAP_SCOPE) == 0u) { sr_err("This PicoMSO firmware does not expose oscilloscope capability."); From 547feb29837e7fdd017187d2a902d84a3c843916 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 07:35:45 -0400 Subject: [PATCH 08/10] Merge multi-analog support * picomso: demultiplex interleaved analog samples across N enabled channels Agent-Logs-Url: https://github.com/dgatf/libsigrok/sessions/48723f40-c5c1-42a6-8d97-1bc47b101273 Co-authored-by: dgatf <33811722+dgatf@users.noreply.github.com> * picomso: use stack-allocated GSList node to avoid per-channel alloc in demux loop Agent-Logs-Url: https://github.com/dgatf/libsigrok/sessions/48723f40-c5c1-42a6-8d97-1bc47b101273 Co-authored-by: dgatf <33811722+dgatf@users.noreply.github.com> * picomso: add analog_channel_mask to capture request for independent analog channel selection Agent-Logs-Url: https://github.com/dgatf/libsigrok/sessions/1150c071-7e8b-426f-973d-1e3517effc90 Co-authored-by: dgatf <33811722+dgatf@users.noreply.github.com> * picomso: add A2, bump protocol version, fix per-channel scope sample budget Agent-Logs-Url: https://github.com/dgatf/libsigrok/sessions/005e0cac-54a9-4189-915a-5da5c18f70f3 Co-authored-by: dgatf <33811722+dgatf@users.noreply.github.com> * Fix analog channel ordering in dual-channel scope mode * Fix max samples --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dgatf <33811722+dgatf@users.noreply.github.com> Co-authored-by: Daniel --- src/hardware/picomso/api.c | 2 +- src/hardware/picomso/protocol.c | 123 ++++++++++++++++++++++++++------ src/hardware/picomso/protocol.h | 13 +++- 3 files changed, 113 insertions(+), 25 deletions(-) diff --git a/src/hardware/picomso/api.c b/src/hardware/picomso/api.c index 29960e7e7..5cef20d69 100644 --- a/src/hardware/picomso/api.c +++ b/src/hardware/picomso/api.c @@ -75,7 +75,7 @@ static const char *channel_names_logic[] = { }; static const char *channel_names_analog[] = { - "A0", + "A0", "A1", }; static gboolean is_plausible(const struct libusb_device_descriptor *des) diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index f1a975fe0..48329ab63 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -315,7 +315,7 @@ static int command_set_mode(const struct sr_dev_inst *sdi, static int command_request_capture(const struct sr_dev_inst *sdi, const struct picomso_request_capture *request) { - uint8_t payload[12u + PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT * 3u]; + uint8_t payload[12u + PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT * 3u + 1u]; uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; size_t response_len; size_t offset; @@ -336,6 +336,10 @@ static int command_request_capture(const struct sr_dev_inst *sdi, payload[offset + 2u] = request->trigger[i].match; } + /* Byte 24: analog channel selection mask (A0=bit0, A1=bit1, ...). */ + payload[12u + PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT * 3u] = + request->analog_channel_mask; + ret = send_request(sdi, PICOMSO_MSG_REQUEST_CAPTURE, payload, (uint16_t)sizeof(payload), PICOMSO_RESPONSE_TYPE_REQUEST, response, &response_len); @@ -418,6 +422,18 @@ static int trigger_match_to_picomso(enum sr_trigger_matches match, return SR_OK; } +static gint compare_channel_index(gconstpointer a, gconstpointer b) +{ + const struct sr_channel *cha = a; + const struct sr_channel *chb = b; + + if (cha->index < chb->index) + return -1; + if (cha->index > chb->index) + return 1; + return 0; +} + static int configure_capture_streams(const struct sr_dev_inst *sdi, uint8_t *streams) { @@ -454,6 +470,9 @@ static int configure_capture_streams(const struct sr_dev_inst *sdi, } } + devc->enabled_analog_channels = + g_slist_sort(devc->enabled_analog_channels, compare_channel_index); + if (enabled_logic > 0) *streams |= PICOMSO_STREAM_LOGIC; if (enabled_analog > 0) @@ -474,6 +493,7 @@ static int build_capture_request(const struct sr_dev_inst *sdi, struct sr_trigger *trigger; struct sr_trigger_stage *stage; struct sr_trigger_match *match; + struct sr_channel *ch; const GSList *l; uint64_t post_trigger_samples; uint64_t pre_trigger_samples; @@ -507,6 +527,20 @@ static int build_capture_request(const struct sr_dev_inst *sdi, request->rate = (uint32_t)devc->cur_samplerate; request->pre_trigger_samples = (uint32_t)pre_trigger_samples; + /* + * Build the analog channel mask from the enabled analog channel list. + * Bit N = analog channel N (A0=bit0, A1=bit1, ...). Each channel's + * hardware analog index is its global index minus NUM_CHANNELS (the + * number of logic channels that precede the analog channels). + */ + request->analog_channel_mask = 0u; + for (l = devc->enabled_analog_channels; l; l = l->next) { + ch = l->data; + if (ch->index >= NUM_CHANNELS) + request->analog_channel_mask |= + (uint8_t)(1u << (ch->index - NUM_CHANNELS)); + } + trigger = sr_session_trigger_get(sdi->session); if (!trigger) return SR_OK; @@ -588,47 +622,94 @@ static int send_scope_analog_data(struct sr_dev_inst *sdi, struct sr_analog_meaning meaning; struct sr_analog_spec spec; float samples[PICOMSO_DATA_BLOCK_SIZE / 2]; - size_t sample_count; - size_t i; + size_t sample_count, num_channels, per_channel, ch_idx, raw_idx, i; uint16_t raw; + const GSList *l; + GSList ch_node = {0}; + int ret; devc = sdi->priv; + /* + * Scope payload format: + * + * - The firmware always sends scope samples as little-endian 16-bit words. + * - Single-channel analog mode uses native 12-bit ADC samples in bits 11:0. + * - Dual-channel analog mode may internally capture 8-bit samples, but the + * firmware expands them to a 12-bit-equivalent range before transmission. + * + * Therefore the host always consumes 16-bit little-endian samples and + * scales them as 12-bit values after masking with 0x0FFF. + */ if ((block->data_len % 2u) != 0u) return SR_ERR; sample_count = block->data_len / 2u; + /* + * N = number of enabled analog channels; incoming samples are interleaved: + * channel k receives raw samples at indices k, k+N, k+2N, ... + * Truncate to a whole number of N-sample frames. + */ + num_channels = (size_t)g_slist_length(devc->enabled_analog_channels); + if (num_channels == 0u) + return SR_OK; + + sample_count -= sample_count % num_channels; + + /* + * limit_samples is a per-channel budget. The total number of interleaved + * samples the driver should forward is therefore limit_samples * num_channels. + */ if (devc->limit_samples && - devc->sent_scope_samples + sample_count > devc->limit_samples) { - sample_count = (size_t)(devc->limit_samples - devc->sent_scope_samples); + devc->sent_scope_samples + sample_count > + devc->limit_samples * (uint64_t)num_channels) { + sample_count = (size_t)( + devc->limit_samples * (uint64_t)num_channels + - devc->sent_scope_samples); + sample_count -= sample_count % num_channels; } if (sample_count == 0u) return SR_OK; - for (i = 0; i < sample_count; i++) { - raw = (uint16_t)block->data[2u * i] - | ((uint16_t)block->data[2u * i + 1u] << 8); + per_channel = sample_count / num_channels; - raw &= 0x0FFFu; - samples[i] = (3.3f * (float)raw) / 4095.0f; - } + /* Emit one analog packet per enabled channel with demultiplexed samples. */ + for (ch_idx = 0, l = devc->enabled_analog_channels; l; + l = l->next, ch_idx++) { + for (i = 0; i < per_channel; i++) { + raw_idx = i * num_channels + ch_idx; + raw = (uint16_t)block->data[2u * raw_idx] + | ((uint16_t)block->data[2u * raw_idx + 1u] << 8); + + /* Scope samples are always transported as 12-bit-scaled values. */ + raw &= 0x0FFFu; + samples[i] = (3.3f * (float)raw) / 4095.0f; + } + + ch_node.data = l->data; + ch_node.next = NULL; - sr_analog_init(&analog, &encoding, &meaning, &spec, 2); - analog.meaning->channels = devc->enabled_analog_channels; - analog.meaning->mq = SR_MQ_VOLTAGE; - analog.meaning->unit = SR_UNIT_VOLT; - analog.meaning->mqflags = 0; - analog.num_samples = sample_count; - analog.data = samples; + sr_analog_init(&analog, &encoding, &meaning, &spec, 2); + analog.meaning->channels = &ch_node; + analog.meaning->mq = SR_MQ_VOLTAGE; + analog.meaning->unit = SR_UNIT_VOLT; + analog.meaning->mqflags = 0; + analog.num_samples = per_channel; + analog.data = samples; - packet.type = SR_DF_ANALOG; - packet.payload = &analog; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + + ret = sr_session_send(sdi, &packet); + if (ret != SR_OK) + return ret; + } devc->sent_scope_samples += sample_count; - return sr_session_send(sdi, &packet); + return SR_OK; } static void finish_acquisition(struct sr_dev_inst *sdi) diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index f7c55f356..e540322bd 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -34,7 +34,7 @@ #define USB_CONFIGURATION 1 #define NUM_CHANNELS 16 -#define NUM_ANALOG_CHANNELS 1 +#define NUM_ANALOG_CHANNELS 3 #define PICOMSO_USB_TIMEOUT_MS 500 #define PICOMSO_POLL_INTERVAL_MS 10 @@ -45,14 +45,14 @@ #define PICOMSO_CTRL_REQUEST_OUT 0x01 #define PICOMSO_PROTOCOL_VERSION_MAJOR 0 -#define PICOMSO_PROTOCOL_VERSION_MINOR 3 +#define PICOMSO_PROTOCOL_VERSION_MINOR 4 #define PICOMSO_PACKET_MAGIC UINT16_C(0x4D53) #define PICOMSO_PACKET_HEADER_SIZE 8u #define PICOMSO_DEFAULT_LIMIT_SAMPLES 1024u #define PICOMSO_MAX_PRE_TRIGGER_SAMPLES 1024u -#define PICOMSO_MAX_POST_TRIGGER_SAMPLES 50000u +#define PICOMSO_MAX_POST_TRIGGER_SAMPLES 40000u #define PICOMSO_MAX_TOTAL_SAMPLES \ (PICOMSO_MAX_PRE_TRIGGER_SAMPLES + PICOMSO_MAX_POST_TRIGGER_SAMPLES) @@ -151,6 +151,13 @@ struct picomso_request_capture { uint32_t rate; uint32_t pre_trigger_samples; struct picomso_trigger_config trigger[PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT]; + /* + * Bitmask of selected analog channels to capture. Bit N corresponds + * to analog channel N (A0=bit0, A1=bit1, ...). The firmware captures + * only the selected channels and interleaves them in ascending channel + * index order. Set to 0 when the scope stream is not active. + */ + uint8_t analog_channel_mask; }; struct picomso_info { From 7e19df3d3d21655ef3238850578a38ec5ec8774e Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 6 Apr 2026 12:02:46 -0400 Subject: [PATCH 09/10] Add firmware-reported capabilities and RP2350 device support --- src/hardware/picomso/api.c | 16 ++++++++----- src/hardware/picomso/protocol.c | 42 ++++++++++++--------------------- src/hardware/picomso/protocol.h | 17 ++++++++++++- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/hardware/picomso/api.c b/src/hardware/picomso/api.c index 5cef20d69..b6af99007 100644 --- a/src/hardware/picomso/api.c +++ b/src/hardware/picomso/api.c @@ -21,8 +21,10 @@ #include "protocol.h" static const struct picomso_profile supported_picomso[] = { - { 0x04b5, 0x2041, "Raspberry Pi", "PicoMSO", NULL, - "Raspberry Pi", "PicoMSO" }, + { 0x2E8A, 0x2040, "PicoMSO", "PicoMSO-RP2040", NULL, + "PicoMSO", "PicoMSO-RP2040" }, + { 0x2E8A, 0x2350, "PicoMSO", "PicoMSO-RP2350", NULL, + "PicoMSO", "PicoMSO-RP2350" }, ALL_ZERO }; @@ -375,9 +377,11 @@ static int config_set(uint32_t key, GVariant *data, break; case SR_CONF_LIMIT_SAMPLES: value = g_variant_get_uint64(data); - if (value == 0 || value > PICOMSO_MAX_POST_TRIGGER_SAMPLES) - return SR_ERR_ARG; - devc->limit_samples = value; + uint32_t max_samples = devc->fw_caps.max_samples_logic; + + if (value == 0 || value > max_samples) + return SR_ERR_ARG; + devc->limit_samples = value; break; case SR_CONF_CAPTURE_RATIO: value = g_variant_get_uint64(data); @@ -409,7 +413,7 @@ static int config_list(uint32_t key, GVariant **data, case SR_CONF_LIMIT_SAMPLES: *data = g_variant_new("(tt)", (guint64)1, - (guint64)PICOMSO_MAX_POST_TRIGGER_SAMPLES); + (guint64)devc->fw_caps.max_samples_logic); break; case SR_CONF_SAMPLERATE: if (!devc) diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index 48329ab63..0de351295 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -238,14 +238,16 @@ static int command_get_info(const struct sr_dev_inst *sdi, } static int command_get_capabilities(const struct sr_dev_inst *sdi, - uint32_t *capabilities) + struct picomso_capabilities *caps) { uint8_t response[PICOMSO_PROTOCOL_IO_BUFFER_SIZE]; size_t response_len; const uint8_t *payload; + uint16_t payload_len; + int ret; - if (!capabilities) + if (!caps) return SR_ERR_ARG; ret = send_request(sdi, PICOMSO_MSG_GET_CAPABILITIES, NULL, 0u, @@ -253,11 +255,18 @@ static int command_get_capabilities(const struct sr_dev_inst *sdi, if (ret != SR_OK) return ret; - if (read_u16_le(response + 6) < 4u) - return SR_ERR; + payload_len = read_u16_le(response + 6); + if (payload_len < sizeof(*caps)) + return SR_ERR_DATA; payload = response + PICOMSO_PACKET_HEADER_SIZE; - *capabilities = read_u32_le(payload); + memcpy(caps, payload, sizeof(*caps)); + + if (caps->version != 1u) + return SR_ERR_DATA; + + if (caps->size != sizeof(*caps)) + return SR_ERR_DATA; return SR_OK; } @@ -928,7 +937,7 @@ SR_PRIV int picomso_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di) break; } - ret = command_get_capabilities(sdi, &devc->capabilities); + ret = command_get_capabilities(sdi, &devc->fw_caps); if (ret != SR_OK) { sr_err("Failed to query PicoMSO device capabilities."); libusb_release_interface(usb->devhdl, USB_INTERFACE); @@ -937,15 +946,6 @@ SR_PRIV int picomso_dev_open(struct sr_dev_inst *sdi, struct sr_dev_driver *di) break; } - if ((devc->capabilities & (PICOMSO_CAP_LOGIC | PICOMSO_CAP_SCOPE)) == 0u) { - sr_err("Connected PicoMSO device exposes neither logic nor scope capability."); - libusb_release_interface(usb->devhdl, USB_INTERFACE); - libusb_close(usb->devhdl); - usb->devhdl = NULL; - ret = SR_ERR; - break; - } - sr_info("Opened PicoMSO device on %d.%d / %s, firmware %u.%u (%s).", usb->bus, usb->address, connection_id, devc->info.protocol_version_major, @@ -1014,18 +1014,6 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) return SR_ERR_ARG; } - if ((streams & PICOMSO_STREAM_SCOPE) && - (devc->capabilities & PICOMSO_CAP_SCOPE) == 0u) { - sr_err("This PicoMSO firmware does not expose oscilloscope capability."); - return SR_ERR_NA; - } - - if ((streams & PICOMSO_STREAM_LOGIC) && - (devc->capabilities & PICOMSO_CAP_LOGIC) == 0u) { - sr_err("This PicoMSO firmware does not expose logic capability."); - return SR_ERR_NA; - } - ret = build_capture_request(sdi, &request); if (ret != SR_OK) return ret; diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index e540322bd..0625c92d3 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -179,6 +179,21 @@ struct picomso_data_block { uint8_t data[PICOMSO_DATA_BLOCK_SIZE]; }; +struct __attribute__((packed)) picomso_capabilities { + uint8_t version; + uint8_t size; + + uint8_t capabilities_flags; + uint8_t max_logic_channels; + uint8_t max_analog_channels; + + uint32_t max_samplerate_logic; + uint32_t max_samplerate_scope; + + uint32_t max_samples_logic; + uint32_t max_samples_scope; +}; + struct dev_context { const struct picomso_profile *profile; char **channel_names; @@ -192,8 +207,8 @@ struct dev_context { uint64_t sent_logic_samples; uint64_t sent_scope_samples; - uint32_t capabilities; struct picomso_info info; + struct picomso_capabilities fw_caps; uint8_t next_seq; uint8_t last_device_status; From 8ecef15d14e9bbc165807e9416d717628c3595c0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 1 May 2026 06:47:59 -0400 Subject: [PATCH 10/10] Clean up --- src/hardware/picomso/protocol.c | 29 ++++++----------------------- src/hardware/picomso/protocol.h | 5 +---- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/hardware/picomso/protocol.c b/src/hardware/picomso/protocol.c index 0de351295..52d0cd7e0 100644 --- a/src/hardware/picomso/protocol.c +++ b/src/hardware/picomso/protocol.c @@ -35,14 +35,6 @@ static uint16_t read_u16_le(const uint8_t *data) return (uint16_t)data[0] | ((uint16_t)data[1] << 8); } -static uint32_t read_u32_le(const uint8_t *data) -{ - return (uint32_t)data[0] - | ((uint32_t)data[1] << 8) - | ((uint32_t)data[2] << 16) - | ((uint32_t)data[3] << 24); -} - static void write_u16_le(uint8_t *data, uint16_t value) { data[0] = (uint8_t)(value & 0xffu); @@ -504,8 +496,6 @@ static int build_capture_request(const struct sr_dev_inst *sdi, struct sr_trigger_match *match; struct sr_channel *ch; const GSList *l; - uint64_t post_trigger_samples; - uint64_t pre_trigger_samples; uint64_t total_samples; unsigned int trigger_index; int ret; @@ -513,28 +503,19 @@ static int build_capture_request(const struct sr_dev_inst *sdi, devc = sdi->priv; memset(request, 0, sizeof(*request)); - post_trigger_samples = devc->limit_samples ? + total_samples = devc->limit_samples ? devc->limit_samples : PICOMSO_DEFAULT_LIMIT_SAMPLES; - if (post_trigger_samples == 0 - || post_trigger_samples > PICOMSO_MAX_POST_TRIGGER_SAMPLES) - return SR_ERR_ARG; if (devc->cur_samplerate == 0) return SR_ERR_ARG; if (devc->capture_ratio > 100) return SR_ERR_ARG; - - pre_trigger_samples = (devc->capture_ratio * post_trigger_samples) / 100; - if (pre_trigger_samples > PICOMSO_MAX_PRE_TRIGGER_SAMPLES) - return SR_ERR_ARG; - - total_samples = pre_trigger_samples + post_trigger_samples; if (total_samples > PICOMSO_MAX_TOTAL_SAMPLES) return SR_ERR_ARG; request->total_samples = (uint32_t)total_samples; request->rate = (uint32_t)devc->cur_samplerate; - request->pre_trigger_samples = (uint32_t)pre_trigger_samples; + request->pre_trigger_samples = request->total_samples * devc->capture_ratio / 100u; /* * Build the analog channel mask from the enabled analog channel list. @@ -1008,8 +989,10 @@ SR_PRIV int picomso_start_acquisition(const struct sr_dev_inst *sdi) if (ret != SR_OK) return ret; - if ((streams & PICOMSO_STREAM_SCOPE) && - devc->cur_samplerate > SR_MHZ(2)) { + guint analog_channels = g_slist_length(devc->enabled_analog_channels); + if ((streams & PICOMSO_STREAM_SCOPE) + && ((devc->cur_samplerate > SR_MHZ(2) && analog_channels == 1) + || (devc->cur_samplerate > SR_MHZ(1) && analog_channels == 2))) { sr_err("Analog stream requested with samplerate above 2 MHz."); return SR_ERR_ARG; } diff --git a/src/hardware/picomso/protocol.h b/src/hardware/picomso/protocol.h index 0625c92d3..e2c566cb0 100644 --- a/src/hardware/picomso/protocol.h +++ b/src/hardware/picomso/protocol.h @@ -51,10 +51,7 @@ #define PICOMSO_PACKET_HEADER_SIZE 8u #define PICOMSO_DEFAULT_LIMIT_SAMPLES 1024u -#define PICOMSO_MAX_PRE_TRIGGER_SAMPLES 1024u -#define PICOMSO_MAX_POST_TRIGGER_SAMPLES 40000u -#define PICOMSO_MAX_TOTAL_SAMPLES \ - (PICOMSO_MAX_PRE_TRIGGER_SAMPLES + PICOMSO_MAX_POST_TRIGGER_SAMPLES) +#define PICOMSO_MAX_TOTAL_SAMPLES 32768u #define PICOMSO_REQUEST_CAPTURE_TRIGGER_COUNT 4u #define PICOMSO_DATA_BLOCK_SIZE 64u