1use embassy_usb_driver::host::{PipeError, UsbHostAllocator, UsbPipe, pipe};
6use embassy_usb_driver::{Direction as UsbDirection, EndpointAddress, EndpointInfo, EndpointType};
7
8use crate::control::SetupPacket;
9use crate::descriptor::ConfigurationDescriptor;
10use crate::handler::EnumerationInfo;
11
12const USB_CLASS_CDC: u8 = 0x02;
14const USB_CLASS_CDC_DATA: u8 = 0x0A;
16const CDC_SUBCLASS_ACM: u8 = 0x02;
18
19const REQ_SET_LINE_CODING: u8 = 0x20;
21const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
23
24#[derive(Clone, Debug)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub struct LineCoding {
28 pub baud_rate: u32,
30 pub stop_bits: u8,
32 pub parity: u8,
34 pub data_bits: u8,
36}
37
38impl Default for LineCoding {
39 fn default() -> Self {
40 Self {
41 baud_rate: 115200,
42 stop_bits: 0,
43 parity: 0,
44 data_bits: 8,
45 }
46 }
47}
48
49impl LineCoding {
50 fn to_bytes(&self) -> [u8; 7] {
51 let baud = self.baud_rate.to_le_bytes();
52 [
53 baud[0],
54 baud[1],
55 baud[2],
56 baud[3],
57 self.stop_bits,
58 self.parity,
59 self.data_bits,
60 ]
61 }
62}
63
64#[derive(Debug)]
66#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub enum CdcAcmError {
68 Transfer(PipeError),
70 NoInterface,
72 NoPipe,
74}
75
76impl From<PipeError> for CdcAcmError {
77 fn from(e: PipeError) -> Self {
78 Self::Transfer(e)
79 }
80}
81
82impl core::fmt::Display for CdcAcmError {
83 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84 match self {
85 Self::Transfer(_e) => write!(f, "Transfer error"),
86 Self::NoInterface => write!(f, "No CDC ACM interface found"),
87 Self::NoPipe => write!(f, "No free pipe"),
88 }
89 }
90}
91
92impl core::error::Error for CdcAcmError {}
93
94impl embedded_io_async::Error for CdcAcmError {
95 fn kind(&self) -> embedded_io_async::ErrorKind {
96 match self {
97 Self::Transfer(e) => match e {
98 PipeError::Disconnected => embedded_io_async::ErrorKind::NotConnected,
99 PipeError::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,
100 PipeError::Timeout => embedded_io_async::ErrorKind::TimedOut,
101 _ => embedded_io_async::ErrorKind::Other,
102 },
103 Self::NoInterface => embedded_io_async::ErrorKind::NotFound,
104 Self::NoPipe => embedded_io_async::ErrorKind::OutOfMemory,
105 }
106 }
107}
108
109#[derive(Clone, Debug)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112pub struct CdcAcmInfo {
113 pub comm_interface: u8,
115 pub data_interface: u8,
117 pub bulk_in_ep: u8,
119 pub bulk_in_mps: u16,
121 pub bulk_out_ep: u8,
123 pub bulk_out_mps: u16,
125}
126
127pub fn find_cdc_acm(config_desc: &[u8]) -> Option<CdcAcmInfo> {
129 let cfg = ConfigurationDescriptor::try_from_slice(config_desc).ok()?;
130
131 let mut comm_iface: Option<u8> = None;
132 let mut data_iface: Option<u8> = None;
133 let mut bulk_in: Option<(u8, u16)> = None;
134 let mut bulk_out: Option<(u8, u16)> = None;
135
136 for iface in cfg.iter_interface() {
137 if iface.interface_class == USB_CLASS_CDC && iface.interface_subclass == CDC_SUBCLASS_ACM {
138 comm_iface = Some(iface.interface_number);
139 } else if iface.interface_class == USB_CLASS_CDC_DATA {
140 data_iface = Some(iface.interface_number);
141 for ep in iface.iter_endpoints() {
142 if ep.transfer_type() == 0x02 {
143 if ep.is_in() {
145 bulk_in = Some((ep.endpoint_address, ep.max_packet_size));
146 } else {
147 bulk_out = Some((ep.endpoint_address, ep.max_packet_size));
148 }
149 }
150 }
151 }
152 }
153
154 if let (Some(comm), Some(data), Some((in_ep, in_mps)), Some((out_ep, out_mps))) =
155 (comm_iface, data_iface, bulk_in, bulk_out)
156 {
157 Some(CdcAcmInfo {
158 comm_interface: comm,
159 data_interface: data,
160 bulk_in_ep: in_ep,
161 bulk_in_mps: in_mps,
162 bulk_out_ep: out_ep,
163 bulk_out_mps: out_mps,
164 })
165 } else {
166 None
167 }
168}
169
170pub struct CdcAcmHost<'d, A: UsbHostAllocator<'d>> {
174 ctrl_ch: A::Pipe<pipe::Control, pipe::InOut>,
175 in_ch: A::Pipe<pipe::Bulk, pipe::In>,
176 out_ch: A::Pipe<pipe::Bulk, pipe::Out>,
177 comm_interface: u8,
178 _phantom: core::marker::PhantomData<&'d ()>,
179}
180
181impl<'d, A: UsbHostAllocator<'d>> CdcAcmHost<'d, A> {
182 pub fn new(alloc: &A, config_desc: &[u8], enum_info: &EnumerationInfo) -> Result<Self, CdcAcmError> {
186 let info = find_cdc_acm(config_desc).ok_or(CdcAcmError::NoInterface)?;
187
188 let ctrl_ep_info = EndpointInfo {
189 addr: EndpointAddress::from_parts(0, UsbDirection::In),
190 ep_type: EndpointType::Control,
191 max_packet_size: enum_info.device_desc.max_packet_size0 as u16,
192 interval_ms: 0,
193 };
194
195 let in_ep_info = EndpointInfo {
196 addr: EndpointAddress::from_parts((info.bulk_in_ep & 0x0F) as usize, UsbDirection::In),
197 ep_type: EndpointType::Bulk,
198 max_packet_size: info.bulk_in_mps,
199 interval_ms: 0,
200 };
201
202 let out_ep_info = EndpointInfo {
203 addr: EndpointAddress::from_parts((info.bulk_out_ep & 0x0F) as usize, UsbDirection::Out),
204 ep_type: EndpointType::Bulk,
205 max_packet_size: info.bulk_out_mps,
206 interval_ms: 0,
207 };
208
209 let device_address = enum_info.device_address;
210 let split = enum_info.split();
211
212 let ctrl_ch = alloc
213 .alloc_pipe::<pipe::Control, pipe::InOut>(device_address, &ctrl_ep_info, split)
214 .map_err(|_| CdcAcmError::NoPipe)?;
215 let in_ch = alloc
216 .alloc_pipe::<pipe::Bulk, pipe::In>(device_address, &in_ep_info, split)
217 .map_err(|_| CdcAcmError::NoPipe)?;
218 let out_ch = alloc
219 .alloc_pipe::<pipe::Bulk, pipe::Out>(device_address, &out_ep_info, split)
220 .map_err(|_| CdcAcmError::NoPipe)?;
221
222 Ok(Self {
223 ctrl_ch,
224 in_ch,
225 out_ch,
226 comm_interface: info.comm_interface,
227 _phantom: core::marker::PhantomData,
228 })
229 }
230
231 pub async fn set_line_coding(&mut self, coding: &LineCoding) -> Result<(), CdcAcmError> {
233 let data = coding.to_bytes();
234 let setup =
235 SetupPacket::class_interface_out(REQ_SET_LINE_CODING, 0, self.comm_interface as u16, data.len() as u16);
236 self.ctrl_ch.control_out(&setup.to_bytes(), &data).await?;
237 Ok(())
238 }
239
240 pub async fn set_control_line_state(&mut self, dtr: bool, rts: bool) -> Result<(), CdcAcmError> {
242 let value = (dtr as u16) | ((rts as u16) << 1);
243 let setup = SetupPacket::class_interface_out(REQ_SET_CONTROL_LINE_STATE, value, self.comm_interface as u16, 0);
244 self.ctrl_ch.control_out(&setup.to_bytes(), &[]).await?;
245 Ok(())
246 }
247
248 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, CdcAcmError> {
250 let n = self.in_ch.request_in(buf).await?;
251 Ok(n)
252 }
253
254 pub async fn write(&mut self, data: &[u8]) -> Result<usize, CdcAcmError> {
256 self.out_ch.request_out(data, false).await?;
257 Ok(data.len())
258 }
259}
260
261impl<'d, A: UsbHostAllocator<'d>> embedded_io_async::ErrorType for CdcAcmHost<'d, A> {
262 type Error = CdcAcmError;
263}
264
265impl<'d, A: UsbHostAllocator<'d>> embedded_io_async::Read for CdcAcmHost<'d, A> {
266 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
267 CdcAcmHost::read(self, buf).await
268 }
269}
270
271impl<'d, A: UsbHostAllocator<'d>> embedded_io_async::Write for CdcAcmHost<'d, A> {
272 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
273 CdcAcmHost::write(self, buf).await
274 }
275
276 async fn flush(&mut self) -> Result<(), Self::Error> {
277 Ok(())
279 }
280}