embassy-usb: Add MIDI class
This commit is contained in:
		
							
								
								
									
										227
									
								
								embassy-usb/src/class/midi.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								embassy-usb/src/class/midi.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
//! MIDI class implementation.
 | 
			
		||||
 | 
			
		||||
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
 | 
			
		||||
use crate::Builder;
 | 
			
		||||
 | 
			
		||||
/// This should be used as `device_class` when building the `UsbDevice`.
 | 
			
		||||
pub const USB_AUDIO_CLASS: u8 = 0x01;
 | 
			
		||||
 | 
			
		||||
const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
 | 
			
		||||
const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;
 | 
			
		||||
const MIDI_IN_JACK_SUBTYPE: u8 = 0x02;
 | 
			
		||||
const MIDI_OUT_JACK_SUBTYPE: u8 = 0x03;
 | 
			
		||||
const EMBEDDED: u8 = 0x01;
 | 
			
		||||
const EXTERNAL: u8 = 0x02;
 | 
			
		||||
const CS_INTERFACE: u8 = 0x24;
 | 
			
		||||
const CS_ENDPOINT: u8 = 0x25;
 | 
			
		||||
const HEADER_SUBTYPE: u8 = 0x01;
 | 
			
		||||
const MS_HEADER_SUBTYPE: u8 = 0x01;
 | 
			
		||||
const MS_GENERAL: u8 = 0x01;
 | 
			
		||||
const PROTOCOL_NONE: u8 = 0x00;
 | 
			
		||||
const MIDI_IN_SIZE: u8 = 0x06;
 | 
			
		||||
const MIDI_OUT_SIZE: u8 = 0x09;
 | 
			
		||||
 | 
			
		||||
/// Packet level implementation of a USB MIDI device.
 | 
			
		||||
///
 | 
			
		||||
/// This class can be used directly and it has the least overhead due to directly reading and
 | 
			
		||||
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like port.
 | 
			
		||||
/// The following constraints must be followed if you use this class directly:
 | 
			
		||||
///
 | 
			
		||||
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes.
 | 
			
		||||
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes.
 | 
			
		||||
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
 | 
			
		||||
///   host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
 | 
			
		||||
///   can be sent if there is no other data to send. This is because USB bulk transactions must be
 | 
			
		||||
///   terminated with a short packet, even if the bulk endpoint is used for stream-like data.
 | 
			
		||||
pub struct MidiClass<'d, D: Driver<'d>> {
 | 
			
		||||
    read_ep: D::EndpointOut,
 | 
			
		||||
    write_ep: D::EndpointIn,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, D: Driver<'d>> MidiClass<'d, D> {
 | 
			
		||||
    /// Creates a new MidiClass with the provided UsbBus, number of input and output jacks and max_packet_size in bytes.
 | 
			
		||||
    /// For full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
 | 
			
		||||
    pub fn new(builder: &mut Builder<'d, D>, n_in_jacks: u8, n_out_jacks: u8, max_packet_size: u16) -> Self {
 | 
			
		||||
        let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);
 | 
			
		||||
 | 
			
		||||
        // Audio control interface
 | 
			
		||||
        let mut iface = func.interface();
 | 
			
		||||
        let audio_if = iface.interface_number();
 | 
			
		||||
        let midi_if = u8::from(audio_if) + 1;
 | 
			
		||||
        let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);
 | 
			
		||||
        alt.descriptor(CS_INTERFACE, &[HEADER_SUBTYPE, 0x00, 0x01, 0x09, 0x00, 0x01, midi_if]);
 | 
			
		||||
 | 
			
		||||
        // MIDIStreaming interface
 | 
			
		||||
        let mut iface = func.interface();
 | 
			
		||||
        let _midi_if = iface.interface_number();
 | 
			
		||||
        let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_MIDISTREAMING_SUBCLASS, PROTOCOL_NONE, None);
 | 
			
		||||
 | 
			
		||||
        let midi_streaming_total_length = 7
 | 
			
		||||
            + (n_in_jacks + n_out_jacks) as usize * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize
 | 
			
		||||
            + 7
 | 
			
		||||
            + (4 + n_out_jacks as usize)
 | 
			
		||||
            + 7
 | 
			
		||||
            + (4 + n_in_jacks as usize);
 | 
			
		||||
 | 
			
		||||
        alt.descriptor(
 | 
			
		||||
            CS_INTERFACE,
 | 
			
		||||
            &[
 | 
			
		||||
                MS_HEADER_SUBTYPE,
 | 
			
		||||
                0x00,
 | 
			
		||||
                0x01,
 | 
			
		||||
                (midi_streaming_total_length & 0xFF) as u8,
 | 
			
		||||
                ((midi_streaming_total_length >> 8) & 0xFF) as u8,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Calculates the index'th external midi in jack id
 | 
			
		||||
        let in_jack_id_ext = |index| 2 * index + 1;
 | 
			
		||||
        // Calculates the index'th embedded midi out jack id
 | 
			
		||||
        let out_jack_id_emb = |index| 2 * index + 2;
 | 
			
		||||
        // Calculates the index'th external midi out jack id
 | 
			
		||||
        let out_jack_id_ext = |index| 2 * n_in_jacks + 2 * index + 1;
 | 
			
		||||
        // Calculates the index'th embedded midi in jack id
 | 
			
		||||
        let in_jack_id_emb = |index| 2 * n_in_jacks + 2 * index + 2;
 | 
			
		||||
 | 
			
		||||
        for i in 0..n_in_jacks {
 | 
			
		||||
            alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EXTERNAL, in_jack_id_ext(i), 0x00]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for i in 0..n_out_jacks {
 | 
			
		||||
            alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EMBEDDED, in_jack_id_emb(i), 0x00]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for i in 0..n_out_jacks {
 | 
			
		||||
            alt.descriptor(
 | 
			
		||||
                CS_INTERFACE,
 | 
			
		||||
                &[
 | 
			
		||||
                    MIDI_OUT_JACK_SUBTYPE,
 | 
			
		||||
                    EXTERNAL,
 | 
			
		||||
                    out_jack_id_ext(i),
 | 
			
		||||
                    0x01,
 | 
			
		||||
                    in_jack_id_emb(i),
 | 
			
		||||
                    0x01,
 | 
			
		||||
                    0x00,
 | 
			
		||||
                ],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for i in 0..n_in_jacks {
 | 
			
		||||
            alt.descriptor(
 | 
			
		||||
                CS_INTERFACE,
 | 
			
		||||
                &[
 | 
			
		||||
                    MIDI_OUT_JACK_SUBTYPE,
 | 
			
		||||
                    EMBEDDED,
 | 
			
		||||
                    out_jack_id_emb(i),
 | 
			
		||||
                    0x01,
 | 
			
		||||
                    in_jack_id_ext(i),
 | 
			
		||||
                    0x01,
 | 
			
		||||
                    0x00,
 | 
			
		||||
                ],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut endpoint_data = [
 | 
			
		||||
            MS_GENERAL, 0, // Number of jacks
 | 
			
		||||
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Jack mappings
 | 
			
		||||
        ];
 | 
			
		||||
        endpoint_data[1] = n_out_jacks;
 | 
			
		||||
        for i in 0..n_out_jacks {
 | 
			
		||||
            endpoint_data[2 + i as usize] = in_jack_id_emb(i);
 | 
			
		||||
        }
 | 
			
		||||
        let read_ep = alt.endpoint_bulk_out(max_packet_size);
 | 
			
		||||
        alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]);
 | 
			
		||||
 | 
			
		||||
        endpoint_data[1] = n_in_jacks;
 | 
			
		||||
        for i in 0..n_in_jacks {
 | 
			
		||||
            endpoint_data[2 + i as usize] = out_jack_id_emb(i);
 | 
			
		||||
        }
 | 
			
		||||
        let write_ep = alt.endpoint_bulk_in(max_packet_size);
 | 
			
		||||
        alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]);
 | 
			
		||||
 | 
			
		||||
        MidiClass { read_ep, write_ep }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets the maximum packet size in bytes.
 | 
			
		||||
    pub fn max_packet_size(&self) -> u16 {
 | 
			
		||||
        // The size is the same for both endpoints.
 | 
			
		||||
        self.read_ep.info().max_packet_size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Writes a single packet into the IN endpoint.
 | 
			
		||||
    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
 | 
			
		||||
        self.write_ep.write(data).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Reads a single packet from the OUT endpoint.
 | 
			
		||||
    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
 | 
			
		||||
        self.read_ep.read(data).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Waits for the USB host to enable this interface
 | 
			
		||||
    pub async fn wait_connection(&mut self) {
 | 
			
		||||
        self.read_ep.wait_enabled().await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Split the class into a sender and receiver.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This allows concurrently sending and receiving packets from separate tasks.
 | 
			
		||||
    pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
 | 
			
		||||
        (
 | 
			
		||||
            Sender {
 | 
			
		||||
                write_ep: self.write_ep,
 | 
			
		||||
            },
 | 
			
		||||
            Receiver { read_ep: self.read_ep },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Midi class packet sender.
 | 
			
		||||
///
 | 
			
		||||
/// You can obtain a `Sender` with [`MidiClass::split`]
 | 
			
		||||
pub struct Sender<'d, D: Driver<'d>> {
 | 
			
		||||
    write_ep: D::EndpointIn,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, D: Driver<'d>> Sender<'d, D> {
 | 
			
		||||
    /// Gets the maximum packet size in bytes.
 | 
			
		||||
    pub fn max_packet_size(&self) -> u16 {
 | 
			
		||||
        // The size is the same for both endpoints.
 | 
			
		||||
        self.write_ep.info().max_packet_size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Writes a single packet.
 | 
			
		||||
    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
 | 
			
		||||
        self.write_ep.write(data).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Waits for the USB host to enable this interface
 | 
			
		||||
    pub async fn wait_connection(&mut self) {
 | 
			
		||||
        self.write_ep.wait_enabled().await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Midi class packet receiver.
 | 
			
		||||
///
 | 
			
		||||
/// You can obtain a `Receiver` with [`MidiClass::split`]
 | 
			
		||||
pub struct Receiver<'d, D: Driver<'d>> {
 | 
			
		||||
    read_ep: D::EndpointOut,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, D: Driver<'d>> Receiver<'d, D> {
 | 
			
		||||
    /// Gets the maximum packet size in bytes.
 | 
			
		||||
    pub fn max_packet_size(&self) -> u16 {
 | 
			
		||||
        // The size is the same for both endpoints.
 | 
			
		||||
        self.read_ep.info().max_packet_size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Reads a single packet.
 | 
			
		||||
    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
 | 
			
		||||
        self.read_ep.read(data).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Waits for the USB host to enable this interface
 | 
			
		||||
    pub async fn wait_connection(&mut self) {
 | 
			
		||||
        self.read_ep.wait_enabled().await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,3 +2,4 @@
 | 
			
		||||
pub mod cdc_acm;
 | 
			
		||||
pub mod cdc_ncm;
 | 
			
		||||
pub mod hid;
 | 
			
		||||
pub mod midi;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user