aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-04-18 10:54:37 +0000
committerGitHub <[email protected]>2025-04-18 10:54:37 +0000
commit7a620661da85c0a4b550abbde7df7e248fe0f155 (patch)
treec5370ee01a06694833d6b63a50e62d7ed2ec4215
parentfce0602dee816ebe1d42bc2482427f88c2dbab7e (diff)
parentb0eacf0eecbef48ac526da6bce250189e4999ce5 (diff)
Merge pull request #4107 from bugadani/cmsisdap
Add CMSIS-DAP driver
-rw-r--r--embassy-usb/src/class/cmsis_dap_v2.rs128
-rw-r--r--embassy-usb/src/class/mod.rs1
2 files changed, 129 insertions, 0 deletions
diff --git a/embassy-usb/src/class/cmsis_dap_v2.rs b/embassy-usb/src/class/cmsis_dap_v2.rs
new file mode 100644
index 000000000..a94e3ddb7
--- /dev/null
+++ b/embassy-usb/src/class/cmsis_dap_v2.rs
@@ -0,0 +1,128 @@
1//! CMSIS-DAP V2 class implementation.
2
3use core::mem::MaybeUninit;
4
5use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
6use crate::types::StringIndex;
7use crate::{msos, Builder, Handler};
8
9/// State for the CMSIS-DAP v2 USB class.
10pub struct State {
11 control: MaybeUninit<Control>,
12}
13
14struct Control {
15 iface_string: StringIndex,
16}
17
18impl Handler for Control {
19 fn get_string(&mut self, index: StringIndex, _lang_id: u16) -> Option<&str> {
20 if index == self.iface_string {
21 Some("CMSIS-DAP v2 Interface")
22 } else {
23 warn!("unknown string index requested");
24 None
25 }
26 }
27}
28
29impl State {
30 /// Create a new `State`.
31 pub const fn new() -> Self {
32 Self {
33 control: MaybeUninit::uninit(),
34 }
35 }
36}
37
38/// USB device class for CMSIS-DAP v2 probes.
39pub struct CmsisDapV2Class<'d, D: Driver<'d>> {
40 read_ep: D::EndpointOut,
41 write_ep: D::EndpointIn,
42 trace_ep: Option<D::EndpointIn>,
43 max_packet_size: u16,
44}
45
46impl<'d, D: Driver<'d>> CmsisDapV2Class<'d, D> {
47 /// Creates a new CmsisDapV2Class with the provided UsbBus and `max_packet_size` in bytes. For
48 /// full-speed devices, `max_packet_size` has to be 64.
49 ///
50 /// The `trace` parameter enables the trace output endpoint. This is optional and can be
51 /// disabled if the probe does not support trace output.
52 pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State, max_packet_size: u16, trace: bool) -> Self {
53 // DAP - Custom Class 0
54 let iface_string = builder.string();
55 let mut function = builder.function(0xFF, 0, 0);
56 function.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
57 function.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
58 "DeviceInterfaceGUIDs",
59 // CMSIS-DAP standard GUID, from https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__ConfigUSB__gr.html
60 msos::PropertyData::RegMultiSz(&["{CDB3B5AD-293B-4663-AA36-1AAE46463776}"]),
61 ));
62 let mut interface = function.interface();
63 let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string));
64 let read_ep = alt.endpoint_bulk_out(max_packet_size);
65 let write_ep = alt.endpoint_bulk_in(max_packet_size);
66 let trace_ep = if trace {
67 Some(alt.endpoint_bulk_in(max_packet_size))
68 } else {
69 None
70 };
71 drop(function);
72
73 builder.handler(state.control.write(Control { iface_string }));
74
75 CmsisDapV2Class {
76 read_ep,
77 write_ep,
78 trace_ep,
79 max_packet_size,
80 }
81 }
82
83 /// Waits for the USB host to enable this interface
84 pub async fn wait_connection(&mut self) {
85 self.read_ep.wait_enabled().await;
86 }
87
88 /// Write data to the host.
89 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
90 for chunk in data.chunks(self.max_packet_size as usize) {
91 self.write_ep.write(chunk).await?;
92 }
93 if data.len() % self.max_packet_size as usize == 0 {
94 self.write_ep.write(&[]).await?;
95 }
96 Ok(())
97 }
98
99 /// Write data to the host via the trace output endpoint.
100 ///
101 /// Returns `EndpointError::Disabled` if the trace output endpoint is not enabled.
102 pub async fn write_trace(&mut self, data: &[u8]) -> Result<(), EndpointError> {
103 let Some(ep) = self.trace_ep.as_mut() else {
104 return Err(EndpointError::Disabled);
105 };
106
107 for chunk in data.chunks(self.max_packet_size as usize) {
108 ep.write(chunk).await?;
109 }
110 if data.len() % self.max_packet_size as usize == 0 {
111 ep.write(&[]).await?;
112 }
113 Ok(())
114 }
115
116 /// Read data from the host.
117 pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
118 let mut n = 0;
119
120 loop {
121 let i = self.read_ep.read(&mut data[n..]).await?;
122 n += i;
123 if i < self.max_packet_size as usize {
124 return Ok(n);
125 }
126 }
127 }
128}
diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs
index 4bd89eb66..c01707971 100644
--- a/embassy-usb/src/class/mod.rs
+++ b/embassy-usb/src/class/mod.rs
@@ -1,6 +1,7 @@
1//! Implementations of well-known USB classes. 1//! Implementations of well-known USB classes.
2pub mod cdc_acm; 2pub mod cdc_acm;
3pub mod cdc_ncm; 3pub mod cdc_ncm;
4pub mod cmsis_dap_v2;
4pub mod hid; 5pub mod hid;
5pub mod midi; 6pub mod midi;
6pub mod uac1; 7pub mod uac1;