From 4dbbf667a4f7733e685486df6b7d6d7f76386158 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 17 Nov 2024 17:34:32 +0800 Subject: [PATCH 1/4] Implement picotool reboot Works: ``` > sudo ./build/picotool reboot -f -u > sudo ./build/picotool reboot -f ``` TODO: Can we keep USB VID/PID? Signed-off-by: Daniel Schaefer --- Cargo.toml | 2 + qtpy/Cargo.toml | 1 + qtpy/src/main.rs | 17 +++- qtpy/src/usbd_picotool_reset.rs | 145 ++++++++++++++++++++++++++++++++ rust-toolchain.toml | 2 + 5 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 qtpy/src/usbd_picotool_reset.rs diff --git a/Cargo.toml b/Cargo.toml index a8e11c48..7ab60960 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,8 @@ usb-device = "0.2.9" heapless = "0.7.16" usbd-serial = "0.1.1" usbd-hid = "0.6.1" +# Can't use right now, depends on embedded-hal 1.0.0 +# usbd-picotool-reset = "0.3.0" fugit = "0.3.7" # LED Matrix is31fl3741 = "0.3.0" diff --git a/qtpy/Cargo.toml b/qtpy/Cargo.toml index 39e128f9..a16c1ae3 100644 --- a/qtpy/Cargo.toml +++ b/qtpy/Cargo.toml @@ -23,6 +23,7 @@ usb-device.workspace = true heapless.workspace = true usbd-serial.workspace = true usbd-hid.workspace = true +#usbd-picotool-reset.workspace = true fugit.workspace = true # C1 Minimal diff --git a/qtpy/src/main.rs b/qtpy/src/main.rs index 1ba7cd1a..45a73761 100644 --- a/qtpy/src/main.rs +++ b/qtpy/src/main.rs @@ -34,11 +34,15 @@ use bsp::hal::{ }; // USB Device support -use usb_device::{class_prelude::*, prelude::*}; +use usb_device::{class_prelude::*, prelude::*, descriptor::lang_id}; // USB Communications Class Device support use usbd_serial::{SerialPort, USB_CLASS_CDC}; +//use usbd_picotool_reset::PicoToolReset; +mod usbd_picotool_reset; +use crate::usbd_picotool_reset::PicoToolReset; + // Used to demonstrate writing formatted strings // use core::fmt::Write; // use heapless::String; @@ -95,10 +99,16 @@ fn main() -> ! { &mut pac.RESETS, )); + let mut picotool: PicoToolReset<_> = PicoToolReset::new(&usb_bus); + // Set up the USB Communications Class Device driver let mut serial = SerialPort::new(&usb_bus); - let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(FRAMEWORK_VID, COMMUNITY_PID)) + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x2e8a, 0x000a)) + //.strings(&[StringDescriptors::new(lang_id::ENGLISH_US) + // .manufacturer("Adafruit") + // .product("QT PY - Framework 16 Inputmodule FW")]) + //.expect("Failed to set strings") .manufacturer("Adafruit") .product("QT PY - Framework 16 Inputmodule FW") .max_power(500) @@ -141,7 +151,8 @@ fn main() -> ! { } // Check for new data - if usb_dev.poll(&mut [&mut serial]) { + // usb_dev.poll(&mut [&mut picotool, &mut serial]); + if usb_dev.poll(&mut [&mut picotool, &mut serial]) { let mut buf = [0u8; 64]; match serial.read(&mut buf) { Err(_e) => { diff --git a/qtpy/src/usbd_picotool_reset.rs b/qtpy/src/usbd_picotool_reset.rs new file mode 100644 index 00000000..b8793a89 --- /dev/null +++ b/qtpy/src/usbd_picotool_reset.rs @@ -0,0 +1,145 @@ +//! From https://github.com/ithinuel/usbd-picotool-reset +//! UsbClass implementation for the picotool reset feature. +//! +//! ## Note +//! +//! For picotool to recognize your device, your device must be using Raspberry Pi's vendor ID (`0x2e8a`) +//! and one of the product ID. You can check [picotool's sources](https://github.com/raspberrypi/picotool/blob/master/picoboot_connection/picoboot_connection.c#L23-L27) +//! for an exhaustive list. + +#![forbid(missing_docs)] +// #![no_std] + +use core::marker::PhantomData; +use usb_device::class_prelude::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator}; +//use usb_device::LangID; +use usb_device::descriptor::lang_id; + +// Vendor specific class +const CLASS_VENDOR_SPECIFIC: u8 = 0xFF; +// cf: https://github.com/raspberrypi/pico-sdk/blob/f396d05f8252d4670d4ea05c8b7ac938ef0cd381/src/common/pico_usb_reset_interface/include/pico/usb_reset_interface.h#L17 +const RESET_INTERFACE_SUBCLASS: u8 = 0x00; +const RESET_INTERFACE_PROTOCOL: u8 = 0x01; +const RESET_REQUEST_BOOTSEL: u8 = 0x01; +//const RESET_REQUEST_FLASH: u8 = 0x02; + +/// Defines which feature of the bootloader are made available after reset. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DisableInterface { + /// Both Mass Storage and Pico boot are enabled. + None, + /// Disables Mass Storage leaving only PicoBoot. + DisableMassStorage, + /// Disables PicoBoot leaving only Mass Storage. + DisablePicoBoot, +} +impl DisableInterface { + const fn into(self) -> u32 { + match self { + DisableInterface::None => 0, + DisableInterface::DisableMassStorage => 1, + DisableInterface::DisablePicoBoot => 2, + } + } +} + +/// Allows to customize the configuration of the UsbClass. +pub trait Config { + /// Configuration for which interface to enable/disable after reset. + const INTERFACE_DISABLE: DisableInterface; + /// Configuration for which pin to show mass storage activity after reset. + const BOOTSEL_ACTIVITY_LED: Option; +} + +/// Default configuration for PicoTool class. +/// +/// This lets both interface enabled after reset and does not display mass storage activity on any +/// LED. +pub enum DefaultConfig {} +impl Config for DefaultConfig { + const INTERFACE_DISABLE: DisableInterface = DisableInterface::None; + + const BOOTSEL_ACTIVITY_LED: Option = None; +} + +/// UsbClass implementation for Picotool's reset feature. +pub struct PicoToolReset<'a, B: UsbBus, C: Config = DefaultConfig> { + intf: InterfaceNumber, + str_idx: StringIndex, + _bus: PhantomData<&'a B>, + _cnf: PhantomData, +} +impl<'a, B: UsbBus, C: Config> PicoToolReset<'a, B, C> { + /// Creates a new instance of PicoToolReset. + pub fn new(alloc: &'a UsbBusAllocator) -> PicoToolReset<'a, B, C> { + Self { + intf: alloc.interface(), + str_idx: alloc.string(), + _bus: PhantomData, + _cnf: PhantomData, + } + } +} + +impl usb_device::class::UsbClass for PicoToolReset<'_, B, C> { + fn get_configuration_descriptors( + &self, + writer: &mut usb_device::descriptor::DescriptorWriter, + ) -> usb_device::Result<()> { + writer.interface_alt( + self.intf, + 0, + CLASS_VENDOR_SPECIFIC, + RESET_INTERFACE_SUBCLASS, + RESET_INTERFACE_PROTOCOL, + Some(self.str_idx), + ) + } + + fn get_string(&self, index: StringIndex, _lang_id: u16) -> Option<&str> { + (index == self.str_idx).then_some("Reset") + } + + fn control_out(&mut self, xfer: usb_device::class_prelude::ControlOut) { + let req = xfer.request(); + if !(req.request_type == usb_device::control::RequestType::Class + && req.recipient == usb_device::control::Recipient::Interface + && req.index == u8::from(self.intf) as u16) + { + return; + } + + match req.request { + RESET_REQUEST_BOOTSEL => { + let mut gpio_mask = C::BOOTSEL_ACTIVITY_LED.map(|led| 1 << led).unwrap_or(0); + if req.value & 0x100 != 0 { + gpio_mask = 1 << (req.value >> 9); + } + rp2040_hal::rom_data::reset_to_usb_boot( + gpio_mask, + u32::from(req.value & 0x7F) | C::INTERFACE_DISABLE.into(), + ); + // no-need to accept/reject, we'll reset the device anyway + unreachable!() + } + //RESET_REQUEST_FLASH => todo!(), + _ => { + // we are not expecting any other USB OUT requests + let _ = xfer.reject(); + } + } + } + + fn control_in(&mut self, xfer: usb_device::class_prelude::ControlIn) { + let req = xfer.request(); + if !(req.request_type == usb_device::control::RequestType::Class + && req.recipient == usb_device::control::Recipient::Interface + && req.index == u8::from(self.intf) as u16) + { + return; + } + // we are not expecting any USB IN requests + let _ = xfer.reject(); + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 460b3c4b..264c9503 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,6 @@ [toolchain] +# Needed for embeded-hal 1.0.0 +# channel = "1.75.0" channel = "1.74.0" targets = ["thumbv6m-none-eabi"] components = ["clippy", "rustfmt"] From cba1dfda0e8b442bdd07637b7da3baaa506e7ed2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 17 Nov 2024 18:36:11 +0800 Subject: [PATCH 2/4] Use usbd-picotool-reset from forked repo Signed-off-by: Daniel Schaefer --- Cargo.lock | 10 +++ Cargo.toml | 1 + qtpy/Cargo.toml | 2 +- qtpy/src/main.rs | 4 +- qtpy/src/usbd_picotool_reset.rs | 145 -------------------------------- 5 files changed, 13 insertions(+), 149 deletions(-) delete mode 100644 qtpy/src/usbd_picotool_reset.rs diff --git a/Cargo.lock b/Cargo.lock index 50a4c9f4..c1fca899 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1647,6 +1647,7 @@ dependencies = [ "smart-leds", "usb-device", "usbd-hid", + "usbd-picotool-reset", "usbd-serial", "ws2812-pio", ] @@ -2362,6 +2363,15 @@ dependencies = [ "usbd-hid-descriptors", ] +[[package]] +name = "usbd-picotool-reset" +version = "0.3.0" +source = "git+https://github.com/JohnAZoidberg/usbd-picotool-reset?branch=rp-2040-hal-0.8#46cbb075a39031cd0fb8c4539aa9ed20dabff76d" +dependencies = [ + "rp2040-hal", + "usb-device", +] + [[package]] name = "usbd-serial" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 7ab60960..8fbc51e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ usbd-serial = "0.1.1" usbd-hid = "0.6.1" # Can't use right now, depends on embedded-hal 1.0.0 # usbd-picotool-reset = "0.3.0" +usbd-picotool-reset = { git = "https://github.com/JohnAZoidberg/usbd-picotool-reset", branch = "rp-2040-hal-0.8" } fugit = "0.3.7" # LED Matrix is31fl3741 = "0.3.0" diff --git a/qtpy/Cargo.toml b/qtpy/Cargo.toml index a16c1ae3..412b5e61 100644 --- a/qtpy/Cargo.toml +++ b/qtpy/Cargo.toml @@ -23,7 +23,7 @@ usb-device.workspace = true heapless.workspace = true usbd-serial.workspace = true usbd-hid.workspace = true -#usbd-picotool-reset.workspace = true +usbd-picotool-reset.workspace = true fugit.workspace = true # C1 Minimal diff --git a/qtpy/src/main.rs b/qtpy/src/main.rs index 45a73761..48b747c8 100644 --- a/qtpy/src/main.rs +++ b/qtpy/src/main.rs @@ -39,9 +39,7 @@ use usb_device::{class_prelude::*, prelude::*, descriptor::lang_id}; // USB Communications Class Device support use usbd_serial::{SerialPort, USB_CLASS_CDC}; -//use usbd_picotool_reset::PicoToolReset; -mod usbd_picotool_reset; -use crate::usbd_picotool_reset::PicoToolReset; +use usbd_picotool_reset::PicoToolReset; // Used to demonstrate writing formatted strings // use core::fmt::Write; diff --git a/qtpy/src/usbd_picotool_reset.rs b/qtpy/src/usbd_picotool_reset.rs deleted file mode 100644 index b8793a89..00000000 --- a/qtpy/src/usbd_picotool_reset.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! From https://github.com/ithinuel/usbd-picotool-reset -//! UsbClass implementation for the picotool reset feature. -//! -//! ## Note -//! -//! For picotool to recognize your device, your device must be using Raspberry Pi's vendor ID (`0x2e8a`) -//! and one of the product ID. You can check [picotool's sources](https://github.com/raspberrypi/picotool/blob/master/picoboot_connection/picoboot_connection.c#L23-L27) -//! for an exhaustive list. - -#![forbid(missing_docs)] -// #![no_std] - -use core::marker::PhantomData; -use usb_device::class_prelude::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator}; -//use usb_device::LangID; -use usb_device::descriptor::lang_id; - -// Vendor specific class -const CLASS_VENDOR_SPECIFIC: u8 = 0xFF; -// cf: https://github.com/raspberrypi/pico-sdk/blob/f396d05f8252d4670d4ea05c8b7ac938ef0cd381/src/common/pico_usb_reset_interface/include/pico/usb_reset_interface.h#L17 -const RESET_INTERFACE_SUBCLASS: u8 = 0x00; -const RESET_INTERFACE_PROTOCOL: u8 = 0x01; -const RESET_REQUEST_BOOTSEL: u8 = 0x01; -//const RESET_REQUEST_FLASH: u8 = 0x02; - -/// Defines which feature of the bootloader are made available after reset. -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DisableInterface { - /// Both Mass Storage and Pico boot are enabled. - None, - /// Disables Mass Storage leaving only PicoBoot. - DisableMassStorage, - /// Disables PicoBoot leaving only Mass Storage. - DisablePicoBoot, -} -impl DisableInterface { - const fn into(self) -> u32 { - match self { - DisableInterface::None => 0, - DisableInterface::DisableMassStorage => 1, - DisableInterface::DisablePicoBoot => 2, - } - } -} - -/// Allows to customize the configuration of the UsbClass. -pub trait Config { - /// Configuration for which interface to enable/disable after reset. - const INTERFACE_DISABLE: DisableInterface; - /// Configuration for which pin to show mass storage activity after reset. - const BOOTSEL_ACTIVITY_LED: Option; -} - -/// Default configuration for PicoTool class. -/// -/// This lets both interface enabled after reset and does not display mass storage activity on any -/// LED. -pub enum DefaultConfig {} -impl Config for DefaultConfig { - const INTERFACE_DISABLE: DisableInterface = DisableInterface::None; - - const BOOTSEL_ACTIVITY_LED: Option = None; -} - -/// UsbClass implementation for Picotool's reset feature. -pub struct PicoToolReset<'a, B: UsbBus, C: Config = DefaultConfig> { - intf: InterfaceNumber, - str_idx: StringIndex, - _bus: PhantomData<&'a B>, - _cnf: PhantomData, -} -impl<'a, B: UsbBus, C: Config> PicoToolReset<'a, B, C> { - /// Creates a new instance of PicoToolReset. - pub fn new(alloc: &'a UsbBusAllocator) -> PicoToolReset<'a, B, C> { - Self { - intf: alloc.interface(), - str_idx: alloc.string(), - _bus: PhantomData, - _cnf: PhantomData, - } - } -} - -impl usb_device::class::UsbClass for PicoToolReset<'_, B, C> { - fn get_configuration_descriptors( - &self, - writer: &mut usb_device::descriptor::DescriptorWriter, - ) -> usb_device::Result<()> { - writer.interface_alt( - self.intf, - 0, - CLASS_VENDOR_SPECIFIC, - RESET_INTERFACE_SUBCLASS, - RESET_INTERFACE_PROTOCOL, - Some(self.str_idx), - ) - } - - fn get_string(&self, index: StringIndex, _lang_id: u16) -> Option<&str> { - (index == self.str_idx).then_some("Reset") - } - - fn control_out(&mut self, xfer: usb_device::class_prelude::ControlOut) { - let req = xfer.request(); - if !(req.request_type == usb_device::control::RequestType::Class - && req.recipient == usb_device::control::Recipient::Interface - && req.index == u8::from(self.intf) as u16) - { - return; - } - - match req.request { - RESET_REQUEST_BOOTSEL => { - let mut gpio_mask = C::BOOTSEL_ACTIVITY_LED.map(|led| 1 << led).unwrap_or(0); - if req.value & 0x100 != 0 { - gpio_mask = 1 << (req.value >> 9); - } - rp2040_hal::rom_data::reset_to_usb_boot( - gpio_mask, - u32::from(req.value & 0x7F) | C::INTERFACE_DISABLE.into(), - ); - // no-need to accept/reject, we'll reset the device anyway - unreachable!() - } - //RESET_REQUEST_FLASH => todo!(), - _ => { - // we are not expecting any other USB OUT requests - let _ = xfer.reject(); - } - } - } - - fn control_in(&mut self, xfer: usb_device::class_prelude::ControlIn) { - let req = xfer.request(); - if !(req.request_type == usb_device::control::RequestType::Class - && req.recipient == usb_device::control::Recipient::Interface - && req.index == u8::from(self.intf) as u16) - { - return; - } - // we are not expecting any USB IN requests - let _ = xfer.reject(); - } -} From 30841c53b60b598a6390a003f272a8babf650f23 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 17 Nov 2024 20:54:13 +0800 Subject: [PATCH 3/4] qtpy: Switch back to Framework VID/PID Depends on: https://github.com/raspberrypi/picotool/pull/177 Signed-off-by: Daniel Schaefer --- qtpy/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/src/main.rs b/qtpy/src/main.rs index 48b747c8..b659da22 100644 --- a/qtpy/src/main.rs +++ b/qtpy/src/main.rs @@ -102,7 +102,7 @@ fn main() -> ! { // Set up the USB Communications Class Device driver let mut serial = SerialPort::new(&usb_bus); - let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x2e8a, 0x000a)) + let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(FRAMEWORK_VID, COMMUNITY_PID)) //.strings(&[StringDescriptors::new(lang_id::ENGLISH_US) // .manufacturer("Adafruit") // .product("QT PY - Framework 16 Inputmodule FW")]) From 70edb66306dc47541a77caa31c4935a8ca9f8137 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 18 Nov 2024 14:29:24 +0800 Subject: [PATCH 4/4] fwupd: Add initial xml and Makefile Signed-off-by: Daniel Schaefer --- fwupd/.gitignore | 1 + fwupd/Makefile | 3 +++ fwupd/firmware.metainfo.xml | 43 +++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 fwupd/.gitignore create mode 100644 fwupd/Makefile create mode 100644 fwupd/firmware.metainfo.xml diff --git a/fwupd/.gitignore b/fwupd/.gitignore new file mode 100644 index 00000000..c2498874 --- /dev/null +++ b/fwupd/.gitignore @@ -0,0 +1 @@ +*.cab diff --git a/fwupd/Makefile b/fwupd/Makefile new file mode 100644 index 00000000..dfc882d4 --- /dev/null +++ b/fwupd/Makefile @@ -0,0 +1,3 @@ +qtpy.cab: firmware.metainfo.xml + cp ../target/thumbv6m-none-eabi/release/qtpy.uf2 . + gcab -c -v $@ qtpy.uf2 firmware.metainfo.xml diff --git a/fwupd/firmware.metainfo.xml b/fwupd/firmware.metainfo.xml new file mode 100644 index 00000000..c511a7ca --- /dev/null +++ b/fwupd/firmware.metainfo.xml @@ -0,0 +1,43 @@ + + + work.frame.inputmodule.qtpy + Framework QT Py + Firmware for the Framework QT Py + +

+ Updating the firmware on your Framework QT Py improves performance and + adds new features. +

+
+ + + 5a5b58d0-9cb4-5f8b-a449-e7b7c820b0cb + + https://github.com/FrameworkComputer/inputmodule-rs + CC0-1.0 + proprietary + + X-Device + + + + + + New firmware! + + + + {{ min_ver }} + org.freedesktop.fwupd + + + + com.microsoft.uf2 + + +
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy