|
| 1 | +//! AUDIO class implementation. |
| 2 | +
|
| 3 | +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 4 | +use crate::Builder; |
| 5 | + |
| 6 | +/// This should be used as `device_class` when building the `UsbDevice`. |
| 7 | +pub const USB_AUDIO_CLASS: u8 = 0x01; |
| 8 | + |
| 9 | +const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01; |
| 10 | +const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03; |
| 11 | +const MIDI_IN_JACK_SUBTYPE: u8 = 0x02; |
| 12 | +const MIDI_OUT_JACK_SUBTYPE: u8 = 0x03; |
| 13 | +const EMBEDDED: u8 = 0x01; |
| 14 | +const EXTERNAL: u8 = 0x02; |
| 15 | +const CS_INTERFACE: u8 = 0x24; |
| 16 | +const CS_ENDPOINT: u8 = 0x25; |
| 17 | +const HEADER_SUBTYPE: u8 = 0x01; |
| 18 | +const MS_HEADER_SUBTYPE: u8 = 0x01; |
| 19 | +const MS_GENERAL: u8 = 0x01; |
| 20 | +const PROTOCOL_NONE: u8 = 0x00; |
| 21 | +const MIDI_IN_SIZE: u8 = 0x06; |
| 22 | +const MIDI_OUT_SIZE: u8 = 0x09; |
| 23 | + |
| 24 | +/// Packet level implementation of a USB AUDIO device. |
| 25 | +/// |
| 26 | +/// This class can be used directly and it has the least overhead due to directly reading and |
| 27 | +/// writing USB packets with no intermediate buffers, but it will not act like a stream-like port. |
| 28 | +/// The following constraints must be followed if you use this class directly: |
| 29 | +/// |
| 30 | +/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes. |
| 31 | +/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes. |
| 32 | +/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the |
| 33 | +/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) |
| 34 | +/// can be sent if there is no other data to send. This is because USB bulk transactions must be |
| 35 | +/// terminated with a short packet, even if the bulk endpoint is used for stream-like data. |
| 36 | +pub struct AudioClass<'d, D: Driver<'d>> { |
| 37 | + read_ep: D::EndpointOut, |
| 38 | + write_ep: D::EndpointIn, |
| 39 | +} |
| 40 | + |
| 41 | +impl<'d, D: Driver<'d>> AudioClass<'d, D> { |
| 42 | + /// Creates a new `AudioClass` with the provided UsbBus, number of input and output jacks and `max_packet_size` in bytes. |
| 43 | + /// For full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64. |
| 44 | + pub fn new(builder: &mut Builder<'d, D>, n_in_jacks: u8, n_out_jacks: u8, max_packet_size: u16) -> Self { |
| 45 | + let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE); |
| 46 | + |
| 47 | + // Audio control interface |
| 48 | + let mut iface = func.interface(); |
| 49 | + let audio_if = iface.interface_number(); |
| 50 | + let midi_if = u8::from(audio_if) + 1; |
| 51 | + let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None); |
| 52 | + alt.descriptor(CS_INTERFACE, &[HEADER_SUBTYPE, 0x00, 0x01, 0x09, 0x00, 0x01, midi_if]); |
| 53 | + |
| 54 | + // MIDIStreaming interface |
| 55 | + let mut iface = func.interface(); |
| 56 | + let _midi_if = iface.interface_number(); |
| 57 | + let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_MIDISTREAMING_SUBCLASS, PROTOCOL_NONE, None); |
| 58 | + |
| 59 | + let midi_streaming_total_length = 7 |
| 60 | + + (n_in_jacks + n_out_jacks) as usize * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize |
| 61 | + + 7 |
| 62 | + + (4 + n_out_jacks as usize) |
| 63 | + + 7 |
| 64 | + + (4 + n_in_jacks as usize); |
| 65 | + |
| 66 | + alt.descriptor( |
| 67 | + CS_INTERFACE, |
| 68 | + &[ |
| 69 | + MS_HEADER_SUBTYPE, |
| 70 | + 0x00, |
| 71 | + 0x01, |
| 72 | + (midi_streaming_total_length & 0xFF) as u8, |
| 73 | + ((midi_streaming_total_length >> 8) & 0xFF) as u8, |
| 74 | + ], |
| 75 | + ); |
| 76 | + |
| 77 | + // Calculates the index'th external midi in jack id |
| 78 | + let in_jack_id_ext = |index| 2 * index + 1; |
| 79 | + // Calculates the index'th embedded midi out jack id |
| 80 | + let out_jack_id_emb = |index| 2 * index + 2; |
| 81 | + // Calculates the index'th external midi out jack id |
| 82 | + let out_jack_id_ext = |index| 2 * n_in_jacks + 2 * index + 1; |
| 83 | + // Calculates the index'th embedded midi in jack id |
| 84 | + let in_jack_id_emb = |index| 2 * n_in_jacks + 2 * index + 2; |
| 85 | + |
| 86 | + for i in 0..n_in_jacks { |
| 87 | + alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EXTERNAL, in_jack_id_ext(i), 0x00]); |
| 88 | + } |
| 89 | + |
| 90 | + for i in 0..n_out_jacks { |
| 91 | + alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EMBEDDED, in_jack_id_emb(i), 0x00]); |
| 92 | + } |
| 93 | + |
| 94 | + for i in 0..n_out_jacks { |
| 95 | + alt.descriptor( |
| 96 | + CS_INTERFACE, |
| 97 | + &[ |
| 98 | + MIDI_OUT_JACK_SUBTYPE, |
| 99 | + EXTERNAL, |
| 100 | + out_jack_id_ext(i), |
| 101 | + 0x01, |
| 102 | + in_jack_id_emb(i), |
| 103 | + 0x01, |
| 104 | + 0x00, |
| 105 | + ], |
| 106 | + ); |
| 107 | + } |
| 108 | + |
| 109 | + for i in 0..n_in_jacks { |
| 110 | + alt.descriptor( |
| 111 | + CS_INTERFACE, |
| 112 | + &[ |
| 113 | + MIDI_OUT_JACK_SUBTYPE, |
| 114 | + EMBEDDED, |
| 115 | + out_jack_id_emb(i), |
| 116 | + 0x01, |
| 117 | + in_jack_id_ext(i), |
| 118 | + 0x01, |
| 119 | + 0x00, |
| 120 | + ], |
| 121 | + ); |
| 122 | + } |
| 123 | + |
| 124 | + let mut endpoint_data = [ |
| 125 | + MS_GENERAL, 0, // Number of jacks |
| 126 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Jack mappings |
| 127 | + ]; |
| 128 | + endpoint_data[1] = n_out_jacks; |
| 129 | + for i in 0..n_out_jacks { |
| 130 | + endpoint_data[2 + i as usize] = in_jack_id_emb(i); |
| 131 | + } |
| 132 | + let read_ep = alt.endpoint_bulk_out(max_packet_size); |
| 133 | + alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]); |
| 134 | + |
| 135 | + endpoint_data[1] = n_in_jacks; |
| 136 | + for i in 0..n_in_jacks { |
| 137 | + endpoint_data[2 + i as usize] = out_jack_id_emb(i); |
| 138 | + } |
| 139 | + let write_ep = alt.endpoint_bulk_in(max_packet_size); |
| 140 | + alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]); |
| 141 | + |
| 142 | + AudioClass { read_ep, write_ep } |
| 143 | + } |
| 144 | + |
| 145 | + /// Gets the maximum packet size in bytes. |
| 146 | + pub fn max_packet_size(&self) -> u16 { |
| 147 | + // The size is the same for both endpoints. |
| 148 | + self.read_ep.info().max_packet_size |
| 149 | + } |
| 150 | + |
| 151 | + /// Writes a single packet into the IN endpoint. |
| 152 | + pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { |
| 153 | + self.write_ep.write(data).await |
| 154 | + } |
| 155 | + |
| 156 | + /// Reads a single packet from the OUT endpoint. |
| 157 | + pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> { |
| 158 | + self.read_ep.read(data).await |
| 159 | + } |
| 160 | + |
| 161 | + /// Waits for the USB host to enable this interface |
| 162 | + pub async fn wait_connection(&mut self) { |
| 163 | + self.read_ep.wait_enabled().await; |
| 164 | + } |
| 165 | + |
| 166 | + /// Split the class into a sender and receiver. |
| 167 | + /// |
| 168 | + /// This allows concurrently sending and receiving packets from separate tasks. |
| 169 | + pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { |
| 170 | + ( |
| 171 | + Sender { |
| 172 | + write_ep: self.write_ep, |
| 173 | + }, |
| 174 | + Receiver { read_ep: self.read_ep }, |
| 175 | + ) |
| 176 | + } |
| 177 | +} |
| 178 | + |
| 179 | +/// Midi class packet sender. |
| 180 | +/// |
| 181 | +/// You can obtain a `Sender` with [`AudioClass::split`] |
| 182 | +pub struct Sender<'d, D: Driver<'d>> { |
| 183 | + write_ep: D::EndpointIn, |
| 184 | +} |
| 185 | + |
| 186 | +impl<'d, D: Driver<'d>> Sender<'d, D> { |
| 187 | + /// Gets the maximum packet size in bytes. |
| 188 | + pub fn max_packet_size(&self) -> u16 { |
| 189 | + // The size is the same for both endpoints. |
| 190 | + self.write_ep.info().max_packet_size |
| 191 | + } |
| 192 | + |
| 193 | + /// Writes a single packet. |
| 194 | + pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { |
| 195 | + self.write_ep.write(data).await |
| 196 | + } |
| 197 | + |
| 198 | + /// Waits for the USB host to enable this interface |
| 199 | + pub async fn wait_connection(&mut self) { |
| 200 | + self.write_ep.wait_enabled().await; |
| 201 | + } |
| 202 | +} |
| 203 | + |
| 204 | +/// Midi class packet receiver. |
| 205 | +/// |
| 206 | +/// You can obtain a `Receiver` with [`AudioClass::split`] |
| 207 | +pub struct Receiver<'d, D: Driver<'d>> { |
| 208 | + read_ep: D::EndpointOut, |
| 209 | +} |
| 210 | + |
| 211 | +impl<'d, D: Driver<'d>> Receiver<'d, D> { |
| 212 | + /// Gets the maximum packet size in bytes. |
| 213 | + pub fn max_packet_size(&self) -> u16 { |
| 214 | + // The size is the same for both endpoints. |
| 215 | + self.read_ep.info().max_packet_size |
| 216 | + } |
| 217 | + |
| 218 | + /// Reads a single packet. |
| 219 | + pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> { |
| 220 | + self.read_ep.read(data).await |
| 221 | + } |
| 222 | + |
| 223 | + /// Waits for the USB host to enable this interface |
| 224 | + pub async fn wait_connection(&mut self) { |
| 225 | + self.read_ep.wait_enabled().await; |
| 226 | + } |
| 227 | +} |
0 commit comments