From c4f1d1515c723bab53204e7c1cd8c455ba2274f3 Mon Sep 17 00:00:00 2001 From: Antoine van Gelder Date: Thu, 28 Sep 2023 14:12:17 +0200 Subject: [PATCH] repo: proof-of-concept for sharing values between host and device code --- device/firmware/moondancer/Cargo.toml | 1 + .../firmware/moondancer/src/bin/moondancer.rs | 5 ++ device/firmware/moondancer/src/lib.rs | 36 +++++++++ device/firmware/moondancer/src/usb.rs | 80 +++++++------------ device/firmware/moondancer/test/test_raw.py | 5 +- device/firmware/smolusb/src/descriptor.rs | 7 +- host/cynthion/shared.py | 31 +++++++ host/cynthion/shared/libgreat.toml | 14 ++++ host/cynthion/shared/registers.toml | 4 + host/cynthion/shared/usb.toml | 72 +++++++++++++++++ host/pyproject.toml | 17 +++- 11 files changed, 214 insertions(+), 58 deletions(-) create mode 100644 host/cynthion/shared.py create mode 100644 host/cynthion/shared/libgreat.toml create mode 100644 host/cynthion/shared/registers.toml create mode 100644 host/cynthion/shared/usb.toml diff --git a/device/firmware/moondancer/Cargo.toml b/device/firmware/moondancer/Cargo.toml index 30e19987..1817204d 100644 --- a/device/firmware/moondancer/Cargo.toml +++ b/device/firmware/moondancer/Cargo.toml @@ -60,6 +60,7 @@ heapless = { version = "=0.7.16", default-features = false, features = ["mpmc_la zerocopy = { version = "=0.7.0-alpha.2", default-features = false } log = { version="=0.4.17", features = ["release_max_level_info"] } +static-toml = { version = "1.0.1" } # - binaries ------------------------------------------------------------------ diff --git a/device/firmware/moondancer/src/bin/moondancer.rs b/device/firmware/moondancer/src/bin/moondancer.rs index 96f2825a..c7b8f187 100644 --- a/device/firmware/moondancer/src/bin/moondancer.rs +++ b/device/firmware/moondancer/src/bin/moondancer.rs @@ -113,6 +113,11 @@ impl<'a> Firmware<'a> { fn new(peripherals: pac::Peripherals) -> Self { // initialize logging moondancer::log::init(hal::Serial::new(peripherals.UART)); + info!( + "{} {}", + moondancer::shared::usb::bManufacturerString, + moondancer::shared::usb::bProductString + ); info!("Logging initialized"); // usb1: aux (host on r0.4) diff --git a/device/firmware/moondancer/src/lib.rs b/device/firmware/moondancer/src/lib.rs index 33018c3e..52933bf3 100644 --- a/device/firmware/moondancer/src/lib.rs +++ b/device/firmware/moondancer/src/lib.rs @@ -27,6 +27,8 @@ pub use libgreat::firmware::BoardInformation; // - constants ---------------------------------------------------------------- pub const SYSTEM_CLOCK_FREQUENCY: u32 = pac::clock::sysclk(); + +// TODO these should be sourced at runtime pub const BOARD_INFORMATION: BoardInformation = BoardInformation { board_id: 0x10_u32.to_le_bytes(), version_string: "v2023.0.1\0", @@ -40,6 +42,40 @@ pub const BOARD_INFORMATION: BoardInformation = BoardInformation { pub const EP_MAX_ENDPOINTS: usize = 16; pub const EP_MAX_PACKET_SIZE: usize = 512; +#[allow(non_upper_case_globals)] +pub mod shared { + /// The values in this module are statically generated at compile + /// time from the corresponding TOML files located in the + /// `cynthion.git/host/cynthion/shared/` directory. + + pub mod libgreat { + static_toml::static_toml! { + static CONFIG = include_toml!("../../../host/cynthion/shared/libgreat.toml"); + } + pub mod endpoints { + use super::CONFIG; + pub static bulk_in_address: u8 = CONFIG.endpoints.bulk_in_address as u8; + pub static bulk_out_address: u8 = CONFIG.endpoints.bulk_out_address as u8; + } + pub mod vendor { + use super::CONFIG; + pub static command_request: u8 = CONFIG.vendor.command_request as u8; + } + } + + pub mod usb { + static_toml::static_toml! { + static CONFIG = include_toml!("../../../host/cynthion/shared/usb.toml"); + } + pub static bVendorId: u16 = CONFIG.b_vendor_id.cynthion as u16; + pub static bProductId: u16 = CONFIG.b_product_id.cynthion as u16; + pub static bManufacturerString: &'static str = CONFIG.b_manufacturer_string.cynthion; + pub static bProductString: &'static str = CONFIG.b_product_string.cynthion; + pub static bInterfaceSubClass: u8 = CONFIG.b_interface_sub_class.moondancer as u8; + pub static bInterfaceProtocol: u8 = CONFIG.b_interface_protocol.moondancer as u8; + } +} + // - types -------------------------------------------------------------------- #[derive(Copy, Clone, Debug)] diff --git a/device/firmware/moondancer/src/usb.rs b/device/firmware/moondancer/src/usb.rs index 4071ffed..df9d77b8 100644 --- a/device/firmware/moondancer/src/usb.rs +++ b/device/firmware/moondancer/src/usb.rs @@ -4,31 +4,9 @@ use smolusb::descriptor::*; // - constants ---------------------------------------------------------------- -pub const VENDOR_ID: u16 = 0x1d50; // OpenMoko, Inc. -pub const PRODUCT_ID: u16 = 0x615b; // Cynthion USB Multitool - pub const DEVICE_VERSION_NUMBER: u16 = 0x0004; // Cynthion r0.4 TODO read from? pub const DEVICE_SERIAL_STRING: &'static str = "r0.4"; // TODO read from? -/// libgreat backend interface subclass TODO document -/// -/// 0x00 - Apollo / Flash Bridge Interface -/// 0x01..0x0f - Reserved -/// 0x10 - Analyzer -/// 0x11 - Moondancer -pub const LIBGREAT_INTERFACE_SUBCLASS: u8 = 0x11; - -/// libgreat backend interface protocol version TODO document -/// -/// 0x01 -> v0.1 -/// 0x10 -> v1.0 -pub const LIBGREAT_INTERFACE_PROTOCOL: u8 = 0x01; - -pub const LIBGREAT_BULK_OUT_ENDPOINT_ADDRESS: u8 = 0x02; -pub const LIBGREAT_BULK_IN_ENDPOINT_ADDRESS: u8 = 0x81; - -pub const LIBGREAT_BULK_OUT_ENDPOINT_NUMBER: u8 = LIBGREAT_BULK_OUT_ENDPOINT_ADDRESS; -pub const LIBGREAT_BULK_IN_ENDPOINT_NUMBER: u8 = LIBGREAT_BULK_IN_ENDPOINT_ADDRESS & 0x7f; // - vendor request ----------------------------------------------------------- @@ -87,14 +65,16 @@ pub mod vendor { // - descriptors -------------------------------------------------------------- -pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor { +use crate::shared; + +pub static DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor { descriptor_version: 0x0200, device_class: 0x00, // Composite device_subclass: 0x00, // Composite device_protocol: 0x00, // Composite max_packet_size: 64, - vendor_id: VENDOR_ID, - product_id: PRODUCT_ID, + vendor_id: shared::usb::bVendorId, + product_id: shared::usb::bProductId, device_version_number: DEVICE_VERSION_NUMBER, manufacturer_string_index: 1, product_string_index: 2, @@ -102,8 +82,7 @@ pub const DEVICE_DESCRIPTOR: DeviceDescriptor = DeviceDescriptor { num_configurations: 1, ..DeviceDescriptor::new() }; - -pub const DEVICE_QUALIFIER_DESCRIPTOR: DeviceQualifierDescriptor = DeviceQualifierDescriptor { +pub static DEVICE_QUALIFIER_DESCRIPTOR: DeviceQualifierDescriptor = DeviceQualifierDescriptor { descriptor_version: 0x0200, device_class: 0x00, // Composite device_subclass: 0x00, // Composite @@ -113,7 +92,7 @@ pub const DEVICE_QUALIFIER_DESCRIPTOR: DeviceQualifierDescriptor = DeviceQualifi ..DeviceQualifierDescriptor::new() }; -pub const CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = ConfigurationDescriptor::new( +pub static CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = ConfigurationDescriptor::new( ConfigurationDescriptorHeader { descriptor_type: DescriptorType::Configuration as u8, configuration_value: 1, @@ -127,22 +106,22 @@ pub const CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = ConfigurationDes interface_number: 0, alternate_setting: 0, interface_class: 0xff, // Vendor-specific - interface_subclass: LIBGREAT_INTERFACE_SUBCLASS, - interface_protocol: LIBGREAT_INTERFACE_PROTOCOL, + interface_subclass: shared::usb::bInterfaceSubClass, + interface_protocol: shared::usb::bInterfaceProtocol, interface_string_index: 5, ..InterfaceDescriptorHeader::new() }, &[ EndpointDescriptor { - endpoint_address: LIBGREAT_BULK_IN_ENDPOINT_ADDRESS, // IN - attributes: 0x02, // Bulk + endpoint_address: shared::libgreat::endpoints::bulk_in_address, // IN + attributes: 0x02, // Bulk max_packet_size: 512, interval: 0, ..EndpointDescriptor::new() }, EndpointDescriptor { - endpoint_address: LIBGREAT_BULK_OUT_ENDPOINT_ADDRESS, // OUT - attributes: 0x02, // Bulk + endpoint_address: shared::libgreat::endpoints::bulk_out_address, // OUT + attributes: 0x02, // Bulk max_packet_size: 512, interval: 0, ..EndpointDescriptor::new() @@ -151,7 +130,7 @@ pub const CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = ConfigurationDes )], ); -pub const OTHER_SPEED_CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = +pub static OTHER_SPEED_CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = ConfigurationDescriptor::new( ConfigurationDescriptorHeader { descriptor_type: DescriptorType::OtherSpeedConfiguration as u8, @@ -166,22 +145,22 @@ pub const OTHER_SPEED_CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = interface_number: 0, alternate_setting: 0, interface_class: 0xff, // Vendor-specific - interface_subclass: LIBGREAT_INTERFACE_SUBCLASS, - interface_protocol: LIBGREAT_INTERFACE_PROTOCOL, + interface_subclass: shared::usb::bInterfaceSubClass, + interface_protocol: shared::usb::bInterfaceProtocol, interface_string_index: 5, ..InterfaceDescriptorHeader::new() }, &[ EndpointDescriptor { - endpoint_address: LIBGREAT_BULK_IN_ENDPOINT_ADDRESS, // IN - attributes: 0x02, // Bulk + endpoint_address: shared::libgreat::endpoints::bulk_in_address, // IN + attributes: 0x02, // Bulk max_packet_size: 512, interval: 0, ..EndpointDescriptor::new() }, EndpointDescriptor { - endpoint_address: LIBGREAT_BULK_OUT_ENDPOINT_ADDRESS, // OUT - attributes: 0x02, // Bulk + endpoint_address: shared::libgreat::endpoints::bulk_out_address, // OUT + attributes: 0x02, // Bulk max_packet_size: 512, interval: 0, ..EndpointDescriptor::new() @@ -190,19 +169,18 @@ pub const OTHER_SPEED_CONFIGURATION_DESCRIPTOR_0: ConfigurationDescriptor = )], ); -pub const USB_STRING_DESCRIPTOR_0: StringDescriptorZero = +pub static USB_STRING_DESCRIPTOR_0: StringDescriptorZero = StringDescriptorZero::new(&[LanguageId::EnglishUnitedStates]); -pub const USB_STRING_DESCRIPTOR_1: StringDescriptor = StringDescriptor::new("Great Scott Gadgets"); // manufacturer -pub const USB_STRING_DESCRIPTOR_2: StringDescriptor = - StringDescriptor::new("Cynthion USB Multitool"); // product -pub const USB_STRING_DESCRIPTOR_3: StringDescriptor = StringDescriptor::new(DEVICE_SERIAL_STRING); // serial -pub const USB_STRING_DESCRIPTOR_4: StringDescriptor = StringDescriptor::new("config0"); // configuration #0 -pub const USB_STRING_DESCRIPTOR_5: StringDescriptor = StringDescriptor::new("interface0"); // interface #0 -pub const USB_STRING_DESCRIPTOR_6: StringDescriptor = StringDescriptor::new("interface1"); // interface #1 -pub const USB_STRING_DESCRIPTOR_7: StringDescriptor = StringDescriptor::new("config1"); // configuration #1 +pub static USB_STRING_DESCRIPTOR_1: StringDescriptor = StringDescriptor::new(shared::usb::bManufacturerString); // manufacturer +pub static USB_STRING_DESCRIPTOR_2: StringDescriptor = StringDescriptor::new(shared::usb::bProductString); // product +pub static USB_STRING_DESCRIPTOR_3: StringDescriptor = StringDescriptor::new(DEVICE_SERIAL_STRING); // serial +pub static USB_STRING_DESCRIPTOR_4: StringDescriptor = StringDescriptor::new("config0"); // configuration #0 +pub static USB_STRING_DESCRIPTOR_5: StringDescriptor = StringDescriptor::new("interface0"); // interface #0 +pub static USB_STRING_DESCRIPTOR_6: StringDescriptor = StringDescriptor::new("interface1"); // interface #1 +pub static USB_STRING_DESCRIPTOR_7: StringDescriptor = StringDescriptor::new("config1"); // configuration #1 -pub const USB_STRING_DESCRIPTORS: &[&StringDescriptor] = &[ +pub static USB_STRING_DESCRIPTORS: &[&StringDescriptor] = &[ &USB_STRING_DESCRIPTOR_1, &USB_STRING_DESCRIPTOR_2, &USB_STRING_DESCRIPTOR_3, diff --git a/device/firmware/moondancer/test/test_raw.py b/device/firmware/moondancer/test/test_raw.py index ac05d26a..b6da4c97 100644 --- a/device/firmware/moondancer/test/test_raw.py +++ b/device/firmware/moondancer/test/test_raw.py @@ -14,9 +14,10 @@ from pygreat.comms_backends.usb1 import USB1CommsBackend as backend import cynthion +from cynthion import shared -VENDOR_ID = cynthion.board.CYNTHION_VENDOR_ID -PRODUCT_ID = cynthion.board.CYNTHION_PRODUCT_ID +VENDOR_ID = shared.usb.bVendorId.cynthion +PRODUCT_ID = shared.usb.bProductId.cynthion EP_MAX_PACKET_SIZE = 512 diff --git a/device/firmware/smolusb/src/descriptor.rs b/device/firmware/smolusb/src/descriptor.rs index 59bb3395..324315af 100644 --- a/device/firmware/smolusb/src/descriptor.rs +++ b/device/firmware/smolusb/src/descriptor.rs @@ -72,7 +72,7 @@ impl TryFrom for DescriptorType { /// USB device descriptor /// /// TODO consider renaming descriptor fields according to LUNA / industry-standard names -#[derive(AsBytes, FromBytes)] +#[derive(AsBytes, FromBytes, Clone, Copy)] #[repr(C, packed)] pub struct DeviceDescriptor { pub _length: u8, // 18 @@ -123,7 +123,7 @@ impl Default for DeviceDescriptor { // - DeviceQualifierDescriptor ------------------------------------------------ /// USB device qualifier descriptor -#[derive(AsBytes, FromBytes)] +#[derive(AsBytes, FromBytes, Clone, Copy)] #[repr(C, packed)] pub struct DeviceQualifierDescriptor { pub _length: u8, // 10 @@ -351,7 +351,7 @@ impl Default for EndpointDescriptor { // - StringDescriptorZero ----------------------------------------------------- /// USB string descriptor language id -#[derive(AsBytes, Copy, Clone, Debug)] +#[derive(AsBytes, Clone, Copy, Debug)] #[repr(u16)] pub enum LanguageId { EnglishUnitedStates = 0x0409, @@ -363,6 +363,7 @@ pub enum LanguageId { impl AsByteSliceIterator for LanguageId {} /// USB string zero descriptor +#[derive(Clone, Copy)] pub struct StringDescriptorZero<'a> { head: StringDescriptorHeader, tail: &'a [LanguageId], diff --git a/host/cynthion/shared.py b/host/cynthion/shared.py new file mode 100644 index 00000000..f586bfe2 --- /dev/null +++ b/host/cynthion/shared.py @@ -0,0 +1,31 @@ +# +# This file is part of Cynthion. +# + +""" +The values in this module are generated at runtime from the +corresponding TOML files located in the `cynthion.git/host/cynthion/shared/` +directory. +""" + +import tomllib + +from collections import namedtuple +from importlib.resources import files +from os import path + +SHARED_TOML_DIR = files("cynthion").joinpath("shared") + +def _dict_to_namedtuple(data, typename="_"): + return namedtuple(typename, data.keys())( + *(_dict_to_namedtuple(v, typename + '_' + k) if isinstance(v, dict) else v for k, v in data.items()) + ) + +with open(path.join(SHARED_TOML_DIR, "libgreat.toml"), "rb") as f: + libgreat = _dict_to_namedtuple(tomllib.load(f)) + +with open(path.join(SHARED_TOML_DIR, "registers.toml"), "rb") as f: + registers = _dict_to_namedtuple(tomllib.load(f)) + +with open(path.join(SHARED_TOML_DIR, "usb.toml"), "rb") as f: + usb = _dict_to_namedtuple(tomllib.load(f)) diff --git a/host/cynthion/shared/libgreat.toml b/host/cynthion/shared/libgreat.toml new file mode 100644 index 00000000..27d2c1ab --- /dev/null +++ b/host/cynthion/shared/libgreat.toml @@ -0,0 +1,14 @@ +### libgreat +### +### This configuration file defines constants that are shared between +### the host and device implementations of the libgreat protocol. +### + +# endpoint configurations +[endpoints] +bulk_in_address = 0x81 +bulk_out_address = 0x02 + +# Vendor request constants +[vendor] +command_request = 0x65 diff --git a/host/cynthion/shared/registers.toml b/host/cynthion/shared/registers.toml new file mode 100644 index 00000000..81ee5509 --- /dev/null +++ b/host/cynthion/shared/registers.toml @@ -0,0 +1,4 @@ +### registers +### +### TODO +### \ No newline at end of file diff --git a/host/cynthion/shared/usb.toml b/host/cynthion/shared/usb.toml new file mode 100644 index 00000000..9b567546 --- /dev/null +++ b/host/cynthion/shared/usb.toml @@ -0,0 +1,72 @@ +### Cynthion shared USB identifiers +### +### This configuration file defines USB identifiers that are shared +### between host and device implementations. +### + +# bVendorId +# +# USB Device Descriptor vendor id field value. +# +[bVendorId] +apollo = 0x1d50 # OpenMoko, Inc. +cynthion = 0x1d50 # OpenMoko, Inc. + +# bProductId +# +# USB Device Descriptor product id field value. +# +[bProductId] +apollo = 0x615c # Apollo Debugger +cynthion = 0x615b # Cynthion USB Multitool + +# iManufacturer +# +# USB Device Descriptor Manufacturer string field value. +# +[bManufacturerString] +apollo = "Great Scott Gadgets" +cynthion = "Great Scott Gadgets" + +# iProduct +# +# USB Device Descriptor product string field value. +# +[bProductString] +apollo = "Apollo Debugger" +cynthion = "Cynthion USB Multitool" + + +# bInterfaceSubClass +# +# Cynthion reports the same idVendor and idProduct values in the USB +# device descriptor irrespective of the gateware running on the device. +# +# Therefore we use the interface subclass field to identify the +# function being exposed by the currently provided interfaces. +# +# Valid values are: +# +# 0x00 - Apollo / Flash Bridge Interface +# 0x01..0x0f - Reserved +# 0x10 - Analyzer +# 0x11 - Moondancer (libgreat) +# +[bInterfaceSubClass] +apollo = 0x00 +analyzer = 0x10 +moondancer = 0x11 + +# bInterfaceProtocol +# +# It is recommended to use the interface protocol field to version the +# protocol used for the the interface subclass in the form: +# +# Bits 0-3: Minor version +# Bits 4-7: Major version +# +# e.g. 0x01 = v0.1, 0x02 = v0.2, 0x10 = v1.0, 0x11 = v1.1 +# +[bInterfaceProtocol] +analyzer = 0x01 +moondancer = 0x01 diff --git a/host/pyproject.toml b/host/pyproject.toml index ff1680a2..829fcf43 100644 --- a/host/pyproject.toml +++ b/host/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=41", "wheel", "setuptools-git-versioning<2"] +requires = ["setuptools>=61", "wheel", "setuptools-git-versioning<2"] build-backend = "setuptools.build_meta" [project] @@ -41,7 +41,20 @@ cynthion_analyzer = "cynthion.commands.cynthion_analyzer:main" cynthion_info = "cynthion.commands.cynthion_info:main" cynthion_program = "cynthion.commands.cynthion_program:main" +[tool.setuptools] +include-package-data = true +py-modules = ["build"] + +[tool.setuptools.cmdclass] +build = "build.SharedTOML" + +[tool.setuptools.packages.find] +namespaces = true +where = ["."] + +[tool.setuptools.package-data] +"cynthion.shared" = ["*.toml"] [tool.setuptools-git-versioning] enabled = true -starting_version = "2023.07.12" \ No newline at end of file +starting_version = "2023.07.12"