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            + 9
154            + (4 + self.n_out_jacks as usize)
155            + 9
156            + (4 + self.n_in_jacks as usize);
157
158        // Streaming extra info
159        writer.write(
160            // len = 7
161            CS_INTERFACE,
162            &[
163                MS_HEADER_SUBTYPE,
164                0x00,
165                0x01, //REVISION
166                (midi_streaming_total_length & 0xFF) as u8,
167                ((midi_streaming_total_length >> 8) & 0xFF) as u8,
168            ],
169        )?;
170
171        // JACKS
172        for i in 0..self.n_in_jacks {
173            writer.write(
174                // len = 6 = MIDI_IN_SIZE
175                CS_INTERFACE,
176                &[
177                    MIDI_IN_JACK_SUBTYPE,
178                    EXTERNAL,
179                    self.in_jack_id_ext(i), // id
180                    0x00,
181                ],
182            )?;
183        }
184
185        for i in 0..self.n_out_jacks {
186            writer.write(
187                // len = 6 = MIDI_IN_SIZE
188                CS_INTERFACE,
189                &[
190                    MIDI_IN_JACK_SUBTYPE,
191                    EMBEDDED,
192                    self.in_jack_id_emb(i), // id
193                    0x00,
194                ],
195            )?;
196        }
197
198        for i in 0..self.n_out_jacks {
199            writer.write(
200                // len = 9 = MIDI_OUT_SIZE
201                CS_INTERFACE,
202                &[
203                    MIDI_OUT_JACK_SUBTYPE,
204                    EXTERNAL,
205                    self.out_jack_id_ext(i), // id
206                    0x01,                    // 1 pin
207                    self.in_jack_id_emb(i),  // pin is connected to this entity...
208                    0x01,                    // ...to the first pin
209                    0x00,
210                ],
211            )?;
212        }
213
214        for i in 0..self.n_in_jacks {
215            writer.write(
216                // len = 9 = MIDI_OUT_SIZE
217                CS_INTERFACE,
218                &[
219                    MIDI_OUT_JACK_SUBTYPE,
220                    EMBEDDED,
221                    self.out_jack_id_emb(i), // id
222                    0x01,                    // 1 pin
223                    self.in_jack_id_ext(i),  // pin is connected to this entity...
224                    0x01,                    // ...to the first pin
225                    0x00,
226                ],
227            )?;
228        }
229
230        let mut endpoint_data = [
231            MS_GENERAL, 0, // number of jacks. must be filled in!
232            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
233            0, // jack mappings. must be filled in and cropped.
234        ];
235
236        writer.endpoint_ex(&self.standard_bulkout, |data| {
237            data[0] = 0; // bRefresh
238            data[1] = 0; // bSynchAddress
239            Ok(2)
240        })?; // len = 9
241
242        endpoint_data[1] = self.n_out_jacks;
243        for i in 0..self.n_out_jacks {
244            endpoint_data[2 + i as usize] = self.in_jack_id_emb(i);
245        }
246        writer.write(
247            // len = 4 + self.n_out_jacks
248            CS_ENDPOINT,
249            &endpoint_data[0..2 + self.n_out_jacks as usize],
250        )?;
251
252        writer.endpoint_ex(&self.standard_bulkin, |data| {
253            data[0] = 0; // bRefresh
254            data[1] = 0; // bSynchAddress
255            Ok(2)
256        })?; // len = 9
257
258        endpoint_data[1] = self.n_in_jacks;
259        for i in 0..self.n_in_jacks {
260            endpoint_data[2 + i as usize] = self.out_jack_id_emb(i);
261        }
262        writer.write(
263            // len = 4 + self.n_in_jacks
264            CS_ENDPOINT,
265            &endpoint_data[0..2 + self.n_in_jacks as usize],
266        )?;
267
268        let midi_streaming_end_byte = writer.position();
269        assert!(midi_streaming_end_byte - midi_streaming_start_byte == midi_streaming_total_length);
270
271        Ok(())
272    }
273}