usb: docs
This commit is contained in:
		| @@ -235,7 +235,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | ||||
|         let index = self.alloc_in.allocate(ep_type)?; | ||||
|         let ep_addr = EndpointAddress::from_parts(index, Direction::In); | ||||
| @@ -243,7 +243,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> | ||||
|             addr: ep_addr, | ||||
|             ep_type, | ||||
|             max_packet_size: packet_size, | ||||
|             interval, | ||||
|             interval_ms, | ||||
|         })) | ||||
|     } | ||||
|  | ||||
| @@ -251,7 +251,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | ||||
|         let index = self.alloc_out.allocate(ep_type)?; | ||||
|         let ep_addr = EndpointAddress::from_parts(index, Direction::Out); | ||||
| @@ -259,7 +259,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> | ||||
|             addr: ep_addr, | ||||
|             ep_type, | ||||
|             max_packet_size: packet_size, | ||||
|             interval, | ||||
|             interval_ms, | ||||
|         })) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -194,13 +194,13 @@ impl<'d, T: Instance> Driver<'d, T> { | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { | ||||
|         trace!( | ||||
|             "allocating type={:?} mps={:?} interval={}, dir={:?}", | ||||
|             "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", | ||||
|             ep_type, | ||||
|             max_packet_size, | ||||
|             interval, | ||||
|             interval_ms, | ||||
|             D::dir() | ||||
|         ); | ||||
|  | ||||
| @@ -281,7 +281,7 @@ impl<'d, T: Instance> Driver<'d, T> { | ||||
|                 addr: EndpointAddress::from_parts(index, D::dir()), | ||||
|                 ep_type, | ||||
|                 max_packet_size, | ||||
|                 interval, | ||||
|                 interval_ms, | ||||
|             }, | ||||
|             buf, | ||||
|         }) | ||||
| @@ -298,18 +298,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval) | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     fn alloc_endpoint_out( | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval) | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | ||||
|   | ||||
| @@ -268,13 +268,13 @@ impl<'d, T: Instance> Driver<'d, T> { | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { | ||||
|         trace!( | ||||
|             "allocating type={:?} mps={:?} interval={}, dir={:?}", | ||||
|             "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", | ||||
|             ep_type, | ||||
|             max_packet_size, | ||||
|             interval, | ||||
|             interval_ms, | ||||
|             D::dir() | ||||
|         ); | ||||
|  | ||||
| @@ -345,7 +345,7 @@ impl<'d, T: Instance> Driver<'d, T> { | ||||
|                 addr: EndpointAddress::from_parts(index, D::dir()), | ||||
|                 ep_type, | ||||
|                 max_packet_size, | ||||
|                 interval, | ||||
|                 interval_ms, | ||||
|             }, | ||||
|             buf, | ||||
|         }) | ||||
| @@ -362,18 +362,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval) | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     fn alloc_endpoint_out( | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval) | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | ||||
|   | ||||
| @@ -217,13 +217,13 @@ impl<'d, T: Instance> Driver<'d, T> { | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Endpoint<'d, T, D>, EndpointAllocError> { | ||||
|         trace!( | ||||
|             "allocating type={:?} mps={:?} interval={}, dir={:?}", | ||||
|             "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", | ||||
|             ep_type, | ||||
|             max_packet_size, | ||||
|             interval, | ||||
|             interval_ms, | ||||
|             D::dir() | ||||
|         ); | ||||
|  | ||||
| @@ -292,7 +292,7 @@ impl<'d, T: Instance> Driver<'d, T> { | ||||
|                 addr: EndpointAddress::from_parts(index, D::dir()), | ||||
|                 ep_type, | ||||
|                 max_packet_size, | ||||
|                 interval, | ||||
|                 interval_ms, | ||||
|             }, | ||||
|         }) | ||||
|     } | ||||
| @@ -308,18 +308,18 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointIn, EndpointAllocError> { | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval) | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     fn alloc_endpoint_out( | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointOut, EndpointAllocError> { | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval) | ||||
|         self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | ||||
|   | ||||
							
								
								
									
										32
									
								
								embassy-usb-driver/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								embassy-usb-driver/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| # embassy-usb-driver | ||||
|  | ||||
| This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these | ||||
| traits to add support for using `embassy-usb` for a given chip/platform. | ||||
|  | ||||
| The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`] | ||||
| APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used | ||||
| with the newer `embassy-usb` without needing updates. | ||||
|  | ||||
| If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate | ||||
| instead of this one. | ||||
|  | ||||
| [`embassy-usb`]: https://crates.io/crates/embassy-usb | ||||
|  | ||||
| ## Interoperability | ||||
|  | ||||
| This crate can run on any executor. | ||||
|  | ||||
| ## Minimum supported Rust version (MSRV) | ||||
|  | ||||
| This crate requires nightly Rust, due to using "async fn in trait" support. | ||||
|  | ||||
| ## License | ||||
|  | ||||
| This work is licensed under either of | ||||
|  | ||||
| - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or | ||||
|   <http://www.apache.org/licenses/LICENSE-2.0>) | ||||
| - MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>) | ||||
|  | ||||
| at your option. | ||||
|  | ||||
| @@ -1,6 +1,8 @@ | ||||
| #![no_std] | ||||
| #![feature(async_fn_in_trait)] | ||||
| #![allow(incomplete_features)] | ||||
| #![doc = include_str!("../README.md")] | ||||
| #![warn(missing_docs)] | ||||
|  | ||||
| /// Direction of USB traffic. Note that in the USB standard the direction is always indicated from | ||||
| /// the perspective of the host, which is backward for devices, but the standard directions are used | ||||
| @@ -95,46 +97,65 @@ impl EndpointAddress { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Infomation for an endpoint. | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub struct EndpointInfo { | ||||
|     /// Endpoint's address. | ||||
|     pub addr: EndpointAddress, | ||||
|     /// Endpoint's type. | ||||
|     pub ep_type: EndpointType, | ||||
|     /// Max packet size, in bytes. | ||||
|     pub max_packet_size: u16, | ||||
|     pub interval: u8, | ||||
|     /// Polling interval, in milliseconds. | ||||
|     pub interval_ms: u8, | ||||
| } | ||||
|  | ||||
| /// Driver for a specific USB peripheral. Implement this to add support for a new hardware | ||||
| /// platform. | ||||
| /// Main USB driver trait. | ||||
| /// | ||||
| /// Implement this to add support for a new hardware platform. | ||||
| pub trait Driver<'a> { | ||||
|     /// Type of the OUT endpoints for this driver. | ||||
|     type EndpointOut: EndpointOut + 'a; | ||||
|     /// Type of the IN endpoints for this driver. | ||||
|     type EndpointIn: EndpointIn + 'a; | ||||
|     /// Type of the control pipe for this driver. | ||||
|     type ControlPipe: ControlPipe + 'a; | ||||
|     /// Type for bus control for this driver. | ||||
|     type Bus: Bus + 'a; | ||||
|  | ||||
|     /// Allocates an endpoint and specified endpoint parameters. This method is called by the device | ||||
|     /// and class implementations to allocate endpoints, and can only be called before | ||||
|     /// [`start`](Self::start) is called. | ||||
|     /// Allocates an OUT endpoint. | ||||
|     /// | ||||
|     /// This method is called by the USB stack to allocate endpoints. | ||||
|     /// It can only be called before [`start`](Self::start) is called. | ||||
|     /// | ||||
|     /// # Arguments | ||||
|     /// | ||||
|     /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should | ||||
|     ///   attempt to return an endpoint with the specified address. If None, the implementation | ||||
|     ///   should return the next available one. | ||||
|     /// * `ep_type` - the endpoint's type. | ||||
|     /// * `max_packet_size` - Maximum packet size in bytes. | ||||
|     /// * `interval` - Polling interval parameter for interrupt endpoints. | ||||
|     /// * `interval_ms` - Polling interval parameter for interrupt endpoints. | ||||
|     fn alloc_endpoint_out( | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointOut, EndpointAllocError>; | ||||
|  | ||||
|     /// Allocates an IN endpoint. | ||||
|     /// | ||||
|     /// This method is called by the USB stack to allocate endpoints. | ||||
|     /// It can only be called before [`start`](Self::start) is called. | ||||
|     /// | ||||
|     /// # Arguments | ||||
|     /// | ||||
|     /// * `ep_type` - the endpoint's type. | ||||
|     /// * `max_packet_size` - Maximum packet size in bytes. | ||||
|     /// * `interval_ms` - Polling interval parameter for interrupt endpoints. | ||||
|     fn alloc_endpoint_in( | ||||
|         &mut self, | ||||
|         ep_type: EndpointType, | ||||
|         max_packet_size: u16, | ||||
|         interval: u8, | ||||
|         interval_ms: u8, | ||||
|     ) -> Result<Self::EndpointIn, EndpointAllocError>; | ||||
|  | ||||
|     /// Start operation of the USB device. | ||||
| @@ -146,35 +167,37 @@ pub trait Driver<'a> { | ||||
|     /// This consumes the `Driver` instance, so it's no longer possible to allocate more | ||||
|     /// endpoints. | ||||
|     fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe); | ||||
|  | ||||
|     /// Indicates that `set_device_address` must be called before accepting the corresponding | ||||
|     /// control transfer, not after. | ||||
|     /// | ||||
|     /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6 | ||||
|     const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false; | ||||
| } | ||||
|  | ||||
| /// USB bus trait. | ||||
| /// | ||||
| /// This trait provides methods that act on the whole bus. It is kept owned by | ||||
| /// the main USB task, and used to manage the bus. | ||||
| pub trait Bus { | ||||
|     /// Enables the USB peripheral. Soon after enabling the device will be reset, so | ||||
|     /// there is no need to perform a USB reset in this method. | ||||
|     /// Enable the USB peripheral. | ||||
|     async fn enable(&mut self); | ||||
|  | ||||
|     /// Disables and powers down the USB peripheral. | ||||
|     /// Disable and powers down the USB peripheral. | ||||
|     async fn disable(&mut self); | ||||
|  | ||||
|     /// Wait for a bus-related event. | ||||
|     /// | ||||
|     /// This method should asynchronously wait for an event to happen, then | ||||
|     /// return it. See [`Event`] for the list of events this method should return. | ||||
|     async fn poll(&mut self) -> Event; | ||||
|  | ||||
|     /// Enables or disables an endpoint. | ||||
|     /// Enable or disable an endpoint. | ||||
|     fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); | ||||
|  | ||||
|     /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | ||||
|     /// should be prepared to receive data again. Only used during control transfers. | ||||
|     /// Set or clear the STALL condition for an endpoint. | ||||
|     /// | ||||
|     /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. | ||||
|     fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); | ||||
|  | ||||
|     /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. | ||||
|     /// Get whether the STALL condition is set for an endpoint. | ||||
|     fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; | ||||
|  | ||||
|     /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the | ||||
|     /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the | ||||
|     /// device. | ||||
|     /// | ||||
|     /// The default implementation just returns `Unsupported`. | ||||
| @@ -187,7 +210,7 @@ pub trait Bus { | ||||
|         Err(Unsupported) | ||||
|     } | ||||
|  | ||||
|     /// Initiates a remote wakeup of the host by the device. | ||||
|     /// Initiate a remote wakeup of the host by the device. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
| @@ -196,25 +219,27 @@ pub trait Bus { | ||||
|     async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; | ||||
| } | ||||
|  | ||||
| /// Endpoint trait, common for OUT and IN. | ||||
| pub trait Endpoint { | ||||
|     /// Get the endpoint address | ||||
|     fn info(&self) -> &EndpointInfo; | ||||
|  | ||||
|     /// Waits for the endpoint to be enabled. | ||||
|     /// Wait for the endpoint to be enabled. | ||||
|     async fn wait_enabled(&mut self); | ||||
| } | ||||
|  | ||||
| /// OUT Endpoint trait. | ||||
| pub trait EndpointOut: Endpoint { | ||||
|     /// Reads a single packet of data from the endpoint, and returns the actual length of | ||||
|     /// Read a single packet of data from the endpoint, and return the actual length of | ||||
|     /// the packet. | ||||
|     /// | ||||
|     /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. | ||||
|     async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; | ||||
| } | ||||
|  | ||||
| /// Trait for USB control pipe. | ||||
| /// USB control pipe trait. | ||||
| /// | ||||
| /// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single | ||||
| /// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single | ||||
| /// unit, and manages them together to implement the control pipe state machine. | ||||
| /// | ||||
| /// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that | ||||
| @@ -232,6 +257,14 @@ pub trait EndpointOut: Endpoint { | ||||
| /// accept() or reject() | ||||
| /// ``` | ||||
| /// | ||||
| /// - control out for setting the device address: | ||||
| /// | ||||
| /// ```not_rust | ||||
| /// setup() | ||||
| /// (...processing...) | ||||
| /// accept_set_address(addr) or reject() | ||||
| /// ``` | ||||
| /// | ||||
| /// - control out with len != 0: | ||||
| /// | ||||
| /// ```not_rust | ||||
| @@ -280,26 +313,26 @@ pub trait ControlPipe { | ||||
|     /// Maximum packet size for the control pipe | ||||
|     fn max_packet_size(&self) -> usize; | ||||
|  | ||||
|     /// Reads a single setup packet from the endpoint. | ||||
|     /// Read a single setup packet from the endpoint. | ||||
|     async fn setup(&mut self) -> [u8; 8]; | ||||
|  | ||||
|     /// Reads a DATA OUT packet into `buf` in response to a control write request. | ||||
|     /// Read a DATA OUT packet into `buf` in response to a control write request. | ||||
|     /// | ||||
|     /// Must be called after `setup()` for requests with `direction` of `Out` | ||||
|     /// and `length` greater than zero. | ||||
|     async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>; | ||||
|  | ||||
|     /// Sends a DATA IN packet with `data` in response to a control read request. | ||||
|     /// Send a DATA IN packet with `data` in response to a control read request. | ||||
|     /// | ||||
|     /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. | ||||
|     async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; | ||||
|  | ||||
|     /// Accepts a control request. | ||||
|     /// Accept a control request. | ||||
|     /// | ||||
|     /// Causes the STATUS packet for the current request to be ACKed. | ||||
|     async fn accept(&mut self); | ||||
|  | ||||
|     /// Rejects a control request. | ||||
|     /// Reject a control request. | ||||
|     /// | ||||
|     /// Sets a STALL condition on the pipe to indicate an error. | ||||
|     async fn reject(&mut self); | ||||
| @@ -311,8 +344,9 @@ pub trait ControlPipe { | ||||
|     async fn accept_set_address(&mut self, addr: u8); | ||||
| } | ||||
|  | ||||
| /// IN Endpoint trait. | ||||
| pub trait EndpointIn: Endpoint { | ||||
|     /// Writes a single packet of data to the endpoint. | ||||
|     /// Write a single packet of data to the endpoint. | ||||
|     async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; | ||||
| } | ||||
|  | ||||
| @@ -338,18 +372,22 @@ pub enum Event { | ||||
|     PowerRemoved, | ||||
| } | ||||
|  | ||||
| /// Allocating an endpoint failed. | ||||
| /// | ||||
| /// This can be due to running out of endpoints, or out of endpoint memory, | ||||
| /// or because the hardware doesn't support the requested combination of features. | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub struct EndpointAllocError; | ||||
|  | ||||
| /// Operation is unsupported by the driver. | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Operation is unsupported by the driver. | ||||
| pub struct Unsupported; | ||||
|  | ||||
| /// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| /// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] | ||||
| pub enum EndpointError { | ||||
|     /// Either the packet to be written is too long to fit in the transmission | ||||
|     /// buffer or the received packet is too long to fit in `buf`. | ||||
|   | ||||
							
								
								
									
										22
									
								
								embassy-usb/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								embassy-usb/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| # embassy-usb | ||||
|  | ||||
| TODO crate description/ | ||||
|  | ||||
| ## Interoperability | ||||
|  | ||||
| This crate can run on any executor. | ||||
|  | ||||
| ## Minimum supported Rust version (MSRV) | ||||
|  | ||||
| This crate requires nightly Rust, due to using "async fn in trait" support. | ||||
|  | ||||
| ## License | ||||
|  | ||||
| This work is licensed under either of | ||||
|  | ||||
| - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or | ||||
|   <http://www.apache.org/licenses/LICENSE-2.0>) | ||||
| - MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>) | ||||
|  | ||||
| at your option. | ||||
|  | ||||
| @@ -349,11 +349,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | ||||
|         self.builder.config_descriptor.write(descriptor_type, descriptor) | ||||
|     } | ||||
|  | ||||
|     fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn { | ||||
|     fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | ||||
|         let ep = self | ||||
|             .builder | ||||
|             .driver | ||||
|             .alloc_endpoint_in(ep_type, max_packet_size, interval) | ||||
|             .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) | ||||
|             .expect("alloc_endpoint_in failed"); | ||||
|  | ||||
|         self.builder.config_descriptor.endpoint(ep.info()); | ||||
| @@ -361,11 +361,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | ||||
|         ep | ||||
|     } | ||||
|  | ||||
|     fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut { | ||||
|     fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | ||||
|         let ep = self | ||||
|             .builder | ||||
|             .driver | ||||
|             .alloc_endpoint_out(ep_type, max_packet_size, interval) | ||||
|             .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) | ||||
|             .expect("alloc_endpoint_out failed"); | ||||
|  | ||||
|         self.builder.config_descriptor.endpoint(ep.info()); | ||||
| @@ -393,25 +393,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | ||||
|     /// | ||||
|     /// Descriptors are written in the order builder functions are called. Note that some | ||||
|     /// classes care about the order. | ||||
|     pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { | ||||
|         self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval) | ||||
|     pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | ||||
|         self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     /// Allocate a INTERRUPT OUT endpoint and write its descriptor. | ||||
|     pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { | ||||
|         self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) | ||||
|     pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | ||||
|         self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. | ||||
|     /// | ||||
|     /// Descriptors are written in the order builder functions are called. Note that some | ||||
|     /// classes care about the order. | ||||
|     pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { | ||||
|         self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) | ||||
|     pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | ||||
|         self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms) | ||||
|     } | ||||
|  | ||||
|     /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. | ||||
|     pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { | ||||
|         self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) | ||||
|     pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | ||||
|         self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| //! CDC-ACM class implementation, aka Serial over USB. | ||||
|  | ||||
| use core::cell::Cell; | ||||
| use core::mem::{self, MaybeUninit}; | ||||
| use core::sync::atomic::{AtomicBool, Ordering}; | ||||
| @@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20; | ||||
| const REQ_GET_LINE_CODING: u8 = 0x21; | ||||
| const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||||
|  | ||||
| /// Internal state for CDC-ACM | ||||
| pub struct State<'a> { | ||||
|     control: MaybeUninit<Control<'a>>, | ||||
|     shared: ControlShared, | ||||
| } | ||||
|  | ||||
| impl<'a> State<'a> { | ||||
|     /// Create a new `State`. | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             control: MaybeUninit::uninit(), | ||||
| @@ -284,10 +288,15 @@ impl From<u8> for StopBits { | ||||
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum ParityType { | ||||
|     /// No parity bit. | ||||
|     None = 0, | ||||
|     /// Parity bit is 1 if the amount of `1` bits in the data byte is odd. | ||||
|     Odd = 1, | ||||
|     /// Parity bit is 1 if the amount of `1` bits in the data byte is even. | ||||
|     Even = 2, | ||||
|     /// Parity bit is always 1 | ||||
|     Mark = 3, | ||||
|     /// Parity bit is always 0 | ||||
|     Space = 4, | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class. | ||||
| use embassy_futures::select::{select, Either}; | ||||
| use embassy_net_driver_channel as ch; | ||||
| use embassy_net_driver_channel::driver::LinkState; | ||||
| @@ -5,11 +6,13 @@ use embassy_usb_driver::Driver; | ||||
|  | ||||
| use super::{CdcNcmClass, Receiver, Sender}; | ||||
|  | ||||
| /// Internal state for the embassy-net integration. | ||||
| pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { | ||||
|     ch_state: ch::State<MTU, N_RX, N_TX>, | ||||
| } | ||||
|  | ||||
| impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> { | ||||
|     /// Create a new `State`. | ||||
|     pub const fn new() -> Self { | ||||
|         Self { | ||||
|             ch_state: ch::State::new(), | ||||
| @@ -17,6 +20,9 @@ impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_ | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Background runner for the CDC-NCM class. | ||||
| /// | ||||
| /// You must call `.run()` in a background task for the class to operate. | ||||
| pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { | ||||
|     tx_usb: Sender<'d, D>, | ||||
|     rx_usb: Receiver<'d, D>, | ||||
| @@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { | ||||
| } | ||||
|  | ||||
| impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { | ||||
|     /// Run the CDC-NCM class. | ||||
|     /// | ||||
|     /// You must call this in a background task for the class to operate. | ||||
|     pub async fn run(mut self) -> ! { | ||||
|         let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); | ||||
|         let rx_fut = async move { | ||||
| @@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { | ||||
|  | ||||
| // would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? | ||||
| //pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; | ||||
| /// Type alias for the embassy-net driver for CDC-NCM. | ||||
| pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; | ||||
|  | ||||
| impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | ||||
|     /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net). | ||||
|     pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( | ||||
|         self, | ||||
|         state: &'d mut State<MTU, N_RX, N_TX>, | ||||
|   | ||||
| @@ -1,18 +1,19 @@ | ||||
| /// CDC-NCM, aka Ethernet over USB. | ||||
| /// | ||||
| /// # Compatibility | ||||
| /// | ||||
| /// Windows: NOT supported in Windows 10. Supported in Windows 11. | ||||
| /// | ||||
| /// Linux: Well-supported since forever. | ||||
| /// | ||||
| /// Android: Support for CDC-NCM is spotty and varies across manufacturers. | ||||
| /// | ||||
| /// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||||
| /// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||||
| ///   it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||||
| ///   This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||||
| ///   and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||||
| //! CDC-NCM class implementation, aka Ethernet over USB. | ||||
| //! | ||||
| //! # Compatibility | ||||
| //! | ||||
| //! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11. | ||||
| //! | ||||
| //! Linux: Well-supported since forever. | ||||
| //! | ||||
| //! Android: Support for CDC-NCM is spotty and varies across manufacturers. | ||||
| //! | ||||
| //! - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||||
| //! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||||
| //!   it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||||
| //!   This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||||
| //!   and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||||
|  | ||||
| use core::intrinsics::copy_nonoverlapping; | ||||
| use core::mem::{size_of, MaybeUninit}; | ||||
|  | ||||
| @@ -114,6 +115,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] { | ||||
|     &buf[..len] | ||||
| } | ||||
|  | ||||
| /// Internal state for the CDC-NCM class. | ||||
| pub struct State<'a> { | ||||
|     comm_control: MaybeUninit<CommControl<'a>>, | ||||
|     data_control: MaybeUninit<DataControl>, | ||||
| @@ -121,6 +123,7 @@ pub struct State<'a> { | ||||
| } | ||||
|  | ||||
| impl<'a> State<'a> { | ||||
|     /// Create a new `State`. | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             comm_control: MaybeUninit::uninit(), | ||||
| @@ -223,6 +226,7 @@ impl ControlHandler for DataControl { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// CDC-NCM class | ||||
| pub struct CdcNcmClass<'d, D: Driver<'d>> { | ||||
|     _comm_if: InterfaceNumber, | ||||
|     comm_ep: D::EndpointIn, | ||||
| @@ -235,6 +239,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> { | ||||
| } | ||||
|  | ||||
| impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | ||||
|     /// Create a new CDC NCM class. | ||||
|     pub fn new( | ||||
|         builder: &mut Builder<'d, D>, | ||||
|         state: &'d mut State<'d>, | ||||
| @@ -319,6 +324,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// 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 { | ||||
| @@ -334,12 +342,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// CDC NCM class packet sender. | ||||
| /// | ||||
| /// You can obtain a `Sender` with [`CdcNcmClass::split`] | ||||
| pub struct Sender<'d, D: Driver<'d>> { | ||||
|     write_ep: D::EndpointIn, | ||||
|     seq: u16, | ||||
| } | ||||
|  | ||||
| impl<'d, D: Driver<'d>> Sender<'d, D> { | ||||
|     /// Write a packet. | ||||
|     /// | ||||
|     /// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers. | ||||
|     pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { | ||||
|         let seq = self.seq; | ||||
|         self.seq = self.seq.wrapping_add(1); | ||||
| @@ -393,6 +407,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// CDC NCM class packet receiver. | ||||
| /// | ||||
| /// You can obtain a `Receiver` with [`CdcNcmClass::split`] | ||||
| pub struct Receiver<'d, D: Driver<'d>> { | ||||
|     data_if: InterfaceNumber, | ||||
|     comm_ep: D::EndpointIn, | ||||
| @@ -400,7 +417,9 @@ pub struct Receiver<'d, D: Driver<'d>> { | ||||
| } | ||||
|  | ||||
| impl<'d, D: Driver<'d>> Receiver<'d, D> { | ||||
|     /// Reads a single packet from the OUT endpoint. | ||||
|     /// Write a network packet. | ||||
|     /// | ||||
|     /// This waits until a packet is succesfully received from the endpoint buffers. | ||||
|     pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | ||||
|         // Retry loop | ||||
|         loop { | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| //! USB HID (Human Interface Device) class implementation. | ||||
|  | ||||
| use core::mem::MaybeUninit; | ||||
| use core::ops::Range; | ||||
| use core::sync::atomic::{AtomicUsize, Ordering}; | ||||
| @@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09; | ||||
| const HID_REQ_GET_PROTOCOL: u8 = 0x03; | ||||
| const HID_REQ_SET_PROTOCOL: u8 = 0x0b; | ||||
|  | ||||
| /// Configuration for the HID class. | ||||
| pub struct Config<'d> { | ||||
|     /// HID report descriptor. | ||||
|     pub report_descriptor: &'d [u8], | ||||
| @@ -46,11 +49,15 @@ pub struct Config<'d> { | ||||
|     pub max_packet_size: u16, | ||||
| } | ||||
|  | ||||
| /// Report ID | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum ReportId { | ||||
|     /// IN report | ||||
|     In(u8), | ||||
|     /// OUT report | ||||
|     Out(u8), | ||||
|     /// Feature report | ||||
|     Feature(u8), | ||||
| } | ||||
|  | ||||
| @@ -65,12 +72,14 @@ impl ReportId { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Internal state for USB HID. | ||||
| pub struct State<'d> { | ||||
|     control: MaybeUninit<Control<'d>>, | ||||
|     out_report_offset: AtomicUsize, | ||||
| } | ||||
|  | ||||
| impl<'d> State<'d> { | ||||
|     /// Create a new `State`. | ||||
|     pub fn new() -> Self { | ||||
|         State { | ||||
|             control: MaybeUninit::uninit(), | ||||
| @@ -79,6 +88,7 @@ impl<'d> State<'d> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// USB HID reader/writer. | ||||
| pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { | ||||
|     reader: HidReader<'d, D, READ_N>, | ||||
|     writer: HidWriter<'d, D, WRITE_N>, | ||||
| @@ -180,20 +190,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// USB HID writer. | ||||
| /// | ||||
| /// You can obtain a `HidWriter` using [`HidReaderWriter::split`]. | ||||
| pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { | ||||
|     ep_in: D::EndpointIn, | ||||
| } | ||||
|  | ||||
| /// USB HID reader. | ||||
| /// | ||||
| /// You can obtain a `HidReader` using [`HidReaderWriter::split`]. | ||||
| pub struct HidReader<'d, D: Driver<'d>, const N: usize> { | ||||
|     ep_out: D::EndpointOut, | ||||
|     offset: &'d AtomicUsize, | ||||
| } | ||||
|  | ||||
| /// Error when reading a HID report. | ||||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum ReadError { | ||||
|     /// The given buffer was too small to read the received report. | ||||
|     BufferOverflow, | ||||
|     /// The endpoint is disabled. | ||||
|     Disabled, | ||||
|     /// The report was only partially read. See [`HidReader::read`] for details. | ||||
|     Sync(Range<usize>), | ||||
| } | ||||
|  | ||||
| @@ -344,6 +364,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Handler for HID-related control requests. | ||||
| pub trait RequestHandler { | ||||
|     /// Reads the value of report `id` into `buf` returning the size. | ||||
|     /// | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| //! Implementations of well-known USB classes. | ||||
| pub mod cdc_acm; | ||||
| pub mod cdc_ncm; | ||||
| pub mod hid; | ||||
|   | ||||
| @@ -126,17 +126,23 @@ impl Request { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Response for a CONTROL OUT request. | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum OutResponse { | ||||
|     /// The request was accepted. | ||||
|     Accepted, | ||||
|     /// The request was rejected. | ||||
|     Rejected, | ||||
| } | ||||
|  | ||||
| /// Response for a CONTROL IN request. | ||||
| #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum InResponse<'a> { | ||||
|     /// The request was accepted. The buffer contains the response data. | ||||
|     Accepted(&'a [u8]), | ||||
|     /// The request was rejected. | ||||
|     Rejected, | ||||
| } | ||||
|  | ||||
| @@ -148,6 +154,7 @@ pub trait ControlHandler { | ||||
|     /// Called after a USB reset after the bus reset sequence is complete. | ||||
|     fn reset(&mut self) {} | ||||
|  | ||||
|     /// Called when a "set alternate setting" control request is done on the interface. | ||||
|     fn set_alternate_setting(&mut self, alternate_setting: u8) { | ||||
|         let _ = alternate_setting; | ||||
|     } | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| //! Utilities for writing USB descriptors. | ||||
|  | ||||
| use crate::builder::Config; | ||||
| use crate::driver::EndpointInfo; | ||||
| use crate::types::*; | ||||
| @@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> { | ||||
|                 endpoint.ep_type as u8, // bmAttributes | ||||
|                 endpoint.max_packet_size as u8, | ||||
|                 (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize | ||||
|                 endpoint.interval,                     // bInterval | ||||
|                 endpoint.interval_ms,                  // bInterval | ||||
|             ], | ||||
|         ); | ||||
|     } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| #![no_std] | ||||
| #![feature(type_alias_impl_trait)] | ||||
| #![doc = include_str!("../README.md")] | ||||
| #![warn(missing_docs)] | ||||
|  | ||||
| // This mod MUST go first, so that the others see its macros. | ||||
| pub(crate) mod fmt; | ||||
| @@ -46,10 +48,13 @@ pub enum UsbDeviceState { | ||||
|     Configured, | ||||
| } | ||||
|  | ||||
| /// Error returned by [`UsbDevice::remote_wakeup`]. | ||||
| #[derive(PartialEq, Eq, Copy, Clone, Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum RemoteWakeupError { | ||||
|     /// The USB device is not suspended, or remote wakeup was not enabled. | ||||
|     InvalidState, | ||||
|     /// The underlying driver doesn't support remote wakeup. | ||||
|     Unsupported, | ||||
| } | ||||
|  | ||||
| @@ -65,6 +70,7 @@ pub const CONFIGURATION_NONE: u8 = 0; | ||||
| /// The bConfiguration value for the single configuration supported by this device. | ||||
| pub const CONFIGURATION_VALUE: u8 = 1; | ||||
|  | ||||
| /// Maximum interface count, configured at compile time. | ||||
| pub const MAX_INTERFACE_COUNT: usize = 4; | ||||
|  | ||||
| const STRING_INDEX_MANUFACTURER: u8 = 1; | ||||
| @@ -100,6 +106,7 @@ struct Interface<'d> { | ||||
|     num_strings: u8, | ||||
| } | ||||
|  | ||||
| /// Main struct for the USB device stack. | ||||
| pub struct UsbDevice<'d, D: Driver<'d>> { | ||||
|     control_buf: &'d mut [u8], | ||||
|     control: D::ControlPipe, | ||||
| @@ -489,7 +496,6 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | ||||
|                         .unwrap(); | ||||
|  | ||||
|                         // TODO check it is valid (not out of range) | ||||
|                         // TODO actually enable/disable endpoints. | ||||
|  | ||||
|                         if let Some(handler) = &mut iface.handler { | ||||
|                             handler.set_alternate_setting(new_altsetting); | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| //! USB types. | ||||
|  | ||||
| /// A handle for a USB interface that contains its number. | ||||
| #[derive(Copy, Clone, Eq, PartialEq)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user