diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-04-15 23:17:50 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-16 04:30:31 +0200 |
| commit | 50d257cc7cb6f80118fc47a8c601db958775105c (patch) | |
| tree | 0c1c6c54c60a5cf41ecd2353b92587d454e551f5 /embassy-usb/src/builder.rs | |
| parent | 1bf7b4d6c3baceeee0b64f3844fd31928d029917 (diff) | |
usb: improved descriptor building API
The same API call allocates interfaces/endpoints/etc and writes their descriptors.
This means less API calls, and less possibility to screw things up.
DescriptorWriter is now private.
Diffstat (limited to 'embassy-usb/src/builder.rs')
| -rw-r--r-- | embassy-usb/src/builder.rs | 325 |
1 files changed, 183 insertions, 142 deletions
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 41cbfa940..7e67b4ae6 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -2,7 +2,7 @@ use heapless::Vec; | |||
| 2 | 2 | ||
| 3 | use super::control::ControlHandler; | 3 | use super::control::ControlHandler; |
| 4 | use super::descriptor::{BosWriter, DescriptorWriter}; | 4 | use super::descriptor::{BosWriter, DescriptorWriter}; |
| 5 | use super::driver::{Driver, EndpointAllocError}; | 5 | use super::driver::{Driver, Endpoint}; |
| 6 | use super::types::*; | 6 | use super::types::*; |
| 7 | use super::DeviceStateHandler; | 7 | use super::DeviceStateHandler; |
| 8 | use super::UsbDevice; | 8 | use super::UsbDevice; |
| @@ -117,7 +117,7 @@ impl<'a> Config<'a> { | |||
| 117 | } | 117 | } |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | /// Used to build new [`UsbDevice`]s. | 120 | /// [`UsbDevice`] builder. |
| 121 | pub struct Builder<'d, D: Driver<'d>> { | 121 | pub struct Builder<'d, D: Driver<'d>> { |
| 122 | config: Config<'d>, | 122 | config: Config<'d>, |
| 123 | handler: Option<&'d dyn DeviceStateHandler>, | 123 | handler: Option<&'d dyn DeviceStateHandler>, |
| @@ -128,10 +128,9 @@ pub struct Builder<'d, D: Driver<'d>> { | |||
| 128 | next_interface_number: u8, | 128 | next_interface_number: u8, |
| 129 | next_string_index: u8, | 129 | next_string_index: u8, |
| 130 | 130 | ||
| 131 | // TODO make not pub? | 131 | device_descriptor: DescriptorWriter<'d>, |
| 132 | pub device_descriptor: DescriptorWriter<'d>, | 132 | config_descriptor: DescriptorWriter<'d>, |
| 133 | pub config_descriptor: DescriptorWriter<'d>, | 133 | bos_descriptor: BosWriter<'d>, |
| 134 | pub bos_descriptor: BosWriter<'d>, | ||
| 135 | } | 134 | } |
| 136 | 135 | ||
| 137 | impl<'d, D: Driver<'d>> Builder<'d, D> { | 136 | impl<'d, D: Driver<'d>> Builder<'d, D> { |
| @@ -207,36 +206,12 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 207 | ) | 206 | ) |
| 208 | } | 207 | } |
| 209 | 208 | ||
| 210 | /// Allocates a new interface number. | ||
| 211 | pub fn alloc_interface(&mut self) -> InterfaceNumber { | ||
| 212 | let number = self.next_interface_number; | ||
| 213 | self.next_interface_number += 1; | ||
| 214 | |||
| 215 | InterfaceNumber::new(number) | ||
| 216 | } | ||
| 217 | |||
| 218 | /// Returns the size of the control request data buffer. Can be used by | 209 | /// Returns the size of the control request data buffer. Can be used by |
| 219 | /// classes to validate the buffer is large enough for their needs. | 210 | /// classes to validate the buffer is large enough for their needs. |
| 220 | pub fn control_buf_len(&self) -> usize { | 211 | pub fn control_buf_len(&self) -> usize { |
| 221 | self.control_buf.len() | 212 | self.control_buf.len() |
| 222 | } | 213 | } |
| 223 | 214 | ||
| 224 | /// Allocates a new interface number, with a handler that will be called | ||
| 225 | /// for all the control requests directed to it. | ||
| 226 | pub fn alloc_interface_with_handler( | ||
| 227 | &mut self, | ||
| 228 | handler: &'d mut dyn ControlHandler, | ||
| 229 | ) -> InterfaceNumber { | ||
| 230 | let number = self.next_interface_number; | ||
| 231 | self.next_interface_number += 1; | ||
| 232 | |||
| 233 | if self.interfaces.push((number, handler)).is_err() { | ||
| 234 | panic!("max class count reached") | ||
| 235 | } | ||
| 236 | |||
| 237 | InterfaceNumber::new(number) | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Allocates a new string index. | 215 | /// Allocates a new string index. |
| 241 | pub fn alloc_string(&mut self) -> StringIndex { | 216 | pub fn alloc_string(&mut self) -> StringIndex { |
| 242 | let index = self.next_string_index; | 217 | let index = self.next_string_index; |
| @@ -245,146 +220,212 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 245 | StringIndex::new(index) | 220 | StringIndex::new(index) |
| 246 | } | 221 | } |
| 247 | 222 | ||
| 248 | /// Allocates an in endpoint. | 223 | /// Add an USB function. |
| 249 | /// | 224 | /// |
| 250 | /// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most | 225 | /// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor |
| 251 | /// cases classes should call the endpoint type specific methods instead. | 226 | /// with the given class/subclass/protocol, associating all the child interfaces. |
| 252 | pub fn alloc_endpoint_in( | 227 | /// |
| 228 | /// If it's not set, no IAD descriptor is added. | ||
| 229 | pub fn function( | ||
| 253 | &mut self, | 230 | &mut self, |
| 254 | ep_addr: Option<EndpointAddress>, | 231 | class: u8, |
| 255 | ep_type: EndpointType, | 232 | subclass: u8, |
| 256 | max_packet_size: u16, | 233 | protocol: u8, |
| 257 | interval: u8, | 234 | ) -> FunctionBuilder<'_, 'd, D> { |
| 258 | ) -> Result<D::EndpointIn, EndpointAllocError> { | 235 | let iface_count_index = if self.config.composite_with_iads { |
| 259 | self.driver | 236 | self.config_descriptor.iad( |
| 260 | .alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval) | 237 | InterfaceNumber::new(self.next_interface_number), |
| 238 | 0, | ||
| 239 | class, | ||
| 240 | subclass, | ||
| 241 | protocol, | ||
| 242 | ); | ||
| 243 | |||
| 244 | Some(self.config_descriptor.position() - 5) | ||
| 245 | } else { | ||
| 246 | None | ||
| 247 | }; | ||
| 248 | |||
| 249 | FunctionBuilder { | ||
| 250 | builder: self, | ||
| 251 | iface_count_index, | ||
| 252 | } | ||
| 261 | } | 253 | } |
| 254 | } | ||
| 255 | |||
| 256 | /// Function builder. | ||
| 257 | /// | ||
| 258 | /// A function is a logical grouping of interfaces that perform a given USB function. | ||
| 259 | /// If [`Config::composite_with_iads`] is set, each function will have an IAD descriptor. | ||
| 260 | /// If not, functions will not be visible as descriptors. | ||
| 261 | pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> { | ||
| 262 | builder: &'a mut Builder<'d, D>, | ||
| 263 | iface_count_index: Option<usize>, | ||
| 264 | } | ||
| 262 | 265 | ||
| 263 | /// Allocates an out endpoint. | 266 | impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { |
| 267 | /// Add an interface to the function. | ||
| 264 | /// | 268 | /// |
| 265 | /// This directly delegates to [`Driver::alloc_endpoint_out`], so see that method for details. In most | 269 | /// Interface numbers are guaranteed to be allocated consecutively, starting from 0. |
| 266 | /// cases classes should call the endpoint type specific methods instead. | 270 | pub fn interface( |
| 267 | pub fn alloc_endpoint_out( | ||
| 268 | &mut self, | 271 | &mut self, |
| 269 | ep_addr: Option<EndpointAddress>, | 272 | handler: Option<&'d mut dyn ControlHandler>, |
| 270 | ep_type: EndpointType, | 273 | ) -> InterfaceBuilder<'_, 'd, D> { |
| 271 | max_packet_size: u16, | 274 | if let Some(i) = self.iface_count_index { |
| 272 | interval: u8, | 275 | self.builder.config_descriptor.buf[i] += 1; |
| 273 | ) -> Result<D::EndpointOut, EndpointAllocError> { | 276 | } |
| 274 | self.driver | 277 | |
| 275 | .alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval) | 278 | let number = self.builder.next_interface_number; |
| 279 | self.builder.next_interface_number += 1; | ||
| 280 | |||
| 281 | if let Some(handler) = handler { | ||
| 282 | if self.builder.interfaces.push((number, handler)).is_err() { | ||
| 283 | panic!("max interface count reached") | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | InterfaceBuilder { | ||
| 288 | builder: self.builder, | ||
| 289 | interface_number: InterfaceNumber::new(number), | ||
| 290 | next_alt_setting_number: 0, | ||
| 291 | } | ||
| 276 | } | 292 | } |
| 293 | } | ||
| 277 | 294 | ||
| 278 | /// Allocates a control in endpoint. | 295 | /// Interface builder. |
| 279 | /// | 296 | pub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> { |
| 280 | /// This crate implements the control state machine only for endpoint 0. If classes want to | 297 | builder: &'a mut Builder<'d, D>, |
| 281 | /// support control requests in other endpoints, the state machine must be implemented manually. | 298 | interface_number: InterfaceNumber, |
| 282 | /// This should rarely be needed by classes. | 299 | next_alt_setting_number: u8, |
| 283 | /// | 300 | } |
| 284 | /// # Arguments | 301 | |
| 285 | /// | 302 | impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { |
| 286 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | 303 | /// Get the interface number. |
| 287 | /// | 304 | pub fn interface_number(&self) -> InterfaceNumber { |
| 288 | /// # Panics | 305 | self.interface_number |
| 289 | /// | ||
| 290 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 291 | /// feasibly recoverable. | ||
| 292 | #[inline] | ||
| 293 | pub fn alloc_control_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 294 | self.alloc_endpoint_in(None, EndpointType::Control, max_packet_size, 0) | ||
| 295 | .expect("alloc_ep failed") | ||
| 296 | } | 306 | } |
| 297 | 307 | ||
| 298 | /// Allocates a control out endpoint. | 308 | /// Add an alternate setting to the interface and write its descriptor. |
| 299 | /// | ||
| 300 | /// This crate implements the control state machine only for endpoint 0. If classes want to | ||
| 301 | /// support control requests in other endpoints, the state machine must be implemented manually. | ||
| 302 | /// This should rarely be needed by classes. | ||
| 303 | /// | ||
| 304 | /// # Arguments | ||
| 305 | /// | 309 | /// |
| 306 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | 310 | /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0. |
| 307 | /// | 311 | /// |
| 308 | /// # Panics | 312 | /// The first alternate setting, with number 0, is the default one. |
| 309 | /// | 313 | pub fn alt_setting( |
| 310 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | 314 | &mut self, |
| 311 | /// feasibly recoverable. | 315 | class: u8, |
| 312 | #[inline] | 316 | subclass: u8, |
| 313 | pub fn alloc_control_pipe(&mut self, max_packet_size: u16) -> D::ControlPipe { | 317 | protocol: u8, |
| 314 | self.driver | 318 | ) -> InterfaceAltBuilder<'_, 'd, D> { |
| 315 | .alloc_control_pipe(max_packet_size) | 319 | let number = self.next_alt_setting_number; |
| 316 | .expect("alloc_control_pipe failed") | 320 | self.next_alt_setting_number += 1; |
| 321 | |||
| 322 | self.builder.config_descriptor.interface_alt( | ||
| 323 | self.interface_number, | ||
| 324 | number, | ||
| 325 | class, | ||
| 326 | subclass, | ||
| 327 | protocol, | ||
| 328 | None, | ||
| 329 | ); | ||
| 330 | |||
| 331 | InterfaceAltBuilder { | ||
| 332 | builder: self.builder, | ||
| 333 | interface_number: self.interface_number, | ||
| 334 | alt_setting_number: number, | ||
| 335 | } | ||
| 317 | } | 336 | } |
| 337 | } | ||
| 318 | 338 | ||
| 319 | /// Allocates a bulk in endpoint. | 339 | /// Interface alternate setting builder. |
| 320 | /// | 340 | pub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> { |
| 321 | /// # Arguments | 341 | builder: &'a mut Builder<'d, D>, |
| 322 | /// | 342 | interface_number: InterfaceNumber, |
| 323 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | 343 | alt_setting_number: u8, |
| 324 | /// | 344 | } |
| 325 | /// # Panics | 345 | |
| 326 | /// | 346 | impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { |
| 327 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | 347 | /// Get the interface number. |
| 328 | /// feasibly recoverable. | 348 | pub fn interface_number(&self) -> InterfaceNumber { |
| 329 | #[inline] | 349 | self.interface_number |
| 330 | pub fn alloc_bulk_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 331 | self.alloc_endpoint_in(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 332 | .expect("alloc_ep failed") | ||
| 333 | } | 350 | } |
| 334 | 351 | ||
| 335 | /// Allocates a bulk out endpoint. | 352 | /// Get the alternate setting number. |
| 336 | /// | 353 | pub fn alt_setting_number(&self) -> u8 { |
| 337 | /// # Arguments | 354 | self.alt_setting_number |
| 338 | /// | ||
| 339 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 340 | /// | ||
| 341 | /// # Panics | ||
| 342 | /// | ||
| 343 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 344 | /// feasibly recoverable. | ||
| 345 | #[inline] | ||
| 346 | pub fn alloc_bulk_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | ||
| 347 | self.alloc_endpoint_out(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 348 | .expect("alloc_ep failed") | ||
| 349 | } | 355 | } |
| 350 | 356 | ||
| 351 | /// Allocates a bulk in endpoint. | 357 | /// Add a custom descriptor to this alternate setting. |
| 352 | /// | ||
| 353 | /// # Arguments | ||
| 354 | /// | ||
| 355 | /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes. | ||
| 356 | /// | 358 | /// |
| 357 | /// # Panics | 359 | /// Descriptors are written in the order builder functions are called. Note that some |
| 358 | /// | 360 | /// classes care about the order. |
| 359 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | 361 | pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) { |
| 360 | /// feasibly recoverable. | 362 | self.builder |
| 361 | #[inline] | 363 | .config_descriptor |
| 362 | pub fn alloc_interrupt_endpoint_in( | 364 | .write(descriptor_type, descriptor) |
| 365 | } | ||
| 366 | |||
| 367 | fn endpoint_in( | ||
| 363 | &mut self, | 368 | &mut self, |
| 369 | ep_addr: Option<EndpointAddress>, | ||
| 370 | ep_type: EndpointType, | ||
| 364 | max_packet_size: u16, | 371 | max_packet_size: u16, |
| 365 | interval: u8, | 372 | interval: u8, |
| 366 | ) -> D::EndpointIn { | 373 | ) -> D::EndpointIn { |
| 367 | self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval) | 374 | let ep = self |
| 368 | .expect("alloc_ep failed") | 375 | .builder |
| 376 | .driver | ||
| 377 | .alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval) | ||
| 378 | .expect("alloc_endpoint_in failed"); | ||
| 379 | |||
| 380 | self.builder.config_descriptor.endpoint(ep.info()); | ||
| 381 | |||
| 382 | ep | ||
| 369 | } | 383 | } |
| 370 | 384 | ||
| 371 | /// Allocates a bulk in endpoint. | 385 | fn endpoint_out( |
| 372 | /// | ||
| 373 | /// # Arguments | ||
| 374 | /// | ||
| 375 | /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes. | ||
| 376 | /// | ||
| 377 | /// # Panics | ||
| 378 | /// | ||
| 379 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 380 | /// feasibly recoverable. | ||
| 381 | #[inline] | ||
| 382 | pub fn alloc_interrupt_endpoint_out( | ||
| 383 | &mut self, | 386 | &mut self, |
| 387 | ep_addr: Option<EndpointAddress>, | ||
| 388 | ep_type: EndpointType, | ||
| 384 | max_packet_size: u16, | 389 | max_packet_size: u16, |
| 385 | interval: u8, | 390 | interval: u8, |
| 386 | ) -> D::EndpointOut { | 391 | ) -> D::EndpointOut { |
| 387 | self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval) | 392 | let ep = self |
| 388 | .expect("alloc_ep failed") | 393 | .builder |
| 394 | .driver | ||
| 395 | .alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval) | ||
| 396 | .expect("alloc_endpoint_out failed"); | ||
| 397 | |||
| 398 | self.builder.config_descriptor.endpoint(ep.info()); | ||
| 399 | |||
| 400 | ep | ||
| 401 | } | ||
| 402 | |||
| 403 | /// Allocate a BULK IN endpoint and write its descriptor. | ||
| 404 | /// | ||
| 405 | /// Descriptors are written in the order builder functions are called. Note that some | ||
| 406 | /// classes care about the order. | ||
| 407 | pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 408 | self.endpoint_in(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 409 | } | ||
| 410 | |||
| 411 | /// Allocate a BULK OUT endpoint and write its descriptor. | ||
| 412 | /// | ||
| 413 | /// Descriptors are written in the order builder functions are called. Note that some | ||
| 414 | /// classes care about the order. | ||
| 415 | pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | ||
| 416 | self.endpoint_out(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Allocate a INTERRUPT IN endpoint and write its descriptor. | ||
| 420 | /// | ||
| 421 | /// Descriptors are written in the order builder functions are called. Note that some | ||
| 422 | /// classes care about the order. | ||
| 423 | pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { | ||
| 424 | self.endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval) | ||
| 425 | } | ||
| 426 | |||
| 427 | /// Allocate a INTERRUPT OUT endpoint and write its descriptor. | ||
| 428 | pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { | ||
| 429 | self.endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval) | ||
| 389 | } | 430 | } |
| 390 | } | 431 | } |
