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