usbd_serial/
cdc_acm.rs

1use core::convert::TryInto;
2use core::mem;
3use usb_device::class_prelude::*;
4use usb_device::descriptor::lang_id::LangID;
5use usb_device::device::DEFAULT_ALTERNATE_SETTING;
6use usb_device::Result;
7
8/// This should be used as `device_class` when building the `UsbDevice`.
9pub const USB_CLASS_CDC: u8 = 0x02;
10
11const USB_CLASS_CDC_DATA: u8 = 0x0a;
12const CDC_SUBCLASS_ACM: u8 = 0x02;
13const CDC_PROTOCOL_NONE: u8 = 0x00;
14
15const CS_INTERFACE: u8 = 0x24;
16const CDC_TYPE_HEADER: u8 = 0x00;
17const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
18const CDC_TYPE_ACM: u8 = 0x02;
19const CDC_TYPE_UNION: u8 = 0x06;
20
21const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
22#[allow(unused)]
23const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
24const REQ_SET_LINE_CODING: u8 = 0x20;
25const REQ_GET_LINE_CODING: u8 = 0x21;
26const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
27
28/// Packet level implementation of a CDC-ACM serial port.
29///
30/// This class can be used directly and it has the least overhead due to directly reading and
31/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
32/// port. The following constraints must be followed if you use this class directly:
33///
34/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the
35///   method will return a `WouldBlock` error if there is no packet to be read.
36/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the
37///   method will return a `WouldBlock` error if the previous packet has not been sent yet.
38/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
39///   host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
40///   can be sent if there is no other data to send. This is because USB bulk transactions must be
41///   terminated with a short packet, even if the bulk endpoint is used for stream-like data.
42pub struct CdcAcmClass<'a, B: UsbBus> {
43    comm_if: InterfaceNumber,
44    comm_if_name: Option<(StringIndex, &'static str)>,
45    comm_ep: EndpointIn<'a, B>,
46    data_if: InterfaceNumber,
47    data_if_name: Option<(StringIndex, &'static str)>,
48    read_ep: EndpointOut<'a, B>,
49    write_ep: EndpointIn<'a, B>,
50    line_coding: LineCoding,
51    dtr: bool,
52    rts: bool,
53}
54
55impl<'a, B: UsbBus> CdcAcmClass<'a, B> {
56    /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
57    /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
58    pub fn new<'alloc: 'a>(
59        alloc: &'alloc UsbBusAllocator<B>,
60        max_packet_size: u16,
61    ) -> CdcAcmClass<'a, B> {
62        Self::new_with_interface_names(alloc, max_packet_size, None, None)
63    }
64
65    /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
66    /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. Additionally,
67    /// this lets you specify optional names for the CDC interfaces, to better organize composite devices.
68    pub fn new_with_interface_names<'alloc: 'a>(
69        alloc: &'alloc UsbBusAllocator<B>,
70        max_packet_size: u16,
71        comm_if_name: Option<&'static str>,
72        data_if_name: Option<&'static str>,
73    ) -> CdcAcmClass<'a, B> {
74        let comm_if_name = comm_if_name.map(|s| (alloc.string(), s));
75        let data_if_name = data_if_name.map(|s| (alloc.string(), s));
76        CdcAcmClass {
77            comm_if: alloc.interface(),
78            comm_if_name,
79            comm_ep: alloc.interrupt(8, 255),
80            data_if: alloc.interface(),
81            data_if_name,
82            read_ep: alloc.bulk(max_packet_size),
83            write_ep: alloc.bulk(max_packet_size),
84            line_coding: LineCoding {
85                stop_bits: StopBits::One,
86                data_bits: 8,
87                parity_type: ParityType::None,
88                data_rate: 9_600,
89            },
90            dtr: false,
91            rts: false,
92        }
93    }
94
95    /// Gets the maximum packet size in bytes.
96    pub fn max_packet_size(&self) -> u16 {
97        // The size is the same for both endpoints.
98        self.read_ep.max_packet_size()
99    }
100
101    /// Gets the current line coding. The line coding contains information that's mainly relevant
102    /// for USB to UART serial port emulators, and can be ignored if not relevant.
103    pub fn line_coding(&self) -> &LineCoding {
104        &self.line_coding
105    }
106
107    /// Gets the DTR (data terminal ready) state
108    pub fn dtr(&self) -> bool {
109        self.dtr
110    }
111
112    /// Gets the RTS (request to send) state
113    pub fn rts(&self) -> bool {
114        self.rts
115    }
116
117    /// Writes a single packet into the IN endpoint.
118    pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> {
119        self.write_ep.write(data)
120    }
121
122    /// Reads a single packet from the OUT endpoint.
123    pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> {
124        self.read_ep.read(data)
125    }
126
127    /// Gets the IN endpoint.
128    pub fn write_ep(&self) -> &EndpointIn<'a, B> {
129        &self.write_ep
130    }
131
132    /// Mutably gets the IN endpoint.
133    pub fn write_ep_mut(&mut self) -> &mut EndpointIn<'a, B> {
134        &mut self.write_ep
135    }
136
137    /// Gets the OUT endpoint.
138    pub fn read_ep(&self) -> &EndpointOut<'a, B> {
139        &self.read_ep
140    }
141
142    /// Mutably gets the OUT endpoint.
143    pub fn read_ep_mut(&mut self) -> &mut EndpointOut<'a, B> {
144        &mut self.read_ep
145    }
146}
147
148impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
149    fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
150        writer.iad(
151            self.comm_if,
152            2,
153            USB_CLASS_CDC,
154            CDC_SUBCLASS_ACM,
155            CDC_PROTOCOL_NONE,
156            None,
157        )?;
158
159        writer.interface_alt(
160            self.comm_if,
161            DEFAULT_ALTERNATE_SETTING,
162            USB_CLASS_CDC,
163            CDC_SUBCLASS_ACM,
164            CDC_PROTOCOL_NONE,
165            self.comm_if_name.map(|n| n.0),
166        )?;
167
168        writer.write(
169            CS_INTERFACE,
170            &[
171                CDC_TYPE_HEADER, // bDescriptorSubtype
172                0x10,
173                0x01, // bcdCDC (1.10)
174            ],
175        )?;
176
177        writer.write(
178            CS_INTERFACE,
179            &[
180                CDC_TYPE_ACM, // bDescriptorSubtype
181                0x00,         // bmCapabilities
182            ],
183        )?;
184
185        writer.write(
186            CS_INTERFACE,
187            &[
188                CDC_TYPE_UNION,      // bDescriptorSubtype
189                self.comm_if.into(), // bControlInterface
190                self.data_if.into(), // bSubordinateInterface
191            ],
192        )?;
193
194        writer.write(
195            CS_INTERFACE,
196            &[
197                CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
198                0x00,                     // bmCapabilities
199                self.data_if.into(),      // bDataInterface
200            ],
201        )?;
202
203        writer.endpoint(&self.comm_ep)?;
204
205        writer.interface_alt(
206            self.data_if,
207            DEFAULT_ALTERNATE_SETTING,
208            USB_CLASS_CDC_DATA,
209            0x00,
210            0x00,
211            self.data_if_name.map(|n| n.0),
212        )?;
213
214        writer.endpoint(&self.write_ep)?;
215        writer.endpoint(&self.read_ep)?;
216
217        Ok(())
218    }
219
220    fn get_string(&self, index: StringIndex, _lang_id: LangID) -> Option<&str> {
221        match (self.comm_if_name, self.data_if_name) {
222            (Some((i, s)), _) if i == index => Some(s),
223            (_, Some((i, s))) if i == index => Some(s),
224            _ => None,
225        }
226    }
227
228    fn reset(&mut self) {
229        self.line_coding = LineCoding::default();
230        self.dtr = false;
231        self.rts = false;
232    }
233
234    fn control_in(&mut self, xfer: ControlIn<B>) {
235        let req = xfer.request();
236
237        if !(req.request_type == control::RequestType::Class
238            && req.recipient == control::Recipient::Interface
239            && req.index == u8::from(self.comm_if) as u16)
240        {
241            return;
242        }
243
244        match req.request {
245            // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
246            REQ_GET_LINE_CODING if req.length == 7 => {
247                xfer.accept(|data| {
248                    data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
249                    data[4] = self.line_coding.stop_bits as u8;
250                    data[5] = self.line_coding.parity_type as u8;
251                    data[6] = self.line_coding.data_bits;
252
253                    Ok(7)
254                })
255                .ok();
256            }
257            _ => {
258                xfer.reject().ok();
259            }
260        }
261    }
262
263    fn control_out(&mut self, xfer: ControlOut<B>) {
264        let req = xfer.request();
265
266        if !(req.request_type == control::RequestType::Class
267            && req.recipient == control::Recipient::Interface
268            && req.index == u8::from(self.comm_if) as u16)
269        {
270            return;
271        }
272
273        match req.request {
274            REQ_SEND_ENCAPSULATED_COMMAND => {
275                // We don't actually support encapsulated commands but pretend we do for standards
276                // compatibility.
277                xfer.accept().ok();
278            }
279            REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
280                self.line_coding.data_rate =
281                    u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
282                self.line_coding.stop_bits = xfer.data()[4].into();
283                self.line_coding.parity_type = xfer.data()[5].into();
284                self.line_coding.data_bits = xfer.data()[6];
285
286                xfer.accept().ok();
287            }
288            REQ_SET_CONTROL_LINE_STATE => {
289                self.dtr = (req.value & 0x0001) != 0;
290                self.rts = (req.value & 0x0002) != 0;
291
292                xfer.accept().ok();
293            }
294            _ => {
295                xfer.reject().ok();
296            }
297        };
298    }
299}
300
301/// Number of stop bits for LineCoding
302#[derive(Copy, Clone, PartialEq, Eq)]
303pub enum StopBits {
304    /// 1 stop bit
305    One = 0,
306
307    /// 1.5 stop bits
308    OnePointFive = 1,
309
310    /// 2 stop bits
311    Two = 2,
312}
313
314impl From<u8> for StopBits {
315    fn from(value: u8) -> Self {
316        if value <= 2 {
317            unsafe { mem::transmute(value) }
318        } else {
319            StopBits::One
320        }
321    }
322}
323
324/// Parity for LineCoding
325#[derive(Copy, Clone, PartialEq, Eq)]
326pub enum ParityType {
327    None = 0,
328    Odd = 1,
329    Even = 2,
330    Mark = 3,
331    Space = 4,
332}
333
334impl From<u8> for ParityType {
335    fn from(value: u8) -> Self {
336        if value <= 4 {
337            unsafe { mem::transmute(value) }
338        } else {
339            ParityType::None
340        }
341    }
342}
343
344/// Line coding parameters
345///
346/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
347/// be ignored if you don't plan to interface with a physical UART.
348pub struct LineCoding {
349    stop_bits: StopBits,
350    data_bits: u8,
351    parity_type: ParityType,
352    data_rate: u32,
353}
354
355impl LineCoding {
356    /// Gets the number of stop bits for UART communication.
357    pub fn stop_bits(&self) -> StopBits {
358        self.stop_bits
359    }
360
361    /// Gets the number of data bits for UART communication.
362    pub fn data_bits(&self) -> u8 {
363        self.data_bits
364    }
365
366    /// Gets the parity type for UART communication.
367    pub fn parity_type(&self) -> ParityType {
368        self.parity_type
369    }
370
371    /// Gets the data rate in bits per second for UART communication.
372    pub fn data_rate(&self) -> u32 {
373        self.data_rate
374    }
375}
376
377impl Default for LineCoding {
378    fn default() -> Self {
379        LineCoding {
380            stop_bits: StopBits::One,
381            data_bits: 8,
382            parity_type: ParityType::None,
383            data_rate: 9_600,
384        }
385    }
386}