1use core::mem::{MaybeUninit, size_of};
18use core::ptr::{addr_of, copy_nonoverlapping};
19
20use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
21use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
22use crate::types::{InterfaceNumber, StringIndex};
23use crate::{Builder, Handler};
24
25pub mod embassy_net;
26
27pub const USB_CLASS_CDC: u8 = 0x02;
29
30const USB_CLASS_CDC_DATA: u8 = 0x0a;
31const CDC_SUBCLASS_NCM: u8 = 0x0d;
32
33const CDC_PROTOCOL_NONE: u8 = 0x00;
34const CDC_PROTOCOL_NTB: u8 = 0x01;
35
36const CS_INTERFACE: u8 = 0x24;
37const CDC_TYPE_HEADER: u8 = 0x00;
38const CDC_TYPE_UNION: u8 = 0x06;
39const CDC_TYPE_ETHERNET: u8 = 0x0F;
40const CDC_TYPE_NCM: u8 = 0x1A;
41
42const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
43const REQ_GET_NTB_PARAMETERS: u8 = 0x80;
50const REQ_SET_NTB_INPUT_SIZE: u8 = 0x86;
56const NTB_MAX_SIZE: usize = 2048;
65const SIG_NTH: u32 = 0x484d_434e;
66const SIG_NDP_NO_FCS: u32 = 0x304d_434e;
67const SIG_NDP_WITH_FCS: u32 = 0x314d_434e;
68
69const ALTERNATE_SETTING_DISABLED: u8 = 0x00;
70const ALTERNATE_SETTING_ENABLED: u8 = 0x01;
71
72#[repr(packed)]
74#[allow(unused)]
75struct NtbOutHeader {
76 nth_sig: u32,
78 nth_len: u16,
79 nth_seq: u16,
80 nth_total_len: u16,
81 nth_first_index: u16,
82
83 ndp_sig: u32,
85 ndp_len: u16,
86 ndp_next_index: u16,
87 ndp_datagram_index: u16,
88 ndp_datagram_len: u16,
89 ndp_term1: u16,
90 ndp_term2: u16,
91}
92
93#[repr(packed)]
94#[allow(unused)]
95struct NtbParameters {
96 length: u16,
97 formats_supported: u16,
98 in_params: NtbParametersDir,
99 out_params: NtbParametersDir,
100}
101
102#[repr(packed)]
103#[allow(unused)]
104struct NtbParametersDir {
105 max_size: u32,
106 divisor: u16,
107 payload_remainder: u16,
108 out_alignment: u16,
109 max_datagram_count: u16,
110}
111
112fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
113 let len = size_of::<T>();
114 unsafe { copy_nonoverlapping(addr_of!(data).cast(), buf.as_mut_ptr(), len) }
115 &buf[..len]
116}
117
118pub struct State<'a> {
120 control: MaybeUninit<Control<'a>>,
121 shared: ControlShared,
122}
123
124impl<'a> Default for State<'a> {
125 fn default() -> Self {
126 Self::new()
127 }
128}
129
130impl<'a> State<'a> {
131 pub fn new() -> Self {
133 Self {
134 control: MaybeUninit::uninit(),
135 shared: ControlShared::default(),
136 }
137 }
138}
139
140#[derive(Default)]
142struct ControlShared {
143 mac_addr: [u8; 6],
144}
145
146struct Control<'a> {
147 mac_addr_string: StringIndex,
148 shared: &'a ControlShared,
149 mac_addr_str: [u8; 12],
150 comm_if: InterfaceNumber,
151 data_if: InterfaceNumber,
152}
153
154impl<'d> Handler for Control<'d> {
155 fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {
156 if iface != self.data_if {
157 return;
158 }
159
160 match alternate_setting {
161 ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"),
162 ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"),
163 _ => unreachable!(),
164 }
165 }
166
167 fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option<OutResponse> {
168 if (req.request_type, req.recipient, req.index)
169 != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
170 {
171 return None;
172 }
173
174 match req.request {
175 REQ_SEND_ENCAPSULATED_COMMAND => {
176 Some(OutResponse::Accepted)
179 }
180 REQ_SET_NTB_INPUT_SIZE => {
181 Some(OutResponse::Accepted)
183 }
184 _ => Some(OutResponse::Rejected),
185 }
186 }
187
188 fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
189 if (req.request_type, req.recipient, req.index)
190 != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
191 {
192 return None;
193 }
194
195 match req.request {
196 REQ_GET_NTB_PARAMETERS => {
197 let res = NtbParameters {
198 length: size_of::<NtbParameters>() as _,
199 formats_supported: 1, in_params: NtbParametersDir {
201 max_size: NTB_MAX_SIZE as _,
202 divisor: 4,
203 payload_remainder: 0,
204 out_alignment: 4,
205 max_datagram_count: 0, },
207 out_params: NtbParametersDir {
208 max_size: NTB_MAX_SIZE as _,
209 divisor: 4,
210 payload_remainder: 0,
211 out_alignment: 4,
212 max_datagram_count: 1, },
214 };
215 Some(InResponse::Accepted(byteify(buf, res)))
216 }
217 _ => Some(InResponse::Rejected),
218 }
219 }
220
221 fn get_string(&mut self, index: StringIndex, _lang_id: u16) -> Option<&str> {
222 if index == self.mac_addr_string {
223 let mac_addr = self.shared.mac_addr;
224 let s = &mut self.mac_addr_str;
225 for i in 0..12 {
226 let n = (mac_addr[i / 2] >> ((1 - i % 2) * 4)) & 0xF;
227 s[i] = match n {
228 0x0..=0x9 => b'0' + n,
229 0xA..=0xF => b'A' + n - 0xA,
230 _ => unreachable!(),
231 }
232 }
233
234 Some(unsafe { core::str::from_utf8_unchecked(s) })
235 } else {
236 warn!("unknown string index requested");
237 None
238 }
239 }
240}
241
242pub struct CdcNcmClass<'d, D: Driver<'d>> {
244 _comm_if: InterfaceNumber,
245 comm_ep: D::EndpointIn,
246
247 data_if: InterfaceNumber,
248 read_ep: D::EndpointOut,
249 write_ep: D::EndpointIn,
250
251 _control: &'d ControlShared,
252
253 max_packet_size: usize,
254}
255
256impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
257 pub fn new(
259 builder: &mut Builder<'d, D>,
260 state: &'d mut State<'d>,
261 mac_address: [u8; 6],
262 max_packet_size: u16,
263 ) -> Self {
264 state.shared.mac_addr = mac_address;
265
266 let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE);
267
268 let mut iface = func.interface();
270 let mac_addr_string = iface.string();
271 let comm_if = iface.interface_number();
272 let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None);
273
274 alt.descriptor(
275 CS_INTERFACE,
276 &[
277 CDC_TYPE_HEADER, 0x10,
279 0x01, ],
281 );
282 alt.descriptor(
283 CS_INTERFACE,
284 &[
285 CDC_TYPE_UNION, comm_if.into(), u8::from(comm_if) + 1, ],
289 );
290 alt.descriptor(
291 CS_INTERFACE,
292 &[
293 CDC_TYPE_ETHERNET, mac_addr_string.into(), 0, 0, 0, 0, 0xea, 0x05, 0, 0, 0, ],
305 );
306 alt.descriptor(
307 CS_INTERFACE,
308 &[
309 CDC_TYPE_NCM, 0x00, 0x01, 0, ],
314 );
315
316 let comm_ep = alt.endpoint_interrupt_in(None, 8, 255);
317
318 let mut iface = func.interface();
320 let data_if = iface.interface_number();
321 let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);
322 let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);
323 let read_ep = alt.endpoint_bulk_out(None, max_packet_size);
324 let write_ep = alt.endpoint_bulk_in(None, max_packet_size);
325
326 drop(func);
327
328 let control = state.control.write(Control {
329 mac_addr_string,
330 shared: &state.shared,
331 mac_addr_str: [0; 12],
332 comm_if,
333 data_if,
334 });
335 builder.handler(control);
336
337 CdcNcmClass {
338 _comm_if: comm_if,
339 comm_ep,
340 data_if,
341 read_ep,
342 write_ep,
343 _control: &state.shared,
344 max_packet_size: max_packet_size as usize,
345 }
346 }
347
348 pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
352 (
353 Sender {
354 write_ep: self.write_ep,
355 seq: 0,
356 max_packet_size: self.max_packet_size,
357 },
358 Receiver {
359 data_if: self.data_if,
360 comm_ep: self.comm_ep,
361 read_ep: self.read_ep,
362 },
363 )
364 }
365}
366
367pub struct Sender<'d, D: Driver<'d>> {
371 write_ep: D::EndpointIn,
372 seq: u16,
373 max_packet_size: usize,
374}
375
376impl<'d, D: Driver<'d>> Sender<'d, D> {
377 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
381 const OUT_HEADER_LEN: usize = 28;
382 const ABS_MAX_PACKET_SIZE: usize = 512;
383
384 let seq = self.seq;
385 self.seq = self.seq.wrapping_add(1);
386
387 let header = NtbOutHeader {
388 nth_sig: SIG_NTH,
389 nth_len: 0x0c,
390 nth_seq: seq,
391 nth_total_len: (data.len() + OUT_HEADER_LEN) as u16,
392 nth_first_index: 0x0c,
393
394 ndp_sig: SIG_NDP_NO_FCS,
395 ndp_len: 0x10,
396 ndp_next_index: 0x00,
397 ndp_datagram_index: OUT_HEADER_LEN as u16,
398 ndp_datagram_len: data.len() as u16,
399 ndp_term1: 0x00,
400 ndp_term2: 0x00,
401 };
402
403 let mut buf = [0; ABS_MAX_PACKET_SIZE];
405 let n = byteify(&mut buf, header);
406 assert_eq!(n.len(), OUT_HEADER_LEN);
407
408 if OUT_HEADER_LEN + data.len() < self.max_packet_size {
409 buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data);
412 self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?;
413 } else {
414 let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN);
415
416 buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1);
417 self.write_ep.write(&buf[..self.max_packet_size]).await?;
418
419 for chunk in d2.chunks(self.max_packet_size) {
420 self.write_ep.write(chunk).await?;
421 }
422
423 if d2.len() % self.max_packet_size == 0 {
425 self.write_ep.write(&[]).await?;
426 }
427 }
428
429 Ok(())
430 }
431}
432
433pub struct Receiver<'d, D: Driver<'d>> {
437 data_if: InterfaceNumber,
438 comm_ep: D::EndpointIn,
439 read_ep: D::EndpointOut,
440}
441
442impl<'d, D: Driver<'d>> Receiver<'d, D> {
443 pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
447 loop {
449 let mut ntb = [0u8; NTB_MAX_SIZE];
451 let mut pos = 0;
452 loop {
453 let n = self.read_ep.read(&mut ntb[pos..]).await?;
454 pos += n;
455 if n < self.read_ep.info().max_packet_size as usize || pos == NTB_MAX_SIZE {
456 break;
457 }
458 }
459
460 let ntb = &ntb[..pos];
461
462 let Some(nth) = ntb.get(..12) else {
464 warn!("Received too short NTB");
465 continue;
466 };
467 let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());
468 if sig != SIG_NTH {
469 warn!("Received bad NTH sig.");
470 continue;
471 }
472 let ndp_idx = u16::from_le_bytes(nth[10..12].try_into().unwrap()) as usize;
473
474 let Some(ndp) = ntb.get(ndp_idx..ndp_idx + 12) else {
476 warn!("NTH has an NDP pointer out of range.");
477 continue;
478 };
479 let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());
480 if sig != SIG_NDP_NO_FCS && sig != SIG_NDP_WITH_FCS {
481 warn!("Received bad NDP sig.");
482 continue;
483 }
484 let datagram_index = u16::from_le_bytes(ndp[8..10].try_into().unwrap()) as usize;
485 let datagram_len = u16::from_le_bytes(ndp[10..12].try_into().unwrap()) as usize;
486
487 if datagram_index == 0 || datagram_len == 0 {
488 continue;
490 }
491
492 let Some(datagram) = ntb.get(datagram_index..datagram_index + datagram_len) else {
494 warn!("NDP has a datagram pointer out of range.");
495 continue;
496 };
497 buf[..datagram_len].copy_from_slice(datagram);
498
499 return Ok(datagram_len);
500 }
501 }
502
503 pub async fn wait_connection(&mut self) -> Result<(), EndpointError> {
505 loop {
506 self.read_ep.wait_enabled().await;
507 self.comm_ep.wait_enabled().await;
508
509 let buf = [
510 0xA1, 0x00, 0x01, 0x00,
514 self.data_if.into(), 0x00,
516 0x00, 0x00,
518 ];
519 match self.comm_ep.write(&buf).await {
520 Ok(()) => break, Err(EndpointError::Disabled) => {} Err(e) => return Err(e),
523 }
524 }
525
526 Ok(())
527 }
528}