usbd_midi/
class.rs

1//! Contains the class implementation.
2
3use usb_device::class_prelude::*;
4use usb_device::Result;
5
6use crate::packet::{UsbMidiEventPacket, UsbMidiEventPacketError};
7
8// Constants for use in descriptors.
9const USB_AUDIO_CLASS: u8 = 0x01;
10const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
11const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;
12const MIDI_IN_JACK_SUBTYPE: u8 = 0x02;
13const MIDI_OUT_JACK_SUBTYPE: u8 = 0x03;
14const EMBEDDED: u8 = 0x01;
15const EXTERNAL: u8 = 0x02;
16const CS_INTERFACE: u8 = 0x24;
17const CS_ENDPOINT: u8 = 0x25;
18const HEADER_SUBTYPE: u8 = 0x01;
19const MS_HEADER_SUBTYPE: u8 = 0x01;
20const MS_GENERAL: u8 = 0x01;
21
22const MIDI_IN_SIZE: u8 = 0x06;
23const MIDI_OUT_SIZE: u8 = 0x09;
24
25/// Size of a single packet in bytes.
26pub const MIDI_PACKET_SIZE: usize = 4;
27
28/// Maximum transfer size of an endpoint.
29pub const MAX_PACKET_SIZE: usize = 64;
30
31/// Packet-level implementation of a USB MIDI device.
32pub struct UsbMidiClass<'a, B: UsbBus> {
33    standard_ac: InterfaceNumber,
34    standard_mc: InterfaceNumber,
35    standard_bulkout: EndpointOut<'a, B>,
36    standard_bulkin: EndpointIn<'a, B>,
37    n_in_jacks: u8,
38    n_out_jacks: u8,
39}
40
41/// Error variants for read operations.
42#[derive(Debug, Clone, Eq, PartialEq)]
43pub enum UsbMidiReadError {
44    /// Parsing of the packet failed.
45    ParsingFailed(UsbMidiEventPacketError),
46    /// USB stack error returned from `usb-device`.
47    UsbError(UsbError),
48}
49
50/// Error returned when passing invalid arguments to `UsbMidiClass::new`.
51#[derive(Debug)]
52pub struct InvalidArguments;
53
54impl<B: UsbBus> UsbMidiClass<'_, B> {
55    /// Creates a new UsbMidiClass with the provided UsbBus and `n_in/out_jacks` embedded input/output jacks
56    /// (or "cables", depending on the terminology).
57    /// Note that a maximum of 16 in and 16 out jacks is supported.
58    pub fn new(
59        alloc: &UsbBusAllocator<B>,
60        n_in_jacks: u8,
61        n_out_jacks: u8,
62    ) -> core::result::Result<UsbMidiClass<'_, B>, InvalidArguments> {
63        if n_in_jacks > 16 || n_out_jacks > 16 {
64            return Err(InvalidArguments);
65        }
66        Ok(UsbMidiClass {
67            standard_ac: alloc.interface(),
68            standard_mc: alloc.interface(),
69            standard_bulkout: alloc.bulk(MAX_PACKET_SIZE as u16),
70            standard_bulkin: alloc.bulk(MAX_PACKET_SIZE as u16),
71            n_in_jacks,
72            n_out_jacks,
73        })
74    }
75
76    /// Sends bytes from a 4-byte buffer and returns either the transferred size or an error.
77    pub fn send_bytes(&mut self, buffer: [u8; 4]) -> Result<usize> {
78        self.standard_bulkin.write(&buffer)
79    }
80
81    /// Sends a `UsbMidiEventPacket` and returns either the transferred size or an error.
82    pub fn send_packet(&mut self, usb_midi: UsbMidiEventPacket) -> Result<usize> {
83        let bytes: [u8; MIDI_PACKET_SIZE] = usb_midi.into();
84        self.standard_bulkin.write(&bytes)
85    }
86
87    /// Reads received bytes into a buffer and returns either the transferred size or an error.
88    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
89        self.standard_bulkout.read(buffer)
90    }
91
92    /// Calculates the index'th external midi in jack id.
93    fn in_jack_id_ext(&self, index: u8) -> u8 {
94        debug_assert!(index < self.n_in_jacks);
95        2 * index + 1
96    }
97
98    /// Calculates the index'th embedded midi out jack id.
99    fn out_jack_id_emb(&self, index: u8) -> u8 {
100        debug_assert!(index < self.n_in_jacks);
101        2 * index + 2
102    }
103
104    /// Calculates the index'th external midi out jack id.
105    fn out_jack_id_ext(&self, index: u8) -> u8 {
106        debug_assert!(index < self.n_out_jacks);
107        2 * self.n_in_jacks + 2 * index + 1
108    }
109
110    /// Calculates the index'th embedded midi in jack id.
111    fn in_jack_id_emb(&self, index: u8) -> u8 {
112        debug_assert!(index < self.n_out_jacks);
113        2 * self.n_in_jacks + 2 * index + 2
114    }
115}
116
117impl<B: UsbBus> UsbClass<B> for UsbMidiClass<'_, B> {
118    fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
119        // AUDIO CONTROL STANDARD
120        writer.interface(
121            self.standard_ac,
122            USB_AUDIO_CLASS,
123            USB_AUDIOCONTROL_SUBCLASS,
124            0, // no protocol,
125        )?;
126
127        // AUDIO CONTROL EXTRA INFO
128        writer.write(
129            CS_INTERFACE,
130            &[
131                HEADER_SUBTYPE,
132                0x00,
133                0x01, // REVISION
134                0x09,
135                0x00, // SIZE of class specific descriptions
136                0x01, // Number of streaming interfaces
137                0x01, // MIDIStreaming interface 1 belongs to this AC interface
138            ],
139        )?;
140
141        // Streaming Standard
142        writer.interface(
143            self.standard_mc,
144            USB_AUDIO_CLASS,
145            USB_MIDISTREAMING_SUBCLASS,
146            0, // no protocol
147        )?; // Num endpoints?
148
149        let midi_streaming_start_byte = writer.position();
150        let midi_streaming_total_length = 7
151            + (self.n_in_jacks + self.n_out_jacks) as usize
152                * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize
153            + if self.n_out_jacks > 0 {
154                9 + (4 + self.n_out_jacks as usize)
155            } else {
156                0
157            }
158            + if self.n_in_jacks > 0 {
159                9 + 4 + self.n_in_jacks as usize
160            } else {
161                0
162            };
163
164        // Streaming extra info
165        writer.write(
166            // len = 7
167            CS_INTERFACE,
168            &[
169                MS_HEADER_SUBTYPE,
170                0x00,
171                0x01, //REVISION
172                (midi_streaming_total_length & 0xFF) as u8,
173                ((midi_streaming_total_length >> 8) & 0xFF) as u8,
174            ],
175        )?;
176
177        // JACKS
178        for i in 0..self.n_in_jacks {
179            writer.write(
180                // len = 6 = MIDI_IN_SIZE
181                CS_INTERFACE,
182                &[
183                    MIDI_IN_JACK_SUBTYPE,
184                    EXTERNAL,
185                    self.in_jack_id_ext(i), // id
186                    0x00,
187                ],
188            )?;
189        }
190
191        for i in 0..self.n_out_jacks {
192            writer.write(
193                // len = 6 = MIDI_IN_SIZE
194                CS_INTERFACE,
195                &[
196                    MIDI_IN_JACK_SUBTYPE,
197                    EMBEDDED,
198                    self.in_jack_id_emb(i), // id
199                    0x00,
200                ],
201            )?;
202        }
203
204        for i in 0..self.n_out_jacks {
205            writer.write(
206                // len = 9 = MIDI_OUT_SIZE
207                CS_INTERFACE,
208                &[
209                    MIDI_OUT_JACK_SUBTYPE,
210                    EXTERNAL,
211                    self.out_jack_id_ext(i), // id
212                    0x01,                    // 1 pin
213                    self.in_jack_id_emb(i),  // pin is connected to this entity...
214                    0x01,                    // ...to the first pin
215                    0x00,
216                ],
217            )?;
218        }
219
220        for i in 0..self.n_in_jacks {
221            writer.write(
222                // len = 9 = MIDI_OUT_SIZE
223                CS_INTERFACE,
224                &[
225                    MIDI_OUT_JACK_SUBTYPE,
226                    EMBEDDED,
227                    self.out_jack_id_emb(i), // id
228                    0x01,                    // 1 pin
229                    self.in_jack_id_ext(i),  // pin is connected to this entity...
230                    0x01,                    // ...to the first pin
231                    0x00,
232                ],
233            )?;
234        }
235
236        let mut endpoint_data = [
237            MS_GENERAL, 0, // number of jacks. must be filled in!
238            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
239            0, // jack mappings. must be filled in and cropped.
240        ];
241
242        if self.n_out_jacks > 0 {
243            writer.endpoint_ex(&self.standard_bulkout, |data| {
244                data[0] = 0; // bRefresh
245                data[1] = 0; // bSynchAddress
246                Ok(2)
247            })?; // len = 9
248
249            endpoint_data[1] = self.n_out_jacks;
250            for i in 0..self.n_out_jacks {
251                endpoint_data[2 + i as usize] = self.in_jack_id_emb(i);
252            }
253            writer.write(
254                // len = 4 + self.n_out_jacks
255                CS_ENDPOINT,
256                &endpoint_data[0..2 + self.n_out_jacks as usize],
257            )?;
258        }
259
260        if self.n_in_jacks > 0 {
261            writer.endpoint_ex(&self.standard_bulkin, |data| {
262                data[0] = 0; // bRefresh
263                data[1] = 0; // bSynchAddress
264                Ok(2)
265            })?; // len = 9
266
267            endpoint_data[1] = self.n_in_jacks;
268            for i in 0..self.n_in_jacks {
269                endpoint_data[2 + i as usize] = self.out_jack_id_emb(i);
270            }
271            writer.write(
272                // len = 4 + self.n_in_jacks
273                CS_ENDPOINT,
274                &endpoint_data[0..2 + self.n_in_jacks as usize],
275            )?;
276        }
277
278        let midi_streaming_end_byte = writer.position();
279        assert!(midi_streaming_end_byte - midi_streaming_start_byte == midi_streaming_total_length);
280
281        Ok(())
282    }
283}