@@ -255,18 +255,47 @@ class control_endpoint : public endpoint
255255 * @param p_callback The callback function to be called when a USB request
256256 * command is received on the control endpoint.
257257 */
258- void on_receive (hal:: callback<void (on_receive_tag)> const & p_callback)
258+ void on_receive (callback<void (on_receive_tag)> const & p_callback)
259259 {
260260 driver_on_receive (p_callback);
261261 }
262262
263+ /* *
264+ * @brief Returns whether the endpoint's receive buffer contains a setup
265+ * packet.
266+ *
267+ * Returns `true` until all 8 bytes of the setup packet have been extracted
268+ * via `read()`. Returns `false` once the setup packet has been fully consumed
269+ * or if the last received packet was not a setup packet.
270+ *
271+ * Returns `std::nullopt` if the implementation does not support setup packet
272+ * detection. Enumerators that require this information should accept a
273+ * callable (as a template parameter or type-erased input parameter) to work
274+ * around legacy implementations.
275+ *
276+ * @note Safe to call from ISR context. Implementations must not throw,
277+ * call throwing functions, or perform I/O within this method.
278+ *
279+ * @return `std::nullopt` if setup packet detection is unsupported.
280+ * @return `true` if the receive buffer contains an unconsumed setup packet.
281+ * @return `false` otherwise.
282+ */
283+ [[nodiscard]] std::optional<bool > has_setup () const noexcept
284+ {
285+ return driver_has_setup ();
286+ }
287+
263288private:
264289 virtual void driver_connect (bool p_should_connect) = 0;
265290 virtual void driver_set_address (u8 p_address) = 0;
266291 virtual void driver_write (scatter_span<byte const > p_data) = 0;
267292 virtual usize driver_read (scatter_span<byte> p_buffer) = 0;
268293 virtual void driver_on_receive (
269294 callback<void (on_receive_tag)> const & p_callback) = 0;
295+ [[nodiscard]] virtual std::optional<bool > driver_has_setup () const noexcept
296+ {
297+ return {};
298+ }
270299};
271300
272301/* *
@@ -454,6 +483,10 @@ concept in_endpoint_type =
454483 */
455484struct setup_packet
456485{
486+ constexpr static usize value_offset = 2 ;
487+ constexpr static usize index_offset = 4 ;
488+ constexpr static usize length_offset = 6 ;
489+
457490 /* *
458491 * @brief Request type classification for setup packets
459492 *
@@ -578,7 +611,7 @@ struct setup_packet
578611 return request_type::invalid;
579612 }
580613
581- return static_cast <enum request_type>(t);
614+ return static_cast <request_type>(t);
582615 }
583616
584617 /* *
@@ -629,47 +662,50 @@ struct setup_packet
629662 */
630663 [[nodiscard]] constexpr u16 value () const
631664 {
632- return from_le_bytes (raw_request_bytes[2 ], raw_request_bytes[3 ]);
665+ return from_le_bytes (raw_request_bytes[value_offset],
666+ raw_request_bytes[value_offset + 1 ]);
633667 }
634668
635669 /* *
636670 * @brief Get wValue field as raw LE bytes for USB bus transmission
637671 */
638672 [[nodiscard]] constexpr std::span<hal::byte const > value_bytes () const
639673 {
640- return std::span (raw_request_bytes).subspan (2 , 2 );
674+ return std::span (raw_request_bytes).subspan (value_offset, sizeof ( u16 ) );
641675 }
642676
643677 /* *
644678 * @brief Get wIndex field in native endianness for program use
645679 */
646680 [[nodiscard]] constexpr u16 index () const
647681 {
648- return from_le_bytes (raw_request_bytes[4 ], raw_request_bytes[5 ]);
682+ return from_le_bytes (raw_request_bytes[index_offset],
683+ raw_request_bytes[index_offset + 1 ]);
649684 }
650685
651686 /* *
652687 * @brief Get wIndex field as raw LE bytes for USB bus transmission
653688 */
654689 [[nodiscard]] constexpr std::span<hal::byte const > index_bytes () const
655690 {
656- return std::span (raw_request_bytes).subspan (4 , 2 );
691+ return std::span (raw_request_bytes).subspan (index_offset, sizeof ( u16 ) );
657692 }
658693
659694 /* *
660695 * @brief Get wLength field in native endianness for program use
661696 */
662697 [[nodiscard]] constexpr u16 length () const
663698 {
664- return from_le_bytes (raw_request_bytes[6 ], raw_request_bytes[7 ]);
699+ return from_le_bytes (raw_request_bytes[length_offset],
700+ raw_request_bytes[length_offset + 1 ]);
665701 }
666702
667703 /* *
668704 * @brief Get wLength field as raw LE bytes for USB bus transmission
669705 */
670706 [[nodiscard]] constexpr std::span<hal::byte const > length_bytes () const
671707 {
672- return std::span (raw_request_bytes).subspan (6 , 2 );
708+ return std::span (raw_request_bytes).subspan (length_offset, sizeof ( u16 ) );
673709 }
674710
675711 /* *
@@ -751,10 +787,6 @@ struct setup_packet
751787 }
752788
753789 std::array<byte, 8 > raw_request_bytes;
754-
755- constexpr static usize value_offset = 2 ;
756- constexpr static usize index_offset = 4 ;
757- constexpr static usize length_offset = 6 ;
758790};
759791
760792/* *
@@ -809,7 +841,6 @@ enum class standard_request_types : hal::byte
809841
810842 return static_cast <standard_request_types>(p_packet.request ());
811843}
812-
813844/* *
814845 * @brief Endpoint I/O interface for USB request handling
815846 *
@@ -820,8 +851,17 @@ enum class standard_request_types : hal::byte
820851 * phase of control transfers.
821852 *
822853 * The direction of data transfer depends on the setup packet:
854+ *
823855 * - Device-to-host (IN): Use `write()` to send response data to the host
824856 * - Host-to-device (OUT): Use `read()` to receive data sent by the host
857+ *
858+ * @throws hal::operation_not_permitted - If a new setup packet is received
859+ * before the current control transfer completes, this exception is thrown to
860+ * interrupt the data phase. Only the enumerator should handle this exception;
861+ * it must catch it and evaluate the new setup packet. If the underlying
862+ * control endpoint implementation does not support setup_packet detection,
863+ * this should not be thrown and the enumerator will have to make assumptions
864+ * about which packets are setup packets and which are not.
825865 */
826866class endpoint_io
827867{
@@ -836,6 +876,7 @@ class endpoint_io
836876 * endpoint
837877 * @return usize - the number of bytes read into the provided buffers. Value
838878 * is 0 if there is no data available within the endpoint.
879+ * @throws hal::operation_not_permitted - See class documentation.
839880 */
840881 usize read (scatter_span<byte> p_buffer)
841882 {
@@ -851,6 +892,7 @@ class endpoint_io
851892 * @param p_buffer - scatter span of const byte buffers containing data to
852893 * write to the endpoint
853894 * @return usize - the number of bytes written from the provided buffers
895+ * @throws hal::operation_not_permitted - See class documentation.
854896 */
855897 usize write (scatter_span<byte const > p_buffer)
856898 {
@@ -934,13 +976,13 @@ class interface
934976 * @param p_start - the starting values for interface numbers and string
935977 * indexes. The `string` field should be cached by the interface in order to
936978 * allow `write_string_descriptor` to work correctly.
937- * @param p_ep_req - endpoint I/O interface used to write descriptor data to
979+ * @param p_endpoint - endpoint I/O interface used to write descriptor data to
938980 * the host.
939981 */
940982 [[nodiscard]] descriptor_count write_descriptors (descriptor_start p_start,
941- endpoint_io& p_ep_req )
983+ endpoint_io& p_endpoint )
942984 {
943- return driver_write_descriptors (p_start, p_ep_req );
985+ return driver_write_descriptors (p_start, p_endpoint );
944986 }
945987
946988 /* *
@@ -955,56 +997,72 @@ class interface
955997 * and descriptor type fields.
956998 *
957999 * @param p_index - Which string index's descriptor should be written.
958- * @param p_ep_req - endpoint I/O interface used to write the string
1000+ * @param p_endpoint - endpoint I/O interface used to write the string
9591001 * descriptor to the host.
9601002 *
9611003 * @returns true - if the string was located and written via the endpoint I/O.
9621004 * @returns false - if the string requested does not belong to this interface.
9631005 */
964- [[nodiscard]] bool write_string_descriptor (u8 p_index, endpoint_io& p_ep_req)
1006+ [[nodiscard]] bool write_string_descriptor (u8 p_index,
1007+ endpoint_io& p_endpoint)
9651008 {
966- return driver_write_string_descriptor (p_index, p_ep_req );
1009+ return driver_write_string_descriptor (p_index, p_endpoint );
9671010 }
968-
9691011 /* *
9701012 * @brief Handle USB requests directed to this interface or its endpoints
9711013 *
972- * This method is called when the USB device receives a setup packet that
973- * targets this interface (interface-specific requests) or one of its
1014+ * This method is called when the USB device receives a setup packet that may
1015+ * target this interface (interface-specific requests) or one of its
9741016 * endpoints (endpoint-specific requests). The interface should examine
975- * the setup packet and handle any requests it recognizes.
1017+ * the setup packet and handle any requests it recognizes. If the setup packet
1018+ * is not for this interface, this API must return false.
1019+ *
1020+ * The interface must handle:
9761021 *
977- * The interface may handle:
9781022 * - Standard requests specific to this interface type
9791023 * - Class-specific requests defined by the interface's USB class
9801024 * - Vendor-specific requests for custom functionality
9811025 * - Endpoint-specific requests for its endpoints
982- * - Anything that isn't a device or configuration level request
1026+ *
1027+ * The `endpoint_io` object is constructed by the enumerator to provide
1028+ * limited access to the control endpoint.
9831029 *
9841030 * If the request has a data phase (wLength > 0), use the endpoint I/O
985- * interface to send response data (device-to-host) or receive data
986- * (host-to-device).
1031+ * interface to send data from device-to-host via the `write` API or
1032+ * receive data from host-to-device via the `read` API.
1033+ *
1034+ * When a new setup packet is received from the HOST, the enumerator ensures
1035+ * that `hal::operation_not_permitted` is thrown from all `endpoint_io` APIs.
1036+ * Implementations of `driver_handle_request` MUST NOT catch this exception;
1037+ * it must propagate to the enumerator for handling. Correct use of RAII to
1038+ * undo state changes is advised. If cleanup requires a catch block, the
1039+ * exception must be rethrown via `throw;` before exiting.
9871040 *
9881041 * @param p_setup - Setup request from the host.
989- * @param p_ep_req - endpoint I/O interface for reading or writing data
990- * during the data phase of the control transfer.
1042+ * @param p_endpoint - endpoint I/O interface for reading or writing data to
1043+ * the host via the control endpoint during the data phase of the control
1044+ * transfer.
9911045 * @return true - if the request was handled by the interface.
9921046 * @return false - if the request could not be handled by interface.
1047+ * @throws hal::operation_not_permitted - If a new setup packet is received
1048+ * before the current control transfer completes. This exception must not be
1049+ * caught by the implementation; it is intended for the enumerator.
9931050 */
994- bool handle_request (setup_packet const & p_setup, endpoint_io& p_ep_req )
1051+ bool handle_request (setup_packet const & p_setup, endpoint_io& p_endpoint )
9951052 {
996- return driver_handle_request (p_setup, p_ep_req );
1053+ return driver_handle_request (p_setup, p_endpoint );
9971054 }
9981055
9991056 virtual ~interface () = default ;
10001057
10011058private:
1002- virtual descriptor_count driver_write_descriptors (descriptor_start p_start,
1003- endpoint_io& p_ep_req) = 0;
1059+ virtual descriptor_count driver_write_descriptors (
1060+ descriptor_start p_start,
1061+ endpoint_io& p_endpoint) = 0;
10041062 virtual bool driver_write_string_descriptor (u8 p_index,
1005- endpoint_io& p_ep_req ) = 0;
1063+ endpoint_io& p_endpoint ) = 0;
10061064 virtual bool driver_handle_request (setup_packet const & p_setup,
1007- endpoint_io& p_ep_req ) = 0;
1065+ endpoint_io& p_endpoint ) = 0;
10081066};
10091067} // namespace hal::v5::usb
10101068
0 commit comments