aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb-serial/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-09-26 13:00:21 +0200
committerDario Nieuwenhuis <[email protected]>2022-09-26 13:00:21 +0200
commitf27a47a37b59bf3b9079f4d4d5f43caf7b7872f8 (patch)
tree732f73b4da7a2e726203f2876651a2141d9468be /embassy-usb-serial/src
parentf4f58249722bc656a13865e06535d208440c3e4a (diff)
usb: move classes into the `embassy-usb` crate.
Diffstat (limited to 'embassy-usb-serial/src')
-rw-r--r--embassy-usb-serial/src/fmt.rs225
-rw-r--r--embassy-usb-serial/src/lib.rs359
2 files changed, 0 insertions, 584 deletions
diff --git a/embassy-usb-serial/src/fmt.rs b/embassy-usb-serial/src/fmt.rs
deleted file mode 100644
index 066970813..000000000
--- a/embassy-usb-serial/src/fmt.rs
+++ /dev/null
@@ -1,225 +0,0 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs
deleted file mode 100644
index 15c2bb0a7..000000000
--- a/embassy-usb-serial/src/lib.rs
+++ /dev/null
@@ -1,359 +0,0 @@
1#![no_std]
2#![feature(type_alias_impl_trait)]
3
4// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt;
6
7use core::cell::Cell;
8use core::mem::{self, MaybeUninit};
9use core::sync::atomic::{AtomicBool, Ordering};
10
11use embassy_sync::blocking_mutex::CriticalSectionMutex;
12use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request};
13use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
14use embassy_usb::types::*;
15use embassy_usb::Builder;
16
17/// This should be used as `device_class` when building the `UsbDevice`.
18pub const USB_CLASS_CDC: u8 = 0x02;
19
20const USB_CLASS_CDC_DATA: u8 = 0x0a;
21const CDC_SUBCLASS_ACM: u8 = 0x02;
22const CDC_PROTOCOL_NONE: u8 = 0x00;
23
24const CS_INTERFACE: u8 = 0x24;
25const CDC_TYPE_HEADER: u8 = 0x00;
26const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
27const CDC_TYPE_ACM: u8 = 0x02;
28const CDC_TYPE_UNION: u8 = 0x06;
29
30const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
31#[allow(unused)]
32const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
33const REQ_SET_LINE_CODING: u8 = 0x20;
34const REQ_GET_LINE_CODING: u8 = 0x21;
35const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
36
37pub struct State<'a> {
38 control: MaybeUninit<Control<'a>>,
39 shared: ControlShared,
40}
41
42impl<'a> State<'a> {
43 pub fn new() -> Self {
44 Self {
45 control: MaybeUninit::uninit(),
46 shared: Default::default(),
47 }
48 }
49}
50
51/// Packet level implementation of a CDC-ACM serial port.
52///
53/// This class can be used directly and it has the least overhead due to directly reading and
54/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
55/// port. The following constraints must be followed if you use this class directly:
56///
57/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes.
58/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes.
59/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
60/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
61/// can be sent if there is no other data to send. This is because USB bulk transactions must be
62/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
63pub struct CdcAcmClass<'d, D: Driver<'d>> {
64 _comm_ep: D::EndpointIn,
65 _data_if: InterfaceNumber,
66 read_ep: D::EndpointOut,
67 write_ep: D::EndpointIn,
68 control: &'d ControlShared,
69}
70
71struct Control<'a> {
72 shared: &'a ControlShared,
73}
74
75/// Shared data between Control and CdcAcmClass
76struct ControlShared {
77 line_coding: CriticalSectionMutex<Cell<LineCoding>>,
78 dtr: AtomicBool,
79 rts: AtomicBool,
80}
81
82impl Default for ControlShared {
83 fn default() -> Self {
84 ControlShared {
85 dtr: AtomicBool::new(false),
86 rts: AtomicBool::new(false),
87 line_coding: CriticalSectionMutex::new(Cell::new(LineCoding {
88 stop_bits: StopBits::One,
89 data_bits: 8,
90 parity_type: ParityType::None,
91 data_rate: 8_000,
92 })),
93 }
94 }
95}
96
97impl<'a> Control<'a> {
98 fn shared(&mut self) -> &'a ControlShared {
99 self.shared
100 }
101}
102
103impl<'d> ControlHandler for Control<'d> {
104 fn reset(&mut self) {
105 let shared = self.shared();
106 shared.line_coding.lock(|x| x.set(LineCoding::default()));
107 shared.dtr.store(false, Ordering::Relaxed);
108 shared.rts.store(false, Ordering::Relaxed);
109 }
110
111 fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse {
112 match req.request {
113 REQ_SEND_ENCAPSULATED_COMMAND => {
114 // We don't actually support encapsulated commands but pretend we do for standards
115 // compatibility.
116 OutResponse::Accepted
117 }
118 REQ_SET_LINE_CODING if data.len() >= 7 => {
119 let coding = LineCoding {
120 data_rate: u32::from_le_bytes(data[0..4].try_into().unwrap()),
121 stop_bits: data[4].into(),
122 parity_type: data[5].into(),
123 data_bits: data[6],
124 };
125 self.shared().line_coding.lock(|x| x.set(coding));
126 debug!("Set line coding to: {:?}", coding);
127
128 OutResponse::Accepted
129 }
130 REQ_SET_CONTROL_LINE_STATE => {
131 let dtr = (req.value & 0x0001) != 0;
132 let rts = (req.value & 0x0002) != 0;
133
134 let shared = self.shared();
135 shared.dtr.store(dtr, Ordering::Relaxed);
136 shared.rts.store(rts, Ordering::Relaxed);
137 debug!("Set dtr {}, rts {}", dtr, rts);
138
139 OutResponse::Accepted
140 }
141 _ => OutResponse::Rejected,
142 }
143 }
144
145 fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
146 match req.request {
147 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
148 REQ_GET_LINE_CODING if req.length == 7 => {
149 debug!("Sending line coding");
150 let coding = self.shared().line_coding.lock(|x| x.get());
151 assert!(buf.len() >= 7);
152 buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
153 buf[4] = coding.stop_bits as u8;
154 buf[5] = coding.parity_type as u8;
155 buf[6] = coding.data_bits;
156 InResponse::Accepted(&buf[0..7])
157 }
158 _ => InResponse::Rejected,
159 }
160 }
161}
162
163impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
164 /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
165 /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
166 pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self {
167 let control = state.control.write(Control { shared: &state.shared });
168
169 let control_shared = &state.shared;
170
171 assert!(builder.control_buf_len() >= 7);
172
173 let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
174
175 // Control interface
176 let mut iface = func.interface();
177 iface.handler(control);
178 let comm_if = iface.interface_number();
179 let data_if = u8::from(comm_if) + 1;
180 let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
181
182 alt.descriptor(
183 CS_INTERFACE,
184 &[
185 CDC_TYPE_HEADER, // bDescriptorSubtype
186 0x10,
187 0x01, // bcdCDC (1.10)
188 ],
189 );
190 alt.descriptor(
191 CS_INTERFACE,
192 &[
193 CDC_TYPE_ACM, // bDescriptorSubtype
194 0x00, // bmCapabilities
195 ],
196 );
197 alt.descriptor(
198 CS_INTERFACE,
199 &[
200 CDC_TYPE_UNION, // bDescriptorSubtype
201 comm_if.into(), // bControlInterface
202 data_if.into(), // bSubordinateInterface
203 ],
204 );
205 alt.descriptor(
206 CS_INTERFACE,
207 &[
208 CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
209 0x00, // bmCapabilities
210 data_if.into(), // bDataInterface
211 ],
212 );
213
214 let comm_ep = alt.endpoint_interrupt_in(8, 255);
215
216 // Data interface
217 let mut iface = func.interface();
218 let data_if = iface.interface_number();
219 let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE);
220 let read_ep = alt.endpoint_bulk_out(max_packet_size);
221 let write_ep = alt.endpoint_bulk_in(max_packet_size);
222
223 CdcAcmClass {
224 _comm_ep: comm_ep,
225 _data_if: data_if,
226 read_ep,
227 write_ep,
228 control: control_shared,
229 }
230 }
231
232 /// Gets the maximum packet size in bytes.
233 pub fn max_packet_size(&self) -> u16 {
234 // The size is the same for both endpoints.
235 self.read_ep.info().max_packet_size
236 }
237
238 /// Gets the current line coding. The line coding contains information that's mainly relevant
239 /// for USB to UART serial port emulators, and can be ignored if not relevant.
240 pub fn line_coding(&self) -> LineCoding {
241 self.control.line_coding.lock(|x| x.get())
242 }
243
244 /// Gets the DTR (data terminal ready) state
245 pub fn dtr(&self) -> bool {
246 self.control.dtr.load(Ordering::Relaxed)
247 }
248
249 /// Gets the RTS (request to send) state
250 pub fn rts(&self) -> bool {
251 self.control.rts.load(Ordering::Relaxed)
252 }
253
254 /// Writes a single packet into the IN endpoint.
255 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
256 self.write_ep.write(data).await
257 }
258
259 /// Reads a single packet from the OUT endpoint.
260 pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
261 self.read_ep.read(data).await
262 }
263
264 /// Waits for the USB host to enable this interface
265 pub async fn wait_connection(&mut self) {
266 self.read_ep.wait_enabled().await
267 }
268}
269
270/// Number of stop bits for LineCoding
271#[derive(Copy, Clone, Debug, PartialEq, Eq)]
272#[cfg_attr(feature = "defmt", derive(defmt::Format))]
273pub enum StopBits {
274 /// 1 stop bit
275 One = 0,
276
277 /// 1.5 stop bits
278 OnePointFive = 1,
279
280 /// 2 stop bits
281 Two = 2,
282}
283
284impl From<u8> for StopBits {
285 fn from(value: u8) -> Self {
286 if value <= 2 {
287 unsafe { mem::transmute(value) }
288 } else {
289 StopBits::One
290 }
291 }
292}
293
294/// Parity for LineCoding
295#[derive(Copy, Clone, Debug, PartialEq, Eq)]
296#[cfg_attr(feature = "defmt", derive(defmt::Format))]
297pub enum ParityType {
298 None = 0,
299 Odd = 1,
300 Even = 2,
301 Mark = 3,
302 Space = 4,
303}
304
305impl From<u8> for ParityType {
306 fn from(value: u8) -> Self {
307 if value <= 4 {
308 unsafe { mem::transmute(value) }
309 } else {
310 ParityType::None
311 }
312 }
313}
314
315/// Line coding parameters
316///
317/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
318/// be ignored if you don't plan to interface with a physical UART.
319#[derive(Clone, Copy, Debug)]
320#[cfg_attr(feature = "defmt", derive(defmt::Format))]
321pub struct LineCoding {
322 stop_bits: StopBits,
323 data_bits: u8,
324 parity_type: ParityType,
325 data_rate: u32,
326}
327
328impl LineCoding {
329 /// Gets the number of stop bits for UART communication.
330 pub fn stop_bits(&self) -> StopBits {
331 self.stop_bits
332 }
333
334 /// Gets the number of data bits for UART communication.
335 pub fn data_bits(&self) -> u8 {
336 self.data_bits
337 }
338
339 /// Gets the parity type for UART communication.
340 pub fn parity_type(&self) -> ParityType {
341 self.parity_type
342 }
343
344 /// Gets the data rate in bits per second for UART communication.
345 pub fn data_rate(&self) -> u32 {
346 self.data_rate
347 }
348}
349
350impl Default for LineCoding {
351 fn default() -> Self {
352 LineCoding {
353 stop_bits: StopBits::One,
354 data_bits: 8,
355 parity_type: ParityType::None,
356 data_rate: 8_000,
357 }
358 }
359}